From c0ad1937d0004800f23b53308b5272208860dbbf Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 31 Dec 2024 14:02:31 -0800 Subject: [PATCH 001/208] src: move more crypto impl detail to ncrypto dep * remove obsolete LogSecret function * move StackOfX509 decl to ncrypto * colocate GetSSLOCSPResponse with callsite * move x509 error code and reason to ncrypto * simplify X509Pointer/X509View pointer derefs a bit * move prime gen and checks to ncrypto * move BN_* methods into ncrypto * move SSLPointer impl to ncrypto * move SSLCtxPointer impl to ncrypto * move EVP_CIPHER methods to ncrypto * move CipherCtx methods to ncrypto PR-URL: https://github.com/nodejs/node/pull/56421 Reviewed-By: Yagiz Nizipli --- deps/ncrypto/ncrypto.cc | 569 +++++++++++++++++++++++++++++++++++ deps/ncrypto/ncrypto.h | 196 +++++++++++- src/crypto/crypto_aes.cc | 137 ++++----- src/crypto/crypto_aes.h | 2 +- src/crypto/crypto_cipher.cc | 304 ++++++++----------- src/crypto/crypto_cipher.h | 4 +- src/crypto/crypto_common.cc | 241 ++------------- src/crypto/crypto_common.h | 32 -- src/crypto/crypto_context.cc | 8 +- src/crypto/crypto_random.cc | 48 +-- src/crypto/crypto_tls.cc | 76 +++-- src/crypto/crypto_util.h | 1 - src/crypto/crypto_x509.cc | 34 +-- src/quic/tlscontext.cc | 10 +- 14 files changed, 1054 insertions(+), 608 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index ac2d771555126a..ebdda72184b709 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -311,6 +311,87 @@ BignumPointer BignumPointer::clone() { return BignumPointer(BN_dup(bn_.get())); } +int BignumPointer::isPrime(int nchecks, + BignumPointer::PrimeCheckCallback innerCb) const { + BignumCtxPointer ctx(BN_CTX_new()); + BignumGenCallbackPointer cb(nullptr); + if (innerCb != nullptr) { + cb = BignumGenCallbackPointer(BN_GENCB_new()); + if (!cb) [[unlikely]] + return -1; + BN_GENCB_set( + cb.get(), + // TODO(@jasnell): This could be refactored to allow inlining. + // Not too important right now tho. + [](int a, int b, BN_GENCB* ctx) mutable -> int { + PrimeCheckCallback& ptr = + *static_cast(BN_GENCB_get_arg(ctx)); + return ptr(a, b) ? 1 : 0; + }, + &innerCb); + } + return BN_is_prime_ex(get(), nchecks, ctx.get(), cb.get()); +} + +BignumPointer BignumPointer::NewPrime(const PrimeConfig& params, + PrimeCheckCallback cb) { + BignumPointer prime(BN_new()); + if (!prime || !prime.generate(params, std::move(cb))) { + return {}; + } + return prime; +} + +bool BignumPointer::generate(const PrimeConfig& params, + PrimeCheckCallback innerCb) const { + // BN_generate_prime_ex() calls RAND_bytes_ex() internally. + // Make sure the CSPRNG is properly seeded. + CSPRNG(nullptr, 0); + BignumGenCallbackPointer cb(nullptr); + if (innerCb != nullptr) { + cb = BignumGenCallbackPointer(BN_GENCB_new()); + if (!cb) [[unlikely]] + return -1; + BN_GENCB_set( + cb.get(), + [](int a, int b, BN_GENCB* ctx) mutable -> int { + PrimeCheckCallback& ptr = + *static_cast(BN_GENCB_get_arg(ctx)); + return ptr(a, b) ? 1 : 0; + }, + &innerCb); + } + if (BN_generate_prime_ex(get(), + params.bits, + params.safe ? 1 : 0, + params.add.get(), + params.rem.get(), + cb.get()) == 0) { + return false; + } + + return true; +} + +BignumPointer BignumPointer::NewSub(const BignumPointer& a, + const BignumPointer& b) { + BignumPointer res = New(); + if (!res) return {}; + if (!BN_sub(res.get(), a.get(), b.get())) { + return {}; + } + return res; +} + +BignumPointer BignumPointer::NewLShift(size_t length) { + BignumPointer res = New(); + if (!res) return {}; + if (!BN_lshift(res.get(), One(), length)) { + return {}; + } + return res; +} + // ============================================================================ // Utility methods @@ -1005,6 +1086,29 @@ X509View X509View::From(const SSLCtxPointer& ctx) { return X509View(SSL_CTX_get0_certificate(ctx.get())); } +std::optional X509View::getFingerprint( + const EVP_MD* method) const { + unsigned int md_size; + unsigned char md[EVP_MAX_MD_SIZE]; + static constexpr char hex[] = "0123456789ABCDEF"; + + if (X509_digest(get(), method, md, &md_size)) { + if (md_size == 0) return std::nullopt; + std::string fingerprint((md_size * 3) - 1, 0); + for (unsigned int i = 0; i < md_size; i++) { + auto idx = 3 * i; + fingerprint[idx] = hex[(md[i] & 0xf0) >> 4]; + fingerprint[idx + 1] = hex[(md[i] & 0x0f)]; + if (i == md_size - 1) break; + fingerprint[idx + 2] = ':'; + } + + return fingerprint; + } + + return std::nullopt; +} + X509Pointer X509View::clone() const { ClearErrorOnReturn clear_error_on_return; if (!cert_) return {}; @@ -1050,6 +1154,53 @@ X509Pointer X509Pointer::IssuerFrom(const SSL_CTX* ctx, const X509View& cert) { X509Pointer X509Pointer::PeerFrom(const SSLPointer& ssl) { return X509Pointer(SSL_get_peer_certificate(ssl.get())); } + +// When adding or removing errors below, please also update the list in the API +// documentation. See the "OpenSSL Error Codes" section of doc/api/errors.md +// Also *please* update the respective section in doc/api/tls.md as well +std::string_view X509Pointer::ErrorCode(int32_t err) { // NOLINT(runtime/int) +#define CASE(CODE) \ + case X509_V_ERR_##CODE: \ + return #CODE; + switch (err) { + CASE(UNABLE_TO_GET_ISSUER_CERT) + CASE(UNABLE_TO_GET_CRL) + CASE(UNABLE_TO_DECRYPT_CERT_SIGNATURE) + CASE(UNABLE_TO_DECRYPT_CRL_SIGNATURE) + CASE(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY) + CASE(CERT_SIGNATURE_FAILURE) + CASE(CRL_SIGNATURE_FAILURE) + CASE(CERT_NOT_YET_VALID) + CASE(CERT_HAS_EXPIRED) + CASE(CRL_NOT_YET_VALID) + CASE(CRL_HAS_EXPIRED) + CASE(ERROR_IN_CERT_NOT_BEFORE_FIELD) + CASE(ERROR_IN_CERT_NOT_AFTER_FIELD) + CASE(ERROR_IN_CRL_LAST_UPDATE_FIELD) + CASE(ERROR_IN_CRL_NEXT_UPDATE_FIELD) + CASE(OUT_OF_MEM) + CASE(DEPTH_ZERO_SELF_SIGNED_CERT) + CASE(SELF_SIGNED_CERT_IN_CHAIN) + CASE(UNABLE_TO_GET_ISSUER_CERT_LOCALLY) + CASE(UNABLE_TO_VERIFY_LEAF_SIGNATURE) + CASE(CERT_CHAIN_TOO_LONG) + CASE(CERT_REVOKED) + CASE(INVALID_CA) + CASE(PATH_LENGTH_EXCEEDED) + CASE(INVALID_PURPOSE) + CASE(CERT_UNTRUSTED) + CASE(CERT_REJECTED) + CASE(HOSTNAME_MISMATCH) + } +#undef CASE + return "UNSPECIFIED"; +} + +std::optional X509Pointer::ErrorReason(int32_t err) { + if (err == X509_V_OK) return std::nullopt; + return X509_verify_cert_error_string(err); +} + // ============================================================================ // BIOPointer @@ -1105,6 +1256,12 @@ BIOPointer BIOPointer::NewFp(FILE* fd, int close_flag) { return BIOPointer(BIO_new_fp(fd, close_flag)); } +BIOPointer BIOPointer::New(const BIGNUM* bn) { + auto res = NewMem(); + if (!res || !BN_print(res.get(), bn)) return {}; + return res; +} + int BIOPointer::Write(BIOPointer* bio, std::string_view message) { if (bio == nullptr || !*bio) return 0; return BIO_write(bio->get(), message.data(), message.size()); @@ -2044,4 +2201,416 @@ Result EVPKeyPointer::writePublicKey( return bio; } +// ============================================================================ + +SSLPointer::SSLPointer(SSL* ssl) : ssl_(ssl) {} + +SSLPointer::SSLPointer(SSLPointer&& other) noexcept : ssl_(other.release()) {} + +SSLPointer& SSLPointer::operator=(SSLPointer&& other) noexcept { + if (this == &other) return *this; + this->~SSLPointer(); + return *new (this) SSLPointer(std::move(other)); +} + +SSLPointer::~SSLPointer() { + reset(); +} + +void SSLPointer::reset(SSL* ssl) { + ssl_.reset(ssl); +} + +SSL* SSLPointer::release() { + return ssl_.release(); +} + +SSLPointer SSLPointer::New(const SSLCtxPointer& ctx) { + if (!ctx) return {}; + return SSLPointer(SSL_new(ctx.get())); +} + +void SSLPointer::getCiphers( + std::function cb) const { + if (!ssl_) return; + STACK_OF(SSL_CIPHER)* ciphers = SSL_get_ciphers(get()); + + // TLSv1.3 ciphers aren't listed by EVP. There are only 5, we could just + // document them, but since there are only 5, easier to just add them manually + // and not have to explain their absence in the API docs. They are lower-cased + // because the docs say they will be. + static constexpr const char* TLS13_CIPHERS[] = { + "tls_aes_256_gcm_sha384", + "tls_chacha20_poly1305_sha256", + "tls_aes_128_gcm_sha256", + "tls_aes_128_ccm_8_sha256", + "tls_aes_128_ccm_sha256"}; + + const int n = sk_SSL_CIPHER_num(ciphers); + + for (int i = 0; i < n; ++i) { + const SSL_CIPHER* cipher = sk_SSL_CIPHER_value(ciphers, i); + cb(SSL_CIPHER_get_name(cipher)); + } + + for (unsigned i = 0; i < 5; ++i) { + cb(TLS13_CIPHERS[i]); + } +} + +bool SSLPointer::setSession(const SSLSessionPointer& session) { + if (!session || !ssl_) return false; + return SSL_set_session(get(), session.get()) == 1; +} + +bool SSLPointer::setSniContext(const SSLCtxPointer& ctx) const { + if (!ctx) return false; + auto x509 = ncrypto::X509View::From(ctx); + if (!x509) return false; + EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx.get()); + STACK_OF(X509) * chain; + int err = SSL_CTX_get0_chain_certs(ctx.get(), &chain); + if (err == 1) err = SSL_use_certificate(get(), x509); + if (err == 1) err = SSL_use_PrivateKey(get(), pkey); + if (err == 1 && chain != nullptr) err = SSL_set1_chain(get(), chain); + return err == 1; +} + +std::optional SSLPointer::verifyPeerCertificate() const { + if (!ssl_) return std::nullopt; + if (X509Pointer::PeerFrom(*this)) { + return SSL_get_verify_result(get()); + } + + const SSL_CIPHER* curr_cipher = SSL_get_current_cipher(get()); + const SSL_SESSION* sess = SSL_get_session(get()); + // Allow no-cert for PSK authentication in TLS1.2 and lower. + // In TLS1.3 check that session was reused because TLS1.3 PSK + // looks like session resumption. + if (SSL_CIPHER_get_auth_nid(curr_cipher) == NID_auth_psk || + (SSL_SESSION_get_protocol_version(sess) == TLS1_3_VERSION && + SSL_session_reused(get()))) { + return X509_V_OK; + } + + return std::nullopt; +} + +const std::string_view SSLPointer::getClientHelloAlpn() const { + if (ssl_ == nullptr) return {}; + const unsigned char* buf; + size_t len; + size_t rem; + + if (!SSL_client_hello_get0_ext( + get(), + TLSEXT_TYPE_application_layer_protocol_negotiation, + &buf, + &rem) || + rem < 2) { + return {}; + } + + len = (buf[0] << 8) | buf[1]; + if (len + 2 != rem) return {}; + return reinterpret_cast(buf + 3); +} + +const std::string_view SSLPointer::getClientHelloServerName() const { + if (ssl_ == nullptr) return {}; + const unsigned char* buf; + size_t len; + size_t rem; + + if (!SSL_client_hello_get0_ext(get(), TLSEXT_TYPE_server_name, &buf, &rem) || + rem <= 2) { + return {}; + } + + len = (*buf << 8) | *(buf + 1); + if (len + 2 != rem) return {}; + rem = len; + + if (rem == 0 || *(buf + 2) != TLSEXT_NAMETYPE_host_name) return {}; + rem--; + if (rem <= 2) return {}; + len = (*(buf + 3) << 8) | *(buf + 4); + if (len + 2 > rem) return {}; + return reinterpret_cast(buf + 5); +} + +std::optional SSLPointer::GetServerName( + const SSL* ssl) { + if (ssl == nullptr) return std::nullopt; + auto res = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (res == nullptr) return std::nullopt; + return res; +} + +std::optional SSLPointer::getServerName() const { + if (!ssl_) return std::nullopt; + return GetServerName(get()); +} + +X509View SSLPointer::getCertificate() const { + if (!ssl_) return {}; + ClearErrorOnReturn clear_error_on_return; + return ncrypto::X509View(SSL_get_certificate(get())); +} + +const SSL_CIPHER* SSLPointer::getCipher() const { + if (!ssl_) return nullptr; + return SSL_get_current_cipher(get()); +} + +bool SSLPointer::isServer() const { + return SSL_is_server(get()) != 0; +} + +EVPKeyPointer SSLPointer::getPeerTempKey() const { + if (!ssl_) return {}; + EVP_PKEY* raw_key = nullptr; + if (!SSL_get_peer_tmp_key(get(), &raw_key)) return {}; + return EVPKeyPointer(raw_key); +} + +SSLCtxPointer::SSLCtxPointer(SSL_CTX* ctx) : ctx_(ctx) {} + +SSLCtxPointer::SSLCtxPointer(SSLCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +SSLCtxPointer& SSLCtxPointer::operator=(SSLCtxPointer&& other) noexcept { + if (this == &other) return *this; + this->~SSLCtxPointer(); + return *new (this) SSLCtxPointer(std::move(other)); +} + +SSLCtxPointer::~SSLCtxPointer() { + reset(); +} + +void SSLCtxPointer::reset(SSL_CTX* ctx) { + ctx_.reset(ctx); +} + +void SSLCtxPointer::reset(const SSL_METHOD* method) { + ctx_.reset(SSL_CTX_new(method)); +} + +SSL_CTX* SSLCtxPointer::release() { + return ctx_.release(); +} + +SSLCtxPointer SSLCtxPointer::NewServer() { + return SSLCtxPointer(SSL_CTX_new(TLS_server_method())); +} + +SSLCtxPointer SSLCtxPointer::NewClient() { + return SSLCtxPointer(SSL_CTX_new(TLS_client_method())); +} + +SSLCtxPointer SSLCtxPointer::New(const SSL_METHOD* method) { + return SSLCtxPointer(SSL_CTX_new(method)); +} + +bool SSLCtxPointer::setGroups(const char* groups) { + return SSL_CTX_set1_groups_list(get(), groups) == 1; +} + +// ============================================================================ + +const Cipher Cipher::FromName(const char* name) { + return Cipher(EVP_get_cipherbyname(name)); +} + +const Cipher Cipher::FromNid(int nid) { + return Cipher(EVP_get_cipherbynid(nid)); +} + +const Cipher Cipher::FromCtx(const CipherCtxPointer& ctx) { + return Cipher(EVP_CIPHER_CTX_cipher(ctx.get())); +} + +int Cipher::getMode() const { + if (!cipher_) return 0; + return EVP_CIPHER_mode(cipher_); +} + +int Cipher::getIvLength() const { + if (!cipher_) return 0; + return EVP_CIPHER_iv_length(cipher_); +} + +int Cipher::getKeyLength() const { + if (!cipher_) return 0; + return EVP_CIPHER_key_length(cipher_); +} + +int Cipher::getBlockSize() const { + if (!cipher_) return 0; + return EVP_CIPHER_block_size(cipher_); +} + +int Cipher::getNid() const { + if (!cipher_) return 0; + return EVP_CIPHER_nid(cipher_); +} + +std::string_view Cipher::getModeLabel() const { + if (!cipher_) return {}; + switch (getMode()) { + case EVP_CIPH_CCM_MODE: + return "ccm"; + case EVP_CIPH_CFB_MODE: + return "cfb"; + case EVP_CIPH_CBC_MODE: + return "cbc"; + case EVP_CIPH_CTR_MODE: + return "ctr"; + case EVP_CIPH_ECB_MODE: + return "ecb"; + case EVP_CIPH_GCM_MODE: + return "gcm"; + case EVP_CIPH_OCB_MODE: + return "ocb"; + case EVP_CIPH_OFB_MODE: + return "ofb"; + case EVP_CIPH_WRAP_MODE: + return "wrap"; + case EVP_CIPH_XTS_MODE: + return "xts"; + case EVP_CIPH_STREAM_CIPHER: + return "stream"; + } + return "{unknown}"; +} + +std::string_view Cipher::getName() const { + if (!cipher_) return {}; + // OBJ_nid2sn(EVP_CIPHER_nid(cipher)) is used here instead of + // EVP_CIPHER_name(cipher) for compatibility with BoringSSL. + return OBJ_nid2sn(getNid()); +} + +bool Cipher::isSupportedAuthenticatedMode() const { + switch (getMode()) { + case EVP_CIPH_CCM_MODE: + case EVP_CIPH_GCM_MODE: +#ifndef OPENSSL_NO_OCB + case EVP_CIPH_OCB_MODE: +#endif + return true; + case EVP_CIPH_STREAM_CIPHER: + return getNid() == NID_chacha20_poly1305; + default: + return false; + } +} + +// ============================================================================ + +CipherCtxPointer CipherCtxPointer::New() { + auto ret = CipherCtxPointer(EVP_CIPHER_CTX_new()); + if (!ret) return {}; + EVP_CIPHER_CTX_init(ret.get()); + return ret; +} + +CipherCtxPointer::CipherCtxPointer(EVP_CIPHER_CTX* ctx) : ctx_(ctx) {} + +CipherCtxPointer::CipherCtxPointer(CipherCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +CipherCtxPointer& CipherCtxPointer::operator=( + CipherCtxPointer&& other) noexcept { + if (this == &other) return *this; + this->~CipherCtxPointer(); + return *new (this) CipherCtxPointer(std::move(other)); +} + +CipherCtxPointer::~CipherCtxPointer() { + reset(); +} + +void CipherCtxPointer::reset(EVP_CIPHER_CTX* ctx) { + ctx_.reset(ctx); +} + +EVP_CIPHER_CTX* CipherCtxPointer::release() { + return ctx_.release(); +} + +void CipherCtxPointer::setFlags(int flags) { + if (!ctx_) return; + EVP_CIPHER_CTX_set_flags(ctx_.get(), flags); +} + +bool CipherCtxPointer::setKeyLength(size_t length) { + if (!ctx_) return false; + return EVP_CIPHER_CTX_set_key_length(ctx_.get(), length); +} + +bool CipherCtxPointer::setIvLength(size_t length) { + if (!ctx_) return false; + return EVP_CIPHER_CTX_ctrl( + ctx_.get(), EVP_CTRL_AEAD_SET_IVLEN, length, nullptr); +} + +bool CipherCtxPointer::setAeadTag(const Buffer& tag) { + if (!ctx_) return false; + return EVP_CIPHER_CTX_ctrl( + ctx_.get(), EVP_CTRL_AEAD_SET_TAG, tag.len, const_cast(tag.data)); +} + +bool CipherCtxPointer::setAeadTagLength(size_t length) { + if (!ctx_) return false; + return EVP_CIPHER_CTX_ctrl( + ctx_.get(), EVP_CTRL_AEAD_SET_TAG, length, nullptr); +} + +bool CipherCtxPointer::setPadding(bool padding) { + if (!ctx_) return false; + return EVP_CIPHER_CTX_set_padding(ctx_.get(), padding); +} + +int CipherCtxPointer::getBlockSize() const { + if (!ctx_) return 0; + return EVP_CIPHER_CTX_block_size(ctx_.get()); +} + +int CipherCtxPointer::getMode() const { + if (!ctx_) return 0; + return EVP_CIPHER_CTX_mode(ctx_.get()); +} + +int CipherCtxPointer::getNid() const { + if (!ctx_) return 0; + return EVP_CIPHER_CTX_nid(ctx_.get()); +} + +bool CipherCtxPointer::init(const Cipher& cipher, + bool encrypt, + const unsigned char* key, + const unsigned char* iv) { + if (!ctx_) return false; + return EVP_CipherInit_ex( + ctx_.get(), cipher, nullptr, key, iv, encrypt ? 1 : 0) == 1; +} + +bool CipherCtxPointer::update(const Buffer& in, + unsigned char* out, + int* out_len, + bool finalize) { + if (!ctx_) return false; + if (!finalize) { + return EVP_CipherUpdate(ctx_.get(), out, out_len, in.data, in.len) == 1; + } + return EVP_CipherFinal_ex(ctx_.get(), out, out_len) == 1; +} + +bool CipherCtxPointer::getAeadTag(size_t len, unsigned char* out) { + if (!ctx_) return false; + return EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_GET_TAG, len, out); +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index fffa75ec718fac..c718ae404dd223 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -195,7 +196,7 @@ template using DeleteFnPtr = typename FunctionDeleter::Pointer; using BignumCtxPointer = DeleteFnPtr; -using CipherCtxPointer = DeleteFnPtr; +using BignumGenCallbackPointer = DeleteFnPtr; using DSAPointer = DeleteFnPtr; using DSASigPointer = DeleteFnPtr; using ECDSASigPointer = DeleteFnPtr; @@ -209,10 +210,10 @@ using HMACCtxPointer = DeleteFnPtr; using NetscapeSPKIPointer = DeleteFnPtr; using PKCS8Pointer = DeleteFnPtr; using RSAPointer = DeleteFnPtr; -using SSLCtxPointer = DeleteFnPtr; -using SSLPointer = DeleteFnPtr; using SSLSessionPointer = DeleteFnPtr; +class CipherCtxPointer; + struct StackOfXASN1Deleter { void operator()(STACK_OF(ASN1_OBJECT) * p) const { sk_ASN1_OBJECT_pop_free(p, ASN1_OBJECT_free); @@ -227,6 +228,40 @@ struct Buffer { size_t len = 0; }; +class Cipher final { + public: + Cipher() = default; + Cipher(const EVP_CIPHER* cipher) : cipher_(cipher) {} + Cipher(const Cipher&) = default; + Cipher& operator=(const Cipher&) = default; + inline Cipher& operator=(const EVP_CIPHER* cipher) { + cipher_ = cipher; + return *this; + } + NCRYPTO_DISALLOW_MOVE(Cipher) + + inline const EVP_CIPHER* get() const { return cipher_; } + inline operator const EVP_CIPHER*() const { return cipher_; } + inline operator bool() const { return cipher_ != nullptr; } + + int getNid() const; + int getMode() const; + int getIvLength() const; + int getKeyLength() const; + int getBlockSize() const; + std::string_view getModeLabel() const; + std::string_view getName() const; + + bool isSupportedAuthenticatedMode() const; + + static const Cipher FromName(const char* name); + static const Cipher FromNid(int nid); + static const Cipher FromCtx(const CipherCtxPointer& ctx); + + private: + const EVP_CIPHER* cipher_ = nullptr; +}; + // A managed pointer to a buffer of data. When destroyed the underlying // buffer will be freed. class DataPointer final { @@ -272,6 +307,7 @@ class BIOPointer final { static BIOPointer NewSecMem(); static BIOPointer New(const BIO_METHOD* method); static BIOPointer New(const void* data, size_t len); + static BIOPointer New(const BIGNUM* bn); static BIOPointer NewFile(std::string_view filename, std::string_view mode); static BIOPointer NewFp(FILE* fd, int flags); @@ -350,8 +386,28 @@ class BignumPointer final { size_t encodeInto(unsigned char* out) const; size_t encodePaddedInto(unsigned char* out, size_t size) const; + using PrimeCheckCallback = std::function; + int isPrime(int checks, + PrimeCheckCallback cb = defaultPrimeCheckCallback) const; + struct PrimeConfig { + int bits; + bool safe = false; + const BignumPointer& add; + const BignumPointer& rem; + }; + + static BignumPointer NewPrime( + const PrimeConfig& params, + PrimeCheckCallback cb = defaultPrimeCheckCallback); + + bool generate(const PrimeConfig& params, + PrimeCheckCallback cb = defaultPrimeCheckCallback) const; + static BignumPointer New(); static BignumPointer NewSecure(); + static BignumPointer NewSub(const BignumPointer& a, const BignumPointer& b); + static BignumPointer NewLShift(size_t length); + static DataPointer Encode(const BIGNUM* bn); static DataPointer EncodePadded(const BIGNUM* bn, size_t size); static size_t EncodePaddedInto(const BIGNUM* bn, @@ -366,6 +422,53 @@ class BignumPointer final { private: DeleteFnPtr bn_; + + static bool defaultPrimeCheckCallback(int, int) { return 1; } +}; + +class CipherCtxPointer final { + public: + static CipherCtxPointer New(); + + CipherCtxPointer() = default; + explicit CipherCtxPointer(EVP_CIPHER_CTX* ctx); + CipherCtxPointer(CipherCtxPointer&& other) noexcept; + CipherCtxPointer& operator=(CipherCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(CipherCtxPointer) + ~CipherCtxPointer(); + + inline bool operator==(std::nullptr_t) const noexcept { + return ctx_ == nullptr; + } + inline operator bool() const { return ctx_ != nullptr; } + inline EVP_CIPHER_CTX* get() const { return ctx_.get(); } + inline operator EVP_CIPHER_CTX*() const { return ctx_.get(); } + void reset(EVP_CIPHER_CTX* ctx = nullptr); + EVP_CIPHER_CTX* release(); + + void setFlags(int flags); + bool setKeyLength(size_t length); + bool setIvLength(size_t length); + bool setAeadTag(const Buffer& tag); + bool setAeadTagLength(size_t length); + bool setPadding(bool padding); + bool init(const Cipher& cipher, + bool encrypt, + const unsigned char* key = nullptr, + const unsigned char* iv = nullptr); + + int getBlockSize() const; + int getMode() const; + int getNid() const; + + bool update(const Buffer& in, + unsigned char* out, + int* out_len, + bool finalize = false); + bool getAeadTag(size_t len, unsigned char* out); + + private: + DeleteFnPtr ctx_; }; class EVPKeyPointer final { @@ -551,7 +654,85 @@ class DHPointer final { DeleteFnPtr dh_; }; +struct StackOfX509Deleter { + void operator()(STACK_OF(X509) * p) const { sk_X509_pop_free(p, X509_free); } +}; +using StackOfX509 = std::unique_ptr; + class X509Pointer; +class X509View; + +class SSLCtxPointer final { + public: + SSLCtxPointer() = default; + explicit SSLCtxPointer(SSL_CTX* ctx); + SSLCtxPointer(SSLCtxPointer&& other) noexcept; + SSLCtxPointer& operator=(SSLCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(SSLCtxPointer) + ~SSLCtxPointer(); + + inline bool operator==(std::nullptr_t) const noexcept { + return ctx_ == nullptr; + } + inline operator bool() const { return ctx_ != nullptr; } + inline SSL_CTX* get() const { return ctx_.get(); } + void reset(SSL_CTX* ctx = nullptr); + void reset(const SSL_METHOD* method); + SSL_CTX* release(); + + bool setGroups(const char* groups); + void setStatusCallback(auto callback) { + if (!ctx_) return; + SSL_CTX_set_tlsext_status_cb(get(), callback); + SSL_CTX_set_tlsext_status_arg(get(), nullptr); + } + + static SSLCtxPointer NewServer(); + static SSLCtxPointer NewClient(); + static SSLCtxPointer New(const SSL_METHOD* method = TLS_method()); + + private: + DeleteFnPtr ctx_; +}; + +class SSLPointer final { + public: + SSLPointer() = default; + explicit SSLPointer(SSL* ssl); + SSLPointer(SSLPointer&& other) noexcept; + SSLPointer& operator=(SSLPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(SSLPointer) + ~SSLPointer(); + + inline bool operator==(std::nullptr_t) noexcept { return ssl_ == nullptr; } + inline operator bool() const { return ssl_ != nullptr; } + inline SSL* get() const { return ssl_.get(); } + inline operator SSL*() const { return ssl_.get(); } + void reset(SSL* ssl = nullptr); + SSL* release(); + + bool setSession(const SSLSessionPointer& session); + bool setSniContext(const SSLCtxPointer& ctx) const; + + const std::string_view getClientHelloAlpn() const; + const std::string_view getClientHelloServerName() const; + + std::optional getServerName() const; + X509View getCertificate() const; + EVPKeyPointer getPeerTempKey() const; + const SSL_CIPHER* getCipher() const; + bool isServer() const; + + std::optional verifyPeerCertificate() const; + + void getCiphers(std::function cb) const; + + static SSLPointer New(const SSLCtxPointer& ctx); + static std::optional GetServerName(const SSL* ssl); + + private: + DeleteFnPtr ssl_; +}; class X509View final { public: @@ -565,6 +746,8 @@ class X509View final { NCRYPTO_DISALLOW_MOVE(X509View) inline X509* get() const { return const_cast(cert_); } + inline operator X509*() const { return const_cast(cert_); } + inline operator const X509*() const { return cert_; } inline bool operator==(std::nullptr_t) noexcept { return cert_ == nullptr; } inline operator bool() const { return cert_ != nullptr; } @@ -589,6 +772,8 @@ class X509View final { bool checkPrivateKey(const EVPKeyPointer& pkey) const; bool checkPublicKey(const EVPKeyPointer& pkey) const; + std::optional getFingerprint(const EVP_MD* method) const; + X509Pointer clone() const; enum class CheckMatch { @@ -624,12 +809,17 @@ class X509Pointer final { inline bool operator==(std::nullptr_t) noexcept { return cert_ == nullptr; } inline operator bool() const { return cert_ != nullptr; } inline X509* get() const { return cert_.get(); } + inline operator X509*() const { return cert_.get(); } + inline operator const X509*() const { return cert_.get(); } void reset(X509* cert = nullptr); X509* release(); X509View view() const; operator X509View() const { return view(); } + static std::string_view ErrorCode(int32_t err); + static std::optional ErrorReason(int32_t err); + private: DeleteFnPtr cert_; }; diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc index d430648aebc540..698f3574e47c3b 100644 --- a/src/crypto/crypto_aes.cc +++ b/src/crypto/crypto_aes.cc @@ -40,43 +40,30 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, ByteSource* out) { CHECK_EQ(key_data.GetKeyType(), kKeyTypeSecret); - const int mode = EVP_CIPHER_mode(params.cipher); + const int mode = params.cipher.getMode(); - CipherCtxPointer ctx(EVP_CIPHER_CTX_new()); - EVP_CIPHER_CTX_init(ctx.get()); - if (mode == EVP_CIPH_WRAP_MODE) - EVP_CIPHER_CTX_set_flags(ctx.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + auto ctx = CipherCtxPointer::New(); + if (mode == EVP_CIPH_WRAP_MODE) { + ctx.setFlags(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + } const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt; - if (!EVP_CipherInit_ex( - ctx.get(), - params.cipher, - nullptr, - nullptr, - nullptr, - encrypt)) { + if (!ctx.init(params.cipher, encrypt)) { // Cipher init failed return WebCryptoCipherStatus::FAILED; } - if (mode == EVP_CIPH_GCM_MODE && !EVP_CIPHER_CTX_ctrl( - ctx.get(), - EVP_CTRL_AEAD_SET_IVLEN, - params.iv.size(), - nullptr)) { + if (mode == EVP_CIPH_GCM_MODE && !ctx.setIvLength(params.iv.size())) { return WebCryptoCipherStatus::FAILED; } - if (!EVP_CIPHER_CTX_set_key_length(ctx.get(), - key_data.GetSymmetricKeySize()) || - !EVP_CipherInit_ex( - ctx.get(), - nullptr, - nullptr, + if (!ctx.setKeyLength(key_data.GetSymmetricKeySize()) || + !ctx.init( + ncrypto::Cipher(), + encrypt, reinterpret_cast(key_data.GetSymmetricKey()), - params.iv.data(), - encrypt)) { + params.iv.data())) { return WebCryptoCipherStatus::FAILED; } @@ -84,17 +71,19 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, if (mode == EVP_CIPH_GCM_MODE) { switch (cipher_mode) { - case kWebCryptoCipherDecrypt: + case kWebCryptoCipherDecrypt: { // If in decrypt mode, the auth tag must be set in the params.tag. CHECK(params.tag); - if (!EVP_CIPHER_CTX_ctrl(ctx.get(), - EVP_CTRL_AEAD_SET_TAG, - params.tag.size(), - const_cast(params.tag.data()))) { + ncrypto::Buffer buffer = { + .data = params.tag.data(), + .len = params.tag.size(), + }; + if (!ctx.setAeadTag(buffer)) { return WebCryptoCipherStatus::FAILED; } break; - case kWebCryptoCipherEncrypt: + } + case kWebCryptoCipherEncrypt: { // In decrypt mode, we grab the tag length here. We'll use it to // ensure that that allocated buffer has enough room for both the // final block and the auth tag. Unlike our other AES-GCM implementation @@ -102,23 +91,22 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, // of the generated ciphertext and returned in the same ArrayBuffer. tag_len = params.length; break; + } default: UNREACHABLE(); } } size_t total = 0; - int buf_len = in.size() + EVP_CIPHER_CTX_block_size(ctx.get()) + tag_len; + int buf_len = in.size() + ctx.getBlockSize() + tag_len; int out_len; - if (mode == EVP_CIPH_GCM_MODE && - params.additional_data.size() && - !EVP_CipherUpdate( - ctx.get(), - nullptr, - &out_len, - params.additional_data.data(), - params.additional_data.size())) { + ncrypto::Buffer buffer = { + .data = params.additional_data.data(), + .len = params.additional_data.size(), + }; + if (mode == EVP_CIPH_GCM_MODE && params.additional_data.size() && + !ctx.update(buffer, nullptr, &out_len)) { return WebCryptoCipherStatus::FAILED; } @@ -132,21 +120,20 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, // // Refs: https://github.com/openssl/openssl/commit/420cb707b880e4fb649094241371701013eeb15f // Refs: https://github.com/nodejs/node/pull/38913#issuecomment-866505244 + buffer = { + .data = in.data(), + .len = in.size(), + }; if (in.empty()) { out_len = 0; - } else if (!EVP_CipherUpdate(ctx.get(), - buf.data(), - &out_len, - in.data(), - in.size())) { + } else if (!ctx.update(buffer, buf.data(), &out_len)) { return WebCryptoCipherStatus::FAILED; } total += out_len; CHECK_LE(out_len, buf_len); - out_len = EVP_CIPHER_CTX_block_size(ctx.get()); - if (!EVP_CipherFinal_ex( - ctx.get(), buf.data() + total, &out_len)) { + out_len = ctx.getBlockSize(); + if (!ctx.update({}, buf.data() + total, &out_len, true)) { return WebCryptoCipherStatus::FAILED; } total += out_len; @@ -154,11 +141,9 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, // If using AES_GCM, grab the generated auth tag and append // it to the end of the ciphertext. if (cipher_mode == kWebCryptoCipherEncrypt && mode == EVP_CIPH_GCM_MODE) { - if (!EVP_CIPHER_CTX_ctrl(ctx.get(), - EVP_CTRL_AEAD_GET_TAG, - tag_len, - buf.data() + total)) + if (!ctx.getAeadTag(tag_len, buf.data() + total)) { return WebCryptoCipherStatus::FAILED; + } total += tag_len; } @@ -221,33 +206,34 @@ WebCryptoCipherStatus AES_CTR_Cipher2(const KeyObjectData& key_data, const ByteSource& in, unsigned const char* counter, unsigned char* out) { - CipherCtxPointer ctx(EVP_CIPHER_CTX_new()); + auto ctx = CipherCtxPointer::New(); + if (!ctx) { + return WebCryptoCipherStatus::FAILED; + } const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt; - if (!EVP_CipherInit_ex( - ctx.get(), + if (!ctx.init( params.cipher, - nullptr, + encrypt, reinterpret_cast(key_data.GetSymmetricKey()), - counter, - encrypt)) { + counter)) { // Cipher init failed return WebCryptoCipherStatus::FAILED; } int out_len = 0; int final_len = 0; - if (!EVP_CipherUpdate( - ctx.get(), - out, - &out_len, - in.data(), - in.size())) { + ncrypto::Buffer buffer = { + .data = in.data(), + .len = in.size(), + }; + if (!ctx.update(buffer, out, &out_len)) { return WebCryptoCipherStatus::FAILED; } - if (!EVP_CipherFinal_ex(ctx.get(), out + out_len, &final_len)) + if (!ctx.update({}, out + out_len, &final_len, true)) { return WebCryptoCipherStatus::FAILED; + } out_len += final_len; if (static_cast(out_len) != in.size()) @@ -262,9 +248,8 @@ WebCryptoCipherStatus AES_CTR_Cipher(Environment* env, const AESCipherConfig& params, const ByteSource& in, ByteSource* out) { - auto num_counters = BignumPointer::New(); - if (!BN_lshift(num_counters.get(), BignumPointer::One(), params.length)) - return WebCryptoCipherStatus::FAILED; + auto num_counters = BignumPointer::NewLShift(params.length); + if (!num_counters) return WebCryptoCipherStatus::FAILED; BignumPointer current_counter = GetCounter(params); @@ -277,10 +262,9 @@ WebCryptoCipherStatus AES_CTR_Cipher(Environment* env, // be incremented more than there are counter values, we fail. if (num_output > num_counters) return WebCryptoCipherStatus::FAILED; - auto remaining_until_reset = BignumPointer::New(); - if (!BN_sub(remaining_until_reset.get(), - num_counters.get(), - current_counter.get())) { + auto remaining_until_reset = + BignumPointer::NewSub(num_counters, current_counter); + if (!remaining_until_reset) { return WebCryptoCipherStatus::FAILED; } @@ -480,13 +464,13 @@ Maybe AESCipherTraits::AdditionalConfig( } #undef V - params->cipher = EVP_get_cipherbynid(cipher_nid); - if (params->cipher == nullptr) { + params->cipher = ncrypto::Cipher::FromNid(cipher_nid); + if (!params->cipher) { THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env); return Nothing(); } - int cipher_op_mode = EVP_CIPHER_mode(params->cipher); + int cipher_op_mode = params->cipher.getMode(); if (cipher_op_mode != EVP_CIPH_WRAP_MODE) { if (!ValidateIV(env, mode, args[offset + 1], params)) { return Nothing(); @@ -505,8 +489,7 @@ Maybe AESCipherTraits::AdditionalConfig( UseDefaultIV(params); } - if (params->iv.size() < - static_cast(EVP_CIPHER_iv_length(params->cipher))) { + if (params->iv.size() < static_cast(params->cipher.getIvLength())) { THROW_ERR_CRYPTO_INVALID_IV(env); return Nothing(); } diff --git a/src/crypto/crypto_aes.h b/src/crypto/crypto_aes.h index 3f554ac8c15c60..7bcfa36afdace4 100644 --- a/src/crypto/crypto_aes.h +++ b/src/crypto/crypto_aes.h @@ -38,7 +38,7 @@ enum AESKeyVariant { struct AESCipherConfig final : public MemoryRetainer { CryptoJobMode mode; AESKeyVariant variant; - const EVP_CIPHER* cipher; + ncrypto::Cipher cipher; size_t length; ByteSource iv; // Used for both iv or counter ByteSource additional_data; diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 21e34333609880..8d8914058fc06c 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -27,26 +27,6 @@ using v8::Value; namespace crypto { namespace { -bool IsSupportedAuthenticatedMode(const EVP_CIPHER* cipher) { - switch (EVP_CIPHER_mode(cipher)) { - case EVP_CIPH_CCM_MODE: - case EVP_CIPH_GCM_MODE: -#ifndef OPENSSL_NO_OCB - case EVP_CIPH_OCB_MODE: -#endif - return true; - case EVP_CIPH_STREAM_CIPHER: - return EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305; - default: - return false; - } -} - -bool IsSupportedAuthenticatedMode(const EVP_CIPHER_CTX* ctx) { - const EVP_CIPHER* cipher = EVP_CIPHER_CTX_cipher(ctx); - return IsSupportedAuthenticatedMode(cipher); -} - bool IsValidGCMTagLength(unsigned int tag_len) { return tag_len == 4 || tag_len == 8 || (tag_len >= 12 && tag_len <= 16); } @@ -59,36 +39,23 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { CHECK(args[1]->IsString() || args[1]->IsInt32()); - const EVP_CIPHER* cipher; - if (args[1]->IsString()) { - Utf8Value name(env->isolate(), args[1]); - cipher = EVP_get_cipherbyname(*name); - } else { - int nid = args[1].As()->Value(); - cipher = EVP_get_cipherbynid(nid); - } + const auto cipher = ([&] { + if (args[1]->IsString()) { + Utf8Value name(env->isolate(), args[1]); + return ncrypto::Cipher::FromName(*name); + } else { + int nid = args[1].As()->Value(); + return ncrypto::Cipher::FromNid(nid); + } + })(); - if (cipher == nullptr) - return; + if (!cipher) return; - int mode = EVP_CIPHER_mode(cipher); - int iv_length = EVP_CIPHER_iv_length(cipher); - int key_length = EVP_CIPHER_key_length(cipher); - int block_length = EVP_CIPHER_block_size(cipher); - const char* mode_label = nullptr; - switch (mode) { - case EVP_CIPH_CBC_MODE: mode_label = "cbc"; break; - case EVP_CIPH_CCM_MODE: mode_label = "ccm"; break; - case EVP_CIPH_CFB_MODE: mode_label = "cfb"; break; - case EVP_CIPH_CTR_MODE: mode_label = "ctr"; break; - case EVP_CIPH_ECB_MODE: mode_label = "ecb"; break; - case EVP_CIPH_GCM_MODE: mode_label = "gcm"; break; - case EVP_CIPH_OCB_MODE: mode_label = "ocb"; break; - case EVP_CIPH_OFB_MODE: mode_label = "ofb"; break; - case EVP_CIPH_WRAP_MODE: mode_label = "wrap"; break; - case EVP_CIPH_XTS_MODE: mode_label = "xts"; break; - case EVP_CIPH_STREAM_CIPHER: mode_label = "stream"; break; - } + int iv_length = cipher.getIvLength(); + int key_length = cipher.getKeyLength(); + int block_length = cipher.getBlockSize(); + auto mode_label = cipher.getModeLabel(); + auto name = cipher.getName(); // If the testKeyLen and testIvLen arguments are specified, // then we will make an attempt to see if they are usable for @@ -99,14 +66,16 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { // Test and input IV or key length to determine if it's acceptable. // If it is, then the getCipherInfo will succeed with the given // values. - CipherCtxPointer ctx(EVP_CIPHER_CTX_new()); - if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr, nullptr, nullptr, 1)) + auto ctx = CipherCtxPointer::New(); + if (!ctx.init(cipher, true)) { return; + } if (args[2]->IsInt32()) { int check_len = args[2].As()->Value(); - if (!EVP_CIPHER_CTX_set_key_length(ctx.get(), check_len)) + if (!ctx.setKeyLength(check_len)) { return; + } key_length = check_len; } @@ -116,7 +85,7 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { // For GCM and OCB modes, we'll check by attempting to // set the value. For everything else, just check that // check_len == iv_length. - switch (mode) { + switch (cipher.getMode()) { case EVP_CIPH_CCM_MODE: if (check_len < 7 || check_len > 13) return; @@ -124,11 +93,7 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { case EVP_CIPH_GCM_MODE: // Fall through case EVP_CIPH_OCB_MODE: - if (!EVP_CIPHER_CTX_ctrl( - ctx.get(), - EVP_CTRL_AEAD_SET_IVLEN, - check_len, - nullptr)) { + if (!ctx.setIvLength(check_len)) { return; } break; @@ -140,38 +105,35 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { } } - if (mode_label != nullptr && - info->Set( - env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "mode"), - OneByteString(env->isolate(), mode_label)).IsNothing()) { + if (mode_label.length() && + info->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "mode"), + OneByteString( + env->isolate(), mode_label.data(), mode_label.length())) + .IsNothing()) { return; } - // OBJ_nid2sn(EVP_CIPHER_nid(cipher)) is used here instead of - // EVP_CIPHER_name(cipher) for compatibility with BoringSSL. - if (info->Set( - env->context(), - env->name_string(), - OneByteString( - env->isolate(), - OBJ_nid2sn(EVP_CIPHER_nid(cipher)))).IsNothing()) { + if (info->Set(env->context(), + env->name_string(), + OneByteString(env->isolate(), name.data(), name.length())) + .IsNothing()) { return; } - if (info->Set( - env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "nid"), - Int32::New(env->isolate(), EVP_CIPHER_nid(cipher))).IsNothing()) { + if (info->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "nid"), + Int32::New(env->isolate(), cipher.getNid())) + .IsNothing()) { return; } // Stream ciphers do not have a meaningful block size - if (mode != EVP_CIPH_STREAM_CIPHER && - info->Set( - env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "blockSize"), - Int32::New(env->isolate(), block_length)).IsNothing()) { + if (cipher.getMode() != EVP_CIPH_STREAM_CIPHER && + info->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "blockSize"), + Int32::New(env->isolate(), block_length)) + .IsNothing()) { return; } @@ -198,42 +160,20 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { void CipherBase::GetSSLCiphers(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - SSLCtxPointer ctx(SSL_CTX_new(TLS_method())); + auto ctx = SSLCtxPointer::New(); if (!ctx) { return ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_new"); } - SSLPointer ssl(SSL_new(ctx.get())); + auto ssl = SSLPointer::New(ctx); if (!ssl) { return ThrowCryptoError(env, ERR_get_error(), "SSL_new"); } - STACK_OF(SSL_CIPHER)* ciphers = SSL_get_ciphers(ssl.get()); - - // TLSv1.3 ciphers aren't listed by EVP. There are only 5, we could just - // document them, but since there are only 5, easier to just add them manually - // and not have to explain their absence in the API docs. They are lower-cased - // because the docs say they will be. - static const char* TLS13_CIPHERS[] = { - "tls_aes_256_gcm_sha384", - "tls_chacha20_poly1305_sha256", - "tls_aes_128_gcm_sha256", - "tls_aes_128_ccm_8_sha256", - "tls_aes_128_ccm_sha256" - }; - - const int n = sk_SSL_CIPHER_num(ciphers); - LocalVector arr(env->isolate(), n + arraysize(TLS13_CIPHERS)); - - for (int i = 0; i < n; ++i) { - const SSL_CIPHER* cipher = sk_SSL_CIPHER_value(ciphers, i); - arr[i] = OneByteString(env->isolate(), SSL_CIPHER_get_name(cipher)); - } - - for (unsigned i = 0; i < arraysize(TLS13_CIPHERS); ++i) { - const char* name = TLS13_CIPHERS[i]; - arr[n + i] = OneByteString(env->isolate(), name); - } + LocalVector arr(env->isolate()); + ssl.getCiphers([&](const std::string_view name) { + arr.push_back(OneByteString(env->isolate(), name.data(), name.length())); + }); args.GetReturnValue().Set(Array::New(env->isolate(), arr.data(), arr.size())); } @@ -363,38 +303,38 @@ void CipherBase::New(const FunctionCallbackInfo& args) { } void CipherBase::CommonInit(const char* cipher_type, - const EVP_CIPHER* cipher, + const ncrypto::Cipher& cipher, const unsigned char* key, int key_len, const unsigned char* iv, int iv_len, unsigned int auth_tag_len) { CHECK(!ctx_); - ctx_.reset(EVP_CIPHER_CTX_new()); + ctx_ = CipherCtxPointer::New(); + CHECK(ctx_); - const int mode = EVP_CIPHER_mode(cipher); - if (mode == EVP_CIPH_WRAP_MODE) - EVP_CIPHER_CTX_set_flags(ctx_.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + if (cipher.getMode() == EVP_CIPH_WRAP_MODE) { + ctx_.setFlags(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + } const bool encrypt = (kind_ == kCipher); - if (1 != EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, - nullptr, nullptr, encrypt)) { + if (!ctx_.init(cipher, encrypt)) { return ThrowCryptoError(env(), ERR_get_error(), "Failed to initialize cipher"); } - if (IsSupportedAuthenticatedMode(cipher)) { + if (cipher.isSupportedAuthenticatedMode()) { CHECK_GE(iv_len, 0); if (!InitAuthenticated(cipher_type, iv_len, auth_tag_len)) return; } - if (!EVP_CIPHER_CTX_set_key_length(ctx_.get(), key_len)) { + if (!ctx_.setKeyLength(key_len)) { ctx_.reset(); return THROW_ERR_CRYPTO_INVALID_KEYLEN(env()); } - if (1 != EVP_CipherInit_ex(ctx_.get(), nullptr, nullptr, key, iv, encrypt)) { + if (!ctx_.init(ncrypto::Cipher(), encrypt, key, iv)) { return ThrowCryptoError(env(), ERR_get_error(), "Failed to initialize cipher"); } @@ -405,9 +345,10 @@ void CipherBase::Init(const char* cipher_type, unsigned int auth_tag_len) { HandleScope scope(env()->isolate()); MarkPopErrorOnReturn mark_pop_error_on_return; - const EVP_CIPHER* const cipher = EVP_get_cipherbyname(cipher_type); - if (cipher == nullptr) + auto cipher = ncrypto::Cipher::FromName(cipher_type); + if (!cipher) { return THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env()); + } unsigned char key[EVP_MAX_KEY_LENGTH]; unsigned char iv[EVP_MAX_IV_LENGTH]; @@ -422,7 +363,7 @@ void CipherBase::Init(const char* cipher_type, iv); CHECK_NE(key_len, 0); - const int mode = EVP_CIPHER_mode(cipher); + const int mode = cipher.getMode(); if (kind_ == kCipher && (mode == EVP_CIPH_CTR_MODE || mode == EVP_CIPH_GCM_MODE || mode == EVP_CIPH_CCM_MODE)) { @@ -433,8 +374,13 @@ void CipherBase::Init(const char* cipher_type, cipher_type); } - CommonInit(cipher_type, cipher, key, key_len, iv, - EVP_CIPHER_iv_length(cipher), auth_tag_len); + CommonInit(cipher_type, + cipher, + key, + key_len, + iv, + cipher.getIvLength(), + auth_tag_len); } void CipherBase::Init(const FunctionCallbackInfo& args) { @@ -469,12 +415,10 @@ void CipherBase::InitIv(const char* cipher_type, HandleScope scope(env()->isolate()); MarkPopErrorOnReturn mark_pop_error_on_return; - const EVP_CIPHER* const cipher = EVP_get_cipherbyname(cipher_type); - if (cipher == nullptr) - return THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env()); + auto cipher = ncrypto::Cipher::FromName(cipher_type); + if (!cipher) return THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env()); - const int expected_iv_len = EVP_CIPHER_iv_length(cipher); - const bool is_authenticated_mode = IsSupportedAuthenticatedMode(cipher); + const int expected_iv_len = cipher.getIvLength(); const bool has_iv = iv_buf.size() > 0; // Throw if no IV was passed and the cipher requires an IV @@ -484,13 +428,12 @@ void CipherBase::InitIv(const char* cipher_type, // Throw if an IV was passed which does not match the cipher's fixed IV length // static_cast for the iv_buf.size() is safe because we've verified // prior that the value is not larger than INT_MAX. - if (!is_authenticated_mode && - has_iv && + if (!cipher.isSupportedAuthenticatedMode() && has_iv && static_cast(iv_buf.size()) != expected_iv_len) { return THROW_ERR_CRYPTO_INVALID_IV(env()); } - if (EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305) { + if (cipher.getNid() == NID_chacha20_poly1305) { CHECK(has_iv); // Check for invalid IV lengths, since OpenSSL does not under some // conditions: @@ -553,15 +496,12 @@ bool CipherBase::InitAuthenticated( CHECK(IsAuthenticatedMode()); MarkPopErrorOnReturn mark_pop_error_on_return; - if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), - EVP_CTRL_AEAD_SET_IVLEN, - iv_len, - nullptr)) { + if (!ctx_.setIvLength(iv_len)) { THROW_ERR_CRYPTO_INVALID_IV(env()); return false; } - const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); + const int mode = ctx_.getMode(); if (mode == EVP_CIPH_GCM_MODE) { if (auth_tag_len != kNoAuthTagLength) { if (!IsValidGCMTagLength(auth_tag_len)) { @@ -581,7 +521,7 @@ bool CipherBase::InitAuthenticated( // length defaults to 16 bytes when encrypting. Unlike GCM, the // authentication tag length also defaults to 16 bytes when decrypting, // whereas GCM would accept any valid authentication tag length. - if (EVP_CIPHER_CTX_nid(ctx_.get()) == NID_chacha20_poly1305) { + if (ctx_.getNid() == NID_chacha20_poly1305) { auth_tag_len = 16; } else { THROW_ERR_CRYPTO_INVALID_AUTH_TAG( @@ -604,8 +544,7 @@ bool CipherBase::InitAuthenticated( } // Tell OpenSSL about the desired length. - if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_SET_TAG, auth_tag_len, - nullptr)) { + if (!ctx_.setAeadTagLength(auth_tag_len)) { THROW_ERR_CRYPTO_INVALID_AUTH_TAG( env(), "Invalid authentication tag length: %u", auth_tag_len); return false; @@ -628,7 +567,7 @@ bool CipherBase::InitAuthenticated( bool CipherBase::CheckCCMMessageLength(int message_len) { CHECK(ctx_); - CHECK(EVP_CIPHER_CTX_mode(ctx_.get()) == EVP_CIPH_CCM_MODE); + CHECK(ctx_.getMode() == EVP_CIPH_CCM_MODE); if (message_len > max_message_size_) { THROW_ERR_CRYPTO_INVALID_MESSAGELEN(env()); @@ -641,7 +580,7 @@ bool CipherBase::CheckCCMMessageLength(int message_len) { bool CipherBase::IsAuthenticatedMode() const { // Check if this cipher operates in an AEAD mode that we support. CHECK(ctx_); - return IsSupportedAuthenticatedMode(ctx_.get()); + return ncrypto::Cipher::FromCtx(ctx_).isSupportedAuthenticatedMode(); } void CipherBase::GetAuthTag(const FunctionCallbackInfo& args) { @@ -679,7 +618,7 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo& args) { } unsigned int tag_len = auth_tag.size(); - const int mode = EVP_CIPHER_CTX_mode(cipher->ctx_.get()); + const int mode = cipher->ctx_.getMode(); bool is_valid; if (mode == EVP_CIPH_GCM_MODE) { // Restrict GCM tag lengths according to NIST 800-38d, page 9. @@ -689,7 +628,8 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo& args) { } else { // At this point, the tag length is already known and must match the // length of the given authentication tag. - CHECK(IsSupportedAuthenticatedMode(cipher->ctx_.get())); + CHECK( + ncrypto::Cipher::FromCtx(cipher->ctx_).isSupportedAuthenticatedMode()); CHECK_NE(cipher->auth_tag_len_, kNoAuthTagLength); is_valid = cipher->auth_tag_len_ == tag_len; } @@ -723,10 +663,11 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo& args) { bool CipherBase::MaybePassAuthTagToOpenSSL() { if (auth_tag_state_ == kAuthTagKnown) { - if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), - EVP_CTRL_AEAD_SET_TAG, - auth_tag_len_, - reinterpret_cast(auth_tag_))) { + ncrypto::Buffer buffer{ + .data = auth_tag_, + .len = auth_tag_len_, + }; + if (!ctx_.setAeadTag(buffer)) { return false; } auth_tag_state_ = kAuthTagPassedToOpenSSL; @@ -742,7 +683,7 @@ bool CipherBase::SetAAD( MarkPopErrorOnReturn mark_pop_error_on_return; int outlen; - const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); + const int mode = ctx_.getMode(); // When in CCM mode, we need to set the authentication tag and the plaintext // length in advance. @@ -761,16 +702,21 @@ bool CipherBase::SetAAD( return false; } + ncrypto::Buffer buffer{ + .data = nullptr, + .len = static_cast(plaintext_len), + }; // Specify the plaintext length. - if (!EVP_CipherUpdate(ctx_.get(), nullptr, &outlen, nullptr, plaintext_len)) + if (!ctx_.update(buffer, nullptr, &outlen)) { return false; + } } - return 1 == EVP_CipherUpdate(ctx_.get(), - nullptr, - &outlen, - data.data(), - data.size()); + ncrypto::Buffer buffer{ + .data = data.data(), + .len = data.size(), + }; + return ctx_.update(buffer, nullptr, &outlen); } void CipherBase::SetAAD(const FunctionCallbackInfo& args) { @@ -797,7 +743,7 @@ CipherBase::UpdateResult CipherBase::Update( return kErrorState; MarkPopErrorOnReturn mark_pop_error_on_return; - const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); + const int mode = ctx_.getMode(); if (mode == EVP_CIPH_CCM_MODE && !CheckCCMMessageLength(len)) return kErrorMessageSize; @@ -807,19 +753,17 @@ CipherBase::UpdateResult CipherBase::Update( if (kind_ == kDecipher && IsAuthenticatedMode()) CHECK(MaybePassAuthTagToOpenSSL()); - const int block_size = EVP_CIPHER_CTX_block_size(ctx_.get()); + const int block_size = ctx_.getBlockSize(); CHECK_GT(block_size, 0); if (len + block_size > INT_MAX) return kErrorState; int buf_len = len + block_size; - // For key wrapping algorithms, get output size by calling - // EVP_CipherUpdate() with null output. + ncrypto::Buffer buffer = { + .data = reinterpret_cast(data), + .len = len, + }; if (kind_ == kCipher && mode == EVP_CIPH_WRAP_MODE && - EVP_CipherUpdate(ctx_.get(), - nullptr, - &buf_len, - reinterpret_cast(data), - len) != 1) { + !ctx_.update(buffer, nullptr, &buf_len)) { return kErrorState; } @@ -828,11 +772,13 @@ CipherBase::UpdateResult CipherBase::Update( *out = ArrayBuffer::NewBackingStore(env()->isolate(), buf_len); } - int r = EVP_CipherUpdate(ctx_.get(), - static_cast((*out)->Data()), - &buf_len, - reinterpret_cast(data), - len); + buffer = { + .data = reinterpret_cast(data), + .len = len, + }; + + bool r = ctx_.update( + buffer, static_cast((*out)->Data()), &buf_len); CHECK_LE(static_cast(buf_len), (*out)->ByteLength()); if (buf_len == 0) { @@ -884,7 +830,7 @@ bool CipherBase::SetAutoPadding(bool auto_padding) { if (!ctx_) return false; MarkPopErrorOnReturn mark_pop_error_on_return; - return EVP_CIPHER_CTX_set_padding(ctx_.get(), auto_padding); + return ctx_.setPadding(auto_padding); } void CipherBase::SetAutoPadding(const FunctionCallbackInfo& args) { @@ -899,21 +845,23 @@ bool CipherBase::Final(std::unique_ptr* out) { if (!ctx_) return false; - const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); + const int mode = ctx_.getMode(); { NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - *out = ArrayBuffer::NewBackingStore(env()->isolate(), - static_cast(EVP_CIPHER_CTX_block_size(ctx_.get()))); + *out = ArrayBuffer::NewBackingStore( + env()->isolate(), static_cast(ctx_.getBlockSize())); } - if (kind_ == kDecipher && IsSupportedAuthenticatedMode(ctx_.get())) + if (kind_ == kDecipher && + ncrypto::Cipher::FromCtx(ctx_).isSupportedAuthenticatedMode()) { MaybePassAuthTagToOpenSSL(); + } // OpenSSL v1.x doesn't verify the presence of the auth tag so do // it ourselves, see https://github.com/nodejs/node/issues/45874. if (OPENSSL_VERSION_NUMBER < 0x30000000L && kind_ == kDecipher && - NID_chacha20_poly1305 == EVP_CIPHER_CTX_nid(ctx_.get()) && + NID_chacha20_poly1305 == ctx_.getNid() && auth_tag_state_ != kAuthTagPassedToOpenSSL) { return false; } @@ -926,9 +874,8 @@ bool CipherBase::Final(std::unique_ptr* out) { *out = ArrayBuffer::NewBackingStore(env()->isolate(), 0); } else { int out_len = (*out)->ByteLength(); - ok = EVP_CipherFinal_ex(ctx_.get(), - static_cast((*out)->Data()), - &out_len) == 1; + ok = ctx_.update( + {}, static_cast((*out)->Data()), &out_len, true); CHECK_LE(static_cast(out_len), (*out)->ByteLength()); if (out_len == 0) { @@ -949,9 +896,8 @@ bool CipherBase::Final(std::unique_ptr* out) { CHECK(mode == EVP_CIPH_GCM_MODE); auth_tag_len_ = sizeof(auth_tag_); } - ok = (1 == EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_GET_TAG, - auth_tag_len_, - reinterpret_cast(auth_tag_))); + ok = ctx_.getAeadTag(auth_tag_len_, + reinterpret_cast(auth_tag_)); } } diff --git a/src/crypto/crypto_cipher.h b/src/crypto/crypto_cipher.h index de436e2e9d2df8..d15a231475d657 100644 --- a/src/crypto/crypto_cipher.h +++ b/src/crypto/crypto_cipher.h @@ -44,7 +44,7 @@ class CipherBase : public BaseObject { static const unsigned kNoAuthTagLength = static_cast(-1); void CommonInit(const char* cipher_type, - const EVP_CIPHER* cipher, + const ncrypto::Cipher& cipher, const unsigned char* key, int key_len, const unsigned char* iv, @@ -85,7 +85,7 @@ class CipherBase : public BaseObject { CipherBase(Environment* env, v8::Local wrap, CipherKind kind); private: - DeleteFnPtr ctx_; + CipherCtxPointer ctx_; const CipherKind kind_; AuthTagState auth_tag_state_; unsigned int auth_tag_len_; diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc index 43a126f863779d..8ea34fe78b2592 100644 --- a/src/crypto/crypto_common.cc +++ b/src/crypto/crypto_common.cc @@ -27,7 +27,7 @@ namespace node { -using v8::Array; +using ncrypto::StackOfX509; using v8::ArrayBuffer; using v8::BackingStore; using v8::Context; @@ -41,54 +41,6 @@ using v8::Undefined; using v8::Value; namespace crypto { -void LogSecret( - const SSLPointer& ssl, - const char* name, - const unsigned char* secret, - size_t secretlen) { - auto keylog_cb = SSL_CTX_get_keylog_callback(SSL_get_SSL_CTX(ssl.get())); - // All supported versions of TLS/SSL fix the client random to the same size. - constexpr size_t kTlsClientRandomSize = SSL3_RANDOM_SIZE; - unsigned char crandom[kTlsClientRandomSize]; - - if (keylog_cb == nullptr || - SSL_get_client_random(ssl.get(), crandom, kTlsClientRandomSize) != - kTlsClientRandomSize) { - return; - } - - std::string line = name; - line += " " + nbytes::HexEncode(reinterpret_cast(crandom), - kTlsClientRandomSize); - line += - " " + nbytes::HexEncode(reinterpret_cast(secret), secretlen); - keylog_cb(ssl.get(), line.c_str()); -} - -MaybeLocal GetSSLOCSPResponse( - Environment* env, - SSL* ssl, - Local default_value) { - const unsigned char* resp; - int len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp); - if (resp == nullptr) - return default_value; - - Local ret; - MaybeLocal maybe_buffer = - Buffer::Copy(env, reinterpret_cast(resp), len); - - if (!maybe_buffer.ToLocal(&ret)) - return MaybeLocal(); - - return ret; -} - -bool SetTLSSession( - const SSLPointer& ssl, - const SSLSessionPointer& session) { - return session != nullptr && SSL_set_session(ssl.get(), session.get()) == 1; -} SSLSessionPointer GetTLSSession(const unsigned char* buf, size_t length) { return SSLSessionPointer(d2i_SSL_SESSION(nullptr, &buf, length)); @@ -97,153 +49,36 @@ SSLSessionPointer GetTLSSession(const unsigned char* buf, size_t length) { long VerifyPeerCertificate( // NOLINT(runtime/int) const SSLPointer& ssl, long def) { // NOLINT(runtime/int) - long err = def; // NOLINT(runtime/int) - if (X509Pointer::PeerFrom(ssl)) { - err = SSL_get_verify_result(ssl.get()); - } else { - const SSL_CIPHER* curr_cipher = SSL_get_current_cipher(ssl.get()); - const SSL_SESSION* sess = SSL_get_session(ssl.get()); - // Allow no-cert for PSK authentication in TLS1.2 and lower. - // In TLS1.3 check that session was reused because TLS1.3 PSK - // looks like session resumption. - if (SSL_CIPHER_get_auth_nid(curr_cipher) == NID_auth_psk || - (SSL_SESSION_get_protocol_version(sess) == TLS1_3_VERSION && - SSL_session_reused(ssl.get()))) { - return X509_V_OK; - } - } - return err; + return ssl.verifyPeerCertificate().value_or(def); } bool UseSNIContext( const SSLPointer& ssl, BaseObjectPtr context) { - auto x509 = ncrypto::X509View::From(context->ctx()); - if (!x509) return false; - SSL_CTX* ctx = context->ctx().get(); - EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx); - STACK_OF(X509)* chain; - - int err = SSL_CTX_get0_chain_certs(ctx, &chain); - if (err == 1) err = SSL_use_certificate(ssl.get(), x509.get()); - if (err == 1) err = SSL_use_PrivateKey(ssl.get(), pkey); - if (err == 1 && chain != nullptr) err = SSL_set1_chain(ssl.get(), chain); - return err == 1; -} - -const char* GetClientHelloALPN(const SSLPointer& ssl) { - const unsigned char* buf; - size_t len; - size_t rem; - - if (!SSL_client_hello_get0_ext( - ssl.get(), - TLSEXT_TYPE_application_layer_protocol_negotiation, - &buf, - &rem) || - rem < 2) { - return nullptr; - } - - len = (buf[0] << 8) | buf[1]; - if (len + 2 != rem) return nullptr; - return reinterpret_cast(buf + 3); -} - -const char* GetClientHelloServerName(const SSLPointer& ssl) { - const unsigned char* buf; - size_t len; - size_t rem; - - if (!SSL_client_hello_get0_ext( - ssl.get(), - TLSEXT_TYPE_server_name, - &buf, - &rem) || rem <= 2) { - return nullptr; - } - - len = (*buf << 8) | *(buf + 1); - if (len + 2 != rem) - return nullptr; - rem = len; - - if (rem == 0 || *(buf + 2) != TLSEXT_NAMETYPE_host_name) return nullptr; - rem--; - if (rem <= 2) - return nullptr; - len = (*(buf + 3) << 8) | *(buf + 4); - if (len + 2 > rem) - return nullptr; - return reinterpret_cast(buf + 5); -} - -const char* GetServerName(SSL* ssl) { - return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + return ssl.setSniContext(context->ctx()); } bool SetGroups(SecureContext* sc, const char* groups) { - return SSL_CTX_set1_groups_list(sc->ctx().get(), groups) == 1; -} - -// When adding or removing errors below, please also update the list in the API -// documentation. See the "OpenSSL Error Codes" section of doc/api/errors.md -const char* X509ErrorCode(long err) { // NOLINT(runtime/int) - const char* code = "UNSPECIFIED"; -#define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break; - switch (err) { - // if you modify anything in here, *please* update the respective section in - // doc/api/tls.md as well - CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT) - CASE_X509_ERR(UNABLE_TO_GET_CRL) - CASE_X509_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE) - CASE_X509_ERR(UNABLE_TO_DECRYPT_CRL_SIGNATURE) - CASE_X509_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY) - CASE_X509_ERR(CERT_SIGNATURE_FAILURE) - CASE_X509_ERR(CRL_SIGNATURE_FAILURE) - CASE_X509_ERR(CERT_NOT_YET_VALID) - CASE_X509_ERR(CERT_HAS_EXPIRED) - CASE_X509_ERR(CRL_NOT_YET_VALID) - CASE_X509_ERR(CRL_HAS_EXPIRED) - CASE_X509_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD) - CASE_X509_ERR(ERROR_IN_CERT_NOT_AFTER_FIELD) - CASE_X509_ERR(ERROR_IN_CRL_LAST_UPDATE_FIELD) - CASE_X509_ERR(ERROR_IN_CRL_NEXT_UPDATE_FIELD) - CASE_X509_ERR(OUT_OF_MEM) - CASE_X509_ERR(DEPTH_ZERO_SELF_SIGNED_CERT) - CASE_X509_ERR(SELF_SIGNED_CERT_IN_CHAIN) - CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY) - CASE_X509_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE) - CASE_X509_ERR(CERT_CHAIN_TOO_LONG) - CASE_X509_ERR(CERT_REVOKED) - CASE_X509_ERR(INVALID_CA) - CASE_X509_ERR(PATH_LENGTH_EXCEEDED) - CASE_X509_ERR(INVALID_PURPOSE) - CASE_X509_ERR(CERT_UNTRUSTED) - CASE_X509_ERR(CERT_REJECTED) - CASE_X509_ERR(HOSTNAME_MISMATCH) - } -#undef CASE_X509_ERR - return code; + return sc->ctx().setGroups(groups); } MaybeLocal GetValidationErrorReason(Environment* env, int err) { - if (err == 0) - return Undefined(env->isolate()); - const char* reason = X509_verify_cert_error_string(err); - return OneByteString(env->isolate(), reason); + auto reason = X509Pointer::ErrorReason(err).value_or(""); + if (reason == "") return Undefined(env->isolate()); + return OneByteString(env->isolate(), reason.data(), reason.length()); } MaybeLocal GetValidationErrorCode(Environment* env, int err) { if (err == 0) return Undefined(env->isolate()); - return OneByteString(env->isolate(), X509ErrorCode(err)); + auto error = X509Pointer::ErrorCode(err); + return OneByteString(env->isolate(), error.data(), error.length()); } MaybeLocal GetCert(Environment* env, const SSLPointer& ssl) { - ClearErrorOnReturn clear_error_on_return; - ncrypto::X509View cert(SSL_get_certificate(ssl.get())); - if (!cert) return Undefined(env->isolate()); - return X509Certificate::toObject(env, cert); + if (auto cert = ssl.getCertificate()) { + return X509Certificate::toObject(env, cert); + } + return Undefined(env->isolate()); } namespace { @@ -366,57 +201,22 @@ MaybeLocal GetLastIssuedCert( MaybeLocal GetCurrentCipherName(Environment* env, const SSLPointer& ssl) { - return GetCipherName(env, SSL_get_current_cipher(ssl.get())); + return GetCipherName(env, ssl.getCipher()); } MaybeLocal GetCurrentCipherVersion(Environment* env, const SSLPointer& ssl) { - return GetCipherVersion(env, SSL_get_current_cipher(ssl.get())); + return GetCipherVersion(env, ssl.getCipher()); } template (*Get)(Environment* env, const SSL_CIPHER* cipher)> MaybeLocal GetCurrentCipherValue(Environment* env, const SSLPointer& ssl) { - return Get(env, SSL_get_current_cipher(ssl.get())); -} - -MaybeLocal GetClientHelloCiphers( - Environment* env, - const SSLPointer& ssl) { - EscapableHandleScope scope(env->isolate()); - const unsigned char* buf; - size_t len = SSL_client_hello_get0_ciphers(ssl.get(), &buf); - size_t count = len / 2; - MaybeStackBuffer, 16> ciphers(count); - int j = 0; - for (size_t n = 0; n < len; n += 2) { - const SSL_CIPHER* cipher = SSL_CIPHER_find(ssl.get(), buf); - buf += 2; - Local obj = Object::New(env->isolate()); - if (!Set(env->context(), - obj, - env->name_string(), - GetCipherName(env, cipher)) || - !Set(env->context(), - obj, - env->standard_name_string(), - GetCipherStandardName(env, cipher)) || - !Set(env->context(), - obj, - env->version_string(), - GetCipherVersion(env, cipher))) { - return MaybeLocal(); - } - ciphers[j++] = obj; - } - Local ret = Array::New(env->isolate(), ciphers.out(), count); - return scope.Escape(ret); + return Get(env, ssl.getCipher()); } - MaybeLocal GetCipherInfo(Environment* env, const SSLPointer& ssl) { - if (SSL_get_current_cipher(ssl.get()) == nullptr) - return MaybeLocal(); + if (ssl.getCipher() == nullptr) return MaybeLocal(); EscapableHandleScope scope(env->isolate()); Local info = Object::New(env->isolate()); @@ -439,15 +239,14 @@ MaybeLocal GetCipherInfo(Environment* env, const SSLPointer& ssl) { } MaybeLocal GetEphemeralKey(Environment* env, const SSLPointer& ssl) { - CHECK_EQ(SSL_is_server(ssl.get()), 0); - EVP_PKEY* raw_key; + CHECK(!ssl.isServer()); EscapableHandleScope scope(env->isolate()); Local info = Object::New(env->isolate()); - if (!SSL_get_peer_tmp_key(ssl.get(), &raw_key)) return scope.Escape(info); + crypto::EVPKeyPointer key = ssl.getPeerTempKey(); + if (!key) return scope.Escape(info); Local context = env->context(); - crypto::EVPKeyPointer key(raw_key); int kid = key.id(); switch (kid) { diff --git a/src/crypto/crypto_common.h b/src/crypto/crypto_common.h index 284aadd6cc2cc3..bce577ade78b58 100644 --- a/src/crypto/crypto_common.h +++ b/src/crypto/crypto_common.h @@ -22,26 +22,6 @@ namespace node { namespace crypto { -struct StackOfX509Deleter { - void operator()(STACK_OF(X509)* p) const { sk_X509_pop_free(p, X509_free); } -}; -using StackOfX509 = std::unique_ptr; - -void LogSecret( - const SSLPointer& ssl, - const char* name, - const unsigned char* secret, - size_t secretlen); - -v8::MaybeLocal GetSSLOCSPResponse( - Environment* env, - SSL* ssl, - v8::Local default_value); - -bool SetTLSSession( - const SSLPointer& ssl, - const SSLSessionPointer& session); - SSLSessionPointer GetTLSSession(const unsigned char* buf, size_t length); long VerifyPeerCertificate( // NOLINT(runtime/int) @@ -50,20 +30,8 @@ long VerifyPeerCertificate( // NOLINT(runtime/int) bool UseSNIContext(const SSLPointer& ssl, BaseObjectPtr context); -const char* GetClientHelloALPN(const SSLPointer& ssl); - -const char* GetClientHelloServerName(const SSLPointer& ssl); - -const char* GetServerName(SSL* ssl); - -v8::MaybeLocal GetClientHelloCiphers( - Environment* env, - const SSLPointer& ssl); - bool SetGroups(SecureContext* sc, const char* groups); -const char* X509ErrorCode(long err); // NOLINT(runtime/int) - v8::MaybeLocal GetValidationErrorReason(Environment* env, int err); v8::MaybeLocal GetValidationErrorCode(Environment* env, int err); diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index aa5fc61f19e435..fa96b8d7a51d46 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -21,6 +21,7 @@ namespace node { +using ncrypto::StackOfX509; using v8::Array; using v8::ArrayBufferView; using v8::Boolean; @@ -550,7 +551,7 @@ void SecureContext::Init(const FunctionCallbackInfo& args) { } } - sc->ctx_.reset(SSL_CTX_new(method)); + sc->ctx_.reset(method); if (!sc->ctx_) { return ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_new"); } @@ -786,9 +787,8 @@ void SecureContext::SetCACert(const BIOPointer& bio) { while (X509Pointer x509 = X509Pointer(PEM_read_bio_X509_AUX( bio.get(), nullptr, NoPasswordCallback, nullptr))) { CHECK_EQ(1, - X509_STORE_add_cert(GetCertStoreOwnedByThisSecureContext(), - x509.get())); - CHECK_EQ(1, SSL_CTX_add_client_CA(ctx_.get(), x509.get())); + X509_STORE_add_cert(GetCertStoreOwnedByThisSecureContext(), x509)); + CHECK_EQ(1, SSL_CTX_add_client_CA(ctx_.get(), x509)); } } diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc index 2c90a796e1195b..a6a206455b52c3 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -7,8 +7,6 @@ #include "threadpoolwork-inl.h" #include "v8.h" -#include -#include #include namespace node { @@ -29,21 +27,12 @@ using v8::Value; namespace crypto { namespace { -using BNGENCBPointer = DeleteFnPtr; - -BNGENCBPointer getBN_GENCB(Environment* env) { +ncrypto::BignumPointer::PrimeCheckCallback getPrimeCheckCallback( + Environment* env) { // The callback is used to check if the operation should be stopped. // Currently, the only check we perform is if env->is_stopping() // is true. - BNGENCBPointer cb(BN_GENCB_new()); - BN_GENCB_set( - cb.get(), - [](int a, int b, BN_GENCB* cb) -> int { - Environment* env = static_cast(BN_GENCB_get_arg(cb)); - return env->is_stopping() ? 0 : 1; - }, - env); - return cb; + return [env](int a, int b) -> bool { return !env->is_stopping(); }; } } // namespace @@ -165,22 +154,14 @@ Maybe RandomPrimeTraits::AdditionalConfig( bool RandomPrimeTraits::DeriveBits(Environment* env, const RandomPrimeConfig& params, ByteSource* unused) { - // BN_generate_prime_ex() calls RAND_bytes_ex() internally. - // Make sure the CSPRNG is properly seeded. - CHECK(ncrypto::CSPRNG(nullptr, 0)); - - BNGENCBPointer cb = getBN_GENCB(env); - - if (BN_generate_prime_ex(params.prime.get(), - params.bits, - params.safe ? 1 : 0, - params.add.get(), - params.rem.get(), - cb.get()) == 0) { - return false; - } - - return true; + return params.prime.generate( + BignumPointer::PrimeConfig{ + .bits = params.bits, + .safe = params.safe, + .add = params.add, + .rem = params.rem, + }, + getPrimeCheckCallback(env)); } void CheckPrimeConfig::MemoryInfo(MemoryTracker* tracker) const { @@ -207,12 +188,7 @@ bool CheckPrimeTraits::DeriveBits( Environment* env, const CheckPrimeConfig& params, ByteSource* out) { - - BignumCtxPointer ctx(BN_CTX_new()); - BNGENCBPointer cb = getBN_GENCB(env); - - int ret = BN_is_prime_ex( - params.candidate.get(), params.checks, ctx.get(), cb.get()); + int ret = params.candidate.isPrime(params.checks, getPrimeCheckCallback(env)); if (ret < 0) return false; ByteSource::Builder buf(1); buf.data()[0] = ret; diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc index 9c7ce849521499..f0bb4786abdc71 100644 --- a/src/crypto/crypto_tls.cc +++ b/src/crypto/crypto_tls.cc @@ -217,10 +217,12 @@ int SSLCertCallback(SSL* s, void* arg) { Local info = Object::New(env->isolate()); - const char* servername = GetServerName(s); - Local servername_str = (servername == nullptr) - ? String::Empty(env->isolate()) - : OneByteString(env->isolate(), servername, strlen(servername)); + auto servername = SSLPointer::GetServerName(s); + Local servername_str = + !servername.has_value() ? String::Empty(env->isolate()) + : OneByteString(env->isolate(), + servername.value().data(), + servername.value().length()); Local ocsp = Boolean::New( env->isolate(), SSL_get_tlsext_status_type(s) == TLSEXT_STATUSTYPE_ocsp); @@ -303,6 +305,20 @@ int SelectALPNCallback( : SSL_TLSEXT_ERR_ALERT_FATAL; } +MaybeLocal GetSSLOCSPResponse(Environment* env, SSL* ssl) { + const unsigned char* resp; + int len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp); + if (resp == nullptr) return Null(env->isolate()); + + Local ret; + MaybeLocal maybe_buffer = + Buffer::Copy(env, reinterpret_cast(resp), len); + + if (!maybe_buffer.ToLocal(&ret)) return MaybeLocal(); + + return ret; +} + int TLSExtStatusCallback(SSL* s, void* arg) { TLSWrap* w = static_cast(SSL_get_app_data(s)); Environment* env = w->env(); @@ -311,7 +327,7 @@ int TLSExtStatusCallback(SSL* s, void* arg) { if (w->is_client()) { // Incoming response Local arg; - if (GetSSLOCSPResponse(env, s, Null(env->isolate())).ToLocal(&arg)) + if (GetSSLOCSPResponse(env, s).ToLocal(&arg)) w->MakeCallback(env->onocspresponse_string(), 1, &arg); // No async acceptance is possible, so always return 1 to accept the @@ -343,8 +359,7 @@ int TLSExtStatusCallback(SSL* s, void* arg) { void ConfigureSecureContext(SecureContext* sc) { // OCSP stapling - SSL_CTX_set_tlsext_status_cb(sc->ctx().get(), TLSExtStatusCallback); - SSL_CTX_set_tlsext_status_arg(sc->ctx().get(), nullptr); + sc->ctx().setStatusCallback(TLSExtStatusCallback); } inline bool Set( @@ -362,6 +377,19 @@ inline bool Set( .IsNothing(); } +inline bool Set(Environment* env, + Local target, + Local name, + const std::string_view& value, + bool ignore_null = true) { + if (value.empty()) return ignore_null; + return !target + ->Set(env->context(), + name, + OneByteString(env->isolate(), value.data(), value.length())) + .IsNothing(); +} + std::string GetBIOError() { std::string ret; ERR_print_errors_cb( @@ -372,6 +400,7 @@ std::string GetBIOError() { static_cast(&ret)); return ret; } + } // namespace TLSWrap::TLSWrap(Environment* env, @@ -1330,9 +1359,11 @@ void TLSWrap::GetServername(const FunctionCallbackInfo& args) { CHECK_NOT_NULL(wrap->ssl_); - const char* servername = GetServerName(wrap->ssl_.get()); - if (servername != nullptr) { - args.GetReturnValue().Set(OneByteString(env->isolate(), servername)); + auto servername = wrap->ssl_.getServerName(); + if (servername.has_value()) { + auto& sn = servername.value(); + args.GetReturnValue().Set( + OneByteString(env->isolate(), sn.data(), sn.length())); } else { args.GetReturnValue().Set(false); } @@ -1361,8 +1392,9 @@ int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); - const char* servername = GetServerName(s); - if (!Set(env, p->GetOwner(), env->servername_string(), servername)) + auto servername = SSLPointer::GetServerName(s); + if (!servername.has_value() || + !Set(env, p->GetOwner(), env->servername_string(), servername.value())) return SSL_TLSEXT_ERR_NOACK; Local ctx = p->object()->Get(env->context(), env->sni_context_string()) @@ -1803,7 +1835,7 @@ void TLSWrap::SetSession(const FunctionCallbackInfo& args) { if (sess == nullptr) return; // TODO(tniessen): figure out error handling - if (!SetTLSSession(w->ssl_, sess)) + if (!w->ssl_.setSession(sess)) return env->ThrowError("SSL_set_session error"); } @@ -1830,15 +1862,19 @@ void TLSWrap::VerifyError(const FunctionCallbackInfo& args) { if (x509_verify_error == X509_V_OK) return args.GetReturnValue().SetNull(); - const char* reason = X509_verify_cert_error_string(x509_verify_error); - const char* code = X509ErrorCode(x509_verify_error); + Local reason; + if (!GetValidationErrorReason(env, x509_verify_error).ToLocal(&reason)) { + return; + } + if (reason->IsUndefined()) [[unlikely]] + return; - Local error = - Exception::Error(OneByteString(env->isolate(), reason)) - ->ToObject(env->isolate()->GetCurrentContext()) - .FromMaybe(Local()); + Local error = Exception::Error(reason.As()) + ->ToObject(env->isolate()->GetCurrentContext()) + .FromMaybe(Local()); - if (Set(env, error, env->code_string(), code)) + auto code = X509Pointer::ErrorCode(x509_verify_error); + if (Set(env, error, env->code_string(), code.data())) args.GetReturnValue().Set(error); } diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index a72c0a2a908294..bd38be97094f1e 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -66,7 +66,6 @@ using EVPMDCtxPointer = ncrypto::EVPMDCtxPointer; using RSAPointer = ncrypto::RSAPointer; using ECPointer = ncrypto::ECPointer; using BignumPointer = ncrypto::BignumPointer; -using BignumCtxPointer = ncrypto::BignumCtxPointer; using NetscapeSPKIPointer = ncrypto::NetscapeSPKIPointer; using ECGroupPointer = ncrypto::ECGroupPointer; using ECPointPointer = ncrypto::ECPointPointer; diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index 9b9bb7be9a8dac..a19b1d07e02609 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -60,34 +60,17 @@ void ManagedX509::MemoryInfo(MemoryTracker* tracker) const { } namespace { -void AddFingerprintDigest(const unsigned char* md, - unsigned int md_size, - char fingerprint[3 * EVP_MAX_MD_SIZE]) { - unsigned int i; - static constexpr char hex[] = "0123456789ABCDEF"; - - for (i = 0; i < md_size; i++) { - fingerprint[3 * i] = hex[(md[i] & 0xf0) >> 4]; - fingerprint[(3 * i) + 1] = hex[(md[i] & 0x0f)]; - fingerprint[(3 * i) + 2] = ':'; - } - - DCHECK_GT(md_size, 0); - fingerprint[(3 * (md_size - 1)) + 2] = '\0'; -} - MaybeLocal GetFingerprintDigest(Environment* env, const EVP_MD* method, const ncrypto::X509View& cert) { - unsigned char md[EVP_MAX_MD_SIZE]; - unsigned int md_size; - char fingerprint[EVP_MAX_MD_SIZE * 3]; - - if (X509_digest(cert.get(), method, md, &md_size)) { - AddFingerprintDigest(md, md_size, fingerprint); - return OneByteString(env->isolate(), fingerprint); + auto fingerprint = cert.getFingerprint(method); + // Returning an empty string indicates that the digest failed for + // some reason. + if (!fingerprint.has_value()) [[unlikely]] { + return Undefined(env->isolate()); } - return Undefined(env->isolate()); + auto& fp = fingerprint.value(); + return OneByteString(env->isolate(), fp.data(), fp.length()); } template @@ -695,9 +678,8 @@ MaybeLocal GetPubKey(Environment* env, OSSL3_CONST RSA* rsa) { } MaybeLocal GetModulusString(Environment* env, const BIGNUM* n) { - auto bio = BIOPointer::NewMem(); + auto bio = BIOPointer::New(n); if (!bio) return {}; - BN_print(bio.get(), n); return ToV8Value(env->context(), bio); } diff --git a/src/quic/tlscontext.cc b/src/quic/tlscontext.cc index fda49710e85938..8e2995589c9116 100644 --- a/src/quic/tlscontext.cc +++ b/src/quic/tlscontext.cc @@ -245,7 +245,7 @@ crypto::SSLCtxPointer TLSContext::Initialize() { switch (side_) { case Side::SERVER: { static constexpr unsigned char kSidCtx[] = "Node.js QUIC Server"; - ctx.reset(SSL_CTX_new(TLS_server_method())); + ctx = crypto::SSLCtxPointer::NewServer(); CHECK_EQ(ngtcp2_crypto_quictls_configure_server_context(ctx.get()), 0); CHECK_EQ(SSL_CTX_set_max_early_data(ctx.get(), UINT32_MAX), 1); SSL_CTX_set_options(ctx.get(), @@ -276,7 +276,7 @@ crypto::SSLCtxPointer TLSContext::Initialize() { break; } case Side::CLIENT: { - ctx.reset(SSL_CTX_new(TLS_client_method())); + ctx = crypto::SSLCtxPointer::NewClient(); CHECK_EQ(ngtcp2_crypto_quictls_configure_client_context(ctx.get()), 0); SSL_CTX_set_session_cache_mode( @@ -557,7 +557,7 @@ crypto::SSLPointer TLSSession::Initialize( reinterpret_cast(buf.base), buf.len); // The early data will just be ignored if it's invalid. - if (crypto::SetTLSSession(ssl, ticket) && + if (ssl.setSession(ticket) && SSL_SESSION_get_max_early_data(ticket.get()) != 0) { ngtcp2_vec rtp = sessionTicket.transport_params(); if (ngtcp2_conn_decode_and_set_0rtt_transport_params( @@ -622,9 +622,7 @@ MaybeLocal TLSSession::cipher_version(Environment* env) const { } const std::string_view TLSSession::servername() const { - const char* servername = crypto::GetServerName(ssl_.get()); - return servername != nullptr ? std::string_view(servername) - : std::string_view(); + return ssl_.getServerName().value_or(std::string_view()); } const std::string_view TLSSession::protocol() const { From 7b472fdfe7e16e2a472440ba148b88fbcfb03635 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 5 Jan 2025 12:36:27 -0800 Subject: [PATCH 002/208] src: minor cleanups on OneByteString usage * Provide a OneByteString variant that accepts std::string_view * Use FIXED_ONE_BYTE_STRING in appropriate places Signed-off-by: James M Snell PR-URL: https://github.com/nodejs/node/pull/56482 Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca Reviewed-By: Chengzhong Wu Reviewed-By: Rafael Gonzaga Reviewed-By: Ethan Arrowood Reviewed-By: Anna Henningsen --- src/crypto/crypto_ec.cc | 8 ++--- src/crypto/crypto_hash.cc | 2 +- src/crypto/crypto_tls.cc | 5 ++- src/histogram.cc | 2 +- src/inspector_js_api.cc | 2 +- src/node_builtins.cc | 17 +++++----- src/node_constants.cc | 68 ++++++++++++++++++++++--------------- src/node_errors.h | 4 +-- src/node_http2.cc | 15 ++++---- src/node_messaging.cc | 2 +- src/node_perf.h | 12 +++---- src/node_process_events.cc | 3 +- src/node_process_methods.cc | 4 +-- src/node_process_object.cc | 10 +++--- src/node_sqlite.cc | 2 +- src/util-inl.h | 5 +++ src/util.h | 3 ++ src/uv.cc | 2 +- 18 files changed, 92 insertions(+), 74 deletions(-) diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc index 78df3ae14407e3..9670f821ef97a6 100644 --- a/src/crypto/crypto_ec.cc +++ b/src/crypto/crypto_ec.cc @@ -767,16 +767,16 @@ Maybe ExportJWKEcKey(Environment* env, const int nid = EC_GROUP_get_curve_name(group); switch (nid) { case NID_X9_62_prime256v1: - crv_name = OneByteString(env->isolate(), "P-256"); + crv_name = FIXED_ONE_BYTE_STRING(env->isolate(), "P-256"); break; case NID_secp256k1: - crv_name = OneByteString(env->isolate(), "secp256k1"); + crv_name = FIXED_ONE_BYTE_STRING(env->isolate(), "secp256k1"); break; case NID_secp384r1: - crv_name = OneByteString(env->isolate(), "P-384"); + crv_name = FIXED_ONE_BYTE_STRING(env->isolate(), "P-384"); break; case NID_secp521r1: - crv_name = OneByteString(env->isolate(), "P-521"); + crv_name = FIXED_ONE_BYTE_STRING(env->isolate(), "P-521"); break; default: { THROW_ERR_CRYPTO_JWK_UNSUPPORTED_CURVE( diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index 5fd20c2fd3968f..58856397cdff92 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -152,7 +152,7 @@ void Hash::GetCachedAliases(const FunctionCallbackInfo& args) { names.reserve(size); values.reserve(size); for (auto& [alias, id] : env->alias_to_md_id_map) { - names.push_back(OneByteString(isolate, alias.c_str(), alias.size())); + names.push_back(OneByteString(isolate, alias)); values.push_back(v8::Uint32::New(isolate, id)); } #else diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc index f0bb4786abdc71..21a4e7538366be 100644 --- a/src/crypto/crypto_tls.cc +++ b/src/crypto/crypto_tls.cc @@ -859,8 +859,7 @@ void TLSWrap::ClearOut() { if (context.IsEmpty()) [[unlikely]] return; const std::string error_str = GetBIOError(); - Local message = OneByteString( - env()->isolate(), error_str.c_str(), error_str.size()); + Local message = OneByteString(env()->isolate(), error_str); if (message.IsEmpty()) [[unlikely]] return; error = Exception::Error(message); @@ -1974,7 +1973,7 @@ void TLSWrap::GetSharedSigalgs(const FunctionCallbackInfo& args) { } else { sig_with_md += "UNDEF"; } - ret_arr[i] = OneByteString(env->isolate(), sig_with_md.c_str()); + ret_arr[i] = OneByteString(env->isolate(), sig_with_md); } args.GetReturnValue().Set( diff --git a/src/histogram.cc b/src/histogram.cc index d111f8b5768261..22300a65a2378c 100644 --- a/src/histogram.cc +++ b/src/histogram.cc @@ -341,7 +341,7 @@ Local IntervalHistogram::GetConstructorTemplate( Isolate* isolate = env->isolate(); tmpl = NewFunctionTemplate(isolate, nullptr); tmpl->Inherit(HandleWrap::GetConstructorTemplate(env)); - tmpl->SetClassName(OneByteString(isolate, "Histogram")); + tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "Histogram")); auto instance = tmpl->InstanceTemplate(); instance->SetInternalFieldCount(HistogramImpl::kInternalFieldCount); HistogramImpl::AddMethods(isolate, tmpl); diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc index 282575601545d1..63ceaccdf2406b 100644 --- a/src/inspector_js_api.cc +++ b/src/inspector_js_api.cc @@ -333,7 +333,7 @@ void Url(const FunctionCallbackInfo& args) { if (url.empty()) { return; } - args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str())); + args.GetReturnValue().Set(OneByteString(env->isolate(), url)); } void Initialize(Local target, Local unused, diff --git a/src/node_builtins.cc b/src/node_builtins.cc index c3ab61b014885e..9a847429a500bf 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc @@ -78,7 +78,7 @@ void BuiltinLoader::GetNatives(Local property, Local out = Object::New(isolate); auto source = env->builtin_loader()->source_.read(); for (auto const& x : *source) { - Local key = OneByteString(isolate, x.first.c_str(), x.first.size()); + Local key = OneByteString(isolate, x.first); out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust(); } info.GetReturnValue().Set(out); @@ -207,7 +207,7 @@ MaybeLocal BuiltinLoader::LoadBuiltinSource(Isolate* isolate, uv_err_name(r), uv_strerror(r), filename); - Local message = OneByteString(isolate, buf.c_str()); + Local message = OneByteString(isolate, buf); isolate->ThrowException(v8::Exception::Error(message)); return MaybeLocal(); } @@ -277,8 +277,7 @@ MaybeLocal BuiltinLoader::LookupAndCompileInternal( } std::string filename_s = std::string("node:") + id; - Local filename = - OneByteString(isolate, filename_s.c_str(), filename_s.size()); + Local filename = OneByteString(isolate, filename_s); ScriptOrigin origin(filename, 0, 0, true); BuiltinCodeCacheData cached_data{}; @@ -595,7 +594,7 @@ void BuiltinLoader::GetBuiltinCategories( return; if (result ->Set(context, - OneByteString(isolate, "cannotBeRequired"), + FIXED_ONE_BYTE_STRING(isolate, "cannotBeRequired"), cannot_be_required_js) .IsNothing()) return; @@ -604,7 +603,7 @@ void BuiltinLoader::GetBuiltinCategories( return; if (result ->Set(context, - OneByteString(isolate, "canBeRequired"), + FIXED_ONE_BYTE_STRING(isolate, "canBeRequired"), can_be_required_js) .IsNothing()) { return; @@ -627,7 +626,7 @@ void BuiltinLoader::GetCacheUsage(const FunctionCallbackInfo& args) { } if (result ->Set(context, - OneByteString(isolate, "compiledWithCache"), + FIXED_ONE_BYTE_STRING(isolate, "compiledWithCache"), builtins_with_cache_js) .IsNothing()) { return; @@ -639,7 +638,7 @@ void BuiltinLoader::GetCacheUsage(const FunctionCallbackInfo& args) { } if (result ->Set(context, - OneByteString(isolate, "compiledWithoutCache"), + FIXED_ONE_BYTE_STRING(isolate, "compiledWithoutCache"), builtins_without_cache_js) .IsNothing()) { return; @@ -651,7 +650,7 @@ void BuiltinLoader::GetCacheUsage(const FunctionCallbackInfo& args) { } if (result ->Set(context, - OneByteString(isolate, "compiledInSnapshot"), + FIXED_ONE_BYTE_STRING(isolate, "compiledInSnapshot"), builtins_in_snapshot_js) .IsNothing()) { return; diff --git a/src/node_constants.cc b/src/node_constants.cc index 41465f627118cd..a390bc8616afc1 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -1337,33 +1337,47 @@ void CreatePerContextProperties(Local target, // Define libuv constants. NODE_DEFINE_CONSTANT(os_constants, UV_UDP_REUSEADDR); - os_constants->Set(env->context(), - OneByteString(isolate, "dlopen"), - dlopen_constants).Check(); - os_constants->Set(env->context(), - OneByteString(isolate, "errno"), - err_constants).Check(); - os_constants->Set(env->context(), - OneByteString(isolate, "signals"), - sig_constants).Check(); - os_constants->Set(env->context(), - OneByteString(isolate, "priority"), - priority_constants).Check(); - target->Set(env->context(), - OneByteString(isolate, "os"), - os_constants).Check(); - target->Set(env->context(), - OneByteString(isolate, "fs"), - fs_constants).Check(); - target->Set(env->context(), - OneByteString(isolate, "crypto"), - crypto_constants).Check(); - target->Set(env->context(), - OneByteString(isolate, "zlib"), - zlib_constants).Check(); - target->Set(env->context(), - OneByteString(isolate, "trace"), - trace_constants).Check(); + os_constants + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "dlopen"), + dlopen_constants) + .Check(); + os_constants + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "errno"), + err_constants) + .Check(); + os_constants + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "signals"), + sig_constants) + .Check(); + os_constants + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "priority"), + priority_constants) + .Check(); + target + ->Set(env->context(), FIXED_ONE_BYTE_STRING(isolate, "os"), os_constants) + .Check(); + target + ->Set(env->context(), FIXED_ONE_BYTE_STRING(isolate, "fs"), fs_constants) + .Check(); + target + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "crypto"), + crypto_constants) + .Check(); + target + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "zlib"), + zlib_constants) + .Check(); + target + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "trace"), + trace_constants) + .Check(); } } // namespace constants diff --git a/src/node_errors.h b/src/node_errors.h index a33177a5d8e7e6..41e00114cb469a 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -118,7 +118,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details); inline v8::Local code( \ v8::Isolate* isolate, const char* format, Args&&... args) { \ std::string message = SPrintF(format, std::forward(args)...); \ - v8::Local js_code = OneByteString(isolate, #code); \ + v8::Local js_code = FIXED_ONE_BYTE_STRING(isolate, #code); \ v8::Local js_msg = \ v8::String::NewFromUtf8(isolate, \ message.c_str(), \ @@ -129,7 +129,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details); ->ToObject(isolate->GetCurrentContext()) \ .ToLocalChecked(); \ e->Set(isolate->GetCurrentContext(), \ - OneByteString(isolate, "code"), \ + FIXED_ONE_BYTE_STRING(isolate, "code"), \ js_code) \ .Check(); \ return e; \ diff --git a/src/node_http2.cc b/src/node_http2.cc index 91e9011cd91c6a..bf0ce4fd20a008 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -725,13 +725,14 @@ MaybeLocal Http2SessionPerformanceEntryTraits::GetDetails( SET(stream_average_duration_string, stream_average_duration) SET(stream_count_string, stream_count) - if (!obj->Set( - env->context(), - env->type_string(), - OneByteString( - env->isolate(), - (entry.details.session_type == NGHTTP2_SESSION_SERVER) - ? "server" : "client")).IsJust()) { + if (!obj->Set(env->context(), + env->type_string(), + FIXED_ONE_BYTE_STRING( + env->isolate(), + (entry.details.session_type == NGHTTP2_SESSION_SERVER) + ? "server" + : "client")) + .IsJust()) { return MaybeLocal(); } diff --git a/src/node_messaging.cc b/src/node_messaging.cc index a1c22cf5005121..73c0c38dc7bf45 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -1660,7 +1660,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data, Local t = FunctionTemplate::New(isolate); t->InstanceTemplate()->SetInternalFieldCount( JSTransferable::kInternalFieldCount); - t->SetClassName(OneByteString(isolate, "JSTransferable")); + t->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "JSTransferable")); isolate_data->set_js_transferable_constructor_template(t); } diff --git a/src/node_perf.h b/src/node_perf.h index 3e994ce5a63b7d..79b3aaf8bb7f5f 100644 --- a/src/node_perf.h +++ b/src/node_perf.h @@ -124,12 +124,12 @@ struct PerformanceEntry { } v8::Local argv[] = { - OneByteString(env->isolate(), name.c_str()), - OneByteString(env->isolate(), GetPerformanceEntryTypeName(Traits::kType)), - v8::Number::New(env->isolate(), start_time), - v8::Number::New(env->isolate(), duration), - detail - }; + OneByteString(env->isolate(), name), + OneByteString(env->isolate(), + GetPerformanceEntryTypeName(Traits::kType)), + v8::Number::New(env->isolate(), start_time), + v8::Number::New(env->isolate(), duration), + detail}; node::MakeSyncCallback( env->isolate(), diff --git a/src/node_process_events.cc b/src/node_process_events.cc index 128ad9fbb1f257..8aac953b3e0db5 100644 --- a/src/node_process_events.cc +++ b/src/node_process_events.cc @@ -21,8 +21,7 @@ using v8::Value; Maybe ProcessEmitWarningSync(Environment* env, std::string_view message) { Isolate* isolate = env->isolate(); Local context = env->context(); - Local message_string = - OneByteString(isolate, message.data(), message.size()); + Local message_string = OneByteString(isolate, message); Local argv[] = {message_string}; Local emit_function = env->process_emit_warning_sync(); diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index 127ef63210b962..c67d1ac00a972b 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -312,12 +312,12 @@ static void GetActiveResourcesInfo(const FunctionCallbackInfo& args) { // Active timeouts resources_info.insert(resources_info.end(), env->timeout_info()[0], - OneByteString(env->isolate(), "Timeout")); + FIXED_ONE_BYTE_STRING(env->isolate(), "Timeout")); // Active immediates resources_info.insert(resources_info.end(), env->immediate_info()->ref_count(), - OneByteString(env->isolate(), "Immediate")); + FIXED_ONE_BYTE_STRING(env->isolate(), "Immediate")); args.GetReturnValue().Set( Array::New(env->isolate(), resources_info.data(), resources_info.size())); diff --git a/src/node_process_object.cc b/src/node_process_object.cc index b1717912ec2d4c..bbbc2403b6fa6d 100644 --- a/src/node_process_object.cc +++ b/src/node_process_object.cc @@ -104,12 +104,10 @@ static void SetVersions(Isolate* isolate, Local versions) { for (const auto& version : versions_array) { versions - ->DefineOwnProperty( - context, - OneByteString(isolate, version.first.data(), version.first.size()), - OneByteString( - isolate, version.second.data(), version.second.size()), - v8::ReadOnly) + ->DefineOwnProperty(context, + OneByteString(isolate, version.first), + OneByteString(isolate, version.second), + v8::ReadOnly) .Check(); } } diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 373931a76a54de..0b8f7ef2a21763 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -1735,7 +1735,7 @@ static void Initialize(Local target, "StatementSync", StatementSync::GetConstructorTemplate(env)); - target->Set(context, OneByteString(isolate, "constants"), constants).Check(); + target->Set(context, env->constants_string(), constants).Check(); } } // namespace sqlite diff --git a/src/util-inl.h b/src/util-inl.h index e078c9a11b2fac..a35e15eeed6576 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -180,6 +180,11 @@ inline v8::Local OneByteString(v8::Isolate* isolate, .ToLocalChecked(); } +inline v8::Local OneByteString(v8::Isolate* isolate, + std::string_view str) { + return OneByteString(isolate, str.data(), str.size()); +} + char ToLower(char c) { return std::tolower(c, std::locale::classic()); } diff --git a/src/util.h b/src/util.h index b1f316eebc7199..0d4676ddade8d9 100644 --- a/src/util.h +++ b/src/util.h @@ -340,6 +340,9 @@ inline v8::Local OneByteString(v8::Isolate* isolate, const unsigned char* data, int length = -1); +inline v8::Local OneByteString(v8::Isolate* isolate, + std::string_view str); + // Used to be a macro, hence the uppercase name. template inline v8::Local FIXED_ONE_BYTE_STRING( diff --git a/src/uv.cc b/src/uv.cc index e6623bc7e162f5..168e7be408ce34 100644 --- a/src/uv.cc +++ b/src/uv.cc @@ -130,7 +130,7 @@ void Initialize(Local target, for (size_t i = 0; i < errors_len; ++i) { const auto& error = per_process::uv_errors_map[i]; const std::string prefixed_name = prefix + error.name; - Local name = OneByteString(isolate, prefixed_name.c_str()); + Local name = OneByteString(isolate, prefixed_name); Local value = Integer::New(isolate, error.value); target->DefineOwnProperty(context, name, value, attributes).Check(); } From 4f45acef19c4c592d659b74e028bd29c4fd1dfcc Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Wed, 8 Jan 2025 16:56:42 +0000 Subject: [PATCH 003/208] inspector: add undici http tracking support Add basic undici http tracking support via inspector protocol. This allows tracking `fetch` calls with an inspector. PR-URL: https://github.com/nodejs/node/pull/56488 Refs: https://github.com/nodejs/node/issues/53946 Reviewed-By: James M Snell Reviewed-By: Benjamin Gruenbaum Reviewed-By: Ethan Arrowood Reviewed-By: Matteo Collina --- lib/internal/inspector/network.js | 23 ++ lib/internal/inspector/network_http.js | 6 +- lib/internal/inspector/network_undici.js | 141 +++++++++++++ lib/internal/inspector_network_tracking.js | 6 +- src/node_builtins.cc | 3 +- test/parallel/test-inspector-network-fetch.js | 196 ++++++++++++++++++ 6 files changed, 367 insertions(+), 8 deletions(-) create mode 100644 lib/internal/inspector/network_undici.js create mode 100644 test/parallel/test-inspector-network-fetch.js diff --git a/lib/internal/inspector/network.js b/lib/internal/inspector/network.js index 18424bee569302..f46268ddc49621 100644 --- a/lib/internal/inspector/network.js +++ b/lib/internal/inspector/network.js @@ -8,6 +8,28 @@ const { const { now } = require('internal/perf/utils'); const kInspectorRequestId = Symbol('kInspectorRequestId'); +// https://chromedevtools.github.io/devtools-protocol/1-3/Network/#type-ResourceType +const kResourceType = { + Document: 'Document', + Stylesheet: 'Stylesheet', + Image: 'Image', + Media: 'Media', + Font: 'Font', + Script: 'Script', + TextTrack: 'TextTrack', + XHR: 'XHR', + Fetch: 'Fetch', + Prefetch: 'Prefetch', + EventSource: 'EventSource', + WebSocket: 'WebSocket', + Manifest: 'Manifest', + SignedExchange: 'SignedExchange', + Ping: 'Ping', + CSPViolationReport: 'CSPViolationReport', + Preflight: 'Preflight', + Other: 'Other', +}; + /** * Return a monotonically increasing time in seconds since an arbitrary point in the past. * @returns {number} @@ -26,6 +48,7 @@ function getNextRequestId() { module.exports = { kInspectorRequestId, + kResourceType, getMonotonicTime, getNextRequestId, }; diff --git a/lib/internal/inspector/network_http.js b/lib/internal/inspector/network_http.js index 87a33b419b1aed..16669f308f3a8e 100644 --- a/lib/internal/inspector/network_http.js +++ b/lib/internal/inspector/network_http.js @@ -10,13 +10,13 @@ const { const { kInspectorRequestId, + kResourceType, getMonotonicTime, getNextRequestId, } = require('internal/inspector/network'); const dc = require('diagnostics_channel'); const { Network } = require('inspector'); -const kResourceType = 'Other'; const kRequestUrl = Symbol('kRequestUrl'); // Convert a Headers object (Map) to a plain object (Map) @@ -79,7 +79,7 @@ function onClientRequestError({ request, error }) { Network.loadingFailed({ requestId: request[kInspectorRequestId], timestamp: getMonotonicTime(), - type: kResourceType, + type: kResourceType.Other, errorText: error.message, }); } @@ -96,7 +96,7 @@ function onClientResponseFinish({ request, response }) { Network.responseReceived({ requestId: request[kInspectorRequestId], timestamp: getMonotonicTime(), - type: kResourceType, + type: kResourceType.Other, response: { url: request[kRequestUrl], status: response.statusCode, diff --git a/lib/internal/inspector/network_undici.js b/lib/internal/inspector/network_undici.js new file mode 100644 index 00000000000000..7afc5970117127 --- /dev/null +++ b/lib/internal/inspector/network_undici.js @@ -0,0 +1,141 @@ +'use strict'; + +const { + DateNow, +} = primordials; + +const { + kInspectorRequestId, + kResourceType, + getMonotonicTime, + getNextRequestId, +} = require('internal/inspector/network'); +const dc = require('diagnostics_channel'); +const { Network } = require('inspector'); + +// Convert an undici request headers array to a plain object (Map) +function requestHeadersArrayToDictionary(headers) { + const dict = {}; + for (let idx = 0; idx < headers.length; idx += 2) { + const key = `${headers[idx]}`; + const value = `${headers[idx + 1]}`; + dict[key] = value; + } + return dict; +}; + +// Convert an undici response headers array to a plain object (Map) +function responseHeadersArrayToDictionary(headers) { + const dict = {}; + for (let idx = 0; idx < headers.length; idx += 2) { + const key = `${headers[idx]}`; + const value = `${headers[idx + 1]}`; + const prevValue = dict[key]; + + if (typeof prevValue === 'string') { + // ChromeDevTools frontend treats 'set-cookie' as a special case + // https://github.com/ChromeDevTools/devtools-frontend/blob/4275917f84266ef40613db3c1784a25f902ea74e/front_end/core/sdk/NetworkRequest.ts#L1368 + if (key.toLowerCase() === 'set-cookie') dict[key] = `${prevValue}\n${value}`; + else dict[key] = `${prevValue}, ${value}`; + } else { + dict[key] = value; + } + } + return dict; +}; + +/** + * When a client request starts, emit Network.requestWillBeSent event. + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-requestWillBeSent + * @param {{ request: undici.Request }} event + */ +function onClientRequestStart({ request }) { + const url = `${request.origin}${request.path}`; + request[kInspectorRequestId] = getNextRequestId(); + Network.requestWillBeSent({ + requestId: request[kInspectorRequestId], + timestamp: getMonotonicTime(), + wallTime: DateNow(), + request: { + url, + method: request.method, + headers: requestHeadersArrayToDictionary(request.headers), + }, + }); +} + +/** + * When a client request errors, emit Network.loadingFailed event. + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-loadingFailed + * @param {{ request: undici.Request, error: any }} event + */ +function onClientRequestError({ request, error }) { + if (typeof request[kInspectorRequestId] !== 'string') { + return; + } + Network.loadingFailed({ + requestId: request[kInspectorRequestId], + timestamp: getMonotonicTime(), + // TODO(legendecas): distinguish between `undici.request` and `undici.fetch`. + type: kResourceType.Fetch, + errorText: error.message, + }); +} + +/** + * When response headers are received, emit Network.responseReceived event. + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-responseReceived + * @param {{ request: undici.Request, response: undici.Response }} event + */ +function onClientResponseHeaders({ request, response }) { + if (typeof request[kInspectorRequestId] !== 'string') { + return; + } + const url = `${request.origin}${request.path}`; + Network.responseReceived({ + requestId: request[kInspectorRequestId], + timestamp: getMonotonicTime(), + // TODO(legendecas): distinguish between `undici.request` and `undici.fetch`. + type: kResourceType.Fetch, + response: { + url, + status: response.statusCode, + statusText: response.statusText, + headers: responseHeadersArrayToDictionary(response.headers), + }, + }); +} + +/** + * When a response is completed, emit Network.loadingFinished event. + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-loadingFinished + * @param {{ request: undici.Request, response: undici.Response }} event + */ +function onClientResponseFinish({ request }) { + if (typeof request[kInspectorRequestId] !== 'string') { + return; + } + Network.loadingFinished({ + requestId: request[kInspectorRequestId], + timestamp: getMonotonicTime(), + }); +} + +function enable() { + dc.subscribe('undici:request:create', onClientRequestStart); + dc.subscribe('undici:request:error', onClientRequestError); + dc.subscribe('undici:request:headers', onClientResponseHeaders); + dc.subscribe('undici:request:trailers', onClientResponseFinish); +} + +function disable() { + dc.subscribe('undici:request:create', onClientRequestStart); + dc.subscribe('undici:request:error', onClientRequestError); + dc.subscribe('undici:request:headers', onClientResponseHeaders); + dc.subscribe('undici:request:trailers', onClientResponseFinish); +} + +module.exports = { + enable, + disable, +}; diff --git a/lib/internal/inspector_network_tracking.js b/lib/internal/inspector_network_tracking.js index 9158bb48f745f8..5748259fb680c1 100644 --- a/lib/internal/inspector_network_tracking.js +++ b/lib/internal/inspector_network_tracking.js @@ -2,14 +2,12 @@ function enable() { require('internal/inspector/network_http').enable(); - // TODO: add undici request/websocket tracking. - // https://github.com/nodejs/node/issues/53946 + require('internal/inspector/network_undici').enable(); } function disable() { require('internal/inspector/network_http').disable(); - // TODO: add undici request/websocket tracking. - // https://github.com/nodejs/node/issues/53946 + require('internal/inspector/network_undici').disable(); } module.exports = { diff --git a/src/node_builtins.cc b/src/node_builtins.cc index 9a847429a500bf..4b3cfd786aaaf2 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc @@ -120,7 +120,8 @@ BuiltinLoader::BuiltinCategories BuiltinLoader::GetBuiltinCategories() const { #if !HAVE_INSPECTOR "inspector", "inspector/promises", "internal/util/inspector", "internal/inspector/network", "internal/inspector/network_http", - "internal/inspector_async_hook", "internal/inspector_network_tracking", + "internal/inspector/network_undici", "internal/inspector_async_hook", + "internal/inspector_network_tracking", #endif // !HAVE_INSPECTOR #if !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT) diff --git a/test/parallel/test-inspector-network-fetch.js b/test/parallel/test-inspector-network-fetch.js new file mode 100644 index 00000000000000..26f6d52ff40694 --- /dev/null +++ b/test/parallel/test-inspector-network-fetch.js @@ -0,0 +1,196 @@ +// Flags: --inspect=0 --experimental-network-inspection +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('node:assert'); +const { addresses } = require('../common/internet'); +const fixtures = require('../common/fixtures'); +const http = require('node:http'); +const https = require('node:https'); +const inspector = require('node:inspector/promises'); + +// Disable certificate validation for the global fetch. +const undici = require('../../deps/undici/src/index.js'); +undici.setGlobalDispatcher(new undici.Agent({ + connect: { + rejectUnauthorized: false, + }, +})); + +const session = new inspector.Session(); +session.connect(); + +const requestHeaders = [ + ['accept-language', 'en-US'], + ['cookie', 'k1=v1'], + ['cookie', 'k2=v2'], + ['age', 1000], + ['x-header1', 'value1'], + ['x-header1', 'value2'], +]; + +const setResponseHeaders = (res) => { + res.setHeader('server', 'node'); + res.setHeader('etag', 12345); + res.setHeader('Set-Cookie', ['key1=value1', 'key2=value2']); + res.setHeader('x-header2', ['value1', 'value2']); +}; + +const handleRequest = (req, res) => { + const path = req.url; + switch (path) { + case '/hello-world': + setResponseHeaders(res); + res.writeHead(200); + res.end('hello world\n'); + break; + default: + assert(false, `Unexpected path: ${path}`); + } +}; + +const httpServer = http.createServer(handleRequest); + +const httpsServer = https.createServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}, handleRequest); + +const terminate = () => { + session.disconnect(); + httpServer.close(); + httpsServer.close(); + inspector.close(); +}; + +const testHttpGet = () => new Promise((resolve, reject) => { + session.on('Network.requestWillBeSent', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(params.request.url, `http://127.0.0.1:${httpServer.address().port}/hello-world`); + assert.strictEqual(params.request.method, 'GET'); + assert.strictEqual(typeof params.request.headers, 'object'); + assert.strictEqual(params.request.headers['accept-language'], 'en-US'); + assert.strictEqual(params.request.headers.cookie, 'k1=v1; k2=v2'); + assert.strictEqual(params.request.headers.age, '1000'); + assert.strictEqual(params.request.headers['x-header1'], 'value1, value2'); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.wallTime, 'number'); + })); + session.on('Network.responseReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(params.type, 'Fetch'); + assert.strictEqual(params.response.status, 200); + assert.strictEqual(params.response.statusText, 'OK'); + assert.strictEqual(params.response.url, `http://127.0.0.1:${httpServer.address().port}/hello-world`); + assert.strictEqual(typeof params.response.headers, 'object'); + assert.strictEqual(params.response.headers.server, 'node'); + assert.strictEqual(params.response.headers.etag, '12345'); + assert.strictEqual(params.response.headers['Set-Cookie'], 'key1=value1\nkey2=value2'); + assert.strictEqual(params.response.headers['x-header2'], 'value1, value2'); + })); + session.on('Network.loadingFinished', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + resolve(); + })); + + fetch(`http://127.0.0.1:${httpServer.address().port}/hello-world`, { + headers: requestHeaders, + }).then(common.mustCall()); +}); + +const testHttpsGet = () => new Promise((resolve, reject) => { + session.on('Network.requestWillBeSent', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(params.request.url, `https://127.0.0.1:${httpsServer.address().port}/hello-world`); + assert.strictEqual(params.request.method, 'GET'); + assert.strictEqual(typeof params.request.headers, 'object'); + assert.strictEqual(params.request.headers['accept-language'], 'en-US'); + assert.strictEqual(params.request.headers.cookie, 'k1=v1; k2=v2'); + assert.strictEqual(params.request.headers.age, '1000'); + assert.strictEqual(params.request.headers['x-header1'], 'value1, value2'); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.wallTime, 'number'); + })); + session.on('Network.responseReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(params.type, 'Fetch'); + assert.strictEqual(params.response.status, 200); + assert.strictEqual(params.response.statusText, 'OK'); + assert.strictEqual(params.response.url, `https://127.0.0.1:${httpsServer.address().port}/hello-world`); + assert.strictEqual(typeof params.response.headers, 'object'); + assert.strictEqual(params.response.headers.server, 'node'); + assert.strictEqual(params.response.headers.etag, '12345'); + assert.strictEqual(params.response.headers['Set-Cookie'], 'key1=value1\nkey2=value2'); + assert.strictEqual(params.response.headers['x-header2'], 'value1, value2'); + })); + session.on('Network.loadingFinished', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + resolve(); + })); + + fetch(`https://127.0.0.1:${httpsServer.address().port}/hello-world`, { + headers: requestHeaders, + }).then(common.mustCall()); +}); + +const testHttpError = () => new Promise((resolve, reject) => { + session.on('Network.requestWillBeSent', common.mustCall()); + session.on('Network.loadingFailed', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(params.type, 'Fetch'); + assert.strictEqual(typeof params.errorText, 'string'); + resolve(); + })); + session.on('Network.responseReceived', common.mustNotCall()); + session.on('Network.loadingFinished', common.mustNotCall()); + + fetch(`http://${addresses.INVALID_HOST}`).catch(common.mustCall()); +}); + + +const testHttpsError = () => new Promise((resolve, reject) => { + session.on('Network.requestWillBeSent', common.mustCall()); + session.on('Network.loadingFailed', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(params.type, 'Fetch'); + assert.strictEqual(typeof params.errorText, 'string'); + resolve(); + })); + session.on('Network.responseReceived', common.mustNotCall()); + session.on('Network.loadingFinished', common.mustNotCall()); + + fetch(`https://${addresses.INVALID_HOST}`).catch(common.mustCall()); +}); + +const testNetworkInspection = async () => { + await testHttpGet(); + session.removeAllListeners(); + await testHttpsGet(); + session.removeAllListeners(); + await testHttpError(); + session.removeAllListeners(); + await testHttpsError(); + session.removeAllListeners(); +}; + +httpServer.listen(0, () => { + httpsServer.listen(0, async () => { + try { + await session.post('Network.enable'); + await testNetworkInspection(); + await session.post('Network.disable'); + } catch (e) { + assert.fail(e); + } finally { + terminate(); + } + }); +}); From 57478281aaa29f204c2d95be67e41667c4e40574 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Wed, 8 Jan 2025 22:17:20 +0100 Subject: [PATCH 004/208] doc: allow request for TSC reviews via the GitHub UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56493 Reviewed-By: Yagiz Nizipli Reviewed-By: Rafael Gonzaga Reviewed-By: Gireesh Punathil Reviewed-By: Ulises Gascón Reviewed-By: Marco Ippolito Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- doc/contributing/collaborator-guide.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/contributing/collaborator-guide.md b/doc/contributing/collaborator-guide.md index eb5ab19f8923e9..e153c86061bb7d 100644 --- a/doc/contributing/collaborator-guide.md +++ b/doc/contributing/collaborator-guide.md @@ -523,7 +523,6 @@ deprecation level of an API. Collaborators can opt to elevate pull requests or issues to the [TSC][]. Do this if a pull request or issue: -* Is labeled `semver-major`, or * Has a significant impact on the codebase, or * Is controversial, or * Is at an impasse among collaborators who are participating in the discussion. @@ -532,6 +531,9 @@ Do this if a pull request or issue: [TSC][]. Do not use the GitHub UI on the right-hand side to assign to `@nodejs/tsc` or request a review from `@nodejs/tsc`. +If a pull request is labeled `semver-major`, you can request a review from the +`@nodejs/tsc` GitHub team. + The TSC serves as the final arbiter where required. ## Landing pull requests From dc5d0f9bb4d017c4a4c7e61653da932df8fd9d93 Mon Sep 17 00:00:00 2001 From: Daeyeon Jeong Date: Thu, 9 Jan 2025 08:12:27 +0900 Subject: [PATCH 005/208] fs: allow `exclude` option in globs to accept glob patterns Signed-off-by: Daeyeon Jeong PR-URL: https://github.com/nodejs/node/pull/56489 Reviewed-By: James M Snell Reviewed-By: Chemi Atlow Reviewed-By: Moshe Atlow Reviewed-By: Jason Zhang --- doc/api/fs.md | 18 +++- lib/internal/fs/glob.js | 151 ++++++++++++++++++++++++++------- test/parallel/test-fs-glob.mjs | 94 ++++++++++++++++++++ 3 files changed, 227 insertions(+), 36 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index 4fafdc453b0603..d61b7c08c9597f 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -1074,6 +1074,9 @@ behavior is similar to `cp dir1/ dir2/`. + +* `value` {any} A value to serialize to a string. If Node.js was started with + the [`--test-update-snapshots`][] flag, the serialized value is written to + `path`. Otherwise, the serialized value is compared to the contents of the + existing snapshot file. +* `path` {string} The file where the serialized `value` is written. +* `options` {Object} Optional configuration options. The following properties + are supported: + * `serializers` {Array} An array of synchronous functions used to serialize + `value` into a string. `value` is passed as the only argument to the first + serializer function. The return value of each serializer is passed as input + to the next serializer. Once all serializers have run, the resulting value + is coerced to a string. **Default:** If no serializers are provided, the + test runner's default serializers are used. + +This function serializes `value` and writes it to the file specified by `path`. + +```js +test('snapshot test with default serialization', (t) => { + t.assert.fileSnapshot({ value1: 1, value2: 2 }, './snapshots/snapshot.json'); +}); +``` + +This function differs from `context.assert.snapshot()` in the following ways: + +* The snapshot file path is explicitly provided by the user. +* Each snapshot file is limited to a single snapshot value. +* No additional escaping is performed by the test runner. + +These differences allow snapshot files to better support features such as syntax +highlighting. + #### `context.assert.snapshot(value[, options])` -Type: Documentation-only +Type: Runtime Passing non-supported argument types is deprecated and, instead of returning `false`, will throw an error in a future version. diff --git a/lib/fs.js b/lib/fs.js index af72ac36144c55..e2996ba9ca4ef6 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -273,12 +273,7 @@ ObjectDefineProperty(exists, kCustomPromisifiedSymbol, { }, }); -// fs.existsSync never throws, it only returns true or false. -// Since fs.existsSync never throws, users have established -// the expectation that passing invalid arguments to it, even like -// fs.existsSync(), would only get a false in return, so we cannot signal -// validation errors to users properly out of compatibility concerns. -// TODO(joyeecheung): deprecate the never-throw-on-invalid-arguments behavior +let showExistsDeprecation = true; /** * Synchronously tests whether or not the given path exists. * @param {string | Buffer | URL} path @@ -288,6 +283,12 @@ function existsSync(path) { try { path = getValidatedPath(path); } catch { + if (showExistsDeprecation) { + process.emitWarning( + 'Passing invalid argument types to fs.existsSync is deprecated', 'DeprecationWarning', 'DEP0187', + ); + showExistsDeprecation = false; + } return false; } diff --git a/test/parallel/test-fs-exists.js b/test/parallel/test-fs-exists.js index 857f3f26174549..3be2197660ce8c 100644 --- a/test/parallel/test-fs-exists.js +++ b/test/parallel/test-fs-exists.js @@ -51,6 +51,8 @@ assert(fs.existsSync(f)); assert(!fs.existsSync(`${f}-NO`)); // fs.existsSync() never throws +const msg = 'Passing invalid argument types to fs.existsSync is deprecated'; +common.expectWarning('DeprecationWarning', msg, 'DEP0187'); assert(!fs.existsSync()); assert(!fs.existsSync({})); assert(!fs.existsSync(new URL('https://foo'))); From ba6800d1d6f2c1662d010d0618d999119da270ca Mon Sep 17 00:00:00 2001 From: Jacob Smith <3012099+JakobJingleheimer@users.noreply.github.com> Date: Fri, 10 Jan 2025 13:44:43 +0000 Subject: [PATCH 017/208] esm: fix jsdoc type refs to `ModuleJobBase` in esm/loader Co-Authored-By: Carlos Espa <43477095+Ceres6@users.noreply.github.com> PR-URL: https://github.com/nodejs/node/pull/56499 Reviewed-By: James M Snell Reviewed-By: Chengzhong Wu Reviewed-By: Antoine du Hamel --- lib/internal/modules/esm/loader.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 19eac728623939..a3b437ade87c75 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -54,6 +54,8 @@ const { tracingChannel } = require('diagnostics_channel'); const onImport = tracingChannel('module.import'); /** + * @typedef {import('./hooks.js').HooksProxy} HooksProxy + * @typedef {import('./module_job.js').ModuleJobBase} ModuleJobBase * @typedef {import('url').URL} URL */ @@ -149,6 +151,7 @@ class ModuleLoader { * to this property and failure to do so will cause undefined * behavior when invoking `import.meta.resolve`. * @see {ModuleLoader.setCustomizations} + * @type {CustomizedModuleLoader} */ #customizations; @@ -202,7 +205,7 @@ class ModuleLoader { * * Calling this function alters how modules are loaded and should be * invoked with care. - * @param {object} customizations + * @param {CustomizedModuleLoader} customizations */ setCustomizations(customizations) { this.#customizations = customizations; @@ -270,7 +273,7 @@ class ModuleLoader { * @param {string} [parentURL] The URL of the module where the module request is initiated. * It's undefined if it's from the root module. * @param {ImportAttributes} importAttributes Attributes from the import statement or expression. - * @returns {Promise} */ async getModuleJobForImport(specifier, parentURL, importAttributes) { const resolveResult = await this.resolve(specifier, parentURL, importAttributes); @@ -284,7 +287,7 @@ class ModuleLoader { * @param {string} specifier See {@link getModuleJobForImport} * @param {string} [parentURL] See {@link getModuleJobForImport} * @param {ImportAttributes} importAttributes See {@link getModuleJobForImport} - * @returns {Promise} */ getModuleJobForRequireInImportedCJS(specifier, parentURL, importAttributes) { const resolveResult = this.resolveSync(specifier, parentURL, importAttributes); @@ -678,7 +681,7 @@ class ModuleLoader { /** * Similar to {@link resolve}, but the results are always synchronously returned. If there are any * asynchronous resolve hooks from module.register(), it will block until the results are returned - * from the loader thread for this to be synchornous. + * from the loader thread for this to be synchronous. * This is here to support `import.meta.resolve()`, `require()` in imported CJS, and * `module.registerHooks()` hooks. * From 3946f1678681f89f37d9e42d67e51dfcadb6c817 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 10 Jan 2025 15:40:28 +0100 Subject: [PATCH 018/208] process: fix symbol key and mark experimental new `node:process` methods PR-URL: https://github.com/nodejs/node/pull/56517 Reviewed-By: Anna Henningsen Reviewed-By: Chengzhong Wu Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- doc/api/process.md | 12 ++++++++---- lib/internal/process/per_thread.js | 6 ++++-- test/parallel/test-process-ref-unref.js | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/doc/api/process.md b/doc/api/process.md index 1bad26bc6ca320..95b35897f9d568 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -3248,11 +3248,13 @@ console.log(`The parent process is pid ${ppid}`); added: v23.6.0 --> +> Stability: 1 - Experimental + * `maybeRefable` {any} An object that may be "refable". An object is "refable" if it implements the Node.js "Refable protocol". -Specifically, this means that the object implements the `Symbol.for('node:ref')` -and `Symbol.for('node:unref')` methods. "Ref'd" objects will keep the Node.js +Specifically, this means that the object implements the `Symbol.for('nodejs.ref')` +and `Symbol.for('nodejs.unref')` methods. "Ref'd" objects will keep the Node.js event loop alive, while "unref'd" objects will not. Historically, this was implemented by using `ref()` and `unref()` methods directly on the objects. This pattern, however, is being deprecated in favor of the "Refable protocol" @@ -4307,11 +4309,13 @@ In [`Worker`][] threads, `process.umask(mask)` will throw an exception. added: v23.6.0 --> +> Stability: 1 - Experimental + * `maybeUnfefable` {any} An object that may be "unref'd". An object is "unrefable" if it implements the Node.js "Refable protocol". -Specifically, this means that the object implements the `Symbol.for('node:ref')` -and `Symbol.for('node:unref')` methods. "Ref'd" objects will keep the Node.js +Specifically, this means that the object implements the `Symbol.for('nodejs.ref')` +and `Symbol.for('nodejs.unref')` methods. "Ref'd" objects will keep the Node.js event loop alive, while "unref'd" objects will not. Historically, this was implemented by using `ref()` and `unref()` methods directly on the objects. This pattern, however, is being deprecated in favor of the "Refable protocol" diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index 0921f583183d71..134e99e2374722 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -421,12 +421,14 @@ function toggleTraceCategoryState(asyncHooksEnabled) { const { arch, platform, version } = process; function ref(maybeRefable) { - const fn = maybeRefable?.[SymbolFor('node:ref')] || maybeRefable?.ref; + const fn = maybeRefable?.[SymbolFor('nodejs.ref')] || maybeRefable?.[SymbolFor('node:ref')] || maybeRefable?.ref; if (typeof fn === 'function') FunctionPrototypeCall(fn, maybeRefable); } function unref(maybeRefable) { - const fn = maybeRefable?.[SymbolFor('node:unref')] || maybeRefable?.unref; + const fn = maybeRefable?.[SymbolFor('nodejs.unref')] || + maybeRefable?.[SymbolFor('node:unref')] || + maybeRefable?.unref; if (typeof fn === 'function') FunctionPrototypeCall(fn, maybeRefable); } diff --git a/test/parallel/test-process-ref-unref.js b/test/parallel/test-process-ref-unref.js index e9db4d56eefc58..6bd508c1dbb9cb 100644 --- a/test/parallel/test-process-ref-unref.js +++ b/test/parallel/test-process-ref-unref.js @@ -23,6 +23,18 @@ class Foo { } class Foo2 { + refCalled = 0; + unrefCalled = 0; + [Symbol.for('nodejs.ref')]() { + this.refCalled++; + } + [Symbol.for('nodejs.unref')]() { + this.unrefCalled++; + } +} + +// TODO(aduh95): remove support for undocumented symbol +class Foo3 { refCalled = 0; unrefCalled = 0; [Symbol.for('node:ref')]() { @@ -39,14 +51,19 @@ describe('process.ref/unref work as expected', () => { // just work. const foo1 = new Foo(); const foo2 = new Foo2(); + const foo3 = new Foo3(); process.ref(foo1); process.unref(foo1); process.ref(foo2); process.unref(foo2); + process.ref(foo3); + process.unref(foo3); strictEqual(foo1.refCalled, 1); strictEqual(foo1.unrefCalled, 1); strictEqual(foo2.refCalled, 1); strictEqual(foo2.unrefCalled, 1); + strictEqual(foo3.refCalled, 1); + strictEqual(foo3.unrefCalled, 1); // Objects that implement the legacy API also just work. const i = setInterval(() => {}, 1000); From 25b22e4754121942194c662f78559d644503fbfa Mon Sep 17 00:00:00 2001 From: npm CLI robot Date: Fri, 10 Jan 2025 08:20:27 -0800 Subject: [PATCH 019/208] deps: upgrade npm to 11.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56274 Reviewed-By: Jordan Harband Reviewed-By: Luigi Pinca Reviewed-By: Michaël Zasso Reviewed-By: Rafael Gonzaga --- deps/npm/docs/content/commands/npm-hook.md | 112 -- deps/npm/docs/content/commands/npm-ls.md | 30 +- deps/npm/docs/content/commands/npm-pack.md | 14 + deps/npm/docs/content/commands/npm-prefix.md | 2 +- deps/npm/docs/content/commands/npm-publish.md | 3 + deps/npm/docs/content/commands/npm.md | 2 +- .../npm/docs/content/configuring-npm/npmrc.md | 2 +- .../content/configuring-npm/package-json.md | 14 + deps/npm/docs/content/using-npm/config.md | 12 +- deps/npm/docs/content/using-npm/developers.md | 7 +- deps/npm/docs/output/commands/npm-access.html | 4 +- .../npm/docs/output/commands/npm-adduser.html | 4 +- deps/npm/docs/output/commands/npm-audit.html | 4 +- deps/npm/docs/output/commands/npm-bugs.html | 4 +- deps/npm/docs/output/commands/npm-cache.html | 4 +- deps/npm/docs/output/commands/npm-ci.html | 4 +- .../docs/output/commands/npm-completion.html | 4 +- deps/npm/docs/output/commands/npm-config.html | 4 +- deps/npm/docs/output/commands/npm-dedupe.html | 4 +- .../docs/output/commands/npm-deprecate.html | 4 +- deps/npm/docs/output/commands/npm-diff.html | 4 +- .../docs/output/commands/npm-dist-tag.html | 4 +- deps/npm/docs/output/commands/npm-docs.html | 4 +- deps/npm/docs/output/commands/npm-doctor.html | 4 +- deps/npm/docs/output/commands/npm-edit.html | 4 +- deps/npm/docs/output/commands/npm-exec.html | 4 +- .../npm/docs/output/commands/npm-explain.html | 4 +- .../npm/docs/output/commands/npm-explore.html | 4 +- .../docs/output/commands/npm-find-dupes.html | 4 +- deps/npm/docs/output/commands/npm-fund.html | 4 +- .../docs/output/commands/npm-help-search.html | 4 +- deps/npm/docs/output/commands/npm-help.html | 4 +- deps/npm/docs/output/commands/npm-hook.html | 234 --- deps/npm/docs/output/commands/npm-init.html | 4 +- .../output/commands/npm-install-ci-test.html | 4 +- .../output/commands/npm-install-test.html | 4 +- .../npm/docs/output/commands/npm-install.html | 4 +- deps/npm/docs/output/commands/npm-link.html | 4 +- deps/npm/docs/output/commands/npm-login.html | 4 +- deps/npm/docs/output/commands/npm-logout.html | 4 +- deps/npm/docs/output/commands/npm-ls.html | 29 +- deps/npm/docs/output/commands/npm-org.html | 4 +- .../docs/output/commands/npm-outdated.html | 4 +- deps/npm/docs/output/commands/npm-owner.html | 4 +- deps/npm/docs/output/commands/npm-pack.html | 16 +- deps/npm/docs/output/commands/npm-ping.html | 4 +- deps/npm/docs/output/commands/npm-pkg.html | 4 +- deps/npm/docs/output/commands/npm-prefix.html | 6 +- .../npm/docs/output/commands/npm-profile.html | 4 +- deps/npm/docs/output/commands/npm-prune.html | 4 +- .../npm/docs/output/commands/npm-publish.html | 6 +- deps/npm/docs/output/commands/npm-query.html | 4 +- .../npm/docs/output/commands/npm-rebuild.html | 4 +- deps/npm/docs/output/commands/npm-repo.html | 4 +- .../npm/docs/output/commands/npm-restart.html | 4 +- deps/npm/docs/output/commands/npm-root.html | 4 +- .../docs/output/commands/npm-run-script.html | 4 +- deps/npm/docs/output/commands/npm-sbom.html | 4 +- deps/npm/docs/output/commands/npm-search.html | 4 +- .../docs/output/commands/npm-shrinkwrap.html | 4 +- deps/npm/docs/output/commands/npm-star.html | 4 +- deps/npm/docs/output/commands/npm-stars.html | 4 +- deps/npm/docs/output/commands/npm-start.html | 4 +- deps/npm/docs/output/commands/npm-stop.html | 4 +- deps/npm/docs/output/commands/npm-team.html | 4 +- deps/npm/docs/output/commands/npm-test.html | 4 +- deps/npm/docs/output/commands/npm-token.html | 4 +- .../docs/output/commands/npm-uninstall.html | 4 +- .../docs/output/commands/npm-unpublish.html | 4 +- deps/npm/docs/output/commands/npm-unstar.html | 4 +- deps/npm/docs/output/commands/npm-update.html | 4 +- .../npm/docs/output/commands/npm-version.html | 4 +- deps/npm/docs/output/commands/npm-view.html | 4 +- deps/npm/docs/output/commands/npm-whoami.html | 4 +- deps/npm/docs/output/commands/npm.html | 6 +- deps/npm/docs/output/commands/npx.html | 4 +- .../docs/output/configuring-npm/folders.html | 4 +- .../docs/output/configuring-npm/install.html | 4 +- .../output/configuring-npm/npm-global.html | 4 +- .../docs/output/configuring-npm/npm-json.html | 16 +- .../configuring-npm/npm-shrinkwrap-json.html | 4 +- .../docs/output/configuring-npm/npmrc.html | 6 +- .../output/configuring-npm/package-json.html | 16 +- .../configuring-npm/package-lock-json.html | 4 +- deps/npm/docs/output/using-npm/config.html | 16 +- .../using-npm/dependency-selectors.html | 4 +- .../npm/docs/output/using-npm/developers.html | 10 +- deps/npm/docs/output/using-npm/logging.html | 4 +- deps/npm/docs/output/using-npm/orgs.html | 4 +- .../docs/output/using-npm/package-spec.html | 4 +- deps/npm/docs/output/using-npm/registry.html | 4 +- deps/npm/docs/output/using-npm/removal.html | 4 +- deps/npm/docs/output/using-npm/scope.html | 4 +- deps/npm/docs/output/using-npm/scripts.html | 4 +- .../npm/docs/output/using-npm/workspaces.html | 4 +- deps/npm/lib/cli/entry.js | 5 - deps/npm/lib/commands/cache.js | 4 +- deps/npm/lib/commands/deprecate.js | 6 +- deps/npm/lib/commands/diff.js | 10 +- deps/npm/lib/commands/dist-tag.js | 10 +- deps/npm/lib/commands/doctor.js | 6 +- deps/npm/lib/commands/explain.js | 2 +- deps/npm/lib/commands/hook.js | 109 -- deps/npm/lib/commands/init.js | 2 +- deps/npm/lib/commands/install.js | 4 +- deps/npm/lib/commands/ls.js | 2 +- deps/npm/lib/commands/pack.js | 4 +- deps/npm/lib/commands/pkg.js | 6 +- deps/npm/lib/commands/prefix.js | 1 - deps/npm/lib/commands/publish.js | 37 + deps/npm/lib/commands/repo.js | 2 +- deps/npm/lib/commands/star.js | 6 +- deps/npm/lib/commands/stars.js | 4 +- deps/npm/lib/commands/view.js | 20 +- deps/npm/lib/utils/cmd-list.js | 1 - deps/npm/lib/utils/ping.js | 4 +- deps/npm/lib/utils/sbom-cyclonedx.js | 4 +- deps/npm/lib/utils/sbom-spdx.js | 2 +- deps/npm/lib/utils/verify-signatures.js | 8 +- deps/npm/man/man1/npm-access.1 | 2 +- deps/npm/man/man1/npm-adduser.1 | 2 +- deps/npm/man/man1/npm-audit.1 | 2 +- deps/npm/man/man1/npm-bugs.1 | 2 +- deps/npm/man/man1/npm-cache.1 | 2 +- deps/npm/man/man1/npm-ci.1 | 2 +- deps/npm/man/man1/npm-completion.1 | 2 +- deps/npm/man/man1/npm-config.1 | 2 +- deps/npm/man/man1/npm-dedupe.1 | 2 +- deps/npm/man/man1/npm-deprecate.1 | 2 +- deps/npm/man/man1/npm-diff.1 | 2 +- deps/npm/man/man1/npm-dist-tag.1 | 2 +- deps/npm/man/man1/npm-docs.1 | 2 +- deps/npm/man/man1/npm-doctor.1 | 2 +- deps/npm/man/man1/npm-edit.1 | 2 +- deps/npm/man/man1/npm-exec.1 | 2 +- deps/npm/man/man1/npm-explain.1 | 2 +- deps/npm/man/man1/npm-explore.1 | 2 +- deps/npm/man/man1/npm-find-dupes.1 | 2 +- deps/npm/man/man1/npm-fund.1 | 2 +- deps/npm/man/man1/npm-help-search.1 | 2 +- deps/npm/man/man1/npm-help.1 | 2 +- deps/npm/man/man1/npm-hook.1 | 115 -- deps/npm/man/man1/npm-init.1 | 2 +- deps/npm/man/man1/npm-install-ci-test.1 | 2 +- deps/npm/man/man1/npm-install-test.1 | 2 +- deps/npm/man/man1/npm-install.1 | 2 +- deps/npm/man/man1/npm-link.1 | 2 +- deps/npm/man/man1/npm-login.1 | 2 +- deps/npm/man/man1/npm-logout.1 | 2 +- deps/npm/man/man1/npm-ls.1 | 16 +- deps/npm/man/man1/npm-org.1 | 2 +- deps/npm/man/man1/npm-outdated.1 | 2 +- deps/npm/man/man1/npm-owner.1 | 2 +- deps/npm/man/man1/npm-pack.1 | 14 +- deps/npm/man/man1/npm-ping.1 | 2 +- deps/npm/man/man1/npm-pkg.1 | 2 +- deps/npm/man/man1/npm-prefix.1 | 4 +- deps/npm/man/man1/npm-profile.1 | 2 +- deps/npm/man/man1/npm-prune.1 | 2 +- deps/npm/man/man1/npm-publish.1 | 4 +- deps/npm/man/man1/npm-query.1 | 2 +- deps/npm/man/man1/npm-rebuild.1 | 2 +- deps/npm/man/man1/npm-repo.1 | 2 +- deps/npm/man/man1/npm-restart.1 | 2 +- deps/npm/man/man1/npm-root.1 | 2 +- deps/npm/man/man1/npm-run-script.1 | 2 +- deps/npm/man/man1/npm-sbom.1 | 2 +- deps/npm/man/man1/npm-search.1 | 2 +- deps/npm/man/man1/npm-shrinkwrap.1 | 2 +- deps/npm/man/man1/npm-star.1 | 2 +- deps/npm/man/man1/npm-stars.1 | 2 +- deps/npm/man/man1/npm-start.1 | 2 +- deps/npm/man/man1/npm-stop.1 | 2 +- deps/npm/man/man1/npm-team.1 | 2 +- deps/npm/man/man1/npm-test.1 | 2 +- deps/npm/man/man1/npm-token.1 | 2 +- deps/npm/man/man1/npm-uninstall.1 | 2 +- deps/npm/man/man1/npm-unpublish.1 | 2 +- deps/npm/man/man1/npm-unstar.1 | 2 +- deps/npm/man/man1/npm-update.1 | 2 +- deps/npm/man/man1/npm-version.1 | 2 +- deps/npm/man/man1/npm-view.1 | 2 +- deps/npm/man/man1/npm-whoami.1 | 2 +- deps/npm/man/man1/npm.1 | 4 +- deps/npm/man/man1/npx.1 | 2 +- deps/npm/man/man5/folders.5 | 2 +- deps/npm/man/man5/install.5 | 2 +- deps/npm/man/man5/npm-global.5 | 2 +- deps/npm/man/man5/npm-json.5 | 18 +- deps/npm/man/man5/npm-shrinkwrap-json.5 | 2 +- deps/npm/man/man5/npmrc.5 | 4 +- deps/npm/man/man5/package-json.5 | 18 +- deps/npm/man/man5/package-lock-json.5 | 2 +- deps/npm/man/man7/config.7 | 8 +- deps/npm/man/man7/dependency-selectors.7 | 2 +- deps/npm/man/man7/developers.7 | 6 +- deps/npm/man/man7/logging.7 | 2 +- deps/npm/man/man7/orgs.7 | 2 +- deps/npm/man/man7/package-spec.7 | 2 +- deps/npm/man/man7/registry.7 | 2 +- deps/npm/man/man7/removal.7 | 2 +- deps/npm/man/man7/scope.7 | 2 +- deps/npm/man/man7/scripts.7 | 2 +- deps/npm/man/man7/workspaces.7 | 2 +- .../@npmcli/arborist/lib/arborist/rebuild.js | 4 +- .../@npmcli/arborist/lib/audit-report.js | 104 +- .../arborist/lib/query-selector-all.js | 4 +- .../@npmcli/arborist/package.json | 18 +- .../config/lib/definitions/definitions.js | 10 +- .../node_modules/@npmcli/config/package.json | 10 +- .../node_modules/pacote/LICENSE | 15 - .../node_modules/pacote/README.md | 283 --- .../node_modules/pacote/bin/index.js | 158 -- .../node_modules/pacote/lib/dir.js | 105 - .../node_modules/pacote/lib/fetcher.js | 497 ----- .../node_modules/pacote/lib/file.js | 94 - .../node_modules/pacote/lib/git.js | 317 --- .../node_modules/pacote/lib/index.js | 23 - .../node_modules/pacote/lib/registry.js | 369 ---- .../node_modules/pacote/lib/remote.js | 89 - .../pacote/lib/util/add-git-sha.js | 15 - .../node_modules/pacote/lib/util/cache-dir.js | 15 - .../pacote/lib/util/is-package-bin.js | 25 - .../node_modules/pacote/lib/util/npm.js | 14 - .../node_modules/pacote/lib/util/protected.js | 5 - .../pacote/lib/util/tar-create-options.js | 31 - .../pacote/lib/util/trailing-slashes.js | 10 - .../node_modules/pacote/package.json | 79 - .../@npmcli/metavuln-calculator/package.json | 10 +- .../@sigstore/bundle/LICENSE | 0 .../@sigstore/bundle/dist/build.js | 0 .../@sigstore/bundle/dist/bundle.js | 0 .../@sigstore/bundle/dist/error.js | 0 .../@sigstore/bundle/dist/index.js | 0 .../@sigstore/bundle/dist/serialized.js | 0 .../@sigstore/bundle/dist/utility.js | 0 .../@sigstore/bundle/dist/validate.js | 0 .../@sigstore/bundle/package.json | 0 .../node_modules => }/@sigstore/core/LICENSE | 0 .../@sigstore/core/dist/asn1/error.js | 0 .../@sigstore/core/dist/asn1/index.js | 0 .../@sigstore/core/dist/asn1/length.js | 0 .../@sigstore/core/dist/asn1/obj.js | 0 .../@sigstore/core/dist/asn1/parse.js | 0 .../@sigstore/core/dist/asn1/tag.js | 0 .../@sigstore/core/dist/crypto.js | 0 .../@sigstore/core/dist/dsse.js | 0 .../@sigstore/core/dist/encoding.js | 0 .../@sigstore/core/dist/index.js | 0 .../@sigstore/core/dist/json.js | 0 .../@sigstore/core/dist/oid.js | 0 .../@sigstore/core/dist/pem.js | 0 .../@sigstore/core/dist/rfc3161/error.js | 0 .../@sigstore/core/dist/rfc3161/index.js | 0 .../@sigstore/core/dist/rfc3161/timestamp.js | 0 .../@sigstore/core/dist/rfc3161/tstinfo.js | 0 .../@sigstore/core/dist/stream.js | 0 .../@sigstore/core/dist/x509/cert.js | 0 .../@sigstore/core/dist/x509/ext.js | 0 .../@sigstore/core/dist/x509/index.js | 0 .../@sigstore/core/dist/x509/sct.js | 0 .../@sigstore/core/package.json | 0 .../node_modules => }/@sigstore/sign/LICENSE | 0 .../@sigstore/sign/dist/bundler/base.js | 0 .../@sigstore/sign/dist/bundler/bundle.js | 0 .../@sigstore/sign/dist/bundler/dsse.js | 0 .../@sigstore/sign/dist/bundler/index.js | 0 .../@sigstore/sign/dist/bundler/message.js | 0 .../@sigstore/sign/dist/error.js | 0 .../@sigstore/sign/dist/external/error.js | 0 .../@sigstore/sign/dist/external/fetch.js | 0 .../@sigstore/sign/dist/external/fulcio.js | 0 .../@sigstore/sign/dist/external/rekor.js | 0 .../@sigstore/sign/dist/external/tsa.js | 0 .../@sigstore/sign/dist/identity/ci.js | 0 .../@sigstore/sign/dist/identity/index.js | 0 .../@sigstore/sign/dist/identity/provider.js | 0 .../@sigstore/sign/dist/index.js | 0 .../@sigstore/sign/dist/signer/fulcio/ca.js | 0 .../sign/dist/signer/fulcio/ephemeral.js | 0 .../sign/dist/signer/fulcio/index.js | 0 .../@sigstore/sign/dist/signer/index.js | 0 .../@sigstore/sign/dist/signer/signer.js | 0 .../@sigstore/sign/dist/types/fetch.js | 0 .../@sigstore/sign/dist/util/index.js | 0 .../@sigstore/sign/dist/util/oidc.js | 0 .../@sigstore/sign/dist/util/ua.js | 0 .../@sigstore/sign/dist/witness/index.js | 0 .../sign/dist/witness/tlog/client.js | 0 .../@sigstore/sign/dist/witness/tlog/entry.js | 0 .../@sigstore/sign/dist/witness/tlog/index.js | 0 .../@sigstore/sign/dist/witness/tsa/client.js | 0 .../@sigstore/sign/dist/witness/tsa/index.js | 0 .../@sigstore/sign/dist/witness/witness.js | 0 .../@sigstore/sign/package.json | 0 .../@sigstore/verify/dist/bundle/dsse.js | 0 .../@sigstore/verify/dist/bundle/index.js | 0 .../@sigstore/verify/dist/bundle/message.js | 0 .../@sigstore/verify/dist/error.js | 0 .../@sigstore/verify/dist/index.js | 0 .../@sigstore/verify/dist/key/certificate.js | 0 .../@sigstore/verify/dist/key/index.js | 0 .../@sigstore/verify/dist/key/sct.js | 0 .../@sigstore/verify/dist/policy.js | 0 .../@sigstore/verify/dist/shared.types.js | 0 .../verify/dist/timestamp/checkpoint.js | 0 .../@sigstore/verify/dist/timestamp/index.js | 0 .../@sigstore/verify/dist/timestamp/merkle.js | 0 .../@sigstore/verify/dist/timestamp/set.js | 0 .../@sigstore/verify/dist/timestamp/tsa.js | 0 .../@sigstore/verify/dist/tlog/dsse.js | 0 .../verify/dist/tlog/hashedrekord.js | 0 .../@sigstore/verify/dist/tlog/index.js | 0 .../@sigstore/verify/dist/tlog/intoto.js | 0 .../@sigstore/verify/dist/trust/filter.js | 0 .../@sigstore/verify/dist/trust/index.js | 0 .../verify/dist/trust/trust.types.js | 0 .../@sigstore/verify/dist/verifier.js | 0 .../@sigstore/verify/package.json | 0 .../node_modules => }/@tufjs/models/LICENSE | 0 .../@tufjs/models/dist/base.js | 0 .../@tufjs/models/dist/delegations.js | 0 .../@tufjs/models/dist/error.js | 0 .../@tufjs/models/dist/file.js | 0 .../@tufjs/models/dist/index.js | 0 .../@tufjs/models/dist/key.js | 0 .../@tufjs/models/dist/metadata.js | 0 .../@tufjs/models/dist/role.js | 0 .../@tufjs/models/dist/root.js | 0 .../@tufjs/models/dist/signature.js | 0 .../@tufjs/models/dist/snapshot.js | 0 .../@tufjs/models/dist/targets.js | 0 .../@tufjs/models/dist/timestamp.js | 0 .../@tufjs/models/dist/utils/guard.js | 0 .../@tufjs/models/dist/utils/index.js | 0 .../@tufjs/models/dist/utils/key.js | 0 .../@tufjs/models/dist/utils/oid.js | 0 .../@tufjs/models/dist/utils/types.js | 0 .../@tufjs/models/dist/utils/verify.js | 0 .../@tufjs/models/package.json | 0 .../npm/node_modules/agent-base/dist/index.js | 9 +- deps/npm/node_modules/agent-base/package.json | 7 +- .../npm/node_modules/aggregate-error/index.js | 47 - deps/npm/node_modules/aggregate-error/license | 9 - .../node_modules/aggregate-error/package.json | 41 - .../node_modules/binary-extensions/index.js | 4 +- .../binary-extensions/package.json | 21 +- .../cacache/node_modules/p-map/index.js | 269 --- .../cacache/node_modules/p-map/license | 9 - .../cacache/node_modules/p-map/package.json | 57 - deps/npm/node_modules/clean-stack/index.js | 40 - deps/npm/node_modules/clean-stack/license | 9 - .../npm/node_modules/clean-stack/package.json | 39 - deps/npm/node_modules/debug/package.json | 7 +- deps/npm/node_modules/debug/src/browser.js | 1 + deps/npm/node_modules/debug/src/common.js | 96 +- deps/npm/node_modules/diff/CONTRIBUTING.md | 10 +- deps/npm/node_modules/diff/dist/diff.js | 1720 ++++++++++------- deps/npm/node_modules/diff/dist/diff.min.js | 38 +- deps/npm/node_modules/diff/lib/convert/dmp.js | 11 +- deps/npm/node_modules/diff/lib/convert/xml.js | 9 +- deps/npm/node_modules/diff/lib/diff/array.js | 20 +- deps/npm/node_modules/diff/lib/diff/base.js | 188 +- .../node_modules/diff/lib/diff/character.js | 18 +- deps/npm/node_modules/diff/lib/diff/css.js | 19 +- deps/npm/node_modules/diff/lib/diff/json.js | 94 +- deps/npm/node_modules/diff/lib/diff/line.js | 79 +- .../node_modules/diff/lib/diff/sentence.js | 17 +- deps/npm/node_modules/diff/lib/diff/word.js | 525 ++++- deps/npm/node_modules/diff/lib/index.es6.js | 1676 +++++++++------- deps/npm/node_modules/diff/lib/index.js | 103 +- deps/npm/node_modules/diff/lib/index.mjs | 1676 +++++++++------- deps/npm/node_modules/diff/lib/patch/apply.js | 411 ++-- .../npm/node_modules/diff/lib/patch/create.js | 497 +++-- .../diff/lib/patch/line-endings.js | 176 ++ deps/npm/node_modules/diff/lib/patch/merge.js | 186 +- deps/npm/node_modules/diff/lib/patch/parse.js | 130 +- .../node_modules/diff/lib/patch/reverse.js | 19 +- deps/npm/node_modules/diff/lib/util/array.js | 7 +- .../diff/lib/util/distance-iterator.js | 33 +- deps/npm/node_modules/diff/lib/util/params.js | 4 +- deps/npm/node_modules/diff/lib/util/string.js | 131 ++ deps/npm/node_modules/diff/package.json | 51 +- deps/npm/node_modules/diff/release-notes.md | 46 +- .../https-proxy-agent/dist/index.js | 21 +- .../https-proxy-agent/package.json | 4 +- deps/npm/node_modules/indent-string/index.js | 35 - deps/npm/node_modules/indent-string/license | 9 - .../node_modules/indent-string/package.json | 37 - .../init-package-json/lib/default-input.js | 5 + .../lib/init-package-json.js | 2 +- .../init-package-json/package.json | 12 +- .../node_modules/libnpmaccess/package.json | 9 +- .../libnpmdiff/lib/format-diff.js | 4 +- .../libnpmdiff/lib/should-print-patch.js | 6 +- deps/npm/node_modules/libnpmdiff/package.json | 16 +- deps/npm/node_modules/libnpmexec/lib/index.js | 2 +- deps/npm/node_modules/libnpmexec/package.json | 14 +- deps/npm/node_modules/libnpmfund/package.json | 10 +- deps/npm/node_modules/libnpmhook/LICENSE.md | 16 - deps/npm/node_modules/libnpmhook/README.md | 271 --- deps/npm/node_modules/libnpmhook/lib/index.js | 70 - deps/npm/node_modules/libnpmhook/package.json | 57 - deps/npm/node_modules/libnpmorg/lib/index.js | 8 +- deps/npm/node_modules/libnpmorg/package.json | 8 +- deps/npm/node_modules/libnpmpack/lib/index.js | 2 +- deps/npm/node_modules/libnpmpack/package.json | 12 +- .../node_modules/libnpmpublish/lib/publish.js | 2 +- .../node_modules/libnpmpublish/package.json | 9 +- .../node_modules/libnpmsearch/package.json | 8 +- deps/npm/node_modules/libnpmteam/package.json | 8 +- .../node_modules/libnpmversion/package.json | 8 +- .../node_modules/npm-package-arg/lib/npa.js | 5 +- .../node_modules/npm-package-arg/package.json | 6 +- .../node_modules/npm-packlist/lib/index.js | 1 + .../node_modules/npm-packlist/package.json | 12 +- deps/npm/node_modules/p-map/index.js | 256 ++- deps/npm/node_modules/p-map/package.json | 28 +- deps/npm/node_modules/pacote/lib/dir.js | 3 + deps/npm/node_modules/pacote/package.json | 13 +- .../socks-proxy-agent/dist/index.js | 16 +- .../socks-proxy-agent/package.json | 4 +- deps/npm/node_modules/sprintf-js/bower.json | 14 - .../node_modules/sprintf-js/demo/angular.html | 20 - deps/npm/node_modules/sprintf-js/gruntfile.js | 36 - deps/npm/node_modules/sprintf-js/test/test.js | 82 - deps/npm/node_modules/walk-up-path/LICENSE | 2 +- .../dist/{cjs => commonjs}/index.js | 0 .../dist/{cjs => commonjs}/package.json | 0 .../walk-up-path/dist/{mjs => esm}/index.js | 0 .../dist/{mjs => esm}/package.json | 0 .../node_modules/walk-up-path/package.json | 75 +- deps/npm/package.json | 54 +- .../test/lib/commands/audit.js.test.cjs | 15 - .../test/lib/commands/completion.js.test.cjs | 1 - .../test/lib/commands/publish.js.test.cjs | 1 - .../test/lib/commands/search.js.test.cjs | 82 - .../test/lib/commands/view.js.test.cjs | 74 +- .../tap-snapshots/test/lib/docs.js.test.cjs | 47 +- .../tap-snapshots/test/lib/npm.js.test.cjs | 76 +- deps/npm/test/fixtures/git-test.tgz | Bin 0 -> 268 bytes .../fixtures/libnpmsearch-stream-result.js | 22 - deps/npm/test/fixtures/mock-npm.js | 20 +- deps/npm/test/lib/cli/entry.js | 8 +- deps/npm/test/lib/commands/audit.js | 48 - deps/npm/test/lib/commands/exec.js | 24 + deps/npm/test/lib/commands/hook.js | 640 ------ deps/npm/test/lib/commands/pkg.js | 20 + deps/npm/test/lib/commands/publish.js | 530 +++-- deps/npm/test/lib/commands/view.js | 15 + 450 files changed, 6414 insertions(+), 8837 deletions(-) delete mode 100644 deps/npm/docs/content/commands/npm-hook.md delete mode 100644 deps/npm/docs/output/commands/npm-hook.html delete mode 100644 deps/npm/lib/commands/hook.js delete mode 100644 deps/npm/man/man1/npm-hook.1 delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/LICENSE delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/README.md delete mode 100755 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/bin/index.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/dir.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/fetcher.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/file.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/git.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/index.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/registry.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/remote.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/add-git-sha.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/cache-dir.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/is-package-bin.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/npm.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/protected.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/tar-create-options.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/trailing-slashes.js delete mode 100644 deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/package.json rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/bundle/LICENSE (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/bundle/dist/build.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/bundle/dist/bundle.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/bundle/dist/error.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/bundle/dist/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/bundle/dist/serialized.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/bundle/dist/utility.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/bundle/dist/validate.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/bundle/package.json (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/LICENSE (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/asn1/error.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/asn1/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/asn1/length.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/asn1/obj.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/asn1/parse.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/asn1/tag.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/crypto.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/dsse.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/encoding.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/json.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/oid.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/pem.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/rfc3161/error.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/rfc3161/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/rfc3161/timestamp.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/rfc3161/tstinfo.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/stream.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/x509/cert.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/x509/ext.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/x509/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/dist/x509/sct.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/core/package.json (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/LICENSE (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/bundler/base.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/bundler/bundle.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/bundler/dsse.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/bundler/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/bundler/message.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/error.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/external/error.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/external/fetch.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/external/fulcio.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/external/rekor.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/external/tsa.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/identity/ci.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/identity/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/identity/provider.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/signer/fulcio/ca.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/signer/fulcio/ephemeral.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/signer/fulcio/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/signer/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/signer/signer.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/types/fetch.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/util/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/util/oidc.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/util/ua.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/witness/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/witness/tlog/client.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/witness/tlog/entry.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/witness/tlog/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/witness/tsa/client.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/witness/tsa/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/dist/witness/witness.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/sign/package.json (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/bundle/dsse.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/bundle/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/bundle/message.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/error.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/key/certificate.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/key/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/key/sct.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/policy.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/shared.types.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/timestamp/checkpoint.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/timestamp/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/timestamp/merkle.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/timestamp/set.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/timestamp/tsa.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/tlog/dsse.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/tlog/hashedrekord.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/tlog/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/tlog/intoto.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/trust/filter.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/trust/index.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/trust/trust.types.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/dist/verifier.js (100%) rename deps/npm/node_modules/{sigstore/node_modules => }/@sigstore/verify/package.json (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/LICENSE (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/base.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/delegations.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/error.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/file.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/index.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/key.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/metadata.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/role.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/root.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/signature.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/snapshot.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/targets.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/timestamp.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/utils/guard.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/utils/index.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/utils/key.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/utils/oid.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/utils/types.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/dist/utils/verify.js (100%) rename deps/npm/node_modules/{tuf-js/node_modules => }/@tufjs/models/package.json (100%) delete mode 100644 deps/npm/node_modules/aggregate-error/index.js delete mode 100644 deps/npm/node_modules/aggregate-error/license delete mode 100644 deps/npm/node_modules/aggregate-error/package.json delete mode 100644 deps/npm/node_modules/cacache/node_modules/p-map/index.js delete mode 100644 deps/npm/node_modules/cacache/node_modules/p-map/license delete mode 100644 deps/npm/node_modules/cacache/node_modules/p-map/package.json delete mode 100644 deps/npm/node_modules/clean-stack/index.js delete mode 100644 deps/npm/node_modules/clean-stack/license delete mode 100644 deps/npm/node_modules/clean-stack/package.json create mode 100644 deps/npm/node_modules/diff/lib/patch/line-endings.js create mode 100644 deps/npm/node_modules/diff/lib/util/string.js delete mode 100644 deps/npm/node_modules/indent-string/index.js delete mode 100644 deps/npm/node_modules/indent-string/license delete mode 100644 deps/npm/node_modules/indent-string/package.json delete mode 100644 deps/npm/node_modules/libnpmhook/LICENSE.md delete mode 100644 deps/npm/node_modules/libnpmhook/README.md delete mode 100644 deps/npm/node_modules/libnpmhook/lib/index.js delete mode 100644 deps/npm/node_modules/libnpmhook/package.json delete mode 100644 deps/npm/node_modules/sprintf-js/bower.json delete mode 100644 deps/npm/node_modules/sprintf-js/demo/angular.html delete mode 100644 deps/npm/node_modules/sprintf-js/gruntfile.js delete mode 100644 deps/npm/node_modules/sprintf-js/test/test.js rename deps/npm/node_modules/walk-up-path/dist/{cjs => commonjs}/index.js (100%) rename deps/npm/node_modules/walk-up-path/dist/{cjs => commonjs}/package.json (100%) rename deps/npm/node_modules/walk-up-path/dist/{mjs => esm}/index.js (100%) rename deps/npm/node_modules/walk-up-path/dist/{mjs => esm}/package.json (100%) create mode 100644 deps/npm/test/fixtures/git-test.tgz delete mode 100644 deps/npm/test/lib/commands/hook.js diff --git a/deps/npm/docs/content/commands/npm-hook.md b/deps/npm/docs/content/commands/npm-hook.md deleted file mode 100644 index 581e78661d6c2f..00000000000000 --- a/deps/npm/docs/content/commands/npm-hook.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: npm-hook -section: 1 -description: Manage registry hooks ---- - -### Synopsis - -```bash -npm hook add [--type=] -npm hook ls [pkg] -npm hook rm -npm hook update -``` - -Note: This command is unaware of workspaces. - -### Description - -Allows you to manage [npm -hooks](https://blog.npmjs.org/post/145260155635/introducing-hooks-get-notifications-of-npm), -including adding, removing, listing, and updating. - -Hooks allow you to configure URL endpoints that will be notified whenever a -change happens to any of the supported entity types. Three different types -of entities can be watched by hooks: packages, owners, and scopes. - -To create a package hook, simply reference the package name. - -To create an owner hook, prefix the owner name with `~` (as in, -`~youruser`). - -To create a scope hook, prefix the scope name with `@` (as in, -`@yourscope`). - -The hook `id` used by `update` and `rm` are the IDs listed in `npm hook ls` -for that particular hook. - -The shared secret will be sent along to the URL endpoint so you can verify -the request came from your own configured hook. - -### Example - -Add a hook to watch a package for changes: - -```bash -$ npm hook add lodash https://example.com/ my-shared-secret -``` - -Add a hook to watch packages belonging to the user `substack`: - -```bash -$ npm hook add ~substack https://example.com/ my-shared-secret -``` - -Add a hook to watch packages in the scope `@npm` - -```bash -$ npm hook add @npm https://example.com/ my-shared-secret -``` - -List all your active hooks: - -```bash -$ npm hook ls -``` - -List your active hooks for the `lodash` package: - -```bash -$ npm hook ls lodash -``` - -Update an existing hook's url: - -```bash -$ npm hook update id-deadbeef https://my-new-website.here/ -``` - -Remove a hook: - -```bash -$ npm hook rm id-deadbeef -``` - -### Configuration - -#### `registry` - -* Default: "https://registry.npmjs.org/" -* Type: URL - -The base URL of the npm registry. - - - -#### `otp` - -* Default: null -* Type: null or String - -This is a one-time password from a two-factor authenticator. It's needed -when publishing or changing package permissions with `npm access`. - -If not set, and a registry response fails with a challenge for a one-time -password, npm will prompt on the command line for one. - - - -### See Also - -* ["Introducing Hooks" blog post](https://blog.npmjs.org/post/145260155635/introducing-hooks-get-notifications-of-npm) diff --git a/deps/npm/docs/content/commands/npm-ls.md b/deps/npm/docs/content/commands/npm-ls.md index 69bc86f85f3508..1169da1f0973b0 100644 --- a/deps/npm/docs/content/commands/npm-ls.md +++ b/deps/npm/docs/content/commands/npm-ls.md @@ -27,7 +27,7 @@ packages will *also* show the paths to the specified packages. For example, running `npm ls promzard` in npm's source tree will show: ```bash -npm@10.9.2 /path/to/npm +npm@11.0.0 /path/to/npm └─┬ init-package-json@0.0.4 └── promzard@0.1.5 ``` @@ -43,34 +43,6 @@ dependencies, not the physical layout of your `node_modules` folder. When run as `ll` or `la`, it shows extended information by default. -### Note: Design Changes Pending - -The `npm ls` command's output and behavior made a _ton_ of sense when npm -created a `node_modules` folder that naively nested every dependency. In -such a case, the logical dependency graph and physical tree of packages on -disk would be roughly identical. - -With the advent of automatic install-time deduplication of dependencies in -npm v3, the `ls` output was modified to display the logical dependency -graph as a tree structure, since this was more useful to most users. -However, without using `npm ls -l`, it became impossible to show _where_ a -package was actually installed much of the time! - -With the advent of automatic installation of `peerDependencies` in npm v7, -this gets even more curious, as `peerDependencies` are logically -"underneath" their dependents in the dependency graph, but are always -physically at or above their location on disk. - -Also, in the years since npm got an `ls` command (in version 0.0.2!), -dependency graphs have gotten much larger as a general rule. Therefore, in -order to avoid dumping an excessive amount of content to the terminal, `npm -ls` now only shows the _top_ level dependencies, unless `--all` is -provided. - -A thorough re-examination of the use cases, intention, behavior, and output -of this command, is currently underway. Expect significant changes to at -least the default human-readable `npm ls` output in npm v8. - ### Configuration #### `all` diff --git a/deps/npm/docs/content/commands/npm-pack.md b/deps/npm/docs/content/commands/npm-pack.md index 2ef859786085f2..2d3e3453d36886 100644 --- a/deps/npm/docs/content/commands/npm-pack.md +++ b/deps/npm/docs/content/commands/npm-pack.md @@ -103,6 +103,20 @@ the specified workspaces, and not on the root project. This value is not exported to the environment for child processes. +#### `ignore-scripts` + +* Default: false +* Type: Boolean + +If true, npm does not run scripts specified in package.json files. + +Note that commands explicitly intended to run a particular script, such as +`npm start`, `npm stop`, `npm restart`, `npm test`, and `npm run-script` +will still run their intended script if `ignore-scripts` is set, but they +will *not* run any pre- or post-scripts. + + + ### Description For anything that's installable (that is, a package folder, tarball, diff --git a/deps/npm/docs/content/commands/npm-prefix.md b/deps/npm/docs/content/commands/npm-prefix.md index 7718ed34ff828c..ad584643a1ecec 100644 --- a/deps/npm/docs/content/commands/npm-prefix.md +++ b/deps/npm/docs/content/commands/npm-prefix.md @@ -7,7 +7,7 @@ description: Display prefix ### Synopsis ```bash -npm prefix [-g] +npm prefix ``` Note: This command is unaware of workspaces. diff --git a/deps/npm/docs/content/commands/npm-publish.md b/deps/npm/docs/content/commands/npm-publish.md index 6bff5ff1c30201..266038c306359c 100644 --- a/deps/npm/docs/content/commands/npm-publish.md +++ b/deps/npm/docs/content/commands/npm-publish.md @@ -85,6 +85,9 @@ See [`developers`](/using-npm/developers) for full details on what's included in the published package, as well as details on how the package is built. +See [`package.json`](/configuring-npm/package-json) for more info on +what can and can't be ignored. + ### Configuration #### `tag` diff --git a/deps/npm/docs/content/commands/npm.md b/deps/npm/docs/content/commands/npm.md index 029b9fa7631544..36398a3cb34f36 100644 --- a/deps/npm/docs/content/commands/npm.md +++ b/deps/npm/docs/content/commands/npm.md @@ -14,7 +14,7 @@ Note: This command is unaware of workspaces. ### Version -10.9.2 +11.0.0 ### Description diff --git a/deps/npm/docs/content/configuring-npm/npmrc.md b/deps/npm/docs/content/configuring-npm/npmrc.md index fb335b2d43da70..cd31ae886f1320 100644 --- a/deps/npm/docs/content/configuring-npm/npmrc.md +++ b/deps/npm/docs/content/configuring-npm/npmrc.md @@ -103,7 +103,7 @@ The full list is: - `username` - `_password` - `email` - - `certfile` (path to certificate file) + - `cafile` (path to certificate authority file) - `keyfile` (path to key file) In order to scope these values, they must be prefixed by a URI fragment. diff --git a/deps/npm/docs/content/configuring-npm/package-json.md b/deps/npm/docs/content/configuring-npm/package-json.md index 7e9daf1317717f..90fbcee64de783 100644 --- a/deps/npm/docs/content/configuring-npm/package-json.md +++ b/deps/npm/docs/content/configuring-npm/package-json.md @@ -324,6 +324,7 @@ Some files are always ignored by default: if you wish it to be published) * `pnpm-lock.yaml` * `yarn.lock` +* `bun.lockb` Most of these ignored files can be included specifically if included in the `files` globs. Exceptions to this are: @@ -334,6 +335,7 @@ the `files` globs. Exceptions to this are: * `package-lock.json` * `pnpm-lock.yaml` * `yarn.lock` +* `bun.lockb` These can not be included. @@ -1129,6 +1131,18 @@ Like the `os` option, you can also block architectures: The host architecture is determined by `process.arch` +### libc + +If your code only runs or builds in certain versions of libc, you can +specify which ones. This field only applies if `os` is `linux`. + +```json +{ + "os": "linux", + "libc": "glibc" +} +``` + ### devEngines The `devEngines` field aids engineers working on a codebase to all be using the same tooling. diff --git a/deps/npm/docs/content/using-npm/config.md b/deps/npm/docs/content/using-npm/config.md index 6143e59d46a040..4209b9bb1ae57c 100644 --- a/deps/npm/docs/content/using-npm/config.md +++ b/deps/npm/docs/content/using-npm/config.md @@ -1833,9 +1833,9 @@ When set to `dev` or `development`, this is an alias for `--include=dev`. * Default: null * Type: null or String * DEPRECATED: `key` and `cert` are no longer used for most registry - operations. Use registry scoped `keyfile` and `certfile` instead. Example: + operations. Use registry scoped `keyfile` and `cafile` instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem - //other-registry.tld/:certfile=/path/to/cert.crt + //other-registry.tld/:cafile=/path/to/cert.crt A client certificate to pass when accessing the registry. Values should be in PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with @@ -1846,8 +1846,8 @@ cert="-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----" ``` It is _not_ the path to a certificate file, though you can set a -registry-scoped "certfile" path like -"//other-registry.tld/:certfile=/path/to/cert.pem". +registry-scoped "cafile" path like +"//other-registry.tld/:cafile=/path/to/cert.pem". @@ -1938,9 +1938,9 @@ Alias for `--init-version` * Default: null * Type: null or String * DEPRECATED: `key` and `cert` are no longer used for most registry - operations. Use registry scoped `keyfile` and `certfile` instead. Example: + operations. Use registry scoped `keyfile` and `cafile` instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem - //other-registry.tld/:certfile=/path/to/cert.crt + //other-registry.tld/:cafile=/path/to/cert.crt A client key to pass when accessing the registry. Values should be in PEM format with newlines replaced by the string "\n". For example: diff --git a/deps/npm/docs/content/using-npm/developers.md b/deps/npm/docs/content/using-npm/developers.md index 5fc2e5876e3dd3..b97ca038b4a4ba 100644 --- a/deps/npm/docs/content/using-npm/developers.md +++ b/deps/npm/docs/content/using-npm/developers.md @@ -112,8 +112,8 @@ as `.gitignore` files: * You can end patterns with a forward slash `/` to specify a directory. * You can negate a pattern by starting it with an exclamation point `!`. -By default, the following paths and files are ignored, so there's no -need to add them to `.npmignore` explicitly: +By default, some paths and files are ignored, so there's no +need to add them to `.npmignore` explicitly. Some examples are: * `.*.swp` * `._*` @@ -148,6 +148,9 @@ property of `package.json`, which is an array of file or directory names that should be included in your package. Sometimes manually picking which items to allow is easier to manage than building a block list. +See [`package.json`](/configuring-npm/package-json) for more info on +what can and can't be ignored. + #### Testing whether your `.npmignore` or `files` config works If you want to double check that your package will include only the files diff --git a/deps/npm/docs/output/commands/npm-access.html b/deps/npm/docs/output/commands/npm-access.html index 9ce3c00e3525d8..705489bba53296 100644 --- a/deps/npm/docs/output/commands/npm-access.html +++ b/deps/npm/docs/output/commands/npm-access.html @@ -141,9 +141,9 @@
-

+

npm-access - @10.9.2 + @11.0.0

Set access level on published packages
diff --git a/deps/npm/docs/output/commands/npm-adduser.html b/deps/npm/docs/output/commands/npm-adduser.html index 5f6627229d2c8b..c71dd30b1fd8b8 100644 --- a/deps/npm/docs/output/commands/npm-adduser.html +++ b/deps/npm/docs/output/commands/npm-adduser.html @@ -141,9 +141,9 @@
-

+

npm-adduser - @10.9.2 + @11.0.0

Add a registry user account
diff --git a/deps/npm/docs/output/commands/npm-audit.html b/deps/npm/docs/output/commands/npm-audit.html index a8934d172e3602..15c2d5129d1806 100644 --- a/deps/npm/docs/output/commands/npm-audit.html +++ b/deps/npm/docs/output/commands/npm-audit.html @@ -141,9 +141,9 @@
-

+

npm-audit - @10.9.2 + @11.0.0

Run a security audit
diff --git a/deps/npm/docs/output/commands/npm-bugs.html b/deps/npm/docs/output/commands/npm-bugs.html index 3ec74a05673259..6afee019696255 100644 --- a/deps/npm/docs/output/commands/npm-bugs.html +++ b/deps/npm/docs/output/commands/npm-bugs.html @@ -141,9 +141,9 @@
-

+

npm-bugs - @10.9.2 + @11.0.0

Report bugs for a package in a web browser
diff --git a/deps/npm/docs/output/commands/npm-cache.html b/deps/npm/docs/output/commands/npm-cache.html index 0a4fd00a276a47..dc0e1d7792d5bc 100644 --- a/deps/npm/docs/output/commands/npm-cache.html +++ b/deps/npm/docs/output/commands/npm-cache.html @@ -141,9 +141,9 @@
-

+

npm-cache - @10.9.2 + @11.0.0

Manipulates packages cache
diff --git a/deps/npm/docs/output/commands/npm-ci.html b/deps/npm/docs/output/commands/npm-ci.html index 3af67dc55c81db..da9bef01d51722 100644 --- a/deps/npm/docs/output/commands/npm-ci.html +++ b/deps/npm/docs/output/commands/npm-ci.html @@ -141,9 +141,9 @@
-

+

npm-ci - @10.9.2 + @11.0.0

Clean install a project
diff --git a/deps/npm/docs/output/commands/npm-completion.html b/deps/npm/docs/output/commands/npm-completion.html index 3f8b509e4ec8d6..687fd873263ce7 100644 --- a/deps/npm/docs/output/commands/npm-completion.html +++ b/deps/npm/docs/output/commands/npm-completion.html @@ -141,9 +141,9 @@
-

+

npm-completion - @10.9.2 + @11.0.0

Tab Completion for npm
diff --git a/deps/npm/docs/output/commands/npm-config.html b/deps/npm/docs/output/commands/npm-config.html index c9b9a2c7550bcc..6db882f847de76 100644 --- a/deps/npm/docs/output/commands/npm-config.html +++ b/deps/npm/docs/output/commands/npm-config.html @@ -141,9 +141,9 @@
-

+

npm-config - @10.9.2 + @11.0.0

Manage the npm configuration files
diff --git a/deps/npm/docs/output/commands/npm-dedupe.html b/deps/npm/docs/output/commands/npm-dedupe.html index 0aa8bf5a5bde77..598f7ff9472d6d 100644 --- a/deps/npm/docs/output/commands/npm-dedupe.html +++ b/deps/npm/docs/output/commands/npm-dedupe.html @@ -141,9 +141,9 @@
-

+

npm-dedupe - @10.9.2 + @11.0.0

Reduce duplication in the package tree
diff --git a/deps/npm/docs/output/commands/npm-deprecate.html b/deps/npm/docs/output/commands/npm-deprecate.html index 0019583ee2135a..88e4c4682407f7 100644 --- a/deps/npm/docs/output/commands/npm-deprecate.html +++ b/deps/npm/docs/output/commands/npm-deprecate.html @@ -141,9 +141,9 @@
-

+

npm-deprecate - @10.9.2 + @11.0.0

Deprecate a version of a package
diff --git a/deps/npm/docs/output/commands/npm-diff.html b/deps/npm/docs/output/commands/npm-diff.html index fe2123ee60fdc8..d682aff9b479c2 100644 --- a/deps/npm/docs/output/commands/npm-diff.html +++ b/deps/npm/docs/output/commands/npm-diff.html @@ -141,9 +141,9 @@
-

+

npm-diff - @10.9.2 + @11.0.0

The registry diff command
diff --git a/deps/npm/docs/output/commands/npm-dist-tag.html b/deps/npm/docs/output/commands/npm-dist-tag.html index dce3f752ba4eaa..34698aa4bcd59c 100644 --- a/deps/npm/docs/output/commands/npm-dist-tag.html +++ b/deps/npm/docs/output/commands/npm-dist-tag.html @@ -141,9 +141,9 @@
-

+

npm-dist-tag - @10.9.2 + @11.0.0

Modify package distribution tags
diff --git a/deps/npm/docs/output/commands/npm-docs.html b/deps/npm/docs/output/commands/npm-docs.html index caef5afe3b1bd4..cf0980ddecfd8c 100644 --- a/deps/npm/docs/output/commands/npm-docs.html +++ b/deps/npm/docs/output/commands/npm-docs.html @@ -141,9 +141,9 @@
-

+

npm-docs - @10.9.2 + @11.0.0

Open documentation for a package in a web browser
diff --git a/deps/npm/docs/output/commands/npm-doctor.html b/deps/npm/docs/output/commands/npm-doctor.html index d9f7a71450ab75..c662329ee54077 100644 --- a/deps/npm/docs/output/commands/npm-doctor.html +++ b/deps/npm/docs/output/commands/npm-doctor.html @@ -141,9 +141,9 @@
-

+

npm-doctor - @10.9.2 + @11.0.0

Check the health of your npm environment
diff --git a/deps/npm/docs/output/commands/npm-edit.html b/deps/npm/docs/output/commands/npm-edit.html index ab835ddcd352f9..bea9f57318650e 100644 --- a/deps/npm/docs/output/commands/npm-edit.html +++ b/deps/npm/docs/output/commands/npm-edit.html @@ -141,9 +141,9 @@
-

+

npm-edit - @10.9.2 + @11.0.0

Edit an installed package
diff --git a/deps/npm/docs/output/commands/npm-exec.html b/deps/npm/docs/output/commands/npm-exec.html index b5b8f66bdbb54f..3a60bfbe9e79a9 100644 --- a/deps/npm/docs/output/commands/npm-exec.html +++ b/deps/npm/docs/output/commands/npm-exec.html @@ -141,9 +141,9 @@
-

+

npm-exec - @10.9.2 + @11.0.0

Run a command from a local or remote npm package
diff --git a/deps/npm/docs/output/commands/npm-explain.html b/deps/npm/docs/output/commands/npm-explain.html index 812ce85f73a130..f9a4cfd44a15fa 100644 --- a/deps/npm/docs/output/commands/npm-explain.html +++ b/deps/npm/docs/output/commands/npm-explain.html @@ -141,9 +141,9 @@
-

+

npm-explain - @10.9.2 + @11.0.0

Explain installed packages
diff --git a/deps/npm/docs/output/commands/npm-explore.html b/deps/npm/docs/output/commands/npm-explore.html index 20d761c0db149c..63c33e340bb64a 100644 --- a/deps/npm/docs/output/commands/npm-explore.html +++ b/deps/npm/docs/output/commands/npm-explore.html @@ -141,9 +141,9 @@
-

+

npm-explore - @10.9.2 + @11.0.0

Browse an installed package
diff --git a/deps/npm/docs/output/commands/npm-find-dupes.html b/deps/npm/docs/output/commands/npm-find-dupes.html index 9b2810cf1b17ac..888337152afff1 100644 --- a/deps/npm/docs/output/commands/npm-find-dupes.html +++ b/deps/npm/docs/output/commands/npm-find-dupes.html @@ -141,9 +141,9 @@
-

+

npm-find-dupes - @10.9.2 + @11.0.0

Find duplication in the package tree
diff --git a/deps/npm/docs/output/commands/npm-fund.html b/deps/npm/docs/output/commands/npm-fund.html index 91e0a71c3b32af..56eff2d49873a5 100644 --- a/deps/npm/docs/output/commands/npm-fund.html +++ b/deps/npm/docs/output/commands/npm-fund.html @@ -141,9 +141,9 @@
-

+

npm-fund - @10.9.2 + @11.0.0

Retrieve funding information
diff --git a/deps/npm/docs/output/commands/npm-help-search.html b/deps/npm/docs/output/commands/npm-help-search.html index 35e2ec587c48e8..cc62294b56227b 100644 --- a/deps/npm/docs/output/commands/npm-help-search.html +++ b/deps/npm/docs/output/commands/npm-help-search.html @@ -141,9 +141,9 @@
-

+

npm-help-search - @10.9.2 + @11.0.0

Search npm help documentation
diff --git a/deps/npm/docs/output/commands/npm-help.html b/deps/npm/docs/output/commands/npm-help.html index 17403f14c89427..f7f6085021a435 100644 --- a/deps/npm/docs/output/commands/npm-help.html +++ b/deps/npm/docs/output/commands/npm-help.html @@ -141,9 +141,9 @@
-

+

npm-help - @10.9.2 + @11.0.0

Get help on npm
diff --git a/deps/npm/docs/output/commands/npm-hook.html b/deps/npm/docs/output/commands/npm-hook.html deleted file mode 100644 index d06784faf03624..00000000000000 --- a/deps/npm/docs/output/commands/npm-hook.html +++ /dev/null @@ -1,234 +0,0 @@ - - -npm-hook - - - - - -
-
-

- npm-hook - @10.9.2 -

-Manage registry hooks -
- -
-

Table of contents

- -
- -

Synopsis

-
npm hook add <pkg> <url> <secret> [--type=<type>]
-npm hook ls [pkg]
-npm hook rm <id>
-npm hook update <id> <url> <secret>
-
-

Note: This command is unaware of workspaces.

-

Description

-

Allows you to manage npm -hooks, -including adding, removing, listing, and updating.

-

Hooks allow you to configure URL endpoints that will be notified whenever a -change happens to any of the supported entity types. Three different types -of entities can be watched by hooks: packages, owners, and scopes.

-

To create a package hook, simply reference the package name.

-

To create an owner hook, prefix the owner name with ~ (as in, -~youruser).

-

To create a scope hook, prefix the scope name with @ (as in, -@yourscope).

-

The hook id used by update and rm are the IDs listed in npm hook ls -for that particular hook.

-

The shared secret will be sent along to the URL endpoint so you can verify -the request came from your own configured hook.

-

Example

-

Add a hook to watch a package for changes:

-
$ npm hook add lodash https://example.com/ my-shared-secret
-
-

Add a hook to watch packages belonging to the user substack:

-
$ npm hook add ~substack https://example.com/ my-shared-secret
-
-

Add a hook to watch packages in the scope @npm

-
$ npm hook add @npm https://example.com/ my-shared-secret
-
-

List all your active hooks:

-
$ npm hook ls
-
-

List your active hooks for the lodash package:

-
$ npm hook ls lodash
-
-

Update an existing hook's url:

-
$ npm hook update id-deadbeef https://my-new-website.here/
-
-

Remove a hook:

-
$ npm hook rm id-deadbeef
-
-

Configuration

-

registry

- -

The base URL of the npm registry.

-

otp

-
    -
  • Default: null
  • -
  • Type: null or String
  • -
-

This is a one-time password from a two-factor authenticator. It's needed -when publishing or changing package permissions with npm access.

-

If not set, and a registry response fails with a challenge for a one-time -password, npm will prompt on the command line for one.

-

See Also

-
- - -
- - - - \ No newline at end of file diff --git a/deps/npm/docs/output/commands/npm-init.html b/deps/npm/docs/output/commands/npm-init.html index 430763db6ba6af..0a95b514d387c2 100644 --- a/deps/npm/docs/output/commands/npm-init.html +++ b/deps/npm/docs/output/commands/npm-init.html @@ -141,9 +141,9 @@
-

+

npm-init - @10.9.2 + @11.0.0

Create a package.json file
diff --git a/deps/npm/docs/output/commands/npm-install-ci-test.html b/deps/npm/docs/output/commands/npm-install-ci-test.html index 6a29d2d54f679e..1ac7a4650e8ed2 100644 --- a/deps/npm/docs/output/commands/npm-install-ci-test.html +++ b/deps/npm/docs/output/commands/npm-install-ci-test.html @@ -141,9 +141,9 @@
-

+

npm-install-ci-test - @10.9.2 + @11.0.0

Install a project with a clean slate and run tests
diff --git a/deps/npm/docs/output/commands/npm-install-test.html b/deps/npm/docs/output/commands/npm-install-test.html index 32bd2271074fb6..e9c0ec8aa23f6b 100644 --- a/deps/npm/docs/output/commands/npm-install-test.html +++ b/deps/npm/docs/output/commands/npm-install-test.html @@ -141,9 +141,9 @@
-

+

npm-install-test - @10.9.2 + @11.0.0

Install package(s) and run tests
diff --git a/deps/npm/docs/output/commands/npm-install.html b/deps/npm/docs/output/commands/npm-install.html index db7d717d18160b..466171e4eeca78 100644 --- a/deps/npm/docs/output/commands/npm-install.html +++ b/deps/npm/docs/output/commands/npm-install.html @@ -141,9 +141,9 @@
-

+

npm-install - @10.9.2 + @11.0.0

Install a package
diff --git a/deps/npm/docs/output/commands/npm-link.html b/deps/npm/docs/output/commands/npm-link.html index 5778cc2a6268d2..76b8ed6c57ea07 100644 --- a/deps/npm/docs/output/commands/npm-link.html +++ b/deps/npm/docs/output/commands/npm-link.html @@ -141,9 +141,9 @@
-

+

npm-link - @10.9.2 + @11.0.0

Symlink a package folder
diff --git a/deps/npm/docs/output/commands/npm-login.html b/deps/npm/docs/output/commands/npm-login.html index 81555fcecefd3e..6d7c32a66c5720 100644 --- a/deps/npm/docs/output/commands/npm-login.html +++ b/deps/npm/docs/output/commands/npm-login.html @@ -141,9 +141,9 @@
-

+

npm-login - @10.9.2 + @11.0.0

Login to a registry user account
diff --git a/deps/npm/docs/output/commands/npm-logout.html b/deps/npm/docs/output/commands/npm-logout.html index 1b6cdf8b923034..8308703080305c 100644 --- a/deps/npm/docs/output/commands/npm-logout.html +++ b/deps/npm/docs/output/commands/npm-logout.html @@ -141,9 +141,9 @@
-

+

npm-logout - @10.9.2 + @11.0.0

Log out of the registry
diff --git a/deps/npm/docs/output/commands/npm-ls.html b/deps/npm/docs/output/commands/npm-ls.html index c9aa847abf1dd4..2f60ef08840272 100644 --- a/deps/npm/docs/output/commands/npm-ls.html +++ b/deps/npm/docs/output/commands/npm-ls.html @@ -141,16 +141,16 @@
-

+

npm-ls - @10.9.2 + @11.0.0

List installed packages

Table of contents

- +

Synopsis

@@ -168,7 +168,7 @@

Description

the results to only the paths to the packages named. Note that nested packages will also show the paths to the specified packages. For example, running npm ls promzard in npm's source tree will show:

-
npm@10.9.2 /path/to/npm
+
npm@11.0.0 /path/to/npm
 └─┬ init-package-json@0.0.4
   └── promzard@0.1.5
 
@@ -179,27 +179,6 @@

Description

The tree shown is the logical dependency tree, based on package dependencies, not the physical layout of your node_modules folder.

When run as ll or la, it shows extended information by default.

-

Note: Design Changes Pending

-

The npm ls command's output and behavior made a ton of sense when npm -created a node_modules folder that naively nested every dependency. In -such a case, the logical dependency graph and physical tree of packages on -disk would be roughly identical.

-

With the advent of automatic install-time deduplication of dependencies in -npm v3, the ls output was modified to display the logical dependency -graph as a tree structure, since this was more useful to most users. -However, without using npm ls -l, it became impossible to show where a -package was actually installed much of the time!

-

With the advent of automatic installation of peerDependencies in npm v7, -this gets even more curious, as peerDependencies are logically -"underneath" their dependents in the dependency graph, but are always -physically at or above their location on disk.

-

Also, in the years since npm got an ls command (in version 0.0.2!), -dependency graphs have gotten much larger as a general rule. Therefore, in -order to avoid dumping an excessive amount of content to the terminal, npm ls now only shows the top level dependencies, unless --all is -provided.

-

A thorough re-examination of the use cases, intention, behavior, and output -of this command, is currently underway. Expect significant changes to at -least the default human-readable npm ls output in npm v8.

Configuration

all

    diff --git a/deps/npm/docs/output/commands/npm-org.html b/deps/npm/docs/output/commands/npm-org.html index 6c45111f034994..ccc310efdb165e 100644 --- a/deps/npm/docs/output/commands/npm-org.html +++ b/deps/npm/docs/output/commands/npm-org.html @@ -141,9 +141,9 @@
    -

    +

    npm-org - @10.9.2 + @11.0.0

    Manage orgs
    diff --git a/deps/npm/docs/output/commands/npm-outdated.html b/deps/npm/docs/output/commands/npm-outdated.html index 9be28fdfd239e1..1875a6f204f73b 100644 --- a/deps/npm/docs/output/commands/npm-outdated.html +++ b/deps/npm/docs/output/commands/npm-outdated.html @@ -141,9 +141,9 @@
    -

    +

    npm-outdated - @10.9.2 + @11.0.0

    Check for outdated packages
    diff --git a/deps/npm/docs/output/commands/npm-owner.html b/deps/npm/docs/output/commands/npm-owner.html index fc0899e5e5d66b..5bf138010b189a 100644 --- a/deps/npm/docs/output/commands/npm-owner.html +++ b/deps/npm/docs/output/commands/npm-owner.html @@ -141,9 +141,9 @@
    -

    +

    npm-owner - @10.9.2 + @11.0.0

    Manage package owners
    diff --git a/deps/npm/docs/output/commands/npm-pack.html b/deps/npm/docs/output/commands/npm-pack.html index 359d463780e510..acc909f46c701f 100644 --- a/deps/npm/docs/output/commands/npm-pack.html +++ b/deps/npm/docs/output/commands/npm-pack.html @@ -141,16 +141,16 @@
    -

    +

    npm-pack - @10.9.2 + @11.0.0

    Create a tarball from a package

    Table of contents

    - +

    Synopsis

    @@ -230,6 +230,16 @@

    include-workspace-root

    all workspaces via the workspaces flag, will cause npm to operate only on the specified workspaces, and not on the root project.

    This value is not exported to the environment for child processes.

    +

    ignore-scripts

    +
      +
    • Default: false
    • +
    • Type: Boolean
    • +
    +

    If true, npm does not run scripts specified in package.json files.

    +

    Note that commands explicitly intended to run a particular script, such as +npm start, npm stop, npm restart, npm test, and npm run-script +will still run their intended script if ignore-scripts is set, but they +will not run any pre- or post-scripts.

    Description

    For anything that's installable (that is, a package folder, tarball, tarball url, git url, name@tag, name@version, name, or scoped name), this diff --git a/deps/npm/docs/output/commands/npm-ping.html b/deps/npm/docs/output/commands/npm-ping.html index 4b3fbbc698cbb7..ca4f2cabf33232 100644 --- a/deps/npm/docs/output/commands/npm-ping.html +++ b/deps/npm/docs/output/commands/npm-ping.html @@ -141,9 +141,9 @@

    -

    +

    npm-ping - @10.9.2 + @11.0.0

    Ping npm registry
    diff --git a/deps/npm/docs/output/commands/npm-pkg.html b/deps/npm/docs/output/commands/npm-pkg.html index f1fe76e8073150..6ff0880e37acb5 100644 --- a/deps/npm/docs/output/commands/npm-pkg.html +++ b/deps/npm/docs/output/commands/npm-pkg.html @@ -141,9 +141,9 @@
    -

    +

    npm-pkg - @10.9.2 + @11.0.0

    Manages your package.json
    diff --git a/deps/npm/docs/output/commands/npm-prefix.html b/deps/npm/docs/output/commands/npm-prefix.html index 9708e55234ab4c..c72108668925da 100644 --- a/deps/npm/docs/output/commands/npm-prefix.html +++ b/deps/npm/docs/output/commands/npm-prefix.html @@ -141,9 +141,9 @@
    -

    +

    npm-prefix - @10.9.2 + @11.0.0

    Display prefix
    @@ -154,7 +154,7 @@

    Table of contents

    Synopsis

    -
    npm prefix [-g]
    +
    npm prefix
     

    Note: This command is unaware of workspaces.

    Description

    diff --git a/deps/npm/docs/output/commands/npm-profile.html b/deps/npm/docs/output/commands/npm-profile.html index 6bb08b1fda63a6..86705775e1c86b 100644 --- a/deps/npm/docs/output/commands/npm-profile.html +++ b/deps/npm/docs/output/commands/npm-profile.html @@ -141,9 +141,9 @@
    -

    +

    npm-profile - @10.9.2 + @11.0.0

    Change settings on your registry profile
    diff --git a/deps/npm/docs/output/commands/npm-prune.html b/deps/npm/docs/output/commands/npm-prune.html index 818522d63754f9..da263ef7fc83a2 100644 --- a/deps/npm/docs/output/commands/npm-prune.html +++ b/deps/npm/docs/output/commands/npm-prune.html @@ -141,9 +141,9 @@
    -

    +

    npm-prune - @10.9.2 + @11.0.0

    Remove extraneous packages
    diff --git a/deps/npm/docs/output/commands/npm-publish.html b/deps/npm/docs/output/commands/npm-publish.html index 37c19e0b8f357c..55b32480044e16 100644 --- a/deps/npm/docs/output/commands/npm-publish.html +++ b/deps/npm/docs/output/commands/npm-publish.html @@ -141,9 +141,9 @@
    -

    +

    npm-publish - @10.9.2 + @11.0.0

    Publish a package
    @@ -225,6 +225,8 @@

    Files included in package

    See developers for full details on what's included in the published package, as well as details on how the package is built.

    +

    See package.json for more info on +what can and can't be ignored.

    Configuration

    tag

      diff --git a/deps/npm/docs/output/commands/npm-query.html b/deps/npm/docs/output/commands/npm-query.html index 4a0210e62f58ea..7c9819a59dfc8a 100644 --- a/deps/npm/docs/output/commands/npm-query.html +++ b/deps/npm/docs/output/commands/npm-query.html @@ -141,9 +141,9 @@
      -

      +

      npm-query - @10.9.2 + @11.0.0

      Dependency selector query
      diff --git a/deps/npm/docs/output/commands/npm-rebuild.html b/deps/npm/docs/output/commands/npm-rebuild.html index 0a356397c4a95b..541163fe454649 100644 --- a/deps/npm/docs/output/commands/npm-rebuild.html +++ b/deps/npm/docs/output/commands/npm-rebuild.html @@ -141,9 +141,9 @@
      -

      +

      npm-rebuild - @10.9.2 + @11.0.0

      Rebuild a package
      diff --git a/deps/npm/docs/output/commands/npm-repo.html b/deps/npm/docs/output/commands/npm-repo.html index ad86f81682375d..cf5ad32161239b 100644 --- a/deps/npm/docs/output/commands/npm-repo.html +++ b/deps/npm/docs/output/commands/npm-repo.html @@ -141,9 +141,9 @@
      -

      +

      npm-repo - @10.9.2 + @11.0.0

      Open package repository page in the browser
      diff --git a/deps/npm/docs/output/commands/npm-restart.html b/deps/npm/docs/output/commands/npm-restart.html index 4a2244b048ecbc..ac1ee9710a9ade 100644 --- a/deps/npm/docs/output/commands/npm-restart.html +++ b/deps/npm/docs/output/commands/npm-restart.html @@ -141,9 +141,9 @@
      -

      +

      npm-restart - @10.9.2 + @11.0.0

      Restart a package
      diff --git a/deps/npm/docs/output/commands/npm-root.html b/deps/npm/docs/output/commands/npm-root.html index 499bc84358b3e5..5e51f975dd7086 100644 --- a/deps/npm/docs/output/commands/npm-root.html +++ b/deps/npm/docs/output/commands/npm-root.html @@ -141,9 +141,9 @@
      -

      +

      npm-root - @10.9.2 + @11.0.0

      Display npm root
      diff --git a/deps/npm/docs/output/commands/npm-run-script.html b/deps/npm/docs/output/commands/npm-run-script.html index 9c0ef4fedbc16e..5d4fd8a4e63296 100644 --- a/deps/npm/docs/output/commands/npm-run-script.html +++ b/deps/npm/docs/output/commands/npm-run-script.html @@ -141,9 +141,9 @@
      -

      +

      npm-run-script - @10.9.2 + @11.0.0

      Run arbitrary package scripts
      diff --git a/deps/npm/docs/output/commands/npm-sbom.html b/deps/npm/docs/output/commands/npm-sbom.html index b648df1654e8a6..7d57f380eb1915 100644 --- a/deps/npm/docs/output/commands/npm-sbom.html +++ b/deps/npm/docs/output/commands/npm-sbom.html @@ -141,9 +141,9 @@
      -

      +

      npm-sbom - @10.9.2 + @11.0.0

      Generate a Software Bill of Materials (SBOM)
      diff --git a/deps/npm/docs/output/commands/npm-search.html b/deps/npm/docs/output/commands/npm-search.html index bfd70c2a8abe92..b109ce855945b9 100644 --- a/deps/npm/docs/output/commands/npm-search.html +++ b/deps/npm/docs/output/commands/npm-search.html @@ -141,9 +141,9 @@
      -

      +

      npm-search - @10.9.2 + @11.0.0

      Search for packages
      diff --git a/deps/npm/docs/output/commands/npm-shrinkwrap.html b/deps/npm/docs/output/commands/npm-shrinkwrap.html index 60d198f85ce67e..023f5fefc3f744 100644 --- a/deps/npm/docs/output/commands/npm-shrinkwrap.html +++ b/deps/npm/docs/output/commands/npm-shrinkwrap.html @@ -141,9 +141,9 @@
      -

      +

      npm-shrinkwrap - @10.9.2 + @11.0.0

      Lock down dependency versions for publication
      diff --git a/deps/npm/docs/output/commands/npm-star.html b/deps/npm/docs/output/commands/npm-star.html index ccda8bb3297d70..2bd9e65a79f585 100644 --- a/deps/npm/docs/output/commands/npm-star.html +++ b/deps/npm/docs/output/commands/npm-star.html @@ -141,9 +141,9 @@
      -

      +

      npm-star - @10.9.2 + @11.0.0

      Mark your favorite packages
      diff --git a/deps/npm/docs/output/commands/npm-stars.html b/deps/npm/docs/output/commands/npm-stars.html index 2f9619190f0122..8ddaec6f6d3b9d 100644 --- a/deps/npm/docs/output/commands/npm-stars.html +++ b/deps/npm/docs/output/commands/npm-stars.html @@ -141,9 +141,9 @@
      -

      +

      npm-stars - @10.9.2 + @11.0.0

      View packages marked as favorites
      diff --git a/deps/npm/docs/output/commands/npm-start.html b/deps/npm/docs/output/commands/npm-start.html index fad3ce05c3a2c9..1b761fe4d940c8 100644 --- a/deps/npm/docs/output/commands/npm-start.html +++ b/deps/npm/docs/output/commands/npm-start.html @@ -141,9 +141,9 @@
      -

      +

      npm-start - @10.9.2 + @11.0.0

      Start a package
      diff --git a/deps/npm/docs/output/commands/npm-stop.html b/deps/npm/docs/output/commands/npm-stop.html index bc70086d9991d5..645e4336830c96 100644 --- a/deps/npm/docs/output/commands/npm-stop.html +++ b/deps/npm/docs/output/commands/npm-stop.html @@ -141,9 +141,9 @@
      -

      +

      npm-stop - @10.9.2 + @11.0.0

      Stop a package
      diff --git a/deps/npm/docs/output/commands/npm-team.html b/deps/npm/docs/output/commands/npm-team.html index e03442dc68bf9c..8abe275d4fbbda 100644 --- a/deps/npm/docs/output/commands/npm-team.html +++ b/deps/npm/docs/output/commands/npm-team.html @@ -141,9 +141,9 @@
      -

      +

      npm-team - @10.9.2 + @11.0.0

      Manage organization teams and team memberships
      diff --git a/deps/npm/docs/output/commands/npm-test.html b/deps/npm/docs/output/commands/npm-test.html index b30af4ed9b3b30..f82bb32675cee6 100644 --- a/deps/npm/docs/output/commands/npm-test.html +++ b/deps/npm/docs/output/commands/npm-test.html @@ -141,9 +141,9 @@
      -

      +

      npm-test - @10.9.2 + @11.0.0

      Test a package
      diff --git a/deps/npm/docs/output/commands/npm-token.html b/deps/npm/docs/output/commands/npm-token.html index 353709607b4bd4..8df68fc4426ec9 100644 --- a/deps/npm/docs/output/commands/npm-token.html +++ b/deps/npm/docs/output/commands/npm-token.html @@ -141,9 +141,9 @@
      -

      +

      npm-token - @10.9.2 + @11.0.0

      Manage your authentication tokens
      diff --git a/deps/npm/docs/output/commands/npm-uninstall.html b/deps/npm/docs/output/commands/npm-uninstall.html index 633dbbe58f933e..e1f43ac059c82b 100644 --- a/deps/npm/docs/output/commands/npm-uninstall.html +++ b/deps/npm/docs/output/commands/npm-uninstall.html @@ -141,9 +141,9 @@
      -

      +

      npm-uninstall - @10.9.2 + @11.0.0

      Remove a package
      diff --git a/deps/npm/docs/output/commands/npm-unpublish.html b/deps/npm/docs/output/commands/npm-unpublish.html index e4f4090936d4ff..4456a22d1be387 100644 --- a/deps/npm/docs/output/commands/npm-unpublish.html +++ b/deps/npm/docs/output/commands/npm-unpublish.html @@ -141,9 +141,9 @@
      -

      +

      npm-unpublish - @10.9.2 + @11.0.0

      Remove a package from the registry
      diff --git a/deps/npm/docs/output/commands/npm-unstar.html b/deps/npm/docs/output/commands/npm-unstar.html index 741ed8e707a4f9..082be228f20225 100644 --- a/deps/npm/docs/output/commands/npm-unstar.html +++ b/deps/npm/docs/output/commands/npm-unstar.html @@ -141,9 +141,9 @@
      -

      +

      npm-unstar - @10.9.2 + @11.0.0

      Remove an item from your favorite packages
      diff --git a/deps/npm/docs/output/commands/npm-update.html b/deps/npm/docs/output/commands/npm-update.html index 287ed19fe9f5f6..0bd67db45ba3a5 100644 --- a/deps/npm/docs/output/commands/npm-update.html +++ b/deps/npm/docs/output/commands/npm-update.html @@ -141,9 +141,9 @@
      -

      +

      npm-update - @10.9.2 + @11.0.0

      Update packages
      diff --git a/deps/npm/docs/output/commands/npm-version.html b/deps/npm/docs/output/commands/npm-version.html index 43b978ffa94c80..e7679c65f407b6 100644 --- a/deps/npm/docs/output/commands/npm-version.html +++ b/deps/npm/docs/output/commands/npm-version.html @@ -141,9 +141,9 @@
      -

      +

      npm-version - @10.9.2 + @11.0.0

      Bump a package version
      diff --git a/deps/npm/docs/output/commands/npm-view.html b/deps/npm/docs/output/commands/npm-view.html index 1e388cfb922462..c8d4fdd52cd849 100644 --- a/deps/npm/docs/output/commands/npm-view.html +++ b/deps/npm/docs/output/commands/npm-view.html @@ -141,9 +141,9 @@
      -

      +

      npm-view - @10.9.2 + @11.0.0

      View registry info
      diff --git a/deps/npm/docs/output/commands/npm-whoami.html b/deps/npm/docs/output/commands/npm-whoami.html index 944a762ad4aea1..ab9c86004719fc 100644 --- a/deps/npm/docs/output/commands/npm-whoami.html +++ b/deps/npm/docs/output/commands/npm-whoami.html @@ -141,9 +141,9 @@
      -

      +

      npm-whoami - @10.9.2 + @11.0.0

      Display npm username
      diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html index eae0fdc7b20659..d3641aebcd135c 100644 --- a/deps/npm/docs/output/commands/npm.html +++ b/deps/npm/docs/output/commands/npm.html @@ -141,9 +141,9 @@
      -

      +

      npm - @10.9.2 + @11.0.0

      javascript package manager
      @@ -158,7 +158,7 @@

      Table of contents

    Note: This command is unaware of workspaces.

    Version

    -

    10.9.2

    +

    11.0.0

    Description

    npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency diff --git a/deps/npm/docs/output/commands/npx.html b/deps/npm/docs/output/commands/npx.html index ec5547306359f7..39ef19db4f5c5c 100644 --- a/deps/npm/docs/output/commands/npx.html +++ b/deps/npm/docs/output/commands/npx.html @@ -141,9 +141,9 @@

    -

    +

    npx - @10.9.2 + @11.0.0

    Run a command from a local or remote npm package
    diff --git a/deps/npm/docs/output/configuring-npm/folders.html b/deps/npm/docs/output/configuring-npm/folders.html index daca0c019a942b..8c5d99c58d1169 100644 --- a/deps/npm/docs/output/configuring-npm/folders.html +++ b/deps/npm/docs/output/configuring-npm/folders.html @@ -141,9 +141,9 @@
    -

    +

    folders - @10.9.2 + @11.0.0

    Folder Structures Used by npm
    diff --git a/deps/npm/docs/output/configuring-npm/install.html b/deps/npm/docs/output/configuring-npm/install.html index abda3bcc06ab62..a563910ccac75d 100644 --- a/deps/npm/docs/output/configuring-npm/install.html +++ b/deps/npm/docs/output/configuring-npm/install.html @@ -141,9 +141,9 @@
    -

    +

    install - @10.9.2 + @11.0.0

    Download and install node and npm
    diff --git a/deps/npm/docs/output/configuring-npm/npm-global.html b/deps/npm/docs/output/configuring-npm/npm-global.html index daca0c019a942b..8c5d99c58d1169 100644 --- a/deps/npm/docs/output/configuring-npm/npm-global.html +++ b/deps/npm/docs/output/configuring-npm/npm-global.html @@ -141,9 +141,9 @@
    -

    +

    folders - @10.9.2 + @11.0.0

    Folder Structures Used by npm
    diff --git a/deps/npm/docs/output/configuring-npm/npm-json.html b/deps/npm/docs/output/configuring-npm/npm-json.html index 1645e91a762b8f..b8b847d60bac0d 100644 --- a/deps/npm/docs/output/configuring-npm/npm-json.html +++ b/deps/npm/docs/output/configuring-npm/npm-json.html @@ -141,16 +141,16 @@
    -

    +

    package.json - @10.9.2 + @11.0.0

    Specifics of npm's package.json handling

    Table of contents

    - +

    Description

    @@ -399,6 +399,7 @@

    files

    if you wish it to be published)
  • pnpm-lock.yaml
  • yarn.lock
  • +
  • bun.lockb

Most of these ignored files can be included specifically if included in the files globs. Exceptions to this are:

@@ -409,6 +410,7 @@

files

  • package-lock.json
  • pnpm-lock.yaml
  • yarn.lock
  • +
  • bun.lockb
  • These can not be included.

    exports

    @@ -1007,6 +1009,14 @@

    cpu

    }

    The host architecture is determined by process.arch

    +

    libc

    +

    If your code only runs or builds in certain versions of libc, you can +specify which ones. This field only applies if os is linux.

    +
    {
    +  "os": "linux",
    +  "libc": "glibc"
    +}
    +

    devEngines

    The devEngines field aids engineers working on a codebase to all be using the same tooling.

    You can specify a devEngines property in your package.json which will run before install, ci, and run commands.

    diff --git a/deps/npm/docs/output/configuring-npm/npm-shrinkwrap-json.html b/deps/npm/docs/output/configuring-npm/npm-shrinkwrap-json.html index 65f0b0c184bef9..25af90bd1bddc6 100644 --- a/deps/npm/docs/output/configuring-npm/npm-shrinkwrap-json.html +++ b/deps/npm/docs/output/configuring-npm/npm-shrinkwrap-json.html @@ -141,9 +141,9 @@
    -

    +

    npm-shrinkwrap.json - @10.9.2 + @11.0.0

    A publishable lockfile
    diff --git a/deps/npm/docs/output/configuring-npm/npmrc.html b/deps/npm/docs/output/configuring-npm/npmrc.html index e5e94afe63a94c..04cea4c5fb66b2 100644 --- a/deps/npm/docs/output/configuring-npm/npmrc.html +++ b/deps/npm/docs/output/configuring-npm/npmrc.html @@ -141,9 +141,9 @@
    -

    +

    npmrc - @10.9.2 + @11.0.0

    The npm config files
    @@ -223,7 +223,7 @@
  • username
  • _password
  • email
  • -
  • certfile (path to certificate file)
  • +
  • cafile (path to certificate authority file)
  • keyfile (path to key file)
  • In order to scope these values, they must be prefixed by a URI fragment. diff --git a/deps/npm/docs/output/configuring-npm/package-json.html b/deps/npm/docs/output/configuring-npm/package-json.html index 1645e91a762b8f..b8b847d60bac0d 100644 --- a/deps/npm/docs/output/configuring-npm/package-json.html +++ b/deps/npm/docs/output/configuring-npm/package-json.html @@ -141,16 +141,16 @@

    -

    +

    package.json - @10.9.2 + @11.0.0

    Specifics of npm's package.json handling

    Table of contents

    - +

    Description

    @@ -399,6 +399,7 @@

    files

    if you wish it to be published)
  • pnpm-lock.yaml
  • yarn.lock
  • +
  • bun.lockb
  • Most of these ignored files can be included specifically if included in the files globs. Exceptions to this are:

    @@ -409,6 +410,7 @@

    files

  • package-lock.json
  • pnpm-lock.yaml
  • yarn.lock
  • +
  • bun.lockb
  • These can not be included.

    exports

    @@ -1007,6 +1009,14 @@

    cpu

    }

    The host architecture is determined by process.arch

    +

    libc

    +

    If your code only runs or builds in certain versions of libc, you can +specify which ones. This field only applies if os is linux.

    +
    {
    +  "os": "linux",
    +  "libc": "glibc"
    +}
    +

    devEngines

    The devEngines field aids engineers working on a codebase to all be using the same tooling.

    You can specify a devEngines property in your package.json which will run before install, ci, and run commands.

    diff --git a/deps/npm/docs/output/configuring-npm/package-lock-json.html b/deps/npm/docs/output/configuring-npm/package-lock-json.html index 80fa8bf8bd4ac8..8017f0332b7065 100644 --- a/deps/npm/docs/output/configuring-npm/package-lock-json.html +++ b/deps/npm/docs/output/configuring-npm/package-lock-json.html @@ -141,9 +141,9 @@
    -

    +

    package-lock.json - @10.9.2 + @11.0.0

    A manifestation of the manifest
    diff --git a/deps/npm/docs/output/using-npm/config.html b/deps/npm/docs/output/using-npm/config.html index 487cc56439e9ed..94207774337468 100644 --- a/deps/npm/docs/output/using-npm/config.html +++ b/deps/npm/docs/output/using-npm/config.html @@ -141,9 +141,9 @@
    -

    +

    config - @10.9.2 + @11.0.0

    More than you probably want to know about npm configuration
    @@ -1468,9 +1468,9 @@

    cert

  • Default: null
  • Type: null or String
  • DEPRECATED: key and cert are no longer used for most registry -operations. Use registry scoped keyfile and certfile instead. Example: +operations. Use registry scoped keyfile and cafile instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem -//other-registry.tld/:certfile=/path/to/cert.crt
  • +//other-registry.tld/:cafile=/path/to/cert.crt

    A client certificate to pass when accessing the registry. Values should be in PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with @@ -1478,8 +1478,8 @@

    cert

    cert="-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----"
     

    It is not the path to a certificate file, though you can set a -registry-scoped "certfile" path like -"//other-registry.tld/:certfile=/path/to/cert.pem".

    +registry-scoped "cafile" path like +"//other-registry.tld/:cafile=/path/to/cert.pem".

    dev

    • Default: false
    • @@ -1543,9 +1543,9 @@

      key

    • Default: null
    • Type: null or String
    • DEPRECATED: key and cert are no longer used for most registry -operations. Use registry scoped keyfile and certfile instead. Example: +operations. Use registry scoped keyfile and cafile instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem -//other-registry.tld/:certfile=/path/to/cert.crt
    • +//other-registry.tld/:cafile=/path/to/cert.crt

    A client key to pass when accessing the registry. Values should be in PEM format with newlines replaced by the string "\n". For example:

    diff --git a/deps/npm/docs/output/using-npm/dependency-selectors.html b/deps/npm/docs/output/using-npm/dependency-selectors.html index 24e34408de9c52..db54f7a6e397bc 100644 --- a/deps/npm/docs/output/using-npm/dependency-selectors.html +++ b/deps/npm/docs/output/using-npm/dependency-selectors.html @@ -141,9 +141,9 @@
    -

    +

    Dependency Selector Syntax & Querying - @10.9.2 + @11.0.0

    Dependency Selector Syntax & Querying
    diff --git a/deps/npm/docs/output/using-npm/developers.html b/deps/npm/docs/output/using-npm/developers.html index d7848c6e6647f3..bc1479514c2e80 100644 --- a/deps/npm/docs/output/using-npm/developers.html +++ b/deps/npm/docs/output/using-npm/developers.html @@ -141,9 +141,9 @@
    -

    +

    developers - @10.9.2 + @11.0.0

    Developer Guide
    @@ -249,8 +249,8 @@

    Keeping files out of your Pa
  • You can end patterns with a forward slash / to specify a directory.
  • You can negate a pattern by starting it with an exclamation point !.
  • -

    By default, the following paths and files are ignored, so there's no -need to add them to .npmignore explicitly:

    +

    By default, some paths and files are ignored, so there's no +need to add them to .npmignore explicitly. Some examples are:

    • .*.swp
    • ._*
    • @@ -283,6 +283,8 @@

      Keeping files out of your Pa property of package.json, which is an array of file or directory names that should be included in your package. Sometimes manually picking which items to allow is easier to manage than building a block list.

      +

      See package.json for more info on +what can and can't be ignored.

      Testing whether your .npmignore or files config works

      If you want to double check that your package will include only the files you intend it to when published, you can run the npm pack command locally diff --git a/deps/npm/docs/output/using-npm/logging.html b/deps/npm/docs/output/using-npm/logging.html index d1e6b0a7532253..4760437326d374 100644 --- a/deps/npm/docs/output/using-npm/logging.html +++ b/deps/npm/docs/output/using-npm/logging.html @@ -141,9 +141,9 @@

      -

      +

      Logging - @10.9.2 + @11.0.0

      Why, What & How We Log
      diff --git a/deps/npm/docs/output/using-npm/orgs.html b/deps/npm/docs/output/using-npm/orgs.html index 5b7007b3618406..68264d661a8b80 100644 --- a/deps/npm/docs/output/using-npm/orgs.html +++ b/deps/npm/docs/output/using-npm/orgs.html @@ -141,9 +141,9 @@
      -

      +

      orgs - @10.9.2 + @11.0.0

      Working with Teams & Orgs
      diff --git a/deps/npm/docs/output/using-npm/package-spec.html b/deps/npm/docs/output/using-npm/package-spec.html index eee81153269653..b23958ad734f51 100644 --- a/deps/npm/docs/output/using-npm/package-spec.html +++ b/deps/npm/docs/output/using-npm/package-spec.html @@ -141,9 +141,9 @@
      -

      +

      package-spec - @10.9.2 + @11.0.0

      Package name specifier
      diff --git a/deps/npm/docs/output/using-npm/registry.html b/deps/npm/docs/output/using-npm/registry.html index bd464be3fd67c9..1ab0e032dc1ef1 100644 --- a/deps/npm/docs/output/using-npm/registry.html +++ b/deps/npm/docs/output/using-npm/registry.html @@ -141,9 +141,9 @@
      -

      +

      registry - @10.9.2 + @11.0.0

      The JavaScript Package Registry
      diff --git a/deps/npm/docs/output/using-npm/removal.html b/deps/npm/docs/output/using-npm/removal.html index f0ccb74eb50811..8645555c8eb43f 100644 --- a/deps/npm/docs/output/using-npm/removal.html +++ b/deps/npm/docs/output/using-npm/removal.html @@ -141,9 +141,9 @@
      -

      +

      removal - @10.9.2 + @11.0.0

      Cleaning the Slate
      diff --git a/deps/npm/docs/output/using-npm/scope.html b/deps/npm/docs/output/using-npm/scope.html index 35ed91f147f107..ff18511303a351 100644 --- a/deps/npm/docs/output/using-npm/scope.html +++ b/deps/npm/docs/output/using-npm/scope.html @@ -141,9 +141,9 @@
      -

      +

      scope - @10.9.2 + @11.0.0

      Scoped packages
      diff --git a/deps/npm/docs/output/using-npm/scripts.html b/deps/npm/docs/output/using-npm/scripts.html index 9982862814e73a..33067cbd524e07 100644 --- a/deps/npm/docs/output/using-npm/scripts.html +++ b/deps/npm/docs/output/using-npm/scripts.html @@ -141,9 +141,9 @@
      -

      +

      scripts - @10.9.2 + @11.0.0

      How npm handles the "scripts" field
      diff --git a/deps/npm/docs/output/using-npm/workspaces.html b/deps/npm/docs/output/using-npm/workspaces.html index 5e2756479c7636..ebdbb762285628 100644 --- a/deps/npm/docs/output/using-npm/workspaces.html +++ b/deps/npm/docs/output/using-npm/workspaces.html @@ -141,9 +141,9 @@
      -

      +

      workspaces - @10.9.2 + @11.0.0

      Working with workspaces
      diff --git a/deps/npm/lib/cli/entry.js b/deps/npm/lib/cli/entry.js index ed73eb89e2d360..f36bc59feaec9b 100644 --- a/deps/npm/lib/cli/entry.js +++ b/deps/npm/lib/cli/entry.js @@ -6,11 +6,6 @@ module.exports = async (process, validateEngines) => { // leak any private CLI configs to other programs process.title = 'npm' - // if npm is called as "npmg" or "npm_g", then run in global mode. - if (process.argv[1][process.argv[1].length - 1] === 'g') { - process.argv.splice(1, 1, 'npm', '-g') - } - // Patch the global fs module here at the app level require('graceful-fs').gracefulify(require('node:fs')) diff --git a/deps/npm/lib/commands/cache.js b/deps/npm/lib/commands/cache.js index 87c70a57dc0edf..45d308a57d0c23 100644 --- a/deps/npm/lib/commands/cache.js +++ b/deps/npm/lib/commands/cache.js @@ -38,7 +38,7 @@ const searchCachePackage = async (path, parsed, cacheKeys) => { try { details = await cacache.get(path, key) packument = jsonParse(details.data) - } catch (_) { + } catch { // if we couldn't parse the packument, abort continue } @@ -131,7 +131,7 @@ class Cache extends BaseCommand { let entry try { entry = await cacache.get(cachePath, key) - } catch (err) { + } catch { log.warn('cache', `Not Found: ${key}`) break } diff --git a/deps/npm/lib/commands/deprecate.js b/deps/npm/lib/commands/deprecate.js index 977fd9fce11dac..95eaf429120fac 100644 --- a/deps/npm/lib/commands/deprecate.js +++ b/deps/npm/lib/commands/deprecate.js @@ -1,4 +1,4 @@ -const fetch = require('npm-registry-fetch') +const npmFetch = require('npm-registry-fetch') const { otplease } = require('../utils/auth.js') const npa = require('npm-package-arg') const { log } = require('proc-log') @@ -47,7 +47,7 @@ class Deprecate extends BaseCommand { } const uri = '/' + p.escapedName - const packument = await fetch.json(uri, { + const packument = await npmFetch.json(uri, { ...this.npm.flatOptions, spec: p, query: { write: true }, @@ -60,7 +60,7 @@ class Deprecate extends BaseCommand { for (const v of versions) { packument.versions[v].deprecated = msg } - return otplease(this.npm, this.npm.flatOptions, opts => fetch(uri, { + return otplease(this.npm, this.npm.flatOptions, opts => npmFetch(uri, { ...opts, spec: p, method: 'PUT', diff --git a/deps/npm/lib/commands/diff.js b/deps/npm/lib/commands/diff.js index 3fa8090a350468..a97eed92c83cba 100644 --- a/deps/npm/lib/commands/diff.js +++ b/deps/npm/lib/commands/diff.js @@ -83,7 +83,7 @@ class Diff extends BaseCommand { try { const { content: pkg } = await pkgJson.normalize(this.prefix) name = pkg.name - } catch (e) { + } catch { log.verbose('diff', 'could not read project dir package.json') } @@ -117,7 +117,7 @@ class Diff extends BaseCommand { try { const { content: pkg } = await pkgJson.normalize(this.prefix) pkgName = pkg.name - } catch (e) { + } catch { log.verbose('diff', 'could not read project dir package.json') noPackageJson = true } @@ -156,7 +156,7 @@ class Diff extends BaseCommand { node = actualTree && actualTree.inventory.query('name', spec.name) .values().next().value - } catch (e) { + } catch { log.verbose('diff', 'failed to load actual install tree') } @@ -230,7 +230,7 @@ class Diff extends BaseCommand { try { const { content: pkg } = await pkgJson.normalize(this.prefix) pkgName = pkg.name - } catch (e) { + } catch { log.verbose('diff', 'could not read project dir package.json') } @@ -265,7 +265,7 @@ class Diff extends BaseCommand { } const arb = new Arborist(opts) actualTree = await arb.loadActual(opts) - } catch (e) { + } catch { log.verbose('diff', 'failed to load actual install tree') } diff --git a/deps/npm/lib/commands/dist-tag.js b/deps/npm/lib/commands/dist-tag.js index 663f0eb44a26ad..3fdecd926a564e 100644 --- a/deps/npm/lib/commands/dist-tag.js +++ b/deps/npm/lib/commands/dist-tag.js @@ -1,5 +1,5 @@ const npa = require('npm-package-arg') -const regFetch = require('npm-registry-fetch') +const npmFetch = require('npm-registry-fetch') const semver = require('semver') const { log, output } = require('proc-log') const { otplease } = require('../utils/auth.js') @@ -119,7 +119,7 @@ class DistTag extends BaseCommand { }, spec, } - await otplease(this.npm, reqOpts, o => regFetch(url, o)) + await otplease(this.npm, reqOpts, o => npmFetch(url, o)) output.standard(`+${t}: ${spec.name}@${version}`) } @@ -145,7 +145,7 @@ class DistTag extends BaseCommand { method: 'DELETE', spec, } - await otplease(this.npm, reqOpts, o => regFetch(url, o)) + await otplease(this.npm, reqOpts, o => npmFetch(url, o)) output.standard(`-${tag}: ${spec.name}@${version}`) } @@ -182,7 +182,7 @@ class DistTag extends BaseCommand { try { output.standard(`${name}:`) await this.list(npa(name), this.npm.flatOptions) - } catch (err) { + } catch { // set the exitCode directly, but ignore the error // since it will have already been logged by this.list() process.exitCode = 1 @@ -191,7 +191,7 @@ class DistTag extends BaseCommand { } async fetchTags (spec, opts) { - const data = await regFetch.json( + const data = await npmFetch.json( `/-/package/${spec.escapedName}/dist-tags`, { ...opts, 'prefer-online': true, spec } ) diff --git a/deps/npm/lib/commands/doctor.js b/deps/npm/lib/commands/doctor.js index 8fbd49b7ca8bfb..8f87fdc17891c5 100644 --- a/deps/npm/lib/commands/doctor.js +++ b/deps/npm/lib/commands/doctor.js @@ -1,6 +1,6 @@ const cacache = require('cacache') const { access, lstat, readdir, constants: { R_OK, W_OK, X_OK } } = require('node:fs/promises') -const fetch = require('make-fetch-happen') +const npmFetch = require('make-fetch-happen') const which = require('which') const pacote = require('pacote') const { resolve } = require('node:path') @@ -166,7 +166,7 @@ class Doctor extends BaseCommand { const currentRange = `^${current}` const url = 'https://nodejs.org/dist/index.json' log.info('doctor', 'Getting Node.js release information') - const res = await fetch(url, { method: 'GET', ...this.npm.flatOptions }) + const res = await npmFetch(url, { method: 'GET', ...this.npm.flatOptions }) const data = await res.json() let maxCurrent = '0.0.0' let maxLTS = '0.0.0' @@ -246,7 +246,7 @@ class Doctor extends BaseCommand { try { await access(f, mask) - } catch (er) { + } catch { ok = false const msg = `Missing permissions on ${f} (expect: ${maskLabel(mask)})` log.error('doctor', 'checkFilesPermission', msg) diff --git a/deps/npm/lib/commands/explain.js b/deps/npm/lib/commands/explain.js index cb0644304d2b55..1505b4dbf5e9a4 100644 --- a/deps/npm/lib/commands/explain.js +++ b/deps/npm/lib/commands/explain.js @@ -109,7 +109,7 @@ class Explain extends ArboristWorkspaceCmd { // otherwise, try to select all matching nodes try { return this.getNodesByVersion(tree, arg) - } catch (er) { + } catch { return [] } } diff --git a/deps/npm/lib/commands/hook.js b/deps/npm/lib/commands/hook.js deleted file mode 100644 index 5793b974197c8d..00000000000000 --- a/deps/npm/lib/commands/hook.js +++ /dev/null @@ -1,109 +0,0 @@ -const hookApi = require('libnpmhook') -const { otplease } = require('../utils/auth.js') -const relativeDate = require('tiny-relative-date') -const { output } = require('proc-log') -const BaseCommand = require('../base-cmd.js') - -class Hook extends BaseCommand { - static description = 'Manage registry hooks' - static name = 'hook' - static params = [ - 'registry', - 'otp', - ] - - static usage = [ - 'add [--type=]', - 'ls [pkg]', - 'rm ', - 'update ', - ] - - async exec (args) { - return otplease(this.npm, { ...this.npm.flatOptions }, (opts) => { - switch (args[0]) { - case 'add': - return this.add(args[1], args[2], args[3], opts) - case 'ls': - return this.ls(args[1], opts) - case 'rm': - return this.rm(args[1], opts) - case 'update': - case 'up': - return this.update(args[1], args[2], args[3], opts) - default: - throw this.usageError() - } - }) - } - - async add (pkg, uri, secret, opts) { - const hook = await hookApi.add(pkg, uri, secret, opts) - if (opts.json) { - output.buffer(hook) - } else if (opts.parseable) { - output.standard(Object.keys(hook).join('\t')) - output.standard(Object.keys(hook).map(k => hook[k]).join('\t')) - } else if (!this.npm.silent) { - output.standard(`+ ${this.hookName(hook)} ${opts.unicode ? ' ➜ ' : ' -> '} ${hook.endpoint}`) - } - } - - async ls (pkg, opts) { - const hooks = await hookApi.ls({ ...opts, package: pkg }) - - if (opts.json) { - output.buffer(hooks) - } else if (opts.parseable) { - output.standard(Object.keys(hooks[0]).join('\t')) - hooks.forEach(hook => { - output.standard(Object.keys(hook).map(k => hook[k]).join('\t')) - }) - } else if (!hooks.length) { - output.standard("You don't have any hooks configured yet.") - } else if (!this.npm.silent) { - output.standard(`You have ${hooks.length} hook${hooks.length !== 1 ? 's' : ''} configured.`) - - for (const hook of hooks) { - output.standard(`Hook ${hook.id}: ${this.hookName(hook)}`) - output.standard(`Endpoint: ${hook.endpoint}`) - if (hook.last_delivery) { - /* eslint-disable-next-line max-len */ - output.standard(`Triggered ${relativeDate(hook.last_delivery)}, response code was "${hook.response_code}"\n`) - } else { - output.standard('Never triggered\n') - } - } - } - } - - async rm (id, opts) { - const hook = await hookApi.rm(id, opts) - if (opts.json) { - output.buffer(hook) - } else if (opts.parseable) { - output.standard(Object.keys(hook).join('\t')) - output.standard(Object.keys(hook).map(k => hook[k]).join('\t')) - } else if (!this.npm.silent) { - output.standard(`- ${this.hookName(hook)} ${opts.unicode ? ' ✘ ' : ' X '} ${hook.endpoint}`) - } - } - - async update (id, uri, secret, opts) { - const hook = await hookApi.update(id, uri, secret, opts) - if (opts.json) { - output.buffer(hook) - } else if (opts.parseable) { - output.standard(Object.keys(hook).join('\t')) - output.standard(Object.keys(hook).map(k => hook[k]).join('\t')) - } else if (!this.npm.silent) { - output.standard(`+ ${this.hookName(hook)} ${opts.unicode ? ' ➜ ' : ' -> '} ${hook.endpoint}`) - } - } - - hookName (hook) { - return `${hook.type === 'owner' ? '~' : ''}${hook.name}` - } -} - -module.exports = Hook diff --git a/deps/npm/lib/commands/init.js b/deps/npm/lib/commands/init.js index 09e8d8522f7f31..bef54b0e4138d9 100644 --- a/deps/npm/lib/commands/init.js +++ b/deps/npm/lib/commands/init.js @@ -192,7 +192,7 @@ class Init extends BaseCommand { // top-level package.json try { statSync(resolve(workspacePath, 'package.json')) - } catch (err) { + } catch { return } diff --git a/deps/npm/lib/commands/install.js b/deps/npm/lib/commands/install.js index 24e5f6819b3141..71f4229bb25664 100644 --- a/deps/npm/lib/commands/install.js +++ b/deps/npm/lib/commands/install.js @@ -68,7 +68,7 @@ class Install extends ArboristWorkspaceCmd { const contents = await readdir(join(partialPath, sibling)) const result = (contents.indexOf('package.json') !== -1) return result - } catch (er) { + } catch { return false } } @@ -86,7 +86,7 @@ class Install extends ArboristWorkspaceCmd { } // no matches return [] - } catch (er) { + } catch { return [] // invalid dir: no matching } } diff --git a/deps/npm/lib/commands/ls.js b/deps/npm/lib/commands/ls.js index 417cb1b40d8c25..bc8beb007e8093 100644 --- a/deps/npm/lib/commands/ls.js +++ b/deps/npm/lib/commands/ls.js @@ -233,7 +233,7 @@ const isGitNode = (node) => { try { const { type } = npa(node.resolved) return type === 'git' || type === 'hosted' - } catch (err) { + } catch { return false } } diff --git a/deps/npm/lib/commands/pack.js b/deps/npm/lib/commands/pack.js index 79e7f49f819ecc..bd190d88d82b68 100644 --- a/deps/npm/lib/commands/pack.js +++ b/deps/npm/lib/commands/pack.js @@ -15,6 +15,7 @@ class Pack extends BaseCommand { 'workspace', 'workspaces', 'include-workspace-root', + 'ignore-scripts', ] static usage = [''] @@ -29,12 +30,13 @@ class Pack extends BaseCommand { const unicode = this.npm.config.get('unicode') const json = this.npm.config.get('json') + const Arborist = require('@npmcli/arborist') // Get the manifests and filenames first so we can bail early on manifest // errors before making any tarballs const manifests = [] for (const arg of args) { const spec = npa(arg) - const manifest = await pacote.manifest(spec, this.npm.flatOptions) + const manifest = await pacote.manifest(spec, { ...this.npm.flatOptions, Arborist }) if (!manifest._id) { throw new Error('Invalid package, must have name and version') } diff --git a/deps/npm/lib/commands/pkg.js b/deps/npm/lib/commands/pkg.js index a011fc10be1070..5a236f6e622709 100644 --- a/deps/npm/lib/commands/pkg.js +++ b/deps/npm/lib/commands/pkg.js @@ -63,12 +63,12 @@ class Pkg extends BaseCommand { if (args.length) { result = new Queryable(result).query(args) - // in case there's only a single result from the query - // just prints that one element to stdout + // in case there's only a single argument and a single result from the query + // just prints that one element to stdout. // TODO(BREAKING_CHANGE): much like other places where we unwrap single // item arrays this should go away. it makes the behavior unknown for users // who don't already know the shape of the data. - if (Object.keys(result).length === 1) { + if (Object.keys(result).length === 1 && args.length === 1) { result = result[args] } } diff --git a/deps/npm/lib/commands/prefix.js b/deps/npm/lib/commands/prefix.js index da8702cf91caaf..907ed5af2c1799 100644 --- a/deps/npm/lib/commands/prefix.js +++ b/deps/npm/lib/commands/prefix.js @@ -5,7 +5,6 @@ class Prefix extends BaseCommand { static description = 'Display prefix' static name = 'prefix' static params = ['global'] - static usage = ['[-g]'] async exec () { return output.standard(this.npm.prefix) diff --git a/deps/npm/lib/commands/publish.js b/deps/npm/lib/commands/publish.js index c2cd3cf9c927b6..c59588fefb241f 100644 --- a/deps/npm/lib/commands/publish.js +++ b/deps/npm/lib/commands/publish.js @@ -116,6 +116,13 @@ class Publish extends BaseCommand { // note that publishConfig might have changed as well! manifest = await this.#getManifest(spec, opts, true) + const isPreRelease = Boolean(semver.parse(manifest.version).prerelease.length) + const isDefaultTag = this.npm.config.isDefault('tag') + + if (isPreRelease && isDefaultTag) { + throw new Error('You must specify a tag using --tag when publishing a prerelease version.') + } + // If we are not in JSON mode then we show the user the contents of the tarball // before it is published so they can see it while their otp is pending if (!json) { @@ -150,6 +157,14 @@ class Publish extends BaseCommand { } } + const latestVersion = await this.#latestPublishedVersion(resolved, registry) + const latestSemverIsGreater = !!latestVersion && semver.gte(latestVersion, manifest.version) + + if (latestSemverIsGreater && isDefaultTag) { + /* eslint-disable-next-line max-len */ + throw new Error(`Cannot implicitly apply the "latest" tag because published version ${latestVersion} is higher than the new version ${manifest.version}. You must specify a tag using --tag.`) + } + const access = opts.access === null ? 'default' : opts.access let msg = `Publishing to ${outputRegistry} with tag ${defaultTag} and ${access} access` if (dryRun) { @@ -189,6 +204,28 @@ class Publish extends BaseCommand { } } + async #latestPublishedVersion (spec, registry) { + try { + const packument = await pacote.packument(spec, { + ...this.npm.flatOptions, + preferOnline: true, + registry, + }) + if (typeof packument?.versions === 'undefined') { + return null + } + const ordered = Object.keys(packument?.versions) + .flatMap(v => { + const s = new semver.SemVer(v) + return s.prerelease.length > 0 ? [] : s + }) + .sort((a, b) => b.compare(a)) + return ordered.length >= 1 ? ordered[0].version : null + } catch (e) { + return null + } + } + // if it's a directory, read it from the file system // otherwise, get the full metadata from whatever it is // XXX can't pacote read the manifest from a directory? diff --git a/deps/npm/lib/commands/repo.js b/deps/npm/lib/commands/repo.js index 3f120c0a3f59f5..0bfb2cf962c9b0 100644 --- a/deps/npm/lib/commands/repo.js +++ b/deps/npm/lib/commands/repo.js @@ -49,7 +49,7 @@ const unknownHostedUrl = url => { const proto = /(git\+)http:$/.test(protocol) ? 'http:' : 'https:' const path = pathname.replace(/\.git$/, '') return `${proto}//${hostname}${path}` - } catch (e) { + } catch { return null } } diff --git a/deps/npm/lib/commands/star.js b/deps/npm/lib/commands/star.js index 1b76955810c726..7d1be1d389730d 100644 --- a/deps/npm/lib/commands/star.js +++ b/deps/npm/lib/commands/star.js @@ -1,4 +1,4 @@ -const fetch = require('npm-registry-fetch') +const npmFetch = require('npm-registry-fetch') const npa = require('npm-package-arg') const { log, output } = require('proc-log') const getIdentity = require('../utils/get-identity') @@ -32,7 +32,7 @@ class Star extends BaseCommand { const username = await getIdentity(this.npm, this.npm.flatOptions) for (const pkg of pkgs) { - const fullData = await fetch.json(pkg.escapedName, { + const fullData = await npmFetch.json(pkg.escapedName, { ...this.npm.flatOptions, spec: pkg, query: { write: true }, @@ -55,7 +55,7 @@ class Star extends BaseCommand { log.verbose('unstar', 'unstarring', body) } - const data = await fetch.json(pkg.escapedName, { + const data = await npmFetch.json(pkg.escapedName, { ...this.npm.flatOptions, spec: pkg, method: 'PUT', diff --git a/deps/npm/lib/commands/stars.js b/deps/npm/lib/commands/stars.js index 1059569979dafe..d059d01250900b 100644 --- a/deps/npm/lib/commands/stars.js +++ b/deps/npm/lib/commands/stars.js @@ -1,4 +1,4 @@ -const fetch = require('npm-registry-fetch') +const npmFetch = require('npm-registry-fetch') const { log, output } = require('proc-log') const getIdentity = require('../utils/get-identity.js') const BaseCommand = require('../base-cmd.js') @@ -16,7 +16,7 @@ class Stars extends BaseCommand { user = await getIdentity(this.npm, this.npm.flatOptions) } - const { rows } = await fetch.json('/-/_view/starredByUser', { + const { rows } = await npmFetch.json('/-/_view/starredByUser', { ...this.npm.flatOptions, query: { key: `"${user}"` }, }) diff --git a/deps/npm/lib/commands/view.js b/deps/npm/lib/commands/view.js index cf7292a2f3b814..627c126f00c21c 100644 --- a/deps/npm/lib/commands/view.js +++ b/deps/npm/lib/commands/view.js @@ -266,6 +266,18 @@ class View extends BaseCommand { const deps = Object.entries(manifest.dependencies || {}).map(([k, dep]) => `${chalk.blue(k)}: ${dep}` ) + // Sort dist-tags by publish time, then tag name, keeping latest at the top of the list + const distTags = Object.entries(packu['dist-tags']) + .sort(([aTag, aVer], [bTag, bVer]) => { + const aTime = aTag === 'latest' ? Infinity : Date.parse(packu.time[aVer]) + const bTime = bTag === 'latest' ? Infinity : Date.parse(packu.time[bVer]) + if (aTime === bTime) { + return aTag > bTag ? -1 : 1 + } + return aTime > bTime ? -1 : 1 + }) + .map(([k, t]) => `${chalk.blue(k)}: ${t}`) + const site = manifest.homepage?.url || manifest.homepage const bins = Object.keys(manifest.bin || {}) const licenseField = manifest.license || 'Proprietary' @@ -333,9 +345,11 @@ class View extends BaseCommand { } res.push('\ndist-tags:') - res.push(columns(Object.entries(packu['dist-tags']).map(([k, t]) => - `${chalk.blue(k)}: ${t}` - ))) + const maxTags = 12 + res.push(columns(distTags.slice(0, maxTags), { padding: 1, sort: false })) + if (distTags.length > maxTags) { + res.push(chalk.dim(`(...and ${distTags.length - maxTags} more.)`)) + } const publisher = manifest._npmUser && unparsePerson({ name: chalk.blue(manifest._npmUser.name), diff --git a/deps/npm/lib/utils/cmd-list.js b/deps/npm/lib/utils/cmd-list.js index 9017b2b80ce527..039d6ffddeb161 100644 --- a/deps/npm/lib/utils/cmd-list.js +++ b/deps/npm/lib/utils/cmd-list.js @@ -26,7 +26,6 @@ const commands = [ 'get', 'help', 'help-search', - 'hook', 'init', 'install', 'install-ci-test', diff --git a/deps/npm/lib/utils/ping.js b/deps/npm/lib/utils/ping.js index 1c8c9e827a4eaa..3d47ca1ecaf545 100644 --- a/deps/npm/lib/utils/ping.js +++ b/deps/npm/lib/utils/ping.js @@ -1,7 +1,7 @@ // ping the npm registry // used by the ping and doctor commands -const fetch = require('npm-registry-fetch') +const npmFetch = require('npm-registry-fetch') module.exports = async (flatOptions) => { - const res = await fetch('/-/ping', { ...flatOptions, cache: false }) + const res = await npmFetch('/-/ping', { ...flatOptions, cache: false }) return res.json().catch(() => ({})) } diff --git a/deps/npm/lib/utils/sbom-cyclonedx.js b/deps/npm/lib/utils/sbom-cyclonedx.js index 989abea58dae83..f3bab28000953f 100644 --- a/deps/npm/lib/utils/sbom-cyclonedx.js +++ b/deps/npm/lib/utils/sbom-cyclonedx.js @@ -94,7 +94,7 @@ const toCyclonedxItem = (node, { packageType }) => { } parsedLicense = parseLicense(license) - } catch (err) { + } catch { parsedLicense = null } @@ -192,7 +192,7 @@ const isGitNode = (node) => { try { const { type } = npa(node.resolved) return type === 'git' || type === 'hosted' - } catch (err) { + } catch { /* istanbul ignore next */ return false } diff --git a/deps/npm/lib/utils/sbom-spdx.js b/deps/npm/lib/utils/sbom-spdx.js index e3af77e10c7513..16aed186567640 100644 --- a/deps/npm/lib/utils/sbom-spdx.js +++ b/deps/npm/lib/utils/sbom-spdx.js @@ -173,7 +173,7 @@ const isGitNode = (node) => { try { const { type } = npa(node.resolved) return type === 'git' || type === 'hosted' - } catch (err) { + } catch { /* istanbul ignore next */ return false } diff --git a/deps/npm/lib/utils/verify-signatures.js b/deps/npm/lib/utils/verify-signatures.js index 09711581d11ddd..0a32742b5ee2a2 100644 --- a/deps/npm/lib/utils/verify-signatures.js +++ b/deps/npm/lib/utils/verify-signatures.js @@ -1,8 +1,7 @@ -const fetch = require('npm-registry-fetch') +const npmFetch = require('npm-registry-fetch') const localeCompare = require('@isaacs/string-locale-compare')('en') const npa = require('npm-package-arg') const pacote = require('pacote') -const pMap = require('p-map') const tufClient = require('@sigstore/tuf') const { log, output } = require('proc-log') @@ -26,6 +25,7 @@ class VerifySignatures { async run () { const start = process.hrtime.bigint() + const { default: pMap } = await import('p-map') // Find all deps in tree const { edges, registries } = this.getEdgesOut(this.tree.inventory.values(), this.filterSet) @@ -202,7 +202,7 @@ class VerifySignatures { // If keys not found in Sigstore TUF repo, fallback to registry keys API if (!keys) { - keys = await fetch.json('/-/npm/v1/keys', { + keys = await npmFetch.json('/-/npm/v1/keys', { ...this.npm.flatOptions, registry, }).then(({ keys: ks }) => ks.map((key) => ({ @@ -253,7 +253,7 @@ class VerifySignatures { } getSpecRegistry (spec) { - return fetch.pickRegistry(spec, this.npm.flatOptions) + return npmFetch.pickRegistry(spec, this.npm.flatOptions) } getValidPackageInfo (edge) { diff --git a/deps/npm/man/man1/npm-access.1 b/deps/npm/man/man1/npm-access.1 index 96beab28fc365d..7352f7065abb39 100644 --- a/deps/npm/man/man1/npm-access.1 +++ b/deps/npm/man/man1/npm-access.1 @@ -1,4 +1,4 @@ -.TH "NPM-ACCESS" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-ACCESS" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-access\fR - Set access level on published packages .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-adduser.1 b/deps/npm/man/man1/npm-adduser.1 index 0096f28229122d..283274b7b9b03f 100644 --- a/deps/npm/man/man1/npm-adduser.1 +++ b/deps/npm/man/man1/npm-adduser.1 @@ -1,4 +1,4 @@ -.TH "NPM-ADDUSER" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-ADDUSER" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-adduser\fR - Add a registry user account .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-audit.1 b/deps/npm/man/man1/npm-audit.1 index 38b977d3871d59..39236214f2dc74 100644 --- a/deps/npm/man/man1/npm-audit.1 +++ b/deps/npm/man/man1/npm-audit.1 @@ -1,4 +1,4 @@ -.TH "NPM-AUDIT" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-AUDIT" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-audit\fR - Run a security audit .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-bugs.1 b/deps/npm/man/man1/npm-bugs.1 index 92c57c2c3c4146..a4cccc82794e63 100644 --- a/deps/npm/man/man1/npm-bugs.1 +++ b/deps/npm/man/man1/npm-bugs.1 @@ -1,4 +1,4 @@ -.TH "NPM-BUGS" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-BUGS" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-bugs\fR - Report bugs for a package in a web browser .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-cache.1 b/deps/npm/man/man1/npm-cache.1 index da188a0914e016..c1d5a37fdc8f4f 100644 --- a/deps/npm/man/man1/npm-cache.1 +++ b/deps/npm/man/man1/npm-cache.1 @@ -1,4 +1,4 @@ -.TH "NPM-CACHE" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-CACHE" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-cache\fR - Manipulates packages cache .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-ci.1 b/deps/npm/man/man1/npm-ci.1 index 1ae5e934e2d2b7..5eb18d43735f08 100644 --- a/deps/npm/man/man1/npm-ci.1 +++ b/deps/npm/man/man1/npm-ci.1 @@ -1,4 +1,4 @@ -.TH "NPM-CI" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-CI" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-ci\fR - Clean install a project .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-completion.1 b/deps/npm/man/man1/npm-completion.1 index ea44a0c9c56a51..260445e84df4fd 100644 --- a/deps/npm/man/man1/npm-completion.1 +++ b/deps/npm/man/man1/npm-completion.1 @@ -1,4 +1,4 @@ -.TH "NPM-COMPLETION" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-COMPLETION" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-completion\fR - Tab Completion for npm .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-config.1 b/deps/npm/man/man1/npm-config.1 index dbe609ba3b727f..11461b0eedb871 100644 --- a/deps/npm/man/man1/npm-config.1 +++ b/deps/npm/man/man1/npm-config.1 @@ -1,4 +1,4 @@ -.TH "NPM-CONFIG" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-CONFIG" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-config\fR - Manage the npm configuration files .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-dedupe.1 b/deps/npm/man/man1/npm-dedupe.1 index 530de6344d5f4e..4753980016b312 100644 --- a/deps/npm/man/man1/npm-dedupe.1 +++ b/deps/npm/man/man1/npm-dedupe.1 @@ -1,4 +1,4 @@ -.TH "NPM-DEDUPE" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-DEDUPE" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-dedupe\fR - Reduce duplication in the package tree .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-deprecate.1 b/deps/npm/man/man1/npm-deprecate.1 index 0333772b97c690..80ff50236102a5 100644 --- a/deps/npm/man/man1/npm-deprecate.1 +++ b/deps/npm/man/man1/npm-deprecate.1 @@ -1,4 +1,4 @@ -.TH "NPM-DEPRECATE" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-DEPRECATE" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-deprecate\fR - Deprecate a version of a package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-diff.1 b/deps/npm/man/man1/npm-diff.1 index 168f4397430854..442ea98f4823e4 100644 --- a/deps/npm/man/man1/npm-diff.1 +++ b/deps/npm/man/man1/npm-diff.1 @@ -1,4 +1,4 @@ -.TH "NPM-DIFF" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-DIFF" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-diff\fR - The registry diff command .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-dist-tag.1 b/deps/npm/man/man1/npm-dist-tag.1 index 97e2927ac1e2fa..839891720c68e1 100644 --- a/deps/npm/man/man1/npm-dist-tag.1 +++ b/deps/npm/man/man1/npm-dist-tag.1 @@ -1,4 +1,4 @@ -.TH "NPM-DIST-TAG" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-DIST-TAG" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-dist-tag\fR - Modify package distribution tags .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-docs.1 b/deps/npm/man/man1/npm-docs.1 index 6b09a835bbf318..fa1ecaa6cd7e24 100644 --- a/deps/npm/man/man1/npm-docs.1 +++ b/deps/npm/man/man1/npm-docs.1 @@ -1,4 +1,4 @@ -.TH "NPM-DOCS" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-DOCS" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-docs\fR - Open documentation for a package in a web browser .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-doctor.1 b/deps/npm/man/man1/npm-doctor.1 index 9a0374b0896b52..8924a8158fa7eb 100644 --- a/deps/npm/man/man1/npm-doctor.1 +++ b/deps/npm/man/man1/npm-doctor.1 @@ -1,4 +1,4 @@ -.TH "NPM-DOCTOR" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-DOCTOR" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-doctor\fR - Check the health of your npm environment .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-edit.1 b/deps/npm/man/man1/npm-edit.1 index 779feeede13656..dc41757464b840 100644 --- a/deps/npm/man/man1/npm-edit.1 +++ b/deps/npm/man/man1/npm-edit.1 @@ -1,4 +1,4 @@ -.TH "NPM-EDIT" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-EDIT" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-edit\fR - Edit an installed package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-exec.1 b/deps/npm/man/man1/npm-exec.1 index 3b5fe4c425ee3e..a959ff3e72e4d4 100644 --- a/deps/npm/man/man1/npm-exec.1 +++ b/deps/npm/man/man1/npm-exec.1 @@ -1,4 +1,4 @@ -.TH "NPM-EXEC" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-EXEC" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-exec\fR - Run a command from a local or remote npm package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-explain.1 b/deps/npm/man/man1/npm-explain.1 index 5f86d19236fdb0..efef3975aa28a2 100644 --- a/deps/npm/man/man1/npm-explain.1 +++ b/deps/npm/man/man1/npm-explain.1 @@ -1,4 +1,4 @@ -.TH "NPM-EXPLAIN" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-EXPLAIN" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-explain\fR - Explain installed packages .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-explore.1 b/deps/npm/man/man1/npm-explore.1 index eb71089b62446f..47b672d843ffe0 100644 --- a/deps/npm/man/man1/npm-explore.1 +++ b/deps/npm/man/man1/npm-explore.1 @@ -1,4 +1,4 @@ -.TH "NPM-EXPLORE" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-EXPLORE" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-explore\fR - Browse an installed package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-find-dupes.1 b/deps/npm/man/man1/npm-find-dupes.1 index 88a42adcec1f20..bc302ea525518f 100644 --- a/deps/npm/man/man1/npm-find-dupes.1 +++ b/deps/npm/man/man1/npm-find-dupes.1 @@ -1,4 +1,4 @@ -.TH "NPM-FIND-DUPES" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-FIND-DUPES" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-find-dupes\fR - Find duplication in the package tree .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-fund.1 b/deps/npm/man/man1/npm-fund.1 index 31c5ceb7ff8c1f..86b8034a0747d1 100644 --- a/deps/npm/man/man1/npm-fund.1 +++ b/deps/npm/man/man1/npm-fund.1 @@ -1,4 +1,4 @@ -.TH "NPM-FUND" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-FUND" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-fund\fR - Retrieve funding information .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-help-search.1 b/deps/npm/man/man1/npm-help-search.1 index c954795c2778c7..7178197085b95e 100644 --- a/deps/npm/man/man1/npm-help-search.1 +++ b/deps/npm/man/man1/npm-help-search.1 @@ -1,4 +1,4 @@ -.TH "NPM-HELP-SEARCH" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-HELP-SEARCH" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-help-search\fR - Search npm help documentation .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-help.1 b/deps/npm/man/man1/npm-help.1 index 2d0fc6e3a2c0e7..09e59985599d80 100644 --- a/deps/npm/man/man1/npm-help.1 +++ b/deps/npm/man/man1/npm-help.1 @@ -1,4 +1,4 @@ -.TH "NPM-HELP" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-HELP" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-help\fR - Get help on npm .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-hook.1 b/deps/npm/man/man1/npm-hook.1 deleted file mode 100644 index 8d5d9334692b9e..00000000000000 --- a/deps/npm/man/man1/npm-hook.1 +++ /dev/null @@ -1,115 +0,0 @@ -.TH "NPM-HOOK" "1" "December 2024" "NPM@10.9.2" "" -.SH "NAME" -\fBnpm-hook\fR - Manage registry hooks -.SS "Synopsis" -.P -.RS 2 -.nf -npm hook add \[lB]--type=\[rB] -npm hook ls \[lB]pkg\[rB] -npm hook rm -npm hook update -.fi -.RE -.P -Note: This command is unaware of workspaces. -.SS "Description" -.P -Allows you to manage \fBnpm hooks\fR \fI\(lahttps://blog.npmjs.org/post/145260155635/introducing-hooks-get-notifications-of-npm\(ra\fR, including adding, removing, listing, and updating. -.P -Hooks allow you to configure URL endpoints that will be notified whenever a change happens to any of the supported entity types. Three different types of entities can be watched by hooks: packages, owners, and scopes. -.P -To create a package hook, simply reference the package name. -.P -To create an owner hook, prefix the owner name with \fB~\fR (as in, \fB~youruser\fR). -.P -To create a scope hook, prefix the scope name with \fB@\fR (as in, \fB@yourscope\fR). -.P -The hook \fBid\fR used by \fBupdate\fR and \fBrm\fR are the IDs listed in \fBnpm hook ls\fR for that particular hook. -.P -The shared secret will be sent along to the URL endpoint so you can verify the request came from your own configured hook. -.SS "Example" -.P -Add a hook to watch a package for changes: -.P -.RS 2 -.nf -$ npm hook add lodash https://example.com/ my-shared-secret -.fi -.RE -.P -Add a hook to watch packages belonging to the user \fBsubstack\fR: -.P -.RS 2 -.nf -$ npm hook add ~substack https://example.com/ my-shared-secret -.fi -.RE -.P -Add a hook to watch packages in the scope \fB@npm\fR -.P -.RS 2 -.nf -$ npm hook add @npm https://example.com/ my-shared-secret -.fi -.RE -.P -List all your active hooks: -.P -.RS 2 -.nf -$ npm hook ls -.fi -.RE -.P -List your active hooks for the \fBlodash\fR package: -.P -.RS 2 -.nf -$ npm hook ls lodash -.fi -.RE -.P -Update an existing hook's url: -.P -.RS 2 -.nf -$ npm hook update id-deadbeef https://my-new-website.here/ -.fi -.RE -.P -Remove a hook: -.P -.RS 2 -.nf -$ npm hook rm id-deadbeef -.fi -.RE -.SS "Configuration" -.SS "\fBregistry\fR" -.RS 0 -.IP \(bu 4 -Default: "https://registry.npmjs.org/" -.IP \(bu 4 -Type: URL -.RE 0 - -.P -The base URL of the npm registry. -.SS "\fBotp\fR" -.RS 0 -.IP \(bu 4 -Default: null -.IP \(bu 4 -Type: null or String -.RE 0 - -.P -This is a one-time password from a two-factor authenticator. It's needed when publishing or changing package permissions with \fBnpm access\fR. -.P -If not set, and a registry response fails with a challenge for a one-time password, npm will prompt on the command line for one. -.SS "See Also" -.RS 0 -.IP \(bu 4 -\fB"Introducing Hooks" blog post\fR \fI\(lahttps://blog.npmjs.org/post/145260155635/introducing-hooks-get-notifications-of-npm\(ra\fR -.RE 0 diff --git a/deps/npm/man/man1/npm-init.1 b/deps/npm/man/man1/npm-init.1 index 1c1b008baa1deb..ace9295a8a95a3 100644 --- a/deps/npm/man/man1/npm-init.1 +++ b/deps/npm/man/man1/npm-init.1 @@ -1,4 +1,4 @@ -.TH "NPM-INIT" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-INIT" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-init\fR - Create a package.json file .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-install-ci-test.1 b/deps/npm/man/man1/npm-install-ci-test.1 index 4bbbb51140521b..72fc3a8595d262 100644 --- a/deps/npm/man/man1/npm-install-ci-test.1 +++ b/deps/npm/man/man1/npm-install-ci-test.1 @@ -1,4 +1,4 @@ -.TH "NPM-INSTALL-CI-TEST" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-INSTALL-CI-TEST" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-install-ci-test\fR - Install a project with a clean slate and run tests .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-install-test.1 b/deps/npm/man/man1/npm-install-test.1 index eef940c066c709..20320bba890ed0 100644 --- a/deps/npm/man/man1/npm-install-test.1 +++ b/deps/npm/man/man1/npm-install-test.1 @@ -1,4 +1,4 @@ -.TH "NPM-INSTALL-TEST" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-INSTALL-TEST" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-install-test\fR - Install package(s) and run tests .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-install.1 b/deps/npm/man/man1/npm-install.1 index bbe4a43c10863e..3b32711397ba25 100644 --- a/deps/npm/man/man1/npm-install.1 +++ b/deps/npm/man/man1/npm-install.1 @@ -1,4 +1,4 @@ -.TH "NPM-INSTALL" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-INSTALL" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-install\fR - Install a package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-link.1 b/deps/npm/man/man1/npm-link.1 index 9aca1f4a7fb80d..0ed6887d3c0d88 100644 --- a/deps/npm/man/man1/npm-link.1 +++ b/deps/npm/man/man1/npm-link.1 @@ -1,4 +1,4 @@ -.TH "NPM-LINK" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-LINK" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-link\fR - Symlink a package folder .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-login.1 b/deps/npm/man/man1/npm-login.1 index a5e0cb71bb0d98..e7e7cc4cd88bc8 100644 --- a/deps/npm/man/man1/npm-login.1 +++ b/deps/npm/man/man1/npm-login.1 @@ -1,4 +1,4 @@ -.TH "NPM-LOGIN" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-LOGIN" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-login\fR - Login to a registry user account .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-logout.1 b/deps/npm/man/man1/npm-logout.1 index 8ac69429ea320d..97d75001502a19 100644 --- a/deps/npm/man/man1/npm-logout.1 +++ b/deps/npm/man/man1/npm-logout.1 @@ -1,4 +1,4 @@ -.TH "NPM-LOGOUT" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-LOGOUT" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-logout\fR - Log out of the registry .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1 index 47d20120898b59..15e44a19c6768e 100644 --- a/deps/npm/man/man1/npm-ls.1 +++ b/deps/npm/man/man1/npm-ls.1 @@ -1,4 +1,4 @@ -.TH "NPM-LS" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-LS" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-ls\fR - List installed packages .SS "Synopsis" @@ -20,7 +20,7 @@ Positional arguments are \fBname@version-range\fR identifiers, which will limit .P .RS 2 .nf -npm@10.9.2 /path/to/npm +npm@11.0.0 /path/to/npm └─┬ init-package-json@0.0.4 └── promzard@0.1.5 .fi @@ -33,18 +33,6 @@ If a project specifies git urls for dependencies these are shown in parentheses The tree shown is the logical dependency tree, based on package dependencies, not the physical layout of your \fBnode_modules\fR folder. .P When run as \fBll\fR or \fBla\fR, it shows extended information by default. -.SS "Note: Design Changes Pending" -.P -The \fBnpm ls\fR command's output and behavior made a \fIton\fR of sense when npm created a \fBnode_modules\fR folder that naively nested every dependency. In such a case, the logical dependency graph and physical tree of packages on disk would be roughly identical. -.P -With the advent of automatic install-time deduplication of dependencies in npm v3, the \fBls\fR output was modified to display the logical dependency graph as a tree structure, since this was more useful to most users. However, without using \fBnpm ls -l\fR, it became impossible to show \fIwhere\fR a package was actually installed much of the time! -.P -With the advent of automatic installation of \fBpeerDependencies\fR in npm v7, this gets even more curious, as \fBpeerDependencies\fR are logically "underneath" their dependents in the dependency graph, but are always physically at or above their location on disk. -.P -Also, in the years since npm got an \fBls\fR command (in version 0.0.2!), dependency graphs have gotten much larger as a general rule. Therefore, in order to avoid dumping an excessive amount of content to the terminal, \fBnpm -ls\fR now only shows the \fItop\fR level dependencies, unless \fB--all\fR is provided. -.P -A thorough re-examination of the use cases, intention, behavior, and output of this command, is currently underway. Expect significant changes to at least the default human-readable \fBnpm ls\fR output in npm v8. .SS "Configuration" .SS "\fBall\fR" .RS 0 diff --git a/deps/npm/man/man1/npm-org.1 b/deps/npm/man/man1/npm-org.1 index 88df3325a344fd..daca78255f42b9 100644 --- a/deps/npm/man/man1/npm-org.1 +++ b/deps/npm/man/man1/npm-org.1 @@ -1,4 +1,4 @@ -.TH "NPM-ORG" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-ORG" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-org\fR - Manage orgs .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-outdated.1 b/deps/npm/man/man1/npm-outdated.1 index 17aabbd0857183..aab4bdff5e9465 100644 --- a/deps/npm/man/man1/npm-outdated.1 +++ b/deps/npm/man/man1/npm-outdated.1 @@ -1,4 +1,4 @@ -.TH "NPM-OUTDATED" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-OUTDATED" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-outdated\fR - Check for outdated packages .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-owner.1 b/deps/npm/man/man1/npm-owner.1 index f01e67e6464e7f..6cf50562ce259a 100644 --- a/deps/npm/man/man1/npm-owner.1 +++ b/deps/npm/man/man1/npm-owner.1 @@ -1,4 +1,4 @@ -.TH "NPM-OWNER" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-OWNER" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-owner\fR - Manage package owners .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-pack.1 b/deps/npm/man/man1/npm-pack.1 index b9b56a31e30eef..a6aa420eb4022e 100644 --- a/deps/npm/man/man1/npm-pack.1 +++ b/deps/npm/man/man1/npm-pack.1 @@ -1,4 +1,4 @@ -.TH "NPM-PACK" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-PACK" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-pack\fR - Create a tarball from a package .SS "Synopsis" @@ -106,6 +106,18 @@ Include the workspace root when workspaces are enabled for a command. When false, specifying individual workspaces via the \fBworkspace\fR config, or all workspaces via the \fBworkspaces\fR flag, will cause npm to operate only on the specified workspaces, and not on the root project. .P This value is not exported to the environment for child processes. +.SS "\fBignore-scripts\fR" +.RS 0 +.IP \(bu 4 +Default: false +.IP \(bu 4 +Type: Boolean +.RE 0 + +.P +If true, npm does not run scripts specified in package.json files. +.P +Note that commands explicitly intended to run a particular script, such as \fBnpm start\fR, \fBnpm stop\fR, \fBnpm restart\fR, \fBnpm test\fR, and \fBnpm run-script\fR will still run their intended script if \fBignore-scripts\fR is set, but they will \fInot\fR run any pre- or post-scripts. .SS "Description" .P For anything that's installable (that is, a package folder, tarball, tarball url, git url, name@tag, name@version, name, or scoped name), this command will fetch it to the cache, copy the tarball to the current working directory as \fB-.tgz\fR, and then write the filenames out to stdout. diff --git a/deps/npm/man/man1/npm-ping.1 b/deps/npm/man/man1/npm-ping.1 index 2d16431b030ea8..0f15acec8c41df 100644 --- a/deps/npm/man/man1/npm-ping.1 +++ b/deps/npm/man/man1/npm-ping.1 @@ -1,4 +1,4 @@ -.TH "NPM-PING" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-PING" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-ping\fR - Ping npm registry .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-pkg.1 b/deps/npm/man/man1/npm-pkg.1 index e55a0280d1211b..cfab22912461e3 100644 --- a/deps/npm/man/man1/npm-pkg.1 +++ b/deps/npm/man/man1/npm-pkg.1 @@ -1,4 +1,4 @@ -.TH "NPM-PKG" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-PKG" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-pkg\fR - Manages your package.json .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-prefix.1 b/deps/npm/man/man1/npm-prefix.1 index dca8d0c47497b4..cac17c68b27925 100644 --- a/deps/npm/man/man1/npm-prefix.1 +++ b/deps/npm/man/man1/npm-prefix.1 @@ -1,11 +1,11 @@ -.TH "NPM-PREFIX" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-PREFIX" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-prefix\fR - Display prefix .SS "Synopsis" .P .RS 2 .nf -npm prefix \[lB]-g\[rB] +npm prefix .fi .RE .P diff --git a/deps/npm/man/man1/npm-profile.1 b/deps/npm/man/man1/npm-profile.1 index 89837b408f5a67..0eed37569a2c23 100644 --- a/deps/npm/man/man1/npm-profile.1 +++ b/deps/npm/man/man1/npm-profile.1 @@ -1,4 +1,4 @@ -.TH "NPM-PROFILE" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-PROFILE" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-profile\fR - Change settings on your registry profile .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-prune.1 b/deps/npm/man/man1/npm-prune.1 index 243426dffaf20f..e01d867a0326ba 100644 --- a/deps/npm/man/man1/npm-prune.1 +++ b/deps/npm/man/man1/npm-prune.1 @@ -1,4 +1,4 @@ -.TH "NPM-PRUNE" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-PRUNE" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-prune\fR - Remove extraneous packages .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-publish.1 b/deps/npm/man/man1/npm-publish.1 index 1110b097182988..f584ca319bd356 100644 --- a/deps/npm/man/man1/npm-publish.1 +++ b/deps/npm/man/man1/npm-publish.1 @@ -1,4 +1,4 @@ -.TH "NPM-PUBLISH" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-PUBLISH" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-publish\fR - Publish a package .SS "Synopsis" @@ -60,6 +60,8 @@ Symbolic links are never included in npm packages. .P See npm help developers for full details on what's included in the published package, as well as details on how the package is built. +.P +See \fB\fBpackage.json\fR\fR \fI\(la/configuring-npm/package-json\(ra\fR for more info on what can and can't be ignored. .SS "Configuration" .SS "\fBtag\fR" .RS 0 diff --git a/deps/npm/man/man1/npm-query.1 b/deps/npm/man/man1/npm-query.1 index 983f2cad388940..83f5eb489e7c04 100644 --- a/deps/npm/man/man1/npm-query.1 +++ b/deps/npm/man/man1/npm-query.1 @@ -1,4 +1,4 @@ -.TH "NPM-QUERY" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-QUERY" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-query\fR - Dependency selector query .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-rebuild.1 b/deps/npm/man/man1/npm-rebuild.1 index 396d9adf779cb1..904ec74ac6ac29 100644 --- a/deps/npm/man/man1/npm-rebuild.1 +++ b/deps/npm/man/man1/npm-rebuild.1 @@ -1,4 +1,4 @@ -.TH "NPM-REBUILD" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-REBUILD" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-rebuild\fR - Rebuild a package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-repo.1 b/deps/npm/man/man1/npm-repo.1 index b9c7bbdfbc233c..1536ae5d38acf6 100644 --- a/deps/npm/man/man1/npm-repo.1 +++ b/deps/npm/man/man1/npm-repo.1 @@ -1,4 +1,4 @@ -.TH "NPM-REPO" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-REPO" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-repo\fR - Open package repository page in the browser .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-restart.1 b/deps/npm/man/man1/npm-restart.1 index 1112bf9180cece..c1f7d129365c1d 100644 --- a/deps/npm/man/man1/npm-restart.1 +++ b/deps/npm/man/man1/npm-restart.1 @@ -1,4 +1,4 @@ -.TH "NPM-RESTART" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-RESTART" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-restart\fR - Restart a package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-root.1 b/deps/npm/man/man1/npm-root.1 index 2456bdba6d1816..4f222a697a594b 100644 --- a/deps/npm/man/man1/npm-root.1 +++ b/deps/npm/man/man1/npm-root.1 @@ -1,4 +1,4 @@ -.TH "NPM-ROOT" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-ROOT" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-root\fR - Display npm root .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-run-script.1 b/deps/npm/man/man1/npm-run-script.1 index a05362361848d2..303dd432acf30f 100644 --- a/deps/npm/man/man1/npm-run-script.1 +++ b/deps/npm/man/man1/npm-run-script.1 @@ -1,4 +1,4 @@ -.TH "NPM-RUN-SCRIPT" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-RUN-SCRIPT" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-run-script\fR - Run arbitrary package scripts .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-sbom.1 b/deps/npm/man/man1/npm-sbom.1 index f784a53edd4c08..9e33d7f257d413 100644 --- a/deps/npm/man/man1/npm-sbom.1 +++ b/deps/npm/man/man1/npm-sbom.1 @@ -1,4 +1,4 @@ -.TH "NPM-SBOM" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-SBOM" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-sbom\fR - Generate a Software Bill of Materials (SBOM) .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-search.1 b/deps/npm/man/man1/npm-search.1 index 3ee7bcb7cb4e92..b4bd3edf353468 100644 --- a/deps/npm/man/man1/npm-search.1 +++ b/deps/npm/man/man1/npm-search.1 @@ -1,4 +1,4 @@ -.TH "NPM-SEARCH" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-SEARCH" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-search\fR - Search for packages .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-shrinkwrap.1 b/deps/npm/man/man1/npm-shrinkwrap.1 index d7e64493a90754..f4003d1eb25870 100644 --- a/deps/npm/man/man1/npm-shrinkwrap.1 +++ b/deps/npm/man/man1/npm-shrinkwrap.1 @@ -1,4 +1,4 @@ -.TH "NPM-SHRINKWRAP" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-SHRINKWRAP" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-shrinkwrap\fR - Lock down dependency versions for publication .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-star.1 b/deps/npm/man/man1/npm-star.1 index 7c3a528ec3a04f..9965ec8c52112f 100644 --- a/deps/npm/man/man1/npm-star.1 +++ b/deps/npm/man/man1/npm-star.1 @@ -1,4 +1,4 @@ -.TH "NPM-STAR" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-STAR" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-star\fR - Mark your favorite packages .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-stars.1 b/deps/npm/man/man1/npm-stars.1 index 61d60d9534da40..a45038b761b465 100644 --- a/deps/npm/man/man1/npm-stars.1 +++ b/deps/npm/man/man1/npm-stars.1 @@ -1,4 +1,4 @@ -.TH "NPM-STARS" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-STARS" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-stars\fR - View packages marked as favorites .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-start.1 b/deps/npm/man/man1/npm-start.1 index 4c687a1ecba49e..89ee167e22fc13 100644 --- a/deps/npm/man/man1/npm-start.1 +++ b/deps/npm/man/man1/npm-start.1 @@ -1,4 +1,4 @@ -.TH "NPM-START" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-START" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-start\fR - Start a package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-stop.1 b/deps/npm/man/man1/npm-stop.1 index dadebe98c52834..05cc8f8abcaf83 100644 --- a/deps/npm/man/man1/npm-stop.1 +++ b/deps/npm/man/man1/npm-stop.1 @@ -1,4 +1,4 @@ -.TH "NPM-STOP" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-STOP" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-stop\fR - Stop a package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-team.1 b/deps/npm/man/man1/npm-team.1 index 42ee63f4743b6e..96351687e77b06 100644 --- a/deps/npm/man/man1/npm-team.1 +++ b/deps/npm/man/man1/npm-team.1 @@ -1,4 +1,4 @@ -.TH "NPM-TEAM" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-TEAM" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-team\fR - Manage organization teams and team memberships .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-test.1 b/deps/npm/man/man1/npm-test.1 index 80eb6a1d7bb9e7..7aff8375de68b4 100644 --- a/deps/npm/man/man1/npm-test.1 +++ b/deps/npm/man/man1/npm-test.1 @@ -1,4 +1,4 @@ -.TH "NPM-TEST" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-TEST" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-test\fR - Test a package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-token.1 b/deps/npm/man/man1/npm-token.1 index 1d14f81553b5c9..139a61c5666398 100644 --- a/deps/npm/man/man1/npm-token.1 +++ b/deps/npm/man/man1/npm-token.1 @@ -1,4 +1,4 @@ -.TH "NPM-TOKEN" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-TOKEN" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-token\fR - Manage your authentication tokens .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-uninstall.1 b/deps/npm/man/man1/npm-uninstall.1 index f5e25641fc57c5..5c1ec68079d40e 100644 --- a/deps/npm/man/man1/npm-uninstall.1 +++ b/deps/npm/man/man1/npm-uninstall.1 @@ -1,4 +1,4 @@ -.TH "NPM-UNINSTALL" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-UNINSTALL" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-uninstall\fR - Remove a package .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-unpublish.1 b/deps/npm/man/man1/npm-unpublish.1 index fcf62bbd7da263..b7d070c4b765a4 100644 --- a/deps/npm/man/man1/npm-unpublish.1 +++ b/deps/npm/man/man1/npm-unpublish.1 @@ -1,4 +1,4 @@ -.TH "NPM-UNPUBLISH" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-UNPUBLISH" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-unpublish\fR - Remove a package from the registry .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-unstar.1 b/deps/npm/man/man1/npm-unstar.1 index e9edf3ab6634dd..8c91421ba21143 100644 --- a/deps/npm/man/man1/npm-unstar.1 +++ b/deps/npm/man/man1/npm-unstar.1 @@ -1,4 +1,4 @@ -.TH "NPM-UNSTAR" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-UNSTAR" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-unstar\fR - Remove an item from your favorite packages .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-update.1 b/deps/npm/man/man1/npm-update.1 index 052008d73485c9..cfac4fa4399024 100644 --- a/deps/npm/man/man1/npm-update.1 +++ b/deps/npm/man/man1/npm-update.1 @@ -1,4 +1,4 @@ -.TH "NPM-UPDATE" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-UPDATE" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-update\fR - Update packages .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-version.1 b/deps/npm/man/man1/npm-version.1 index f79a0c84c3f8fd..fef0dc3ceb3720 100644 --- a/deps/npm/man/man1/npm-version.1 +++ b/deps/npm/man/man1/npm-version.1 @@ -1,4 +1,4 @@ -.TH "NPM-VERSION" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-VERSION" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-version\fR - Bump a package version .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-view.1 b/deps/npm/man/man1/npm-view.1 index e862ddc4b8780d..db2487155a2d98 100644 --- a/deps/npm/man/man1/npm-view.1 +++ b/deps/npm/man/man1/npm-view.1 @@ -1,4 +1,4 @@ -.TH "NPM-VIEW" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-VIEW" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-view\fR - View registry info .SS "Synopsis" diff --git a/deps/npm/man/man1/npm-whoami.1 b/deps/npm/man/man1/npm-whoami.1 index a003f634a2b293..4202647f6c69f5 100644 --- a/deps/npm/man/man1/npm-whoami.1 +++ b/deps/npm/man/man1/npm-whoami.1 @@ -1,4 +1,4 @@ -.TH "NPM-WHOAMI" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM-WHOAMI" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-whoami\fR - Display npm username .SS "Synopsis" diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1 index 28e8774ad1b4d0..ffcdbf0f7bd45e 100644 --- a/deps/npm/man/man1/npm.1 +++ b/deps/npm/man/man1/npm.1 @@ -1,4 +1,4 @@ -.TH "NPM" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPM" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm\fR - javascript package manager .SS "Synopsis" @@ -12,7 +12,7 @@ npm Note: This command is unaware of workspaces. .SS "Version" .P -10.9.2 +11.0.0 .SS "Description" .P npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency conflicts intelligently. diff --git a/deps/npm/man/man1/npx.1 b/deps/npm/man/man1/npx.1 index 9b4ba1af1dc365..3e25d37e42fd9a 100644 --- a/deps/npm/man/man1/npx.1 +++ b/deps/npm/man/man1/npx.1 @@ -1,4 +1,4 @@ -.TH "NPX" "1" "December 2024" "NPM@10.9.2" "" +.TH "NPX" "1" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpx\fR - Run a command from a local or remote npm package .SS "Synopsis" diff --git a/deps/npm/man/man5/folders.5 b/deps/npm/man/man5/folders.5 index 8eb8a8230333f9..8348e518a170df 100644 --- a/deps/npm/man/man5/folders.5 +++ b/deps/npm/man/man5/folders.5 @@ -1,4 +1,4 @@ -.TH "FOLDERS" "5" "December 2024" "NPM@10.9.2" "" +.TH "FOLDERS" "5" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBfolders\fR - Folder Structures Used by npm .SS "Description" diff --git a/deps/npm/man/man5/install.5 b/deps/npm/man/man5/install.5 index 0b75adf308685a..b49be9e98c7c32 100644 --- a/deps/npm/man/man5/install.5 +++ b/deps/npm/man/man5/install.5 @@ -1,4 +1,4 @@ -.TH "INSTALL" "5" "December 2024" "NPM@10.9.2" "" +.TH "INSTALL" "5" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBinstall\fR - Download and install node and npm .SS "Description" diff --git a/deps/npm/man/man5/npm-global.5 b/deps/npm/man/man5/npm-global.5 index 8eb8a8230333f9..8348e518a170df 100644 --- a/deps/npm/man/man5/npm-global.5 +++ b/deps/npm/man/man5/npm-global.5 @@ -1,4 +1,4 @@ -.TH "FOLDERS" "5" "December 2024" "NPM@10.9.2" "" +.TH "FOLDERS" "5" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBfolders\fR - Folder Structures Used by npm .SS "Description" diff --git a/deps/npm/man/man5/npm-json.5 b/deps/npm/man/man5/npm-json.5 index 81fa9a8c95b8dc..4b807fbf00e9a4 100644 --- a/deps/npm/man/man5/npm-json.5 +++ b/deps/npm/man/man5/npm-json.5 @@ -1,4 +1,4 @@ -.TH "PACKAGE.JSON" "5" "December 2024" "NPM@10.9.2" "" +.TH "PACKAGE.JSON" "5" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBpackage.json\fR - Specifics of npm's package.json handling .SS "Description" @@ -317,6 +317,8 @@ Some files are always ignored by default: \fBpnpm-lock.yaml\fR .IP \(bu 4 \fByarn.lock\fR +.IP \(bu 4 +\fBbun.lockb\fR .RE 0 .P @@ -334,6 +336,8 @@ Most of these ignored files can be included specifically if included in the \fBf \fBpnpm-lock.yaml\fR .IP \(bu 4 \fByarn.lock\fR +.IP \(bu 4 +\fBbun.lockb\fR .RE 0 .P @@ -1044,6 +1048,18 @@ Like the \fBos\fR option, you can also block architectures: .RE .P The host architecture is determined by \fBprocess.arch\fR +.SS "libc" +.P +If your code only runs or builds in certain versions of libc, you can specify which ones. This field only applies if \fBos\fR is \fBlinux\fR. +.P +.RS 2 +.nf +{ + "os": "linux", + "libc": "glibc" +} +.fi +.RE .SS "devEngines" .P The \fBdevEngines\fR field aids engineers working on a codebase to all be using the same tooling. diff --git a/deps/npm/man/man5/npm-shrinkwrap-json.5 b/deps/npm/man/man5/npm-shrinkwrap-json.5 index 7e8efb285e959e..0600f5d61aa99d 100644 --- a/deps/npm/man/man5/npm-shrinkwrap-json.5 +++ b/deps/npm/man/man5/npm-shrinkwrap-json.5 @@ -1,4 +1,4 @@ -.TH "NPM-SHRINKWRAP.JSON" "5" "December 2024" "NPM@10.9.2" "" +.TH "NPM-SHRINKWRAP.JSON" "5" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpm-shrinkwrap.json\fR - A publishable lockfile .SS "Description" diff --git a/deps/npm/man/man5/npmrc.5 b/deps/npm/man/man5/npmrc.5 index 947a0fe433faa3..098793f1a970ad 100644 --- a/deps/npm/man/man5/npmrc.5 +++ b/deps/npm/man/man5/npmrc.5 @@ -1,4 +1,4 @@ -.TH "NPMRC" "5" "December 2024" "NPM@10.9.2" "" +.TH "NPMRC" "5" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBnpmrc\fR - The npm config files .SS "Description" @@ -89,7 +89,7 @@ The full list is: .IP \(bu 4 \fBemail\fR .IP \(bu 4 -\fBcertfile\fR (path to certificate file) +\fBcafile\fR (path to certificate authority file) .IP \(bu 4 \fBkeyfile\fR (path to key file) .RE 0 diff --git a/deps/npm/man/man5/package-json.5 b/deps/npm/man/man5/package-json.5 index 81fa9a8c95b8dc..4b807fbf00e9a4 100644 --- a/deps/npm/man/man5/package-json.5 +++ b/deps/npm/man/man5/package-json.5 @@ -1,4 +1,4 @@ -.TH "PACKAGE.JSON" "5" "December 2024" "NPM@10.9.2" "" +.TH "PACKAGE.JSON" "5" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBpackage.json\fR - Specifics of npm's package.json handling .SS "Description" @@ -317,6 +317,8 @@ Some files are always ignored by default: \fBpnpm-lock.yaml\fR .IP \(bu 4 \fByarn.lock\fR +.IP \(bu 4 +\fBbun.lockb\fR .RE 0 .P @@ -334,6 +336,8 @@ Most of these ignored files can be included specifically if included in the \fBf \fBpnpm-lock.yaml\fR .IP \(bu 4 \fByarn.lock\fR +.IP \(bu 4 +\fBbun.lockb\fR .RE 0 .P @@ -1044,6 +1048,18 @@ Like the \fBos\fR option, you can also block architectures: .RE .P The host architecture is determined by \fBprocess.arch\fR +.SS "libc" +.P +If your code only runs or builds in certain versions of libc, you can specify which ones. This field only applies if \fBos\fR is \fBlinux\fR. +.P +.RS 2 +.nf +{ + "os": "linux", + "libc": "glibc" +} +.fi +.RE .SS "devEngines" .P The \fBdevEngines\fR field aids engineers working on a codebase to all be using the same tooling. diff --git a/deps/npm/man/man5/package-lock-json.5 b/deps/npm/man/man5/package-lock-json.5 index 78deecab3757c1..0fe3cf8920411d 100644 --- a/deps/npm/man/man5/package-lock-json.5 +++ b/deps/npm/man/man5/package-lock-json.5 @@ -1,4 +1,4 @@ -.TH "PACKAGE-LOCK.JSON" "5" "December 2024" "NPM@10.9.2" "" +.TH "PACKAGE-LOCK.JSON" "5" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBpackage-lock.json\fR - A manifestation of the manifest .SS "Description" diff --git a/deps/npm/man/man7/config.7 b/deps/npm/man/man7/config.7 index 0ce1f25f38d5d3..c9bfc8f9cf23c5 100644 --- a/deps/npm/man/man7/config.7 +++ b/deps/npm/man/man7/config.7 @@ -1,4 +1,4 @@ -.TH "CONFIG" "7" "December 2024" "NPM@10.9.2" "" +.TH "CONFIG" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBconfig\fR - More than you probably want to know about npm configuration .SS "Description" @@ -1835,7 +1835,7 @@ Default: null .IP \(bu 4 Type: null or String .IP \(bu 4 -DEPRECATED: \fBkey\fR and \fBcert\fR are no longer used for most registry operations. Use registry scoped \fBkeyfile\fR and \fBcertfile\fR instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem //other-registry.tld/:certfile=/path/to/cert.crt +DEPRECATED: \fBkey\fR and \fBcert\fR are no longer used for most registry operations. Use registry scoped \fBkeyfile\fR and \fBcafile\fR instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem //other-registry.tld/:cafile=/path/to/cert.crt .RE 0 .P @@ -1847,7 +1847,7 @@ cert="-----BEGIN CERTIFICATE-----\[rs]nXXXX\[rs]nXXXX\[rs]n-----END CERTIFICATE- .fi .RE .P -It is \fInot\fR the path to a certificate file, though you can set a registry-scoped "certfile" path like "//other-registry.tld/:certfile=/path/to/cert.pem". +It is \fInot\fR the path to a certificate file, though you can set a registry-scoped "cafile" path like "//other-registry.tld/:cafile=/path/to/cert.pem". .SS "\fBdev\fR" .RS 0 .IP \(bu 4 @@ -1951,7 +1951,7 @@ Default: null .IP \(bu 4 Type: null or String .IP \(bu 4 -DEPRECATED: \fBkey\fR and \fBcert\fR are no longer used for most registry operations. Use registry scoped \fBkeyfile\fR and \fBcertfile\fR instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem //other-registry.tld/:certfile=/path/to/cert.crt +DEPRECATED: \fBkey\fR and \fBcert\fR are no longer used for most registry operations. Use registry scoped \fBkeyfile\fR and \fBcafile\fR instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem //other-registry.tld/:cafile=/path/to/cert.crt .RE 0 .P diff --git a/deps/npm/man/man7/dependency-selectors.7 b/deps/npm/man/man7/dependency-selectors.7 index e15648cb400af4..7331ef393a6a46 100644 --- a/deps/npm/man/man7/dependency-selectors.7 +++ b/deps/npm/man/man7/dependency-selectors.7 @@ -1,4 +1,4 @@ -.TH "QUERYING" "7" "December 2024" "NPM@10.9.2" "" +.TH "QUERYING" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBQuerying\fR - Dependency Selector Syntax & Querying .SS "Description" diff --git a/deps/npm/man/man7/developers.7 b/deps/npm/man/man7/developers.7 index a5d429c60a5cef..b6fc11026bff41 100644 --- a/deps/npm/man/man7/developers.7 +++ b/deps/npm/man/man7/developers.7 @@ -1,4 +1,4 @@ -.TH "DEVELOPERS" "7" "December 2024" "NPM@10.9.2" "" +.TH "DEVELOPERS" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBdevelopers\fR - Developer Guide .SS "Description" @@ -91,7 +91,7 @@ You can negate a pattern by starting it with an exclamation point \fB!\fR. .RE 0 .P -By default, the following paths and files are ignored, so there's no need to add them to \fB.npmignore\fR explicitly: +By default, some paths and files are ignored, so there's no need to add them to \fB.npmignore\fR explicitly. Some examples are: .RS 0 .IP \(bu 4 \fB.*.swp\fR @@ -140,6 +140,8 @@ The following paths and files are never ignored, so adding them to \fB.npmignore .P If, given the structure of your project, you find \fB.npmignore\fR to be a maintenance headache, you might instead try populating the \fBfiles\fR property of \fBpackage.json\fR, which is an array of file or directory names that should be included in your package. Sometimes manually picking which items to allow is easier to manage than building a block list. +.P +See \fB\fBpackage.json\fR\fR \fI\(la/configuring-npm/package-json\(ra\fR for more info on what can and can't be ignored. .SS "Testing whether your \fB.npmignore\fR or \fBfiles\fR config works" .P If you want to double check that your package will include only the files you intend it to when published, you can run the \fBnpm pack\fR command locally which will generate a tarball in the working directory, the same way it does for publishing. diff --git a/deps/npm/man/man7/logging.7 b/deps/npm/man/man7/logging.7 index 200ad578584521..4b1bd5a17fd290 100644 --- a/deps/npm/man/man7/logging.7 +++ b/deps/npm/man/man7/logging.7 @@ -1,4 +1,4 @@ -.TH "LOGGING" "7" "December 2024" "NPM@10.9.2" "" +.TH "LOGGING" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBLogging\fR - Why, What & How We Log .SS "Description" diff --git a/deps/npm/man/man7/orgs.7 b/deps/npm/man/man7/orgs.7 index 8f12f6e0434a19..4f61c68e1a7090 100644 --- a/deps/npm/man/man7/orgs.7 +++ b/deps/npm/man/man7/orgs.7 @@ -1,4 +1,4 @@ -.TH "ORGS" "7" "December 2024" "NPM@10.9.2" "" +.TH "ORGS" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBorgs\fR - Working with Teams & Orgs .SS "Description" diff --git a/deps/npm/man/man7/package-spec.7 b/deps/npm/man/man7/package-spec.7 index f8a0ac73903272..0d68ede455119b 100644 --- a/deps/npm/man/man7/package-spec.7 +++ b/deps/npm/man/man7/package-spec.7 @@ -1,4 +1,4 @@ -.TH "PACKAGE-SPEC" "7" "December 2024" "NPM@10.9.2" "" +.TH "PACKAGE-SPEC" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBpackage-spec\fR - Package name specifier .SS "Description" diff --git a/deps/npm/man/man7/registry.7 b/deps/npm/man/man7/registry.7 index 1e8134daaf751d..82e7b3c722ee50 100644 --- a/deps/npm/man/man7/registry.7 +++ b/deps/npm/man/man7/registry.7 @@ -1,4 +1,4 @@ -.TH "REGISTRY" "7" "December 2024" "NPM@10.9.2" "" +.TH "REGISTRY" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBregistry\fR - The JavaScript Package Registry .SS "Description" diff --git a/deps/npm/man/man7/removal.7 b/deps/npm/man/man7/removal.7 index 74a7703f177e47..f571d2a6f3be38 100644 --- a/deps/npm/man/man7/removal.7 +++ b/deps/npm/man/man7/removal.7 @@ -1,4 +1,4 @@ -.TH "REMOVAL" "7" "December 2024" "NPM@10.9.2" "" +.TH "REMOVAL" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBremoval\fR - Cleaning the Slate .SS "Synopsis" diff --git a/deps/npm/man/man7/scope.7 b/deps/npm/man/man7/scope.7 index ccf0d9e7ee99c4..f665aafe4ab591 100644 --- a/deps/npm/man/man7/scope.7 +++ b/deps/npm/man/man7/scope.7 @@ -1,4 +1,4 @@ -.TH "SCOPE" "7" "December 2024" "NPM@10.9.2" "" +.TH "SCOPE" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBscope\fR - Scoped packages .SS "Description" diff --git a/deps/npm/man/man7/scripts.7 b/deps/npm/man/man7/scripts.7 index 1758473fc89dbb..e28461883c1fab 100644 --- a/deps/npm/man/man7/scripts.7 +++ b/deps/npm/man/man7/scripts.7 @@ -1,4 +1,4 @@ -.TH "SCRIPTS" "7" "December 2024" "NPM@10.9.2" "" +.TH "SCRIPTS" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBscripts\fR - How npm handles the "scripts" field .SS "Description" diff --git a/deps/npm/man/man7/workspaces.7 b/deps/npm/man/man7/workspaces.7 index cd7df95b81cf3e..c3cdec35116936 100644 --- a/deps/npm/man/man7/workspaces.7 +++ b/deps/npm/man/man7/workspaces.7 @@ -1,4 +1,4 @@ -.TH "WORKSPACES" "7" "December 2024" "NPM@10.9.2" "" +.TH "WORKSPACES" "7" "December 2024" "NPM@11.0.0" "" .SH "NAME" \fBworkspaces\fR - Working with workspaces .SS "Description" diff --git a/deps/npm/node_modules/@npmcli/arborist/lib/arborist/rebuild.js b/deps/npm/node_modules/@npmcli/arborist/lib/arborist/rebuild.js index 82f84772f9a856..3340ddaa67067a 100644 --- a/deps/npm/node_modules/@npmcli/arborist/lib/arborist/rebuild.js +++ b/deps/npm/node_modules/@npmcli/arborist/lib/arborist/rebuild.js @@ -154,7 +154,9 @@ module.exports = cls => class Builder extends cls { // links should run prepare scripts and only link bins after that if (type === 'links') { - await this.#runScripts('prepare') + if (!this.options.ignoreScripts) { + await this.#runScripts('prepare') + } } if (this.options.binLinks) { await this.#linkAllBins() diff --git a/deps/npm/node_modules/@npmcli/arborist/lib/audit-report.js b/deps/npm/node_modules/@npmcli/arborist/lib/audit-report.js index f7700ce9119de3..dbd9be8bd3865a 100644 --- a/deps/npm/node_modules/@npmcli/arborist/lib/audit-report.js +++ b/deps/npm/node_modules/@npmcli/arborist/lib/audit-report.js @@ -15,7 +15,7 @@ const _init = Symbol('init') const _omit = Symbol('omit') const { log, time } = require('proc-log') -const fetch = require('npm-registry-fetch') +const npmFetch = require('npm-registry-fetch') class AuditReport extends Map { static load (tree, opts) { @@ -274,33 +274,6 @@ class AuditReport extends Map { throw new Error('do not call AuditReport.set() directly') } - // convert a quick-audit into a bulk advisory listing - static auditToBulk (report) { - if (!report.advisories) { - // tack on the report json where the response body would go - throw Object.assign(new Error('Invalid advisory report'), { - body: JSON.stringify(report), - }) - } - - const bulk = {} - const { advisories } = report - for (const advisory of Object.values(advisories)) { - const { - id, - url, - title, - severity = 'high', - vulnerable_versions = '*', - module_name: name, - } = advisory - bulk[name] = bulk[name] || [] - bulk[name].push({ id, url, title, severity, vulnerable_versions }) - } - - return bulk - } - async [_getReport] () { // if we're not auditing, just return false if (this.options.audit === false || this.options.offline === true || this.tree.inventory.size === 1) { @@ -309,39 +282,24 @@ class AuditReport extends Map { const timeEnd = time.start('auditReport:getReport') try { - try { - // first try the super fast bulk advisory listing - const body = prepareBulkData(this.tree, this[_omit], this.filterSet) - log.silly('audit', 'bulk request', body) - - // no sense asking if we don't have anything to audit, - // we know it'll be empty - if (!Object.keys(body).length) { - return null - } + const body = prepareBulkData(this.tree, this[_omit], this.filterSet) + log.silly('audit', 'bulk request', body) - const res = await fetch('/-/npm/v1/security/advisories/bulk', { - ...this.options, - registry: this.options.auditRegistry || this.options.registry, - method: 'POST', - gzip: true, - body, - }) - - return await res.json() - } catch (er) { - log.silly('audit', 'bulk request failed', String(er.body)) - // that failed, try the quick audit endpoint - const body = prepareData(this.tree, this.options) - const res = await fetch('/-/npm/v1/security/audits/quick', { - ...this.options, - registry: this.options.auditRegistry || this.options.registry, - method: 'POST', - gzip: true, - body, - }) - return AuditReport.auditToBulk(await res.json()) + // no sense asking if we don't have anything to audit, + // we know it'll be empty + if (!Object.keys(body).length) { + return null } + + const res = await npmFetch('/-/npm/v1/security/advisories/bulk', { + ...this.options, + registry: this.options.auditRegistry || this.options.registry, + method: 'POST', + gzip: true, + body, + }) + + return await res.json() } catch (er) { log.verbose('audit error', er) log.silly('audit error', String(er.body)) @@ -384,32 +342,4 @@ const prepareBulkData = (tree, omit, filterSet) => { return payload } -const prepareData = (tree, opts) => { - const { npmVersion: npm_version } = opts - const node_version = process.version - const { platform, arch } = process - const { NODE_ENV: node_env } = process.env - const data = tree.meta.commit() - // the legacy audit endpoint doesn't support any kind of pre-filtering - // we just have to get the advisories and skip over them in the report - return { - name: data.name, - version: data.version, - requires: { - ...(tree.package.devDependencies || {}), - ...(tree.package.peerDependencies || {}), - ...(tree.package.optionalDependencies || {}), - ...(tree.package.dependencies || {}), - }, - dependencies: data.dependencies, - metadata: { - node_version, - npm_version, - platform, - arch, - node_env, - }, - } -} - module.exports = AuditReport diff --git a/deps/npm/node_modules/@npmcli/arborist/lib/query-selector-all.js b/deps/npm/node_modules/@npmcli/arborist/lib/query-selector-all.js index fa48d5f84b5562..c2cd00d0a2e2ee 100644 --- a/deps/npm/node_modules/@npmcli/arborist/lib/query-selector-all.js +++ b/deps/npm/node_modules/@npmcli/arborist/lib/query-selector-all.js @@ -8,7 +8,7 @@ const { minimatch } = require('minimatch') const npa = require('npm-package-arg') const pacote = require('pacote') const semver = require('semver') -const fetch = require('npm-registry-fetch') +const npmFetch = require('npm-registry-fetch') // handle results for parsed query asts, results are stored in a map that has a // key that points to each ast selector node and stores the resulting array of @@ -461,7 +461,7 @@ class Results { packages[node.name].push(node.version) } }) - const res = await fetch('/-/npm/v1/security/advisories/bulk', { + const res = await npmFetch('/-/npm/v1/security/advisories/bulk', { ...this.flatOptions, registry: this.flatOptions.auditRegistry || this.flatOptions.registry, method: 'POST', diff --git a/deps/npm/node_modules/@npmcli/arborist/package.json b/deps/npm/node_modules/@npmcli/arborist/package.json index b1e2b21a254635..a8c9ae04152440 100644 --- a/deps/npm/node_modules/@npmcli/arborist/package.json +++ b/deps/npm/node_modules/@npmcli/arborist/package.json @@ -1,13 +1,13 @@ { "name": "@npmcli/arborist", - "version": "8.0.0", + "version": "9.0.0", "description": "Manage node_modules trees", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^4.0.0", "@npmcli/installed-package-contents": "^3.0.0", "@npmcli/map-workspaces": "^4.0.1", - "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/metavuln-calculator": "^9.0.0", "@npmcli/name-from-folder": "^3.0.0", "@npmcli/node-gyp": "^4.0.0", "@npmcli/package-json": "^6.0.1", @@ -18,7 +18,6 @@ "cacache": "^19.0.1", "common-ancestor-path": "^1.0.1", "hosted-git-info": "^8.0.0", - "json-parse-even-better-errors": "^4.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^10.2.2", "minimatch": "^9.0.4", @@ -27,7 +26,7 @@ "npm-package-arg": "^12.0.0", "npm-pick-manifest": "^10.0.0", "npm-registry-fetch": "^18.0.1", - "pacote": "^19.0.0", + "pacote": "^21.0.0", "parse-conflict-json": "^4.0.0", "proc-log": "^5.0.0", "proggy": "^3.0.0", @@ -37,11 +36,12 @@ "semver": "^7.3.7", "ssri": "^12.0.0", "treeverse": "^3.0.0", - "walk-up-path": "^3.0.1" + "walk-up-path": "^4.0.0" }, "devDependencies": { "@npmcli/eslint-config": "^5.0.1", - "@npmcli/template-oss": "4.23.3", + "@npmcli/mock-registry": "^1.0.0", + "@npmcli/template-oss": "4.23.6", "benchmark": "^2.1.4", "minify-registry-metadata": "^4.0.0", "nock": "^13.3.3", @@ -82,18 +82,18 @@ "test-env": [ "LC_ALL=sk" ], - "timeout": "360", + "timeout": "720", "nyc-arg": [ "--exclude", "tap-snapshots/**" ] }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" } } diff --git a/deps/npm/node_modules/@npmcli/config/lib/definitions/definitions.js b/deps/npm/node_modules/@npmcli/config/lib/definitions/definitions.js index 627d624be4a024..8a84abd50ff699 100644 --- a/deps/npm/node_modules/@npmcli/config/lib/definitions/definitions.js +++ b/deps/npm/node_modules/@npmcli/config/lib/definitions/definitions.js @@ -397,14 +397,14 @@ const definitions = { \`\`\` It is _not_ the path to a certificate file, though you can set a registry-scoped - "certfile" path like "//other-registry.tld/:certfile=/path/to/cert.pem". + "cafile" path like "//other-registry.tld/:cafile=/path/to/cert.pem". `, deprecated: ` \`key\` and \`cert\` are no longer used for most registry operations. - Use registry scoped \`keyfile\` and \`certfile\` instead. + Use registry scoped \`keyfile\` and \`cafile\` instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem - //other-registry.tld/:certfile=/path/to/cert.crt + //other-registry.tld/:cafile=/path/to/cert.crt `, flatten, }), @@ -1077,10 +1077,10 @@ const definitions = { `, deprecated: ` \`key\` and \`cert\` are no longer used for most registry operations. - Use registry scoped \`keyfile\` and \`certfile\` instead. + Use registry scoped \`keyfile\` and \`cafile\` instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem - //other-registry.tld/:certfile=/path/to/cert.crt + //other-registry.tld/:cafile=/path/to/cert.crt `, flatten, }), diff --git a/deps/npm/node_modules/@npmcli/config/package.json b/deps/npm/node_modules/@npmcli/config/package.json index 18c677393b5ff3..eb89879ffe52fa 100644 --- a/deps/npm/node_modules/@npmcli/config/package.json +++ b/deps/npm/node_modules/@npmcli/config/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/config", - "version": "9.0.0", + "version": "10.0.0", "files": [ "bin/", "lib/" @@ -33,7 +33,7 @@ "devDependencies": { "@npmcli/eslint-config": "^5.0.1", "@npmcli/mock-globals": "^1.0.0", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.6", "tap": "^16.3.8" }, "dependencies": { @@ -44,14 +44,14 @@ "nopt": "^8.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", - "walk-up-path": "^3.0.1" + "walk-up-path": "^4.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" } } diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/LICENSE b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/LICENSE deleted file mode 100644 index a03cd0ed0b338b..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -The ISC License - -Copyright (c) Isaac Z. Schlueter, Kat Marchán, npm, Inc., and Contributors - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/README.md b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/README.md deleted file mode 100644 index dbb0051de23a4d..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/README.md +++ /dev/null @@ -1,283 +0,0 @@ -# pacote - -Fetches package manifests and tarballs from the npm registry. - -## USAGE - -```js -const pacote = require('pacote') - -// get a package manifest -pacote.manifest('foo@1.x').then(manifest => console.log('got it', manifest)) - -// extract a package into a folder -pacote.extract('github:npm/cli', 'some/path', options) - .then(({from, resolved, integrity}) => { - console.log('extracted!', from, resolved, integrity) - }) - -pacote.tarball('https://server.com/package.tgz').then(data => { - console.log('got ' + data.length + ' bytes of tarball data') -}) -``` - -`pacote` works with any kind of package specifier that npm can install. If -you can pass it to the npm CLI, you can pass it to pacote. (In fact, that's -exactly what the npm CLI does.) - -Anything that you can do with one kind of package, you can do with another. - -Data that isn't relevant (like a packument for a tarball) will be -simulated. - -`prepare` scripts will be run when generating tarballs from `git` and -`directory` locations, to simulate what _would_ be published to the -registry, so that you get a working package instead of just raw source -code that might need to be transpiled. - -## CLI - -This module exports a command line interface that can do most of what is -described below. Run `pacote -h` to learn more. - -``` -Pacote - The JavaScript Package Handler, v10.1.1 - -Usage: - - pacote resolve - Resolve a specifier and output the fully resolved target - Returns integrity and from if '--long' flag is set. - - pacote manifest - Fetch a manifest and print to stdout - - pacote packument - Fetch a full packument and print to stdout - - pacote tarball [] - Fetch a package tarball and save to - If is missing or '-', the tarball will be streamed to stdout. - - pacote extract - Extract a package to the destination folder. - -Configuration values all match the names of configs passed to npm, or -options passed to Pacote. Additional flags for this executable: - - --long Print an object from 'resolve', including integrity and spec. - --json Print result objects as JSON rather than node's default. - (This is the default if stdout is not a TTY.) - --help -h Print this helpful text. - -For example '--cache=/path/to/folder' will use that folder as the cache. -``` - -## API - -The `spec` refers to any kind of package specifier that npm can install. -If you can pass it to the npm CLI, you can pass it to pacote. (In fact, -that's exactly what the npm CLI does.) - -See below for valid `opts` values. - -* `pacote.resolve(spec, opts)` Resolve a specifier like `foo@latest` or - `github:user/project` all the way to a tarball url, tarball file, or git - repo with commit hash. - -* `pacote.extract(spec, dest, opts)` Extract a package's tarball into a - destination folder. Returns a promise that resolves to the - `{from,resolved,integrity}` of the extracted package. - -* `pacote.manifest(spec, opts)` Fetch (or simulate) a package's manifest - (basically, the `package.json` file, plus a bit of metadata). - See below for more on manifests and packuments. Returns a Promise that - resolves to the manifest object. - -* `pacote.packument(spec, opts)` Fetch (or simulate) a package's packument - (basically, the top-level package document listing all the manifests that - the registry returns). See below for more on manifests and packuments. - Returns a Promise that resolves to the packument object. - -* `pacote.tarball(spec, opts)` Get a package tarball data as a buffer in - memory. Returns a Promise that resolves to the tarball data Buffer, with - `from`, `resolved`, and `integrity` fields attached. - -* `pacote.tarball.file(spec, dest, opts)` Save a package tarball data to - a file on disk. Returns a Promise that resolves to - `{from,integrity,resolved}` of the fetched tarball. - -* `pacote.tarball.stream(spec, streamHandler, opts)` Fetch a tarball and - make the stream available to the `streamHandler` function. - - This is mostly an internal function, but it is exposed because it does - provide some functionality that may be difficult to achieve otherwise. - - The `streamHandler` function MUST return a Promise that resolves when - the stream (and all associated work) is ended, or rejects if the stream - has an error. - - The `streamHandler` function MAY be called multiple times, as Pacote - retries requests in some scenarios, such as cache corruption or - retriable network failures. - -### Options - -Options are passed to -[`npm-registry-fetch`](http://npm.im/npm-registry-fetch) and -[`cacache`](http://npm.im/cacache), so in addition to these, anything for -those modules can be given to pacote as well. - -Options object is cloned, and mutated along the way to add integrity, -resolved, and other properties, as they are determined. - -* `cache` Where to store cache entries and temp files. Passed to - [`cacache`](http://npm.im/cacache). Defaults to the same cache directory - that npm will use by default, based on platform and environment. -* `where` Base folder for resolving relative `file:` dependencies. -* `resolved` Shortcut for looking up resolved values. Should be specified - if known. -* `integrity` Expected integrity of fetched package tarball. If specified, - tarballs with mismatched integrity values will raise an `EINTEGRITY` - error. -* `umask` Permission mode mask for extracted files and directories. - Defaults to `0o22`. See "Extracted File Modes" below. -* `fmode` Minimum permission mode for extracted files. Defaults to - `0o666`. See "Extracted File Modes" below. -* `dmode` Minimum permission mode for extracted directories. Defaults to - `0o777`. See "Extracted File Modes" below. -* `preferOnline` Prefer to revalidate cache entries, even when it would not - be strictly necessary. Default `false`. -* `before` When picking a manifest from a packument, only consider - packages published before the specified date. Default `null`. -* `defaultTag` The default `dist-tag` to use when choosing a manifest from a - packument. Defaults to `latest`. -* `registry` The npm registry to use by default. Defaults to - `https://registry.npmjs.org/`. -* `fullMetadata` Fetch the full metadata from the registry for packuments, - including information not strictly required for installation (author, - description, etc.) Defaults to `true` when `before` is set, since the - version publish time is part of the extended packument metadata. -* `fullReadJson` Use the slower `read-package-json` package insted of - `read-package-json-fast` in order to include extra fields like "readme" in - the manifest. Defaults to `false`. -* `packumentCache` For registry packuments only, you may provide a `Map` - object which will be used to cache packument requests between pacote - calls. This allows you to easily avoid hitting the registry multiple - times (even just to validate the cache) for a given packument, since it - is unlikely to change in the span of a single command. -* `verifySignatures` A boolean that will make pacote verify the - integrity signature of a manifest, if present. There must be a - configured `_keys` entry in the config that is scoped to the - registry the manifest is being fetched from. -* `verifyAttestations` A boolean that will make pacote verify Sigstore - attestations, if present. There must be a configured `_keys` entry in the - config that is scoped to the registry the manifest is being fetched from. -* `tufCache` Where to store metadata/target files when retrieving the package - attestation key material via TUF. Defaults to the same cache directory that - npm will use by default, based on platform and environment. - -### Advanced API - -Each different type of fetcher is exposed for more advanced usage such as -using helper methods from this classes: - -* `DirFetcher` -* `FileFetcher` -* `GitFetcher` -* `RegistryFetcher` -* `RemoteFetcher` - -## Extracted File Modes - -Files are extracted with a mode matching the following formula: - -``` -( (tarball entry mode value) | (minimum mode option) ) ~ (umask) -``` - -This is in order to prevent unreadable files or unlistable directories from -cluttering a project's `node_modules` folder, even if the package tarball -specifies that the file should be inaccessible. - -It also prevents files from being group- or world-writable without explicit -opt-in by the user, because all file and directory modes are masked against -the `umask` value. - -So, a file which is `0o771` in the tarball, using the default `fmode` of -`0o666` and `umask` of `0o22`, will result in a file mode of `0o755`: - -``` -(0o771 | 0o666) => 0o777 -(0o777 ~ 0o22) => 0o755 -``` - -In almost every case, the defaults are appropriate. To respect exactly -what is in the package tarball (even if this makes an unusable system), set -both `dmode` and `fmode` options to `0`. Otherwise, the `umask` config -should be used in most cases where file mode modifications are required, -and this functions more or less the same as the `umask` value in most Unix -systems. - -## Extracted File Ownership - -When running as `root` on Unix systems, all extracted files and folders -will have their owning `uid` and `gid` values set to match the ownership -of the containing folder. - -This prevents `root`-owned files showing up in a project's `node_modules` -folder when a user runs `sudo npm install`. - -## Manifests - -A `manifest` is similar to a `package.json` file. However, it has a few -pieces of extra metadata, and sometimes lacks metadata that is inessential -to package installation. - -In addition to the common `package.json` fields, manifests include: - -* `manifest._resolved` The tarball url or file path where the package - artifact can be found. -* `manifest._from` A normalized form of the spec passed in as an argument. -* `manifest._integrity` The integrity value for the package artifact. -* `manifest._id` The canonical spec of this package version: name@version. -* `manifest.dist` Registry manifests (those included in a packument) have a - `dist` object. Only `tarball` is required, though at least one of - `shasum` or `integrity` is almost always present. - - * `tarball` The url to the associated package artifact. (Copied by - Pacote to `manifest._resolved`.) - * `integrity` The integrity SRI string for the artifact. This may not - be present for older packages on the npm registry. (Copied by Pacote - to `manifest._integrity`.) - * `shasum` Legacy integrity value. Hexadecimal-encoded sha1 hash. - (Converted to an SRI string and copied by Pacote to - `manifest._integrity` when `dist.integrity` is not present.) - * `fileCount` Number of files in the tarball. - * `unpackedSize` Size on disk of the package when unpacked. - * `signatures` Signatures of the shasum. Includes the keyid that - correlates to a [`key from the npm - registry`](https://registry.npmjs.org/-/npm/v1/keys) - -## Packuments - -A packument is the top-level package document that lists the set of -manifests for available versions for a package. - -When a packument is fetched with `accept: -application/vnd.npm.install-v1+json` in the HTTP headers, only the most -minimum necessary metadata is returned. Additional metadata is returned -when fetched with only `accept: application/json`. - -For Pacote's purposes, the following fields are relevant: - -* `versions` An object where each key is a version, and each value is the - manifest for that version. -* `dist-tags` An object mapping dist-tags to version numbers. This is how - `foo@latest` gets turned into `foo@1.2.3`. -* `time` In the full packument, an object mapping version numbers to - publication times, for the `opts.before` functionality. - -Pacote adds the following field, regardless of the accept header: - -* `_contentLength` The size of the packument. diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/bin/index.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/bin/index.js deleted file mode 100755 index f35b62ca71a537..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/bin/index.js +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env node - -const run = conf => { - const pacote = require('../') - switch (conf._[0]) { - case 'resolve': - case 'manifest': - case 'packument': - if (conf._[0] === 'resolve' && conf.long) { - return pacote.manifest(conf._[1], conf).then(mani => ({ - resolved: mani._resolved, - integrity: mani._integrity, - from: mani._from, - })) - } - return pacote[conf._[0]](conf._[1], conf) - - case 'tarball': - if (!conf._[2] || conf._[2] === '-') { - return pacote.tarball.stream(conf._[1], stream => { - stream.pipe( - conf.testStdout || - /* istanbul ignore next */ - process.stdout - ) - // make sure it resolves something falsey - return stream.promise().then(() => { - return false - }) - }, conf) - } else { - return pacote.tarball.file(conf._[1], conf._[2], conf) - } - - case 'extract': - return pacote.extract(conf._[1], conf._[2], conf) - - default: /* istanbul ignore next */ { - throw new Error(`bad command: ${conf._[0]}`) - } - } -} - -const version = require('../package.json').version -const usage = () => -`Pacote - The JavaScript Package Handler, v${version} - -Usage: - - pacote resolve - Resolve a specifier and output the fully resolved target - Returns integrity and from if '--long' flag is set. - - pacote manifest - Fetch a manifest and print to stdout - - pacote packument - Fetch a full packument and print to stdout - - pacote tarball [] - Fetch a package tarball and save to - If is missing or '-', the tarball will be streamed to stdout. - - pacote extract - Extract a package to the destination folder. - -Configuration values all match the names of configs passed to npm, or -options passed to Pacote. Additional flags for this executable: - - --long Print an object from 'resolve', including integrity and spec. - --json Print result objects as JSON rather than node's default. - (This is the default if stdout is not a TTY.) - --help -h Print this helpful text. - -For example '--cache=/path/to/folder' will use that folder as the cache. -` - -const shouldJSON = (conf, result) => - conf.json || - !process.stdout.isTTY && - conf.json === undefined && - result && - typeof result === 'object' - -const pretty = (conf, result) => - shouldJSON(conf, result) ? JSON.stringify(result, 0, 2) : result - -let addedLogListener = false -const main = args => { - const conf = parse(args) - if (conf.help || conf.h) { - return console.log(usage()) - } - - if (!addedLogListener) { - process.on('log', console.error) - addedLogListener = true - } - - try { - return run(conf) - .then(result => result && console.log(pretty(conf, result))) - .catch(er => { - console.error(er) - process.exit(1) - }) - } catch (er) { - console.error(er.message) - console.error(usage()) - } -} - -const parseArg = arg => { - const split = arg.slice(2).split('=') - const k = split.shift() - const v = split.join('=') - const no = /^no-/.test(k) && !v - const key = (no ? k.slice(3) : k) - .replace(/^tag$/, 'defaultTag') - .replace(/-([a-z])/g, (_, c) => c.toUpperCase()) - const value = v ? v.replace(/^~/, process.env.HOME) : !no - return { key, value } -} - -const parse = args => { - const conf = { - _: [], - cache: process.env.HOME + '/.npm/_cacache', - } - let dashdash = false - args.forEach(arg => { - if (dashdash) { - conf._.push(arg) - } else if (arg === '--') { - dashdash = true - } else if (arg === '-h') { - conf.help = true - } else if (/^--/.test(arg)) { - const { key, value } = parseArg(arg) - conf[key] = value - } else { - conf._.push(arg) - } - }) - return conf -} - -if (module === require.main) { - main(process.argv.slice(2)) -} else { - module.exports = { - main, - run, - usage, - parseArg, - parse, - } -} diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/dir.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/dir.js deleted file mode 100644 index 04846eb8a6e221..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/dir.js +++ /dev/null @@ -1,105 +0,0 @@ -const { resolve } = require('node:path') -const packlist = require('npm-packlist') -const runScript = require('@npmcli/run-script') -const tar = require('tar') -const { Minipass } = require('minipass') -const Fetcher = require('./fetcher.js') -const FileFetcher = require('./file.js') -const _ = require('./util/protected.js') -const tarCreateOptions = require('./util/tar-create-options.js') - -class DirFetcher extends Fetcher { - constructor (spec, opts) { - super(spec, opts) - // just the fully resolved filename - this.resolved = this.spec.fetchSpec - - this.tree = opts.tree || null - this.Arborist = opts.Arborist || null - } - - // exposes tarCreateOptions as public API - static tarCreateOptions (manifest) { - return tarCreateOptions(manifest) - } - - get types () { - return ['directory'] - } - - #prepareDir () { - return this.manifest().then(mani => { - if (!mani.scripts || !mani.scripts.prepare) { - return - } - if (this.opts.ignoreScripts) { - return - } - - // we *only* run prepare. - // pre/post-pack is run by the npm CLI for publish and pack, - // but this function is *also* run when installing git deps - const stdio = this.opts.foregroundScripts ? 'inherit' : 'pipe' - - return runScript({ - // this || undefined is because runScript will be unhappy with the default null value - scriptShell: this.opts.scriptShell || undefined, - pkg: mani, - event: 'prepare', - path: this.resolved, - stdio, - env: { - npm_package_resolved: this.resolved, - npm_package_integrity: this.integrity, - npm_package_json: resolve(this.resolved, 'package.json'), - }, - }) - }) - } - - [_.tarballFromResolved] () { - if (!this.tree && !this.Arborist) { - throw new Error('DirFetcher requires either a tree or an Arborist constructor to pack') - } - - const stream = new Minipass() - stream.resolved = this.resolved - stream.integrity = this.integrity - - const { prefix, workspaces } = this.opts - - // run the prepare script, get the list of files, and tar it up - // pipe to the stream, and proxy errors the chain. - this.#prepareDir() - .then(async () => { - if (!this.tree) { - const arb = new this.Arborist({ path: this.resolved }) - this.tree = await arb.loadActual() - } - return packlist(this.tree, { path: this.resolved, prefix, workspaces }) - }) - .then(files => tar.c(tarCreateOptions(this.package), files) - .on('error', er => stream.emit('error', er)).pipe(stream)) - .catch(er => stream.emit('error', er)) - return stream - } - - manifest () { - if (this.package) { - return Promise.resolve(this.package) - } - - return this[_.readPackageJson](this.resolved) - .then(mani => this.package = { - ...mani, - _integrity: this.integrity && String(this.integrity), - _resolved: this.resolved, - _from: this.from, - }) - } - - packument () { - return FileFetcher.prototype.packument.apply(this) - } -} -module.exports = DirFetcher diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/fetcher.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/fetcher.js deleted file mode 100644 index f2ac97619d3af1..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/fetcher.js +++ /dev/null @@ -1,497 +0,0 @@ -// This is the base class that the other fetcher types in lib -// all descend from. -// It handles the unpacking and retry logic that is shared among -// all of the other Fetcher types. - -const { basename, dirname } = require('node:path') -const { rm, mkdir } = require('node:fs/promises') -const PackageJson = require('@npmcli/package-json') -const cacache = require('cacache') -const fsm = require('fs-minipass') -const getContents = require('@npmcli/installed-package-contents') -const npa = require('npm-package-arg') -const retry = require('promise-retry') -const ssri = require('ssri') -const tar = require('tar') -const { Minipass } = require('minipass') -const { log } = require('proc-log') -const _ = require('./util/protected.js') -const cacheDir = require('./util/cache-dir.js') -const isPackageBin = require('./util/is-package-bin.js') -const removeTrailingSlashes = require('./util/trailing-slashes.js') - -// Pacote is only concerned with the package.json contents -const packageJsonPrepare = (p) => PackageJson.prepare(p).then(pkg => pkg.content) -const packageJsonNormalize = (p) => PackageJson.normalize(p).then(pkg => pkg.content) - -class FetcherBase { - constructor (spec, opts) { - if (!opts || typeof opts !== 'object') { - throw new TypeError('options object is required') - } - this.spec = npa(spec, opts.where) - - this.allowGitIgnore = !!opts.allowGitIgnore - - // a bit redundant because presumably the caller already knows this, - // but it makes it easier to not have to keep track of the requested - // spec when we're dispatching thousands of these at once, and normalizing - // is nice. saveSpec is preferred if set, because it turns stuff like - // x/y#committish into github:x/y#committish. use name@rawSpec for - // registry deps so that we turn xyz and xyz@ -> xyz@ - this.from = this.spec.registry - ? `${this.spec.name}@${this.spec.rawSpec}` : this.spec.saveSpec - - this.#assertType() - // clone the opts object so that others aren't upset when we mutate it - // by adding/modifying the integrity value. - this.opts = { ...opts } - - this.cache = opts.cache || cacheDir().cacache - this.tufCache = opts.tufCache || cacheDir().tufcache - this.resolved = opts.resolved || null - - // default to caching/verifying with sha512, that's what we usually have - // need to change this default, or start overriding it, when sha512 - // is no longer strong enough. - this.defaultIntegrityAlgorithm = opts.defaultIntegrityAlgorithm || 'sha512' - - if (typeof opts.integrity === 'string') { - this.opts.integrity = ssri.parse(opts.integrity) - } - - this.package = null - this.type = this.constructor.name - this.fmode = opts.fmode || 0o666 - this.dmode = opts.dmode || 0o777 - // we don't need a default umask, because we don't chmod files coming - // out of package tarballs. they're forced to have a mode that is - // valid, regardless of what's in the tarball entry, and then we let - // the process's umask setting do its job. but if configured, we do - // respect it. - this.umask = opts.umask || 0 - - this.preferOnline = !!opts.preferOnline - this.preferOffline = !!opts.preferOffline - this.offline = !!opts.offline - - this.before = opts.before - this.fullMetadata = this.before ? true : !!opts.fullMetadata - this.fullReadJson = !!opts.fullReadJson - this[_.readPackageJson] = this.fullReadJson - ? packageJsonPrepare - : packageJsonNormalize - - // rrh is a registry hostname or 'never' or 'always' - // defaults to registry.npmjs.org - this.replaceRegistryHost = (!opts.replaceRegistryHost || opts.replaceRegistryHost === 'npmjs') ? - 'registry.npmjs.org' : opts.replaceRegistryHost - - this.defaultTag = opts.defaultTag || 'latest' - this.registry = removeTrailingSlashes(opts.registry || 'https://registry.npmjs.org') - - // command to run 'prepare' scripts on directories and git dirs - // To use pacote with yarn, for example, set npmBin to 'yarn' - // and npmCliConfig with yarn's equivalents. - this.npmBin = opts.npmBin || 'npm' - - // command to install deps for preparing - this.npmInstallCmd = opts.npmInstallCmd || ['install', '--force'] - - // XXX fill more of this in based on what we know from this.opts - // we explicitly DO NOT fill in --tag, though, since we are often - // going to be packing in the context of a publish, which may set - // a dist-tag, but certainly wants to keep defaulting to latest. - this.npmCliConfig = opts.npmCliConfig || [ - `--cache=${dirname(this.cache)}`, - `--prefer-offline=${!!this.preferOffline}`, - `--prefer-online=${!!this.preferOnline}`, - `--offline=${!!this.offline}`, - ...(this.before ? [`--before=${this.before.toISOString()}`] : []), - '--no-progress', - '--no-save', - '--no-audit', - // override any omit settings from the environment - '--include=dev', - '--include=peer', - '--include=optional', - // we need the actual things, not just the lockfile - '--no-package-lock-only', - '--no-dry-run', - ] - } - - get integrity () { - return this.opts.integrity || null - } - - set integrity (i) { - if (!i) { - return - } - - i = ssri.parse(i) - const current = this.opts.integrity - - // do not ever update an existing hash value, but do - // merge in NEW algos and hashes that we don't already have. - if (current) { - current.merge(i) - } else { - this.opts.integrity = i - } - } - - get notImplementedError () { - return new Error('not implemented in this fetcher type: ' + this.type) - } - - // override in child classes - // Returns a Promise that resolves to this.resolved string value - resolve () { - return this.resolved ? Promise.resolve(this.resolved) - : Promise.reject(this.notImplementedError) - } - - packument () { - return Promise.reject(this.notImplementedError) - } - - // override in child class - // returns a manifest containing: - // - name - // - version - // - _resolved - // - _integrity - // - plus whatever else was in there (corgi, full metadata, or pj file) - manifest () { - return Promise.reject(this.notImplementedError) - } - - // private, should be overridden. - // Note that they should *not* calculate or check integrity or cache, - // but *just* return the raw tarball data stream. - [_.tarballFromResolved] () { - throw this.notImplementedError - } - - // public, should not be overridden - tarball () { - return this.tarballStream(stream => stream.concat().then(data => { - data.integrity = this.integrity && String(this.integrity) - data.resolved = this.resolved - data.from = this.from - return data - })) - } - - // private - // Note: cacache will raise a EINTEGRITY error if the integrity doesn't match - #tarballFromCache () { - const startTime = Date.now() - const stream = cacache.get.stream.byDigest(this.cache, this.integrity, this.opts) - const elapsedTime = Date.now() - startTime - // cache is good, so log it as a hit in particular since there was no fetch logged - log.http( - 'cache', - `${this.spec} ${elapsedTime}ms (cache hit)` - ) - return stream - } - - get [_.cacheFetches] () { - return true - } - - #istream (stream) { - // if not caching this, just return it - if (!this.opts.cache || !this[_.cacheFetches]) { - // instead of creating a new integrity stream, we only piggyback on the - // provided stream's events - if (stream.hasIntegrityEmitter) { - stream.on('integrity', i => this.integrity = i) - return stream - } - - const istream = ssri.integrityStream(this.opts) - istream.on('integrity', i => this.integrity = i) - stream.on('error', err => istream.emit('error', err)) - return stream.pipe(istream) - } - - // we have to return a stream that gets ALL the data, and proxies errors, - // but then pipe from the original tarball stream into the cache as well. - // To do this without losing any data, and since the cacache put stream - // is not a passthrough, we have to pipe from the original stream into - // the cache AFTER we pipe into the middleStream. Since the cache stream - // has an asynchronous flush to write its contents to disk, we need to - // defer the middleStream end until the cache stream ends. - const middleStream = new Minipass() - stream.on('error', err => middleStream.emit('error', err)) - stream.pipe(middleStream, { end: false }) - const cstream = cacache.put.stream( - this.opts.cache, - `pacote:tarball:${this.from}`, - this.opts - ) - cstream.on('integrity', i => this.integrity = i) - cstream.on('error', err => stream.emit('error', err)) - stream.pipe(cstream) - - // eslint-disable-next-line promise/catch-or-return - cstream.promise().catch(() => {}).then(() => middleStream.end()) - return middleStream - } - - pickIntegrityAlgorithm () { - return this.integrity ? this.integrity.pickAlgorithm(this.opts) - : this.defaultIntegrityAlgorithm - } - - // TODO: check error class, once those are rolled out to our deps - isDataCorruptionError (er) { - return er.code === 'EINTEGRITY' || er.code === 'Z_DATA_ERROR' - } - - // override the types getter - get types () { - return false - } - - #assertType () { - if (this.types && !this.types.includes(this.spec.type)) { - throw new TypeError(`Wrong spec type (${ - this.spec.type - }) for ${ - this.constructor.name - }. Supported types: ${this.types.join(', ')}`) - } - } - - // We allow ENOENTs from cacache, but not anywhere else. - // An ENOENT trying to read a tgz file, for example, is Right Out. - isRetriableError (er) { - // TODO: check error class, once those are rolled out to our deps - return this.isDataCorruptionError(er) || - er.code === 'ENOENT' || - er.code === 'EISDIR' - } - - // Mostly internal, but has some uses - // Pass in a function which returns a promise - // Function will be called 1 or more times with streams that may fail. - // Retries: - // Function MUST handle errors on the stream by rejecting the promise, - // so that retry logic can pick it up and either retry or fail whatever - // promise it was making (ie, failing extraction, etc.) - // - // The return value of this method is a Promise that resolves the same - // as whatever the streamHandler resolves to. - // - // This should never be overridden by child classes, but it is public. - tarballStream (streamHandler) { - // Only short-circuit via cache if we have everything else we'll need, - // and the user has not expressed a preference for checking online. - - const fromCache = ( - !this.preferOnline && - this.integrity && - this.resolved - ) ? streamHandler(this.#tarballFromCache()).catch(er => { - if (this.isDataCorruptionError(er)) { - log.warn('tarball', `cached data for ${ - this.spec - } (${this.integrity}) seems to be corrupted. Refreshing cache.`) - return this.cleanupCached().then(() => { - throw er - }) - } else { - throw er - } - }) : null - - const fromResolved = er => { - if (er) { - if (!this.isRetriableError(er)) { - throw er - } - log.silly('tarball', `no local data for ${ - this.spec - }. Extracting by manifest.`) - } - return this.resolve().then(() => retry(tryAgain => - streamHandler(this.#istream(this[_.tarballFromResolved]())) - .catch(streamErr => { - // Most likely data integrity. A cache ENOENT error is unlikely - // here, since we're definitely not reading from the cache, but it - // IS possible that the fetch subsystem accessed the cache, and the - // entry got blown away or something. Try one more time to be sure. - if (this.isRetriableError(streamErr)) { - log.warn('tarball', `tarball data for ${ - this.spec - } (${this.integrity}) seems to be corrupted. Trying again.`) - return this.cleanupCached().then(() => tryAgain(streamErr)) - } - throw streamErr - }), { retries: 1, minTimeout: 0, maxTimeout: 0 })) - } - - return fromCache ? fromCache.catch(fromResolved) : fromResolved() - } - - cleanupCached () { - return cacache.rm.content(this.cache, this.integrity, this.opts) - } - - #empty (path) { - return getContents({ path, depth: 1 }).then(contents => Promise.all( - contents.map(entry => rm(entry, { recursive: true, force: true })))) - } - - async #mkdir (dest) { - await this.#empty(dest) - return await mkdir(dest, { recursive: true }) - } - - // extraction is always the same. the only difference is where - // the tarball comes from. - async extract (dest) { - await this.#mkdir(dest) - return this.tarballStream((tarball) => this.#extract(dest, tarball)) - } - - #toFile (dest) { - return this.tarballStream(str => new Promise((res, rej) => { - const writer = new fsm.WriteStream(dest) - str.on('error', er => writer.emit('error', er)) - writer.on('error', er => rej(er)) - writer.on('close', () => res({ - integrity: this.integrity && String(this.integrity), - resolved: this.resolved, - from: this.from, - })) - str.pipe(writer) - })) - } - - // don't use this.#mkdir because we don't want to rimraf anything - async tarballFile (dest) { - const dir = dirname(dest) - await mkdir(dir, { recursive: true }) - return this.#toFile(dest) - } - - #extract (dest, tarball) { - const extractor = tar.x(this.#tarxOptions({ cwd: dest })) - const p = new Promise((resolve, reject) => { - extractor.on('end', () => { - resolve({ - resolved: this.resolved, - integrity: this.integrity && String(this.integrity), - from: this.from, - }) - }) - - extractor.on('error', er => { - log.warn('tar', er.message) - log.silly('tar', er) - reject(er) - }) - - tarball.on('error', er => reject(er)) - }) - - tarball.pipe(extractor) - return p - } - - // always ensure that entries are at least as permissive as our configured - // dmode/fmode, but never more permissive than the umask allows. - #entryMode (path, mode, type) { - const m = /Directory|GNUDumpDir/.test(type) ? this.dmode - : /File$/.test(type) ? this.fmode - : /* istanbul ignore next - should never happen in a pkg */ 0 - - // make sure package bins are executable - const exe = isPackageBin(this.package, path) ? 0o111 : 0 - // always ensure that files are read/writable by the owner - return ((mode | m) & ~this.umask) | exe | 0o600 - } - - #tarxOptions ({ cwd }) { - const sawIgnores = new Set() - return { - cwd, - noChmod: true, - noMtime: true, - filter: (name, entry) => { - if (/Link$/.test(entry.type)) { - return false - } - entry.mode = this.#entryMode(entry.path, entry.mode, entry.type) - // this replicates the npm pack behavior where .gitignore files - // are treated like .npmignore files, but only if a .npmignore - // file is not present. - if (/File$/.test(entry.type)) { - const base = basename(entry.path) - if (base === '.npmignore') { - sawIgnores.add(entry.path) - } else if (base === '.gitignore' && !this.allowGitIgnore) { - // rename, but only if there's not already a .npmignore - const ni = entry.path.replace(/\.gitignore$/, '.npmignore') - if (sawIgnores.has(ni)) { - return false - } - entry.path = ni - } - return true - } - }, - strip: 1, - onwarn: /* istanbul ignore next - we can trust that tar logs */ - (code, msg, data) => { - log.warn('tar', code, msg) - log.silly('tar', code, msg, data) - }, - umask: this.umask, - // always ignore ownership info from tarball metadata - preserveOwner: false, - } - } -} - -module.exports = FetcherBase - -// Child classes -const GitFetcher = require('./git.js') -const RegistryFetcher = require('./registry.js') -const FileFetcher = require('./file.js') -const DirFetcher = require('./dir.js') -const RemoteFetcher = require('./remote.js') - -// Get an appropriate fetcher object from a spec and options -FetcherBase.get = (rawSpec, opts = {}) => { - const spec = npa(rawSpec, opts.where) - switch (spec.type) { - case 'git': - return new GitFetcher(spec, opts) - - case 'remote': - return new RemoteFetcher(spec, opts) - - case 'version': - case 'range': - case 'tag': - case 'alias': - return new RegistryFetcher(spec.subSpec || spec, opts) - - case 'file': - return new FileFetcher(spec, opts) - - case 'directory': - return new DirFetcher(spec, opts) - - default: - throw new TypeError('Unknown spec type: ' + spec.type) - } -} diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/file.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/file.js deleted file mode 100644 index 2021325085e4f0..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/file.js +++ /dev/null @@ -1,94 +0,0 @@ -const { resolve } = require('node:path') -const { stat, chmod } = require('node:fs/promises') -const cacache = require('cacache') -const fsm = require('fs-minipass') -const Fetcher = require('./fetcher.js') -const _ = require('./util/protected.js') - -class FileFetcher extends Fetcher { - constructor (spec, opts) { - super(spec, opts) - // just the fully resolved filename - this.resolved = this.spec.fetchSpec - } - - get types () { - return ['file'] - } - - manifest () { - if (this.package) { - return Promise.resolve(this.package) - } - - // have to unpack the tarball for this. - return cacache.tmp.withTmp(this.cache, this.opts, dir => - this.extract(dir) - .then(() => this[_.readPackageJson](dir)) - .then(mani => this.package = { - ...mani, - _integrity: this.integrity && String(this.integrity), - _resolved: this.resolved, - _from: this.from, - })) - } - - #exeBins (pkg, dest) { - if (!pkg.bin) { - return Promise.resolve() - } - - return Promise.all(Object.keys(pkg.bin).map(async k => { - const script = resolve(dest, pkg.bin[k]) - // Best effort. Ignore errors here, the only result is that - // a bin script is not executable. But if it's missing or - // something, we just leave it for a later stage to trip over - // when we can provide a more useful contextual error. - try { - const st = await stat(script) - const mode = st.mode | 0o111 - if (mode === st.mode) { - return - } - await chmod(script, mode) - } catch { - // Ignore errors here - } - })) - } - - extract (dest) { - // if we've already loaded the manifest, then the super got it. - // but if not, read the unpacked manifest and chmod properly. - return super.extract(dest) - .then(result => this.package ? result - : this[_.readPackageJson](dest).then(pkg => - this.#exeBins(pkg, dest)).then(() => result)) - } - - [_.tarballFromResolved] () { - // create a read stream and return it - return new fsm.ReadStream(this.resolved) - } - - packument () { - // simulate based on manifest - return this.manifest().then(mani => ({ - name: mani.name, - 'dist-tags': { - [this.defaultTag]: mani.version, - }, - versions: { - [mani.version]: { - ...mani, - dist: { - tarball: `file:${this.resolved}`, - integrity: this.integrity && String(this.integrity), - }, - }, - }, - })) - } -} - -module.exports = FileFetcher diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/git.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/git.js deleted file mode 100644 index 077193a86f026f..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/git.js +++ /dev/null @@ -1,317 +0,0 @@ -const cacache = require('cacache') -const git = require('@npmcli/git') -const npa = require('npm-package-arg') -const pickManifest = require('npm-pick-manifest') -const { Minipass } = require('minipass') -const { log } = require('proc-log') -const DirFetcher = require('./dir.js') -const Fetcher = require('./fetcher.js') -const FileFetcher = require('./file.js') -const RemoteFetcher = require('./remote.js') -const _ = require('./util/protected.js') -const addGitSha = require('./util/add-git-sha.js') -const npm = require('./util/npm.js') - -const hashre = /^[a-f0-9]{40}$/ - -// get the repository url. -// prefer https if there's auth, since ssh will drop that. -// otherwise, prefer ssh if available (more secure). -// We have to add the git+ back because npa suppresses it. -const repoUrl = (h, opts) => - h.sshurl && !(h.https && h.auth) && addGitPlus(h.sshurl(opts)) || - h.https && addGitPlus(h.https(opts)) - -// add git+ to the url, but only one time. -const addGitPlus = url => url && `git+${url}`.replace(/^(git\+)+/, 'git+') - -class GitFetcher extends Fetcher { - constructor (spec, opts) { - super(spec, opts) - - // we never want to compare integrity for git dependencies: npm/rfcs#525 - if (this.opts.integrity) { - delete this.opts.integrity - log.warn(`skipping integrity check for git dependency ${this.spec.fetchSpec}`) - } - - this.resolvedRef = null - if (this.spec.hosted) { - this.from = this.spec.hosted.shortcut({ noCommittish: false }) - } - - // shortcut: avoid full clone when we can go straight to the tgz - // if we have the full sha and it's a hosted git platform - if (this.spec.gitCommittish && hashre.test(this.spec.gitCommittish)) { - this.resolvedSha = this.spec.gitCommittish - // use hosted.tarball() when we shell to RemoteFetcher later - this.resolved = this.spec.hosted - ? repoUrl(this.spec.hosted, { noCommittish: false }) - : this.spec.rawSpec - } else { - this.resolvedSha = '' - } - - this.Arborist = opts.Arborist || null - } - - // just exposed to make it easier to test all the combinations - static repoUrl (hosted, opts) { - return repoUrl(hosted, opts) - } - - get types () { - return ['git'] - } - - resolve () { - // likely a hosted git repo with a sha, so get the tarball url - // but in general, no reason to resolve() more than necessary! - if (this.resolved) { - return super.resolve() - } - - // fetch the git repo and then look at the current hash - const h = this.spec.hosted - // try to use ssh, fall back to git. - return h - ? this.#resolvedFromHosted(h) - : this.#resolvedFromRepo(this.spec.fetchSpec) - } - - // first try https, since that's faster and passphrase-less for - // public repos, and supports private repos when auth is provided. - // Fall back to SSH to support private repos - // NB: we always store the https url in resolved field if auth - // is present, otherwise ssh if the hosted type provides it - #resolvedFromHosted (hosted) { - return this.#resolvedFromRepo(hosted.https && hosted.https()).catch(er => { - // Throw early since we know pathspec errors will fail again if retried - if (er instanceof git.errors.GitPathspecError) { - throw er - } - const ssh = hosted.sshurl && hosted.sshurl() - // no fallthrough if we can't fall through or have https auth - if (!ssh || hosted.auth) { - throw er - } - return this.#resolvedFromRepo(ssh) - }) - } - - #resolvedFromRepo (gitRemote) { - // XXX make this a custom error class - if (!gitRemote) { - return Promise.reject(new Error(`No git url for ${this.spec}`)) - } - const gitRange = this.spec.gitRange - const name = this.spec.name - return git.revs(gitRemote, this.opts).then(remoteRefs => { - return gitRange ? pickManifest({ - versions: remoteRefs.versions, - 'dist-tags': remoteRefs['dist-tags'], - name, - }, gitRange, this.opts) - : this.spec.gitCommittish ? - remoteRefs.refs[this.spec.gitCommittish] || - remoteRefs.refs[remoteRefs.shas[this.spec.gitCommittish]] - : remoteRefs.refs.HEAD // no git committish, get default head - }).then(revDoc => { - // the committish provided isn't in the rev list - // things like HEAD~3 or @yesterday can land here. - if (!revDoc || !revDoc.sha) { - return this.#resolvedFromClone() - } - - this.resolvedRef = revDoc - this.resolvedSha = revDoc.sha - this.#addGitSha(revDoc.sha) - return this.resolved - }) - } - - #setResolvedWithSha (withSha) { - // we haven't cloned, so a tgz download is still faster - // of course, if it's not a known host, we can't do that. - this.resolved = !this.spec.hosted ? withSha - : repoUrl(npa(withSha).hosted, { noCommittish: false }) - } - - // when we get the git sha, we affix it to our spec to build up - // either a git url with a hash, or a tarball download URL - #addGitSha (sha) { - this.#setResolvedWithSha(addGitSha(this.spec, sha)) - } - - #resolvedFromClone () { - // do a full or shallow clone, then look at the HEAD - // kind of wasteful, but no other option, really - return this.#clone(() => this.resolved) - } - - #prepareDir (dir) { - return this[_.readPackageJson](dir).then(mani => { - // no need if we aren't going to do any preparation. - const scripts = mani.scripts - if (!mani.workspaces && (!scripts || !( - scripts.postinstall || - scripts.build || - scripts.preinstall || - scripts.install || - scripts.prepack || - scripts.prepare))) { - return - } - - // to avoid cases where we have an cycle of git deps that depend - // on one another, we only ever do preparation for one instance - // of a given git dep along the chain of installations. - // Note that this does mean that a dependency MAY in theory end up - // trying to run its prepare script using a dependency that has not - // been properly prepared itself, but that edge case is smaller - // and less hazardous than a fork bomb of npm and git commands. - const noPrepare = !process.env._PACOTE_NO_PREPARE_ ? [] - : process.env._PACOTE_NO_PREPARE_.split('\n') - if (noPrepare.includes(this.resolved)) { - log.info('prepare', 'skip prepare, already seen', this.resolved) - return - } - noPrepare.push(this.resolved) - - // the DirFetcher will do its own preparation to run the prepare scripts - // All we have to do is put the deps in place so that it can succeed. - return npm( - this.npmBin, - [].concat(this.npmInstallCmd).concat(this.npmCliConfig), - dir, - { ...process.env, _PACOTE_NO_PREPARE_: noPrepare.join('\n') }, - { message: 'git dep preparation failed' } - ) - }) - } - - [_.tarballFromResolved] () { - const stream = new Minipass() - stream.resolved = this.resolved - stream.from = this.from - - // check it out and then shell out to the DirFetcher tarball packer - this.#clone(dir => this.#prepareDir(dir) - .then(() => new Promise((res, rej) => { - if (!this.Arborist) { - throw new Error('GitFetcher requires an Arborist constructor to pack a tarball') - } - const df = new DirFetcher(`file:${dir}`, { - ...this.opts, - Arborist: this.Arborist, - resolved: null, - integrity: null, - }) - const dirStream = df[_.tarballFromResolved]() - dirStream.on('error', rej) - dirStream.on('end', res) - dirStream.pipe(stream) - }))).catch( - /* istanbul ignore next: very unlikely and hard to test */ - er => stream.emit('error', er) - ) - return stream - } - - // clone a git repo into a temp folder (or fetch and unpack if possible) - // handler accepts a directory, and returns a promise that resolves - // when we're done with it, at which point, cacache deletes it - // - // TODO: after cloning, create a tarball of the folder, and add to the cache - // with cacache.put.stream(), using a key that's deterministic based on the - // spec and repo, so that we don't ever clone the same thing multiple times. - #clone (handler, tarballOk = true) { - const o = { tmpPrefix: 'git-clone' } - const ref = this.resolvedSha || this.spec.gitCommittish - const h = this.spec.hosted - const resolved = this.resolved - - // can be set manually to false to fall back to actual git clone - tarballOk = tarballOk && - h && resolved === repoUrl(h, { noCommittish: false }) && h.tarball - - return cacache.tmp.withTmp(this.cache, o, async tmp => { - // if we're resolved, and have a tarball url, shell out to RemoteFetcher - if (tarballOk) { - const nameat = this.spec.name ? `${this.spec.name}@` : '' - return new RemoteFetcher(h.tarball({ noCommittish: false }), { - ...this.opts, - allowGitIgnore: true, - pkgid: `git:${nameat}${this.resolved}`, - resolved: this.resolved, - integrity: null, // it'll always be different, if we have one - }).extract(tmp).then(() => handler(tmp), er => { - // fall back to ssh download if tarball fails - if (er.constructor.name.match(/^Http/)) { - return this.#clone(handler, false) - } else { - throw er - } - }) - } - - const sha = await ( - h ? this.#cloneHosted(ref, tmp) - : this.#cloneRepo(this.spec.fetchSpec, ref, tmp) - ) - this.resolvedSha = sha - if (!this.resolved) { - await this.#addGitSha(sha) - } - return handler(tmp) - }) - } - - // first try https, since that's faster and passphrase-less for - // public repos, and supports private repos when auth is provided. - // Fall back to SSH to support private repos - // NB: we always store the https url in resolved field if auth - // is present, otherwise ssh if the hosted type provides it - #cloneHosted (ref, tmp) { - const hosted = this.spec.hosted - return this.#cloneRepo(hosted.https({ noCommittish: true }), ref, tmp) - .catch(er => { - // Throw early since we know pathspec errors will fail again if retried - if (er instanceof git.errors.GitPathspecError) { - throw er - } - const ssh = hosted.sshurl && hosted.sshurl({ noCommittish: true }) - // no fallthrough if we can't fall through or have https auth - if (!ssh || hosted.auth) { - throw er - } - return this.#cloneRepo(ssh, ref, tmp) - }) - } - - #cloneRepo (repo, ref, tmp) { - const { opts, spec } = this - return git.clone(repo, ref, tmp, { ...opts, spec }) - } - - manifest () { - if (this.package) { - return Promise.resolve(this.package) - } - - return this.spec.hosted && this.resolved - ? FileFetcher.prototype.manifest.apply(this) - : this.#clone(dir => - this[_.readPackageJson](dir) - .then(mani => this.package = { - ...mani, - _resolved: this.resolved, - _from: this.from, - })) - } - - packument () { - return FileFetcher.prototype.packument.apply(this) - } -} -module.exports = GitFetcher diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/index.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/index.js deleted file mode 100644 index f35314d275d5fd..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/index.js +++ /dev/null @@ -1,23 +0,0 @@ -const { get } = require('./fetcher.js') -const GitFetcher = require('./git.js') -const RegistryFetcher = require('./registry.js') -const FileFetcher = require('./file.js') -const DirFetcher = require('./dir.js') -const RemoteFetcher = require('./remote.js') - -const tarball = (spec, opts) => get(spec, opts).tarball() -tarball.stream = (spec, handler, opts) => get(spec, opts).tarballStream(handler) -tarball.file = (spec, dest, opts) => get(spec, opts).tarballFile(dest) - -module.exports = { - GitFetcher, - RegistryFetcher, - FileFetcher, - DirFetcher, - RemoteFetcher, - resolve: (spec, opts) => get(spec, opts).resolve(), - extract: (spec, dest, opts) => get(spec, opts).extract(dest), - manifest: (spec, opts) => get(spec, opts).manifest(), - packument: (spec, opts) => get(spec, opts).packument(), - tarball, -} diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/registry.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/registry.js deleted file mode 100644 index 1ecf4ee1773499..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/registry.js +++ /dev/null @@ -1,369 +0,0 @@ -const crypto = require('node:crypto') -const PackageJson = require('@npmcli/package-json') -const pickManifest = require('npm-pick-manifest') -const ssri = require('ssri') -const npa = require('npm-package-arg') -const sigstore = require('sigstore') -const fetch = require('npm-registry-fetch') -const Fetcher = require('./fetcher.js') -const RemoteFetcher = require('./remote.js') -const pacoteVersion = require('../package.json').version -const removeTrailingSlashes = require('./util/trailing-slashes.js') -const _ = require('./util/protected.js') - -// Corgis are cute. 🐕🐶 -const corgiDoc = 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*' -const fullDoc = 'application/json' - -// Some really old packages have no time field in their packument so we need a -// cutoff date. -const MISSING_TIME_CUTOFF = '2015-01-01T00:00:00.000Z' - -class RegistryFetcher extends Fetcher { - #cacheKey - constructor (spec, opts) { - super(spec, opts) - - // you usually don't want to fetch the same packument multiple times in - // the span of a given script or command, no matter how many pacote calls - // are made, so this lets us avoid doing that. It's only relevant for - // registry fetchers, because other types simulate their packument from - // the manifest, which they memoize on this.package, so it's very cheap - // already. - this.packumentCache = this.opts.packumentCache || null - - this.registry = fetch.pickRegistry(spec, opts) - this.packumentUrl = `${removeTrailingSlashes(this.registry)}/${this.spec.escapedName}` - this.#cacheKey = `${this.fullMetadata ? 'full' : 'corgi'}:${this.packumentUrl}` - - const parsed = new URL(this.registry) - const regKey = `//${parsed.host}${parsed.pathname}` - // unlike the nerf-darted auth keys, this one does *not* allow a mismatch - // of trailing slashes. It must match exactly. - if (this.opts[`${regKey}:_keys`]) { - this.registryKeys = this.opts[`${regKey}:_keys`] - } - - // XXX pacote <=9 has some logic to ignore opts.resolved if - // the resolved URL doesn't go to the same registry. - // Consider reproducing that here, to throw away this.resolved - // in that case. - } - - async resolve () { - // fetching the manifest sets resolved and (if present) integrity - await this.manifest() - if (!this.resolved) { - throw Object.assign( - new Error('Invalid package manifest: no `dist.tarball` field'), - { package: this.spec.toString() } - ) - } - return this.resolved - } - - #headers () { - return { - // npm will override UA, but ensure that we always send *something* - 'user-agent': this.opts.userAgent || - `pacote/${pacoteVersion} node/${process.version}`, - ...(this.opts.headers || {}), - 'pacote-version': pacoteVersion, - 'pacote-req-type': 'packument', - 'pacote-pkg-id': `registry:${this.spec.name}`, - accept: this.fullMetadata ? fullDoc : corgiDoc, - } - } - - async packument () { - // note this might be either an in-flight promise for a request, - // or the actual packument, but we never want to make more than - // one request at a time for the same thing regardless. - if (this.packumentCache?.has(this.#cacheKey)) { - return this.packumentCache.get(this.#cacheKey) - } - - // npm-registry-fetch the packument - // set the appropriate header for corgis if fullMetadata isn't set - // return the res.json() promise - try { - const res = await fetch(this.packumentUrl, { - ...this.opts, - headers: this.#headers(), - spec: this.spec, - - // never check integrity for packuments themselves - integrity: null, - }) - const packument = await res.json() - const contentLength = res.headers.get('content-length') - if (contentLength) { - packument._contentLength = Number(contentLength) - } - this.packumentCache?.set(this.#cacheKey, packument) - return packument - } catch (err) { - this.packumentCache?.delete(this.#cacheKey) - if (err.code !== 'E404' || this.fullMetadata) { - throw err - } - // possible that corgis are not supported by this registry - this.fullMetadata = true - return this.packument() - } - } - - async manifest () { - if (this.package) { - return this.package - } - - // When verifying signatures, we need to fetch the full/uncompressed - // packument to get publish time as this is not included in the - // corgi/compressed packument. - if (this.opts.verifySignatures) { - this.fullMetadata = true - } - - const packument = await this.packument() - const steps = PackageJson.normalizeSteps.filter(s => s !== '_attributes') - const mani = await new PackageJson().fromContent(pickManifest(packument, this.spec.fetchSpec, { - ...this.opts, - defaultTag: this.defaultTag, - before: this.before, - })).normalize({ steps }).then(p => p.content) - - /* XXX add ETARGET and E403 revalidation of cached packuments here */ - - // add _time from packument if fetched with fullMetadata - const time = packument.time?.[mani.version] - if (time) { - mani._time = time - } - - // add _resolved and _integrity from dist object - const { dist } = mani - if (dist) { - this.resolved = mani._resolved = dist.tarball - mani._from = this.from - const distIntegrity = dist.integrity ? ssri.parse(dist.integrity) - : dist.shasum ? ssri.fromHex(dist.shasum, 'sha1', { ...this.opts }) - : null - if (distIntegrity) { - if (this.integrity && !this.integrity.match(distIntegrity)) { - // only bork if they have algos in common. - // otherwise we end up breaking if we have saved a sha512 - // previously for the tarball, but the manifest only - // provides a sha1, which is possible for older publishes. - // Otherwise, this is almost certainly a case of holding it - // wrong, and will result in weird or insecure behavior - // later on when building package tree. - for (const algo of Object.keys(this.integrity)) { - if (distIntegrity[algo]) { - throw Object.assign(new Error( - `Integrity checksum failed when using ${algo}: ` + - `wanted ${this.integrity} but got ${distIntegrity}.` - ), { code: 'EINTEGRITY' }) - } - } - } - // made it this far, the integrity is worthwhile. accept it. - // the setter here will take care of merging it into what we already - // had. - this.integrity = distIntegrity - } - } - if (this.integrity) { - mani._integrity = String(this.integrity) - if (dist.signatures) { - if (this.opts.verifySignatures) { - // validate and throw on error, then set _signatures - const message = `${mani._id}:${mani._integrity}` - for (const signature of dist.signatures) { - const publicKey = this.registryKeys && - this.registryKeys.filter(key => (key.keyid === signature.keyid))[0] - if (!publicKey) { - throw Object.assign(new Error( - `${mani._id} has a registry signature with keyid: ${signature.keyid} ` + - 'but no corresponding public key can be found' - ), { code: 'EMISSINGSIGNATUREKEY' }) - } - - const publishedTime = Date.parse(mani._time || MISSING_TIME_CUTOFF) - const validPublicKey = !publicKey.expires || - publishedTime < Date.parse(publicKey.expires) - if (!validPublicKey) { - throw Object.assign(new Error( - `${mani._id} has a registry signature with keyid: ${signature.keyid} ` + - `but the corresponding public key has expired ${publicKey.expires}` - ), { code: 'EEXPIREDSIGNATUREKEY' }) - } - const verifier = crypto.createVerify('SHA256') - verifier.write(message) - verifier.end() - const valid = verifier.verify( - publicKey.pemkey, - signature.sig, - 'base64' - ) - if (!valid) { - throw Object.assign(new Error( - `${mani._id} has an invalid registry signature with ` + - `keyid: ${publicKey.keyid} and signature: ${signature.sig}` - ), { - code: 'EINTEGRITYSIGNATURE', - keyid: publicKey.keyid, - signature: signature.sig, - resolved: mani._resolved, - integrity: mani._integrity, - }) - } - } - mani._signatures = dist.signatures - } else { - mani._signatures = dist.signatures - } - } - - if (dist.attestations) { - if (this.opts.verifyAttestations) { - // Always fetch attestations from the current registry host - const attestationsPath = new URL(dist.attestations.url).pathname - const attestationsUrl = removeTrailingSlashes(this.registry) + attestationsPath - const res = await fetch(attestationsUrl, { - ...this.opts, - // disable integrity check for attestations json payload, we check the - // integrity in the verification steps below - integrity: null, - }) - const { attestations } = await res.json() - const bundles = attestations.map(({ predicateType, bundle }) => { - const statement = JSON.parse( - Buffer.from(bundle.dsseEnvelope.payload, 'base64').toString('utf8') - ) - const keyid = bundle.dsseEnvelope.signatures[0].keyid - const signature = bundle.dsseEnvelope.signatures[0].sig - - return { - predicateType, - bundle, - statement, - keyid, - signature, - } - }) - - const attestationKeyIds = bundles.map((b) => b.keyid).filter((k) => !!k) - const attestationRegistryKeys = (this.registryKeys || []) - .filter(key => attestationKeyIds.includes(key.keyid)) - if (!attestationRegistryKeys.length) { - throw Object.assign(new Error( - `${mani._id} has attestations but no corresponding public key(s) can be found` - ), { code: 'EMISSINGSIGNATUREKEY' }) - } - - for (const { predicateType, bundle, keyid, signature, statement } of bundles) { - const publicKey = attestationRegistryKeys.find(key => key.keyid === keyid) - // Publish attestations have a keyid set and a valid public key must be found - if (keyid) { - if (!publicKey) { - throw Object.assign(new Error( - `${mani._id} has attestations with keyid: ${keyid} ` + - 'but no corresponding public key can be found' - ), { code: 'EMISSINGSIGNATUREKEY' }) - } - - const integratedTime = new Date( - Number( - bundle.verificationMaterial.tlogEntries[0].integratedTime - ) * 1000 - ) - const validPublicKey = !publicKey.expires || - (integratedTime < Date.parse(publicKey.expires)) - if (!validPublicKey) { - throw Object.assign(new Error( - `${mani._id} has attestations with keyid: ${keyid} ` + - `but the corresponding public key has expired ${publicKey.expires}` - ), { code: 'EEXPIREDSIGNATUREKEY' }) - } - } - - const subject = { - name: statement.subject[0].name, - sha512: statement.subject[0].digest.sha512, - } - - // Only type 'version' can be turned into a PURL - const purl = this.spec.type === 'version' ? npa.toPurl(this.spec) : this.spec - // Verify the statement subject matches the package, version - if (subject.name !== purl) { - throw Object.assign(new Error( - `${mani._id} package name and version (PURL): ${purl} ` + - `doesn't match what was signed: ${subject.name}` - ), { code: 'EATTESTATIONSUBJECT' }) - } - - // Verify the statement subject matches the tarball integrity - const integrityHexDigest = ssri.parse(this.integrity).hexDigest() - if (subject.sha512 !== integrityHexDigest) { - throw Object.assign(new Error( - `${mani._id} package integrity (hex digest): ` + - `${integrityHexDigest} ` + - `doesn't match what was signed: ${subject.sha512}` - ), { code: 'EATTESTATIONSUBJECT' }) - } - - try { - // Provenance attestations are signed with a signing certificate - // (including the key) so we don't need to return a public key. - // - // Publish attestations are signed with a keyid so we need to - // specify a public key from the keys endpoint: `registry-host.tld/-/npm/v1/keys` - const options = { - tufCachePath: this.tufCache, - tufForceCache: true, - keySelector: publicKey ? () => publicKey.pemkey : undefined, - } - await sigstore.verify(bundle, options) - } catch (e) { - throw Object.assign(new Error( - `${mani._id} failed to verify attestation: ${e.message}` - ), { - code: 'EATTESTATIONVERIFY', - predicateType, - keyid, - signature, - resolved: mani._resolved, - integrity: mani._integrity, - }) - } - } - mani._attestations = dist.attestations - } else { - mani._attestations = dist.attestations - } - } - } - - this.package = mani - return this.package - } - - [_.tarballFromResolved] () { - // we use a RemoteFetcher to get the actual tarball stream - return new RemoteFetcher(this.resolved, { - ...this.opts, - resolved: this.resolved, - pkgid: `registry:${this.spec.name}@${this.resolved}`, - })[_.tarballFromResolved]() - } - - get types () { - return [ - 'tag', - 'version', - 'range', - ] - } -} -module.exports = RegistryFetcher diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/remote.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/remote.js deleted file mode 100644 index bd321e65a1f18a..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/remote.js +++ /dev/null @@ -1,89 +0,0 @@ -const fetch = require('npm-registry-fetch') -const { Minipass } = require('minipass') -const Fetcher = require('./fetcher.js') -const FileFetcher = require('./file.js') -const _ = require('./util/protected.js') -const pacoteVersion = require('../package.json').version - -class RemoteFetcher extends Fetcher { - constructor (spec, opts) { - super(spec, opts) - this.resolved = this.spec.fetchSpec - const resolvedURL = new URL(this.resolved) - if (this.replaceRegistryHost !== 'never' - && (this.replaceRegistryHost === 'always' - || this.replaceRegistryHost === resolvedURL.host)) { - this.resolved = new URL(resolvedURL.pathname, this.registry).href - } - - // nam is a fermented pork sausage that is good to eat - const nameat = this.spec.name ? `${this.spec.name}@` : '' - this.pkgid = opts.pkgid ? opts.pkgid : `remote:${nameat}${this.resolved}` - } - - // Don't need to cache tarball fetches in pacote, because make-fetch-happen - // will write into cacache anyway. - get [_.cacheFetches] () { - return false - } - - [_.tarballFromResolved] () { - const stream = new Minipass() - stream.hasIntegrityEmitter = true - - const fetchOpts = { - ...this.opts, - headers: this.#headers(), - spec: this.spec, - integrity: this.integrity, - algorithms: [this.pickIntegrityAlgorithm()], - } - - // eslint-disable-next-line promise/always-return - fetch(this.resolved, fetchOpts).then(res => { - res.body.on('error', - /* istanbul ignore next - exceedingly rare and hard to simulate */ - er => stream.emit('error', er) - ) - - res.body.on('integrity', i => { - this.integrity = i - stream.emit('integrity', i) - }) - - res.body.pipe(stream) - }).catch(er => stream.emit('error', er)) - - return stream - } - - #headers () { - return { - // npm will override this, but ensure that we always send *something* - 'user-agent': this.opts.userAgent || - `pacote/${pacoteVersion} node/${process.version}`, - ...(this.opts.headers || {}), - 'pacote-version': pacoteVersion, - 'pacote-req-type': 'tarball', - 'pacote-pkg-id': this.pkgid, - ...(this.integrity ? { 'pacote-integrity': String(this.integrity) } - : {}), - ...(this.opts.headers || {}), - } - } - - get types () { - return ['remote'] - } - - // getting a packument and/or manifest is the same as with a file: spec. - // unpack the tarball stream, and then read from the package.json file. - packument () { - return FileFetcher.prototype.packument.apply(this) - } - - manifest () { - return FileFetcher.prototype.manifest.apply(this) - } -} -module.exports = RemoteFetcher diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/add-git-sha.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/add-git-sha.js deleted file mode 100644 index 843fe5b600cafa..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/add-git-sha.js +++ /dev/null @@ -1,15 +0,0 @@ -// add a sha to a git remote url spec -const addGitSha = (spec, sha) => { - if (spec.hosted) { - const h = spec.hosted - const opt = { noCommittish: true } - const base = h.https && h.auth ? h.https(opt) : h.shortcut(opt) - - return `${base}#${sha}` - } else { - // don't use new URL for this, because it doesn't handle scp urls - return spec.rawSpec.replace(/#.*$/, '') + `#${sha}` - } -} - -module.exports = addGitSha diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/cache-dir.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/cache-dir.js deleted file mode 100644 index ba5683a7bb5bf3..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/cache-dir.js +++ /dev/null @@ -1,15 +0,0 @@ -const { resolve } = require('node:path') -const { tmpdir, homedir } = require('node:os') - -module.exports = (fakePlatform = false) => { - const temp = tmpdir() - const uidOrPid = process.getuid ? process.getuid() : process.pid - const home = homedir() || resolve(temp, 'npm-' + uidOrPid) - const platform = fakePlatform || process.platform - const cacheExtra = platform === 'win32' ? 'npm-cache' : '.npm' - const cacheRoot = (platform === 'win32' && process.env.LOCALAPPDATA) || home - return { - cacache: resolve(cacheRoot, cacheExtra, '_cacache'), - tufcache: resolve(cacheRoot, cacheExtra, '_tuf'), - } -} diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/is-package-bin.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/is-package-bin.js deleted file mode 100644 index 49a3f73f537ce9..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/is-package-bin.js +++ /dev/null @@ -1,25 +0,0 @@ -// Function to determine whether a path is in the package.bin set. -// Used to prevent issues when people publish a package from a -// windows machine, and then install with --no-bin-links. -// -// Note: this is not possible in remote or file fetchers, since -// we don't have the manifest until AFTER we've unpacked. But the -// main use case is registry fetching with git a distant second, -// so that's an acceptable edge case to not handle. - -const binObj = (name, bin) => - typeof bin === 'string' ? { [name]: bin } : bin - -const hasBin = (pkg, path) => { - const bin = binObj(pkg.name, pkg.bin) - const p = path.replace(/^[^\\/]*\//, '') - for (const kv of Object.entries(bin)) { - if (kv[1] === p) { - return true - } - } - return false -} - -module.exports = (pkg, path) => - pkg && pkg.bin ? hasBin(pkg, path) : false diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/npm.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/npm.js deleted file mode 100644 index a3005c255565fb..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/npm.js +++ /dev/null @@ -1,14 +0,0 @@ -// run an npm command -const spawn = require('@npmcli/promise-spawn') - -module.exports = (npmBin, npmCommand, cwd, env, extra) => { - const isJS = npmBin.endsWith('.js') - const cmd = isJS ? process.execPath : npmBin - const args = (isJS ? [npmBin] : []).concat(npmCommand) - // when installing to run the `prepare` script for a git dep, we need - // to ensure that we don't run into a cycle of checking out packages - // in temp directories. this lets us link previously-seen repos that - // are also being prepared. - - return spawn(cmd, args, { cwd, env }, extra) -} diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/protected.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/protected.js deleted file mode 100644 index e05203b481e6aa..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/protected.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - cacheFetches: Symbol.for('pacote.Fetcher._cacheFetches'), - readPackageJson: Symbol.for('package.Fetcher._readPackageJson'), - tarballFromResolved: Symbol.for('pacote.Fetcher._tarballFromResolved'), -} diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/tar-create-options.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/tar-create-options.js deleted file mode 100644 index d070f0f7ba2d4e..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/tar-create-options.js +++ /dev/null @@ -1,31 +0,0 @@ -const isPackageBin = require('./is-package-bin.js') - -const tarCreateOptions = manifest => ({ - cwd: manifest._resolved, - prefix: 'package/', - portable: true, - gzip: { - // forcing the level to 9 seems to avoid some - // platform specific optimizations that cause - // integrity mismatch errors due to differing - // end results after compression - level: 9, - }, - - // ensure that package bins are always executable - // Note that npm-packlist is already filtering out - // anything that is not a regular file, ignored by - // .npmignore or package.json "files", etc. - filter: (path, stat) => { - if (isPackageBin(manifest, path)) { - stat.mode |= 0o111 - } - return true - }, - - // Provide a specific date in the 1980s for the benefit of zip, - // which is confounded by files dated at the Unix epoch 0. - mtime: new Date('1985-10-26T08:15:00.000Z'), -}) - -module.exports = tarCreateOptions diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/trailing-slashes.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/trailing-slashes.js deleted file mode 100644 index c50cb6173b92eb..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/lib/util/trailing-slashes.js +++ /dev/null @@ -1,10 +0,0 @@ -const removeTrailingSlashes = (input) => { - // in order to avoid regexp redos detection - let output = input - while (output.endsWith('/')) { - output = output.slice(0, -1) - } - return output -} - -module.exports = removeTrailingSlashes diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/package.json b/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/package.json deleted file mode 100644 index 335c7a6c87bd3c..00000000000000 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote/package.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "name": "pacote", - "version": "20.0.0", - "description": "JavaScript package downloader", - "author": "GitHub Inc.", - "bin": { - "pacote": "bin/index.js" - }, - "license": "ISC", - "main": "lib/index.js", - "scripts": { - "test": "tap", - "snap": "tap", - "lint": "npm run eslint", - "postlint": "template-oss-check", - "lintfix": "npm run eslint -- --fix", - "posttest": "npm run lint", - "template-oss-apply": "template-oss-apply --force", - "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"" - }, - "tap": { - "timeout": 300, - "nyc-arg": [ - "--exclude", - "tap-snapshots/**" - ] - }, - "devDependencies": { - "@npmcli/arborist": "^7.1.0", - "@npmcli/eslint-config": "^5.0.0", - "@npmcli/template-oss": "4.23.3", - "hosted-git-info": "^8.0.0", - "mutate-fs": "^2.1.1", - "nock": "^13.2.4", - "npm-registry-mock": "^1.3.2", - "tap": "^16.0.1" - }, - "files": [ - "bin/", - "lib/" - ], - "keywords": [ - "packages", - "npm", - "git" - ], - "dependencies": { - "@npmcli/git": "^6.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/package-json": "^6.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "@npmcli/run-script": "^9.0.0", - "cacache": "^19.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^7.0.2", - "npm-package-arg": "^12.0.0", - "npm-packlist": "^9.0.0", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^18.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "sigstore": "^3.0.0", - "ssri": "^12.0.0", - "tar": "^6.1.11" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/npm/pacote.git" - }, - "templateOSS": { - "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", - "windowsCI": false, - "publish": "true" - } -} diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/package.json b/deps/npm/node_modules/@npmcli/metavuln-calculator/package.json index df0b8f2f4faf1c..1343b37427304e 100644 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/package.json +++ b/deps/npm/node_modules/@npmcli/metavuln-calculator/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/metavuln-calculator", - "version": "8.0.1", + "version": "9.0.0", "main": "lib/index.js", "files": [ "bin/", @@ -34,23 +34,23 @@ }, "devDependencies": { "@npmcli/eslint-config": "^5.0.0", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.4", "require-inject": "^1.4.4", "tap": "^16.0.1" }, "dependencies": { "cacache": "^19.0.0", "json-parse-even-better-errors": "^4.0.0", - "pacote": "^20.0.0", + "pacote": "^21.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.4", "publish": "true", "ciVersions": [ "16.14.0", diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/LICENSE b/deps/npm/node_modules/@sigstore/bundle/LICENSE similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/LICENSE rename to deps/npm/node_modules/@sigstore/bundle/LICENSE diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/build.js b/deps/npm/node_modules/@sigstore/bundle/dist/build.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/build.js rename to deps/npm/node_modules/@sigstore/bundle/dist/build.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/bundle.js b/deps/npm/node_modules/@sigstore/bundle/dist/bundle.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/bundle.js rename to deps/npm/node_modules/@sigstore/bundle/dist/bundle.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/error.js b/deps/npm/node_modules/@sigstore/bundle/dist/error.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/error.js rename to deps/npm/node_modules/@sigstore/bundle/dist/error.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/index.js b/deps/npm/node_modules/@sigstore/bundle/dist/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/index.js rename to deps/npm/node_modules/@sigstore/bundle/dist/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/serialized.js b/deps/npm/node_modules/@sigstore/bundle/dist/serialized.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/serialized.js rename to deps/npm/node_modules/@sigstore/bundle/dist/serialized.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/utility.js b/deps/npm/node_modules/@sigstore/bundle/dist/utility.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/utility.js rename to deps/npm/node_modules/@sigstore/bundle/dist/utility.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/validate.js b/deps/npm/node_modules/@sigstore/bundle/dist/validate.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/dist/validate.js rename to deps/npm/node_modules/@sigstore/bundle/dist/validate.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/package.json b/deps/npm/node_modules/@sigstore/bundle/package.json similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/bundle/package.json rename to deps/npm/node_modules/@sigstore/bundle/package.json diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/LICENSE b/deps/npm/node_modules/@sigstore/core/LICENSE similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/LICENSE rename to deps/npm/node_modules/@sigstore/core/LICENSE diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/error.js b/deps/npm/node_modules/@sigstore/core/dist/asn1/error.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/error.js rename to deps/npm/node_modules/@sigstore/core/dist/asn1/error.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/index.js b/deps/npm/node_modules/@sigstore/core/dist/asn1/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/index.js rename to deps/npm/node_modules/@sigstore/core/dist/asn1/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/length.js b/deps/npm/node_modules/@sigstore/core/dist/asn1/length.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/length.js rename to deps/npm/node_modules/@sigstore/core/dist/asn1/length.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/obj.js b/deps/npm/node_modules/@sigstore/core/dist/asn1/obj.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/obj.js rename to deps/npm/node_modules/@sigstore/core/dist/asn1/obj.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/parse.js b/deps/npm/node_modules/@sigstore/core/dist/asn1/parse.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/parse.js rename to deps/npm/node_modules/@sigstore/core/dist/asn1/parse.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/tag.js b/deps/npm/node_modules/@sigstore/core/dist/asn1/tag.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/asn1/tag.js rename to deps/npm/node_modules/@sigstore/core/dist/asn1/tag.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/crypto.js b/deps/npm/node_modules/@sigstore/core/dist/crypto.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/crypto.js rename to deps/npm/node_modules/@sigstore/core/dist/crypto.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/dsse.js b/deps/npm/node_modules/@sigstore/core/dist/dsse.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/dsse.js rename to deps/npm/node_modules/@sigstore/core/dist/dsse.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/encoding.js b/deps/npm/node_modules/@sigstore/core/dist/encoding.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/encoding.js rename to deps/npm/node_modules/@sigstore/core/dist/encoding.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/index.js b/deps/npm/node_modules/@sigstore/core/dist/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/index.js rename to deps/npm/node_modules/@sigstore/core/dist/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/json.js b/deps/npm/node_modules/@sigstore/core/dist/json.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/json.js rename to deps/npm/node_modules/@sigstore/core/dist/json.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/oid.js b/deps/npm/node_modules/@sigstore/core/dist/oid.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/oid.js rename to deps/npm/node_modules/@sigstore/core/dist/oid.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/pem.js b/deps/npm/node_modules/@sigstore/core/dist/pem.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/pem.js rename to deps/npm/node_modules/@sigstore/core/dist/pem.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/rfc3161/error.js b/deps/npm/node_modules/@sigstore/core/dist/rfc3161/error.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/rfc3161/error.js rename to deps/npm/node_modules/@sigstore/core/dist/rfc3161/error.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/rfc3161/index.js b/deps/npm/node_modules/@sigstore/core/dist/rfc3161/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/rfc3161/index.js rename to deps/npm/node_modules/@sigstore/core/dist/rfc3161/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/rfc3161/timestamp.js b/deps/npm/node_modules/@sigstore/core/dist/rfc3161/timestamp.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/rfc3161/timestamp.js rename to deps/npm/node_modules/@sigstore/core/dist/rfc3161/timestamp.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/rfc3161/tstinfo.js b/deps/npm/node_modules/@sigstore/core/dist/rfc3161/tstinfo.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/rfc3161/tstinfo.js rename to deps/npm/node_modules/@sigstore/core/dist/rfc3161/tstinfo.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/stream.js b/deps/npm/node_modules/@sigstore/core/dist/stream.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/stream.js rename to deps/npm/node_modules/@sigstore/core/dist/stream.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/x509/cert.js b/deps/npm/node_modules/@sigstore/core/dist/x509/cert.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/x509/cert.js rename to deps/npm/node_modules/@sigstore/core/dist/x509/cert.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/x509/ext.js b/deps/npm/node_modules/@sigstore/core/dist/x509/ext.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/x509/ext.js rename to deps/npm/node_modules/@sigstore/core/dist/x509/ext.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/x509/index.js b/deps/npm/node_modules/@sigstore/core/dist/x509/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/x509/index.js rename to deps/npm/node_modules/@sigstore/core/dist/x509/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/x509/sct.js b/deps/npm/node_modules/@sigstore/core/dist/x509/sct.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/dist/x509/sct.js rename to deps/npm/node_modules/@sigstore/core/dist/x509/sct.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/core/package.json b/deps/npm/node_modules/@sigstore/core/package.json similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/core/package.json rename to deps/npm/node_modules/@sigstore/core/package.json diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/LICENSE b/deps/npm/node_modules/@sigstore/sign/LICENSE similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/LICENSE rename to deps/npm/node_modules/@sigstore/sign/LICENSE diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/base.js b/deps/npm/node_modules/@sigstore/sign/dist/bundler/base.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/base.js rename to deps/npm/node_modules/@sigstore/sign/dist/bundler/base.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/bundle.js b/deps/npm/node_modules/@sigstore/sign/dist/bundler/bundle.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/bundle.js rename to deps/npm/node_modules/@sigstore/sign/dist/bundler/bundle.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/dsse.js b/deps/npm/node_modules/@sigstore/sign/dist/bundler/dsse.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/dsse.js rename to deps/npm/node_modules/@sigstore/sign/dist/bundler/dsse.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/index.js b/deps/npm/node_modules/@sigstore/sign/dist/bundler/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/index.js rename to deps/npm/node_modules/@sigstore/sign/dist/bundler/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/message.js b/deps/npm/node_modules/@sigstore/sign/dist/bundler/message.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/bundler/message.js rename to deps/npm/node_modules/@sigstore/sign/dist/bundler/message.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/error.js b/deps/npm/node_modules/@sigstore/sign/dist/error.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/error.js rename to deps/npm/node_modules/@sigstore/sign/dist/error.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/error.js b/deps/npm/node_modules/@sigstore/sign/dist/external/error.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/error.js rename to deps/npm/node_modules/@sigstore/sign/dist/external/error.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/fetch.js b/deps/npm/node_modules/@sigstore/sign/dist/external/fetch.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/fetch.js rename to deps/npm/node_modules/@sigstore/sign/dist/external/fetch.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/fulcio.js b/deps/npm/node_modules/@sigstore/sign/dist/external/fulcio.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/fulcio.js rename to deps/npm/node_modules/@sigstore/sign/dist/external/fulcio.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/rekor.js b/deps/npm/node_modules/@sigstore/sign/dist/external/rekor.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/rekor.js rename to deps/npm/node_modules/@sigstore/sign/dist/external/rekor.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/tsa.js b/deps/npm/node_modules/@sigstore/sign/dist/external/tsa.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/external/tsa.js rename to deps/npm/node_modules/@sigstore/sign/dist/external/tsa.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/identity/ci.js b/deps/npm/node_modules/@sigstore/sign/dist/identity/ci.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/identity/ci.js rename to deps/npm/node_modules/@sigstore/sign/dist/identity/ci.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/identity/index.js b/deps/npm/node_modules/@sigstore/sign/dist/identity/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/identity/index.js rename to deps/npm/node_modules/@sigstore/sign/dist/identity/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/identity/provider.js b/deps/npm/node_modules/@sigstore/sign/dist/identity/provider.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/identity/provider.js rename to deps/npm/node_modules/@sigstore/sign/dist/identity/provider.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/index.js b/deps/npm/node_modules/@sigstore/sign/dist/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/index.js rename to deps/npm/node_modules/@sigstore/sign/dist/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/fulcio/ca.js b/deps/npm/node_modules/@sigstore/sign/dist/signer/fulcio/ca.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/fulcio/ca.js rename to deps/npm/node_modules/@sigstore/sign/dist/signer/fulcio/ca.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/fulcio/ephemeral.js b/deps/npm/node_modules/@sigstore/sign/dist/signer/fulcio/ephemeral.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/fulcio/ephemeral.js rename to deps/npm/node_modules/@sigstore/sign/dist/signer/fulcio/ephemeral.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/fulcio/index.js b/deps/npm/node_modules/@sigstore/sign/dist/signer/fulcio/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/fulcio/index.js rename to deps/npm/node_modules/@sigstore/sign/dist/signer/fulcio/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/index.js b/deps/npm/node_modules/@sigstore/sign/dist/signer/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/index.js rename to deps/npm/node_modules/@sigstore/sign/dist/signer/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/signer.js b/deps/npm/node_modules/@sigstore/sign/dist/signer/signer.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/signer/signer.js rename to deps/npm/node_modules/@sigstore/sign/dist/signer/signer.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/types/fetch.js b/deps/npm/node_modules/@sigstore/sign/dist/types/fetch.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/types/fetch.js rename to deps/npm/node_modules/@sigstore/sign/dist/types/fetch.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/util/index.js b/deps/npm/node_modules/@sigstore/sign/dist/util/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/util/index.js rename to deps/npm/node_modules/@sigstore/sign/dist/util/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/util/oidc.js b/deps/npm/node_modules/@sigstore/sign/dist/util/oidc.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/util/oidc.js rename to deps/npm/node_modules/@sigstore/sign/dist/util/oidc.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/util/ua.js b/deps/npm/node_modules/@sigstore/sign/dist/util/ua.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/util/ua.js rename to deps/npm/node_modules/@sigstore/sign/dist/util/ua.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/index.js b/deps/npm/node_modules/@sigstore/sign/dist/witness/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/index.js rename to deps/npm/node_modules/@sigstore/sign/dist/witness/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tlog/client.js b/deps/npm/node_modules/@sigstore/sign/dist/witness/tlog/client.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tlog/client.js rename to deps/npm/node_modules/@sigstore/sign/dist/witness/tlog/client.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tlog/entry.js b/deps/npm/node_modules/@sigstore/sign/dist/witness/tlog/entry.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tlog/entry.js rename to deps/npm/node_modules/@sigstore/sign/dist/witness/tlog/entry.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tlog/index.js b/deps/npm/node_modules/@sigstore/sign/dist/witness/tlog/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tlog/index.js rename to deps/npm/node_modules/@sigstore/sign/dist/witness/tlog/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tsa/client.js b/deps/npm/node_modules/@sigstore/sign/dist/witness/tsa/client.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tsa/client.js rename to deps/npm/node_modules/@sigstore/sign/dist/witness/tsa/client.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tsa/index.js b/deps/npm/node_modules/@sigstore/sign/dist/witness/tsa/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/tsa/index.js rename to deps/npm/node_modules/@sigstore/sign/dist/witness/tsa/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/witness.js b/deps/npm/node_modules/@sigstore/sign/dist/witness/witness.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/dist/witness/witness.js rename to deps/npm/node_modules/@sigstore/sign/dist/witness/witness.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/package.json b/deps/npm/node_modules/@sigstore/sign/package.json similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/sign/package.json rename to deps/npm/node_modules/@sigstore/sign/package.json diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/bundle/dsse.js b/deps/npm/node_modules/@sigstore/verify/dist/bundle/dsse.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/bundle/dsse.js rename to deps/npm/node_modules/@sigstore/verify/dist/bundle/dsse.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/bundle/index.js b/deps/npm/node_modules/@sigstore/verify/dist/bundle/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/bundle/index.js rename to deps/npm/node_modules/@sigstore/verify/dist/bundle/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/bundle/message.js b/deps/npm/node_modules/@sigstore/verify/dist/bundle/message.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/bundle/message.js rename to deps/npm/node_modules/@sigstore/verify/dist/bundle/message.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/error.js b/deps/npm/node_modules/@sigstore/verify/dist/error.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/error.js rename to deps/npm/node_modules/@sigstore/verify/dist/error.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/index.js b/deps/npm/node_modules/@sigstore/verify/dist/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/index.js rename to deps/npm/node_modules/@sigstore/verify/dist/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/key/certificate.js b/deps/npm/node_modules/@sigstore/verify/dist/key/certificate.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/key/certificate.js rename to deps/npm/node_modules/@sigstore/verify/dist/key/certificate.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/key/index.js b/deps/npm/node_modules/@sigstore/verify/dist/key/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/key/index.js rename to deps/npm/node_modules/@sigstore/verify/dist/key/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/key/sct.js b/deps/npm/node_modules/@sigstore/verify/dist/key/sct.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/key/sct.js rename to deps/npm/node_modules/@sigstore/verify/dist/key/sct.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/policy.js b/deps/npm/node_modules/@sigstore/verify/dist/policy.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/policy.js rename to deps/npm/node_modules/@sigstore/verify/dist/policy.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/shared.types.js b/deps/npm/node_modules/@sigstore/verify/dist/shared.types.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/shared.types.js rename to deps/npm/node_modules/@sigstore/verify/dist/shared.types.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/checkpoint.js b/deps/npm/node_modules/@sigstore/verify/dist/timestamp/checkpoint.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/checkpoint.js rename to deps/npm/node_modules/@sigstore/verify/dist/timestamp/checkpoint.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/index.js b/deps/npm/node_modules/@sigstore/verify/dist/timestamp/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/index.js rename to deps/npm/node_modules/@sigstore/verify/dist/timestamp/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/merkle.js b/deps/npm/node_modules/@sigstore/verify/dist/timestamp/merkle.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/merkle.js rename to deps/npm/node_modules/@sigstore/verify/dist/timestamp/merkle.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/set.js b/deps/npm/node_modules/@sigstore/verify/dist/timestamp/set.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/set.js rename to deps/npm/node_modules/@sigstore/verify/dist/timestamp/set.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/tsa.js b/deps/npm/node_modules/@sigstore/verify/dist/timestamp/tsa.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/timestamp/tsa.js rename to deps/npm/node_modules/@sigstore/verify/dist/timestamp/tsa.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/tlog/dsse.js b/deps/npm/node_modules/@sigstore/verify/dist/tlog/dsse.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/tlog/dsse.js rename to deps/npm/node_modules/@sigstore/verify/dist/tlog/dsse.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/tlog/hashedrekord.js b/deps/npm/node_modules/@sigstore/verify/dist/tlog/hashedrekord.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/tlog/hashedrekord.js rename to deps/npm/node_modules/@sigstore/verify/dist/tlog/hashedrekord.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/tlog/index.js b/deps/npm/node_modules/@sigstore/verify/dist/tlog/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/tlog/index.js rename to deps/npm/node_modules/@sigstore/verify/dist/tlog/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/tlog/intoto.js b/deps/npm/node_modules/@sigstore/verify/dist/tlog/intoto.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/tlog/intoto.js rename to deps/npm/node_modules/@sigstore/verify/dist/tlog/intoto.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/trust/filter.js b/deps/npm/node_modules/@sigstore/verify/dist/trust/filter.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/trust/filter.js rename to deps/npm/node_modules/@sigstore/verify/dist/trust/filter.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/trust/index.js b/deps/npm/node_modules/@sigstore/verify/dist/trust/index.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/trust/index.js rename to deps/npm/node_modules/@sigstore/verify/dist/trust/index.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/trust/trust.types.js b/deps/npm/node_modules/@sigstore/verify/dist/trust/trust.types.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/trust/trust.types.js rename to deps/npm/node_modules/@sigstore/verify/dist/trust/trust.types.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/verifier.js b/deps/npm/node_modules/@sigstore/verify/dist/verifier.js similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/dist/verifier.js rename to deps/npm/node_modules/@sigstore/verify/dist/verifier.js diff --git a/deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/package.json b/deps/npm/node_modules/@sigstore/verify/package.json similarity index 100% rename from deps/npm/node_modules/sigstore/node_modules/@sigstore/verify/package.json rename to deps/npm/node_modules/@sigstore/verify/package.json diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/LICENSE b/deps/npm/node_modules/@tufjs/models/LICENSE similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/LICENSE rename to deps/npm/node_modules/@tufjs/models/LICENSE diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/base.js b/deps/npm/node_modules/@tufjs/models/dist/base.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/base.js rename to deps/npm/node_modules/@tufjs/models/dist/base.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/delegations.js b/deps/npm/node_modules/@tufjs/models/dist/delegations.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/delegations.js rename to deps/npm/node_modules/@tufjs/models/dist/delegations.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/error.js b/deps/npm/node_modules/@tufjs/models/dist/error.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/error.js rename to deps/npm/node_modules/@tufjs/models/dist/error.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/file.js b/deps/npm/node_modules/@tufjs/models/dist/file.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/file.js rename to deps/npm/node_modules/@tufjs/models/dist/file.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/index.js b/deps/npm/node_modules/@tufjs/models/dist/index.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/index.js rename to deps/npm/node_modules/@tufjs/models/dist/index.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/key.js b/deps/npm/node_modules/@tufjs/models/dist/key.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/key.js rename to deps/npm/node_modules/@tufjs/models/dist/key.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/metadata.js b/deps/npm/node_modules/@tufjs/models/dist/metadata.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/metadata.js rename to deps/npm/node_modules/@tufjs/models/dist/metadata.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/role.js b/deps/npm/node_modules/@tufjs/models/dist/role.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/role.js rename to deps/npm/node_modules/@tufjs/models/dist/role.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/root.js b/deps/npm/node_modules/@tufjs/models/dist/root.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/root.js rename to deps/npm/node_modules/@tufjs/models/dist/root.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/signature.js b/deps/npm/node_modules/@tufjs/models/dist/signature.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/signature.js rename to deps/npm/node_modules/@tufjs/models/dist/signature.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/snapshot.js b/deps/npm/node_modules/@tufjs/models/dist/snapshot.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/snapshot.js rename to deps/npm/node_modules/@tufjs/models/dist/snapshot.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/targets.js b/deps/npm/node_modules/@tufjs/models/dist/targets.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/targets.js rename to deps/npm/node_modules/@tufjs/models/dist/targets.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/timestamp.js b/deps/npm/node_modules/@tufjs/models/dist/timestamp.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/timestamp.js rename to deps/npm/node_modules/@tufjs/models/dist/timestamp.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/guard.js b/deps/npm/node_modules/@tufjs/models/dist/utils/guard.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/guard.js rename to deps/npm/node_modules/@tufjs/models/dist/utils/guard.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/index.js b/deps/npm/node_modules/@tufjs/models/dist/utils/index.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/index.js rename to deps/npm/node_modules/@tufjs/models/dist/utils/index.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/key.js b/deps/npm/node_modules/@tufjs/models/dist/utils/key.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/key.js rename to deps/npm/node_modules/@tufjs/models/dist/utils/key.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/oid.js b/deps/npm/node_modules/@tufjs/models/dist/utils/oid.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/oid.js rename to deps/npm/node_modules/@tufjs/models/dist/utils/oid.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/types.js b/deps/npm/node_modules/@tufjs/models/dist/utils/types.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/types.js rename to deps/npm/node_modules/@tufjs/models/dist/utils/types.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/verify.js b/deps/npm/node_modules/@tufjs/models/dist/utils/verify.js similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/dist/utils/verify.js rename to deps/npm/node_modules/@tufjs/models/dist/utils/verify.js diff --git a/deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/package.json b/deps/npm/node_modules/@tufjs/models/package.json similarity index 100% rename from deps/npm/node_modules/tuf-js/node_modules/@tufjs/models/package.json rename to deps/npm/node_modules/@tufjs/models/package.json diff --git a/deps/npm/node_modules/agent-base/dist/index.js b/deps/npm/node_modules/agent-base/dist/index.js index 69396356e74db7..c3c4099c73c027 100644 --- a/deps/npm/node_modules/agent-base/dist/index.js +++ b/deps/npm/node_modules/agent-base/dist/index.js @@ -133,8 +133,13 @@ class Agent extends http.Agent { .then((socket) => { this.decrementSockets(name, fakeSocket); if (socket instanceof http.Agent) { - // @ts-expect-error `addRequest()` isn't defined in `@types/node` - return socket.addRequest(req, connectOpts); + try { + // @ts-expect-error `addRequest()` isn't defined in `@types/node` + return socket.addRequest(req, connectOpts); + } + catch (err) { + return cb(err); + } } this[INTERNAL].currentSocket = socket; // @ts-expect-error `createSocket()` isn't defined in `@types/node` diff --git a/deps/npm/node_modules/agent-base/package.json b/deps/npm/node_modules/agent-base/package.json index 8e95171707fef1..175ee71fb70eae 100644 --- a/deps/npm/node_modules/agent-base/package.json +++ b/deps/npm/node_modules/agent-base/package.json @@ -1,6 +1,6 @@ { "name": "agent-base", - "version": "7.1.1", + "version": "7.1.3", "description": "Turn a function into an `http.Agent` instance", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -21,9 +21,6 @@ ], "author": "Nathan Rajlich (http://n8.io/)", "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "devDependencies": { "@types/debug": "^4.1.7", "@types/jest": "^29.5.1", @@ -34,7 +31,7 @@ "jest": "^29.5.0", "ts-jest": "^29.1.0", "typescript": "^5.0.4", - "ws": "^3.3.3", + "ws": "^5.2.4", "tsconfig": "0.0.0" }, "engines": { diff --git a/deps/npm/node_modules/aggregate-error/index.js b/deps/npm/node_modules/aggregate-error/index.js deleted file mode 100644 index ba5bf022116855..00000000000000 --- a/deps/npm/node_modules/aggregate-error/index.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; -const indentString = require('indent-string'); -const cleanStack = require('clean-stack'); - -const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); - -class AggregateError extends Error { - constructor(errors) { - if (!Array.isArray(errors)) { - throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); - } - - errors = [...errors].map(error => { - if (error instanceof Error) { - return error; - } - - if (error !== null && typeof error === 'object') { - // Handle plain error objects with message property and/or possibly other metadata - return Object.assign(new Error(error.message), error); - } - - return new Error(error); - }); - - let message = errors - .map(error => { - // The `stack` property is not standardized, so we can't assume it exists - return typeof error.stack === 'string' ? cleanInternalStack(cleanStack(error.stack)) : String(error); - }) - .join('\n'); - message = '\n' + indentString(message, 4); - super(message); - - this.name = 'AggregateError'; - - Object.defineProperty(this, '_errors', {value: errors}); - } - - * [Symbol.iterator]() { - for (const error of this._errors) { - yield error; - } - } -} - -module.exports = AggregateError; diff --git a/deps/npm/node_modules/aggregate-error/license b/deps/npm/node_modules/aggregate-error/license deleted file mode 100644 index e7af2f77107d73..00000000000000 --- a/deps/npm/node_modules/aggregate-error/license +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) Sindre Sorhus (sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/npm/node_modules/aggregate-error/package.json b/deps/npm/node_modules/aggregate-error/package.json deleted file mode 100644 index 74fcc37611e642..00000000000000 --- a/deps/npm/node_modules/aggregate-error/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "aggregate-error", - "version": "3.1.0", - "description": "Create an error from multiple errors", - "license": "MIT", - "repository": "sindresorhus/aggregate-error", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "aggregate", - "error", - "combine", - "multiple", - "many", - "collection", - "iterable", - "iterator" - ], - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "devDependencies": { - "ava": "^2.4.0", - "tsd": "^0.7.1", - "xo": "^0.25.3" - } -} diff --git a/deps/npm/node_modules/binary-extensions/index.js b/deps/npm/node_modules/binary-extensions/index.js index d46e4688671141..6c99c7eb54f175 100644 --- a/deps/npm/node_modules/binary-extensions/index.js +++ b/deps/npm/node_modules/binary-extensions/index.js @@ -1 +1,3 @@ -module.exports = require('./binary-extensions.json'); +import binaryExtensions from './binary-extensions.json' with {type: 'json'}; + +export default binaryExtensions; diff --git a/deps/npm/node_modules/binary-extensions/package.json b/deps/npm/node_modules/binary-extensions/package.json index 4710c339aeb2d5..0d309f782bb7ad 100644 --- a/deps/npm/node_modules/binary-extensions/package.json +++ b/deps/npm/node_modules/binary-extensions/package.json @@ -1,6 +1,6 @@ { "name": "binary-extensions", - "version": "2.3.0", + "version": "3.0.0", "description": "List of binary file extensions", "license": "MIT", "repository": "sindresorhus/binary-extensions", @@ -10,18 +10,23 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", + "exports": { + "types": "./index.d.ts", + "default": "./index.js" + }, "sideEffects": false, "engines": { - "node": ">=8" + "node": ">=18.20" }, "scripts": { - "test": "xo && ava && tsd" + "//test": "xo && ava && tsd", + "test": "ava && tsd" }, "files": [ "index.js", "index.d.ts", - "binary-extensions.json", - "binary-extensions.json.d.ts" + "binary-extensions.json" ], "keywords": [ "binary", @@ -33,8 +38,8 @@ "array" ], "devDependencies": { - "ava": "^1.4.1", - "tsd": "^0.7.2", - "xo": "^0.24.0" + "ava": "^6.1.2", + "tsd": "^0.31.0", + "xo": "^0.58.0" } } diff --git a/deps/npm/node_modules/cacache/node_modules/p-map/index.js b/deps/npm/node_modules/cacache/node_modules/p-map/index.js deleted file mode 100644 index 2f7d91ccca4eda..00000000000000 --- a/deps/npm/node_modules/cacache/node_modules/p-map/index.js +++ /dev/null @@ -1,269 +0,0 @@ -export default async function pMap( - iterable, - mapper, - { - concurrency = Number.POSITIVE_INFINITY, - stopOnError = true, - signal, - } = {}, -) { - return new Promise((resolve, reject_) => { - if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) { - throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`); - } - - if (typeof mapper !== 'function') { - throw new TypeError('Mapper function is required'); - } - - if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) { - throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`); - } - - const result = []; - const errors = []; - const skippedIndexesMap = new Map(); - let isRejected = false; - let isResolved = false; - let isIterableDone = false; - let resolvingCount = 0; - let currentIndex = 0; - const iterator = iterable[Symbol.iterator] === undefined ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator](); - - const reject = reason => { - isRejected = true; - isResolved = true; - reject_(reason); - }; - - if (signal) { - if (signal.aborted) { - reject(signal.reason); - } - - signal.addEventListener('abort', () => { - reject(signal.reason); - }); - } - - const next = async () => { - if (isResolved) { - return; - } - - const nextItem = await iterator.next(); - - const index = currentIndex; - currentIndex++; - - // Note: `iterator.next()` can be called many times in parallel. - // This can cause multiple calls to this `next()` function to - // receive a `nextItem` with `done === true`. - // The shutdown logic that rejects/resolves must be protected - // so it runs only one time as the `skippedIndex` logic is - // non-idempotent. - if (nextItem.done) { - isIterableDone = true; - - if (resolvingCount === 0 && !isResolved) { - if (!stopOnError && errors.length > 0) { - reject(new AggregateError(errors)); // eslint-disable-line unicorn/error-message - return; - } - - isResolved = true; - - if (skippedIndexesMap.size === 0) { - resolve(result); - return; - } - - const pureResult = []; - - // Support multiple `pMapSkip`'s. - for (const [index, value] of result.entries()) { - if (skippedIndexesMap.get(index) === pMapSkip) { - continue; - } - - pureResult.push(value); - } - - resolve(pureResult); - } - - return; - } - - resolvingCount++; - - // Intentionally detached - (async () => { - try { - const element = await nextItem.value; - - if (isResolved) { - return; - } - - const value = await mapper(element, index); - - // Use Map to stage the index of the element. - if (value === pMapSkip) { - skippedIndexesMap.set(index, value); - } - - result[index] = value; - - resolvingCount--; - await next(); - } catch (error) { - if (stopOnError) { - reject(error); - } else { - errors.push(error); - resolvingCount--; - - // In that case we can't really continue regardless of `stopOnError` state - // since an iterable is likely to continue throwing after it throws once. - // If we continue calling `next()` indefinitely we will likely end up - // in an infinite loop of failed iteration. - try { - await next(); - } catch (error) { - reject(error); - } - } - } - })(); - }; - - // Create the concurrent runners in a detached (non-awaited) - // promise. We need this so we can await the `next()` calls - // to stop creating runners before hitting the concurrency limit - // if the iterable has already been marked as done. - // NOTE: We *must* do this for async iterators otherwise we'll spin up - // infinite `next()` calls by default and never start the event loop. - (async () => { - for (let index = 0; index < concurrency; index++) { - try { - // eslint-disable-next-line no-await-in-loop - await next(); - } catch (error) { - reject(error); - break; - } - - if (isIterableDone || isRejected) { - break; - } - } - })(); - }); -} - -export function pMapIterable( - iterable, - mapper, - { - concurrency = Number.POSITIVE_INFINITY, - backpressure = concurrency, - } = {}, -) { - if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) { - throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`); - } - - if (typeof mapper !== 'function') { - throw new TypeError('Mapper function is required'); - } - - if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) { - throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`); - } - - if (!((Number.isSafeInteger(backpressure) && backpressure >= concurrency) || backpressure === Number.POSITIVE_INFINITY)) { - throw new TypeError(`Expected \`backpressure\` to be an integer from \`concurrency\` (${concurrency}) and up or \`Infinity\`, got \`${backpressure}\` (${typeof backpressure})`); - } - - return { - async * [Symbol.asyncIterator]() { - const iterator = iterable[Symbol.asyncIterator] === undefined ? iterable[Symbol.iterator]() : iterable[Symbol.asyncIterator](); - - const promises = []; - let runningMappersCount = 0; - let isDone = false; - let index = 0; - - function trySpawn() { - if (isDone || !(runningMappersCount < concurrency && promises.length < backpressure)) { - return; - } - - const promise = (async () => { - const {done, value} = await iterator.next(); - - if (done) { - return {done: true}; - } - - runningMappersCount++; - - // Spawn if still below concurrency and backpressure limit - trySpawn(); - - try { - const returnValue = await mapper(await value, index++); - - runningMappersCount--; - - if (returnValue === pMapSkip) { - const index = promises.indexOf(promise); - - if (index > 0) { - promises.splice(index, 1); - } - } - - // Spawn if still below backpressure limit and just dropped below concurrency limit - trySpawn(); - - return {done: false, value: returnValue}; - } catch (error) { - isDone = true; - return {error}; - } - })(); - - promises.push(promise); - } - - trySpawn(); - - while (promises.length > 0) { - const {error, done, value} = await promises[0]; // eslint-disable-line no-await-in-loop - - promises.shift(); - - if (error) { - throw error; - } - - if (done) { - return; - } - - // Spawn if just dropped below backpressure limit and below the concurrency limit - trySpawn(); - - if (value === pMapSkip) { - continue; - } - - yield value; - } - }, - }; -} - -export const pMapSkip = Symbol('skip'); diff --git a/deps/npm/node_modules/cacache/node_modules/p-map/license b/deps/npm/node_modules/cacache/node_modules/p-map/license deleted file mode 100644 index fa7ceba3eb4a96..00000000000000 --- a/deps/npm/node_modules/cacache/node_modules/p-map/license +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) Sindre Sorhus (https://sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/npm/node_modules/cacache/node_modules/p-map/package.json b/deps/npm/node_modules/cacache/node_modules/p-map/package.json deleted file mode 100644 index ea58f599f3a035..00000000000000 --- a/deps/npm/node_modules/cacache/node_modules/p-map/package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "p-map", - "version": "7.0.2", - "description": "Map over promises concurrently", - "license": "MIT", - "repository": "sindresorhus/p-map", - "funding": "https://github.com/sponsors/sindresorhus", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "https://sindresorhus.com" - }, - "type": "module", - "exports": { - "types": "./index.d.ts", - "default": "./index.js" - }, - "sideEffects": false, - "engines": { - "node": ">=18" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "promise", - "map", - "resolved", - "wait", - "collection", - "iterable", - "iterator", - "race", - "fulfilled", - "async", - "await", - "promises", - "concurrently", - "concurrency", - "parallel", - "bluebird" - ], - "devDependencies": { - "ava": "^5.2.0", - "chalk": "^5.3.0", - "delay": "^6.0.0", - "in-range": "^3.0.0", - "random-int": "^3.0.0", - "time-span": "^5.1.0", - "tsd": "^0.29.0", - "xo": "^0.56.0" - } -} diff --git a/deps/npm/node_modules/clean-stack/index.js b/deps/npm/node_modules/clean-stack/index.js deleted file mode 100644 index 8c1dcc4cd02a25..00000000000000 --- a/deps/npm/node_modules/clean-stack/index.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; -const os = require('os'); - -const extractPathRegex = /\s+at.*(?:\(|\s)(.*)\)?/; -const pathRegex = /^(?:(?:(?:node|(?:internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)\.js:\d+:\d+)|native)/; -const homeDir = typeof os.homedir === 'undefined' ? '' : os.homedir(); - -module.exports = (stack, options) => { - options = Object.assign({pretty: false}, options); - - return stack.replace(/\\/g, '/') - .split('\n') - .filter(line => { - const pathMatches = line.match(extractPathRegex); - if (pathMatches === null || !pathMatches[1]) { - return true; - } - - const match = pathMatches[1]; - - // Electron - if ( - match.includes('.app/Contents/Resources/electron.asar') || - match.includes('.app/Contents/Resources/default_app.asar') - ) { - return false; - } - - return !pathRegex.test(match); - }) - .filter(line => line.trim() !== '') - .map(line => { - if (options.pretty) { - return line.replace(extractPathRegex, (m, p1) => m.replace(p1, p1.replace(homeDir, '~'))); - } - - return line; - }) - .join('\n'); -}; diff --git a/deps/npm/node_modules/clean-stack/license b/deps/npm/node_modules/clean-stack/license deleted file mode 100644 index e7af2f77107d73..00000000000000 --- a/deps/npm/node_modules/clean-stack/license +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) Sindre Sorhus (sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/npm/node_modules/clean-stack/package.json b/deps/npm/node_modules/clean-stack/package.json deleted file mode 100644 index 719fdff55e7b6c..00000000000000 --- a/deps/npm/node_modules/clean-stack/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "clean-stack", - "version": "2.2.0", - "description": "Clean up error stack traces", - "license": "MIT", - "repository": "sindresorhus/clean-stack", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=6" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "clean", - "stack", - "trace", - "traces", - "error", - "err", - "electron" - ], - "devDependencies": { - "ava": "^1.4.1", - "tsd": "^0.7.2", - "xo": "^0.24.0" - }, - "browser": { - "os": false - } -} diff --git a/deps/npm/node_modules/debug/package.json b/deps/npm/node_modules/debug/package.json index 2f782eb9aef450..60dfcf57cae407 100644 --- a/deps/npm/node_modules/debug/package.json +++ b/deps/npm/node_modules/debug/package.json @@ -1,6 +1,6 @@ { "name": "debug", - "version": "4.3.7", + "version": "4.4.0", "repository": { "type": "git", "url": "git://github.com/debug-js/debug.git" @@ -56,5 +56,10 @@ "browser": "./src/browser.js", "engines": { "node": ">=6.0" + }, + "xo": { + "rules": { + "import/extensions": "off" + } } } diff --git a/deps/npm/node_modules/debug/src/browser.js b/deps/npm/node_modules/debug/src/browser.js index 8d808e5889da5f..df8e179e8b5d9b 100644 --- a/deps/npm/node_modules/debug/src/browser.js +++ b/deps/npm/node_modules/debug/src/browser.js @@ -129,6 +129,7 @@ function useColors() { // Is webkit? http://stackoverflow.com/a/16459606/376773 // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + // eslint-disable-next-line no-return-assign return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || // Is firebug? http://stackoverflow.com/a/398120/376773 (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || diff --git a/deps/npm/node_modules/debug/src/common.js b/deps/npm/node_modules/debug/src/common.js index e3291b20faa1a6..528c7ecf417cd5 100644 --- a/deps/npm/node_modules/debug/src/common.js +++ b/deps/npm/node_modules/debug/src/common.js @@ -166,24 +166,62 @@ function setup(env) { createDebug.names = []; createDebug.skips = []; - let i; - const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - const len = split.length; - - for (i = 0; i < len; i++) { - if (!split[i]) { - // ignore empty strings - continue; + const split = (typeof namespaces === 'string' ? namespaces : '') + .trim() + .replace(' ', ',') + .split(',') + .filter(Boolean); + + for (const ns of split) { + if (ns[0] === '-') { + createDebug.skips.push(ns.slice(1)); + } else { + createDebug.names.push(ns); } + } + } - namespaces = split[i].replace(/\*/g, '.*?'); - - if (namespaces[0] === '-') { - createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$')); + /** + * Checks if the given string matches a namespace template, honoring + * asterisks as wildcards. + * + * @param {String} search + * @param {String} template + * @return {Boolean} + */ + function matchesTemplate(search, template) { + let searchIndex = 0; + let templateIndex = 0; + let starIndex = -1; + let matchIndex = 0; + + while (searchIndex < search.length) { + if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) { + // Match character or proceed with wildcard + if (template[templateIndex] === '*') { + starIndex = templateIndex; + matchIndex = searchIndex; + templateIndex++; // Skip the '*' + } else { + searchIndex++; + templateIndex++; + } + } else if (starIndex !== -1) { // eslint-disable-line no-negated-condition + // Backtrack to the last '*' and try to match more characters + templateIndex = starIndex + 1; + matchIndex++; + searchIndex = matchIndex; } else { - createDebug.names.push(new RegExp('^' + namespaces + '$')); + return false; // No match } } + + // Handle trailing '*' in template + while (templateIndex < template.length && template[templateIndex] === '*') { + templateIndex++; + } + + return templateIndex === template.length; } /** @@ -194,8 +232,8 @@ function setup(env) { */ function disable() { const namespaces = [ - ...createDebug.names.map(toNamespace), - ...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace) + ...createDebug.names, + ...createDebug.skips.map(namespace => '-' + namespace) ].join(','); createDebug.enable(''); return namespaces; @@ -209,21 +247,14 @@ function setup(env) { * @api public */ function enabled(name) { - if (name[name.length - 1] === '*') { - return true; - } - - let i; - let len; - - for (i = 0, len = createDebug.skips.length; i < len; i++) { - if (createDebug.skips[i].test(name)) { + for (const skip of createDebug.skips) { + if (matchesTemplate(name, skip)) { return false; } } - for (i = 0, len = createDebug.names.length; i < len; i++) { - if (createDebug.names[i].test(name)) { + for (const ns of createDebug.names) { + if (matchesTemplate(name, ns)) { return true; } } @@ -231,19 +262,6 @@ function setup(env) { return false; } - /** - * Convert regexp to namespace - * - * @param {RegExp} regxep - * @return {String} namespace - * @api private - */ - function toNamespace(regexp) { - return regexp.toString() - .substring(2, regexp.toString().length - 2) - .replace(/\.\*\?$/, '*'); - } - /** * Coerce `val`. * diff --git a/deps/npm/node_modules/diff/CONTRIBUTING.md b/deps/npm/node_modules/diff/CONTRIBUTING.md index c974cf678e2c5e..199c556c1ffb02 100644 --- a/deps/npm/node_modules/diff/CONTRIBUTING.md +++ b/deps/npm/node_modules/diff/CONTRIBUTING.md @@ -26,11 +26,15 @@ If you notice any problems, please report them to the GitHub issue tracker at ## Releasing -A full release may be completed with the following: +A full release may be completed by first updating the `"version"` property in package.json, then running the following: ``` yarn clean -yarn grunt -yarn grunt uglify +yarn grunt release yarn publish ``` + +After releasing, remember to: +* commit the `package.json` change and push it to GitHub +* create a new version tag on GitHub +* update `diff.js` on the `gh-pages` branch to the latest built version from the `dist/` folder. diff --git a/deps/npm/node_modules/diff/dist/diff.js b/deps/npm/node_modules/diff/dist/diff.js index 76232b293d549f..2c2c33344ecd25 100644 --- a/deps/npm/node_modules/diff/dist/diff.js +++ b/deps/npm/node_modules/diff/dist/diff.js @@ -1,66 +1,95 @@ +/*! + + diff v7.0.0 + +BSD 3-Clause License + +Copyright (c) 2009-2015, Kevin Decker +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +@license +*/ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = global || self, factory(global.Diff = {})); -}(this, (function (exports) { 'use strict'; + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Diff = {})); +})(this, (function (exports) { 'use strict'; function Diff() {} Diff.prototype = { diff: function diff(oldString, newString) { var _options$timeout; - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var callback = options.callback; - if (typeof options === 'function') { callback = options; options = {}; } - - this.options = options; var self = this; - function done(value) { + value = self.postProcess(value, options); if (callback) { setTimeout(function () { - callback(undefined, value); + callback(value); }, 0); return true; } else { return value; } - } // Allow subclasses to massage the input prior to running - + } - oldString = this.castInput(oldString); - newString = this.castInput(newString); - oldString = this.removeEmpty(this.tokenize(oldString)); - newString = this.removeEmpty(this.tokenize(newString)); + // Allow subclasses to massage the input prior to running + oldString = this.castInput(oldString, options); + newString = this.castInput(newString, options); + oldString = this.removeEmpty(this.tokenize(oldString, options)); + newString = this.removeEmpty(this.tokenize(newString, options)); var newLen = newString.length, - oldLen = oldString.length; + oldLen = oldString.length; var editLength = 1; var maxEditLength = newLen + oldLen; - - if (options.maxEditLength) { + if (options.maxEditLength != null) { maxEditLength = Math.min(maxEditLength, options.maxEditLength); } - var maxExecutionTime = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : Infinity; var abortAfterTimestamp = Date.now() + maxExecutionTime; var bestPath = [{ oldPos: -1, lastComponent: undefined - }]; // Seed editLength = 0, i.e. the content starts with the same values - - var newPos = this.extractCommon(bestPath[0], newString, oldString, 0); + }]; + // Seed editLength = 0, i.e. the content starts with the same values + var newPos = this.extractCommon(bestPath[0], newString, oldString, 0, options); if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) { // Identity per the equality and tokenizer - return done([{ - value: this.join(newString), - count: newString.length - }]); - } // Once we hit the right edge of the edit graph on some diagonal k, we can + return done(buildValues(self, bestPath[0].lastComponent, newString, oldString, self.useLongestToken)); + } + + // Once we hit the right edge of the edit graph on some diagonal k, we can // definitely reach the end of the edit graph in no more than k edits, so // there's no point in considering any moves to diagonal k+1 any more (from // which we're guaranteed to need at least k+1 more edits). @@ -77,81 +106,67 @@ // where the new text simply appends d characters on the end of the // original text of length n, the true Myers algorithm will take O(n+d^2) // time while this optimization needs only O(n+d) time. - - var minDiagonalToConsider = -Infinity, - maxDiagonalToConsider = Infinity; // Main worker method. checks all permutations of a given edit length for acceptance. + maxDiagonalToConsider = Infinity; + // Main worker method. checks all permutations of a given edit length for acceptance. function execEditLength() { for (var diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) { var basePath = void 0; var removePath = bestPath[diagonalPath - 1], - addPath = bestPath[diagonalPath + 1]; - + addPath = bestPath[diagonalPath + 1]; if (removePath) { // No one else is going to attempt to use this value, clear it bestPath[diagonalPath - 1] = undefined; } - var canAdd = false; - if (addPath) { // what newPos will be after we do an insertion: var addPathNewPos = addPath.oldPos - diagonalPath; canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen; } - var canRemove = removePath && removePath.oldPos + 1 < oldLen; - if (!canAdd && !canRemove) { // If this path is a terminal then prune bestPath[diagonalPath] = undefined; continue; - } // Select the diagonal that we want to branch from. We select the prior + } + + // Select the diagonal that we want to branch from. We select the prior // path whose position in the old string is the farthest from the origin // and does not pass the bounds of the diff graph - // TODO: Remove the `+ 1` here to make behavior match Myers algorithm - // and prefer to order removals before insertions. - - - if (!canRemove || canAdd && removePath.oldPos + 1 < addPath.oldPos) { - basePath = self.addToPath(addPath, true, undefined, 0); + if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) { + basePath = self.addToPath(addPath, true, false, 0, options); } else { - basePath = self.addToPath(removePath, undefined, true, 1); + basePath = self.addToPath(removePath, false, true, 1, options); } - - newPos = self.extractCommon(basePath, newString, oldString, diagonalPath); - + newPos = self.extractCommon(basePath, newString, oldString, diagonalPath, options); if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) { // If we have hit the end of both strings, then we are done return done(buildValues(self, basePath.lastComponent, newString, oldString, self.useLongestToken)); } else { bestPath[diagonalPath] = basePath; - if (basePath.oldPos + 1 >= oldLen) { maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1); } - if (newPos + 1 >= newLen) { minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1); } } } - editLength++; - } // Performs the length of edit iteration. Is a bit fugly as this has to support the + } + + // Performs the length of edit iteration. Is a bit fugly as this has to support the // sync and async mode which is never fun. Loops over execEditLength until a value // is produced, or until the edit length exceeds options.maxEditLength (if given), // in which case it will return undefined. - - if (callback) { (function exec() { setTimeout(function () { if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) { return callback(); } - if (!execEditLength()) { exec(); } @@ -160,17 +175,15 @@ } else { while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) { var ret = execEditLength(); - if (ret) { return ret; } } } }, - addToPath: function addToPath(path, added, removed, oldPosInc) { + addToPath: function addToPath(path, added, removed, oldPosInc, options) { var last = path.lastComponent; - - if (last && last.added === added && last.removed === removed) { + if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) { return { oldPos: path.oldPos + oldPosInc, lastComponent: { @@ -192,80 +205,83 @@ }; } }, - extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { + extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath, options) { var newLen = newString.length, - oldLen = oldString.length, - oldPos = basePath.oldPos, - newPos = oldPos - diagonalPath, - commonCount = 0; - - while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + oldLen = oldString.length, + oldPos = basePath.oldPos, + newPos = oldPos - diagonalPath, + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldString[oldPos + 1], newString[newPos + 1], options)) { newPos++; oldPos++; commonCount++; + if (options.oneChangePerToken) { + basePath.lastComponent = { + count: 1, + previousComponent: basePath.lastComponent, + added: false, + removed: false + }; + } } - - if (commonCount) { + if (commonCount && !options.oneChangePerToken) { basePath.lastComponent = { count: commonCount, - previousComponent: basePath.lastComponent + previousComponent: basePath.lastComponent, + added: false, + removed: false }; } - basePath.oldPos = oldPos; return newPos; }, - equals: function equals(left, right) { - if (this.options.comparator) { - return this.options.comparator(left, right); + equals: function equals(left, right, options) { + if (options.comparator) { + return options.comparator(left, right); } else { - return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); + return left === right || options.ignoreCase && left.toLowerCase() === right.toLowerCase(); } }, removeEmpty: function removeEmpty(array) { var ret = []; - for (var i = 0; i < array.length; i++) { if (array[i]) { ret.push(array[i]); } } - return ret; }, castInput: function castInput(value) { return value; }, tokenize: function tokenize(value) { - return value.split(''); + return Array.from(value); }, join: function join(chars) { return chars.join(''); + }, + postProcess: function postProcess(changeObjects) { + return changeObjects; } }; - function buildValues(diff, lastComponent, newString, oldString, useLongestToken) { // First we convert our linked list of components in reverse order to an // array in the right order: var components = []; var nextComponent; - while (lastComponent) { components.push(lastComponent); nextComponent = lastComponent.previousComponent; delete lastComponent.previousComponent; lastComponent = nextComponent; } - components.reverse(); var componentPos = 0, - componentLen = components.length, - newPos = 0, - oldPos = 0; - + componentLen = components.length, + newPos = 0, + oldPos = 0; for (; componentPos < componentLen; componentPos++) { var component = components[componentPos]; - if (!component.removed) { if (!component.added && useLongestToken) { var value = newString.slice(newPos, newPos + component.count); @@ -277,36 +293,17 @@ } else { component.value = diff.join(newString.slice(newPos, newPos + component.count)); } + newPos += component.count; - newPos += component.count; // Common case - + // Common case if (!component.added) { oldPos += component.count; } } else { component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); - oldPos += component.count; // Reverse add and remove so removes are output first to match common convention - // The diffing algorithm is tied to add then remove output and this is the simplest - // route to get the desired output with minimal overhead. - - if (componentPos && components[componentPos - 1].added) { - var tmp = components[componentPos - 1]; - components[componentPos - 1] = components[componentPos]; - components[componentPos] = tmp; - } + oldPos += component.count; } - } // Special case handle for when one terminal is ignored (i.e. whitespace). - // For this case we merge the terminal into the prior string and drop the change. - // This is only available for string mode. - - - var finalComponent = components[componentLen - 1]; - - if (componentLen > 1 && typeof finalComponent.value === 'string' && (finalComponent.added || finalComponent.removed) && diff.equals('', finalComponent.value)) { - components[componentLen - 2].value += finalComponent.value; - components.pop(); } - return components; } @@ -315,21 +312,114 @@ return characterDiff.diff(oldStr, newStr, options); } - function generateOptions(options, defaults) { - if (typeof options === 'function') { - defaults.callback = options; - } else if (options) { - for (var name in options) { - /* istanbul ignore else */ - if (options.hasOwnProperty(name)) { - defaults[name] = options[name]; - } + function longestCommonPrefix(str1, str2) { + var i; + for (i = 0; i < str1.length && i < str2.length; i++) { + if (str1[i] != str2[i]) { + return str1.slice(0, i); } } + return str1.slice(0, i); + } + function longestCommonSuffix(str1, str2) { + var i; - return defaults; + // Unlike longestCommonPrefix, we need a special case to handle all scenarios + // where we return the empty string since str1.slice(-0) will return the + // entire string. + if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) { + return ''; + } + for (i = 0; i < str1.length && i < str2.length; i++) { + if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) { + return str1.slice(-i); + } + } + return str1.slice(-i); + } + function replacePrefix(string, oldPrefix, newPrefix) { + if (string.slice(0, oldPrefix.length) != oldPrefix) { + throw Error("string ".concat(JSON.stringify(string), " doesn't start with prefix ").concat(JSON.stringify(oldPrefix), "; this is a bug")); + } + return newPrefix + string.slice(oldPrefix.length); + } + function replaceSuffix(string, oldSuffix, newSuffix) { + if (!oldSuffix) { + return string + newSuffix; + } + if (string.slice(-oldSuffix.length) != oldSuffix) { + throw Error("string ".concat(JSON.stringify(string), " doesn't end with suffix ").concat(JSON.stringify(oldSuffix), "; this is a bug")); + } + return string.slice(0, -oldSuffix.length) + newSuffix; + } + function removePrefix(string, oldPrefix) { + return replacePrefix(string, oldPrefix, ''); + } + function removeSuffix(string, oldSuffix) { + return replaceSuffix(string, oldSuffix, ''); + } + function maximumOverlap(string1, string2) { + return string2.slice(0, overlapCount(string1, string2)); } + // Nicked from https://stackoverflow.com/a/60422853/1709587 + function overlapCount(a, b) { + // Deal with cases where the strings differ in length + var startA = 0; + if (a.length > b.length) { + startA = a.length - b.length; + } + var endB = b.length; + if (a.length < b.length) { + endB = a.length; + } + // Create a back-reference for each index + // that should be followed in case of a mismatch. + // We only need B to make these references: + var map = Array(endB); + var k = 0; // Index that lags behind j + map[0] = 0; + for (var j = 1; j < endB; j++) { + if (b[j] == b[k]) { + map[j] = map[k]; // skip over the same character (optional optimisation) + } else { + map[j] = k; + } + while (k > 0 && b[j] != b[k]) { + k = map[k]; + } + if (b[j] == b[k]) { + k++; + } + } + // Phase 2: use these references while iterating over A + k = 0; + for (var i = startA; i < a.length; i++) { + while (k > 0 && a[i] != b[k]) { + k = map[k]; + } + if (a[i] == b[k]) { + k++; + } + } + return k; + } + + /** + * Returns true if the string consistently uses Windows line endings. + */ + function hasOnlyWinLineEndings(string) { + return string.includes('\r\n') && !string.startsWith('\n') && !string.match(/[^\r]\n/); + } + + /** + * Returns true if the string consistently uses Unix line endings. + */ + function hasOnlyUnixLineEndings(string) { + return !string.includes('\r\n') && string.includes('\n'); + } + + // Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode // // Ranges and exceptions: // Latin-1 Supplement, 0080–00FF @@ -347,82 +437,330 @@ // - U+02DC ˜ ˜ Small Tilde // - U+02DD ˝ ˝ Double Acute Accent // Latin Extended Additional, 1E00–1EFF + var extendedWordChars = "a-zA-Z0-9_\\u{C0}-\\u{FF}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}"; - var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; - var reWhitespace = /\S/; + // Each token is one of the following: + // - A punctuation mark plus the surrounding whitespace + // - A word plus the surrounding whitespace + // - Pure whitespace (but only in the special case where this the entire text + // is just whitespace) + // + // We have to include surrounding whitespace in the tokens because the two + // alternative approaches produce horribly broken results: + // * If we just discard the whitespace, we can't fully reproduce the original + // text from the sequence of tokens and any attempt to render the diff will + // get the whitespace wrong. + // * If we have separate tokens for whitespace, then in a typical text every + // second token will be a single space character. But this often results in + // the optimal diff between two texts being a perverse one that preserves + // the spaces between words but deletes and reinserts actual common words. + // See https://github.com/kpdecker/jsdiff/issues/160#issuecomment-1866099640 + // for an example. + // + // Keeping the surrounding whitespace of course has implications for .equals + // and .join, not just .tokenize. + + // This regex does NOT fully implement the tokenization rules described above. + // Instead, it gives runs of whitespace their own "token". The tokenize method + // then handles stitching whitespace tokens onto adjacent word or punctuation + // tokens. + var tokenizeIncludingWhitespace = new RegExp("[".concat(extendedWordChars, "]+|\\s+|[^").concat(extendedWordChars, "]"), 'ug'); var wordDiff = new Diff(); - - wordDiff.equals = function (left, right) { - if (this.options.ignoreCase) { + wordDiff.equals = function (left, right, options) { + if (options.ignoreCase) { left = left.toLowerCase(); right = right.toLowerCase(); } - - return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); + return left.trim() === right.trim(); }; - wordDiff.tokenize = function (value) { - // All whitespace symbols except newline group into one token, each newline - in separate token - var tokens = value.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/); // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. - - for (var i = 0; i < tokens.length - 1; i++) { - // If we have an empty string in the next field and we have only word chars before and after, merge - if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { - tokens[i] += tokens[i + 2]; - tokens.splice(i + 1, 2); - i--; + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var parts; + if (options.intlSegmenter) { + if (options.intlSegmenter.resolvedOptions().granularity != 'word') { + throw new Error('The segmenter passed must have a granularity of "word"'); } + parts = Array.from(options.intlSegmenter.segment(value), function (segment) { + return segment.segment; + }); + } else { + parts = value.match(tokenizeIncludingWhitespace) || []; } - + var tokens = []; + var prevPart = null; + parts.forEach(function (part) { + if (/\s/.test(part)) { + if (prevPart == null) { + tokens.push(part); + } else { + tokens.push(tokens.pop() + part); + } + } else if (/\s/.test(prevPart)) { + if (tokens[tokens.length - 1] == prevPart) { + tokens.push(tokens.pop() + part); + } else { + tokens.push(prevPart + part); + } + } else { + tokens.push(part); + } + prevPart = part; + }); return tokens; }; - - function diffWords(oldStr, newStr, options) { - options = generateOptions(options, { - ignoreWhitespace: true + wordDiff.join = function (tokens) { + // Tokens being joined here will always have appeared consecutively in the + // same text, so we can simply strip off the leading whitespace from all the + // tokens except the first (and except any whitespace-only tokens - but such + // a token will always be the first and only token anyway) and then join them + // and the whitespace around words and punctuation will end up correct. + return tokens.map(function (token, i) { + if (i == 0) { + return token; + } else { + return token.replace(/^\s+/, ''); + } + }).join(''); + }; + wordDiff.postProcess = function (changes, options) { + if (!changes || options.oneChangePerToken) { + return changes; + } + var lastKeep = null; + // Change objects representing any insertion or deletion since the last + // "keep" change object. There can be at most one of each. + var insertion = null; + var deletion = null; + changes.forEach(function (change) { + if (change.added) { + insertion = change; + } else if (change.removed) { + deletion = change; + } else { + if (insertion || deletion) { + // May be false at start of text + dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, change); + } + lastKeep = change; + insertion = null; + deletion = null; + } }); + if (insertion || deletion) { + dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, null); + } + return changes; + }; + function diffWords(oldStr, newStr, options) { + // This option has never been documented and never will be (it's clearer to + // just call `diffWordsWithSpace` directly if you need that behavior), but + // has existed in jsdiff for a long time, so we retain support for it here + // for the sake of backwards compatibility. + if ((options === null || options === void 0 ? void 0 : options.ignoreWhitespace) != null && !options.ignoreWhitespace) { + return diffWordsWithSpace(oldStr, newStr, options); + } return wordDiff.diff(oldStr, newStr, options); } + function dedupeWhitespaceInChangeObjects(startKeep, deletion, insertion, endKeep) { + // Before returning, we tidy up the leading and trailing whitespace of the + // change objects to eliminate cases where trailing whitespace in one object + // is repeated as leading whitespace in the next. + // Below are examples of the outcomes we want here to explain the code. + // I=insert, K=keep, D=delete + // 1. diffing 'foo bar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz' + // After cleanup, we want: K:'foo ' D:'bar ' K:'baz' + // + // 2. Diffing 'foo bar baz' vs 'foo qux baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' I:' qux ' K:' baz' + // After cleanup, we want K:'foo ' D:'bar' I:'qux' K:' baz' + // + // 3. Diffing 'foo\nbar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:'\nbar ' K:' baz' + // After cleanup, we want K'foo' D:'\nbar' K:' baz' + // + // 4. Diffing 'foo baz' vs 'foo\nbar baz' + // Prior to cleanup, we have K:'foo\n' I:'\nbar ' K:' baz' + // After cleanup, we ideally want K'foo' I:'\nbar' K:' baz' + // but don't actually manage this currently (the pre-cleanup change + // objects don't contain enough information to make it possible). + // + // 5. Diffing 'foo bar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz' + // After cleanup, we want K:'foo ' D:' bar ' K:'baz' + // + // Our handling is unavoidably imperfect in the case where there's a single + // indel between keeps and the whitespace has changed. For instance, consider + // diffing 'foo\tbar\nbaz' vs 'foo baz'. Unless we create an extra change + // object to represent the insertion of the space character (which isn't even + // a token), we have no way to avoid losing information about the texts' + // original whitespace in the result we return. Still, we do our best to + // output something that will look sensible if we e.g. print it with + // insertions in green and deletions in red. + + // Between two "keep" change objects (or before the first or after the last + // change object), we can have either: + // * A "delete" followed by an "insert" + // * Just an "insert" + // * Just a "delete" + // We handle the three cases separately. + if (deletion && insertion) { + var oldWsPrefix = deletion.value.match(/^\s*/)[0]; + var oldWsSuffix = deletion.value.match(/\s*$/)[0]; + var newWsPrefix = insertion.value.match(/^\s*/)[0]; + var newWsSuffix = insertion.value.match(/\s*$/)[0]; + if (startKeep) { + var commonWsPrefix = longestCommonPrefix(oldWsPrefix, newWsPrefix); + startKeep.value = replaceSuffix(startKeep.value, newWsPrefix, commonWsPrefix); + deletion.value = removePrefix(deletion.value, commonWsPrefix); + insertion.value = removePrefix(insertion.value, commonWsPrefix); + } + if (endKeep) { + var commonWsSuffix = longestCommonSuffix(oldWsSuffix, newWsSuffix); + endKeep.value = replacePrefix(endKeep.value, newWsSuffix, commonWsSuffix); + deletion.value = removeSuffix(deletion.value, commonWsSuffix); + insertion.value = removeSuffix(insertion.value, commonWsSuffix); + } + } else if (insertion) { + // The whitespaces all reflect what was in the new text rather than + // the old, so we essentially have no information about whitespace + // insertion or deletion. We just want to dedupe the whitespace. + // We do that by having each change object keep its trailing + // whitespace and deleting duplicate leading whitespace where + // present. + if (startKeep) { + insertion.value = insertion.value.replace(/^\s*/, ''); + } + if (endKeep) { + endKeep.value = endKeep.value.replace(/^\s*/, ''); + } + // otherwise we've got a deletion and no insertion + } else if (startKeep && endKeep) { + var newWsFull = endKeep.value.match(/^\s*/)[0], + delWsStart = deletion.value.match(/^\s*/)[0], + delWsEnd = deletion.value.match(/\s*$/)[0]; + + // Any whitespace that comes straight after startKeep in both the old and + // new texts, assign to startKeep and remove from the deletion. + var newWsStart = longestCommonPrefix(newWsFull, delWsStart); + deletion.value = removePrefix(deletion.value, newWsStart); + + // Any whitespace that comes straight before endKeep in both the old and + // new texts, and hasn't already been assigned to startKeep, assign to + // endKeep and remove from the deletion. + var newWsEnd = longestCommonSuffix(removePrefix(newWsFull, newWsStart), delWsEnd); + deletion.value = removeSuffix(deletion.value, newWsEnd); + endKeep.value = replacePrefix(endKeep.value, newWsFull, newWsEnd); + + // If there's any whitespace from the new text that HASN'T already been + // assigned, assign it to the start: + startKeep.value = replaceSuffix(startKeep.value, newWsFull, newWsFull.slice(0, newWsFull.length - newWsEnd.length)); + } else if (endKeep) { + // We are at the start of the text. Preserve all the whitespace on + // endKeep, and just remove whitespace from the end of deletion to the + // extent that it overlaps with the start of endKeep. + var endKeepWsPrefix = endKeep.value.match(/^\s*/)[0]; + var deletionWsSuffix = deletion.value.match(/\s*$/)[0]; + var overlap = maximumOverlap(deletionWsSuffix, endKeepWsPrefix); + deletion.value = removeSuffix(deletion.value, overlap); + } else if (startKeep) { + // We are at the END of the text. Preserve all the whitespace on + // startKeep, and just remove whitespace from the start of deletion to + // the extent that it overlaps with the end of startKeep. + var startKeepWsSuffix = startKeep.value.match(/\s*$/)[0]; + var deletionWsPrefix = deletion.value.match(/^\s*/)[0]; + var _overlap = maximumOverlap(startKeepWsSuffix, deletionWsPrefix); + deletion.value = removePrefix(deletion.value, _overlap); + } + } + var wordWithSpaceDiff = new Diff(); + wordWithSpaceDiff.tokenize = function (value) { + // Slightly different to the tokenizeIncludingWhitespace regex used above in + // that this one treats each individual newline as a distinct tokens, rather + // than merging them into other surrounding whitespace. This was requested + // in https://github.com/kpdecker/jsdiff/issues/180 & + // https://github.com/kpdecker/jsdiff/issues/211 + var regex = new RegExp("(\\r?\\n)|[".concat(extendedWordChars, "]+|[^\\S\\n\\r]+|[^").concat(extendedWordChars, "]"), 'ug'); + return value.match(regex) || []; + }; function diffWordsWithSpace(oldStr, newStr, options) { - return wordDiff.diff(oldStr, newStr, options); + return wordWithSpaceDiff.diff(oldStr, newStr, options); } - var lineDiff = new Diff(); + function generateOptions(options, defaults) { + if (typeof options === 'function') { + defaults.callback = options; + } else if (options) { + for (var name in options) { + /* istanbul ignore else */ + if (options.hasOwnProperty(name)) { + defaults[name] = options[name]; + } + } + } + return defaults; + } - lineDiff.tokenize = function (value) { - if (this.options.stripTrailingCr) { + var lineDiff = new Diff(); + lineDiff.tokenize = function (value, options) { + if (options.stripTrailingCr) { // remove one \r before \n to match GNU diff's --strip-trailing-cr behavior value = value.replace(/\r\n/g, '\n'); } - var retLines = [], - linesAndNewlines = value.split(/(\n|\r\n)/); // Ignore the final empty token that occurs if the string ends with a new line + linesAndNewlines = value.split(/(\n|\r\n)/); + // Ignore the final empty token that occurs if the string ends with a new line if (!linesAndNewlines[linesAndNewlines.length - 1]) { linesAndNewlines.pop(); - } // Merge the content and line separators into single tokens - + } + // Merge the content and line separators into single tokens for (var i = 0; i < linesAndNewlines.length; i++) { var line = linesAndNewlines[i]; - - if (i % 2 && !this.options.newlineIsToken) { + if (i % 2 && !options.newlineIsToken) { retLines[retLines.length - 1] += line; } else { - if (this.options.ignoreWhitespace) { - line = line.trim(); - } - retLines.push(line); } } - return retLines; }; - + lineDiff.equals = function (left, right, options) { + // If we're ignoring whitespace, we need to normalise lines by stripping + // whitespace before checking equality. (This has an annoying interaction + // with newlineIsToken that requires special handling: if newlines get their + // own token, then we DON'T want to trim the *newline* tokens down to empty + // strings, since this would cause us to treat whitespace-only line content + // as equal to a separator between lines, which would be weird and + // inconsistent with the documented behavior of the options.) + if (options.ignoreWhitespace) { + if (!options.newlineIsToken || !left.includes('\n')) { + left = left.trim(); + } + if (!options.newlineIsToken || !right.includes('\n')) { + right = right.trim(); + } + } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) { + if (left.endsWith('\n')) { + left = left.slice(0, -1); + } + if (right.endsWith('\n')) { + right = right.slice(0, -1); + } + } + return Diff.prototype.equals.call(this, left, right, options); + }; function diffLines(oldStr, newStr, callback) { return lineDiff.diff(oldStr, newStr, callback); } + + // Kept for backwards compatibility. This is a rather arbitrary wrapper method + // that just calls `diffLines` with `ignoreWhitespace: true`. It's confusing to + // have two ways to do exactly the same thing in the API, so we no longer + // document this one (library users should explicitly use `diffLines` with + // `ignoreWhitespace: true` instead) but we keep it around to maintain + // compatibility with code that used old versions. function diffTrimmedLines(oldStr, newStr, callback) { var options = generateOptions(callback, { ignoreWhitespace: true @@ -431,42 +769,67 @@ } var sentenceDiff = new Diff(); - sentenceDiff.tokenize = function (value) { return value.split(/(\S.+?[.!?])(?=\s+|$)/); }; - function diffSentences(oldStr, newStr, callback) { return sentenceDiff.diff(oldStr, newStr, callback); } var cssDiff = new Diff(); - cssDiff.tokenize = function (value) { return value.split(/([{}:;,]|\s+)/); }; - function diffCss(oldStr, newStr, callback) { return cssDiff.diff(oldStr, newStr, callback); } - function _typeof(obj) { - "@babel/helpers - typeof"; - - if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { - _typeof = function (obj) { - return typeof obj; - }; - } else { - _typeof = function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }; + function ownKeys(e, r) { + var t = Object.keys(e); + if (Object.getOwnPropertySymbols) { + var o = Object.getOwnPropertySymbols(e); + r && (o = o.filter(function (r) { + return Object.getOwnPropertyDescriptor(e, r).enumerable; + })), t.push.apply(t, o); } - - return _typeof(obj); + return t; } + function _objectSpread2(e) { + for (var r = 1; r < arguments.length; r++) { + var t = null != arguments[r] ? arguments[r] : {}; + r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { + _defineProperty(e, r, t[r]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { + Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); + }); + } + return e; + } + function _toPrimitive(t, r) { + if ("object" != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != typeof i) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return ("string" === r ? String : Number)(t); + } + function _toPropertyKey(t) { + var i = _toPrimitive(t, "string"); + return "symbol" == typeof i ? i : i + ""; + } + function _typeof(o) { + "@babel/helpers - typeof"; + return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { + return typeof o; + } : function (o) { + return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; + }, _typeof(o); + } function _defineProperty(obj, key, value) { + key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, @@ -477,56 +840,17 @@ } else { obj[key] = value; } - return obj; } - - function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - if (enumerableOnly) symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - }); - keys.push.apply(keys, symbols); - } - - return keys; - } - - function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i] != null ? arguments[i] : {}; - - if (i % 2) { - ownKeys(Object(source), true).forEach(function (key) { - _defineProperty(target, key, source[key]); - }); - } else if (Object.getOwnPropertyDescriptors) { - Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); - } else { - ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - } - - return target; - } - function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } - function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } - function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); + if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } - function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); @@ -535,238 +859,263 @@ if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; } - function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } - var objectPrototypeToString = Object.prototype.toString; - var jsonDiff = new Diff(); // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a + var jsonDiff = new Diff(); + // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: - jsonDiff.useLongestToken = true; jsonDiff.tokenize = lineDiff.tokenize; - - jsonDiff.castInput = function (value) { - var _this$options = this.options, - undefinedReplacement = _this$options.undefinedReplacement, - _this$options$stringi = _this$options.stringifyReplacer, - stringifyReplacer = _this$options$stringi === void 0 ? function (k, v) { - return typeof v === 'undefined' ? undefinedReplacement : v; - } : _this$options$stringi; + jsonDiff.castInput = function (value, options) { + var undefinedReplacement = options.undefinedReplacement, + _options$stringifyRep = options.stringifyReplacer, + stringifyReplacer = _options$stringifyRep === void 0 ? function (k, v) { + return typeof v === 'undefined' ? undefinedReplacement : v; + } : _options$stringifyRep; return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); }; - - jsonDiff.equals = function (left, right) { - return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); + jsonDiff.equals = function (left, right, options) { + return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'), options); }; - function diffJson(oldObj, newObj, options) { return jsonDiff.diff(oldObj, newObj, options); - } // This function handles the presence of circular references by bailing out when encountering an - // object that is already on the "stack" of items being processed. Accepts an optional replacer + } + // This function handles the presence of circular references by bailing out when encountering an + // object that is already on the "stack" of items being processed. Accepts an optional replacer function canonicalize(obj, stack, replacementStack, replacer, key) { stack = stack || []; replacementStack = replacementStack || []; - if (replacer) { obj = replacer(key, obj); } - var i; - for (i = 0; i < stack.length; i += 1) { if (stack[i] === obj) { return replacementStack[i]; } } - var canonicalizedObj; - - if ('[object Array]' === objectPrototypeToString.call(obj)) { + if ('[object Array]' === Object.prototype.toString.call(obj)) { stack.push(obj); canonicalizedObj = new Array(obj.length); replacementStack.push(canonicalizedObj); - for (i = 0; i < obj.length; i += 1) { canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); } - stack.pop(); replacementStack.pop(); return canonicalizedObj; } - if (obj && obj.toJSON) { obj = obj.toJSON(); } - if (_typeof(obj) === 'object' && obj !== null) { stack.push(obj); canonicalizedObj = {}; replacementStack.push(canonicalizedObj); - var sortedKeys = [], - _key; - + _key; for (_key in obj) { /* istanbul ignore else */ - if (obj.hasOwnProperty(_key)) { + if (Object.prototype.hasOwnProperty.call(obj, _key)) { sortedKeys.push(_key); } } - sortedKeys.sort(); - for (i = 0; i < sortedKeys.length; i += 1) { _key = sortedKeys[i]; canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); } - stack.pop(); replacementStack.pop(); } else { canonicalizedObj = obj; } - return canonicalizedObj; } var arrayDiff = new Diff(); - arrayDiff.tokenize = function (value) { return value.slice(); }; - arrayDiff.join = arrayDiff.removeEmpty = function (value) { return value; }; - function diffArrays(oldArr, newArr, callback) { return arrayDiff.diff(oldArr, newArr, callback); } - function parsePatch(uniDiff) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], - list = [], - i = 0; + function unixToWin(patch) { + if (Array.isArray(patch)) { + return patch.map(unixToWin); + } + return _objectSpread2(_objectSpread2({}, patch), {}, { + hunks: patch.hunks.map(function (hunk) { + return _objectSpread2(_objectSpread2({}, hunk), {}, { + lines: hunk.lines.map(function (line, i) { + var _hunk$lines; + return line.startsWith('\\') || line.endsWith('\r') || (_hunk$lines = hunk.lines[i + 1]) !== null && _hunk$lines !== void 0 && _hunk$lines.startsWith('\\') ? line : line + '\r'; + }) + }); + }) + }); + } + function winToUnix(patch) { + if (Array.isArray(patch)) { + return patch.map(winToUnix); + } + return _objectSpread2(_objectSpread2({}, patch), {}, { + hunks: patch.hunks.map(function (hunk) { + return _objectSpread2(_objectSpread2({}, hunk), {}, { + lines: hunk.lines.map(function (line) { + return line.endsWith('\r') ? line.substring(0, line.length - 1) : line; + }) + }); + }) + }); + } + + /** + * Returns true if the patch consistently uses Unix line endings (or only involves one line and has + * no line endings). + */ + function isUnix(patch) { + if (!Array.isArray(patch)) { + patch = [patch]; + } + return !patch.some(function (index) { + return index.hunks.some(function (hunk) { + return hunk.lines.some(function (line) { + return !line.startsWith('\\') && line.endsWith('\r'); + }); + }); + }); + } + + /** + * Returns true if the patch uses Windows line endings and only Windows line endings. + */ + function isWin(patch) { + if (!Array.isArray(patch)) { + patch = [patch]; + } + return patch.some(function (index) { + return index.hunks.some(function (hunk) { + return hunk.lines.some(function (line) { + return line.endsWith('\r'); + }); + }); + }) && patch.every(function (index) { + return index.hunks.every(function (hunk) { + return hunk.lines.every(function (line, i) { + var _hunk$lines2; + return line.startsWith('\\') || line.endsWith('\r') || ((_hunk$lines2 = hunk.lines[i + 1]) === null || _hunk$lines2 === void 0 ? void 0 : _hunk$lines2.startsWith('\\')); + }); + }); + }); + } + function parsePatch(uniDiff) { + var diffstr = uniDiff.split(/\n/), + list = [], + i = 0; function parseIndex() { var index = {}; - list.push(index); // Parse diff metadata + list.push(index); + // Parse diff metadata while (i < diffstr.length) { - var line = diffstr[i]; // File header found, end parsing diff metadata + var line = diffstr[i]; + // File header found, end parsing diff metadata if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { break; - } // Diff index - + } + // Diff index var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); - if (header) { index.index = header[1]; } - i++; - } // Parse file headers if they are defined. Unified diff requires them, but - // there's no technical issues to have an isolated hunk without file header - + } + // Parse file headers if they are defined. Unified diff requires them, but + // there's no technical issues to have an isolated hunk without file header + parseFileHeader(index); parseFileHeader(index); - parseFileHeader(index); // Parse hunks + // Parse hunks index.hunks = []; - while (i < diffstr.length) { var _line = diffstr[i]; - - if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { + if (/^(Index:\s|diff\s|\-\-\-\s|\+\+\+\s|===================================================================)/.test(_line)) { break; } else if (/^@@/.test(_line)) { index.hunks.push(parseHunk()); - } else if (_line && options.strict) { - // Ignore unexpected content unless in strict mode + } else if (_line) { throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); } else { i++; } } - } // Parses the --- and +++ headers, if none are found, no lines - // are consumed. - + } + // Parses the --- and +++ headers, if none are found, no lines + // are consumed. function parseFileHeader(index) { - var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); - + var fileHeader = /^(---|\+\+\+)\s+(.*)\r?$/.exec(diffstr[i]); if (fileHeader) { var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; var data = fileHeader[2].split('\t', 2); var fileName = data[0].replace(/\\\\/g, '\\'); - if (/^".*"$/.test(fileName)) { fileName = fileName.substr(1, fileName.length - 2); } - index[keyPrefix + 'FileName'] = fileName; index[keyPrefix + 'Header'] = (data[1] || '').trim(); i++; } - } // Parses a hunk - // This assumes that we are at the start of a hunk. - + } + // Parses a hunk + // This assumes that we are at the start of a hunk. function parseHunk() { var chunkHeaderIndex = i, - chunkHeaderLine = diffstr[i++], - chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); + chunkHeaderLine = diffstr[i++], + chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); var hunk = { oldStart: +chunkHeader[1], oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2], newStart: +chunkHeader[3], newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4], - lines: [], - linedelimiters: [] - }; // Unified Diff Format quirk: If the chunk size is 0, + lines: [] + }; + + // Unified Diff Format quirk: If the chunk size is 0, // the first number is one lower than one would expect. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 - if (hunk.oldLines === 0) { hunk.oldStart += 1; } - if (hunk.newLines === 0) { hunk.newStart += 1; } - var addCount = 0, - removeCount = 0; - - for (; i < diffstr.length; i++) { - // Lines starting with '---' could be mistaken for the "remove line" operation - // But they could be the header for the next file. Therefore prune such cases out. - if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { - break; - } - + removeCount = 0; + for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || (_diffstr$i = diffstr[i]) !== null && _diffstr$i !== void 0 && _diffstr$i.startsWith('\\')); i++) { + var _diffstr$i; var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; - if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { hunk.lines.push(diffstr[i]); - hunk.linedelimiters.push(delimiters[i] || '\n'); - if (operation === '+') { addCount++; } else if (operation === '-') { @@ -776,37 +1125,30 @@ removeCount++; } } else { - break; + throw new Error("Hunk at line ".concat(chunkHeaderIndex + 1, " contained invalid line ").concat(diffstr[i])); } - } // Handle the empty block count case - + } + // Handle the empty block count case if (!addCount && hunk.newLines === 1) { hunk.newLines = 0; } - if (!removeCount && hunk.oldLines === 1) { hunk.oldLines = 0; - } // Perform optional sanity checking - - - if (options.strict) { - if (addCount !== hunk.newLines) { - throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } - - if (removeCount !== hunk.oldLines) { - throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } } + // Perform sanity checking + if (addCount !== hunk.newLines) { + throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + if (removeCount !== hunk.oldLines) { + throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } return hunk; } - while (i < diffstr.length) { parseIndex(); } - return list; } @@ -815,210 +1157,275 @@ // start of 2, this will iterate 2, 3, 1, 4, 0. function distanceIterator (start, minLine, maxLine) { var wantForward = true, - backwardExhausted = false, - forwardExhausted = false, - localOffset = 1; + backwardExhausted = false, + forwardExhausted = false, + localOffset = 1; return function iterator() { if (wantForward && !forwardExhausted) { if (backwardExhausted) { localOffset++; } else { wantForward = false; - } // Check if trying to fit beyond text length, and if not, check it fits - // after offset location (or desired location on first iteration) - + } + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) if (start + localOffset <= maxLine) { - return localOffset; + return start + localOffset; } - forwardExhausted = true; } - if (!backwardExhausted) { if (!forwardExhausted) { wantForward = true; - } // Check if trying to fit before text beginning, and if not, check it fits - // before offset location - + } + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location if (minLine <= start - localOffset) { - return -localOffset++; + return start - localOffset++; } - backwardExhausted = true; return iterator(); - } // We tried to fit hunk before text beginning and beyond text length, then - // hunk can't fit on the text. Return undefined + } + // We tried to fit hunk before text beginning and beyond text length, then + // hunk can't fit on the text. Return undefined }; } function applyPatch(source, uniDiff) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - if (typeof uniDiff === 'string') { uniDiff = parsePatch(uniDiff); } - if (Array.isArray(uniDiff)) { if (uniDiff.length > 1) { throw new Error('applyPatch only works with a single input.'); } - uniDiff = uniDiff[0]; - } // Apply the diff to the input - - - var lines = source.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], - hunks = uniDiff.hunks, - compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) { - return line === patchContent; - }, - errorCount = 0, - fuzzFactor = options.fuzzFactor || 0, - minLine = 0, - offset = 0, - removeEOFNL, - addEOFNL; - /** - * Checks if the hunk exactly fits on the provided location - */ + } + if (options.autoConvertLineEndings || options.autoConvertLineEndings == null) { + if (hasOnlyWinLineEndings(source) && isUnix(uniDiff)) { + uniDiff = unixToWin(uniDiff); + } else if (hasOnlyUnixLineEndings(source) && isWin(uniDiff)) { + uniDiff = winToUnix(uniDiff); + } + } + // Apply the diff to the input + var lines = source.split('\n'), + hunks = uniDiff.hunks, + compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) { + return line === patchContent; + }, + fuzzFactor = options.fuzzFactor || 0, + minLine = 0; + if (fuzzFactor < 0 || !Number.isInteger(fuzzFactor)) { + throw new Error('fuzzFactor must be a non-negative integer'); + } - function hunkFits(hunk, toPos) { - for (var j = 0; j < hunk.lines.length; j++) { - var line = hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line; + // Special case for empty patch. + if (!hunks.length) { + return source; + } - if (operation === ' ' || operation === '-') { - // Context sanity check - if (!compareLine(toPos + 1, lines[toPos], operation, content)) { - errorCount++; + // Before anything else, handle EOFNL insertion/removal. If the patch tells us to make a change + // to the EOFNL that is redundant/impossible - i.e. to remove a newline that's not there, or add a + // newline that already exists - then we either return false and fail to apply the patch (if + // fuzzFactor is 0) or simply ignore the problem and do nothing (if fuzzFactor is >0). + // If we do need to remove/add a newline at EOF, this will always be in the final hunk: + var prevLine = '', + removeEOFNL = false, + addEOFNL = false; + for (var i = 0; i < hunks[hunks.length - 1].lines.length; i++) { + var line = hunks[hunks.length - 1].lines[i]; + if (line[0] == '\\') { + if (prevLine[0] == '+') { + removeEOFNL = true; + } else if (prevLine[0] == '-') { + addEOFNL = true; + } + } + prevLine = line; + } + if (removeEOFNL) { + if (addEOFNL) { + // This means the final line gets changed but doesn't have a trailing newline in either the + // original or patched version. In that case, we do nothing if fuzzFactor > 0, and if + // fuzzFactor is 0, we simply validate that the source file has no trailing newline. + if (!fuzzFactor && lines[lines.length - 1] == '') { + return false; + } + } else if (lines[lines.length - 1] == '') { + lines.pop(); + } else if (!fuzzFactor) { + return false; + } + } else if (addEOFNL) { + if (lines[lines.length - 1] != '') { + lines.push(''); + } else if (!fuzzFactor) { + return false; + } + } - if (errorCount > fuzzFactor) { - return false; + /** + * Checks if the hunk can be made to fit at the provided location with at most `maxErrors` + * insertions, substitutions, or deletions, while ensuring also that: + * - lines deleted in the hunk match exactly, and + * - wherever an insertion operation or block of insertion operations appears in the hunk, the + * immediately preceding and following lines of context match exactly + * + * `toPos` should be set such that lines[toPos] is meant to match hunkLines[0]. + * + * If the hunk can be applied, returns an object with properties `oldLineLastI` and + * `replacementLines`. Otherwise, returns null. + */ + function applyHunk(hunkLines, toPos, maxErrors) { + var hunkLinesI = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; + var lastContextLineMatched = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; + var patchedLines = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : []; + var patchedLinesLength = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0; + var nConsecutiveOldContextLines = 0; + var nextContextLineMustMatch = false; + for (; hunkLinesI < hunkLines.length; hunkLinesI++) { + var hunkLine = hunkLines[hunkLinesI], + operation = hunkLine.length > 0 ? hunkLine[0] : ' ', + content = hunkLine.length > 0 ? hunkLine.substr(1) : hunkLine; + if (operation === '-') { + if (compareLine(toPos + 1, lines[toPos], operation, content)) { + toPos++; + nConsecutiveOldContextLines = 0; + } else { + if (!maxErrors || lines[toPos] == null) { + return null; } + patchedLines[patchedLinesLength] = lines[toPos]; + return applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1); + } + } + if (operation === '+') { + if (!lastContextLineMatched) { + return null; } + patchedLines[patchedLinesLength] = content; + patchedLinesLength++; + nConsecutiveOldContextLines = 0; + nextContextLineMustMatch = true; + } + if (operation === ' ') { + nConsecutiveOldContextLines++; + patchedLines[patchedLinesLength] = lines[toPos]; + if (compareLine(toPos + 1, lines[toPos], operation, content)) { + patchedLinesLength++; + lastContextLineMatched = true; + nextContextLineMustMatch = false; + toPos++; + } else { + if (nextContextLineMustMatch || !maxErrors) { + return null; + } - toPos++; + // Consider 3 possibilities in sequence: + // 1. lines contains a *substitution* not included in the patch context, or + // 2. lines contains an *insertion* not included in the patch context, or + // 3. lines contains a *deletion* not included in the patch context + // The first two options are of course only possible if the line from lines is non-null - + // i.e. only option 3 is possible if we've overrun the end of the old file. + return lines[toPos] && (applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength + 1) || applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1)) || applyHunk(hunkLines, toPos, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength); + } } } - return true; - } // Search best fit offsets for each hunk based on the previous ones - - - for (var i = 0; i < hunks.length; i++) { - var hunk = hunks[i], - maxLine = lines.length - hunk.oldLines, - localOffset = 0, - toPos = offset + hunk.oldStart - 1; - var iterator = distanceIterator(toPos, minLine, maxLine); + // Before returning, trim any unmodified context lines off the end of patchedLines and reduce + // toPos (and thus oldLineLastI) accordingly. This allows later hunks to be applied to a region + // that starts in this hunk's trailing context. + patchedLinesLength -= nConsecutiveOldContextLines; + toPos -= nConsecutiveOldContextLines; + patchedLines.length = patchedLinesLength; + return { + patchedLines: patchedLines, + oldLineLastI: toPos - 1 + }; + } + var resultLines = []; - for (; localOffset !== undefined; localOffset = iterator()) { - if (hunkFits(hunk, toPos + localOffset)) { - hunk.offset = offset += localOffset; + // Search best fit offsets for each hunk based on the previous ones + var prevHunkOffset = 0; + for (var _i = 0; _i < hunks.length; _i++) { + var hunk = hunks[_i]; + var hunkResult = void 0; + var maxLine = lines.length - hunk.oldLines + fuzzFactor; + var toPos = void 0; + for (var maxErrors = 0; maxErrors <= fuzzFactor; maxErrors++) { + toPos = hunk.oldStart + prevHunkOffset - 1; + var iterator = distanceIterator(toPos, minLine, maxLine); + for (; toPos !== undefined; toPos = iterator()) { + hunkResult = applyHunk(hunk.lines, toPos, maxErrors); + if (hunkResult) { + break; + } + } + if (hunkResult) { break; } } - - if (localOffset === undefined) { + if (!hunkResult) { return false; - } // Set lower text limit to end of the current hunk, so next ones don't try - // to fit over already patched text - - - minLine = hunk.offset + hunk.oldStart + hunk.oldLines; - } // Apply patch hunks - - - var diffOffset = 0; - - for (var _i = 0; _i < hunks.length; _i++) { - var _hunk = hunks[_i], - _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; - - diffOffset += _hunk.newLines - _hunk.oldLines; + } - for (var j = 0; j < _hunk.lines.length; j++) { - var line = _hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line, - delimiter = _hunk.linedelimiters && _hunk.linedelimiters[j] || '\n'; + // Copy everything from the end of where we applied the last hunk to the start of this hunk + for (var _i2 = minLine; _i2 < toPos; _i2++) { + resultLines.push(lines[_i2]); + } - if (operation === ' ') { - _toPos++; - } else if (operation === '-') { - lines.splice(_toPos, 1); - delimiters.splice(_toPos, 1); - /* istanbul ignore else */ - } else if (operation === '+') { - lines.splice(_toPos, 0, content); - delimiters.splice(_toPos, 0, delimiter); - _toPos++; - } else if (operation === '\\') { - var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; - - if (previousOperation === '+') { - removeEOFNL = true; - } else if (previousOperation === '-') { - addEOFNL = true; - } - } + // Add the lines produced by applying the hunk: + for (var _i3 = 0; _i3 < hunkResult.patchedLines.length; _i3++) { + var _line = hunkResult.patchedLines[_i3]; + resultLines.push(_line); } - } // Handle EOFNL insertion/removal + // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text + minLine = hunkResult.oldLineLastI + 1; - if (removeEOFNL) { - while (!lines[lines.length - 1]) { - lines.pop(); - delimiters.pop(); - } - } else if (addEOFNL) { - lines.push(''); - delimiters.push('\n'); + // Note the offset between where the patch said the hunk should've applied and where we + // applied it, so we can adjust future hunks accordingly: + prevHunkOffset = toPos + 1 - hunk.oldStart; } - for (var _k = 0; _k < lines.length - 1; _k++) { - lines[_k] = lines[_k] + delimiters[_k]; + // Copy over the rest of the lines from the old text + for (var _i4 = minLine; _i4 < lines.length; _i4++) { + resultLines.push(lines[_i4]); } + return resultLines.join('\n'); + } - return lines.join(''); - } // Wrapper that supports multiple file patches via callbacks. - + // Wrapper that supports multiple file patches via callbacks. function applyPatches(uniDiff, options) { if (typeof uniDiff === 'string') { uniDiff = parsePatch(uniDiff); } - var currentIndex = 0; - function processIndex() { var index = uniDiff[currentIndex++]; - if (!index) { return options.complete(); } - options.loadFile(index, function (err, data) { if (err) { return options.complete(err); } - var updatedContent = applyPatch(data, index, options); options.patched(index, updatedContent, function (err) { if (err) { return options.complete(err); } - processIndex(); }); }); } - processIndex(); } @@ -1026,206 +1433,238 @@ if (!options) { options = {}; } - + if (typeof options === 'function') { + options = { + callback: options + }; + } if (typeof options.context === 'undefined') { options.context = 4; } - - var diff = diffLines(oldStr, newStr, options); - - if (!diff) { - return; + if (options.newlineIsToken) { + throw new Error('newlineIsToken may not be used with patch-generation functions, only with diffing functions'); } - - diff.push({ - value: '', - lines: [] - }); // Append an empty value to make cleanup easier - - function contextLines(lines) { - return lines.map(function (entry) { - return ' ' + entry; - }); + if (!options.callback) { + return diffLinesResultToPatch(diffLines(oldStr, newStr, options)); + } else { + var _options = options, + _callback = _options.callback; + diffLines(oldStr, newStr, _objectSpread2(_objectSpread2({}, options), {}, { + callback: function callback(diff) { + var patch = diffLinesResultToPatch(diff); + _callback(patch); + } + })); } + function diffLinesResultToPatch(diff) { + // STEP 1: Build up the patch with no "\ No newline at end of file" lines and with the arrays + // of lines containing trailing newline characters. We'll tidy up later... - var hunks = []; - var oldRangeStart = 0, + if (!diff) { + return; + } + diff.push({ + value: '', + lines: [] + }); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function (entry) { + return ' ' + entry; + }); + } + var hunks = []; + var oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1; - - var _loop = function _loop(i) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - var _curRange; - - // If we have previous context, start with that - if (!oldRangeStart) { - var prev = diff[i - 1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; + var _loop = function _loop() { + var current = diff[i], + lines = current.lines || splitLines(current.value); + current.lines = lines; + if (current.added || current.removed) { + var _curRange; + // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + if (prev) { + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } } - } // Output our changes - - (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) { - return (current.added ? '+' : '-') + entry; - }))); // Track the updated file position + // Output our changes + (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) { + return (current.added ? '+' : '-') + entry; + }))); - - if (current.added) { - newLine += lines.length; + // Track the updated file position + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= options.context * 2 && i < diff.length - 2) { + var _curRange2; + // Overlapping + (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines))); + } else { + var _curRange3; + // end the range and output + var contextSize = Math.min(lines.length, options.context); + (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize)))); + var _hunk = { + oldStart: oldRangeStart, + oldLines: oldLine - oldRangeStart + contextSize, + newStart: newRangeStart, + newLines: newLine - newRangeStart + contextSize, + lines: curRange + }; + hunks.push(_hunk); + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; + } + } oldLine += lines.length; + newLine += lines.length; } - } else { - // Identical context lines. Track line changes - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= options.context * 2 && i < diff.length - 2) { - var _curRange2; - - // Overlapping - (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines))); - } else { - var _curRange3; - - // end the range and output - var contextSize = Math.min(lines.length, options.context); - - (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize)))); - - var hunk = { - oldStart: oldRangeStart, - oldLines: oldLine - oldRangeStart + contextSize, - newStart: newRangeStart, - newLines: newLine - newRangeStart + contextSize, - lines: curRange - }; - - if (i >= diff.length - 2 && lines.length <= options.context) { - // EOF is inside this hunk - var oldEOFNewline = /\n$/.test(oldStr); - var newEOFNewline = /\n$/.test(newStr); - var noNlBeforeAdds = lines.length == 0 && curRange.length > hunk.oldLines; - - if (!oldEOFNewline && noNlBeforeAdds && oldStr.length > 0) { - // special case: old has no eol and no trailing context; no-nl can end up before adds - // however, if the old file is empty, do not output the no-nl line - curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); - } - - if (!oldEOFNewline && !noNlBeforeAdds || !newEOFNewline) { - curRange.push('\\ No newline at end of file'); - } - } + }; + for (var i = 0; i < diff.length; i++) { + _loop(); + } - hunks.push(hunk); - oldRangeStart = 0; - newRangeStart = 0; - curRange = []; + // Step 2: eliminate the trailing `\n` from each line of each hunk, and, where needed, add + // "\ No newline at end of file". + for (var _i = 0, _hunks = hunks; _i < _hunks.length; _i++) { + var hunk = _hunks[_i]; + for (var _i2 = 0; _i2 < hunk.lines.length; _i2++) { + if (hunk.lines[_i2].endsWith('\n')) { + hunk.lines[_i2] = hunk.lines[_i2].slice(0, -1); + } else { + hunk.lines.splice(_i2 + 1, 0, '\\ No newline at end of file'); + _i2++; // Skip the line we just added, then continue iterating } } - - oldLine += lines.length; - newLine += lines.length; } - }; - - for (var i = 0; i < diff.length; i++) { - _loop(i); + return { + oldFileName: oldFileName, + newFileName: newFileName, + oldHeader: oldHeader, + newHeader: newHeader, + hunks: hunks + }; } - - return { - oldFileName: oldFileName, - newFileName: newFileName, - oldHeader: oldHeader, - newHeader: newHeader, - hunks: hunks - }; } function formatPatch(diff) { if (Array.isArray(diff)) { return diff.map(formatPatch).join('\n'); } - var ret = []; - if (diff.oldFileName == diff.newFileName) { ret.push('Index: ' + diff.oldFileName); } - ret.push('==================================================================='); ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); - for (var i = 0; i < diff.hunks.length; i++) { - var hunk = diff.hunks[i]; // Unified Diff Format quirk: If the chunk size is 0, + var hunk = diff.hunks[i]; + // Unified Diff Format quirk: If the chunk size is 0, // the first number is one lower than one would expect. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 - if (hunk.oldLines === 0) { hunk.oldStart -= 1; } - if (hunk.newLines === 0) { hunk.newStart -= 1; } - ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); ret.push.apply(ret, hunk.lines); } - return ret.join('\n') + '\n'; } function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { - return formatPatch(structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)); + var _options2; + if (typeof options === 'function') { + options = { + callback: options + }; + } + if (!((_options2 = options) !== null && _options2 !== void 0 && _options2.callback)) { + var patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); + if (!patchObj) { + return; + } + return formatPatch(patchObj); + } else { + var _options3 = options, + _callback2 = _options3.callback; + structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, _objectSpread2(_objectSpread2({}, options), {}, { + callback: function callback(patchObj) { + if (!patchObj) { + _callback2(); + } else { + _callback2(formatPatch(patchObj)); + } + } + })); + } } function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); } + /** + * Split `text` into an array of lines, including the trailing newline character (where present) + */ + function splitLines(text) { + var hasTrailingNl = text.endsWith('\n'); + var result = text.split('\n').map(function (line) { + return line + '\n'; + }); + if (hasTrailingNl) { + result.pop(); + } else { + result.push(result.pop().slice(0, -1)); + } + return result; + } + function arrayEqual(a, b) { if (a.length !== b.length) { return false; } - return arrayStartsWith(a, b); } function arrayStartsWith(array, start) { if (start.length > array.length) { return false; } - for (var i = 0; i < start.length; i++) { if (start[i] !== array[i]) { return false; } } - return true; } function calcLineCount(hunk) { var _calcOldNewLineCount = calcOldNewLineCount(hunk.lines), - oldLines = _calcOldNewLineCount.oldLines, - newLines = _calcOldNewLineCount.newLines; - + oldLines = _calcOldNewLineCount.oldLines, + newLines = _calcOldNewLineCount.newLines; if (oldLines !== undefined) { hunk.oldLines = oldLines; } else { delete hunk.oldLines; } - if (newLines !== undefined) { hunk.newLines = newLines; } else { @@ -1235,14 +1674,14 @@ function merge(mine, theirs, base) { mine = loadPatch(mine, base); theirs = loadPatch(theirs, base); - var ret = {}; // For index we just let it pass through as it doesn't have any necessary meaning. + var ret = {}; + + // For index we just let it pass through as it doesn't have any necessary meaning. // Leaving sanity checks on this to the API consumer that may know more about the // meaning in their own context. - if (mine.index || theirs.index) { ret.index = mine.index || theirs.index; } - if (mine.newFileName || theirs.newFileName) { if (!fileNameChanged(mine)) { // No header or no change in ours, use theirs (and ours if theirs does not exist) @@ -1264,21 +1703,18 @@ ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); } } - ret.hunks = []; var mineIndex = 0, - theirsIndex = 0, - mineOffset = 0, - theirsOffset = 0; - + theirsIndex = 0, + mineOffset = 0, + theirsOffset = 0; while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { var mineCurrent = mine.hunks[mineIndex] || { - oldStart: Infinity - }, - theirsCurrent = theirs.hunks[theirsIndex] || { - oldStart: Infinity - }; - + oldStart: Infinity + }, + theirsCurrent = theirs.hunks[theirsIndex] || { + oldStart: Infinity + }; if (hunkBefore(mineCurrent, theirsCurrent)) { // This patch does not overlap with any of the others, yay. ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); @@ -1304,30 +1740,23 @@ ret.hunks.push(mergedHunk); } } - return ret; } - function loadPatch(param, base) { if (typeof param === 'string') { if (/^@@/m.test(param) || /^Index:/m.test(param)) { return parsePatch(param)[0]; } - if (!base) { throw new Error('Must provide a base reference or pass in a patch'); } - return structuredPatch(undefined, undefined, base, param); } - return param; } - function fileNameChanged(patch) { return patch.newFileName && patch.newFileName !== patch.oldFileName; } - function selectField(index, mine, theirs) { if (mine === theirs) { return mine; @@ -1339,11 +1768,9 @@ }; } } - function hunkBefore(test, check) { return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; } - function cloneHunk(hunk, offset) { return { oldStart: hunk.oldStart, @@ -1353,39 +1780,37 @@ lines: hunk.lines }; } - function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { // This will generally result in a conflicted hunk, but there are cases where the context // is the only overlap where we can successfully merge the content here. var mine = { - offset: mineOffset, - lines: mineLines, - index: 0 - }, - their = { - offset: theirOffset, - lines: theirLines, - index: 0 - }; // Handle any leading content + offset: mineOffset, + lines: mineLines, + index: 0 + }, + their = { + offset: theirOffset, + lines: theirLines, + index: 0 + }; + // Handle any leading content insertLeading(hunk, mine, their); - insertLeading(hunk, their, mine); // Now in the overlap content. Scan through and select the best changes from each. + insertLeading(hunk, their, mine); + // Now in the overlap content. Scan through and select the best changes from each. while (mine.index < mine.lines.length && their.index < their.lines.length) { var mineCurrent = mine.lines[mine.index], - theirCurrent = their.lines[their.index]; - + theirCurrent = their.lines[their.index]; if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { // Both modified ... mutualChange(hunk, mine, their); } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { var _hunk$lines; - // Mine inserted (_hunk$lines = hunk.lines).push.apply(_hunk$lines, _toConsumableArray(collectChange(mine))); } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { var _hunk$lines2; - // Theirs inserted (_hunk$lines2 = hunk.lines).push.apply(_hunk$lines2, _toConsumableArray(collectChange(their))); } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') { @@ -1403,57 +1828,44 @@ // Context mismatch conflict(hunk, collectChange(mine), collectChange(their)); } - } // Now push anything that may be remaining - + } + // Now push anything that may be remaining insertTrailing(hunk, mine); insertTrailing(hunk, their); calcLineCount(hunk); } - function mutualChange(hunk, mine, their) { var myChanges = collectChange(mine), - theirChanges = collectChange(their); - + theirChanges = collectChange(their); if (allRemoves(myChanges) && allRemoves(theirChanges)) { // Special case for remove changes that are supersets of one another if (arrayStartsWith(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { var _hunk$lines3; - (_hunk$lines3 = hunk.lines).push.apply(_hunk$lines3, _toConsumableArray(myChanges)); - return; } else if (arrayStartsWith(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { var _hunk$lines4; - (_hunk$lines4 = hunk.lines).push.apply(_hunk$lines4, _toConsumableArray(theirChanges)); - return; } } else if (arrayEqual(myChanges, theirChanges)) { var _hunk$lines5; - (_hunk$lines5 = hunk.lines).push.apply(_hunk$lines5, _toConsumableArray(myChanges)); - return; } - conflict(hunk, myChanges, theirChanges); } - function removal(hunk, mine, their, swap) { var myChanges = collectChange(mine), - theirChanges = collectContext(their, myChanges); - + theirChanges = collectContext(their, myChanges); if (theirChanges.merged) { var _hunk$lines6; - (_hunk$lines6 = hunk.lines).push.apply(_hunk$lines6, _toConsumableArray(theirChanges.merged)); } else { conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); } } - function conflict(hunk, mine, their) { hunk.conflict = true; hunk.lines.push({ @@ -1462,7 +1874,6 @@ theirs: their }); } - function insertLeading(hunk, insert, their) { while (insert.offset < their.offset && insert.index < insert.lines.length) { var line = insert.lines[insert.index++]; @@ -1470,25 +1881,22 @@ insert.offset++; } } - function insertTrailing(hunk, insert) { while (insert.index < insert.lines.length) { var line = insert.lines[insert.index++]; hunk.lines.push(line); } } - function collectChange(state) { var ret = [], - operation = state.lines[state.index][0]; - + operation = state.lines[state.index][0]; while (state.index < state.lines.length) { - var line = state.lines[state.index]; // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. + var line = state.lines[state.index]; + // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. if (operation === '-' && line[0] === '+') { operation = '+'; } - if (operation === line[0]) { ret.push(line); state.index++; @@ -1496,39 +1904,35 @@ break; } } - return ret; } - function collectContext(state, matchChanges) { var changes = [], - merged = [], - matchIndex = 0, - contextChanges = false, - conflicted = false; - + merged = [], + matchIndex = 0, + contextChanges = false, + conflicted = false; while (matchIndex < matchChanges.length && state.index < state.lines.length) { var change = state.lines[state.index], - match = matchChanges[matchIndex]; // Once we've hit our add, then we are done + match = matchChanges[matchIndex]; + // Once we've hit our add, then we are done if (match[0] === '+') { break; } - contextChanges = contextChanges || change[0] !== ' '; merged.push(match); - matchIndex++; // Consume any additions in the other block as a conflict to attempt - // to pull in the remaining context after this + matchIndex++; + // Consume any additions in the other block as a conflict to attempt + // to pull in the remaining context after this if (change[0] === '+') { conflicted = true; - while (change[0] === '+') { changes.push(change); change = state.lines[++state.index]; } } - if (match.substr(1) === change.substr(1)) { changes.push(change); state.index++; @@ -1536,44 +1940,35 @@ conflicted = true; } } - if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { conflicted = true; } - if (conflicted) { return changes; } - while (matchIndex < matchChanges.length) { merged.push(matchChanges[matchIndex++]); } - return { merged: merged, changes: changes }; } - function allRemoves(changes) { return changes.reduce(function (prev, change) { return prev && change[0] === '-'; }, true); } - function skipRemoveSuperset(state, removeChanges, delta) { for (var i = 0; i < delta; i++) { var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); - if (state.lines[state.index + i] !== ' ' + changeContent) { return false; } } - state.index += delta; return true; } - function calcOldNewLineCount(lines) { var oldLines = 0; var newLines = 0; @@ -1581,7 +1976,6 @@ if (typeof line !== 'string') { var myCount = calcOldNewLineCount(line.mine); var theirCount = calcOldNewLineCount(line.theirs); - if (oldLines !== undefined) { if (myCount.oldLines === theirCount.oldLines) { oldLines += myCount.oldLines; @@ -1589,7 +1983,6 @@ oldLines = undefined; } } - if (newLines !== undefined) { if (myCount.newLines === theirCount.newLines) { newLines += myCount.newLines; @@ -1601,7 +1994,6 @@ if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { newLines++; } - if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { oldLines++; } @@ -1617,7 +2009,6 @@ if (Array.isArray(structuredPatch)) { return structuredPatch.map(reversePatch).reverse(); } - return _objectSpread2(_objectSpread2({}, structuredPatch), {}, { oldFileName: structuredPatch.newFileName, oldHeader: structuredPatch.newHeader, @@ -1629,16 +2020,13 @@ oldStart: hunk.newStart, newLines: hunk.oldLines, newStart: hunk.oldStart, - linedelimiters: hunk.linedelimiters, lines: hunk.lines.map(function (l) { if (l.startsWith('-')) { return "+".concat(l.slice(1)); } - if (l.startsWith('+')) { return "-".concat(l.slice(1)); } - return l; }) }; @@ -1649,12 +2037,10 @@ // See: http://code.google.com/p/google-diff-match-patch/wiki/API function convertChangesToDMP(changes) { var ret = [], - change, - operation; - + change, + operation; for (var i = 0; i < changes.length; i++) { change = changes[i]; - if (change.added) { operation = 1; } else if (change.removed) { @@ -1662,37 +2048,29 @@ } else { operation = 0; } - ret.push([operation, change.value]); } - return ret; } function convertChangesToXML(changes) { var ret = []; - for (var i = 0; i < changes.length; i++) { var change = changes[i]; - if (change.added) { ret.push(''); } else if (change.removed) { ret.push(''); } - ret.push(escapeHTML(change.value)); - if (change.added) { ret.push(''); } else if (change.removed) { ret.push(''); } } - return ret.join(''); } - function escapeHTML(s) { var n = s; n = n.replace(/&/g, '&'); @@ -1725,6 +2103,4 @@ exports.reversePatch = reversePatch; exports.structuredPatch = structuredPatch; - Object.defineProperty(exports, '__esModule', { value: true }); - -}))); +})); diff --git a/deps/npm/node_modules/diff/dist/diff.min.js b/deps/npm/node_modules/diff/dist/diff.min.js index 078bcc5c2e6a73..4d96b763e537a1 100644 --- a/deps/npm/node_modules/diff/dist/diff.min.js +++ b/deps/npm/node_modules/diff/dist/diff.min.js @@ -1 +1,37 @@ -!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e=e||self).Diff={})}(this,function(e){"use strict";function t(){}t.prototype={diff:function(s,a,e){var n,t=2=c&&f<=v+1)return d([{value:this.join(a),count:a.length}]);var m=-1/0,g=1/0;function w(){for(var e=Math.max(m,-p);e<=Math.min(g,p);e+=2){var n=void 0,t=h[e-1],r=h[e+1];t&&(h[e-1]=void 0);var i,o=!1;r&&(i=r.oldPos-e,o=r&&0<=i&&i=c&&f<=v+1)return d(function(e,n,t,r,i){var o,l=[];for(;n;)l.push(n),o=n.previousComponent,delete n.previousComponent,n=o;l.reverse();for(var s=0,a=l.length,u=0,d=0;se.length?t:e}),p.value=e.join(c)):p.value=e.join(t.slice(u,u+p.count)),u+=p.count,p.added||(d+=p.count))}var h=l[a-1];1=c&&(g=Math.min(g,e-1)),f<=v+1&&(m=Math.max(m,e+1))}else h[e]=void 0}p++}if(r)!function e(){setTimeout(function(){return il?r():void(w()||e())},0)}();else for(;p<=i&&Date.now()<=l;){var y=w();if(y)return y}},addToPath:function(e,n,t,r){var i=e.lastComponent;return i&&i.added===n&&i.removed===t?{oldPos:e.oldPos+r,lastComponent:{count:i.count+1,added:n,removed:t,previousComponent:i.previousComponent}}:{oldPos:e.oldPos+r,lastComponent:{count:1,added:n,removed:t,previousComponent:i}}},extractCommon:function(e,n,t,r){for(var i=n.length,o=t.length,l=e.oldPos,s=l-r,a=0;s+1e.length)&&(n=e.length);for(var t=0,r=new Array(n);t=c.length-2&&a.length<=f.context&&(i=/\n$/.test(u),o=/\n$/.test(d),l=0==a.length&&m.length>r.oldLines,!i&&l&&0e.length)return!1;for(var t=0;t"):i.removed&&t.push(""),t.push((n=i.value,n.replace(/&/g,"&").replace(//g,">").replace(/"/g,"""))),i.added?t.push(""):i.removed&&t.push("")}return t.join("")},e.createPatch=function(e,n,t,r,i,o){return b(e,e,n,t,r,i,o)},e.createTwoFilesPatch=b,e.diffArrays=function(e,n,t){return g.diff(e,n,t)},e.diffChars=function(e,n,t){return r.diff(e,n,t)},e.diffCss=function(e,n,t){return d.diff(e,n,t)},e.diffJson=function(e,n,t){return v.diff(e,n,t)},e.diffLines=L,e.diffSentences=function(e,n,t){return u.diff(e,n,t)},e.diffTrimmedLines=function(e,n,t){var r=i(t,{ignoreWhitespace:!0});return a.diff(e,n,r)},e.diffWords=function(e,n,t){return t=i(t,{ignoreWhitespace:!0}),s.diff(e,n,t)},e.diffWordsWithSpace=function(e,n,t){return s.diff(e,n,t)},e.formatPatch=S,e.merge=function(e,n,t){e=N(e,t),n=N(n,t);var r={};(e.index||n.index)&&(r.index=e.index||n.index),(e.newFileName||n.newFileName)&&(P(e)?P(n)?(r.oldFileName=j(r,e.oldFileName,n.oldFileName),r.newFileName=j(r,e.newFileName,n.newFileName),r.oldHeader=j(r,e.oldHeader,n.oldHeader),r.newHeader=j(r,e.newHeader,n.newHeader)):(r.oldFileName=e.oldFileName,r.newFileName=e.newFileName,r.oldHeader=e.oldHeader,r.newHeader=e.newHeader):(r.oldFileName=n.oldFileName||e.oldFileName,r.newFileName=n.newFileName||e.newFileName,r.oldHeader=n.oldHeader||e.oldHeader,r.newHeader=n.newHeader||e.newHeader)),r.hunks=[];for(var i=0,o=0,l=0,s=0;i +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +@license +*/ +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).Diff={})}(this,function(e){"use strict";function r(){}function w(e,n,t,r,i){for(var o,l=[];n;)l.push(n),o=n.previousComponent,delete n.previousComponent,n=o;l.reverse();for(var a=0,u=l.length,s=0,f=0;ae.length?n:e}),d.value=e.join(c)):d.value=e.join(t.slice(s,s+d.count)),s+=d.count,d.added||(f+=d.count))}return l}r.prototype={diff:function(l,a){var u=2=d&&c<=v+1)return f(w(s,p[0].lastComponent,a,l,s.useLongestToken));var g=-1/0,m=1/0;function i(){for(var e=Math.max(g,-h);e<=Math.min(m,h);e+=2){var n=void 0,t=p[e-1],r=p[e+1],i=(t&&(p[e-1]=void 0),!1),o=(r&&(o=r.oldPos-e,i=r&&0<=o&&o=d&&c<=v+1)return f(w(s,n.lastComponent,a,l,s.useLongestToken));(p[e]=n).oldPos+1>=d&&(m=Math.min(m,e-1)),c<=v+1&&(g=Math.max(g,e+1))}else p[e]=void 0}h++}if(n)!function e(){setTimeout(function(){if(tr)return n();i()||e()},0)}();else for(;h<=t&&Date.now()<=r;){var o=i();if(o)return o}},addToPath:function(e,n,t,r,i){var o=e.lastComponent;return o&&!i.oneChangePerToken&&o.added===n&&o.removed===t?{oldPos:e.oldPos+r,lastComponent:{count:o.count+1,added:n,removed:t,previousComponent:o.previousComponent}}:{oldPos:e.oldPos+r,lastComponent:{count:1,added:n,removed:t,previousComponent:o}}},extractCommon:function(e,n,t,r,i){for(var o=n.length,l=t.length,a=e.oldPos,u=a-r,s=0;u+1n.length&&(t=e.length-n.length);var r=n.length;e.lengthe.length)&&(n=e.length);for(var t=0,r=new Array(n);te.length)return!1;for(var t=0;t"):r.removed&&n.push(""),n.push(r.value.replace(/&/g,"&").replace(//g,">").replace(/"/g,""")),r.added?n.push(""):r.removed&&n.push("")}return n.join("")},e.createPatch=function(e,n,t,r,i,o){return M(e,e,n,t,r,i,o)},e.createTwoFilesPatch=M,e.diffArrays=function(e,n,t){return F.diff(e,n,t)},e.diffChars=function(e,n,t){return I.diff(e,n,t)},e.diffCss=function(e,n,t){return m.diff(e,n,t)},e.diffJson=function(e,n,t){return x.diff(e,n,t)},e.diffLines=y,e.diffSentences=function(e,n,t){return g.diff(e,n,t)},e.diffTrimmedLines=function(e,n,t){return t=function(e,n){if("function"==typeof e)n.callback=e;else if(e)for(var t in e)e.hasOwnProperty(t)&&(n[t]=e[t]);return n}(t,{ignoreWhitespace:!0}),v.diff(e,n,t)},e.diffWords=function(e,n,t){return null==(null==t?void 0:t.ignoreWhitespace)||t.ignoreWhitespace?i.diff(e,n,t):a(e,n,t)},e.diffWordsWithSpace=a,e.formatPatch=E,e.merge=function(e,n,t){e=J(e,t),n=J(n,t);for(var r={},i=((e.index||n.index)&&(r.index=e.index||n.index),(e.newFileName||n.newFileName)&&(q(e)?q(n)?(r.oldFileName=H(r,e.oldFileName,n.oldFileName),r.newFileName=H(r,e.newFileName,n.newFileName),r.oldHeader=H(r,e.oldHeader,n.oldHeader),r.newHeader=H(r,e.newHeader,n.newHeader)):(r.oldFileName=e.oldFileName,r.newFileName=e.newFileName,r.oldHeader=e.oldHeader,r.newHeader=e.newHeader):(r.oldFileName=n.oldFileName||e.oldFileName,r.newFileName=n.newFileName||e.newFileName,r.oldHeader=n.oldHeader||e.oldHeader,r.newHeader=n.newHeader||e.newHeader)),r.hunks=[],0),o=0,l=0,a=0;i'); } else if (change.removed) { ret.push(''); } - ret.push(escapeHTML(change.value)); - if (change.added) { ret.push(''); } else if (change.removed) { ret.push(''); } } - return ret.join(''); } - function escapeHTML(s) { var n = s; n = n.replace(/&/g, '&'); @@ -39,4 +32,4 @@ function escapeHTML(s) { n = n.replace(/"/g, '"'); return n; } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb252ZXJ0L3htbC5qcyJdLCJuYW1lcyI6WyJjb252ZXJ0Q2hhbmdlc1RvWE1MIiwiY2hhbmdlcyIsInJldCIsImkiLCJsZW5ndGgiLCJjaGFuZ2UiLCJhZGRlZCIsInB1c2giLCJyZW1vdmVkIiwiZXNjYXBlSFRNTCIsInZhbHVlIiwiam9pbiIsInMiLCJuIiwicmVwbGFjZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQU8sU0FBU0EsbUJBQVQsQ0FBNkJDLE9BQTdCLEVBQXNDO0FBQzNDLE1BQUlDLEdBQUcsR0FBRyxFQUFWOztBQUNBLE9BQUssSUFBSUMsQ0FBQyxHQUFHLENBQWIsRUFBZ0JBLENBQUMsR0FBR0YsT0FBTyxDQUFDRyxNQUE1QixFQUFvQ0QsQ0FBQyxFQUFyQyxFQUF5QztBQUN2QyxRQUFJRSxNQUFNLEdBQUdKLE9BQU8sQ0FBQ0UsQ0FBRCxDQUFwQjs7QUFDQSxRQUFJRSxNQUFNLENBQUNDLEtBQVgsRUFBa0I7QUFDaEJKLE1BQUFBLEdBQUcsQ0FBQ0ssSUFBSixDQUFTLE9BQVQ7QUFDRCxLQUZELE1BRU8sSUFBSUYsTUFBTSxDQUFDRyxPQUFYLEVBQW9CO0FBQ3pCTixNQUFBQSxHQUFHLENBQUNLLElBQUosQ0FBUyxPQUFUO0FBQ0Q7O0FBRURMLElBQUFBLEdBQUcsQ0FBQ0ssSUFBSixDQUFTRSxVQUFVLENBQUNKLE1BQU0sQ0FBQ0ssS0FBUixDQUFuQjs7QUFFQSxRQUFJTCxNQUFNLENBQUNDLEtBQVgsRUFBa0I7QUFDaEJKLE1BQUFBLEdBQUcsQ0FBQ0ssSUFBSixDQUFTLFFBQVQ7QUFDRCxLQUZELE1BRU8sSUFBSUYsTUFBTSxDQUFDRyxPQUFYLEVBQW9CO0FBQ3pCTixNQUFBQSxHQUFHLENBQUNLLElBQUosQ0FBUyxRQUFUO0FBQ0Q7QUFDRjs7QUFDRCxTQUFPTCxHQUFHLENBQUNTLElBQUosQ0FBUyxFQUFULENBQVA7QUFDRDs7QUFFRCxTQUFTRixVQUFULENBQW9CRyxDQUFwQixFQUF1QjtBQUNyQixNQUFJQyxDQUFDLEdBQUdELENBQVI7QUFDQUMsRUFBQUEsQ0FBQyxHQUFHQSxDQUFDLENBQUNDLE9BQUYsQ0FBVSxJQUFWLEVBQWdCLE9BQWhCLENBQUo7QUFDQUQsRUFBQUEsQ0FBQyxHQUFHQSxDQUFDLENBQUNDLE9BQUYsQ0FBVSxJQUFWLEVBQWdCLE1BQWhCLENBQUo7QUFDQUQsRUFBQUEsQ0FBQyxHQUFHQSxDQUFDLENBQUNDLE9BQUYsQ0FBVSxJQUFWLEVBQWdCLE1BQWhCLENBQUo7QUFDQUQsRUFBQUEsQ0FBQyxHQUFHQSxDQUFDLENBQUNDLE9BQUYsQ0FBVSxJQUFWLEVBQWdCLFFBQWhCLENBQUo7QUFFQSxTQUFPRCxDQUFQO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gY29udmVydENoYW5nZXNUb1hNTChjaGFuZ2VzKSB7XG4gIGxldCByZXQgPSBbXTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBjaGFuZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgbGV0IGNoYW5nZSA9IGNoYW5nZXNbaV07XG4gICAgaWYgKGNoYW5nZS5hZGRlZCkge1xuICAgICAgcmV0LnB1c2goJzxpbnM+Jyk7XG4gICAgfSBlbHNlIGlmIChjaGFuZ2UucmVtb3ZlZCkge1xuICAgICAgcmV0LnB1c2goJzxkZWw+Jyk7XG4gICAgfVxuXG4gICAgcmV0LnB1c2goZXNjYXBlSFRNTChjaGFuZ2UudmFsdWUpKTtcblxuICAgIGlmIChjaGFuZ2UuYWRkZWQpIHtcbiAgICAgIHJldC5wdXNoKCc8L2lucz4nKTtcbiAgICB9IGVsc2UgaWYgKGNoYW5nZS5yZW1vdmVkKSB7XG4gICAgICByZXQucHVzaCgnPC9kZWw+Jyk7XG4gICAgfVxuICB9XG4gIHJldHVybiByZXQuam9pbignJyk7XG59XG5cbmZ1bmN0aW9uIGVzY2FwZUhUTUwocykge1xuICBsZXQgbiA9IHM7XG4gIG4gPSBuLnJlcGxhY2UoLyYvZywgJyZhbXA7Jyk7XG4gIG4gPSBuLnJlcGxhY2UoLzwvZywgJyZsdDsnKTtcbiAgbiA9IG4ucmVwbGFjZSgvPi9nLCAnJmd0OycpO1xuICBuID0gbi5yZXBsYWNlKC9cIi9nLCAnJnF1b3Q7Jyk7XG5cbiAgcmV0dXJuIG47XG59XG4iXX0= +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjb252ZXJ0Q2hhbmdlc1RvWE1MIiwiY2hhbmdlcyIsInJldCIsImkiLCJsZW5ndGgiLCJjaGFuZ2UiLCJhZGRlZCIsInB1c2giLCJyZW1vdmVkIiwiZXNjYXBlSFRNTCIsInZhbHVlIiwiam9pbiIsInMiLCJuIiwicmVwbGFjZSJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb252ZXJ0L3htbC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gY29udmVydENoYW5nZXNUb1hNTChjaGFuZ2VzKSB7XG4gIGxldCByZXQgPSBbXTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBjaGFuZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgbGV0IGNoYW5nZSA9IGNoYW5nZXNbaV07XG4gICAgaWYgKGNoYW5nZS5hZGRlZCkge1xuICAgICAgcmV0LnB1c2goJzxpbnM+Jyk7XG4gICAgfSBlbHNlIGlmIChjaGFuZ2UucmVtb3ZlZCkge1xuICAgICAgcmV0LnB1c2goJzxkZWw+Jyk7XG4gICAgfVxuXG4gICAgcmV0LnB1c2goZXNjYXBlSFRNTChjaGFuZ2UudmFsdWUpKTtcblxuICAgIGlmIChjaGFuZ2UuYWRkZWQpIHtcbiAgICAgIHJldC5wdXNoKCc8L2lucz4nKTtcbiAgICB9IGVsc2UgaWYgKGNoYW5nZS5yZW1vdmVkKSB7XG4gICAgICByZXQucHVzaCgnPC9kZWw+Jyk7XG4gICAgfVxuICB9XG4gIHJldHVybiByZXQuam9pbignJyk7XG59XG5cbmZ1bmN0aW9uIGVzY2FwZUhUTUwocykge1xuICBsZXQgbiA9IHM7XG4gIG4gPSBuLnJlcGxhY2UoLyYvZywgJyZhbXA7Jyk7XG4gIG4gPSBuLnJlcGxhY2UoLzwvZywgJyZsdDsnKTtcbiAgbiA9IG4ucmVwbGFjZSgvPi9nLCAnJmd0OycpO1xuICBuID0gbi5yZXBsYWNlKC9cIi9nLCAnJnF1b3Q7Jyk7XG5cbiAgcmV0dXJuIG47XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBQU8sU0FBU0EsbUJBQW1CQSxDQUFDQyxPQUFPLEVBQUU7RUFDM0MsSUFBSUMsR0FBRyxHQUFHLEVBQUU7RUFDWixLQUFLLElBQUlDLENBQUMsR0FBRyxDQUFDLEVBQUVBLENBQUMsR0FBR0YsT0FBTyxDQUFDRyxNQUFNLEVBQUVELENBQUMsRUFBRSxFQUFFO0lBQ3ZDLElBQUlFLE1BQU0sR0FBR0osT0FBTyxDQUFDRSxDQUFDLENBQUM7SUFDdkIsSUFBSUUsTUFBTSxDQUFDQyxLQUFLLEVBQUU7TUFDaEJKLEdBQUcsQ0FBQ0ssSUFBSSxDQUFDLE9BQU8sQ0FBQztJQUNuQixDQUFDLE1BQU0sSUFBSUYsTUFBTSxDQUFDRyxPQUFPLEVBQUU7TUFDekJOLEdBQUcsQ0FBQ0ssSUFBSSxDQUFDLE9BQU8sQ0FBQztJQUNuQjtJQUVBTCxHQUFHLENBQUNLLElBQUksQ0FBQ0UsVUFBVSxDQUFDSixNQUFNLENBQUNLLEtBQUssQ0FBQyxDQUFDO0lBRWxDLElBQUlMLE1BQU0sQ0FBQ0MsS0FBSyxFQUFFO01BQ2hCSixHQUFHLENBQUNLLElBQUksQ0FBQyxRQUFRLENBQUM7SUFDcEIsQ0FBQyxNQUFNLElBQUlGLE1BQU0sQ0FBQ0csT0FBTyxFQUFFO01BQ3pCTixHQUFHLENBQUNLLElBQUksQ0FBQyxRQUFRLENBQUM7SUFDcEI7RUFDRjtFQUNBLE9BQU9MLEdBQUcsQ0FBQ1MsSUFBSSxDQUFDLEVBQUUsQ0FBQztBQUNyQjtBQUVBLFNBQVNGLFVBQVVBLENBQUNHLENBQUMsRUFBRTtFQUNyQixJQUFJQyxDQUFDLEdBQUdELENBQUM7RUFDVEMsQ0FBQyxHQUFHQSxDQUFDLENBQUNDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDO0VBQzVCRCxDQUFDLEdBQUdBLENBQUMsQ0FBQ0MsT0FBTyxDQUFDLElBQUksRUFBRSxNQUFNLENBQUM7RUFDM0JELENBQUMsR0FBR0EsQ0FBQyxDQUFDQyxPQUFPLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQztFQUMzQkQsQ0FBQyxHQUFHQSxDQUFDLENBQUNDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDO0VBRTdCLE9BQU9ELENBQUM7QUFDViIsImlnbm9yZUxpc3QiOltdfQ== diff --git a/deps/npm/node_modules/diff/lib/diff/array.js b/deps/npm/node_modules/diff/lib/diff/array.js index 19e36809893c1d..bd0802db42ec22 100644 --- a/deps/npm/node_modules/diff/lib/diff/array.js +++ b/deps/npm/node_modules/diff/lib/diff/array.js @@ -4,20 +4,21 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.diffArrays = diffArrays; exports.arrayDiff = void 0; - +exports.diffArrays = diffArrays; /*istanbul ignore end*/ var /*istanbul ignore start*/ _base = _interopRequireDefault(require("./base")) /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /*istanbul ignore end*/ -var arrayDiff = new +var arrayDiff = +/*istanbul ignore start*/ +exports.arrayDiff = +/*istanbul ignore end*/ +new /*istanbul ignore start*/ _base /*istanbul ignore end*/ @@ -26,20 +27,13 @@ _base "default" /*istanbul ignore end*/ ](); - -/*istanbul ignore start*/ -exports.arrayDiff = arrayDiff; - -/*istanbul ignore end*/ arrayDiff.tokenize = function (value) { return value.slice(); }; - arrayDiff.join = arrayDiff.removeEmpty = function (value) { return value; }; - function diffArrays(oldArr, newArr, callback) { return arrayDiff.diff(oldArr, newArr, callback); } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2FycmF5LmpzIl0sIm5hbWVzIjpbImFycmF5RGlmZiIsIkRpZmYiLCJ0b2tlbml6ZSIsInZhbHVlIiwic2xpY2UiLCJqb2luIiwicmVtb3ZlRW1wdHkiLCJkaWZmQXJyYXlzIiwib2xkQXJyIiwibmV3QXJyIiwiY2FsbGJhY2siLCJkaWZmIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7Ozs7QUFFTyxJQUFNQSxTQUFTLEdBQUc7QUFBSUM7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUEsQ0FBSixFQUFsQjs7Ozs7O0FBQ1BELFNBQVMsQ0FBQ0UsUUFBVixHQUFxQixVQUFTQyxLQUFULEVBQWdCO0FBQ25DLFNBQU9BLEtBQUssQ0FBQ0MsS0FBTixFQUFQO0FBQ0QsQ0FGRDs7QUFHQUosU0FBUyxDQUFDSyxJQUFWLEdBQWlCTCxTQUFTLENBQUNNLFdBQVYsR0FBd0IsVUFBU0gsS0FBVCxFQUFnQjtBQUN2RCxTQUFPQSxLQUFQO0FBQ0QsQ0FGRDs7QUFJTyxTQUFTSSxVQUFULENBQW9CQyxNQUFwQixFQUE0QkMsTUFBNUIsRUFBb0NDLFFBQXBDLEVBQThDO0FBQUUsU0FBT1YsU0FBUyxDQUFDVyxJQUFWLENBQWVILE1BQWYsRUFBdUJDLE1BQXZCLEVBQStCQyxRQUEvQixDQUFQO0FBQWtEIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcblxuZXhwb3J0IGNvbnN0IGFycmF5RGlmZiA9IG5ldyBEaWZmKCk7XG5hcnJheURpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWUuc2xpY2UoKTtcbn07XG5hcnJheURpZmYuam9pbiA9IGFycmF5RGlmZi5yZW1vdmVFbXB0eSA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gIHJldHVybiB2YWx1ZTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBkaWZmQXJyYXlzKG9sZEFyciwgbmV3QXJyLCBjYWxsYmFjaykgeyByZXR1cm4gYXJyYXlEaWZmLmRpZmYob2xkQXJyLCBuZXdBcnIsIGNhbGxiYWNrKTsgfVxuIl19 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwib2JqIiwiX19lc01vZHVsZSIsImFycmF5RGlmZiIsImV4cG9ydHMiLCJEaWZmIiwidG9rZW5pemUiLCJ2YWx1ZSIsInNsaWNlIiwiam9pbiIsInJlbW92ZUVtcHR5IiwiZGlmZkFycmF5cyIsIm9sZEFyciIsIm5ld0FyciIsImNhbGxiYWNrIiwiZGlmZiJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2FycmF5LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cbmV4cG9ydCBjb25zdCBhcnJheURpZmYgPSBuZXcgRGlmZigpO1xuYXJyYXlEaWZmLnRva2VuaXplID0gZnVuY3Rpb24odmFsdWUpIHtcbiAgcmV0dXJuIHZhbHVlLnNsaWNlKCk7XG59O1xuYXJyYXlEaWZmLmpvaW4gPSBhcnJheURpZmYucmVtb3ZlRW1wdHkgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWU7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZkFycmF5cyhvbGRBcnIsIG5ld0FyciwgY2FsbGJhY2spIHsgcmV0dXJuIGFycmF5RGlmZi5kaWZmKG9sZEFyciwgbmV3QXJyLCBjYWxsYmFjayk7IH1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBQSxLQUFBLEdBQUFDLHNCQUFBLENBQUFDLE9BQUE7QUFBQTtBQUFBO0FBQTBCLG1DQUFBRCx1QkFBQUUsR0FBQSxXQUFBQSxHQUFBLElBQUFBLEdBQUEsQ0FBQUMsVUFBQSxHQUFBRCxHQUFBLGdCQUFBQSxHQUFBO0FBQUE7QUFFbkIsSUFBTUUsU0FBUztBQUFBO0FBQUFDLE9BQUEsQ0FBQUQsU0FBQTtBQUFBO0FBQUc7QUFBSUU7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUEsQ0FBSSxDQUFDLENBQUM7QUFDbkNGLFNBQVMsQ0FBQ0csUUFBUSxHQUFHLFVBQVNDLEtBQUssRUFBRTtFQUNuQyxPQUFPQSxLQUFLLENBQUNDLEtBQUssQ0FBQyxDQUFDO0FBQ3RCLENBQUM7QUFDREwsU0FBUyxDQUFDTSxJQUFJLEdBQUdOLFNBQVMsQ0FBQ08sV0FBVyxHQUFHLFVBQVNILEtBQUssRUFBRTtFQUN2RCxPQUFPQSxLQUFLO0FBQ2QsQ0FBQztBQUVNLFNBQVNJLFVBQVVBLENBQUNDLE1BQU0sRUFBRUMsTUFBTSxFQUFFQyxRQUFRLEVBQUU7RUFBRSxPQUFPWCxTQUFTLENBQUNZLElBQUksQ0FBQ0gsTUFBTSxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsQ0FBQztBQUFFIiwiaWdub3JlTGlzdCI6W119 diff --git a/deps/npm/node_modules/diff/lib/diff/base.js b/deps/npm/node_modules/diff/lib/diff/base.js index 428e7fd97e8193..d2b4b447f51fe9 100644 --- a/deps/npm/node_modules/diff/lib/diff/base.js +++ b/deps/npm/node_modules/diff/lib/diff/base.js @@ -5,56 +5,47 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = Diff; - /*istanbul ignore end*/ function Diff() {} - Diff.prototype = { /*istanbul ignore start*/ - /*istanbul ignore end*/ diff: function diff(oldString, newString) { /*istanbul ignore start*/ var _options$timeout; - var /*istanbul ignore end*/ options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var callback = options.callback; - if (typeof options === 'function') { callback = options; options = {}; } - - this.options = options; var self = this; - function done(value) { + value = self.postProcess(value, options); if (callback) { setTimeout(function () { - callback(undefined, value); + callback(value); }, 0); return true; } else { return value; } - } // Allow subclasses to massage the input prior to running - + } - oldString = this.castInput(oldString); - newString = this.castInput(newString); - oldString = this.removeEmpty(this.tokenize(oldString)); - newString = this.removeEmpty(this.tokenize(newString)); + // Allow subclasses to massage the input prior to running + oldString = this.castInput(oldString, options); + newString = this.castInput(newString, options); + oldString = this.removeEmpty(this.tokenize(oldString, options)); + newString = this.removeEmpty(this.tokenize(newString, options)); var newLen = newString.length, - oldLen = oldString.length; + oldLen = oldString.length; var editLength = 1; var maxEditLength = newLen + oldLen; - - if (options.maxEditLength) { + if (options.maxEditLength != null) { maxEditLength = Math.min(maxEditLength, options.maxEditLength); } - var maxExecutionTime = /*istanbul ignore start*/ (_options$timeout = @@ -64,17 +55,16 @@ Diff.prototype = { var bestPath = [{ oldPos: -1, lastComponent: undefined - }]; // Seed editLength = 0, i.e. the content starts with the same values - - var newPos = this.extractCommon(bestPath[0], newString, oldString, 0); + }]; + // Seed editLength = 0, i.e. the content starts with the same values + var newPos = this.extractCommon(bestPath[0], newString, oldString, 0, options); if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) { // Identity per the equality and tokenizer - return done([{ - value: this.join(newString), - count: newString.length - }]); - } // Once we hit the right edge of the edit graph on some diagonal k, we can + return done(buildValues(self, bestPath[0].lastComponent, newString, oldString, self.useLongestToken)); + } + + // Once we hit the right edge of the edit graph on some diagonal k, we can // definitely reach the end of the edit graph in no more than k edits, so // there's no point in considering any moves to diagonal k+1 any more (from // which we're guaranteed to need at least k+1 more edits). @@ -91,11 +81,10 @@ Diff.prototype = { // where the new text simply appends d characters on the end of the // original text of length n, the true Myers algorithm will take O(n+d^2) // time while this optimization needs only O(n+d) time. - - var minDiagonalToConsider = -Infinity, - maxDiagonalToConsider = Infinity; // Main worker method. checks all permutations of a given edit length for acceptance. + maxDiagonalToConsider = Infinity; + // Main worker method. checks all permutations of a given edit length for acceptance. function execEditLength() { for (var diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) { var basePath = @@ -104,72 +93,59 @@ Diff.prototype = { /*istanbul ignore end*/ ; var removePath = bestPath[diagonalPath - 1], - addPath = bestPath[diagonalPath + 1]; - + addPath = bestPath[diagonalPath + 1]; if (removePath) { // No one else is going to attempt to use this value, clear it bestPath[diagonalPath - 1] = undefined; } - var canAdd = false; - if (addPath) { // what newPos will be after we do an insertion: var addPathNewPos = addPath.oldPos - diagonalPath; canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen; } - var canRemove = removePath && removePath.oldPos + 1 < oldLen; - if (!canAdd && !canRemove) { // If this path is a terminal then prune bestPath[diagonalPath] = undefined; continue; - } // Select the diagonal that we want to branch from. We select the prior + } + + // Select the diagonal that we want to branch from. We select the prior // path whose position in the old string is the farthest from the origin // and does not pass the bounds of the diff graph - // TODO: Remove the `+ 1` here to make behavior match Myers algorithm - // and prefer to order removals before insertions. - - - if (!canRemove || canAdd && removePath.oldPos + 1 < addPath.oldPos) { - basePath = self.addToPath(addPath, true, undefined, 0); + if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) { + basePath = self.addToPath(addPath, true, false, 0, options); } else { - basePath = self.addToPath(removePath, undefined, true, 1); + basePath = self.addToPath(removePath, false, true, 1, options); } - - newPos = self.extractCommon(basePath, newString, oldString, diagonalPath); - + newPos = self.extractCommon(basePath, newString, oldString, diagonalPath, options); if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) { // If we have hit the end of both strings, then we are done return done(buildValues(self, basePath.lastComponent, newString, oldString, self.useLongestToken)); } else { bestPath[diagonalPath] = basePath; - if (basePath.oldPos + 1 >= oldLen) { maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1); } - if (newPos + 1 >= newLen) { minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1); } } } - editLength++; - } // Performs the length of edit iteration. Is a bit fugly as this has to support the + } + + // Performs the length of edit iteration. Is a bit fugly as this has to support the // sync and async mode which is never fun. Loops over execEditLength until a value // is produced, or until the edit length exceeds options.maxEditLength (if given), // in which case it will return undefined. - - if (callback) { (function exec() { setTimeout(function () { if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) { return callback(); } - if (!execEditLength()) { exec(); } @@ -178,21 +154,17 @@ Diff.prototype = { } else { while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) { var ret = execEditLength(); - if (ret) { return ret; } } } }, - /*istanbul ignore start*/ - /*istanbul ignore end*/ - addToPath: function addToPath(path, added, removed, oldPosInc) { + addToPath: function addToPath(path, added, removed, oldPosInc, options) { var last = path.lastComponent; - - if (last && last.added === added && last.removed === removed) { + if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) { return { oldPos: path.oldPos + oldPosInc, lastComponent: { @@ -214,104 +186,97 @@ Diff.prototype = { }; } }, - /*istanbul ignore start*/ - /*istanbul ignore end*/ - extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { + extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath, options) { var newLen = newString.length, - oldLen = oldString.length, - oldPos = basePath.oldPos, - newPos = oldPos - diagonalPath, - commonCount = 0; - - while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + oldLen = oldString.length, + oldPos = basePath.oldPos, + newPos = oldPos - diagonalPath, + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldString[oldPos + 1], newString[newPos + 1], options)) { newPos++; oldPos++; commonCount++; + if (options.oneChangePerToken) { + basePath.lastComponent = { + count: 1, + previousComponent: basePath.lastComponent, + added: false, + removed: false + }; + } } - - if (commonCount) { + if (commonCount && !options.oneChangePerToken) { basePath.lastComponent = { count: commonCount, - previousComponent: basePath.lastComponent + previousComponent: basePath.lastComponent, + added: false, + removed: false }; } - basePath.oldPos = oldPos; return newPos; }, - /*istanbul ignore start*/ - /*istanbul ignore end*/ - equals: function equals(left, right) { - if (this.options.comparator) { - return this.options.comparator(left, right); + equals: function equals(left, right, options) { + if (options.comparator) { + return options.comparator(left, right); } else { - return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); + return left === right || options.ignoreCase && left.toLowerCase() === right.toLowerCase(); } }, - /*istanbul ignore start*/ - /*istanbul ignore end*/ removeEmpty: function removeEmpty(array) { var ret = []; - for (var i = 0; i < array.length; i++) { if (array[i]) { ret.push(array[i]); } } - return ret; }, - /*istanbul ignore start*/ - /*istanbul ignore end*/ castInput: function castInput(value) { return value; }, - /*istanbul ignore start*/ - /*istanbul ignore end*/ tokenize: function tokenize(value) { - return value.split(''); + return Array.from(value); }, - /*istanbul ignore start*/ - /*istanbul ignore end*/ join: function join(chars) { return chars.join(''); + }, + /*istanbul ignore start*/ + /*istanbul ignore end*/ + postProcess: function postProcess(changeObjects) { + return changeObjects; } }; - function buildValues(diff, lastComponent, newString, oldString, useLongestToken) { // First we convert our linked list of components in reverse order to an // array in the right order: var components = []; var nextComponent; - while (lastComponent) { components.push(lastComponent); nextComponent = lastComponent.previousComponent; delete lastComponent.previousComponent; lastComponent = nextComponent; } - components.reverse(); var componentPos = 0, - componentLen = components.length, - newPos = 0, - oldPos = 0; - + componentLen = components.length, + newPos = 0, + oldPos = 0; for (; componentPos < componentLen; componentPos++) { var component = components[componentPos]; - if (!component.removed) { if (!component.added && useLongestToken) { var value = newString.slice(newPos, newPos + component.count); @@ -323,36 +288,17 @@ function buildValues(diff, lastComponent, newString, oldString, useLongestToken) } else { component.value = diff.join(newString.slice(newPos, newPos + component.count)); } + newPos += component.count; - newPos += component.count; // Common case - + // Common case if (!component.added) { oldPos += component.count; } } else { component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); - oldPos += component.count; // Reverse add and remove so removes are output first to match common convention - // The diffing algorithm is tied to add then remove output and this is the simplest - // route to get the desired output with minimal overhead. - - if (componentPos && components[componentPos - 1].added) { - var tmp = components[componentPos - 1]; - components[componentPos - 1] = components[componentPos]; - components[componentPos] = tmp; - } + oldPos += component.count; } - } // Special case handle for when one terminal is ignored (i.e. whitespace). - // For this case we merge the terminal into the prior string and drop the change. - // This is only available for string mode. - - - var finalComponent = components[componentLen - 1]; - - if (componentLen > 1 && typeof finalComponent.value === 'string' && (finalComponent.added || finalComponent.removed) && diff.equals('', finalComponent.value)) { - components[componentLen - 2].value += finalComponent.value; - components.pop(); } - return components; } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2Jhc2UuanMiXSwibmFtZXMiOlsiRGlmZiIsInByb3RvdHlwZSIsImRpZmYiLCJvbGRTdHJpbmciLCJuZXdTdHJpbmciLCJvcHRpb25zIiwiY2FsbGJhY2siLCJzZWxmIiwiZG9uZSIsInZhbHVlIiwic2V0VGltZW91dCIsInVuZGVmaW5lZCIsImNhc3RJbnB1dCIsInJlbW92ZUVtcHR5IiwidG9rZW5pemUiLCJuZXdMZW4iLCJsZW5ndGgiLCJvbGRMZW4iLCJlZGl0TGVuZ3RoIiwibWF4RWRpdExlbmd0aCIsIk1hdGgiLCJtaW4iLCJtYXhFeGVjdXRpb25UaW1lIiwidGltZW91dCIsIkluZmluaXR5IiwiYWJvcnRBZnRlclRpbWVzdGFtcCIsIkRhdGUiLCJub3ciLCJiZXN0UGF0aCIsIm9sZFBvcyIsImxhc3RDb21wb25lbnQiLCJuZXdQb3MiLCJleHRyYWN0Q29tbW9uIiwiam9pbiIsImNvdW50IiwibWluRGlhZ29uYWxUb0NvbnNpZGVyIiwibWF4RGlhZ29uYWxUb0NvbnNpZGVyIiwiZXhlY0VkaXRMZW5ndGgiLCJkaWFnb25hbFBhdGgiLCJtYXgiLCJiYXNlUGF0aCIsInJlbW92ZVBhdGgiLCJhZGRQYXRoIiwiY2FuQWRkIiwiYWRkUGF0aE5ld1BvcyIsImNhblJlbW92ZSIsImFkZFRvUGF0aCIsImJ1aWxkVmFsdWVzIiwidXNlTG9uZ2VzdFRva2VuIiwiZXhlYyIsInJldCIsInBhdGgiLCJhZGRlZCIsInJlbW92ZWQiLCJvbGRQb3NJbmMiLCJsYXN0IiwicHJldmlvdXNDb21wb25lbnQiLCJjb21tb25Db3VudCIsImVxdWFscyIsImxlZnQiLCJyaWdodCIsImNvbXBhcmF0b3IiLCJpZ25vcmVDYXNlIiwidG9Mb3dlckNhc2UiLCJhcnJheSIsImkiLCJwdXNoIiwic3BsaXQiLCJjaGFycyIsImNvbXBvbmVudHMiLCJuZXh0Q29tcG9uZW50IiwicmV2ZXJzZSIsImNvbXBvbmVudFBvcyIsImNvbXBvbmVudExlbiIsImNvbXBv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IERpZmYoKSB7fVxuXG5EaWZmLnByb3RvdHlwZSA9IHtcbiAgZGlmZihvbGRTdHJpbmcsIG5ld1N0cmluZywgb3B0aW9ucyA9IHt9KSB7XG4gICAgbGV0IGNhbGxiYWNrID0gb3B0aW9ucy5jYWxsYmFjaztcbiAgICBpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIGNhbGxiYWNrID0gb3B0aW9ucztcbiAgICAgIG9wdGlvbnMgPSB7fTtcbiAgICB9XG4gICAgdGhpcy5vcHRpb25zID0gb3B0aW9ucztcblxuICAgIGxldCBzZWxmID0gdGhpcztcblxuICAgIGZ1bmN0aW9uIGRvbmUodmFsdWUpIHtcbiAgICAgIGlmIChjYWxsYmFjaykge1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkgeyBjYWxsYmFjayh1bmRlZmluZWQsIHZhbHVlKTsgfSwgMCk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEFsbG93IHN1YmNsYXNzZXMgdG8gbWFzc2FnZSB0aGUgaW5wdXQgcHJpb3IgdG8gcnVubmluZ1xuICAgIG9sZFN0cmluZyA9IHRoaXMuY2FzdElucHV0KG9sZFN0cmluZyk7XG4gICAgbmV3U3RyaW5nID0gdGhpcy5jYXN0SW5wdXQobmV3U3RyaW5nKTtcblxuICAgIG9sZFN0cmluZyA9IHRoaXMucmVtb3ZlRW1wdHkodGhpcy50b2tlbml6ZShvbGRTdHJpbmcpKTtcbiAgICBuZXdTdHJpbmcgPSB0aGlzLnJlbW92ZUVtcHR5KHRoaXMudG9rZW5pemUobmV3U3RyaW5nKSk7XG5cbiAgICBsZXQgbmV3TGVuID0gbmV3U3RyaW5nLmxlbmd0aCwgb2xkTGVuID0gb2xkU3RyaW5nLmxlbmd0aDtcbiAgICBsZXQgZWRpdExlbmd0aCA9IDE7XG4gICAgbGV0IG1heEVkaXRMZW5ndGggPSBuZXdMZW4gKyBvbGRMZW47XG4gICAgaWYob3B0aW9ucy5tYXhFZGl0TGVuZ3RoKSB7XG4gICAgICBtYXhFZGl0TGVuZ3RoID0gTWF0aC5taW4obWF4RWRpdExlbmd0aCwgb3B0aW9ucy5tYXhFZGl0TGVuZ3RoKTtcbiAgICB9XG4gICAgY29uc3QgbWF4RXhlY3V0aW9uVGltZSA9IG9wdGlvbnMudGltZW91dCA/PyBJbmZpbml0eTtcbiAgICBjb25zdCBhYm9ydEFmdGVyVGltZXN0YW1wID0gRGF0ZS5ub3coKSArIG1heEV4ZWN1dGlvblRpbWU7XG5cbiAgICBsZXQgYmVzdFBhdGggPSBbeyBvbGRQb3M6IC0xLCBsYXN0Q29tcG9uZW50OiB1bmRlZmluZWQgfV07XG5cbiAgICAvLyBTZWVkIGVkaXRMZW5ndGggPSAwLCBpLmUuIHRoZSBjb250ZW50IHN0YXJ0cyB3aXRoIHRoZSBzYW1lIHZhbHVlc1xuICAgIGxldCBuZXdQb3MgPSB0aGlzLmV4dHJhY3RDb21tb24oYmVzdFBhdGhbMF0sIG5ld1N0cmluZywgb2xkU3RyaW5nLCAwKTtcbiAgICBpZiAoYmVzdFBhdGhbMF0ub2xkUG9zICsgMSA+PSBvbGRMZW4gJiYgbmV3UG9zICsgMSA+PSBuZXdMZW4pIHtcbiAgICAgIC8vIElkZW50aXR5IHBlciB0aGUgZXF1YWxpdHkgYW5kIHRva2VuaXplclxuICAgICAgcmV0dXJuIGRvbmUoW3t2YWx1ZTogdGhpcy5qb2luKG5ld1N0cmluZyksIGNvdW50OiBuZXdTdHJpbmcubGVuZ3RofV0pO1xuICAgIH1cblxuICAgIC8vIE9uY2Ugd2UgaGl0IHRoZSByaWdodCBlZGdlIG9mIHRoZSBlZGl0IGdyYXBoIG9uIHNvbWUgZGlhZ29uYWwgaywgd2UgY2FuXG4gICAgLy8gZGVmaW5pdGVseSByZWFjaCB0aGUgZW5kIG9mIHRoZSBlZGl0IGdyYXBoIGluIG5vIG1vcmUgdGhhbiBrIGVkaXRzLCBzb1xuICAgIC8vIHRoZXJlJ3Mgbm8gcG9pbnQgaW4gY29uc2lkZXJpbmcgYW55IG1vdmVzIHRvIGRpYWdvbmFsIGsrMSBhbnkgbW9yZSAoZnJvbVxuICAgIC8vIHdoaWNoIHdlJ3JlIGd1YXJhbnRlZWQgdG8gbmVlZCBhdCBsZWFzdCBrKzEgbW9yZSBlZGl0cykuXG4gICAgLy8gU2ltaWxhcmx5LCBvbmNlIHdlJ3ZlIHJlYWNoZWQgdGhlIGJvdHRvbSBvZiB0aGUgZWRpdCBncmFwaCwgdGhlcmUncyBub1xuICAgIC8vIHBvaW50IGNvbnNpZGVyaW5nIG1vdmVzIHRvIGxvd2VyIGRpYWdvbmFscy5cbiAgICAvLyBXZSByZWNvcmQgdGhpcyBmYWN0IGJ5IHNldHRpbmcgbWluRGlhZ29uYWxUb0NvbnNpZGVyIGFuZFxuICAgIC8vIG1heERpYWdvbmFsVG9Db25zaWRlciB0byBzb21lIGZpbml0ZSB2YWx1ZSBvbmNlIHdlJ3ZlIGhpdCB0aGUgZWRnZSBvZlxuICAgIC8vIHRoZSBlZGl0IGdyYXBoLlxuICAgIC8vIFRoaXMgb3B0aW1pemF0aW9uIGlzIG5vdCBmYWl0aGZ1bCB0byB0aGUgb3JpZ2luYWwgYWxnb3JpdGhtIHByZXNlbnRlZCBpblxuICAgIC8vIE15ZXJzJ3MgcGFwZXIsIHdoaWNoIGluc3RlYWQgcG9pbnRsZXNzbHkgZXh0ZW5kcyBELXBhdGhzIG9mZiB0aGUgZW5kIG9mXG4gICAgLy8gdGhlIGVkaXQgZ3JhcGggLSBzZWUgcGFnZSA3IG9mIE15ZXJzJ3MgcGFwZXIgd2hpY2ggbm90ZXMgdGhpcyBwb2ludFxuICAgIC8vIGV4cGxpY2l0bHkgYW5kIGlsbHVzdHJhdGVzIGl0IHdpdGggYSBkaWFncmFtLiBUaGlzIGhhcyBtYWpvciBwZXJmb3JtYW5jZVxuICAgIC8vIGltcGxpY2F0aW9ucyBmb3Igc29tZSBjb21tb24gc2NlbmFyaW9zLiBGb3IgaW5zdGFuY2UsIHRvIGNvbXB1dGUgYSBkaWZmXG4gICAgLy8gd2hlcmUgdGhlIG5ldyB0ZXh0IHNpbXBseSBhcHBlbmRzIGQgY2hhcmFjdGVycyBvbiB0aGUgZW5kIG9mIHRoZVxuICAgIC8vIG9yaWdpbmFsIHRleHQgb2YgbGVuZ3RoIG4sIHRoZSB0cnVlIE15ZXJzIGFsZ29yaXRobSB3aWxsIHRha2UgTyhuK2ReMilcbiAgICAvLyB0aW1lIHdoaWxlIHRoaXMgb3B0aW1pemF0aW9uIG5lZWRzIG9ubHkgTyhuK2QpIHRpbWUuXG4gICAgbGV0IG1pbkRpYWdvbmFsVG9Db25zaWRlciA9IC1JbmZpbml0eSwgbWF4RGlhZ29uYWxUb0NvbnNpZGVyID0gSW5maW5pdHk7XG5cbiAgICAvLyBNYWluIHdvcmtlciBtZXRob2QuIGNoZWNrcyBhbGwgcGVybXV0YXRpb25zIG9mIGEgZ2l2ZW4gZWRpdCBsZW5ndGggZm9yIGFjY2VwdGFuY2UuXG4gICAgZnVuY3Rpb24gZXhlY0VkaXRMZW5ndGgoKSB7XG4gICAgICBmb3IgKFxuICAgICAgICBsZXQgZGlhZ29uYWxQYXRoID0gTWF0aC5tYXgobWluRGlhZ29uYWxUb0NvbnNpZGVyLCAtZWRpdExlbmd0aCk7XG4gICAgICAgIGRpYWdvbmFsUGF0aCA8PSBNYXRoLm1pbihtYXhEaWFnb25hbFRvQ29uc2lkZXIsIGVkaXRMZW5ndGgpO1xuICAgICAgICBkaWFnb25hbFBhdGggKz0gMlxuICAgICAgKSB7XG4gICAgICAgIGxldCBiYXNlUGF0aDtcbiAgICAgICAgbGV0IHJlbW92ZVBhdGggPSBiZXN0UGF0aFtkaWFnb25hbFBhdGggLSAxXSxcbiAgICAgICAgICAgIGFkZFBhdGggPSBiZXN0UGF0aFtkaWFnb25hbFBhdGggKyAxXTtcbiAgICAgICAgaWYgKHJlbW92ZVBhdGgpIHtcbiAgICAgICAgICAvLyBObyBvbmUgZWxzZSBpcyBnb2luZyB0byBhdHRlbXB0IHRvIHVzZSB0aGlzIHZhbHVlLCBjbGVhciBpdFxuICAgICAgICAgIGJlc3RQYXRoW2RpYWdvbmFsUGF0aCAtIDFdID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGNhbkFkZCA9IGZhbHNlO1xuICAgICAgICBpZiAoYWRkUGF0aCkge1xuICAgICAgICAgIC8vIHdoYXQgbmV3UG9zIHdpbGwgYmUgYWZ0ZXIgd2UgZG8gYW4gaW5zZXJ0aW9uOlxuICAgICAgICAgIGNvbnN0IGFkZFBhdGhOZXdQb3MgPSBhZGRQYXRoLm9sZFBvcyAtIGRpYWdvbmFsUGF0aDtcbiAgICAgICAgICBjYW5BZGQgPSBhZGRQYXRoICYmIDAgPD0gYWRkUGF0aE5ld1BvcyAmJiBhZGRQYXRoTmV3UG9zIDwgbmV3TGVuO1xuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGNhblJlbW92ZSA9IHJlbW92ZVBhdGggJiYgcmVtb3ZlUGF0aC5vbGRQb3MgKyAxIDwgb2xkTGVuO1xuICAgICAgICBpZiAoIWNhbkFkZCAmJiAhY2FuUmVtb3ZlKSB7XG4gICAgICAgICAgLy8gSWYgdGhpcyBwYXRoIGlzIGEgdGVybWluYWwgdGhlbiBwcnVuZVxuICAgICAgICAgIGJlc3RQYXRoW2RpYWdvbmFsUGF0aF0gPSB1bmRlZmluZWQ7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTZWxlY3QgdGhlIGRpYWdvbmFsIHRoYXQgd2Ugd2FudCB0byBicmFuY2ggZnJvbS4gV2Ugc2VsZWN0IHRoZSBwcmlvclxuICAgICAgICAvLyBwYXRoIHdob3NlIHBvc2l0aW9uIGluIHRoZSBvbGQgc3RyaW5nIGlzIHRoZSBmYXJ0aGVzdCBmcm9tIHRoZSBvcmlnaW5cbiAgICAgICAgLy8gYW5kIGRvZXMgbm90IHBhc3MgdGhlIGJvdW5kcyBvZiB0aGUgZGlmZiBncmFwaFxuICAgICAgICAvLyBUT0RPOiBSZW1vdmUgdGhlIGArIDFgIGhlcmUgdG8gbWFrZSBiZWhhdmlvciBtYXRjaCBNeWVycyBhbGdvcml0aG1cbiAgICAgICAgLy8gICAgICAgYW5kIHByZWZlciB0byBvcmRlciByZW1vdmFscyBiZWZvcmUgaW5zZXJ0aW9ucy5cbiAgICAgICAgaWYgKCFjYW5SZW1vdmUgfHwgKGNhbkFkZCAmJiByZW1vdmVQYXRoLm9sZFBvcyArIDEgPCBhZGRQYXRoLm9sZFBvcykpIHtcbiAgICAgICAgICBiYXNlUGF0aCA9IHNlbGYuYWRkVG9QYXRoKGFkZFBhdGgsIHRydWUsIHVuZGVmaW5lZCwgMCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYmFzZVBhdGggPSBzZWxmLmFkZFRvUGF0aChyZW1vdmVQYXRoLCB1bmRlZmluZWQsIHRydWUsIDEpO1xuICAgICAgICB9XG5cbiAgICAgICAgbmV3UG9zID0gc2VsZi5leHRyYWN0Q29tbW9uKGJhc2VQYXRoLCBuZXdTdHJpbmcsIG9sZFN0cmluZywgZGlhZ29uYWxQYXRoKTtcblxuICAgICAgICBpZiAoYmFzZVBhdGgub2xkUG9zICsgMSA+PSBvbGRMZW4gJiYgbmV3UG9zICsgMSA+PSBuZXdMZW4pIHtcbiAgICAgICAgICAvLyBJZiB3ZSBoYXZlIGhpdCB0aGUgZW5kIG9mIGJvdGggc3RyaW5ncywgdGhlbiB3ZSBhcmUgZG9uZVxuICAgICAgICAgIHJldHVybiBkb25lKGJ1aWxkVmFsdWVzKHNlbGYsIGJhc2VQYXRoLmxhc3RDb21wb25lbnQsIG5ld1N0cmluZywgb2xkU3RyaW5nLCBzZWxmLnVzZUxvbmdlc3RUb2tlbikpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGJlc3RQYXRoW2RpYWdvbmFsUGF0aF0gPSBiYXNlUGF0aDtcbiAgICAgICAgICBpZiAoYmFzZVBhdGgub2xkUG9zICsgMSA+PSBvbGRMZW4pIHtcbiAgICAgICAgICAgIG1heERpYWdvbmFsVG9Db25zaWRlciA9IE1hdGgubWluKG1heERpYWdvbmFsVG9Db25zaWRlciwgZGlhZ29uYWxQYXRoIC0gMSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChuZXdQb3MgKyAxID49IG5ld0xlbikge1xuICAgICAgICAgICAgbWluRGlhZ29uYWxUb0NvbnNpZGVyID0gTWF0aC5tYXgobWluRGlhZ29uYWxUb0NvbnNpZGVyLCBkaWFnb25hbFBhdGggKyAxKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZWRpdExlbmd0aCsrO1xuICAgIH1cblxuICAgIC8vIFBlcmZvcm1zIHRoZSBsZW5ndGggb2YgZWRpdCBpdGVyYXRpb24uIElzIGEgYml0IGZ1Z2x5IGFzIHRoaXMgaGFzIHRvIHN1cHBvcnQgdGhlXG4gICAgLy8gc3luYyBhbmQgYXN5bmMgbW9kZSB3aGljaCBpcyBuZXZlciBmdW4uIExvb3BzIG92ZXIgZXhlY0VkaXRMZW5ndGggdW50aWwgYSB2YWx1ZVxuICAgIC8vIGlzIHByb2R1Y2VkLCBvciB1bnRpbCB0aGUgZWRpdCBsZW5ndGggZXhjZWVkcyBvcHRpb25zLm1heEVkaXRMZW5ndGggKGlmIGdpdmVuKSxcbiAgICAvLyBpbiB3aGljaCBjYXNlIGl0IHdpbGwgcmV0dXJuIHVuZGVmaW5lZC5cbiAgICBpZiAoY2FsbGJhY2spIHtcbiAgICAgIChmdW5jdGlvbiBleGVjKCkge1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgIGlmIChlZGl0TGVuZ3RoID4gbWF4RWRpdExlbmd0aCB8fCBEYXRlLm5vdygpID4gYWJvcnRBZnRlclRpbWVzdGFtcCkge1xuICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKCFleGVjRWRpdExlbmd0aCgpKSB7XG4gICAgICAgICAgICBleGVjKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9LCAwKTtcbiAgICAgIH0oKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHdoaWxlIChlZGl0TGVuZ3RoIDw9IG1heEVkaXRMZW5ndGggJiYgRGF0ZS5ub3coKSA8PSBhYm9ydEFmdGVyVGltZXN0YW1wKSB7XG4gICAgICAgIGxldCByZXQgPSBleGVjRWRpdExlbmd0aCgpO1xuICAgICAgICBpZiAocmV0KSB7XG4gICAgICAgICAgcmV0dXJuIHJldDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSxcblxuICBhZGRUb1BhdGgocGF0aCwgYWRkZWQsIHJlbW92ZWQsIG9sZFBvc0luYykge1xuICAgIGxldCBsYXN0ID0gcGF0aC5sYXN0Q29tcG9uZW50O1xuICAgIGlmIChsYXN0ICYmIGxhc3QuYWRkZWQgPT09IGFkZGVkICYmIGxhc3QucmVtb3ZlZCA9PT0gcmVtb3ZlZCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgb2xkUG9zOiBwYXRoLm9sZFBvcyArIG9sZFBvc0luYyxcbiAgICAgICAgbGFzdENvbXBvbmVudDoge2NvdW50OiBsYXN0LmNvdW50ICsgMSwgYWRkZWQ6IGFkZGVkLCByZW1vdmVkOiByZW1vdmVkLCBwcmV2aW91c0NvbXBvbmVudDogbGFzdC5wcmV2aW91c0NvbXBvbmVudCB9XG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBvbGRQb3M6IHBhdGgub2xkUG9zICsgb2xkUG9zSW5jLFxuICAgICAgICBsYXN0Q29tcG9uZW50OiB7Y291bnQ6IDEsIGFkZGVkOiBhZGRlZCwgcmVtb3ZlZDogcmVtb3ZlZCwgcHJldmlvdXNDb21wb25lbnQ6IGxhc3QgfVxuICAgICAgfTtcbiAgICB9XG4gIH0sXG4gIGV4dHJhY3RDb21tb24oYmFzZVBhdGgsIG5ld1N0cmluZywgb2xkU3RyaW5nLCBkaWFnb25hbFBhdGgpIHtcbiAgICBsZXQgbmV3TGVuID0gbmV3U3RyaW5nLmxlbmd0aCxcbiAgICAgICAgb2xkTGVuID0gb2xkU3RyaW5nLmxlbmd0aCxcbiAgICAgICAgb2xkUG9zID0gYmFzZVBhdGgub2xkUG9zLFxuICAgICAgICBuZXdQb3MgPSBvbGRQb3MgLSBkaWFnb25hbFBhdGgsXG5cbiAgICAgICAgY29tbW9uQ291bnQgPSAwO1xuICAgIHdoaWxlIChuZXdQb3MgKyAxIDwgbmV3TGVuICYmIG9sZFBvcyArIDEgPCBvbGRMZW4gJiYgdGhpcy5lcXVhbHMobmV3U3RyaW5nW25ld1BvcyArIDFdLCBvbGRTdHJpbmdbb2xkUG9zICsgMV0pKSB7XG4gICAgICBuZXdQb3MrKztcbiAgICAgIG9sZFBvcysrO1xuICAgICAgY29tbW9uQ291bnQrKztcbiAgICB9XG5cbiAgICBpZiAoY29tbW9uQ291bnQpIHtcbiAgICAgIGJhc2VQYXRoLmxhc3RDb21wb25lbnQgPSB7Y291bnQ6IGNvbW1vbkNvdW50LCBwcmV2aW91c0NvbXBvbmVudDogYmFzZVBhdGgubGFzdENvbXBvbmVudH07XG4gICAgfVxuXG4gICAgYmFzZVBhdGgub2xkUG9zID0gb2xkUG9zO1xuICAgIHJldHVybiBuZXdQb3M7XG4gIH0sXG5cbiAgZXF1YWxzKGxlZnQsIHJpZ2h0KSB7XG4gICAgaWYgKHRoaXMub3B0aW9ucy5jb21wYXJhdG9yKSB7XG4gICAgICByZXR1cm4gdGhpcy5vcHRpb25zLmNvbXBhcmF0b3IobGVmdCwgcmlnaHQpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbGVmdCA9PT0gcmlnaHRcbiAgICAgICAgfHwgKHRoaXMub3B0aW9ucy5pZ25vcmVDYXNlICYmIGxlZnQudG9Mb3dlckNhc2UoKSA9PT0gcmlnaHQudG9Mb3dlckNhc2UoKSk7XG4gICAgfVxuICB9LFxuICByZW1vdmVFbXB0eShhcnJheSkge1xuICAgIGxldCByZXQgPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGFycmF5Lmxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAoYXJyYXlbaV0pIHtcbiAgICAgICAgcmV0LnB1c2goYXJyYXlbaV0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmV0O1xuICB9LFxuICBjYXN0SW5wdXQodmFsdWUpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH0sXG4gIHRva2VuaXplKHZhbHVlKSB7XG4gICAgcmV0dXJuIHZhbHVlLnNwbGl0KCcnKTtcbiAgfSxcbiAgam9pbihjaGFycykge1xuICAgIHJldHVybiBjaGFycy5qb2luKCcnKTtcbiAgfVxufTtcblxuZnVuY3Rpb24gYnVpbGRWYWx1ZXMoZGlmZiwgbGFzdENvbXBvbmVudCwgbmV3U3RyaW5nLCBvbGRTdHJpbmcsIHVzZUxvbmdlc3RUb2tlbikge1xuICAvLyBGaXJzdCB3ZSBjb252ZXJ0IG91ciBsaW5rZWQgbGlzdCBvZiBjb21wb25lbnRzIGluIHJldmVyc2Ugb3JkZXIgdG8gYW5cbiAgLy8gYXJyYXkgaW4gdGhlIHJpZ2h0IG9yZGVyOlxuICBjb25zdCBjb21wb25lbnRzID0gW107XG4gIGxldCBuZXh0Q29tcG9uZW50O1xuICB3aGlsZSAobGFzdENvbXBvbmVudCkge1xuICAgIGNvbXBvbmVudHMucHVzaChsYXN0Q29tcG9uZW50KTtcbiAgICBuZXh0Q29tcG9uZW50ID0gbGFzdENvbXBvbmVudC5wcmV2aW91c0NvbXBvbmVudDtcbiAgICBkZWxldGUgbGFzdENvbXBvbmVudC5wcmV2aW91c0NvbXBvbmVudDtcbiAgICBsYXN0Q29tcG9uZW50ID0gbmV4dENvbXBvbmVudDtcbiAgfVxuICBjb21wb25lbnRzLnJldmVyc2UoKTtcblxuICBsZXQgY29tcG9uZW50UG9zID0gMCxcbiAgICAgIGNvbXBvbmVudExlbiA9IGNvbXBvbmVudHMubGVuZ3RoLFxuICAgICAgbmV3UG9zID0gMCxcbiAgICAgIG9sZFBvcyA9IDA7XG5cbiAgZm9yICg7IGNvbXBvbmVudFBvcyA8IGNvbXBvbmVudExlbjsgY29tcG9uZW50UG9zKyspIHtcbiAgICBsZXQgY29tcG9uZW50ID0gY29tcG9uZW50c1tjb21wb25lbnRQb3NdO1xuICAgIGlmICghY29tcG9uZW50LnJlbW92ZWQpIHtcbiAgICAgIGlmICghY29tcG9uZW50LmFkZGVkICYmIHVzZUxvbmdlc3RUb2tlbikge1xuICAgICAgICBsZXQgdmFsdWUgPSBuZXdTdHJpbmcuc2xpY2UobmV3UG9zLCBuZXdQb3MgKyBjb21wb25lbnQuY291bnQpO1xuICAgICAgICB2YWx1ZSA9IHZhbHVlLm1hcChmdW5jdGlvbih2YWx1ZSwgaSkge1xuICAgICAgICAgIGxldCBvbGRWYWx1ZSA9IG9sZFN0cmluZ1tvbGRQb3MgKyBpXTtcbiAgICAgICAgICByZXR1cm4gb2xkVmFsdWUubGVuZ3RoID4gdmFsdWUubGVuZ3RoID8gb2xkVmFsdWUgOiB2YWx1ZTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgY29tcG9uZW50LnZhbHVlID0gZGlmZi5qb2luKHZhbHVlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbXBvbmVudC52YWx1ZSA9IGRpZmYuam9pbihuZXdTdHJpbmcuc2xpY2UobmV3UG9zLCBuZXdQb3MgKyBjb21wb25lbnQuY291bnQpKTtcbiAgICAgIH1cbiAgICAgIG5ld1BvcyArPSBjb21wb25lbnQuY291bnQ7XG5cbiAgICAgIC8vIENvbW1vbiBjYXNlXG4gICAgICBpZiAoIWNvbXBvbmVudC5hZGRlZCkge1xuICAgICAgICBvbGRQb3MgKz0gY29tcG9uZW50LmNvdW50O1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBjb21wb25lbnQudmFsdWUgPSBkaWZmLmpvaW4ob2xkU3RyaW5nLnNsaWNlKG9sZFBvcywgb2xkUG9zICsgY29tcG9uZW50LmNvdW50KSk7XG4gICAgICBvbGRQb3MgKz0gY29tcG9uZW50LmNvdW50O1xuXG4gICAgICAvLyBSZXZlcnNlIGFkZCBhbmQgcmVtb3ZlIHNvIHJlbW92ZXMgYXJlIG91dHB1dCBmaXJzdCB0byBtYXRjaCBjb21tb24gY29udmVudGlvblxuICAgICAgLy8gVGhlIGRpZmZpbmcgYWxnb3JpdGhtIGlzIHRpZWQgdG8gYWRkIHRoZW4gcmVtb3ZlIG91dHB1dCBhbmQgdGhpcyBpcyB0aGUgc2ltcGxlc3RcbiAgICAgIC8vIHJvdXRlIHRvIGdldCB0aGUgZGVzaXJlZCBvdXRwdXQgd2l0aCBtaW5pbWFsIG92ZXJoZWFkLlxuICAgICAgaWYgKGNvbXBvbmVudFBvcyAmJiBjb21wb25lbnRzW2NvbXBvbmVudFBvcyAtIDFdLmFkZGVkKSB7XG4gICAgICAgIGxldCB0bXAgPSBjb21wb25lbnRzW2NvbXBvbmVudFBvcyAtIDFdO1xuICAgICAgICBjb21wb25lbnRzW2NvbXBvbmVudFBvcyAtIDFdID0gY29tcG9uZW50c1tjb21wb25lbnRQb3NdO1xuICAgICAgICBjb21wb25lbnRzW2NvbXBvbmVudFBvc10gPSB0bXA7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gU3BlY2lhbCBjYXNlIGhhbmRsZSBmb3Igd2hlbiBvbmUgdGVybWluYWwgaXMgaWdub3JlZCAoaS5lLiB3aGl0ZXNwYWNlKS5cbiAgLy8gRm9yIHRoaXMgY2FzZSB3ZSBtZXJnZSB0aGUgdGVybWluYWwgaW50byB0aGUgcHJpb3Igc3RyaW5nIGFuZCBkcm9wIHRoZSBjaGFuZ2UuXG4gIC8vIFRoaXMgaXMgb25seSBhdmFpbGFibGUgZm9yIHN0cmluZyBtb2RlLlxuICBsZXQgZmluYWxDb21wb25lbnQgPSBjb21wb25lbnRzW2NvbXBvbmVudExlbiAtIDFdO1xuICBpZiAoY29tcG9uZW50TGVuID4gMVxuICAgICAgJiYgdHlwZW9mIGZpbmFsQ29tcG9uZW50LnZhbHVlID09PSAnc3RyaW5nJ1xuICAgICAgJiYgKGZpbmFsQ29tcG9uZW50LmFkZGVkIHx8IGZpbmFsQ29tcG9uZW50LnJlbW92ZWQpXG4gICAgICAmJiBkaWZmLmVxdWFscygnJywgZmluYWxDb21wb25lbnQudmFsdWUpKSB7XG4gICAgY29tcG9uZW50c1tjb21wb25lbnRMZW4gLSAyXS52YWx1ZSArPSBmaW5hbENvbXBvbmVudC52YWx1ZTtcbiAgICBjb21wb25lbnRzLnBvcCgpO1xuICB9XG5cbiAgcmV0dXJuIGNvbXBvbmVudHM7XG59XG4iXX0= +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJEaWZmIiwicHJvdG90eXBlIiwiZGlmZiIsIm9sZFN0cmluZyIsIm5ld1N0cmluZyIsIl9vcHRpb25zJHRpbWVvdXQiLCJvcHRpb25zIiwiYXJndW1lbnRzIiwibGVuZ3RoIiwidW5kZWZpbmVkIiwiY2FsbGJhY2siLCJzZWxmIiwiZG9uZSIsInZhbHVlIiwicG9zdFByb2Nlc3MiLCJzZXRUaW1lb3V0IiwiY2FzdElucHV0IiwicmVtb3ZlRW1wdHkiLCJ0b2tlbml6ZSIsIm5ld0xlbiIsIm9sZExlbiIsImVkaXRMZW5ndGgiLCJtYXhFZGl0TGVuZ3RoIiwiTWF0aCIsIm1pbiIsIm1heEV4ZWN1dGlvblRpbWUiLCJ0aW1lb3V0IiwiSW5maW5pdHkiLCJhYm9ydEFmdGVyVGltZXN0YW1wIiwiRGF0ZSIsIm5vdyIsImJlc3RQYXRoIiwib2xkUG9zIiwibGFzdENvbXBvbmVudCIsIm5ld1BvcyIsImV4dHJhY3RDb21tb24iLCJidWlsZFZhbHVlcyIsInVzZUxvbmdlc3RUb2tlbiIsIm1pbkRpYWdvbmFsVG9Db25zaWRlciIsIm1heERpYWdvbmFsVG9Db25zaWRlciIsImV4ZWNFZGl0TGVuZ3RoIiwiZGlhZ29uYWxQYXRoIiwibWF4IiwiYmFzZVBhdGgiLCJyZW1vdmVQYXRoIiwiYWRkUGF0aCIsImNhbkFkZCIsImFkZFBhdGhOZXdQb3MiLCJjYW5SZW1vdmUiLCJhZGRUb1BhdGgiLCJleGVjIiwicmV0IiwicGF0aCIsImFkZGVkIiwicmVtb3ZlZCIsIm9sZFBvc0luYyIsImxhc3QiLCJvbmVDaGFuZ2VQZXJUb2tlbiIsImNvdW50IiwicHJldmlvdXNDb21wb25lbnQiLCJjb21tb25Db3VudCIsImVxdWFscyIsImxlZnQiLCJyaWdodCIsImNvbXBhcmF0b3IiLCJpZ25vcmVDYXNlIiwidG9Mb3dlckNhc2UiLCJhcnJheSIsImkiLCJwdXNoIiwiQXJyYXkiLCJmcm9tIiwiam9pbiIsImNoYXJzIiwiY2hhbmdlT2JqZWN0cyIsImNvbXBvbmVudHMiLCJuZXh0Q29tcG9uZW50IiwicmV2ZXJzZSIsImNvbXBvbmVudFBvcyIsImNvbXBvbmVudExlbiIsImNvbXBvbmVudCIsInNsaWNlIiwibWFwIiwib2xkVmFsdWUiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvZGlmZi9iYXNlLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIERpZmYoKSB7fVxuXG5EaWZmLnByb3RvdHlwZSA9IHtcbiAgZGlmZihvbGRTdHJpbmcsIG5ld1N0cmluZywgb3B0aW9ucyA9IHt9KSB7XG4gICAgbGV0IGNhbGxiYWNrID0gb3B0aW9ucy5jYWxsYmFjaztcbiAgICBpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIGNhbGxiYWNrID0gb3B0aW9ucztcbiAgICAgIG9wdGlvbnMgPSB7fTtcbiAgICB9XG5cbiAgICBsZXQgc2VsZiA9IHRoaXM7XG5cbiAgICBmdW5jdGlvbiBkb25lKHZhbHVlKSB7XG4gICAgICB2YWx1ZSA9IHNlbGYucG9zdFByb2Nlc3ModmFsdWUsIG9wdGlvbnMpO1xuICAgICAgaWYgKGNhbGxiYWNrKSB7XG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7IGNhbGxiYWNrKHZhbHVlKTsgfSwgMCk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEFsbG93IHN1YmNsYXNzZXMgdG8gbWFzc2FnZSB0aGUgaW5wdXQgcHJpb3IgdG8gcnVubmluZ1xuICAgIG9sZFN0cmluZyA9IHRoaXMuY2FzdElucHV0KG9sZFN0cmluZywgb3B0aW9ucyk7XG4gICAgbmV3U3RyaW5nID0gdGhpcy5jYXN0SW5wdXQobmV3U3RyaW5nLCBvcHRpb25zKTtcblxuICAgIG9sZFN0cmluZyA9IHRoaXMucmVtb3ZlRW1wdHkodGhpcy50b2tlbml6ZShvbGRTdHJpbmcsIG9wdGlvbnMpKTtcbiAgICBuZXdTdHJpbmcgPSB0aGlzLnJlbW92ZUVtcHR5KHRoaXMudG9rZW5pemUobmV3U3RyaW5nLCBvcHRpb25zKSk7XG5cbiAgICBsZXQgbmV3TGVuID0gbmV3U3RyaW5nLmxlbmd0aCwgb2xkTGVuID0gb2xkU3RyaW5nLmxlbmd0aDtcbiAgICBsZXQgZWRpdExlbmd0aCA9IDE7XG4gICAgbGV0IG1heEVkaXRMZW5ndGggPSBuZXdMZW4gKyBvbGRMZW47XG4gICAgaWYob3B0aW9ucy5tYXhFZGl0TGVuZ3RoICE9IG51bGwpIHtcbiAgICAgIG1heEVkaXRMZW5ndGggPSBNYXRoLm1pbihtYXhFZGl0TGVuZ3RoLCBvcHRpb25zLm1heEVkaXRMZW5ndGgpO1xuICAgIH1cbiAgICBjb25zdCBtYXhFeGVjdXRpb25UaW1lID0gb3B0aW9ucy50aW1lb3V0ID8/IEluZmluaXR5O1xuICAgIGNvbnN0IGFib3J0QWZ0ZXJUaW1lc3RhbXAgPSBEYXRlLm5vdygpICsgbWF4RXhlY3V0aW9uVGltZTtcblxuICAgIGxldCBiZXN0UGF0aCA9IFt7IG9sZFBvczogLTEsIGxhc3RDb21wb25lbnQ6IHVuZGVmaW5lZCB9XTtcblxuICAgIC8vIFNlZWQgZWRpdExlbmd0aCA9IDAsIGkuZS4gdGhlIGNvbnRlbnQgc3RhcnRzIHdpdGggdGhlIHNhbWUgdmFsdWVzXG4gICAgbGV0IG5ld1BvcyA9IHRoaXMuZXh0cmFjdENvbW1vbihiZXN0UGF0aFswXSwgbmV3U3RyaW5nLCBvbGRTdHJpbmcsIDAsIG9wdGlvbnMpO1xuICAgIGlmIChiZXN0UGF0aFswXS5vbGRQb3MgKyAxID49IG9sZExlbiAmJiBuZXdQb3MgKyAxID49IG5ld0xlbikge1xuICAgICAgLy8gSWRlbnRpdHkgcGVyIHRoZSBlcXVhbGl0eSBhbmQgdG9rZW5pemVyXG4gICAgICByZXR1cm4gZG9uZShidWlsZFZhbHVlcyhzZWxmLCBiZXN0UGF0aFswXS5sYXN0Q29tcG9uZW50LCBuZXdTdHJpbmcsIG9sZFN0cmluZywgc2VsZi51c2VMb25nZXN0VG9rZW4pKTtcbiAgICB9XG5cbiAgICAvLyBPbmNlIHdlIGhpdCB0aGUgcmlnaHQgZWRnZSBvZiB0aGUgZWRpdCBncmFwaCBvbiBzb21lIGRpYWdvbmFsIGssIHdlIGNhblxuICAgIC8vIGRlZmluaXRlbHkgcmVhY2ggdGhlIGVuZCBvZiB0aGUgZWRpdCBncmFwaCBpbiBubyBtb3JlIHRoYW4gayBlZGl0cywgc29cbiAgICAvLyB0aGVyZSdzIG5vIHBvaW50IGluIGNvbnNpZGVyaW5nIGFueSBtb3ZlcyB0byBkaWFnb25hbCBrKzEgYW55IG1vcmUgKGZyb21cbiAgICAvLyB3aGljaCB3ZSdyZSBndWFyYW50ZWVkIHRvIG5lZWQgYXQgbGVhc3QgaysxIG1vcmUgZWRpdHMpLlxuICAgIC8vIFNpbWlsYXJseSwgb25jZSB3ZSd2ZSByZWFjaGVkIHRoZSBib3R0b20gb2YgdGhlIGVkaXQgZ3JhcGgsIHRoZXJlJ3Mgbm9cbiAgICAvLyBwb2ludCBjb25zaWRlcmluZyBtb3ZlcyB0byBsb3dlciBkaWFnb25hbHMuXG4gICAgLy8gV2UgcmVjb3JkIHRoaXMgZmFjdCBieSBzZXR0aW5nIG1pbkRpYWdvbmFsVG9Db25zaWRlciBhbmRcbiAgICAvLyBtYXhEaWFnb25hbFRvQ29uc2lkZXIgdG8gc29tZSBmaW5pdGUgdmFsdWUgb25jZSB3ZSd2ZSBoaXQgdGhlIGVkZ2Ugb2ZcbiAgICAvLyB0aGUgZWRpdCBncmFwaC5cbiAgICAvLyBUaGlzIG9wdGltaXphdGlvbiBpcyBub3QgZmFpdGhmdWwgdG8gdGhlIG9yaWdpbmFsIGFsZ29yaXRobSBwcmVzZW50ZWQgaW5cbiAgICAvLyBNeWVycydzIHBhcGVyLCB3aGljaCBpbnN0ZWFkIHBvaW50bGVzc2x5IGV4dGVuZHMgRC1wYXRocyBvZmYgdGhlIGVuZCBvZlxuICAgIC8vIHRoZSBlZGl0IGdyYXBoIC0gc2VlIHBhZ2UgNyBvZiBNeWVycydzIHBhcGVyIHdoaWNoIG5vdGVzIHRoaXMgcG9pbnRcbiAgICAvLyBleHBsaWNpdGx5IGFuZCBpbGx1c3RyYXRlcyBpdCB3aXRoIGEgZGlhZ3JhbS4gVGhpcyBoYXMgbWFqb3IgcGVyZm9ybWFuY2VcbiAgICAvLyBpbXBsaWNhdGlvbnMgZm9yIHNvbWUgY29tbW9uIHNjZW5hcmlvcy4gRm9yIGluc3RhbmNlLCB0byBjb21wdXRlIGEgZGlmZlxuICAgIC8vIHdoZXJlIHRoZSBuZXcgdGV4dCBzaW1wbHkgYXBwZW5kcyBkIGNoYXJhY3RlcnMgb24gdGhlIGVuZCBvZiB0aGVcbiAgICAvLyBvcmlnaW5hbCB0ZXh0IG9mIGxlbmd0aCBuLCB0aGUgdHJ1ZSBNeWVycyBhbGdvcml0aG0gd2lsbCB0YWtlIE8obitkXjIpXG4gICAgLy8gdGltZSB3aGlsZSB0aGlzIG9wdGltaXphdGlvbiBuZWVkcyBvbmx5IE8obitkKSB0aW1lLlxuICAgIGxldCBtaW5EaWFnb25hbFRvQ29uc2lkZXIgPSAtSW5maW5pdHksIG1heERpYWdvbmFsVG9Db25zaWRlciA9IEluZmluaXR5O1xuXG4gICAgLy8gTWFpbiB3b3JrZXIgbWV0aG9kLiBjaGVja3MgYWxsIHBlcm11dGF0aW9ucyBvZiBhIGdpdmVuIGVkaXQgbGVuZ3RoIGZvciBhY2NlcHRhbmNlLlxuICAgIGZ1bmN0aW9uIGV4ZWNFZGl0TGVuZ3RoKCkge1xuICAgICAgZm9yIChcbiAgICAgICAgbGV0IGRpYWdvbmFsUGF0aCA9IE1hdGgubWF4KG1pbkRpYWdvbmFsVG9Db25zaWRlciwgLWVkaXRMZW5ndGgpO1xuICAgICAgICBkaWFnb25hbFBhdGggPD0gTWF0aC5taW4obWF4RGlhZ29uYWxUb0NvbnNpZGVyLCBlZGl0TGVuZ3RoKTtcbiAgICAgICAgZGlhZ29uYWxQYXRoICs9IDJcbiAgICAgICkge1xuICAgICAgICBsZXQgYmFzZVBhdGg7XG4gICAgICAgIGxldCByZW1vdmVQYXRoID0gYmVzdFBhdGhbZGlhZ29uYWxQYXRoIC0gMV0sXG4gICAgICAgICAgICBhZGRQYXRoID0gYmVzdFBhdGhbZGlhZ29uYWxQYXRoICsgMV07XG4gICAgICAgIGlmIChyZW1vdmVQYXRoKSB7XG4gICAgICAgICAgLy8gTm8gb25lIGVsc2UgaXMgZ29pbmcgdG8gYXR0ZW1wdCB0byB1c2UgdGhpcyB2YWx1ZSwgY2xlYXIgaXRcbiAgICAgICAgICBiZXN0UGF0aFtkaWFnb25hbFBhdGggLSAxXSA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBjYW5BZGQgPSBmYWxzZTtcbiAgICAgICAgaWYgKGFkZFBhdGgpIHtcbiAgICAgICAgICAvLyB3aGF0IG5ld1BvcyB3aWxsIGJlIGFmdGVyIHdlIGRvIGFuIGluc2VydGlvbjpcbiAgICAgICAgICBjb25zdCBhZGRQYXRoTmV3UG9zID0gYWRkUGF0aC5vbGRQb3MgLSBkaWFnb25hbFBhdGg7XG4gICAgICAgICAgY2FuQWRkID0gYWRkUGF0aCAmJiAwIDw9IGFkZFBhdGhOZXdQb3MgJiYgYWRkUGF0aE5ld1BvcyA8IG5ld0xlbjtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBjYW5SZW1vdmUgPSByZW1vdmVQYXRoICYmIHJlbW92ZVBhdGgub2xkUG9zICsgMSA8IG9sZExlbjtcbiAgICAgICAgaWYgKCFjYW5BZGQgJiYgIWNhblJlbW92ZSkge1xuICAgICAgICAgIC8vIElmIHRoaXMgcGF0aCBpcyBhIHRlcm1pbmFsIHRoZW4gcHJ1bmVcbiAgICAgICAgICBiZXN0UGF0aFtkaWFnb25hbFBhdGhdID0gdW5kZWZpbmVkO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gU2VsZWN0IHRoZSBkaWFnb25hbCB0aGF0IHdlIHdhbnQgdG8gYnJhbmNoIGZyb20uIFdlIHNlbGVjdCB0aGUgcHJpb3JcbiAgICAgICAgLy8gcGF0aCB3aG9zZSBwb3NpdGlvbiBpbiB0aGUgb2xkIHN0cmluZyBpcyB0aGUgZmFydGhlc3QgZnJvbSB0aGUgb3JpZ2luXG4gICAgICAgIC8vIGFuZCBkb2VzIG5vdCBwYXNzIHRoZSBib3VuZHMgb2YgdGhlIGRpZmYgZ3JhcGhcbiAgICAgICAgaWYgKCFjYW5SZW1vdmUgfHwgKGNhbkFkZCAmJiByZW1vdmVQYXRoLm9sZFBvcyA8IGFkZFBhdGgub2xkUG9zKSkge1xuICAgICAgICAgIGJhc2VQYXRoID0gc2VsZi5hZGRUb1BhdGgoYWRkUGF0aCwgdHJ1ZSwgZmFsc2UsIDAsIG9wdGlvbnMpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGJhc2VQYXRoID0gc2VsZi5hZGRUb1BhdGgocmVtb3ZlUGF0aCwgZmFsc2UsIHRydWUsIDEsIG9wdGlvbnMpO1xuICAgICAgICB9XG5cbiAgICAgICAgbmV3UG9zID0gc2VsZi5leHRyYWN0Q29tbW9uKGJhc2VQYXRoLCBuZXdTdHJpbmcsIG9sZFN0cmluZywgZGlhZ29uYWxQYXRoLCBvcHRpb25zKTtcblxuICAgICAgICBpZiAoYmFzZVBhdGgub2xkUG9zICsgMSA+PSBvbGRMZW4gJiYgbmV3UG9zICsgMSA+PSBuZXdMZW4pIHtcbiAgICAgICAgICAvLyBJZiB3ZSBoYXZlIGhpdCB0aGUgZW5kIG9mIGJvdGggc3RyaW5ncywgdGhlbiB3ZSBhcmUgZG9uZVxuICAgICAgICAgIHJldHVybiBkb25lKGJ1aWxkVmFsdWVzKHNlbGYsIGJhc2VQYXRoLmxhc3RDb21wb25lbnQsIG5ld1N0cmluZywgb2xkU3RyaW5nLCBzZWxmLnVzZUxvbmdlc3RUb2tlbikpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGJlc3RQYXRoW2RpYWdvbmFsUGF0aF0gPSBiYXNlUGF0aDtcbiAgICAgICAgICBpZiAoYmFzZVBhdGgub2xkUG9zICsgMSA+PSBvbGRMZW4pIHtcbiAgICAgICAgICAgIG1heERpYWdvbmFsVG9Db25zaWRlciA9IE1hdGgubWluKG1heERpYWdvbmFsVG9Db25zaWRlciwgZGlhZ29uYWxQYXRoIC0gMSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChuZXdQb3MgKyAxID49IG5ld0xlbikge1xuICAgICAgICAgICAgbWluRGlhZ29uYWxUb0NvbnNpZGVyID0gTWF0aC5tYXgobWluRGlhZ29uYWxUb0NvbnNpZGVyLCBkaWFnb25hbFBhdGggKyAxKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZWRpdExlbmd0aCsrO1xuICAgIH1cblxuICAgIC8vIFBlcmZvcm1zIHRoZSBsZW5ndGggb2YgZWRpdCBpdGVyYXRpb24uIElzIGEgYml0IGZ1Z2x5IGFzIHRoaXMgaGFzIHRvIHN1cHBvcnQgdGhlXG4gICAgLy8gc3luYyBhbmQgYXN5bmMgbW9kZSB3aGljaCBpcyBuZXZlciBmdW4uIExvb3BzIG92ZXIgZXhlY0VkaXRMZW5ndGggdW50aWwgYSB2YWx1ZVxuICAgIC8vIGlzIHByb2R1Y2VkLCBvciB1bnRpbCB0aGUgZWRpdCBsZW5ndGggZXhjZWVkcyBvcHRpb25zLm1heEVkaXRMZW5ndGggKGlmIGdpdmVuKSxcbiAgICAvLyBpbiB3aGljaCBjYXNlIGl0IHdpbGwgcmV0dXJuIHVuZGVmaW5lZC5cbiAgICBpZiAoY2FsbGJhY2spIHtcbiAgICAgIChmdW5jdGlvbiBleGVjKCkge1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgIGlmIChlZGl0TGVuZ3RoID4gbWF4RWRpdExlbmd0aCB8fCBEYXRlLm5vdygpID4gYWJvcnRBZnRlclRpbWVzdGFtcCkge1xuICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKCFleGVjRWRpdExlbmd0aCgpKSB7XG4gICAgICAgICAgICBleGVjKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9LCAwKTtcbiAgICAgIH0oKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHdoaWxlIChlZGl0TGVuZ3RoIDw9IG1heEVkaXRMZW5ndGggJiYgRGF0ZS5ub3coKSA8PSBhYm9ydEFmdGVyVGltZXN0YW1wKSB7XG4gICAgICAgIGxldCByZXQgPSBleGVjRWRpdExlbmd0aCgpO1xuICAgICAgICBpZiAocmV0KSB7XG4gICAgICAgICAgcmV0dXJuIHJldDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSxcblxuICBhZGRUb1BhdGgocGF0aCwgYWRkZWQsIHJlbW92ZWQsIG9sZFBvc0luYywgb3B0aW9ucykge1xuICAgIGxldCBsYXN0ID0gcGF0aC5sYXN0Q29tcG9uZW50O1xuICAgIGlmIChsYXN0ICYmICFvcHRpb25zLm9uZUNoYW5nZVBlclRva2VuICYmIGxhc3QuYWRkZWQgPT09IGFkZGVkICYmIGxhc3QucmVtb3ZlZCA9PT0gcmVtb3ZlZCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgb2xkUG9zOiBwYXRoLm9sZFBvcyArIG9sZFBvc0luYyxcbiAgICAgICAgbGFzdENvbXBvbmVudDoge2NvdW50OiBsYXN0LmNvdW50ICsgMSwgYWRkZWQ6IGFkZGVkLCByZW1vdmVkOiByZW1vdmVkLCBwcmV2aW91c0NvbXBvbmVudDogbGFzdC5wcmV2aW91c0NvbXBvbmVudCB9XG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBvbGRQb3M6IHBhdGgub2xkUG9zICsgb2xkUG9zSW5jLFxuICAgICAgICBsYXN0Q29tcG9uZW50OiB7Y291bnQ6IDEsIGFkZGVkOiBhZGRlZCwgcmVtb3ZlZDogcmVtb3ZlZCwgcHJldmlvdXNDb21wb25lbnQ6IGxhc3QgfVxuICAgICAgfTtcbiAgICB9XG4gIH0sXG4gIGV4dHJhY3RDb21tb24oYmFzZVBhdGgsIG5ld1N0cmluZywgb2xkU3RyaW5nLCBkaWFnb25hbFBhdGgsIG9wdGlvbnMpIHtcbiAgICBsZXQgbmV3TGVuID0gbmV3U3RyaW5nLmxlbmd0aCxcbiAgICAgICAgb2xkTGVuID0gb2xkU3RyaW5nLmxlbmd0aCxcbiAgICAgICAgb2xkUG9zID0gYmFzZVBhdGgub2xkUG9zLFxuICAgICAgICBuZXdQb3MgPSBvbGRQb3MgLSBkaWFnb25hbFBhdGgsXG5cbiAgICAgICAgY29tbW9uQ291bnQgPSAwO1xuICAgIHdoaWxlIChuZXdQb3MgKyAxIDwgbmV3TGVuICYmIG9sZFBvcyArIDEgPCBvbGRMZW4gJiYgdGhpcy5lcXVhbHMob2xkU3RyaW5nW29sZFBvcyArIDFdLCBuZXdTdHJpbmdbbmV3UG9zICsgMV0sIG9wdGlvbnMpKSB7XG4gICAgICBuZXdQb3MrKztcbiAgICAgIG9sZFBvcysrO1xuICAgICAgY29tbW9uQ291bnQrKztcbiAgICAgIGlmIChvcHRpb25zLm9uZUNoYW5nZVBlclRva2VuKSB7XG4gICAgICAgIGJhc2VQYXRoLmxhc3RDb21wb25lbnQgPSB7Y291bnQ6IDEsIHByZXZpb3VzQ29tcG9uZW50OiBiYXNlUGF0aC5sYXN0Q29tcG9uZW50LCBhZGRlZDogZmFsc2UsIHJlbW92ZWQ6IGZhbHNlfTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoY29tbW9uQ291bnQgJiYgIW9wdGlvbnMub25lQ2hhbmdlUGVyVG9rZW4pIHtcbiAgICAgIGJhc2VQYXRoLmxhc3RDb21wb25lbnQgPSB7Y291bnQ6IGNvbW1vbkNvdW50LCBwcmV2aW91c0NvbXBvbmVudDogYmFzZVBhdGgubGFzdENvbXBvbmVudCwgYWRkZWQ6IGZhbHNlLCByZW1vdmVkOiBmYWxzZX07XG4gICAgfVxuXG4gICAgYmFzZVBhdGgub2xkUG9zID0gb2xkUG9zO1xuICAgIHJldHVybiBuZXdQb3M7XG4gIH0sXG5cbiAgZXF1YWxzKGxlZnQsIHJpZ2h0LCBvcHRpb25zKSB7XG4gICAgaWYgKG9wdGlvbnMuY29tcGFyYXRvcikge1xuICAgICAgcmV0dXJuIG9wdGlvbnMuY29tcGFyYXRvcihsZWZ0LCByaWdodCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBsZWZ0ID09PSByaWdodFxuICAgICAgICB8fCAob3B0aW9ucy5pZ25vcmVDYXNlICYmIGxlZnQudG9Mb3dlckNhc2UoKSA9PT0gcmlnaHQudG9Mb3dlckNhc2UoKSk7XG4gICAgfVxuICB9LFxuICByZW1vdmVFbXB0eShhcnJheSkge1xuICAgIGxldCByZXQgPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGFycmF5Lmxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAoYXJyYXlbaV0pIHtcbiAgICAgICAgcmV0LnB1c2goYXJyYXlbaV0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmV0O1xuICB9LFxuICBjYXN0SW5wdXQodmFsdWUpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH0sXG4gIHRva2VuaXplKHZhbHVlKSB7XG4gICAgcmV0dXJuIEFycmF5LmZyb20odmFsdWUpO1xuICB9LFxuICBqb2luKGNoYXJzKSB7XG4gICAgcmV0dXJuIGNoYXJzLmpvaW4oJycpO1xuICB9LFxuICBwb3N0UHJvY2VzcyhjaGFuZ2VPYmplY3RzKSB7XG4gICAgcmV0dXJuIGNoYW5nZU9iamVjdHM7XG4gIH1cbn07XG5cbmZ1bmN0aW9uIGJ1aWxkVmFsdWVzKGRpZmYsIGxhc3RDb21wb25lbnQsIG5ld1N0cmluZywgb2xkU3RyaW5nLCB1c2VMb25nZXN0VG9rZW4pIHtcbiAgLy8gRmlyc3Qgd2UgY29udmVydCBvdXIgbGlua2VkIGxpc3Qgb2YgY29tcG9uZW50cyBpbiByZXZlcnNlIG9yZGVyIHRvIGFuXG4gIC8vIGFycmF5IGluIHRoZSByaWdodCBvcmRlcjpcbiAgY29uc3QgY29tcG9uZW50cyA9IFtdO1xuICBsZXQgbmV4dENvbXBvbmVudDtcbiAgd2hpbGUgKGxhc3RDb21wb25lbnQpIHtcbiAgICBjb21wb25lbnRzLnB1c2gobGFzdENvbXBvbmVudCk7XG4gICAgbmV4dENvbXBvbmVudCA9IGxhc3RDb21wb25lbnQucHJldmlvdXNDb21wb25lbnQ7XG4gICAgZGVsZXRlIGxhc3RDb21wb25lbnQucHJldmlvdXNDb21wb25lbnQ7XG4gICAgbGFzdENvbXBvbmVudCA9IG5leHRDb21wb25lbnQ7XG4gIH1cbiAgY29tcG9uZW50cy5yZXZlcnNlKCk7XG5cbiAgbGV0IGNvbXBvbmVudFBvcyA9IDAsXG4gICAgICBjb21wb25lbnRMZW4gPSBjb21wb25lbnRzLmxlbmd0aCxcbiAgICAgIG5ld1BvcyA9IDAsXG4gICAgICBvbGRQb3MgPSAwO1xuXG4gIGZvciAoOyBjb21wb25lbnRQb3MgPCBjb21wb25lbnRMZW47IGNvbXBvbmVudFBvcysrKSB7XG4gICAgbGV0IGNvbXBvbmVudCA9IGNvbXBvbmVudHNbY29tcG9uZW50UG9zXTtcbiAgICBpZiAoIWNvbXBvbmVudC5yZW1vdmVkKSB7XG4gICAgICBpZiAoIWNvbXBvbmVudC5hZGRlZCAmJiB1c2VMb25nZXN0VG9rZW4pIHtcbiAgICAgICAgbGV0IHZhbHVlID0gbmV3U3RyaW5nLnNsaWNlKG5ld1BvcywgbmV3UG9zICsgY29tcG9uZW50LmNvdW50KTtcbiAgICAgICAgdmFsdWUgPSB2YWx1ZS5tYXAoZnVuY3Rpb24odmFsdWUsIGkpIHtcbiAgICAgICAgICBsZXQgb2xkVmFsdWUgPSBvbGRTdHJpbmdbb2xkUG9zICsgaV07XG4gICAgICAgICAgcmV0dXJuIG9sZFZhbHVlLmxlbmd0aCA+IHZhbHVlLmxlbmd0aCA/IG9sZFZhbHVlIDogdmFsdWU7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNvbXBvbmVudC52YWx1ZSA9IGRpZmYuam9pbih2YWx1ZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb21wb25lbnQudmFsdWUgPSBkaWZmLmpvaW4obmV3U3RyaW5nLnNsaWNlKG5ld1BvcywgbmV3UG9zICsgY29tcG9uZW50LmNvdW50KSk7XG4gICAgICB9XG4gICAgICBuZXdQb3MgKz0gY29tcG9uZW50LmNvdW50O1xuXG4gICAgICAvLyBDb21tb24gY2FzZVxuICAgICAgaWYgKCFjb21wb25lbnQuYWRkZWQpIHtcbiAgICAgICAgb2xkUG9zICs9IGNvbXBvbmVudC5jb3VudDtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgY29tcG9uZW50LnZhbHVlID0gZGlmZi5qb2luKG9sZFN0cmluZy5zbGljZShvbGRQb3MsIG9sZFBvcyArIGNvbXBvbmVudC5jb3VudCkpO1xuICAgICAgb2xkUG9zICs9IGNvbXBvbmVudC5jb3VudDtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gY29tcG9uZW50cztcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFBZSxTQUFTQSxJQUFJQSxDQUFBLEVBQUcsQ0FBQztBQUVoQ0EsSUFBSSxD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 diff --git a/deps/npm/node_modules/diff/lib/diff/character.js b/deps/npm/node_modules/diff/lib/diff/character.js index 7ddfa205e394a9..6a3cf1c4d76d8d 100644 --- a/deps/npm/node_modules/diff/lib/diff/character.js +++ b/deps/npm/node_modules/diff/lib/diff/character.js @@ -4,20 +4,21 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.diffChars = diffChars; exports.characterDiff = void 0; - +exports.diffChars = diffChars; /*istanbul ignore end*/ var /*istanbul ignore start*/ _base = _interopRequireDefault(require("./base")) /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /*istanbul ignore end*/ -var characterDiff = new +var characterDiff = +/*istanbul ignore start*/ +exports.characterDiff = +/*istanbul ignore end*/ +new /*istanbul ignore start*/ _base /*istanbul ignore end*/ @@ -26,12 +27,7 @@ _base "default" /*istanbul ignore end*/ ](); - -/*istanbul ignore start*/ -exports.characterDiff = characterDiff; - -/*istanbul ignore end*/ function diffChars(oldStr, newStr, options) { return characterDiff.diff(oldStr, newStr, options); } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2NoYXJhY3Rlci5qcyJdLCJuYW1lcyI6WyJjaGFyYWN0ZXJEaWZmIiwiRGlmZiIsImRpZmZDaGFycyIsIm9sZFN0ciIsIm5ld1N0ciIsIm9wdGlvbnMiLCJkaWZmIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7Ozs7QUFFTyxJQUFNQSxhQUFhLEdBQUc7QUFBSUM7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUEsQ0FBSixFQUF0Qjs7Ozs7O0FBQ0EsU0FBU0MsU0FBVCxDQUFtQkMsTUFBbkIsRUFBMkJDLE1BQTNCLEVBQW1DQyxPQUFuQyxFQUE0QztBQUFFLFNBQU9MLGFBQWEsQ0FBQ00sSUFBZCxDQUFtQkgsTUFBbkIsRUFBMkJDLE1BQTNCLEVBQW1DQyxPQUFuQyxDQUFQO0FBQXFEIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcblxuZXhwb3J0IGNvbnN0IGNoYXJhY3RlckRpZmYgPSBuZXcgRGlmZigpO1xuZXhwb3J0IGZ1bmN0aW9uIGRpZmZDaGFycyhvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucykgeyByZXR1cm4gY2hhcmFjdGVyRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTsgfVxuIl19 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwib2JqIiwiX19lc01vZHVsZSIsImNoYXJhY3RlckRpZmYiLCJleHBvcnRzIiwiRGlmZiIsImRpZmZDaGFycyIsIm9sZFN0ciIsIm5ld1N0ciIsIm9wdGlvbnMiLCJkaWZmIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RpZmYvY2hhcmFjdGVyLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cbmV4cG9ydCBjb25zdCBjaGFyYWN0ZXJEaWZmID0gbmV3IERpZmYoKTtcbmV4cG9ydCBmdW5jdGlvbiBkaWZmQ2hhcnMob2xkU3RyLCBuZXdTdHIsIG9wdGlvbnMpIHsgcmV0dXJuIGNoYXJhY3RlckRpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucyk7IH1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBQSxLQUFBLEdBQUFDLHNCQUFBLENBQUFDLE9BQUE7QUFBQTtBQUFBO0FBQTBCLG1DQUFBRCx1QkFBQUUsR0FBQSxXQUFBQSxHQUFBLElBQUFBLEdBQUEsQ0FBQUMsVUFBQSxHQUFBRCxHQUFBLGdCQUFBQSxHQUFBO0FBQUE7QUFFbkIsSUFBTUUsYUFBYTtBQUFBO0FBQUFDLE9BQUEsQ0FBQUQsYUFBQTtBQUFBO0FBQUc7QUFBSUU7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUEsQ0FBSSxDQUFDLENBQUM7QUFDaEMsU0FBU0MsU0FBU0EsQ0FBQ0MsTUFBTSxFQUFFQyxNQUFNLEVBQUVDLE9BQU8sRUFBRTtFQUFFLE9BQU9OLGFBQWEsQ0FBQ08sSUFBSSxDQUFDSCxNQUFNLEVBQUVDLE1BQU0sRUFBRUMsT0FBTyxDQUFDO0FBQUUiLCJpZ25vcmVMaXN0IjpbXX0= diff --git a/deps/npm/node_modules/diff/lib/diff/css.js b/deps/npm/node_modules/diff/lib/diff/css.js index e3ad1fcba5f0eb..63218278183472 100644 --- a/deps/npm/node_modules/diff/lib/diff/css.js +++ b/deps/npm/node_modules/diff/lib/diff/css.js @@ -4,20 +4,21 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.diffCss = diffCss; exports.cssDiff = void 0; - +exports.diffCss = diffCss; /*istanbul ignore end*/ var /*istanbul ignore start*/ _base = _interopRequireDefault(require("./base")) /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /*istanbul ignore end*/ -var cssDiff = new +var cssDiff = +/*istanbul ignore start*/ +exports.cssDiff = +/*istanbul ignore end*/ +new /*istanbul ignore start*/ _base /*istanbul ignore end*/ @@ -26,16 +27,10 @@ _base "default" /*istanbul ignore end*/ ](); - -/*istanbul ignore start*/ -exports.cssDiff = cssDiff; - -/*istanbul ignore end*/ cssDiff.tokenize = function (value) { return value.split(/([{}:;,]|\s+)/); }; - function diffCss(oldStr, newStr, callback) { return cssDiff.diff(oldStr, newStr, callback); } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2Nzcy5qcyJdLCJuYW1lcyI6WyJjc3NEaWZmIiwiRGlmZiIsInRva2VuaXplIiwidmFsdWUiLCJzcGxpdCIsImRpZmZDc3MiLCJvbGRTdHIiLCJuZXdTdHIiLCJjYWxsYmFjayIsImRpZmYiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOzs7OztBQUVPLElBQU1BLE9BQU8sR0FBRztBQUFJQztBQUFBQTtBQUFBQTtBQUFBQTtBQUFBQTtBQUFBQTtBQUFBQTtBQUFBQSxDQUFKLEVBQWhCOzs7Ozs7QUFDUEQsT0FBTyxDQUFDRSxRQUFSLEdBQW1CLFVBQVNDLEtBQVQsRUFBZ0I7QUFDakMsU0FBT0EsS0FBSyxDQUFDQyxLQUFOLENBQVksZUFBWixDQUFQO0FBQ0QsQ0FGRDs7QUFJTyxTQUFTQyxPQUFULENBQWlCQyxNQUFqQixFQUF5QkMsTUFBekIsRUFBaUNDLFFBQWpDLEVBQTJDO0FBQUUsU0FBT1IsT0FBTyxDQUFDUyxJQUFSLENBQWFILE1BQWIsRUFBcUJDLE1BQXJCLEVBQTZCQyxRQUE3QixDQUFQO0FBQWdEIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcblxuZXhwb3J0IGNvbnN0IGNzc0RpZmYgPSBuZXcgRGlmZigpO1xuY3NzRGlmZi50b2tlbml6ZSA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gIHJldHVybiB2YWx1ZS5zcGxpdCgvKFt7fTo7LF18XFxzKykvKTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBkaWZmQ3NzKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjaykgeyByZXR1cm4gY3NzRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjayk7IH1cbiJdfQ== +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwib2JqIiwiX19lc01vZHVsZSIsImNzc0RpZmYiLCJleHBvcnRzIiwiRGlmZiIsInRva2VuaXplIiwidmFsdWUiLCJzcGxpdCIsImRpZmZDc3MiLCJvbGRTdHIiLCJuZXdTdHIiLCJjYWxsYmFjayIsImRpZmYiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvZGlmZi9jc3MuanMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcblxuZXhwb3J0IGNvbnN0IGNzc0RpZmYgPSBuZXcgRGlmZigpO1xuY3NzRGlmZi50b2tlbml6ZSA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gIHJldHVybiB2YWx1ZS5zcGxpdCgvKFt7fTo7LF18XFxzKykvKTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBkaWZmQ3NzKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjaykgeyByZXR1cm4gY3NzRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjayk7IH1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBQSxLQUFBLEdBQUFDLHNCQUFBLENBQUFDLE9BQUE7QUFBQTtBQUFBO0FBQTBCLG1DQUFBRCx1QkFBQUUsR0FBQSxXQUFBQSxHQUFBLElBQUFBLEdBQUEsQ0FBQUMsVUFBQSxHQUFBRCxHQUFBLGdCQUFBQSxHQUFBO0FBQUE7QUFFbkIsSUFBTUUsT0FBTztBQUFBO0FBQUFDLE9BQUEsQ0FBQUQsT0FBQTtBQUFBO0FBQUc7QUFBSUU7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUEsQ0FBSSxDQUFDLENBQUM7QUFDakNGLE9BQU8sQ0FBQ0csUUFBUSxHQUFHLFVBQVNDLEtBQUssRUFBRTtFQUNqQyxPQUFPQSxLQUFLLENBQUNDLEtBQUssQ0FBQyxlQUFlLENBQUM7QUFDckMsQ0FBQztBQUVNLFNBQVNDLE9BQU9BLENBQUNDLE1BQU0sRUFBRUMsTUFBTSxFQUFFQyxRQUFRLEVBQUU7RUFBRSxPQUFPVCxPQUFPLENBQUNVLElBQUksQ0FBQ0gsTUFBTSxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsQ0FBQztBQUFFIiwiaWdub3JlTGlzdCI6W119 diff --git a/deps/npm/node_modules/diff/lib/diff/json.js b/deps/npm/node_modules/diff/lib/diff/json.js index 67c2f175f73b4f..a3f07480ee7dd9 100644 --- a/deps/npm/node_modules/diff/lib/diff/json.js +++ b/deps/npm/node_modules/diff/lib/diff/json.js @@ -4,30 +4,28 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.diffJson = diffJson; exports.canonicalize = canonicalize; +exports.diffJson = diffJson; exports.jsonDiff = void 0; - /*istanbul ignore end*/ var /*istanbul ignore start*/ _base = _interopRequireDefault(require("./base")) /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _line = require("./line") /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - -function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } - +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +/*istanbul ignore end*/ +var jsonDiff = +/*istanbul ignore start*/ +exports.jsonDiff = /*istanbul ignore end*/ -var objectPrototypeToString = Object.prototype.toString; -var jsonDiff = new +new /*istanbul ignore start*/ _base /*istanbul ignore end*/ @@ -35,13 +33,9 @@ _base /*istanbul ignore start*/ "default" /*istanbul ignore end*/ -](); // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a +](); +// Discriminate between two lines of pretty-printed, serialized JSON where one of them has a // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: - -/*istanbul ignore start*/ -exports.jsonDiff = jsonDiff; - -/*istanbul ignore end*/ jsonDiff.useLongestToken = true; jsonDiff.tokenize = /*istanbul ignore start*/ @@ -52,26 +46,28 @@ _line lineDiff /*istanbul ignore end*/ .tokenize; - -jsonDiff.castInput = function (value) { - /*istanbul ignore start*/ - var _this$options = - /*istanbul ignore end*/ - this.options, - undefinedReplacement = _this$options.undefinedReplacement, - _this$options$stringi = _this$options.stringifyReplacer, - stringifyReplacer = _this$options$stringi === void 0 ? function (k, v) - /*istanbul ignore start*/ - { - return ( - /*istanbul ignore end*/ - typeof v === 'undefined' ? undefinedReplacement : v - ); - } : _this$options$stringi; +jsonDiff.castInput = function (value, options) { + var + /*istanbul ignore start*/ + /*istanbul ignore end*/ + undefinedReplacement = options.undefinedReplacement, + /*istanbul ignore start*/ + _options$stringifyRep = + /*istanbul ignore end*/ + options.stringifyReplacer, + /*istanbul ignore start*/ + /*istanbul ignore end*/ + stringifyReplacer = _options$stringifyRep === void 0 ? function (k, v) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + typeof v === 'undefined' ? undefinedReplacement : v + ); + } : _options$stringifyRep; return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); }; - -jsonDiff.equals = function (left, right) { +jsonDiff.equals = function (left, right, options) { return ( /*istanbul ignore start*/ _base @@ -80,52 +76,42 @@ jsonDiff.equals = function (left, right) { /*istanbul ignore start*/ "default" /*istanbul ignore end*/ - ].prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')) + ].prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'), options) ); }; - function diffJson(oldObj, newObj, options) { return jsonDiff.diff(oldObj, newObj, options); -} // This function handles the presence of circular references by bailing out when encountering an -// object that is already on the "stack" of items being processed. Accepts an optional replacer - +} +// This function handles the presence of circular references by bailing out when encountering an +// object that is already on the "stack" of items being processed. Accepts an optional replacer function canonicalize(obj, stack, replacementStack, replacer, key) { stack = stack || []; replacementStack = replacementStack || []; - if (replacer) { obj = replacer(key, obj); } - var i; - for (i = 0; i < stack.length; i += 1) { if (stack[i] === obj) { return replacementStack[i]; } } - var canonicalizedObj; - - if ('[object Array]' === objectPrototypeToString.call(obj)) { + if ('[object Array]' === Object.prototype.toString.call(obj)) { stack.push(obj); canonicalizedObj = new Array(obj.length); replacementStack.push(canonicalizedObj); - for (i = 0; i < obj.length; i += 1) { canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); } - stack.pop(); replacementStack.pop(); return canonicalizedObj; } - if (obj && obj.toJSON) { obj = obj.toJSON(); } - if ( /*istanbul ignore start*/ _typeof( @@ -134,30 +120,24 @@ function canonicalize(obj, stack, replacementStack, replacer, key) { stack.push(obj); canonicalizedObj = {}; replacementStack.push(canonicalizedObj); - var sortedKeys = [], - _key; - + _key; for (_key in obj) { /* istanbul ignore else */ - if (obj.hasOwnProperty(_key)) { + if (Object.prototype.hasOwnProperty.call(obj, _key)) { sortedKeys.push(_key); } } - sortedKeys.sort(); - for (i = 0; i < sortedKeys.length; i += 1) { _key = sortedKeys[i]; canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); } - stack.pop(); replacementStack.pop(); } else { canonicalizedObj = obj; } - return canonicalizedObj; } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2pzb24uanMiXSwibmFtZXMiOlsib2JqZWN0UHJvdG90eXBlVG9TdHJpbmciLCJPYmplY3QiLCJwcm90b3R5cGUiLCJ0b1N0cmluZyIsImpzb25EaWZmIiwiRGlmZiIsInVzZUxvbmdlc3RUb2tlbiIsInRva2VuaXplIiwibGluZURpZmYiLCJjYXN0SW5wdXQiLCJ2YWx1ZSIsIm9wdGlvbnMiLCJ1bmRlZmluZWRSZXBsYWNlbWVudCIsInN0cmluZ2lmeVJlcGxhY2VyIiwiayIsInYiLCJKU09OIiwic3RyaW5naWZ5IiwiY2Fub25pY2FsaXplIiwiZXF1YWxzIiwibGVmdCIsInJpZ2h0IiwiY2FsbCIsInJlcGxhY2UiLCJkaWZmSnNvbiIsIm9sZE9iaiIsIm5ld09iaiIsImRpZmYiLCJvYmoiLCJzdGFjayIsInJlcGxhY2VtZW50U3RhY2siLCJyZXBsYWNlciIsImtleSIsImkiLCJsZW5ndGgiLCJjYW5vbmljYWxpemVkT2JqIiwicHVzaCIsIkFycmF5IiwicG9wIiwidG9KU09OIiwic29ydGVkS2V5cyIsImhhc093blByb3BlcnR5Iiwic29ydCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQUNBO0FBQUE7QUFBQTtBQUFBO0FBQUE7Ozs7Ozs7QUFFQSxJQUFNQSx1QkFBdUIsR0FBR0MsTUFBTSxDQUFDQyxTQUFQLENBQWlCQyxRQUFqRDtBQUdPLElBQU1DLFFBQVEsR0FBRztBQUFJQztBQUFBQTtBQUFBQTtBQUFBQTtBQUFBQTtBQUFBQTtBQUFBQTtBQUFBQSxDQUFKLEVBQWpCLEMsQ0FDUDtBQUNBOzs7Ozs7QUFDQUQsUUFBUSxDQUFDRSxlQUFULEdBQTJCLElBQTNCO0FBRUFGLFFBQVEsQ0FBQ0csUUFBVDtBQUFvQkM7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQTtBQUFBLENBQVNELFFBQTdCOztBQUNBSCxRQUFRLENBQUNLLFNBQVQsR0FBcUIsVUFBU0MsS0FB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Z0IsR0FBR1AsR0FBbkI7QUFDRDs7QUFDRCxTQUFPTyxnQkFBUDtBQUNEIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcbmltcG9ydCB7bGluZURpZmZ9IGZyb20gJy4vbGluZSc7XG5cbmNvbnN0IG9iamVjdFByb3RvdHlwZVRvU3RyaW5nID0gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZztcblxuXG5leHBvcnQgY29uc3QganNvbkRpZmYgPSBuZXcgRGlmZigpO1xuLy8gRGlzY3JpbWluYXRlIGJldHdlZW4gdHdvIGxpbmVzIG9mIHByZXR0eS1wcmludGVkLCBzZXJpYWxpemVkIEpTT04gd2hlcmUgb25lIG9mIHRoZW0gaGFzIGFcbi8vIGRhbmdsaW5nIGNvbW1hIGFuZCB0aGUgb3RoZXIgZG9lc24ndC4gVHVybnMgb3V0IGluY2x1ZGluZyB0aGUgZGFuZ2xpbmcgY29tbWEgeWllbGRzIHRoZSBuaWNlc3Qgb3V0cHV0OlxuanNvbkRpZmYudXNlTG9uZ2VzdFRva2VuID0gdHJ1ZTtcblxuanNvbkRpZmYudG9rZW5pemUgPSBsaW5lRGlmZi50b2tlbml6ZTtcbmpzb25EaWZmLmNhc3RJbnB1dCA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gIGNvbnN0IHt1bmRlZmluZWRSZXBsYWNlbWVudCwgc3RyaW5naWZ5UmVwbGFjZXIgPSAoaywgdikgPT4gdHlwZW9mIHYgPT09ICd1bmRlZmluZWQnID8gdW5kZWZpbmVkUmVwbGFjZW1lbnQgOiB2fSA9IHRoaXMub3B0aW9ucztcblxuICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/IHZhbHVlIDogSlNPTi5zdHJpbmdpZnkoY2Fub25pY2FsaXplKHZhbHVlLCBudWxsLCBudWxsLCBzdHJpbmdpZnlSZXBsYWNlciksIHN0cmluZ2lmeVJlcGxhY2VyLCAnICAnKTtcbn07XG5qc29uRGlmZi5lcXVhbHMgPSBmdW5jdGlvbihsZWZ0LCByaWdodCkge1xuICByZXR1cm4gRGlmZi5wcm90b3R5cGUuZXF1YWxzLmNhbGwoanNvbkRpZmYsIGxlZnQucmVwbGFjZSgvLChbXFxyXFxuXSkvZywgJyQxJyksIHJpZ2h0LnJlcGxhY2UoLywoW1xcclxcbl0pL2csICckMScpKTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBkaWZmSnNvbihvbGRPYmosIG5ld09iaiwgb3B0aW9ucykgeyByZXR1cm4ganNvbkRpZmYuZGlmZihvbGRPYmosIG5ld09iaiwgb3B0aW9ucyk7IH1cblxuLy8gVGhpcyBmdW5jdGlvbiBoYW5kbGVzIHRoZSBwcmVzZW5jZSBvZiBjaXJjdWxhciByZWZlcmVuY2VzIGJ5IGJhaWxpbmcgb3V0IHdoZW4gZW5jb3VudGVyaW5nIGFuXG4vLyBvYmplY3QgdGhhdCBpcyBhbHJlYWR5IG9uIHRoZSBcInN0YWNrXCIgb2YgaXRlbXMgYmVpbmcgcHJvY2Vzc2VkLiBBY2NlcHRzIGFuIG9wdGlvbmFsIHJlcGxhY2VyXG5leHBvcnQgZnVuY3Rpb24gY2Fub25pY2FsaXplKG9iaiwgc3RhY2ssIHJlcGxhY2VtZW50U3RhY2ssIHJlcGxhY2VyLCBrZXkpIHtcbiAgc3RhY2sgPSBzdGFjayB8fCBbXTtcbiAgcmVwbGFjZW1lbnRTdGFjayA9IHJlcGxhY2VtZW50U3RhY2sgfHwgW107XG5cbiAgaWYgKHJlcGxhY2VyKSB7XG4gICAgb2JqID0gcmVwbGFjZXIoa2V5LCBvYmopO1xuICB9XG5cbiAgbGV0IGk7XG5cbiAgZm9yIChpID0gMDsgaSA8IHN0YWNrLmxlbmd0aDsgaSArPSAxKSB7XG4gICAgaWYgKHN0YWNrW2ldID09PSBvYmopIHtcbiAgICAgIHJldHVybiByZXBsYWNlbWVudFN0YWNrW2ldO1xuICAgIH1cbiAgfVxuXG4gIGxldCBjYW5vbmljYWxpemVkT2JqO1xuXG4gIGlmICgnW29iamVjdCBBcnJheV0nID09PSBvYmplY3RQcm90b3R5cGVUb1N0cmluZy5jYWxsKG9iaikpIHtcbiAgICBzdGFjay5wdXNoKG9iaik7XG4gICAgY2Fub25pY2FsaXplZE9iaiA9IG5ldyBBcnJheShvYmoubGVuZ3RoKTtcbiAgICByZXBsYWNlbWVudFN0YWNrLnB1c2goY2Fub25pY2FsaXplZE9iaik7XG4gICAgZm9yIChpID0gMDsgaSA8IG9iai5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgY2Fub25pY2FsaXplZE9ialtpXSA9IGNhbm9uaWNhbGl6ZShvYmpbaV0sIHN0YWNrLCByZXBsYWNlbWVudFN0YWNrLCByZXBsYWNlciwga2V5KTtcbiAgICB9XG4gICAgc3RhY2sucG9wKCk7XG4gICAgcmVwbGFjZW1lbnRTdGFjay5wb3AoKTtcbiAgICByZXR1cm4gY2Fub25pY2FsaXplZE9iajtcbiAgfVxuXG4gIGlmIChvYmogJiYgb2JqLnRvSlNPTikge1xuICAgIG9iaiA9IG9iai50b0pTT04oKTtcbiAgfVxuXG4gIGlmICh0eXBlb2Ygb2JqID09PSAnb2JqZWN0JyAmJiBvYmogIT09IG51bGwpIHtcbiAgICBzdGFjay5wdXNoKG9iaik7XG4gICAgY2Fub25pY2FsaXplZE9iaiA9IHt9O1xuICAgIHJlcGxhY2VtZW50U3RhY2sucHVzaChjYW5vbmljYWxpemVkT2JqKTtcbiAgICBsZXQgc29ydGVkS2V5cyA9IFtdLFxuICAgICAgICBrZXk7XG4gICAgZm9yIChrZXkgaW4gb2JqKSB7XG4gICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgZWxzZSAqL1xuICAgICAgaWYgKG9iai5oYXNPd25Qcm9wZXJ0eShrZXkpKSB7XG4gICAgICAgIHNvcnRlZEtleXMucHVzaChrZXkpO1xuICAgICAgfVxuICAgIH1cbiAgICBzb3J0ZWRLZXlzLnNvcnQoKTtcbiAgICBmb3IgKGkgPSAwOyBpIDwgc29ydGVkS2V5cy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAga2V5ID0gc29ydGVkS2V5c1tpXTtcbiAgICAgIGNhbm9uaWNhbGl6ZWRPYmpba2V5XSA9IGNhbm9uaWNhbGl6ZShvYmpba2V5XSwgc3RhY2ssIHJlcGxhY2VtZW50U3RhY2ssIHJlcGxhY2VyLCBrZXkpO1xuICAgIH1cbiAgICBzdGFjay5wb3AoKTtcbiAgICByZXBsYWNlbWVudFN0YWNrLnBvcCgpO1xuICB9IGVsc2Uge1xuICAgIGNhbm9uaWNhbGl6ZWRPYmogPSBvYmo7XG4gIH1cbiAgcmV0dXJuIGNhbm9uaWNhbGl6ZWRPYmo7XG59XG4iXX0= +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX2xpbmUiLCJvYmoiLCJfX2VzTW9kdWxlIiwiX3R5cGVvZiIsIm8iLCJTeW1ib2wiLCJpdGVyYXRvciIsImNvbnN0cnVjdG9yIiwicHJvdG90eXBlIiwianNvbkRpZmYiLCJleHBvcnRzIiwiRGlmZiIsInVzZUxvbmdlc3RUb2tlbiIsInRva2VuaXplIiwibGluZURpZmYiLCJjYXN0SW5wdXQiLCJ2YWx1ZSIsIm9wdGlvbnMiLCJ1bmRlZmluZWRSZXBsYWNlbWVudCIsIl9vcHRpb25zJHN0cmluZ2lmeVJlcCIsInN0cmluZ2lmeVJlcGxhY2VyIiwiayIsInYiLCJKU09OIiwic3RyaW5naWZ5IiwiY2Fub25pY2FsaXplIiwiZXF1YWxzIiwibGVmdCIsInJpZ2h0IiwiY2FsbCIsInJlcGxhY2UiLCJkaWZmSnNvbiIsIm9sZE9iaiIsIm5ld09iaiIsImRpZmYiLCJzdGFjayIsInJlcGxhY2VtZW50U3RhY2siLCJyZXBsYWNlciIsImtleSIsImkiLCJsZW5ndGgiLCJjYW5vbmljYWxpemVkT2JqIiwiT2JqZWN0IiwidG9TdHJpbmciLCJwdXNoIiwiQXJyYXkiLCJwb3AiLCJ0b0pTT04iLCJzb3J0ZWRLZXlzIiwiaGFzT3duUHJvcGVydHkiLCJzb3J0Il0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RpZmYvanNvbi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgRGlmZiBmcm9tICcuL2Jhc2UnO1xuaW1wb3J0IHtsaW5lRGlmZn0gZnJvbSAnLi9saW5lJztcblxuZXhwb3J0IGNvbnN0IGpzb25EaWZmID0gbmV3IERpZmYoKTtcbi8vIERpc2NyaW1pbmF0ZSBiZXR3ZWVuIHR3byBsaW5lcyBvZiBwcmV0dHktcHJpbnRlZCwgc2VyaWFsaXplZCBKU09OIHdoZXJlIG9uZSBvZiB0aGVtIGhhcyBhXG4vLyBkYW5nbGluZyBjb21tYSBhbmQgdGhlIG90aGVyIGRvZXNuJ3QuIFR1cm5zIG91dCBpbmNsdWRpbmcgdGhlIGRhbmdsaW5nIGNvbW1hIHlpZWxkcyB0aGUgbmljZXN0IG91dHB1dDpcbmpzb25EaWZmLnVzZUxvbmdlc3RUb2tlbiA9IHRydWU7XG5cbmpzb25EaWZmLnRva2VuaXplID0gbGluZURpZmYudG9rZW5pemU7XG5qc29uRGlmZi5jYXN0SW5wdXQgPSBmdW5jdGlvbih2YWx1ZSwgb3B0aW9ucykge1xuICBjb25zdCB7dW5kZWZpbmVkUmVwbGFjZW1lbnQsIHN0cmluZ2lmeVJlcGxhY2VyID0gKGssIHYpID0+IHR5cGVvZiB2ID09PSAndW5kZWZpbmVkJyA/IHVuZGVmaW5lZFJlcGxhY2VtZW50IDogdn0gPSBvcHRpb25zO1xuXG4gIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnID8gdmFsdWUgOiBKU09OLnN0cmluZ2lmeShjYW5vbmljYWxpemUodmFsdWUsIG51bGwsIG51bGwsIHN0cmluZ2lmeVJlcGxhY2VyKSwgc3RyaW5naWZ5UmVwbGFjZXIsICcgICcpO1xufTtcbmpzb25EaWZmLmVxdWFscyA9IGZ1bmN0aW9uKGxlZnQsIHJpZ2h0LCBvcHRpb25zKSB7XG4gIHJldHVybiBEaWZmLnByb3RvdHlwZS5lcXVhbHMuY2FsbChqc29uRGlmZiwgbGVmdC5yZXBsYWNlKC8sKFtcXHJcXG5dKS9nLCAnJDEnKSwgcmlnaHQucmVwbGFjZSgvLChbXFxyXFxuXSkvZywgJyQxJyksIG9wdGlvbnMpO1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIGRpZmZKc29uKG9sZE9iaiwgbmV3T2JqLCBvcHRpb25zKSB7IHJldHVybiBqc29uRGlmZi5kaWZmKG9sZE9iaiwgbmV3T2JqLCBvcHRpb25zKTsgfVxuXG4vLyBUaGlzIGZ1bmN0aW9uIGhhbmRsZXMgdGhlIHByZXNlbmNlIG9mIGNpcmN1bGFyIHJlZmVyZW5jZXMgYnkgYmFpbGluZyBvdXQgd2hlbiBlbmNvdW50ZXJpbmcgYW5cbi8vIG9iamVjdCB0aGF0IGlzIGFscmVhZHkgb24gdGhlIFwic3RhY2tcIiBvZiBpdGVtcyBiZWluZyBwcm9jZXNzZWQuIEFjY2VwdHMgYW4gb3B0aW9uYWwgcmVwbGFjZXJcbmV4cG9ydCBmdW5jdGlvbiBjYW5vbmljYWxpemUob2JqLCBzdGFjaywgcmVwbGFjZW1lbnRTdGFjaywgcmVwbGFjZXIsIGtleSkge1xuICBzdGFjayA9IHN0YWNrIHx8IFtdO1xuICByZXBsYWNlbWVudFN0YWNrID0gcmVwbGFjZW1lbnRTdGFjayB8fCBbXTtcblxuICBpZiAocmVwbGFjZXIpIHtcbiAgICBvYmogPSByZXBsYWNlcihrZXksIG9iaik7XG4gIH1cblxuICBsZXQgaTtcblxuICBmb3IgKGkgPSAwOyBpIDwgc3RhY2subGVuZ3RoOyBpICs9IDEpIHtcbiAgICBpZiAoc3RhY2tbaV0gPT09IG9iaikge1xuICAgICAgcmV0dXJuIHJlcGxhY2VtZW50U3RhY2tbaV07XG4gICAgfVxuICB9XG5cbiAgbGV0IGNhbm9uaWNhbGl6ZWRPYmo7XG5cbiAgaWYgKCdbb2JqZWN0IEFycmF5XScgPT09IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvYmopKSB7XG4gICAgc3RhY2sucHVzaChvYmopO1xuICAgIGNhbm9uaWNhbGl6ZWRPYmogPSBuZXcgQXJyYXkob2JqLmxlbmd0aCk7XG4gICAgcmVwbGFjZW1lbnRTdGFjay5wdXNoKGNhbm9uaWNhbGl6ZWRPYmopO1xuICAgIGZvciAoaSA9IDA7IGkgPCBvYmoubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgIGNhbm9uaWNhbGl6ZWRPYmpbaV0gPSBjYW5vbmljYWxpemUob2JqW2ldLCBzdGFjaywgcmVwbGFjZW1lbnRTdGFjaywgcmVwbGFjZXIsIGtleSk7XG4gICAgfVxuICAgIHN0YWNrLnBvcCgpO1xuICAgIHJlcGxhY2VtZW50U3RhY2sucG9wKCk7XG4gICAgcmV0dXJuIGNhbm9uaWNhbGl6ZWRPYmo7XG4gIH1cblxuICBpZiAob2JqICYmIG9iai50b0pTT04pIHtcbiAgICBvYmogPSBvYmoudG9KU09OKCk7XG4gIH1cblxuICBpZiAodHlwZW9mIG9iaiA9PT0gJ29iamVjdCcgJiYgb2JqICE9PSBudWxsKSB7XG4gICAgc3RhY2sucHVzaChvYmopO1xuICAgIGNhbm9uaWNhbGl6ZWRPYmogPSB7fTtcbiAgICByZXBsYWNlbWVudFN0YWNrLnB1c2goY2Fub25pY2FsaXplZE9iaik7XG4gICAgbGV0IHNvcnRlZEtleXMgPSBbXSxcbiAgICAgICAga2V5O1xuICAgIGZvciAoa2V5IGluIG9iaikge1xuICAgICAgLyogaXN0YW5idWwgaWdub3JlIGVsc2UgKi9cbiAgICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBrZXkpKSB7XG4gICAgICAgIHNvcnRlZEtleXMucHVzaChrZXkpO1xuICAgICAgfVxuICAgIH1cbiAgICBzb3J0ZWRLZXlzLnNvcnQoKTtcbiAgICBmb3IgKGkgPSAwOyBpIDwgc29ydGVkS2V5cy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAga2V5ID0gc29ydGVkS2V5c1tpXTtcbiAgICAgIGNhbm9uaWNhbGl6ZWRPYmpba2V5XSA9IGNhbm9uaWNhbGl6ZShvYmpba2V5XSwgc3RhY2ssIHJlcGxhY2VtZW50U3RhY2ssIHJlcGxhY2VyLCBrZXkpO1xuICAgIH1cbiAgICBzdGFjay5wb3AoKTtcbiAgICByZXBsYWNlbWVudFN0YWNrLnBvcCgpO1xuICB9IGVsc2Uge1xuICAgIGNhbm9uaWNhbGl6ZWRPYmogPSBvYmo7XG4gIH1cbiAgcmV0dXJuIGNhbm9uaWNhbGl6ZWRPYmo7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUFBLEtBQUEsR0FBQUMsc0JBQUEsQ0FBQUMsT0FBQTtBQUFBO0FBQUE7QUFDQTtBQUFBO0FBQUFDLEtBQUEsR0FBQUQsT0FBQTtBQUFBO0FBQUE7QUFBZ0MsbUNBQUFELHVCQUFBRyxHQUFBLFdBQUFBLEdBQUEsSUFBQUEsR0FBQSxDQUFBQyxVQUFBLEdBQUFELEdBQUEsZ0JBQUFBLEdBQUE7QUFBQSxTQUFBRSxRQUFBQyxDQUFBLHNDQUFBRCxPQUFBLHdCQUFBRSxN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 diff --git a/deps/npm/node_modules/diff/lib/diff/line.js b/deps/npm/node_modules/diff/lib/diff/line.js index 30bc74d2383d20..71f3f2471d1094 100644 --- a/deps/npm/node_modules/diff/lib/diff/line.js +++ b/deps/npm/node_modules/diff/lib/diff/line.js @@ -7,24 +7,24 @@ Object.defineProperty(exports, "__esModule", { exports.diffLines = diffLines; exports.diffTrimmedLines = diffTrimmedLines; exports.lineDiff = void 0; - /*istanbul ignore end*/ var /*istanbul ignore start*/ _base = _interopRequireDefault(require("./base")) /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _params = require("../util/params") /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /*istanbul ignore end*/ -var lineDiff = new +var lineDiff = +/*istanbul ignore start*/ +exports.lineDiff = +/*istanbul ignore end*/ +new /*istanbul ignore start*/ _base /*istanbul ignore end*/ @@ -33,52 +33,79 @@ _base "default" /*istanbul ignore end*/ ](); - -/*istanbul ignore start*/ -exports.lineDiff = lineDiff; - -/*istanbul ignore end*/ -lineDiff.tokenize = function (value) { - if (this.options.stripTrailingCr) { +lineDiff.tokenize = function (value, options) { + if (options.stripTrailingCr) { // remove one \r before \n to match GNU diff's --strip-trailing-cr behavior value = value.replace(/\r\n/g, '\n'); } - var retLines = [], - linesAndNewlines = value.split(/(\n|\r\n)/); // Ignore the final empty token that occurs if the string ends with a new line + linesAndNewlines = value.split(/(\n|\r\n)/); + // Ignore the final empty token that occurs if the string ends with a new line if (!linesAndNewlines[linesAndNewlines.length - 1]) { linesAndNewlines.pop(); - } // Merge the content and line separators into single tokens - + } + // Merge the content and line separators into single tokens for (var i = 0; i < linesAndNewlines.length; i++) { var line = linesAndNewlines[i]; - - if (i % 2 && !this.options.newlineIsToken) { + if (i % 2 && !options.newlineIsToken) { retLines[retLines.length - 1] += line; } else { - if (this.options.ignoreWhitespace) { - line = line.trim(); - } - retLines.push(line); } } - return retLines; }; - +lineDiff.equals = function (left, right, options) { + // If we're ignoring whitespace, we need to normalise lines by stripping + // whitespace before checking equality. (This has an annoying interaction + // with newlineIsToken that requires special handling: if newlines get their + // own token, then we DON'T want to trim the *newline* tokens down to empty + // strings, since this would cause us to treat whitespace-only line content + // as equal to a separator between lines, which would be weird and + // inconsistent with the documented behavior of the options.) + if (options.ignoreWhitespace) { + if (!options.newlineIsToken || !left.includes('\n')) { + left = left.trim(); + } + if (!options.newlineIsToken || !right.includes('\n')) { + right = right.trim(); + } + } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) { + if (left.endsWith('\n')) { + left = left.slice(0, -1); + } + if (right.endsWith('\n')) { + right = right.slice(0, -1); + } + } + return ( + /*istanbul ignore start*/ + _base + /*istanbul ignore end*/ + [ + /*istanbul ignore start*/ + "default" + /*istanbul ignore end*/ + ].prototype.equals.call(this, left, right, options) + ); +}; function diffLines(oldStr, newStr, callback) { return lineDiff.diff(oldStr, newStr, callback); } +// Kept for backwards compatibility. This is a rather arbitrary wrapper method +// that just calls `diffLines` with `ignoreWhitespace: true`. It's confusing to +// have two ways to do exactly the same thing in the API, so we no longer +// document this one (library users should explicitly use `diffLines` with +// `ignoreWhitespace: true` instead) but we keep it around to maintain +// compatibility with code that used old versions. function diffTrimmedLines(oldStr, newStr, callback) { var options = /*istanbul ignore start*/ (0, /*istanbul ignore end*/ - /*istanbul ignore start*/ _params /*istanbul ignore end*/ @@ -91,4 +118,4 @@ function diffTrimmedLines(oldStr, newStr, callback) { }); return lineDiff.diff(oldStr, newStr, options); } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2xpbmUuanMiXSwibmFtZXMiOlsibGluZURpZmYiLCJEaWZmIiwidG9rZW5pemUiLCJ2YWx1ZSIsIm9wdGlvbnMiLCJzdHJpcFRyYWlsaW5nQ3IiLCJyZXBsYWNlIiwicmV0TGluZXMiLCJsaW5lc0FuZE5ld2xpbmVzIiwic3BsaXQiLCJsZW5ndGgiLCJwb3AiLCJpIiwibGluZSIsIm5ld2xpbmVJc1Rva2VuIiwiaWdub3JlV2hpdGVzcGFjZSIsInRyaW0iLCJwdXNoIiwiZGlmZkxpbmVzIiwib2xkU3RyIiwibmV3U3RyIiwiY2FsbGJhY2siLCJkaWZmIiwiZGlmZlRyaW1tZWRMaW5lcyIsImdlbmVyYXRlT3B0aW9ucyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQUNBO0FBQUE7QUFBQTtBQUFBO0FBQUE7Ozs7O0FBRU8sSUFBTUEsUUFBUSxHQUFHO0FBQUlDO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBLENBQUosRUFBakI7Ozs7OztBQUNQRCxRQUFRLENBQUNFLFFBQVQsR0FBb0IsVUFBU0MsS0FBVCxFQUFnQjtBQUNsQyxNQUFHLEtBQUtDLE9BQUwsQ0FBYUMsZUFBaEIsRUFBaUM7QUFDL0I7QUFDQUYsSUFBQUEsS0FBSyxHQUFHQSxLQUFLLENBQUNHLE9BQU4sQ0FBYyxPQUFkLEVBQXVCLElBQXZCLENBQVI7QUFDRDs7QUFFRCxNQUFJQyxRQUFRLEdBQUcsRUFBZjtBQUFBLE1BQ0lDLGdCQUFnQixHQUFHTCxLQUFLLENBQUNNLEtBQU4sQ0FBWSxXQUFaLENBRHZCLENBTmtDLENBU2xDOztBQUNBLE1BQUksQ0FBQ0QsZ0JBQWdCLENBQUNBLGdCQUFnQixDQUFDRSxNQUFqQixHQUEwQixDQUEzQixDQUFyQixFQUFvRDtBQUNsREYsSUFBQUEsZ0JBQWdCLENBQUNHLEdBQWpCO0FBQ0QsR0FaaUMsQ0FjbEM7OztBQUNBLE9BQUssSUFBSUMsQ0FBQyxHQUFHLENBQWIsRUFBZ0JBLENBQUMsR0FBR0osZ0JBQWdCLENBQUNFLE1BQXJDLEVBQTZDRSxDQUFDLEVBQTlDLEVBQWtEO0FBQ2hELFFBQUlDLElBQUksR0FBR0wsZ0JBQWdCLENBQUNJLENBQUQsQ0FBM0I7O0FBRUEsUUFBSUEsQ0FBQyxHQUFHLENBQUosSUFBUyxDQUFDLEtBQUtSLE9BQUwsQ0FBYVUsY0FBM0IsRUFBMkM7QUFDekNQLE1BQUFBLFFBQVEsQ0FBQ0EsUUFBUSxDQUFDRyxNQUFULEdBQWtCLENBQW5CLENBQVIsSUFBaUNHLElBQWpDO0FBQ0QsS0FGRCxNQUVPO0FBQ0wsVUFBSSxLQUFLVCxPQUFMLENBQWFXLGdCQUFqQixFQUFtQztBQUNqQ0YsUUFBQUEsSUFBSSxHQUFHQSxJQUFJLENBQUNHLElBQUwsRUFBUDtBQUNEOztBQUNEVCxNQUFBQSxRQUFRLENBQUNVLElBQVQsQ0FBY0osSUFBZDtBQUNEO0FBQ0Y7O0FBRUQsU0FBT04sUUFBUDtBQUNELENBN0JEOztBQStCTyxTQUFTVyxTQUFULENBQW1CQyxNQUFuQixFQUEyQkMsTUFBM0IsRUFBbUNDLFFBQW5DLEVBQTZDO0FBQUUsU0FBT3JCLFFBQVEsQ0FBQ3NCLElBQVQsQ0FBY0gsTUFBZCxFQUFzQkMsTUFBdEIsRUFBOEJDLFFBQTlCLENBQVA7QUFBaUQ7O0FBQ2hHLFNBQVNFLGdCQUFULENBQTBCSixNQUExQixFQUFrQ0MsTUFBbEMsRUFBMENDLFFBQTFDLEVBQW9EO0FBQ3pELE1BQUlqQixPQUFPO0FBQUc7QUFBQTtBQUFBOztBQUFBb0I7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQTtBQUFBLEdBQWdCSCxRQUFoQixFQUEwQjtBQUFDTixJQUFBQSxnQkFBZ0IsRUFBRTtBQUFuQixHQUExQixDQUFkO0FBQ0EsU0FBT2YsUUFBUSxDQUFDc0IsSUFBVCxDQUFjSCxNQUFkLEVBQXNCQyxNQUF0QixFQUE4QmhCLE9BQTlCLENBQVA7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5pbXBvcnQge2dlbmVyYXRlT3B0aW9uc30gZnJvbSAnLi4vdXRpbC9wYXJhbXMnO1xuXG5leHBvcnQgY29uc3QgbGluZURpZmYgPSBuZXcgRGlmZigpO1xubGluZURpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICBpZih0aGlzLm9wdGlvbnMuc3RyaXBUcmFpbGluZ0NyKSB7XG4gICAgLy8gcmVtb3ZlIG9uZSBcXHIgYmVmb3JlIFxcbiB0byBtYXRjaCBHTlUgZGlmZidzIC0tc3RyaXAtdHJhaWxpbmctY3IgYmVoYXZpb3JcbiAgICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UoL1xcclxcbi9nLCAnXFxuJyk7XG4gIH1cblxuICBsZXQgcmV0TGluZXMgPSBbXSxcbiAgICAgIGxpbmVzQW5kTmV3bGluZXMgPSB2YWx1ZS5zcGxpdCgvKFxcbnxcXHJcXG4pLyk7XG5cbiAgLy8gSWdub3JlIHRoZSBmaW5hbCBlbXB0eSB0b2tlbiB0aGF0IG9jY3VycyBpZiB0aGUgc3RyaW5nIGVuZHMgd2l0aCBhIG5ldyBsaW5lXG4gIGlmICghbGluZXNBbmROZXdsaW5lc1tsaW5lc0FuZE5ld2xpbmVzLmxlbmd0aCAtIDFdKSB7XG4gICAgbGluZXNBbmROZXdsaW5lcy5wb3AoKTtcbiAgfVxuXG4gIC8vIE1lcmdlIHRoZSBjb250ZW50IGFuZCBsaW5lIHNlcGFyYXRvcnMgaW50byBzaW5nbGUgdG9rZW5zXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbGluZXNBbmROZXdsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgIGxldCBsaW5lID0gbGluZXNBbmROZXdsaW5lc1tpXTtcblxuICAgIGlmIChpICUgMiAmJiAhdGhpcy5vcHRpb25zLm5ld2xpbmVJc1Rva2VuKSB7XG4gICAgICByZXRMaW5lc1tyZXRMaW5lcy5sZW5ndGggLSAxXSArPSBsaW5lO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAodGhpcy5vcHRpb25zLmlnbm9yZVdoaXRlc3BhY2UpIHtcbiAgICAgICAgbGluZSA9IGxpbmUudHJpbSgpO1xuICAgICAgfVxuICAgICAgcmV0TGluZXMucHVzaChsaW5lKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmV0TGluZXM7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZkxpbmVzKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjaykgeyByZXR1cm4gbGluZURpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spOyB9XG5leHBvcnQgZnVuY3Rpb24gZGlmZlRyaW1tZWRMaW5lcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHtcbiAgbGV0IG9wdGlvbnMgPSBnZW5lcmF0ZU9wdGlvbnMoY2FsbGJhY2ssIHtpZ25vcmVXaGl0ZXNwYWNlOiB0cnVlfSk7XG4gIHJldHVybiBsaW5lRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbn1cbiJdfQ== +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX3BhcmFtcyIsIm9iaiIsIl9fZXNNb2R1bGUiLCJsaW5lRGlmZiIsImV4cG9ydHMiLCJEaWZmIiwidG9rZW5pemUiLCJ2YWx1ZSIsIm9wdGlvbnMiLCJzdHJpcFRyYWlsaW5nQ3IiLCJyZXBsYWNlIiwicmV0TGluZXMiLCJsaW5lc0FuZE5ld2xpbmVzIiwic3BsaXQiLCJsZW5ndGgiLCJwb3AiLCJpIiwibGluZSIsIm5ld2xpbmVJc1Rva2VuIiwicHVzaCIsImVxdWFscyIsImxlZnQiLCJyaWdodCIsImlnbm9yZVdoaXRlc3BhY2UiLCJpbmNsdWRlcyIsInRyaW0iLCJpZ25vcmVOZXdsaW5lQXRFb2YiLCJlbmRzV2l0aCIsInNsaWNlIiwicHJvdG90eXBlIiwiY2FsbCIsImRpZmZMaW5lcyIsIm9sZFN0ciIsIm5ld1N0ciIsImNhbGxiYWNrIiwiZGlmZiIsImRpZmZUcmltbWVkTGluZXMiLCJnZW5lcmF0ZU9wdGlvbnMiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvZGlmZi9saW5lLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5pbXBvcnQge2dlbmVyYXRlT3B0aW9uc30gZnJvbSAnLi4vdXRpbC9wYXJhbXMnO1xuXG5leHBvcnQgY29uc3QgbGluZURpZmYgPSBuZXcgRGlmZigpO1xubGluZURpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSwgb3B0aW9ucykge1xuICBpZihvcHRpb25zLnN0cmlwVHJhaWxpbmdDcikge1xuICAgIC8vIHJlbW92ZSBvbmUgXFxyIGJlZm9yZSBcXG4gdG8gbWF0Y2ggR05VIGRpZmYncyAtLXN0cmlwLXRyYWlsaW5nLWNyIGJlaGF2aW9yXG4gICAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKC9cXHJcXG4vZywgJ1xcbicpO1xuICB9XG5cbiAgbGV0IHJldExpbmVzID0gW10sXG4gICAgICBsaW5lc0FuZE5ld2xpbmVzID0gdmFsdWUuc3BsaXQoLyhcXG58XFxyXFxuKS8pO1xuXG4gIC8vIElnbm9yZSB0aGUgZmluYWwgZW1wdHkgdG9rZW4gdGhhdCBvY2N1cnMgaWYgdGhlIHN0cmluZyBlbmRzIHdpdGggYSBuZXcgbGluZVxuICBpZiAoIWxpbmVzQW5kTmV3bGluZXNbbGluZXNBbmROZXdsaW5lcy5sZW5ndGggLSAxXSkge1xuICAgIGxpbmVzQW5kTmV3bGluZXMucG9wKCk7XG4gIH1cblxuICAvLyBNZXJnZSB0aGUgY29udGVudCBhbmQgbGluZSBzZXBhcmF0b3JzIGludG8gc2luZ2xlIHRva2Vuc1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGxpbmVzQW5kTmV3bGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICBsZXQgbGluZSA9IGxpbmVzQW5kTmV3bGluZXNbaV07XG5cbiAgICBpZiAoaSAlIDIgJiYgIW9wdGlvbnMubmV3bGluZUlzVG9rZW4pIHtcbiAgICAgIHJldExpbmVzW3JldExpbmVzLmxlbmd0aCAtIDFdICs9IGxpbmU7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldExpbmVzLnB1c2gobGluZSk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHJldExpbmVzO1xufTtcblxubGluZURpZmYuZXF1YWxzID0gZnVuY3Rpb24obGVmdCwgcmlnaHQsIG9wdGlvbnMpIHtcbiAgLy8gSWYgd2UncmUgaWdub3Jpbmcgd2hpdGVzcGFjZSwgd2UgbmVlZCB0byBub3JtYWxpc2UgbGluZXMgYnkgc3RyaXBwaW5nXG4gIC8vIHdoaXRlc3BhY2UgYmVmb3JlIGNoZWNraW5nIGVxdWFsaXR5LiAoVGhpcyBoYXMgYW4gYW5ub3lpbmcgaW50ZXJhY3Rpb25cbiAgLy8gd2l0aCBuZXdsaW5lSXNUb2tlbiB0aGF0IHJlcXVpcmVzIHNwZWNpYWwgaGFuZGxpbmc6IGlmIG5ld2xpbmVzIGdldCB0aGVpclxuICAvLyBvd24gdG9rZW4sIHRoZW4gd2UgRE9OJ1Qgd2FudCB0byB0cmltIHRoZSAqbmV3bGluZSogdG9rZW5zIGRvd24gdG8gZW1wdHlcbiAgLy8gc3RyaW5ncywgc2luY2UgdGhpcyB3b3VsZCBjYXVzZSB1cyB0byB0cmVhdCB3aGl0ZXNwYWNlLW9ubHkgbGluZSBjb250ZW50XG4gIC8vIGFzIGVxdWFsIHRvIGEgc2VwYXJhdG9yIGJldHdlZW4gbGluZXMsIHdoaWNoIHdvdWxkIGJlIHdlaXJkIGFuZFxuICAvLyBpbmNvbnNpc3RlbnQgd2l0aCB0aGUgZG9jdW1lbnRlZCBiZWhhdmlvciBvZiB0aGUgb3B0aW9ucy4pXG4gIGlmIChvcHRpb25zLmlnbm9yZVdoaXRlc3BhY2UpIHtcbiAgICBpZiAoIW9wdGlvbnMubmV3bGluZUlzVG9rZW4gfHwgIWxlZnQuaW5jbHVkZXMoJ1xcbicpKSB7XG4gICAgICBsZWZ0ID0gbGVmdC50cmltKCk7XG4gICAgfVxuICAgIGlmICghb3B0aW9ucy5uZXdsaW5lSXNUb2tlbiB8fCAhcmlnaHQuaW5jbHVkZXMoJ1xcbicpKSB7XG4gICAgICByaWdodCA9IHJpZ2h0LnRyaW0oKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAob3B0aW9ucy5pZ25vcmVOZXdsaW5lQXRFb2YgJiYgIW9wdGlvbnMubmV3bGluZUlzVG9rZW4pIHtcbiAgICBpZiAobGVmdC5lbmRzV2l0aCgnXFxuJykpIHtcbiAgICAgIGxlZnQgPSBsZWZ0LnNsaWNlKDAsIC0xKTtcbiAgICB9XG4gICAgaWYgKHJpZ2h0LmVuZHNXaXRoKCdcXG4nKSkge1xuICAgICAgcmlnaHQgPSByaWdodC5zbGljZSgwLCAtMSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBEaWZmLnByb3RvdHlwZS5lcXVhbHMuY2FsbCh0aGlzLCBsZWZ0LCByaWdodCwgb3B0aW9ucyk7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZkxpbmVzKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjaykgeyByZXR1cm4gbGluZURpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spOyB9XG5cbi8vIEtlcHQgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5LiBUaGlzIGlzIGEgcmF0aGVyIGFyYml0cmFyeSB3cmFwcGVyIG1ldGhvZFxuLy8gdGhhdCBqdXN0IGNhbGxzIGBkaWZmTGluZXNgIHdpdGggYGlnbm9yZVdoaXRlc3BhY2U6IHRydWVgLiBJdCdzIGNvbmZ1c2luZyB0b1xuLy8gaGF2ZSB0d28gd2F5cyB0byBkbyBleGFjdGx5IHRoZSBzYW1lIHRoaW5nIGluIHRoZSBBUEksIHNvIHdlIG5vIGxvbmdlclxuLy8gZG9jdW1lbnQgdGhpcyBvbmUgKGxpYnJhcnkgdXNlcnMgc2hvdWxkIGV4cGxpY2l0bHkgdXNlIGBkaWZmTGluZXNgIHdpdGhcbi8vIGBpZ25vcmVXaGl0ZXNwYWNlOiB0cnVlYCBpbnN0ZWFkKSBidXQgd2Uga2VlcCBpdCBhcm91bmQgdG8gbWFpbnRhaW5cbi8vIGNvbXBhdGliaWxpdHkgd2l0aCBjb2RlIHRoYXQgdXNlZCBvbGQgdmVyc2lvbnMuXG5leHBvcnQgZnVuY3Rpb24gZGlmZlRyaW1tZWRMaW5lcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHtcbiAgbGV0IG9wdGlvbnMgPSBnZW5lcmF0ZU9wdGlvbnMoY2FsbGJhY2ssIHtpZ25vcmVXaGl0ZXNwYWNlOiB0cnVlfSk7XG4gIHJldHVybiBsaW5lRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQUEsS0FBQSxHQUFBQyxzQkFBQSxDQUFBQyxPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQUMsT0FBQSxHQUFBRCxPQUFBO0FBQUE7QUFBQTtBQUErQyxtQ0FBQUQsdUJBQUFHLEdBQUEsV0FBQUEsR0FBQSxJQUFBQSxHQUFBLENBQUFDLFVBQUEsR0FBQUQsR0FBQSxnQkFBQUEsR0FBQTtBQUFBO0FBRXhDLElBQU1FLFFBQVE7QUFBQTtBQUFBQyxPQUFBLENBQUFELFFBQUE7QUFBQTtBQUFHO0FBQUlFO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBLENBQUksQ0FBQyxDQUFDO0FBQ2xDRixRQUFRLENBQUNHLFFBQVEsR0FBRyxVQUFTQyxLQUFLLEVBQUVDLE9BQU8sRUFBRTtFQUMzQyxJQUFHQSxPQUFPLENBQUNDLGVBQWUsRUFBRTtJQUMxQjtJQUNBRixLQUFLLEdBQUdBLEtBQUssQ0FBQ0csT0FBTyxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUM7RUFDdEM7RUFFQSxJQUFJQyxRQUFRLEdBQUcsRUFBRTtJQUNiQyxnQkFBZ0IsR0FBR0wsS0FBSyxDQUFDTSxLQUFLLENBQUMsV0FBVyxDQUFDOztFQUUvQztFQUNBLElBQUksQ0FBQ0QsZ0JBQWdCLENBQUNBLGdCQUFnQixDQUFDRSxNQUFNLEdBQUcsQ0FBQyxDQUFDLEVBQUU7SUFDbERGLGdCQUFnQixDQUFDRyxHQUFHLENBQUMsQ0FBQztFQUN4Qjs7RUFFQTtFQUNBLEtBQUssSUFBSUMsQ0FBQyxHQUFHLENBQUMsRUFBRUEsQ0FBQyxHQUFHSixnQkFBZ0IsQ0FBQ0UsTUFBTSxFQUFFRSxDQUFDLEVBQUUsRUFBRTtJQUNoRCxJQUFJQyxJQUFJLEdBQUdMLGdCQUFnQixDQUFDSSxDQUFDLENBQUM7SUFFOUIsSUFBSUEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDUixPQUFPLENBQUNVLGNBQWMsRUFBRTtNQUNwQ1AsUUFBUSxDQUFDQSxRQUFRLENBQUNHLE1BQU0sR0FBRyxDQUFDLENBQUMsSUFBSUcsSUFBSTtJQUN2QyxDQUFDLE1BQU07TUFDTE4sUUFBUSxDQUFDUSxJQUFJLENBQUNGLElBQUksQ0FBQztJQUNyQjtFQUNGO0VBRUEsT0FBT04sUUFBUTtBQUNqQixDQUFDO0FBRURSLFFBQVEsQ0FBQ2lCLE1BQU0sR0FBRyxVQUFTQyxJQUFJLEVBQUVDLEtBQUssRUFBRWQsT0FBTyxFQUFFO0VBQy9DO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsSUFBSUEsT0FBTyxDQUFDZSxnQkFBZ0IsRUFBRTtJQUM1QixJQUFJLENBQUNmLE9BQU8sQ0FBQ1UsY0FBYyxJQUFJLENBQUNHLElBQUksQ0FBQ0csUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFO01BQ25ESCxJQUFJLEdBQUdBLElBQUksQ0FBQ0ksSUFBSSxDQUFDLENBQUM7SUFDcEI7SUFDQSxJQUFJLENBQUNqQixPQUFPLENBQUNVLGNBQWMsSUFBSSxDQUFDSSxLQUFLLENBQUNFLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRTtNQUNwREYsS0FBSyxHQUFHQSxLQUFLLENBQUNHLElBQUksQ0FBQyxDQUFDO0lBQ3RCO0VBQ0YsQ0FBQyxNQUFNLElBQUlqQixPQUFPLENBQUNrQixrQkFBa0IsSUFBSSxDQUFDbEIsT0FBTyxDQUFDVSxjQUFjLEVBQUU7SUFDaEUsSUFBSUcsSUFBSSxDQUFDTSxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUU7TUFDdkJOLElBQUksR0FBR0EsSUFBSSxDQUFDTyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzFCO0lBQ0EsSUFBSU4sS0FBSyxDQUFDSyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUU7TUFDeEJMLEtBQUssR0FBR0EsS0FBSyxDQUFDTSxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzVCO0VBQ0Y7RUFDQSxPQUFPdkI7SUFBQUE7SUFBQUE7SUFBQUE7SUFBQUE7SUFBQUE7SUFBQUE7SUFBQUE7SUFBQUEsQ0FBSSxDQUFDd0IsU0FBUyxDQUFDVCxNQUFNLENBQUNVLElBQUksQ0FBQyxJQUFJLEVBQUVULElBQUksRUFBRUMsS0FBSyxFQUFFZCxPQUFPO0VBQUM7QUFDL0QsQ0FBQztBQUVNLFNBQVN1QixTQUFTQSxDQUFDQyxNQUFNLEVBQUVDLE1BQU0sRUFBRUMsUUFBUSxFQUFFO0VBQUUsT0FBTy9CLFFBQVEsQ0FBQ2dDLElBQUksQ0FBQ0gsTUFBTSxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsQ0FBQztBQUFFOztBQUV0RztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDTyxTQUFTRSxnQkFBZ0JBLENBQUNKLE1BQU0sRUFBRUMsTUFBTSxFQUFFQyxRQUFRLEVBQUU7RUFDekQsSUFBSTFCLE9BQU87RUFBRztFQUFBO0VBQUE7RUFBQTZCO0VBQUFBO0VBQUFBO0VBQUFBO0VBQUFBO0VBQUFBLGVBQWU7RUFBQTtFQUFBLENBQUNILFFBQVEsRUFBRTtJQUFDWCxnQkFBZ0IsRUFBRTtFQUFJLENBQUMsQ0FBQztFQUNqRSxPQUFPcEIsUUFBUSxDQUFDZ0MsSUFBSSxDQUFDSCxNQUFNLEVBQUVDLE1BQU0sRUFBRXpCLE9BQU8sQ0FBQztBQUMvQyIsImlnbm9yZUxpc3QiOltdfQ== diff --git a/deps/npm/node_modules/diff/lib/diff/sentence.js b/deps/npm/node_modules/diff/lib/diff/sentence.js index 95158d6f58f9f9..66d8ece2669383 100644 --- a/deps/npm/node_modules/diff/lib/diff/sentence.js +++ b/deps/npm/node_modules/diff/lib/diff/sentence.js @@ -6,18 +6,19 @@ Object.defineProperty(exports, "__esModule", { }); exports.diffSentences = diffSentences; exports.sentenceDiff = void 0; - /*istanbul ignore end*/ var /*istanbul ignore start*/ _base = _interopRequireDefault(require("./base")) /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /*istanbul ignore end*/ -var sentenceDiff = new +var sentenceDiff = +/*istanbul ignore start*/ +exports.sentenceDiff = +/*istanbul ignore end*/ +new /*istanbul ignore start*/ _base /*istanbul ignore end*/ @@ -26,16 +27,10 @@ _base "default" /*istanbul ignore end*/ ](); - -/*istanbul ignore start*/ -exports.sentenceDiff = sentenceDiff; - -/*istanbul ignore end*/ sentenceDiff.tokenize = function (value) { return value.split(/(\S.+?[.!?])(?=\s+|$)/); }; - function diffSentences(oldStr, newStr, callback) { return sentenceDiff.diff(oldStr, newStr, callback); } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL3NlbnRlbmNlLmpzIl0sIm5hbWVzIjpbInNlbnRlbmNlRGlmZiIsIkRpZmYiLCJ0b2tlbml6ZSIsInZhbHVlIiwic3BsaXQiLCJkaWZmU2VudGVuY2VzIiwib2xkU3RyIiwibmV3U3RyIiwiY2FsbGJhY2siLCJkaWZmIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7Ozs7QUFHTyxJQUFNQSxZQUFZLEdBQUc7QUFBSUM7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUEsQ0FBSixFQUFyQjs7Ozs7O0FBQ1BELFlBQVksQ0FBQ0UsUUFBYixHQUF3QixVQUFTQyxLQUFULEVBQWdCO0FBQ3RDLFNBQU9BLEtBQUssQ0FBQ0MsS0FBTixDQUFZLHVCQUFaLENBQVA7QUFDRCxDQUZEOztBQUlPLFNBQVNDLGFBQVQsQ0FBdUJDLE1BQXZCLEVBQStCQyxNQUEvQixFQUF1Q0MsUUFBdkMsRUFBaUQ7QUFBRSxTQUFPUixZQUFZLENBQUNTLElBQWIsQ0FBa0JILE1BQWxCLEVBQTBCQyxNQUExQixFQUFrQ0MsUUFBbEMsQ0FBUDtBQUFxRCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cblxuZXhwb3J0IGNvbnN0IHNlbnRlbmNlRGlmZiA9IG5ldyBEaWZmKCk7XG5zZW50ZW5jZURpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWUuc3BsaXQoLyhcXFMuKz9bLiE/XSkoPz1cXHMrfCQpLyk7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZlNlbnRlbmNlcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHsgcmV0dXJuIHNlbnRlbmNlRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjayk7IH1cbiJdfQ== +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwib2JqIiwiX19lc01vZHVsZSIsInNlbnRlbmNlRGlmZiIsImV4cG9ydHMiLCJEaWZmIiwidG9rZW5pemUiLCJ2YWx1ZSIsInNwbGl0IiwiZGlmZlNlbnRlbmNlcyIsIm9sZFN0ciIsIm5ld1N0ciIsImNhbGxiYWNrIiwiZGlmZiJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL3NlbnRlbmNlLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cblxuZXhwb3J0IGNvbnN0IHNlbnRlbmNlRGlmZiA9IG5ldyBEaWZmKCk7XG5zZW50ZW5jZURpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWUuc3BsaXQoLyhcXFMuKz9bLiE/XSkoPz1cXHMrfCQpLyk7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZlNlbnRlbmNlcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHsgcmV0dXJuIHNlbnRlbmNlRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjayk7IH1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBQSxLQUFBLEdBQUFDLHNCQUFBLENBQUFDLE9BQUE7QUFBQTtBQUFBO0FBQTBCLG1DQUFBRCx1QkFBQUUsR0FBQSxXQUFBQSxHQUFBLElBQUFBLEdBQUEsQ0FBQUMsVUFBQSxHQUFBRCxHQUFBLGdCQUFBQSxHQUFBO0FBQUE7QUFHbkIsSUFBTUUsWUFBWTtBQUFBO0FBQUFDLE9BQUEsQ0FBQUQsWUFBQTtBQUFBO0FBQUc7QUFBSUU7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUEsQ0FBSSxDQUFDLENBQUM7QUFDdENGLFlBQVksQ0FBQ0csUUFBUSxHQUFHLFVBQVNDLEtBQUssRUFBRTtFQUN0QyxPQUFPQSxLQUFLLENBQUNDLEtBQUssQ0FBQyx1QkFBdUIsQ0FBQztBQUM3QyxDQUFDO0FBRU0sU0FBU0MsYUFBYUEsQ0FBQ0MsTUFBTSxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsRUFBRTtFQUFFLE9BQU9ULFlBQVksQ0FBQ1UsSUFBSSxDQUFDSCxNQUFNLEVBQUVDLE1BQU0sRUFBRUMsUUFBUSxDQUFDO0FBQUUiLCJpZ25vcmVMaXN0IjpbXX0= diff --git a/deps/npm/node_modules/diff/lib/diff/word.js b/deps/npm/node_modules/diff/lib/diff/word.js index cef7fe17befe60..64919db4f6ff9e 100644 --- a/deps/npm/node_modules/diff/lib/diff/word.js +++ b/deps/npm/node_modules/diff/lib/diff/word.js @@ -6,23 +6,19 @@ Object.defineProperty(exports, "__esModule", { }); exports.diffWords = diffWords; exports.diffWordsWithSpace = diffWordsWithSpace; -exports.wordDiff = void 0; - +exports.wordWithSpaceDiff = exports.wordDiff = void 0; /*istanbul ignore end*/ var /*istanbul ignore start*/ _base = _interopRequireDefault(require("./base")) /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ -_params = require("../util/params") +_string = require("../util/string") /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /*istanbul ignore end*/ // Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode // @@ -42,9 +38,43 @@ _params = require("../util/params") // - U+02DC ˜ ˜ Small Tilde // - U+02DD ˝ ˝ Double Acute Accent // Latin Extended Additional, 1E00–1EFF -var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; -var reWhitespace = /\S/; -var wordDiff = new +var extendedWordChars = "a-zA-Z0-9_\\u{C0}-\\u{FF}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}"; + +// Each token is one of the following: +// - A punctuation mark plus the surrounding whitespace +// - A word plus the surrounding whitespace +// - Pure whitespace (but only in the special case where this the entire text +// is just whitespace) +// +// We have to include surrounding whitespace in the tokens because the two +// alternative approaches produce horribly broken results: +// * If we just discard the whitespace, we can't fully reproduce the original +// text from the sequence of tokens and any attempt to render the diff will +// get the whitespace wrong. +// * If we have separate tokens for whitespace, then in a typical text every +// second token will be a single space character. But this often results in +// the optimal diff between two texts being a perverse one that preserves +// the spaces between words but deletes and reinserts actual common words. +// See https://github.com/kpdecker/jsdiff/issues/160#issuecomment-1866099640 +// for an example. +// +// Keeping the surrounding whitespace of course has implications for .equals +// and .join, not just .tokenize. + +// This regex does NOT fully implement the tokenization rules described above. +// Instead, it gives runs of whitespace their own "token". The tokenize method +// then handles stitching whitespace tokens onto adjacent word or punctuation +// tokens. +var tokenizeIncludingWhitespace = new RegExp( +/*istanbul ignore start*/ +"[".concat( +/*istanbul ignore end*/ +extendedWordChars, "]+|\\s+|[^").concat(extendedWordChars, "]"), 'ug'); +var wordDiff = +/*istanbul ignore start*/ +exports.wordDiff = +/*istanbul ignore end*/ +new /*istanbul ignore start*/ _base /*istanbul ignore end*/ @@ -53,56 +83,461 @@ _base "default" /*istanbul ignore end*/ ](); - -/*istanbul ignore start*/ -exports.wordDiff = wordDiff; - -/*istanbul ignore end*/ -wordDiff.equals = function (left, right) { - if (this.options.ignoreCase) { +wordDiff.equals = function (left, right, options) { + if (options.ignoreCase) { left = left.toLowerCase(); right = right.toLowerCase(); } - - return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); + return left.trim() === right.trim(); }; - wordDiff.tokenize = function (value) { - // All whitespace symbols except newline group into one token, each newline - in separate token - var tokens = value.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/); // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. - - for (var i = 0; i < tokens.length - 1; i++) { - // If we have an empty string in the next field and we have only word chars before and after, merge - if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { - tokens[i] += tokens[i + 2]; - tokens.splice(i + 1, 2); - i--; + /*istanbul ignore start*/ + var + /*istanbul ignore end*/ + options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var parts; + if (options.intlSegmenter) { + if (options.intlSegmenter.resolvedOptions().granularity != 'word') { + throw new Error('The segmenter passed must have a granularity of "word"'); } + parts = Array.from(options.intlSegmenter.segment(value), function (segment) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + segment.segment + ); + }); + } else { + parts = value.match(tokenizeIncludingWhitespace) || []; } - + var tokens = []; + var prevPart = null; + parts.forEach(function (part) { + if (/\s/.test(part)) { + if (prevPart == null) { + tokens.push(part); + } else { + tokens.push(tokens.pop() + part); + } + } else if (/\s/.test(prevPart)) { + if (tokens[tokens.length - 1] == prevPart) { + tokens.push(tokens.pop() + part); + } else { + tokens.push(prevPart + part); + } + } else { + tokens.push(part); + } + prevPart = part; + }); return tokens; }; - +wordDiff.join = function (tokens) { + // Tokens being joined here will always have appeared consecutively in the + // same text, so we can simply strip off the leading whitespace from all the + // tokens except the first (and except any whitespace-only tokens - but such + // a token will always be the first and only token anyway) and then join them + // and the whitespace around words and punctuation will end up correct. + return tokens.map(function (token, i) { + if (i == 0) { + return token; + } else { + return token.replace(/^\s+/, ''); + } + }).join(''); +}; +wordDiff.postProcess = function (changes, options) { + if (!changes || options.oneChangePerToken) { + return changes; + } + var lastKeep = null; + // Change objects representing any insertion or deletion since the last + // "keep" change object. There can be at most one of each. + var insertion = null; + var deletion = null; + changes.forEach(function (change) { + if (change.added) { + insertion = change; + } else if (change.removed) { + deletion = change; + } else { + if (insertion || deletion) { + // May be false at start of text + dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, change); + } + lastKeep = change; + insertion = null; + deletion = null; + } + }); + if (insertion || deletion) { + dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, null); + } + return changes; +}; function diffWords(oldStr, newStr, options) { - options = - /*istanbul ignore start*/ - (0, - /*istanbul ignore end*/ - + // This option has never been documented and never will be (it's clearer to + // just call `diffWordsWithSpace` directly if you need that behavior), but + // has existed in jsdiff for a long time, so we retain support for it here + // for the sake of backwards compatibility. + if ( /*istanbul ignore start*/ - _params + ( /*istanbul ignore end*/ - . - /*istanbul ignore start*/ - generateOptions) - /*istanbul ignore end*/ - (options, { - ignoreWhitespace: true - }); + options === null || options === void 0 ? void 0 : options.ignoreWhitespace) != null && !options.ignoreWhitespace) { + return diffWordsWithSpace(oldStr, newStr, options); + } return wordDiff.diff(oldStr, newStr, options); } +function dedupeWhitespaceInChangeObjects(startKeep, deletion, insertion, endKeep) { + // Before returning, we tidy up the leading and trailing whitespace of the + // change objects to eliminate cases where trailing whitespace in one object + // is repeated as leading whitespace in the next. + // Below are examples of the outcomes we want here to explain the code. + // I=insert, K=keep, D=delete + // 1. diffing 'foo bar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz' + // After cleanup, we want: K:'foo ' D:'bar ' K:'baz' + // + // 2. Diffing 'foo bar baz' vs 'foo qux baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' I:' qux ' K:' baz' + // After cleanup, we want K:'foo ' D:'bar' I:'qux' K:' baz' + // + // 3. Diffing 'foo\nbar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:'\nbar ' K:' baz' + // After cleanup, we want K'foo' D:'\nbar' K:' baz' + // + // 4. Diffing 'foo baz' vs 'foo\nbar baz' + // Prior to cleanup, we have K:'foo\n' I:'\nbar ' K:' baz' + // After cleanup, we ideally want K'foo' I:'\nbar' K:' baz' + // but don't actually manage this currently (the pre-cleanup change + // objects don't contain enough information to make it possible). + // + // 5. Diffing 'foo bar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz' + // After cleanup, we want K:'foo ' D:' bar ' K:'baz' + // + // Our handling is unavoidably imperfect in the case where there's a single + // indel between keeps and the whitespace has changed. For instance, consider + // diffing 'foo\tbar\nbaz' vs 'foo baz'. Unless we create an extra change + // object to represent the insertion of the space character (which isn't even + // a token), we have no way to avoid losing information about the texts' + // original whitespace in the result we return. Still, we do our best to + // output something that will look sensible if we e.g. print it with + // insertions in green and deletions in red. + // Between two "keep" change objects (or before the first or after the last + // change object), we can have either: + // * A "delete" followed by an "insert" + // * Just an "insert" + // * Just a "delete" + // We handle the three cases separately. + if (deletion && insertion) { + var oldWsPrefix = deletion.value.match(/^\s*/)[0]; + var oldWsSuffix = deletion.value.match(/\s*$/)[0]; + var newWsPrefix = insertion.value.match(/^\s*/)[0]; + var newWsSuffix = insertion.value.match(/\s*$/)[0]; + if (startKeep) { + var commonWsPrefix = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + longestCommonPrefix) + /*istanbul ignore end*/ + (oldWsPrefix, newWsPrefix); + startKeep.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + replaceSuffix) + /*istanbul ignore end*/ + (startKeep.value, newWsPrefix, commonWsPrefix); + deletion.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + removePrefix) + /*istanbul ignore end*/ + (deletion.value, commonWsPrefix); + insertion.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + removePrefix) + /*istanbul ignore end*/ + (insertion.value, commonWsPrefix); + } + if (endKeep) { + var commonWsSuffix = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + longestCommonSuffix) + /*istanbul ignore end*/ + (oldWsSuffix, newWsSuffix); + endKeep.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + replacePrefix) + /*istanbul ignore end*/ + (endKeep.value, newWsSuffix, commonWsSuffix); + deletion.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + removeSuffix) + /*istanbul ignore end*/ + (deletion.value, commonWsSuffix); + insertion.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + removeSuffix) + /*istanbul ignore end*/ + (insertion.value, commonWsSuffix); + } + } else if (insertion) { + // The whitespaces all reflect what was in the new text rather than + // the old, so we essentially have no information about whitespace + // insertion or deletion. We just want to dedupe the whitespace. + // We do that by having each change object keep its trailing + // whitespace and deleting duplicate leading whitespace where + // present. + if (startKeep) { + insertion.value = insertion.value.replace(/^\s*/, ''); + } + if (endKeep) { + endKeep.value = endKeep.value.replace(/^\s*/, ''); + } + // otherwise we've got a deletion and no insertion + } else if (startKeep && endKeep) { + var newWsFull = endKeep.value.match(/^\s*/)[0], + delWsStart = deletion.value.match(/^\s*/)[0], + delWsEnd = deletion.value.match(/\s*$/)[0]; + + // Any whitespace that comes straight after startKeep in both the old and + // new texts, assign to startKeep and remove from the deletion. + var newWsStart = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + longestCommonPrefix) + /*istanbul ignore end*/ + (newWsFull, delWsStart); + deletion.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + removePrefix) + /*istanbul ignore end*/ + (deletion.value, newWsStart); + + // Any whitespace that comes straight before endKeep in both the old and + // new texts, and hasn't already been assigned to startKeep, assign to + // endKeep and remove from the deletion. + var newWsEnd = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + longestCommonSuffix) + /*istanbul ignore end*/ + ( + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + removePrefix) + /*istanbul ignore end*/ + (newWsFull, newWsStart), delWsEnd); + deletion.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + removeSuffix) + /*istanbul ignore end*/ + (deletion.value, newWsEnd); + endKeep.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + replacePrefix) + /*istanbul ignore end*/ + (endKeep.value, newWsFull, newWsEnd); + + // If there's any whitespace from the new text that HASN'T already been + // assigned, assign it to the start: + startKeep.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + replaceSuffix) + /*istanbul ignore end*/ + (startKeep.value, newWsFull, newWsFull.slice(0, newWsFull.length - newWsEnd.length)); + } else if (endKeep) { + // We are at the start of the text. Preserve all the whitespace on + // endKeep, and just remove whitespace from the end of deletion to the + // extent that it overlaps with the start of endKeep. + var endKeepWsPrefix = endKeep.value.match(/^\s*/)[0]; + var deletionWsSuffix = deletion.value.match(/\s*$/)[0]; + var overlap = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + maximumOverlap) + /*istanbul ignore end*/ + (deletionWsSuffix, endKeepWsPrefix); + deletion.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + removeSuffix) + /*istanbul ignore end*/ + (deletion.value, overlap); + } else if (startKeep) { + // We are at the END of the text. Preserve all the whitespace on + // startKeep, and just remove whitespace from the start of deletion to + // the extent that it overlaps with the end of startKeep. + var startKeepWsSuffix = startKeep.value.match(/\s*$/)[0]; + var deletionWsPrefix = deletion.value.match(/^\s*/)[0]; + var _overlap = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + maximumOverlap) + /*istanbul ignore end*/ + (startKeepWsSuffix, deletionWsPrefix); + deletion.value = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + removePrefix) + /*istanbul ignore end*/ + (deletion.value, _overlap); + } +} +var wordWithSpaceDiff = +/*istanbul ignore start*/ +exports.wordWithSpaceDiff = +/*istanbul ignore end*/ +new +/*istanbul ignore start*/ +_base +/*istanbul ignore end*/ +[ +/*istanbul ignore start*/ +"default" +/*istanbul ignore end*/ +](); +wordWithSpaceDiff.tokenize = function (value) { + // Slightly different to the tokenizeIncludingWhitespace regex used above in + // that this one treats each individual newline as a distinct tokens, rather + // than merging them into other surrounding whitespace. This was requested + // in https://github.com/kpdecker/jsdiff/issues/180 & + // https://github.com/kpdecker/jsdiff/issues/211 + var regex = new RegExp( + /*istanbul ignore start*/ + "(\\r?\\n)|[".concat( + /*istanbul ignore end*/ + extendedWordChars, "]+|[^\\S\\n\\r]+|[^").concat(extendedWordChars, "]"), 'ug'); + return value.match(regex) || []; +}; function diffWordsWithSpace(oldStr, newStr, options) { - return wordDiff.diff(oldStr, newStr, options); + return wordWithSpaceDiff.diff(oldStr, newStr, options); } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL3dvcmQuanMiXSwibmFtZXMiOlsiZXh0ZW5kZWRXb3JkQ2hhcnMiLCJyZVdoaXRlc3BhY2UiLCJ3b3JkRGlmZiIsIkRpZmYiLCJlcXVhbHMiLCJsZWZ0IiwicmlnaHQiLCJvcHRpb25zIiwiaWdub3JlQ2FzZSIsInRvTG93ZXJDYXNlIiwiaWdub3JlV2hpdGVzcGFjZSIsInRlc3QiLCJ0b2tlbml6ZSIsInZhbHVlIiwidG9rZW5zIiwic3BsaXQiLCJpIiwibGVuZ3RoIiwic3BsaWNlIiwiZGlmZldvcmRzIiwib2xkU3RyIiwibmV3U3RyIiwiZ2VuZXJhdGVPcHRpb25zIiwiZGlmZiIsImRpZmZXb3Jkc1dpdGhTcGFjZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQUNBO0FBQUE7QUFBQTtBQUFBO0FBQUE7Ozs7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBTUEsaUJBQWlCLEdBQUcsK0RBQTFCO0FBRUEsSUFBTUMsWUFBWSxHQUFHLElBQXJCO0FBRU8sSUFBTUMsUUFBUSxHQUFHO0FBQUlDO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBLENBQUosRUFBakI7Ozs7OztBQUNQRCxRQUFRLENBQUNFLE1BQVQsR0FBa0IsVUFBU0MsSUFBVCxFQUFlQyxLQUFmLEVBQXNCO0FBQ3RDLE1BQUksS0FBS0MsT0FBTCxDQUFhQyxVQUFqQixFQUE2QjtBQUMzQkgsSUFBQUEsSUFBSSxHQUFHQSxJQUFJLENBQUNJLFdBQUwsRUFBUDtBQUNBSCxJQUFBQSxLQUFLLEdBQUdBLEtBQUssQ0FBQ0csV0FBTixFQUFSO0FBQ0Q7O0FBQ0QsU0FBT0osSUFBSSxLQUFLQyxLQUFULElBQW1CLEtBQUtDLE9BQUwsQ0FBYUcsZ0JBQWIsSUFBaUMsQ0FBQ1QsWUFBWSxDQUFDVSxJQUFiLENBQWtCTixJQUFsQixDQUFsQyxJQUE2RCxDQUFDSixZQUFZLENBQUNVLElBQWIsQ0FBa0JMLEtBQWxCLENBQXhGO0FBQ0QsQ0FORDs7QUFPQUosUUFBUSxDQUFDVSxRQUFULEdBQW9CLFVBQVNDLEtBQVQsRUFBZ0I7QUFDbEM7QUFDQSxNQUFJQyxNQUFNLEdBQUdELEtBQUssQ0FBQ0UsS0FBTixDQUFZLGlDQUFaLENBQWIsQ0FGa0MsQ0FJbEM7O0FBQ0EsT0FBSyxJQUFJQyxDQUFDLEdBQUcsQ0FBYixFQUFnQkEsQ0FBQyxHQUFHRixNQUFNLENBQUNHLE1BQVAsR0FBZ0IsQ0FBcEMsRUFBdUNELENBQUMsRUFBeEMsRUFBNEM7QUFDMUM7QUFDQSxRQUFJLENBQUNGLE1BQU0sQ0FBQ0UsQ0FBQyxHQUFHLENBQUwsQ0FBUCxJQUFrQkYsTUFBTSxDQUFDRSxDQUFDLEdBQUcsQ0FBTCxDQUF4QixJQUNLaEIsaUJBQWlCLENBQUNXLElBQWxCLENBQXVCRyxNQUFNLENBQUNFLENBQUQsQ0FBN0IsQ0FETCxJQUVLaEIsaUJBQWlCLENBQUNXLElBQWxCLENBQXVCRyxNQUFNLENBQUNFLENBQUMsR0FBRyxDQUFMLENBQTdCLENBRlQsRUFFZ0Q7QUFDOUNGLE1BQUFBLE1BQU0sQ0FBQ0UsQ0FBRCxDQUFOLElBQWFGLE1BQU0sQ0FBQ0UsQ0FBQyxHQUFHLENBQUwsQ0FBbkI7QUFDQUYsTUFBQUEsTUFBTSxDQUFDSSxNQUFQLENBQWNGLENBQUMsR0FBRyxDQUFsQixFQUFxQixDQUFyQjtBQUNBQSxNQUFBQSxDQUFDO0FBQ0Y7QUFDRjs7QUFFRCxTQUFPRixNQUFQO0FBQ0QsQ0FqQkQ7O0FBbUJPLFNBQVNLLFNBQVQsQ0FBbUJDLE1BQW5CLEVBQTJCQyxNQUEzQixFQUFtQ2QsT0FBbkMsRUFBNEM7QUFDakRBLEVBQUFBLE9BQU87QUFBRztBQUFBO0FBQUE7O0FBQUFlO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUE7QUFBQSxHQUFnQmYsT0FBaEIsRUFBeUI7QUFBQ0csSUFBQUEsZ0JBQWdCLEVBQUU7QUFBbkIsR0FBekIsQ0FBVjtBQUNBLFNBQU9SLFFBQVEsQ0FBQ3FCLElBQVQsQ0FBY0gsTUFBZCxFQUFzQkMsTUFBdEIsRUFBOEJkLE9BQTlCLENBQVA7QUFDRDs7QUFFTSxTQUFTaUIsa0JBQVQsQ0FBNEJKLE1BQTVCLEVBQW9DQyxNQUFwQyxFQUE0Q2QsT0FBNUMsRUFBcUQ7QUFDMUQsU0FBT0wsUUFBUSxDQUFDcUIsSUFBVCxDQUFjSCxNQUFkLEVBQXNCQyxNQUF0QixFQUE4QmQsT0FBOUIsQ0FBUDtBQUNEIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcbmltcG9ydCB7Z2VuZXJhdGVPcHRpb25zfSBmcm9tICcuLi91dGlsL3BhcmFtcyc7XG5cbi8vIEJhc2VkIG9uIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xhdGluX3NjcmlwdF9pbl9Vbmljb2RlXG4vL1xuLy8gUmFuZ2VzIGFuZCBleGNlcHRpb25zOlxuLy8gTGF0aW4tMSBTdXBwbGVtZW50LCAwMDgw4oCTMDBGRlxuLy8gIC0gVSswMEQ3ICDDlyBNdWx0aXBsaWNhdGlvbiBzaWduXG4vLyAgLSBVKzAwRjcgIMO3IERpdmlzaW9uIHNpZ25cbi8vIExhdGluIEV4dGVuZGVkLUEsIDAxMDDigJMwMTdGXG4vLyBMYXRpbiBFeHRlbmRlZC1CLCAwMTgw4oCTMDI0RlxuLy8gSVBBIEV4dGVuc2lvbnMsIDAyNTDigJMwMkFGXG4vLyBTcGFjaW5nIE1vZGlmaWVyIExldHRlcnMsIDAyQjDigJMwMkZGXG4vLyAgLSBVKzAyQzcgIMuHICYjNzExOyAgQ2Fyb25cbi8vICAtIFUrMDJEOCAgy5ggJiM3Mjg7ICBCcmV2ZVxuLy8gIC0gVSswMkQ5ICDLmSAmIzcyOTsgIERvdCBBYm92ZVxuLy8gIC0gVSswMkRBICDLmiAmIzczMDsgIFJpbmcgQWJvdmVcbi8vICAtIFUrMDJEQiAgy5sgJiM3MzE7ICBPZ29uZWtcbi8vICAtIFUrMDJEQyAgy5wgJiM3MzI7ICBTbWFsbCBUaWxkZVxuLy8gIC0gVSswMkREICDLnSAmIzczMzsgIERvdWJsZSBBY3V0ZSBBY2NlbnRcbi8vIExhdGluIEV4dGVuZGVkIEFkZGl0aW9uYWwsIDFFMDDigJMxRUZGXG5jb25zdCBleHRlbmRlZFdvcmRDaGFycyA9IC9eW2EtekEtWlxcdXtDMH0tXFx1e0ZGfVxcdXtEOH0tXFx1e0Y2fVxcdXtGOH0tXFx1ezJDNn1cXHV7MkM4fS1cXHV7MkQ3fVxcdXsyREV9LVxcdXsyRkZ9XFx1ezFFMDB9LVxcdXsxRUZGfV0rJC91O1xuXG5jb25zdCByZVdoaXRlc3BhY2UgPSAvXFxTLztcblxuZXhwb3J0IGNvbnN0IHdvcmREaWZmID0gbmV3IERpZmYoKTtcbndvcmREaWZmLmVxdWFscyA9IGZ1bmN0aW9uKGxlZnQsIHJpZ2h0KSB7XG4gIGlmICh0aGlzLm9wdGlvbnMuaWdub3JlQ2FzZSkge1xuICAgIGxlZnQgPSBsZWZ0LnRvTG93ZXJDYXNlKCk7XG4gICAgcmlnaHQgPSByaWdodC50b0xvd2VyQ2FzZSgpO1xuICB9XG4gIHJldHVybiBsZWZ0ID09PSByaWdodCB8fCAodGhpcy5vcHRpb25zLmlnbm9yZVdoaXRlc3BhY2UgJiYgIXJlV2hpdGVzcGFjZS50ZXN0KGxlZnQpICYmICFyZVdoaXRlc3BhY2UudGVzdChyaWdodCkpO1xufTtcbndvcmREaWZmLnRva2VuaXplID0gZnVuY3Rpb24odmFsdWUpIHtcbiAgLy8gQWxsIHdoaXRlc3BhY2Ugc3ltYm9scyBleGNlcHQgbmV3bGluZSBncm91cCBpbnRvIG9uZSB0b2tlbiwgZWFjaCBuZXdsaW5lIC0gaW4gc2VwYXJhdGUgdG9rZW5cbiAgbGV0IHRva2VucyA9IHZhbHVlLnNwbGl0KC8oW15cXFNcXHJcXG5dK3xbKClbXFxde30nXCJcXHJcXG5dfFxcYikvKTtcblxuICAvLyBKb2luIHRoZSBib3VuZGFyeSBzcGxpdHMgdGhhdCB3ZSBkbyBub3QgY29uc2lkZXIgdG8gYmUgYm91bmRhcmllcy4gVGhpcyBpcyBwcmltYXJpbHkgdGhlIGV4dGVuZGVkIExhdGluIGNoYXJhY3RlciBzZXQuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdG9rZW5zLmxlbmd0aCAtIDE7IGkrKykge1xuICAgIC8vIElmIHdlIGhhdmUgYW4gZW1wdHkgc3RyaW5nIGluIHRoZSBuZXh0IGZpZWxkIGFuZCB3ZSBoYXZlIG9ubHkgd29yZCBjaGFycyBiZWZvcmUgYW5kIGFmdGVyLCBtZXJnZVxuICAgIGlmICghdG9rZW5zW2kgKyAxXSAmJiB0b2tlbnNbaSArIDJdXG4gICAgICAgICAgJiYgZXh0ZW5kZWRXb3JkQ2hhcnMudGVzdCh0b2tlbnNbaV0pXG4gICAgICAgICAgJiYgZXh0ZW5kZWRXb3JkQ2hhcnMudGVzdCh0b2tlbnNbaSArIDJdKSkge1xuICAgICAgdG9rZW5zW2ldICs9IHRva2Vuc1tpICsgMl07XG4gICAgICB0b2tlbnMuc3BsaWNlKGkgKyAxLCAyKTtcbiAgICAgIGktLTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gdG9rZW5zO1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIGRpZmZXb3JkcyhvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucykge1xuICBvcHRpb25zID0gZ2VuZXJhdGVPcHRpb25zKG9wdGlvbnMsIHtpZ25vcmVXaGl0ZXNwYWNlOiB0cnVlfSk7XG4gIHJldHVybiB3b3JkRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRpZmZXb3Jkc1dpdGhTcGFjZShvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucykge1xuICByZXR1cm4gd29yZERpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucyk7XG59XG4iXX0= +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX3N0cmluZyIsIm9iaiIsIl9fZXNNb2R1bGUiLCJleHRlbmRlZFdvcmRDaGFycyIsInRva2VuaXplSW5jbHVkaW5nV2hpdGVzcGFjZSIsIlJlZ0V4cCIsImNvbmNhdCIsIndvcmREaWZmIiwiZXhwb3J0cyIsIkRpZmYiLCJlcXVhbHMiLCJsZWZ0IiwicmlnaHQiLCJvcHRpb25zIiwiaWdub3JlQ2FzZSIsInRvTG93ZXJDYXNlIiwidHJpbSIsInRva2VuaXplIiwidmFsdWUiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJ1bmRlZmluZWQiLCJwYXJ0cyIsImludGxTZWdtZW50ZXIiLCJyZXNvbHZlZE9wdGlvbnMiLCJncmFudWxhcml0eSIsIkVycm9yIiwiQXJyYXkiLCJmcm9tIiwic2VnbWVudCIsIm1hdGNoIiwidG9rZW5zIiwicHJldlBhcnQiLCJmb3JFYWNoIiwicGFydCIsInRlc3QiLCJwdXNoIiwicG9wIiwiam9pbiIsIm1hcCIsInRva2VuIiwiaSIsInJlcGxhY2UiLCJwb3N0UHJvY2VzcyIsImNoYW5nZXMiLCJvbmVDaGFuZ2VQZXJUb2tlbiIsImxhc3RLZWVwIiwiaW5zZXJ0aW9uIiwiZGVsZXRpb24iLCJjaGFuZ2UiLCJhZGRlZCIsInJlbW92ZWQiLCJkZWR1cGVXaGl0ZXNwYWNlSW5DaGFuZ2VPYmplY3RzIiwiZGlmZldvcmRzIiwib2xkU3RyIiwibmV3U3RyIiwiaWdub3JlV2hpdGVzcGFjZSIsImRpZmZXb3Jkc1dpdGhTcGFjZSIsImRpZmYiLCJzdGFydEtlZXAiLCJlbmRLZWVwIiwib2xkV3NQcmVmaXgiLCJvbGRXc1N1ZmZpeCIsIm5ld1dzUHJlZml4IiwibmV3V3NTdWZmaXgiLCJjb21tb25Xc1ByZWZpeCIsImxvbmdlc3RDb21tb25QcmVmaXgiLCJyZXBsYWNlU3VmZml4IiwicmVtb3ZlUHJlZml4IiwiY29tbW9uV3NTdWZmaXgiLCJsb25nZXN0Q29tbW9uU3VmZml4IiwicmVwbGFjZVByZWZpeCIsInJlbW92ZVN1ZmZpeCIsIm5ld1dzRnVsbCIsImRlbFdzU3RhcnQiLCJkZWxXc0VuZCIsIm5ld1dzU3RhcnQiLCJuZXdXc0VuZCIsInNsaWNlIiwiZW5kS2VlcFdzUHJlZml4IiwiZGVsZXRpb25Xc1N1ZmZpeCIsIm92ZXJsYXAiLCJtYXhpbXVtT3ZlcmxhcCIsInN0YXJ0S2VlcFdzU3VmZml4IiwiZGVsZXRpb25Xc1ByZWZpeCIsIndvcmRXaXRoU3BhY2VEaWZmIiwicmVnZXgiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvZGlmZi93b3JkLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5pbXBvcnQgeyBsb25nZXN0Q29tbW9uUHJlZml4LCBsb25nZXN0Q29tbW9uU3VmZml4LCByZXBsYWNlUHJlZml4LCByZXBsYWNlU3VmZml4LCByZW1vdmVQcmVmaXgsIHJlbW92ZVN1ZmZpeCwgbWF4aW11bU92ZXJsYXAgfSBmcm9tICcuLi91dGlsL3N0cmluZyc7XG5cbi8vIEJhc2VkIG9uIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xhdGluX3NjcmlwdF9pbl9Vbmljb2RlXG4vL1xuLy8gUmFuZ2VzIGFuZCBleGNlcHRpb25zOlxuLy8gTGF0aW4tMSBTdXBwbGVtZW50LCAwMDgw4oCTMDBGRlxuLy8gIC0gVSswMEQ3ICDDlyBNdWx0aXBsaWNhdGlvbiBzaWduXG4vLyAgLSBVKzAwRjcgIMO3IERpdmlzaW9uIHNpZ25cbi8vIExhdGluIEV4dGVuZGVkLUEsIDAxMDDigJMwMTdGXG4vLyBMYXRpbiBFeHRlbmRlZC1CLCAwMTgw4oCTMDI0RlxuLy8gSVBBIEV4dGVuc2lvbnMsIDAyNTDigJMwMkFGXG4vLyBTcGFjaW5nIE1vZGlmaWVyIExldHRlcnMsIDAyQjDigJMwMkZGXG4vLyAgLSBVKzAyQzcgIMuHICYjNzExOyAgQ2Fyb25cbi8vICAtIFUrMDJEOCAgy5ggJiM3Mjg7ICBCcmV2ZVxuLy8gIC0gVSswMkQ5ICDLmSAmIzcyOTsgIERvdCBBYm92ZVxuLy8gIC0gVSswMkRBICDLmiAmIzczMDsgIFJpbmcgQWJvdmVcbi8vICAtIFUrMDJEQiAgy5sgJiM3MzE7ICBPZ29uZWtcbi8vICAtIFUrMDJEQyAgy5wgJiM3MzI7ICBTbWFsbCBUaWxkZVxuLy8gIC0gVSswMkREICDLnSAmIzczMzsgIERvdWJsZSBBY3V0ZSBBY2NlbnRcbi8vIExhdGluIEV4dGVuZGVkIEFkZGl0aW9uYWwsIDFFMDDigJMxRUZGXG5jb25zdCBleHRlbmRlZFdvcmRDaGFycyA9ICdhLXpBLVowLTlfXFxcXHV7QzB9LVxcXFx1e0ZGfVxcXFx1e0Q4fS1cXFxcdXtGNn1cXFxcdXtGOH0tXFxcXHV7MkM2fVxcXFx1ezJDOH0tXFxcXHV7MkQ3fVxcXFx1ezJERX0tXFxcXHV7MkZGfVxcXFx1ezFFMDB9LVxcXFx1ezFFRkZ9JztcblxuLy8gRWFjaCB0b2tlbiBpcyBvbmUgb2YgdGhlIGZvbGxvd2luZzpcbi8vIC0gQSBwdW5jdHVhdGlvbiBtYXJrIHBsdXMgdGhlIHN1cnJvdW5kaW5nIHdoaXRlc3BhY2Vcbi8vIC0gQSB3b3JkIHBsdXMgdGhlIHN1cnJvdW5kaW5nIHdoaXRlc3BhY2Vcbi8vIC0gUHVyZSB3aGl0ZXNwYWNlIChidXQgb25seSBpbiB0aGUgc3BlY2lhbCBjYXNlIHdoZXJlIHRoaXMgdGhlIGVudGlyZSB0ZXh0XG4vLyAgIGlzIGp1c3Qgd2hpdGVzcGFjZSlcbi8vXG4vLyBXZSBoYXZlIHRvIGluY2x1ZGUgc3Vycm91bmRpbmcgd2hpdGVzcGFjZSBpbiB0aGUgdG9rZW5zIGJlY2F1c2UgdGhlIHR3b1xuLy8gYWx0ZXJuYXRpdmUgYXBwcm9hY2hlcyBwcm9kdWNlIGhvcnJpYmx5IGJyb2tlbiByZXN1bHRzOlxuLy8gKiBJZiB3ZSBqdXN0IGRpc2NhcmQgdGhlIHdoaXRlc3BhY2UsIHdlIGNhbid0IGZ1bGx5IHJlcHJvZHVjZSB0aGUgb3JpZ2luYWxcbi8vICAgdGV4dCBmcm9tIHRoZSBzZXF1ZW5jZSBvZiB0b2tlbnMgYW5kIGFueSBhdHRlbXB0IHRvIHJlbmRlciB0aGUgZGlmZiB3aWxsXG4vLyAgIGdldCB0aGUgd2hpdGVzcGFjZSB3cm9uZy5cbi8vICogSWYgd2UgaGF2ZSBzZXBhcmF0ZSB0b2tlbnMgZm9yIHdoaXRlc3BhY2UsIHRoZW4gaW4gYSB0eXBpY2FsIHRleHQgZXZlcnlcbi8vICAgc2Vjb25kIHRva2VuIHdpbGwgYmUgYSBzaW5nbGUgc3BhY2UgY2hhcmFjdGVyLiBCdXQgdGhpcyBvZnRlbiByZXN1bHRzIGluXG4vLyAgIHRoZSBvcHRpbWFsIGRpZmYgYmV0d2VlbiB0d28gdGV4dHMgYmVpbmcgYSBwZXJ2ZXJzZSBvbmUgdGhhdCBwcmVzZXJ2ZXNcbi8vICAgdGhlIHNwYWNlcyBiZXR3ZWVuIHdvcmRzIGJ1dCBkZWxldGVzIGFuZCByZWluc2VydHMgYWN0dWFsIGNvbW1vbiB3b3Jkcy5cbi8vICAgU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9rcGRlY2tlci9qc2RpZmYvaXNzdWVzLzE2MCNpc3N1ZWNvbW1lbnQtMTg2NjA5OTY0MFxuLy8gICBmb3IgYW4gZXhhbXBsZS5cbi8vXG4vLyBLZWVwaW5nIHRoZSBzdXJyb3VuZGluZyB3aGl0ZXNwYWNlIG9mIGNvdXJzZSBoYXMgaW1wbGljYXRpb25zIGZvciAuZXF1YWxzXG4vLyBhbmQgLmpvaW4sIG5vdCBqdXN0IC50b2tlbml6ZS5cblxuLy8gVGhpcyByZWdleCBkb2VzIE5PVCBmdWxseSBpbXBsZW1lbnQgdGhlIHRva2VuaXphdGlvbiBydWxlcyBkZXNjcmliZWQgYWJvdmUuXG4vLyBJbnN0ZWFkLCBpdCBnaXZlcyBydW5zIG9mIHdoaXRlc3BhY2UgdGhlaXIgb3duIFwidG9rZW5cIi4gVGhlIHRva2VuaXplIG1ldGhvZFxuLy8gdGhlbiBoYW5kbGVzIHN0aXRjaGluZyB3aGl0ZXNwYWNlIHRva2VucyBvbnRvIGFkamFjZW50IHdvcmQgb3IgcHVuY3R1YXRpb25cbi8vIHRva2Vucy5cbmNvbnN0IHRva2VuaXplSW5jbHVkaW5nV2hpdGVzcGFjZSA9IG5ldyBSZWdFeHAoYFske2V4dGVuZGVkV29yZENoYXJzfV0rfFxcXFxzK3xbXiR7ZXh0ZW5kZWRXb3JkQ2hhcnN9XWAsICd1ZycpO1xuXG5leHBvcnQgY29uc3Qgd29yZERpZmYgPSBuZXcgRGlmZigpO1xud29yZERpZmYuZXF1YWxzID0gZnVuY3Rpb24obGVmdCwgcmlnaHQsIG9wdGlvbnMpIHtcbiAgaWYgKG9wdGlvbnMuaWdub3JlQ2FzZSkge1xuICAgIGxlZnQgPSBsZWZ0LnRvTG93ZXJDYXNlKCk7XG4gICAgcmlnaHQgPSByaWdodC50b0xvd2VyQ2FzZSgpO1xuICB9XG5cbiAgcmV0dXJuIGxlZnQudHJpbSgpID09PSByaWdodC50cmltKCk7XG59O1xuXG53b3JkRGlmZi50b2tlbml6ZSA9IGZ1bmN0aW9uKHZhbHVlLCBvcHRpb25zID0ge30pIHtcbiAgbGV0IHBhcnRzO1xuICBpZiAob3B0aW9ucy5pbnRsU2VnbWVudGVyKSB7XG4gICAgaWYgKG9wdGlvbnMuaW50bFNlZ21lbnRlci5yZXNvbHZlZE9wdGlvbnMoKS5ncmFudWxhcml0eSAhPSAnd29yZCcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVGhlIHNlZ21lbnRlciBwYXNzZWQgbXVzdCBoYXZlIGEgZ3JhbnVsYXJpdHkgb2YgXCJ3b3JkXCInKTtcbiAgICB9XG4gICAgcGFydHMgPSBBcnJheS5mcm9tKG9wdGlvbnMuaW50bFNlZ21lbnRlci5zZWdtZW50KHZhbHVlKSwgc2VnbWVudCA9PiBzZWdtZW50LnNlZ21lbnQpO1xuICB9IGVsc2Uge1xuICAgIHBhcnRzID0gdmFsdWUubWF0Y2godG9rZW5pemVJbmNsdWRpbmdXaGl0ZXNwYWNlKSB8fCBbXTtcbiAgfVxuICBjb25zdCB0b2tlbnMgPSBbXTtcbiAgbGV0IHByZXZQYXJ0ID0gbnVsbDtcbiAgcGFydHMuZm9yRWFjaChwYXJ0ID0+IHtcbiAgICBpZiAoKC9cXHMvKS50ZXN0KHBhcnQpKSB7XG4gICAgICBpZiAocHJldlBhcnQgPT0gbnVsbCkge1xuICAgICAgICB0b2tlbnMucHVzaChwYXJ0KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRva2Vucy5wdXNoKHRva2Vucy5wb3AoKSArIHBhcnQpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoKC9cXHMvKS50ZXN0KHByZXZQYXJ0KSkge1xuICAgICAgaWYgKHRva2Vuc1t0b2tlbnMubGVuZ3RoIC0gMV0gPT0gcHJldlBhcnQpIHtcbiAgICAgICAgdG9rZW5zLnB1c2godG9rZW5zLnBvcCgpICsgcGFydCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0b2tlbnMucHVzaChwcmV2UGFydCArIHBhcnQpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0b2tlbnMucHVzaChwYXJ0KTtcbiAgICB9XG5cbiAgICBwcmV2UGFydCA9IHBhcnQ7XG4gIH0pO1xuICByZXR1cm4gdG9rZW5zO1xufTtcblxud29yZERpZmYuam9pbiA9IGZ1bmN0aW9uKHRva2Vucykge1xuICAvLyBUb2tlbnMgYmVpbmcgam9pbmVkIGhlcmUgd2lsbCBhbHdheXMgaGF2ZSBhcHBlYXJlZCBjb25zZWN1dGl2ZWx5IGluIHRoZVxuICAvLyBzYW1lIHRleHQsIHNvIHdlIGNhbiBzaW1wbHkgc3RyaXAgb2ZmIHRoZSBsZWFkaW5nIHdoaXRlc3BhY2UgZnJvbSBhbGwgdGhlXG4gIC8vIHRva2VucyBleGNlcHQgdGhlIGZpcnN0IChhbmQgZXhjZXB0IGFueSB3aGl0ZXNwYWNlLW9ubHkgdG9rZW5zIC0gYnV0IHN1Y2hcbiAgLy8gYSB0b2tlbiB3aWxsIGFsd2F5cyBiZSB0aGUgZmlyc3QgYW5kIG9ubHkgdG9rZW4gYW55d2F5KSBhbmQgdGhlbiBqb2luIHRoZW1cbiAgLy8gYW5kIHRoZSB3aGl0ZXNwYWNlIGFyb3VuZCB3b3JkcyBhbmQgcHVuY3R1YXRpb24gd2lsbCBlbmQgdXAgY29ycmVjdC5cbiAgcmV0dXJuIHRva2Vucy5tYXAoKHRva2VuLCBpKSA9PiB7XG4gICAgaWYgKGkgPT0gMCkge1xuICAgICAgcmV0dXJuIHRva2VuO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdG9rZW4ucmVwbGFjZSgoL15cXHMrLyksICcnKTtcbiAgICB9XG4gIH0pLmpvaW4oJycpO1xufTtcblxud29yZERpZmYucG9zdFByb2Nlc3MgPSBmdW5jdGlvbihjaGFuZ2VzLCBvcHRpb25zKSB7XG4gIGlmICghY2hhbmdlcyB8fCBvcHRpb25zLm9uZUNoYW5nZVBlclRva2VuKSB7XG4gICAgcmV0dXJuIGNoYW5nZXM7XG4gIH1cblxuICBsZXQgbGFzdEtlZXAgPSBudWxsO1xuICAvLyBDaGFuZ2Ugb2JqZWN0cyByZXByZXNlbnRpbmcgYW55IGluc2VydGlvbiBvciBkZWxldGlvbiBzaW5jZSB0aGUgbGFzdFxuICAvLyBcImtlZXBcIiBjaGFuZ2Ugb2JqZWN0LiBUaGVyZSBjYW4gYmUgYXQgbW9zdCBvbmUgb2YgZWFjaC5cbiAgbGV0IGluc2VydGlvbiA9IG51bGw7XG4gIGxldCBkZWxldGlvbiA9IG51bGw7XG4gIGNoYW5nZXMuZm9yRWFjaChjaGFuZ2UgPT4ge1xuICAgIGlmIChjaGFuZ2UuYWRkZWQpIHtcbiAgICAgIGluc2VydGlvbiA9IGNoYW5nZTtcbiAgICB9IGVsc2UgaWYgKGNoYW5nZS5yZW1vdmVkKSB7XG4gICAgICBkZWxldGlvbiA9IGNoYW5nZTtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKGluc2VydGlvbiB8fCBkZWxldGlvbikgeyAvLyBNYXkgYmUgZmFsc2UgYXQgc3RhcnQgb2YgdGV4dFxuICAgICAgICBkZWR1cGVXaGl0ZXNwYWNlSW5DaGFuZ2VPYmplY3RzKGxhc3RLZWVwLCBkZWxldGlvbiwgaW5zZXJ0aW9uLCBjaGFuZ2UpO1xuICAgICAgfVxuICAgICAgbGFzdEtlZXAgPSBjaGFuZ2U7XG4gICAgICBpbnNlcnRpb24gPSBudWxsO1xuICAgICAgZGVsZXRpb24gPSBudWxsO1xuICAgIH1cbiAgfSk7XG4gIGlmIChpbnNlcnRpb24gfHwgZGVsZXRpb24pIHtcbiAgICBkZWR1cGVXaGl0ZXNwYWNlSW5DaGFuZ2VPYmplY3RzKGxhc3RLZWVwLCBkZWxldGlvbiwgaW5zZXJ0aW9uLCBudWxsKTtcbiAgfVxuICByZXR1cm4gY2hhbmdlcztcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBkaWZmV29yZHMob2xkU3RyLCBuZXdTdHIsIG9wdGlvbnMpIHtcbiAgLy8gVGhpcyBvcHRpb24gaGFzIG5ldmVyIGJlZW4gZG9jdW1lbnRlZCBhbmQgbmV2ZXIgd2lsbCBiZSAoaXQncyBjbGVhcmVyIHRvXG4gIC8vIGp1c3QgY2FsbCBgZGlmZldvcmRzV2l0aFNwYWNlYCBkaXJlY3RseSBpZiB5b3UgbmVlZCB0aGF0IGJlaGF2aW9yKSwgYnV0XG4gIC8vIGhhcyBleGlzdGVkIGluIGpzZGlmZiBmb3IgYSBsb25nIHRpbWUsIHNvIHdlIHJldGFpbiBzdXBwb3J0IGZvciBpdCBoZXJlXG4gIC8vIGZvciB0aGUgc2FrZSBvZiBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eS5cbiAgaWYgKG9wdGlvbnM/Lmlnbm9yZVdoaXRlc3BhY2UgIT0gbnVsbCAmJiAhb3B0aW9ucy5pZ25vcmVXaGl0ZXNwYWNlKSB7XG4gICAgcmV0dXJuIGRpZmZXb3Jkc1dpdGhTcGFjZShvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucyk7XG4gIH1cblxuICByZXR1cm4gd29yZERpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucyk7XG59XG5cbmZ1bmN0aW9uIGRlZHVwZVdoaXRlc3BhY2VJbkNoYW5nZU9iamVjdHMoc3RhcnRLZWVwLCBkZWxldGlvbiwgaW5zZXJ0aW9uLCBlbmRLZWVwKSB7XG4gIC8vIEJlZm9yZSByZXR1cm5pbmcsIHdlIHRpZHkgdXAgdGhlIGxlYWRpbmcgYW5kIHRyYWlsaW5nIHdoaXRlc3BhY2Ugb2YgdGhlXG4gIC8vIGNoYW5nZSBvYmplY3RzIHRvIGVsaW1pbmF0ZSBjYXNlcyB3aGVyZSB0cmFpbGluZyB3aGl0ZXNwYWNlIGluIG9uZSBvYmplY3RcbiAgLy8gaXMgcmVwZWF0ZWQgYXMgbGVhZGluZyB3aGl0ZXNwYWNlIGluIHRoZSBuZXh0LlxuICAvLyBCZWxvdyBhcmUgZXhhbXBsZXMgb2YgdGhlIG91dGNvbWVzIHdlIHdhbnQgaGVyZSB0byBleHBsYWluIHRoZSBjb2RlLlxuICAvLyBJPWluc2VydCwgSz1rZWVwLCBEPWRlbGV0ZVxuICAvLyAxLiBkaWZmaW5nICdmb28gYmFyIGJheicgdnMgJ2ZvbyBiYXonXG4gIC8vICAgIFByaW9yIHRvIGNsZWFudXAsIHdlIGhhdmUgSzonZm9vICcgRDonIGJhciAnIEs6JyBiYXonXG4gIC8vICAgIEFmdGVyIGNsZWFudXAsIHdlIHdhbnQ6ICAgSzonZm9vICcgRDonYmFyICcgSzonYmF6J1xuICAvL1xuICAvLyAyLiBEaWZmaW5nICdmb28gYmFyIGJheicgdnMgJ2ZvbyBxdXggYmF6J1xuICAvLyAgICBQcmlvciB0byBjbGVhbnVwLCB3ZSBoYXZlIEs6J2ZvbyAnIEQ6JyBiYXIgJyBJOicgcXV4ICcgSzonIGJheidcbiAgLy8gICAgQWZ0ZXIgY2xlYW51cCwgd2Ugd2FudCBLOidmb28gJyBEOidiYXInIEk6J3F1eCcgSzonIGJheidcbiAgLy9cbiAgLy8gMy4gRGlmZmluZyAnZm9vXFxuYmFyIGJheicgdnMgJ2ZvbyBiYXonXG4gIC8vICAgIFByaW9yIHRvIGNsZWFudXAsIHdlIGhhdmUgSzonZm9vICcgRDonXFxuYmFyICcgSzonIGJheidcbiAgLy8gICAgQWZ0ZXIgY2xlYW51cCwgd2Ugd2FudCBLJ2ZvbycgRDonXFxuYmFyJyBLOicgYmF6J1xuICAvL1xuICAvLyA0LiBEaWZmaW5nICdmb28gYmF6JyB2cyAnZm9vXFxuYmFyIGJheidcbiAgLy8gICAgUHJpb3IgdG8gY2xlYW51cCwgd2UgaGF2ZSBLOidmb29cXG4nIEk6J1xcbmJhciAnIEs6JyBiYXonXG4gIC8vICAgIEFmdGVyIGNsZWFudXAsIHdlIGlkZWFsbHkgd2FudCBLJ2ZvbycgSTonXFxuYmFyJyBLOicgYmF6J1xuICAvLyAgICBidXQgZG9uJ3QgYWN0dWFsbHkgbWFuYWdlIHRoaXMgY3VycmVudGx5ICh0aGUgcHJlLWNsZWFudXAgY2hhbmdlXG4gIC8vICAgIG9iamVjdHMgZG9uJ3QgY29udGFpbiBlbm91Z2ggaW5mb3JtYXRpb24gdG8gbWFrZSBpdCBwb3NzaWJsZSkuXG4gIC8vXG4gIC8vIDUuIERpZmZpbmcgJ2ZvbyAgIGJhciBiYXonIHZzICdmb28gIGJheidcbiAgLy8gICAgUHJpb3IgdG8gY2xlYW51cCwgd2UgaGF2ZSBLOidmb28gICcgRDonICAgYmFyICcgSzonICBiYXonXG4gIC8vICAgIEFmdGVyIGNsZWFudXAsIHdlIHdhbnQgSzonZm9vICAnIEQ6JyBiYXIgJyBLOidiYXonXG4gIC8vXG4gIC8vIE91ciBoYW5kbGluZyBpcyB1bmF2b2lkYWJseSBpbXBlcmZlY3QgaW4gdGhlIGNhc2Ugd2hlcmUgdGhlcmUncyBhIHNpbmdsZVxuICAvLyBpbmRlbCBiZXR3ZWVuIGtlZXBzIGFuZCB0aGUgd2hpdGVzcGFjZSBoYXMgY2hhbmdlZC4gRm9yIGluc3RhbmNlLCBjb25zaWRlclxuICAvLyBkaWZmaW5nICdmb29cXHRiYXJcXG5iYXonIHZzICdmb28gYmF6Jy4gVW5sZXNzIHdlIGNyZWF0ZSBhbiBleHRyYSBjaGFuZ2VcbiAgLy8gb2JqZWN0IHRvIHJlcHJlc2VudCB0aGUgaW5zZXJ0aW9uIG9mIHRoZSBzcGFjZSBjaGFyYWN0ZXIgKHdoaWNoIGlzbid0IGV2ZW5cbiAgLy8gYSB0b2tlbiksIHdlIGhhdmUgbm8gd2F5IHRvIGF2b2lkIGxvc2luZyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdGV4dHMnXG4gIC8vIG9yaWdpbmFsIHdoaXRlc3BhY2UgaW4gdGhlIHJlc3VsdCB3ZSByZXR1cm4uIFN0aWxsLCB3ZSBkbyBvdXIgYmVzdCB0b1xuICAvLyBvdXRwdXQgc29tZXRoaW5nIHRoYXQgd2lsbCBsb29rIHNlbnNpYmxlIGlmIHdlIGUuZy4gcHJpbnQgaXQgd2l0aFxuICAvLyBpbnNlcnRpb25zIGluIGdyZWVuIGFuZCBkZWxldGlvbnMgaW4gcmVkLlxuXG4gIC8vIEJldHdlZW4gdHdvIFwia2VlcFwiIGNoYW5nZSBvYmplY3RzIChvciBiZWZvcmUgdGhlIGZpcnN0IG9yIGFmdGVyIHRoZSBsYXN0XG4gIC8vIGNoYW5nZSBvYmplY3QpLCB3ZSBjYW4gaGF2ZSBlaXRoZXI6XG4gIC8vICogQSBcImRlbGV0ZVwiIGZvbGxvd2VkIGJ5IGFuIFwiaW5zZXJ0XCJcbiAgLy8gKiBKdXN0IGFuIFwiaW5zZXJ0XCJcbiAgLy8gKiBKdXN0IGEgXCJkZWxldGVcIlxuICAvLyBXZSBoYW5kbGUgdGhlIHRocmVlIGNhc2VzIHNlcGFyYXRlbHkuXG4gIGlmIChkZWxldGlvbiAmJiBpbnNlcnRpb24pIHtcbiAgICBjb25zdCBvbGRXc1ByZWZpeCA9IGRlbGV0aW9uLnZhbHVlLm1hdGNoKC9eXFxzKi8pWzBdO1xuICAgIGNvbnN0IG9sZFdzU3VmZml4ID0gZGVsZXRpb24udmFsdWUubWF0Y2goL1xccyokLylbMF07XG4gICAgY29uc3QgbmV3V3NQcmVmaXggPSBpbnNlcnRpb24udmFsdWUubWF0Y2goL15cXHMqLylbMF07XG4gICAgY29uc3QgbmV3V3NTdWZmaXggPSBpbnNlcnRpb24udmFsdWUubWF0Y2goL1xccyokLylbMF07XG5cbiAgICBpZiAoc3RhcnRLZWVwKSB7XG4gICAgICBjb25zdCBjb21tb25Xc1ByZWZpeCA9IGxvbmdlc3RDb21tb25QcmVmaXgob2xkV3NQcmVmaXgsIG5ld1dzUHJlZml4KTtcbiAgICAgIHN0YXJ0S2VlcC52YWx1ZSA9IHJlcGxhY2VTdWZmaXgoc3RhcnRLZWVwLnZhbHVlLCBuZXdXc1ByZWZpeCwgY29tbW9uV3NQcmVmaXgpO1xuICAgICAgZGVsZXRpb24udmFsdWUgPSByZW1vdmVQcmVmaXgoZGVsZXRpb24udmFsdWUsIGNvbW1vbldzUHJlZml4KTtcbiAgICAgIGluc2VydGlvbi52YWx1ZSA9IHJlbW92ZVByZWZpeChpbnNlcnRpb24udmFsdWUsIGNvbW1vbldzUHJlZml4KTtcbiAgICB9XG4gICAgaWYgKGVuZEtlZXApIHtcbiAgICAgIGNvbnN0IGNvbW1vbldzU3VmZml4ID0gbG9uZ2VzdENvbW1vblN1ZmZpeChvbGRXc1N1ZmZpeCwgbmV3V3NTdWZmaXgpO1xuICAgICAgZW5kS2VlcC52YWx1ZSA9IHJlcGxhY2VQcmVmaXgoZW5kS2VlcC52YWx1ZSwgbmV3V3NTdWZmaXgsIGNvbW1vbldzU3VmZml4KTtcbiAgICAgIGRlbGV0aW9uLnZhbHVlID0gcmVtb3ZlU3VmZml4KGRlbGV0aW9uLnZhbHVlLCBjb21tb25Xc1N1ZmZpeCk7XG4gICAgICBpbnNlcnRpb24udmFsdWUgPSByZW1vdmVTdWZmaXgoaW5zZXJ0aW9uLnZhbHVlLCBjb21tb25Xc1N1ZmZpeCk7XG4gICAgfVxuICB9IGVsc2UgaWYgKGluc2VydGlvbikge1xuICAgIC8vIFRoZSB3aGl0ZXNwYWNlcyBhbGwgcmVmbGVjdCB3aGF0IHdhcyBpbiB0aGUgbmV3IHRleHQgcmF0aGVyIHRoYW5cbiAgICAvLyB0aGUgb2xkLCBzbyB3ZSBlc3NlbnRpYWxseSBoYXZlIG5vIGluZm9ybWF0aW9uIGFib3V0IHdoaXRlc3BhY2VcbiAgICAvLyBpbnNlcnRpb24gb3IgZGVsZXRpb24uIFdlIGp1c3Qgd2FudCB0byBkZWR1cGUgdGhlIHdoaXRlc3BhY2UuXG4gICAgLy8gV2UgZG8gdGhhdCBieSBoYXZpbmcgZWFjaCBjaGFuZ2Ugb2JqZWN0IGtlZXAgaXRzIHRyYWlsaW5nXG4gICAgLy8gd2hpdGVzcGFjZSBhbmQgZGVsZXRpbmcgZHVwbGljYXRlIGxlYWRpbmcgd2hpdGVzcGFjZSB3aGVyZVxuICAgIC8vIHByZXNlbnQuXG4gICAgaWYgKHN0YXJ0S2VlcCkge1xuICAgICAgaW5zZXJ0aW9uLnZhbHVlID0gaW5zZXJ0aW9uLnZhbHVlLnJlcGxhY2UoL15cXHMqLywgJycpO1xuICAgIH1cbiAgICBpZiAoZW5kS2VlcCkge1xuICAgICAgZW5kS2VlcC52YWx1ZSA9IGVuZEtlZXAudmFsdWUucmVwbGFjZSgvXlxccyovLCAnJyk7XG4gICAgfVxuICAvLyBvdGhlcndpc2Ugd2UndmUgZ290IGEgZGVsZXRpb24gYW5kIG5vIGluc2VydGlvblxuICB9IGVsc2UgaWYgKHN0YXJ0S2VlcCAmJiBlbmRLZWVwKSB7XG4gICAgY29uc3QgbmV3V3NGdWxsID0gZW5kS2VlcC52YWx1ZS5tYXRjaCgvXlxccyovKVswXSxcbiAgICAgICAgZGVsV3NTdGFydCA9IGRlbGV0aW9uLnZhbHVlLm1hdGNoKC9eXFxzKi8pWzBdLFxuICAgICAgICBkZWxXc0VuZCA9IGRlbGV0aW9uLnZhbHVlLm1hdGNoKC9cXHMqJC8pWzBdO1xuXG4gICAgLy8gQW55IHdoaXRlc3BhY2UgdGhhdCBjb21lcyBzdHJhaWdodCBhZnRlciBzdGFydEtlZXAgaW4gYm90aCB0aGUgb2xkIGFuZFxuICAgIC8vIG5ldyB0ZXh0cywgYXNzaWduIHRvIHN0YXJ0S2VlcCBhbmQgcmVtb3ZlIGZyb20gdGhlIGRlbGV0aW9uLlxuICAgIGNvbnN0IG5ld1dzU3RhcnQgPSBsb25nZXN0Q29tbW9uUHJlZml4KG5ld1dzRnVsbCwgZGVsV3NTdGFydCk7XG4gICAgZGVsZXRpb24udmFsdWUgPSByZW1vdmVQcmVmaXgoZGVsZXRpb24udmFsdWUsIG5ld1dzU3RhcnQpO1xuXG4gICAgLy8gQW55IHdoaXRlc3BhY2UgdGhhdCBjb21lcyBzdHJhaWdodCBiZWZvcmUgZW5kS2VlcCBpbiBib3RoIHRoZSBvbGQgYW5kXG4gICAgLy8gbmV3IHRleHRzLCBhbmQgaGFzbid0IGFscmVhZHkgYmVlbiBhc3NpZ25lZCB0byBzdGFydEtlZXAsIGFzc2lnbiB0b1xuICAgIC8vIGVuZEtlZXAgYW5kIHJlbW92ZSBmcm9tIHRoZSBkZWxldGlvbi5cbiAgICBjb25zdCBuZXdXc0VuZCA9IGxvbmdlc3RDb21tb25TdWZmaXgoXG4gICAgICByZW1vdmVQcmVmaXgobmV3V3NGdWxsLCBuZXdXc1N0YXJ0KSxcbiAgICAgIGRlbFdzRW5kXG4gICAgKTtcbiAgICBkZWxldGlvbi52YWx1ZSA9IHJlbW92ZVN1ZmZpeChkZWxldGlvbi52YWx1ZSwgbmV3V3NFbmQpO1xuICAgIGVuZEtlZXAudmFsdWUgPSByZXBsYWNlUHJlZml4KGVuZEtlZXAudmFsdWUsIG5ld1dzRnVsbCwgbmV3V3NFbmQpO1xuXG4gICAgLy8gSWYgdGhlcmUncyBhbnkgd2hpdGVzcGFjZSBmcm9tIHRoZSBuZXcgdGV4dCB0aGF0IEhBU04nVCBhbHJlYWR5IGJlZW5cbiAgICAvLyBhc3NpZ25lZCwgYXNzaWduIGl0IHRvIHRoZSBzdGFydDpcbiAgICBzdGFydEtlZXAudmFsdWUgPSByZXBsYWNlU3VmZml4KFxuICAgICAgc3RhcnRLZWVwLnZhbHVlLFxuICAgICAgbmV3V3NGdWxsLFxuICAgICAgbmV3V3NGdWxsLnNsaWNlKDAsIG5ld1dzRnVsbC5sZW5ndGggLSBuZXdXc0VuZC5sZW5ndGgpXG4gICAgKTtcbiAgfSBlbHNlIGlmIChlbmRLZWVwKSB7XG4gICAgLy8gV2UgYXJlIGF0IHRoZSBzdGFydCBvZiB0aGUgdGV4dC4gUHJlc2VydmUgYWxsIHRoZSB3aGl0ZXNwYWNlIG9uXG4gICAgLy8gZW5kS2VlcCwgYW5kIGp1c3QgcmVtb3ZlIHdoaXRlc3BhY2UgZnJvbSB0aGUgZW5kIG9mIGRlbGV0aW9uIHRvIHRoZVxuICAgIC8vIGV4dGVudCB0aGF0IGl0IG92ZXJsYXBzIHdpdGggdGhlIHN0YXJ0IG9mIGVuZEtlZXAuXG4gICAgY29uc3QgZW5kS2VlcFdzUHJlZml4ID0gZW5kS2VlcC52YWx1ZS5tYXRjaCgvXlxccyovKVswXTtcbiAgICBjb25zdCBkZWxldGlvbldzU3VmZml4ID0gZGVsZXRpb24udmFsdWUubWF0Y2goL1xccyokLylbMF07XG4gICAgY29uc3Qgb3ZlcmxhcCA9IG1heGltdW1PdmVybGFwKGRlbGV0aW9uV3NTdWZmaXgsIGVuZEtlZXBXc1ByZWZpeCk7XG4gICAgZGVsZXRpb24udmFsdWUgPSByZW1vdmVTdWZmaXgoZGVsZXRpb24udmFsdWUsIG92ZXJsYXApO1xuICB9IGVsc2UgaWYgKHN0YXJ0S2VlcCkge1xuICAgIC8vIFdlIGFyZSBhdCB0aGUgRU5EIG9mIHRoZSB0ZXh0LiBQcmVzZXJ2ZSBhbGwgdGhlIHdoaXRlc3BhY2Ugb25cbiAgICAvLyBzdGFydEtlZXAsIGFuZCBqdXN0IHJlbW92ZSB3aGl0ZXNwYWNlIGZyb20gdGhlIHN0YXJ0IG9mIGRlbGV0aW9uIHRvXG4gICAgLy8gdGhlIGV4dGVudCB0aGF0IGl0IG92ZXJsYXBzIHdpdGggdGhlIGVuZCBvZiBzdGFydEtlZXAuXG4gICAgY29uc3Qgc3RhcnRLZWVwV3NTdWZmaXggPSBzdGFydEtlZXAudmFsdWUubWF0Y2goL1xccyokLylbMF07XG4gICAgY29uc3QgZGVsZXRpb25Xc1ByZWZpeCA9IGRlbGV0aW9uLnZhbHVlLm1hdGNoKC9eXFxzKi8pWzBdO1xuICAgIGNvbnN0IG92ZXJsYXAgPSBtYXhpbXVtT3ZlcmxhcChzdGFydEtlZXBXc1N1ZmZpeCwgZGVsZXRpb25Xc1ByZWZpeCk7XG4gICAgZGVsZXRpb24udmFsdWUgPSByZW1vdmVQcmVmaXgoZGVsZXRpb24udmFsdWUsIG92ZXJsYXApO1xuICB9XG59XG5cblxuZXhwb3J0IGNvbnN0IHdvcmRXaXRoU3BhY2VEaWZmID0gbmV3IERpZmYoKTtcbndvcmRXaXRoU3BhY2VEaWZmLnRva2VuaXplID0gZnVuY3Rpb24odmFsdWUpIHtcbiAgLy8gU2xpZ2h0bHkgZGlmZmVyZW50IHRvIHRoZSB0b2tlbml6ZUluY2x1ZGluZ1doaXRlc3BhY2UgcmVnZXggdXNlZCBhYm92ZSBpblxuICAvLyB0aGF0IHRoaXMgb25lIHRyZWF0cyBlYWNoIGluZGl2aWR1YWwgbmV3bGluZSBhcyBhIGRpc3RpbmN0IHRva2VucywgcmF0aGVyXG4gIC8vIHRoYW4gbWVyZ2luZyB0aGVtIGludG8gb3RoZXIgc3Vycm91bmRpbmcgd2hpdGVzcGFjZS4gVGhpcyB3YXMgcmVxdWVzdGVkXG4gIC8vIGluIGh0dHBzOi8vZ2l0aHViLmNvbS9rcGRlY2tlci9qc2RpZmYvaXNzdWVzLzE4MCAmXG4gIC8vICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9rcGRlY2tlci9qc2RpZmYvaXNzdWVzLzIxMVxuICBjb25zdCByZWdleCA9IG5ldyBSZWdFeHAoYChcXFxccj9cXFxcbil8WyR7ZXh0ZW5kZWRXb3JkQ2hhcnN9XSt8W15cXFxcU1xcXFxuXFxcXHJdK3xbXiR7ZXh0ZW5kZWRXb3JkQ2hhcnN9XWAsICd1ZycpO1xuICByZXR1cm4gdmFsdWUubWF0Y2gocmVnZXgpIHx8IFtdO1xufTtcbmV4cG9ydCBmdW5jdGlvbiBkaWZmV29yZHNXaXRoU3BhY2Uob2xkU3RyLCBuZXdTdHIsIG9wdGlvbnMpIHtcbiAgcmV0dXJuIHdvcmRXaXRoU3BhY2VEaWZmLmRpZmYob2xkU3RyLCBuZXdTdHIsIG9wdGlvbnMpO1xufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBQSxLQUFBLEdBQUFDLHNCQUFBLENBQUFDLE9BQUE7QUFBQTtBQUFBO0FBQ0E7QUFBQTtBQUFBQyxPQUFBLEdBQUFELE9BQUE7QUFBQTtBQUFBO0FBQW9KLG1DQUFBRCx1QkFBQUcsR0FBQSxXQUFBQSxHQUFBLElBQUFBLEdBQUEsQ0FBQUMsVUFBQSxHQUFBRCxHQUFBLGdCQUFBQSxHQUFBO0FBQUE7QUFFcEo7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBTUUsaUJBQWlCLEdBQUcsK0dBQStHOztBQUV6STtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBTUMsMkJBQTJCLEdBQUcsSUFBSUMsTUFBTTtBQUFBO0FBQUEsSUFBQUMsTUFBQTtBQUFBO0FBQUtILGlCQUFpQixnQkFBQUcsTUFBQSxDQUFhSCxpQkFBaUIsUUFBSyxJQUFJLENBQUM7QUFFckcsSUFBTUksUUFBUTtBQUFBO0FBQUFDLE9BQUEsQ0FBQUQsUUFBQTtBQUFBO0FBQUc7QUFBSUU7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUEsQ0FBSSxDQUFDLENBQUM7QUFDbENGLFFBQVEsQ0FBQ0csTUFBTSxHQUFHLFVBQVNDLElBQUks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 diff --git a/deps/npm/node_modules/diff/lib/index.es6.js b/deps/npm/node_modules/diff/lib/index.es6.js index a0ace0182ab14e..6e872723d85817 100644 --- a/deps/npm/node_modules/diff/lib/index.es6.js +++ b/deps/npm/node_modules/diff/lib/index.es6.js @@ -2,59 +2,52 @@ function Diff() {} Diff.prototype = { diff: function diff(oldString, newString) { var _options$timeout; - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var callback = options.callback; - if (typeof options === 'function') { callback = options; options = {}; } - - this.options = options; var self = this; - function done(value) { + value = self.postProcess(value, options); if (callback) { setTimeout(function () { - callback(undefined, value); + callback(value); }, 0); return true; } else { return value; } - } // Allow subclasses to massage the input prior to running - + } - oldString = this.castInput(oldString); - newString = this.castInput(newString); - oldString = this.removeEmpty(this.tokenize(oldString)); - newString = this.removeEmpty(this.tokenize(newString)); + // Allow subclasses to massage the input prior to running + oldString = this.castInput(oldString, options); + newString = this.castInput(newString, options); + oldString = this.removeEmpty(this.tokenize(oldString, options)); + newString = this.removeEmpty(this.tokenize(newString, options)); var newLen = newString.length, - oldLen = oldString.length; + oldLen = oldString.length; var editLength = 1; var maxEditLength = newLen + oldLen; - - if (options.maxEditLength) { + if (options.maxEditLength != null) { maxEditLength = Math.min(maxEditLength, options.maxEditLength); } - var maxExecutionTime = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : Infinity; var abortAfterTimestamp = Date.now() + maxExecutionTime; var bestPath = [{ oldPos: -1, lastComponent: undefined - }]; // Seed editLength = 0, i.e. the content starts with the same values - - var newPos = this.extractCommon(bestPath[0], newString, oldString, 0); + }]; + // Seed editLength = 0, i.e. the content starts with the same values + var newPos = this.extractCommon(bestPath[0], newString, oldString, 0, options); if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) { // Identity per the equality and tokenizer - return done([{ - value: this.join(newString), - count: newString.length - }]); - } // Once we hit the right edge of the edit graph on some diagonal k, we can + return done(buildValues(self, bestPath[0].lastComponent, newString, oldString, self.useLongestToken)); + } + + // Once we hit the right edge of the edit graph on some diagonal k, we can // definitely reach the end of the edit graph in no more than k edits, so // there's no point in considering any moves to diagonal k+1 any more (from // which we're guaranteed to need at least k+1 more edits). @@ -71,81 +64,67 @@ Diff.prototype = { // where the new text simply appends d characters on the end of the // original text of length n, the true Myers algorithm will take O(n+d^2) // time while this optimization needs only O(n+d) time. - - var minDiagonalToConsider = -Infinity, - maxDiagonalToConsider = Infinity; // Main worker method. checks all permutations of a given edit length for acceptance. + maxDiagonalToConsider = Infinity; + // Main worker method. checks all permutations of a given edit length for acceptance. function execEditLength() { for (var diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) { var basePath = void 0; var removePath = bestPath[diagonalPath - 1], - addPath = bestPath[diagonalPath + 1]; - + addPath = bestPath[diagonalPath + 1]; if (removePath) { // No one else is going to attempt to use this value, clear it bestPath[diagonalPath - 1] = undefined; } - var canAdd = false; - if (addPath) { // what newPos will be after we do an insertion: var addPathNewPos = addPath.oldPos - diagonalPath; canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen; } - var canRemove = removePath && removePath.oldPos + 1 < oldLen; - if (!canAdd && !canRemove) { // If this path is a terminal then prune bestPath[diagonalPath] = undefined; continue; - } // Select the diagonal that we want to branch from. We select the prior + } + + // Select the diagonal that we want to branch from. We select the prior // path whose position in the old string is the farthest from the origin // and does not pass the bounds of the diff graph - // TODO: Remove the `+ 1` here to make behavior match Myers algorithm - // and prefer to order removals before insertions. - - - if (!canRemove || canAdd && removePath.oldPos + 1 < addPath.oldPos) { - basePath = self.addToPath(addPath, true, undefined, 0); + if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) { + basePath = self.addToPath(addPath, true, false, 0, options); } else { - basePath = self.addToPath(removePath, undefined, true, 1); + basePath = self.addToPath(removePath, false, true, 1, options); } - - newPos = self.extractCommon(basePath, newString, oldString, diagonalPath); - + newPos = self.extractCommon(basePath, newString, oldString, diagonalPath, options); if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) { // If we have hit the end of both strings, then we are done return done(buildValues(self, basePath.lastComponent, newString, oldString, self.useLongestToken)); } else { bestPath[diagonalPath] = basePath; - if (basePath.oldPos + 1 >= oldLen) { maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1); } - if (newPos + 1 >= newLen) { minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1); } } } - editLength++; - } // Performs the length of edit iteration. Is a bit fugly as this has to support the + } + + // Performs the length of edit iteration. Is a bit fugly as this has to support the // sync and async mode which is never fun. Loops over execEditLength until a value // is produced, or until the edit length exceeds options.maxEditLength (if given), // in which case it will return undefined. - - if (callback) { (function exec() { setTimeout(function () { if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) { return callback(); } - if (!execEditLength()) { exec(); } @@ -154,17 +133,15 @@ Diff.prototype = { } else { while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) { var ret = execEditLength(); - if (ret) { return ret; } } } }, - addToPath: function addToPath(path, added, removed, oldPosInc) { + addToPath: function addToPath(path, added, removed, oldPosInc, options) { var last = path.lastComponent; - - if (last && last.added === added && last.removed === removed) { + if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) { return { oldPos: path.oldPos + oldPosInc, lastComponent: { @@ -186,80 +163,83 @@ Diff.prototype = { }; } }, - extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { + extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath, options) { var newLen = newString.length, - oldLen = oldString.length, - oldPos = basePath.oldPos, - newPos = oldPos - diagonalPath, - commonCount = 0; - - while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + oldLen = oldString.length, + oldPos = basePath.oldPos, + newPos = oldPos - diagonalPath, + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldString[oldPos + 1], newString[newPos + 1], options)) { newPos++; oldPos++; commonCount++; + if (options.oneChangePerToken) { + basePath.lastComponent = { + count: 1, + previousComponent: basePath.lastComponent, + added: false, + removed: false + }; + } } - - if (commonCount) { + if (commonCount && !options.oneChangePerToken) { basePath.lastComponent = { count: commonCount, - previousComponent: basePath.lastComponent + previousComponent: basePath.lastComponent, + added: false, + removed: false }; } - basePath.oldPos = oldPos; return newPos; }, - equals: function equals(left, right) { - if (this.options.comparator) { - return this.options.comparator(left, right); + equals: function equals(left, right, options) { + if (options.comparator) { + return options.comparator(left, right); } else { - return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); + return left === right || options.ignoreCase && left.toLowerCase() === right.toLowerCase(); } }, removeEmpty: function removeEmpty(array) { var ret = []; - for (var i = 0; i < array.length; i++) { if (array[i]) { ret.push(array[i]); } } - return ret; }, castInput: function castInput(value) { return value; }, tokenize: function tokenize(value) { - return value.split(''); + return Array.from(value); }, join: function join(chars) { return chars.join(''); + }, + postProcess: function postProcess(changeObjects) { + return changeObjects; } }; - function buildValues(diff, lastComponent, newString, oldString, useLongestToken) { // First we convert our linked list of components in reverse order to an // array in the right order: var components = []; var nextComponent; - while (lastComponent) { components.push(lastComponent); nextComponent = lastComponent.previousComponent; delete lastComponent.previousComponent; lastComponent = nextComponent; } - components.reverse(); var componentPos = 0, - componentLen = components.length, - newPos = 0, - oldPos = 0; - + componentLen = components.length, + newPos = 0, + oldPos = 0; for (; componentPos < componentLen; componentPos++) { var component = components[componentPos]; - if (!component.removed) { if (!component.added && useLongestToken) { var value = newString.slice(newPos, newPos + component.count); @@ -271,36 +251,17 @@ function buildValues(diff, lastComponent, newString, oldString, useLongestToken) } else { component.value = diff.join(newString.slice(newPos, newPos + component.count)); } + newPos += component.count; - newPos += component.count; // Common case - + // Common case if (!component.added) { oldPos += component.count; } } else { component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); - oldPos += component.count; // Reverse add and remove so removes are output first to match common convention - // The diffing algorithm is tied to add then remove output and this is the simplest - // route to get the desired output with minimal overhead. - - if (componentPos && components[componentPos - 1].added) { - var tmp = components[componentPos - 1]; - components[componentPos - 1] = components[componentPos]; - components[componentPos] = tmp; - } + oldPos += component.count; } - } // Special case handle for when one terminal is ignored (i.e. whitespace). - // For this case we merge the terminal into the prior string and drop the change. - // This is only available for string mode. - - - var finalComponent = components[componentLen - 1]; - - if (componentLen > 1 && typeof finalComponent.value === 'string' && (finalComponent.added || finalComponent.removed) && diff.equals('', finalComponent.value)) { - components[componentLen - 2].value += finalComponent.value; - components.pop(); } - return components; } @@ -309,21 +270,114 @@ function diffChars(oldStr, newStr, options) { return characterDiff.diff(oldStr, newStr, options); } -function generateOptions(options, defaults) { - if (typeof options === 'function') { - defaults.callback = options; - } else if (options) { - for (var name in options) { - /* istanbul ignore else */ - if (options.hasOwnProperty(name)) { - defaults[name] = options[name]; - } +function longestCommonPrefix(str1, str2) { + var i; + for (i = 0; i < str1.length && i < str2.length; i++) { + if (str1[i] != str2[i]) { + return str1.slice(0, i); } } + return str1.slice(0, i); +} +function longestCommonSuffix(str1, str2) { + var i; - return defaults; + // Unlike longestCommonPrefix, we need a special case to handle all scenarios + // where we return the empty string since str1.slice(-0) will return the + // entire string. + if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) { + return ''; + } + for (i = 0; i < str1.length && i < str2.length; i++) { + if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) { + return str1.slice(-i); + } + } + return str1.slice(-i); +} +function replacePrefix(string, oldPrefix, newPrefix) { + if (string.slice(0, oldPrefix.length) != oldPrefix) { + throw Error("string ".concat(JSON.stringify(string), " doesn't start with prefix ").concat(JSON.stringify(oldPrefix), "; this is a bug")); + } + return newPrefix + string.slice(oldPrefix.length); +} +function replaceSuffix(string, oldSuffix, newSuffix) { + if (!oldSuffix) { + return string + newSuffix; + } + if (string.slice(-oldSuffix.length) != oldSuffix) { + throw Error("string ".concat(JSON.stringify(string), " doesn't end with suffix ").concat(JSON.stringify(oldSuffix), "; this is a bug")); + } + return string.slice(0, -oldSuffix.length) + newSuffix; +} +function removePrefix(string, oldPrefix) { + return replacePrefix(string, oldPrefix, ''); +} +function removeSuffix(string, oldSuffix) { + return replaceSuffix(string, oldSuffix, ''); +} +function maximumOverlap(string1, string2) { + return string2.slice(0, overlapCount(string1, string2)); +} + +// Nicked from https://stackoverflow.com/a/60422853/1709587 +function overlapCount(a, b) { + // Deal with cases where the strings differ in length + var startA = 0; + if (a.length > b.length) { + startA = a.length - b.length; + } + var endB = b.length; + if (a.length < b.length) { + endB = a.length; + } + // Create a back-reference for each index + // that should be followed in case of a mismatch. + // We only need B to make these references: + var map = Array(endB); + var k = 0; // Index that lags behind j + map[0] = 0; + for (var j = 1; j < endB; j++) { + if (b[j] == b[k]) { + map[j] = map[k]; // skip over the same character (optional optimisation) + } else { + map[j] = k; + } + while (k > 0 && b[j] != b[k]) { + k = map[k]; + } + if (b[j] == b[k]) { + k++; + } + } + // Phase 2: use these references while iterating over A + k = 0; + for (var i = startA; i < a.length; i++) { + while (k > 0 && a[i] != b[k]) { + k = map[k]; + } + if (a[i] == b[k]) { + k++; + } + } + return k; +} + +/** + * Returns true if the string consistently uses Windows line endings. + */ +function hasOnlyWinLineEndings(string) { + return string.includes('\r\n') && !string.startsWith('\n') && !string.match(/[^\r]\n/); +} + +/** + * Returns true if the string consistently uses Unix line endings. + */ +function hasOnlyUnixLineEndings(string) { + return !string.includes('\r\n') && string.includes('\n'); } +// Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode // // Ranges and exceptions: // Latin-1 Supplement, 0080–00FF @@ -341,82 +395,330 @@ function generateOptions(options, defaults) { // - U+02DC ˜ ˜ Small Tilde // - U+02DD ˝ ˝ Double Acute Accent // Latin Extended Additional, 1E00–1EFF +var extendedWordChars = "a-zA-Z0-9_\\u{C0}-\\u{FF}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}"; -var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; -var reWhitespace = /\S/; +// Each token is one of the following: +// - A punctuation mark plus the surrounding whitespace +// - A word plus the surrounding whitespace +// - Pure whitespace (but only in the special case where this the entire text +// is just whitespace) +// +// We have to include surrounding whitespace in the tokens because the two +// alternative approaches produce horribly broken results: +// * If we just discard the whitespace, we can't fully reproduce the original +// text from the sequence of tokens and any attempt to render the diff will +// get the whitespace wrong. +// * If we have separate tokens for whitespace, then in a typical text every +// second token will be a single space character. But this often results in +// the optimal diff between two texts being a perverse one that preserves +// the spaces between words but deletes and reinserts actual common words. +// See https://github.com/kpdecker/jsdiff/issues/160#issuecomment-1866099640 +// for an example. +// +// Keeping the surrounding whitespace of course has implications for .equals +// and .join, not just .tokenize. + +// This regex does NOT fully implement the tokenization rules described above. +// Instead, it gives runs of whitespace their own "token". The tokenize method +// then handles stitching whitespace tokens onto adjacent word or punctuation +// tokens. +var tokenizeIncludingWhitespace = new RegExp("[".concat(extendedWordChars, "]+|\\s+|[^").concat(extendedWordChars, "]"), 'ug'); var wordDiff = new Diff(); - -wordDiff.equals = function (left, right) { - if (this.options.ignoreCase) { +wordDiff.equals = function (left, right, options) { + if (options.ignoreCase) { left = left.toLowerCase(); right = right.toLowerCase(); } - - return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); + return left.trim() === right.trim(); }; - wordDiff.tokenize = function (value) { - // All whitespace symbols except newline group into one token, each newline - in separate token - var tokens = value.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/); // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. - - for (var i = 0; i < tokens.length - 1; i++) { - // If we have an empty string in the next field and we have only word chars before and after, merge - if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { - tokens[i] += tokens[i + 2]; - tokens.splice(i + 1, 2); - i--; + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var parts; + if (options.intlSegmenter) { + if (options.intlSegmenter.resolvedOptions().granularity != 'word') { + throw new Error('The segmenter passed must have a granularity of "word"'); } + parts = Array.from(options.intlSegmenter.segment(value), function (segment) { + return segment.segment; + }); + } else { + parts = value.match(tokenizeIncludingWhitespace) || []; } - + var tokens = []; + var prevPart = null; + parts.forEach(function (part) { + if (/\s/.test(part)) { + if (prevPart == null) { + tokens.push(part); + } else { + tokens.push(tokens.pop() + part); + } + } else if (/\s/.test(prevPart)) { + if (tokens[tokens.length - 1] == prevPart) { + tokens.push(tokens.pop() + part); + } else { + tokens.push(prevPart + part); + } + } else { + tokens.push(part); + } + prevPart = part; + }); return tokens; }; - -function diffWords(oldStr, newStr, options) { - options = generateOptions(options, { - ignoreWhitespace: true +wordDiff.join = function (tokens) { + // Tokens being joined here will always have appeared consecutively in the + // same text, so we can simply strip off the leading whitespace from all the + // tokens except the first (and except any whitespace-only tokens - but such + // a token will always be the first and only token anyway) and then join them + // and the whitespace around words and punctuation will end up correct. + return tokens.map(function (token, i) { + if (i == 0) { + return token; + } else { + return token.replace(/^\s+/, ''); + } + }).join(''); +}; +wordDiff.postProcess = function (changes, options) { + if (!changes || options.oneChangePerToken) { + return changes; + } + var lastKeep = null; + // Change objects representing any insertion or deletion since the last + // "keep" change object. There can be at most one of each. + var insertion = null; + var deletion = null; + changes.forEach(function (change) { + if (change.added) { + insertion = change; + } else if (change.removed) { + deletion = change; + } else { + if (insertion || deletion) { + // May be false at start of text + dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, change); + } + lastKeep = change; + insertion = null; + deletion = null; + } }); + if (insertion || deletion) { + dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, null); + } + return changes; +}; +function diffWords(oldStr, newStr, options) { + // This option has never been documented and never will be (it's clearer to + // just call `diffWordsWithSpace` directly if you need that behavior), but + // has existed in jsdiff for a long time, so we retain support for it here + // for the sake of backwards compatibility. + if ((options === null || options === void 0 ? void 0 : options.ignoreWhitespace) != null && !options.ignoreWhitespace) { + return diffWordsWithSpace(oldStr, newStr, options); + } return wordDiff.diff(oldStr, newStr, options); } +function dedupeWhitespaceInChangeObjects(startKeep, deletion, insertion, endKeep) { + // Before returning, we tidy up the leading and trailing whitespace of the + // change objects to eliminate cases where trailing whitespace in one object + // is repeated as leading whitespace in the next. + // Below are examples of the outcomes we want here to explain the code. + // I=insert, K=keep, D=delete + // 1. diffing 'foo bar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz' + // After cleanup, we want: K:'foo ' D:'bar ' K:'baz' + // + // 2. Diffing 'foo bar baz' vs 'foo qux baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' I:' qux ' K:' baz' + // After cleanup, we want K:'foo ' D:'bar' I:'qux' K:' baz' + // + // 3. Diffing 'foo\nbar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:'\nbar ' K:' baz' + // After cleanup, we want K'foo' D:'\nbar' K:' baz' + // + // 4. Diffing 'foo baz' vs 'foo\nbar baz' + // Prior to cleanup, we have K:'foo\n' I:'\nbar ' K:' baz' + // After cleanup, we ideally want K'foo' I:'\nbar' K:' baz' + // but don't actually manage this currently (the pre-cleanup change + // objects don't contain enough information to make it possible). + // + // 5. Diffing 'foo bar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz' + // After cleanup, we want K:'foo ' D:' bar ' K:'baz' + // + // Our handling is unavoidably imperfect in the case where there's a single + // indel between keeps and the whitespace has changed. For instance, consider + // diffing 'foo\tbar\nbaz' vs 'foo baz'. Unless we create an extra change + // object to represent the insertion of the space character (which isn't even + // a token), we have no way to avoid losing information about the texts' + // original whitespace in the result we return. Still, we do our best to + // output something that will look sensible if we e.g. print it with + // insertions in green and deletions in red. + + // Between two "keep" change objects (or before the first or after the last + // change object), we can have either: + // * A "delete" followed by an "insert" + // * Just an "insert" + // * Just a "delete" + // We handle the three cases separately. + if (deletion && insertion) { + var oldWsPrefix = deletion.value.match(/^\s*/)[0]; + var oldWsSuffix = deletion.value.match(/\s*$/)[0]; + var newWsPrefix = insertion.value.match(/^\s*/)[0]; + var newWsSuffix = insertion.value.match(/\s*$/)[0]; + if (startKeep) { + var commonWsPrefix = longestCommonPrefix(oldWsPrefix, newWsPrefix); + startKeep.value = replaceSuffix(startKeep.value, newWsPrefix, commonWsPrefix); + deletion.value = removePrefix(deletion.value, commonWsPrefix); + insertion.value = removePrefix(insertion.value, commonWsPrefix); + } + if (endKeep) { + var commonWsSuffix = longestCommonSuffix(oldWsSuffix, newWsSuffix); + endKeep.value = replacePrefix(endKeep.value, newWsSuffix, commonWsSuffix); + deletion.value = removeSuffix(deletion.value, commonWsSuffix); + insertion.value = removeSuffix(insertion.value, commonWsSuffix); + } + } else if (insertion) { + // The whitespaces all reflect what was in the new text rather than + // the old, so we essentially have no information about whitespace + // insertion or deletion. We just want to dedupe the whitespace. + // We do that by having each change object keep its trailing + // whitespace and deleting duplicate leading whitespace where + // present. + if (startKeep) { + insertion.value = insertion.value.replace(/^\s*/, ''); + } + if (endKeep) { + endKeep.value = endKeep.value.replace(/^\s*/, ''); + } + // otherwise we've got a deletion and no insertion + } else if (startKeep && endKeep) { + var newWsFull = endKeep.value.match(/^\s*/)[0], + delWsStart = deletion.value.match(/^\s*/)[0], + delWsEnd = deletion.value.match(/\s*$/)[0]; + + // Any whitespace that comes straight after startKeep in both the old and + // new texts, assign to startKeep and remove from the deletion. + var newWsStart = longestCommonPrefix(newWsFull, delWsStart); + deletion.value = removePrefix(deletion.value, newWsStart); + + // Any whitespace that comes straight before endKeep in both the old and + // new texts, and hasn't already been assigned to startKeep, assign to + // endKeep and remove from the deletion. + var newWsEnd = longestCommonSuffix(removePrefix(newWsFull, newWsStart), delWsEnd); + deletion.value = removeSuffix(deletion.value, newWsEnd); + endKeep.value = replacePrefix(endKeep.value, newWsFull, newWsEnd); + + // If there's any whitespace from the new text that HASN'T already been + // assigned, assign it to the start: + startKeep.value = replaceSuffix(startKeep.value, newWsFull, newWsFull.slice(0, newWsFull.length - newWsEnd.length)); + } else if (endKeep) { + // We are at the start of the text. Preserve all the whitespace on + // endKeep, and just remove whitespace from the end of deletion to the + // extent that it overlaps with the start of endKeep. + var endKeepWsPrefix = endKeep.value.match(/^\s*/)[0]; + var deletionWsSuffix = deletion.value.match(/\s*$/)[0]; + var overlap = maximumOverlap(deletionWsSuffix, endKeepWsPrefix); + deletion.value = removeSuffix(deletion.value, overlap); + } else if (startKeep) { + // We are at the END of the text. Preserve all the whitespace on + // startKeep, and just remove whitespace from the start of deletion to + // the extent that it overlaps with the end of startKeep. + var startKeepWsSuffix = startKeep.value.match(/\s*$/)[0]; + var deletionWsPrefix = deletion.value.match(/^\s*/)[0]; + var _overlap = maximumOverlap(startKeepWsSuffix, deletionWsPrefix); + deletion.value = removePrefix(deletion.value, _overlap); + } +} +var wordWithSpaceDiff = new Diff(); +wordWithSpaceDiff.tokenize = function (value) { + // Slightly different to the tokenizeIncludingWhitespace regex used above in + // that this one treats each individual newline as a distinct tokens, rather + // than merging them into other surrounding whitespace. This was requested + // in https://github.com/kpdecker/jsdiff/issues/180 & + // https://github.com/kpdecker/jsdiff/issues/211 + var regex = new RegExp("(\\r?\\n)|[".concat(extendedWordChars, "]+|[^\\S\\n\\r]+|[^").concat(extendedWordChars, "]"), 'ug'); + return value.match(regex) || []; +}; function diffWordsWithSpace(oldStr, newStr, options) { - return wordDiff.diff(oldStr, newStr, options); + return wordWithSpaceDiff.diff(oldStr, newStr, options); } -var lineDiff = new Diff(); +function generateOptions(options, defaults) { + if (typeof options === 'function') { + defaults.callback = options; + } else if (options) { + for (var name in options) { + /* istanbul ignore else */ + if (options.hasOwnProperty(name)) { + defaults[name] = options[name]; + } + } + } + return defaults; +} -lineDiff.tokenize = function (value) { - if (this.options.stripTrailingCr) { +var lineDiff = new Diff(); +lineDiff.tokenize = function (value, options) { + if (options.stripTrailingCr) { // remove one \r before \n to match GNU diff's --strip-trailing-cr behavior value = value.replace(/\r\n/g, '\n'); } - var retLines = [], - linesAndNewlines = value.split(/(\n|\r\n)/); // Ignore the final empty token that occurs if the string ends with a new line + linesAndNewlines = value.split(/(\n|\r\n)/); + // Ignore the final empty token that occurs if the string ends with a new line if (!linesAndNewlines[linesAndNewlines.length - 1]) { linesAndNewlines.pop(); - } // Merge the content and line separators into single tokens - + } + // Merge the content and line separators into single tokens for (var i = 0; i < linesAndNewlines.length; i++) { var line = linesAndNewlines[i]; - - if (i % 2 && !this.options.newlineIsToken) { + if (i % 2 && !options.newlineIsToken) { retLines[retLines.length - 1] += line; } else { - if (this.options.ignoreWhitespace) { - line = line.trim(); - } - retLines.push(line); } } - return retLines; }; - +lineDiff.equals = function (left, right, options) { + // If we're ignoring whitespace, we need to normalise lines by stripping + // whitespace before checking equality. (This has an annoying interaction + // with newlineIsToken that requires special handling: if newlines get their + // own token, then we DON'T want to trim the *newline* tokens down to empty + // strings, since this would cause us to treat whitespace-only line content + // as equal to a separator between lines, which would be weird and + // inconsistent with the documented behavior of the options.) + if (options.ignoreWhitespace) { + if (!options.newlineIsToken || !left.includes('\n')) { + left = left.trim(); + } + if (!options.newlineIsToken || !right.includes('\n')) { + right = right.trim(); + } + } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) { + if (left.endsWith('\n')) { + left = left.slice(0, -1); + } + if (right.endsWith('\n')) { + right = right.slice(0, -1); + } + } + return Diff.prototype.equals.call(this, left, right, options); +}; function diffLines(oldStr, newStr, callback) { return lineDiff.diff(oldStr, newStr, callback); } + +// Kept for backwards compatibility. This is a rather arbitrary wrapper method +// that just calls `diffLines` with `ignoreWhitespace: true`. It's confusing to +// have two ways to do exactly the same thing in the API, so we no longer +// document this one (library users should explicitly use `diffLines` with +// `ignoreWhitespace: true` instead) but we keep it around to maintain +// compatibility with code that used old versions. function diffTrimmedLines(oldStr, newStr, callback) { var options = generateOptions(callback, { ignoreWhitespace: true @@ -425,42 +727,67 @@ function diffTrimmedLines(oldStr, newStr, callback) { } var sentenceDiff = new Diff(); - sentenceDiff.tokenize = function (value) { return value.split(/(\S.+?[.!?])(?=\s+|$)/); }; - function diffSentences(oldStr, newStr, callback) { return sentenceDiff.diff(oldStr, newStr, callback); } var cssDiff = new Diff(); - cssDiff.tokenize = function (value) { return value.split(/([{}:;,]|\s+)/); }; - function diffCss(oldStr, newStr, callback) { return cssDiff.diff(oldStr, newStr, callback); } -function _typeof(obj) { - "@babel/helpers - typeof"; - - if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { - _typeof = function (obj) { - return typeof obj; - }; - } else { - _typeof = function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }; +function ownKeys(e, r) { + var t = Object.keys(e); + if (Object.getOwnPropertySymbols) { + var o = Object.getOwnPropertySymbols(e); + r && (o = o.filter(function (r) { + return Object.getOwnPropertyDescriptor(e, r).enumerable; + })), t.push.apply(t, o); } - - return _typeof(obj); + return t; +} +function _objectSpread2(e) { + for (var r = 1; r < arguments.length; r++) { + var t = null != arguments[r] ? arguments[r] : {}; + r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { + _defineProperty(e, r, t[r]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { + Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); + }); + } + return e; +} +function _toPrimitive(t, r) { + if ("object" != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != typeof i) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return ("string" === r ? String : Number)(t); +} +function _toPropertyKey(t) { + var i = _toPrimitive(t, "string"); + return "symbol" == typeof i ? i : i + ""; } +function _typeof(o) { + "@babel/helpers - typeof"; + return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { + return typeof o; + } : function (o) { + return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; + }, _typeof(o); +} function _defineProperty(obj, key, value) { + key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, @@ -471,56 +798,17 @@ function _defineProperty(obj, key, value) { } else { obj[key] = value; } - return obj; } - -function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - if (enumerableOnly) symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - }); - keys.push.apply(keys, symbols); - } - - return keys; -} - -function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i] != null ? arguments[i] : {}; - - if (i % 2) { - ownKeys(Object(source), true).forEach(function (key) { - _defineProperty(target, key, source[key]); - }); - } else if (Object.getOwnPropertyDescriptors) { - Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); - } else { - ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - } - - return target; -} - function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } - function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } - function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); + if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } - function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); @@ -529,238 +817,263 @@ function _unsupportedIterableToArray(o, minLen) { if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; } - function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } -var objectPrototypeToString = Object.prototype.toString; -var jsonDiff = new Diff(); // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a +var jsonDiff = new Diff(); +// Discriminate between two lines of pretty-printed, serialized JSON where one of them has a // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: - jsonDiff.useLongestToken = true; jsonDiff.tokenize = lineDiff.tokenize; - -jsonDiff.castInput = function (value) { - var _this$options = this.options, - undefinedReplacement = _this$options.undefinedReplacement, - _this$options$stringi = _this$options.stringifyReplacer, - stringifyReplacer = _this$options$stringi === void 0 ? function (k, v) { - return typeof v === 'undefined' ? undefinedReplacement : v; - } : _this$options$stringi; +jsonDiff.castInput = function (value, options) { + var undefinedReplacement = options.undefinedReplacement, + _options$stringifyRep = options.stringifyReplacer, + stringifyReplacer = _options$stringifyRep === void 0 ? function (k, v) { + return typeof v === 'undefined' ? undefinedReplacement : v; + } : _options$stringifyRep; return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); }; - -jsonDiff.equals = function (left, right) { - return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); +jsonDiff.equals = function (left, right, options) { + return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'), options); }; - function diffJson(oldObj, newObj, options) { return jsonDiff.diff(oldObj, newObj, options); -} // This function handles the presence of circular references by bailing out when encountering an -// object that is already on the "stack" of items being processed. Accepts an optional replacer +} +// This function handles the presence of circular references by bailing out when encountering an +// object that is already on the "stack" of items being processed. Accepts an optional replacer function canonicalize(obj, stack, replacementStack, replacer, key) { stack = stack || []; replacementStack = replacementStack || []; - if (replacer) { obj = replacer(key, obj); } - var i; - for (i = 0; i < stack.length; i += 1) { if (stack[i] === obj) { return replacementStack[i]; } } - var canonicalizedObj; - - if ('[object Array]' === objectPrototypeToString.call(obj)) { + if ('[object Array]' === Object.prototype.toString.call(obj)) { stack.push(obj); canonicalizedObj = new Array(obj.length); replacementStack.push(canonicalizedObj); - for (i = 0; i < obj.length; i += 1) { canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); } - stack.pop(); replacementStack.pop(); return canonicalizedObj; } - if (obj && obj.toJSON) { obj = obj.toJSON(); } - if (_typeof(obj) === 'object' && obj !== null) { stack.push(obj); canonicalizedObj = {}; replacementStack.push(canonicalizedObj); - var sortedKeys = [], - _key; - + _key; for (_key in obj) { /* istanbul ignore else */ - if (obj.hasOwnProperty(_key)) { + if (Object.prototype.hasOwnProperty.call(obj, _key)) { sortedKeys.push(_key); } } - sortedKeys.sort(); - for (i = 0; i < sortedKeys.length; i += 1) { _key = sortedKeys[i]; canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); } - stack.pop(); replacementStack.pop(); } else { canonicalizedObj = obj; } - return canonicalizedObj; } var arrayDiff = new Diff(); - arrayDiff.tokenize = function (value) { return value.slice(); }; - arrayDiff.join = arrayDiff.removeEmpty = function (value) { return value; }; - function diffArrays(oldArr, newArr, callback) { return arrayDiff.diff(oldArr, newArr, callback); } -function parsePatch(uniDiff) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], - list = [], - i = 0; +function unixToWin(patch) { + if (Array.isArray(patch)) { + return patch.map(unixToWin); + } + return _objectSpread2(_objectSpread2({}, patch), {}, { + hunks: patch.hunks.map(function (hunk) { + return _objectSpread2(_objectSpread2({}, hunk), {}, { + lines: hunk.lines.map(function (line, i) { + var _hunk$lines; + return line.startsWith('\\') || line.endsWith('\r') || (_hunk$lines = hunk.lines[i + 1]) !== null && _hunk$lines !== void 0 && _hunk$lines.startsWith('\\') ? line : line + '\r'; + }) + }); + }) + }); +} +function winToUnix(patch) { + if (Array.isArray(patch)) { + return patch.map(winToUnix); + } + return _objectSpread2(_objectSpread2({}, patch), {}, { + hunks: patch.hunks.map(function (hunk) { + return _objectSpread2(_objectSpread2({}, hunk), {}, { + lines: hunk.lines.map(function (line) { + return line.endsWith('\r') ? line.substring(0, line.length - 1) : line; + }) + }); + }) + }); +} + +/** + * Returns true if the patch consistently uses Unix line endings (or only involves one line and has + * no line endings). + */ +function isUnix(patch) { + if (!Array.isArray(patch)) { + patch = [patch]; + } + return !patch.some(function (index) { + return index.hunks.some(function (hunk) { + return hunk.lines.some(function (line) { + return !line.startsWith('\\') && line.endsWith('\r'); + }); + }); + }); +} + +/** + * Returns true if the patch uses Windows line endings and only Windows line endings. + */ +function isWin(patch) { + if (!Array.isArray(patch)) { + patch = [patch]; + } + return patch.some(function (index) { + return index.hunks.some(function (hunk) { + return hunk.lines.some(function (line) { + return line.endsWith('\r'); + }); + }); + }) && patch.every(function (index) { + return index.hunks.every(function (hunk) { + return hunk.lines.every(function (line, i) { + var _hunk$lines2; + return line.startsWith('\\') || line.endsWith('\r') || ((_hunk$lines2 = hunk.lines[i + 1]) === null || _hunk$lines2 === void 0 ? void 0 : _hunk$lines2.startsWith('\\')); + }); + }); + }); +} +function parsePatch(uniDiff) { + var diffstr = uniDiff.split(/\n/), + list = [], + i = 0; function parseIndex() { var index = {}; - list.push(index); // Parse diff metadata + list.push(index); + // Parse diff metadata while (i < diffstr.length) { - var line = diffstr[i]; // File header found, end parsing diff metadata + var line = diffstr[i]; + // File header found, end parsing diff metadata if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { break; - } // Diff index - + } + // Diff index var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); - if (header) { index.index = header[1]; } - i++; - } // Parse file headers if they are defined. Unified diff requires them, but - // there's no technical issues to have an isolated hunk without file header - + } + // Parse file headers if they are defined. Unified diff requires them, but + // there's no technical issues to have an isolated hunk without file header + parseFileHeader(index); parseFileHeader(index); - parseFileHeader(index); // Parse hunks + // Parse hunks index.hunks = []; - while (i < diffstr.length) { var _line = diffstr[i]; - - if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { + if (/^(Index:\s|diff\s|\-\-\-\s|\+\+\+\s|===================================================================)/.test(_line)) { break; } else if (/^@@/.test(_line)) { index.hunks.push(parseHunk()); - } else if (_line && options.strict) { - // Ignore unexpected content unless in strict mode + } else if (_line) { throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); } else { i++; } } - } // Parses the --- and +++ headers, if none are found, no lines - // are consumed. - + } + // Parses the --- and +++ headers, if none are found, no lines + // are consumed. function parseFileHeader(index) { - var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); - + var fileHeader = /^(---|\+\+\+)\s+(.*)\r?$/.exec(diffstr[i]); if (fileHeader) { var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; var data = fileHeader[2].split('\t', 2); var fileName = data[0].replace(/\\\\/g, '\\'); - if (/^".*"$/.test(fileName)) { fileName = fileName.substr(1, fileName.length - 2); } - index[keyPrefix + 'FileName'] = fileName; index[keyPrefix + 'Header'] = (data[1] || '').trim(); i++; } - } // Parses a hunk - // This assumes that we are at the start of a hunk. - + } + // Parses a hunk + // This assumes that we are at the start of a hunk. function parseHunk() { var chunkHeaderIndex = i, - chunkHeaderLine = diffstr[i++], - chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); + chunkHeaderLine = diffstr[i++], + chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); var hunk = { oldStart: +chunkHeader[1], oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2], newStart: +chunkHeader[3], newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4], - lines: [], - linedelimiters: [] - }; // Unified Diff Format quirk: If the chunk size is 0, + lines: [] + }; + + // Unified Diff Format quirk: If the chunk size is 0, // the first number is one lower than one would expect. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 - if (hunk.oldLines === 0) { hunk.oldStart += 1; } - if (hunk.newLines === 0) { hunk.newStart += 1; } - var addCount = 0, - removeCount = 0; - - for (; i < diffstr.length; i++) { - // Lines starting with '---' could be mistaken for the "remove line" operation - // But they could be the header for the next file. Therefore prune such cases out. - if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { - break; - } - + removeCount = 0; + for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || (_diffstr$i = diffstr[i]) !== null && _diffstr$i !== void 0 && _diffstr$i.startsWith('\\')); i++) { + var _diffstr$i; var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; - if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { hunk.lines.push(diffstr[i]); - hunk.linedelimiters.push(delimiters[i] || '\n'); - if (operation === '+') { addCount++; } else if (operation === '-') { @@ -770,37 +1083,30 @@ function parsePatch(uniDiff) { removeCount++; } } else { - break; + throw new Error("Hunk at line ".concat(chunkHeaderIndex + 1, " contained invalid line ").concat(diffstr[i])); } - } // Handle the empty block count case - + } + // Handle the empty block count case if (!addCount && hunk.newLines === 1) { hunk.newLines = 0; } - if (!removeCount && hunk.oldLines === 1) { hunk.oldLines = 0; - } // Perform optional sanity checking - - - if (options.strict) { - if (addCount !== hunk.newLines) { - throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } - - if (removeCount !== hunk.oldLines) { - throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } } + // Perform sanity checking + if (addCount !== hunk.newLines) { + throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + if (removeCount !== hunk.oldLines) { + throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } return hunk; } - while (i < diffstr.length) { parseIndex(); } - return list; } @@ -809,210 +1115,275 @@ function parsePatch(uniDiff) { // start of 2, this will iterate 2, 3, 1, 4, 0. function distanceIterator (start, minLine, maxLine) { var wantForward = true, - backwardExhausted = false, - forwardExhausted = false, - localOffset = 1; + backwardExhausted = false, + forwardExhausted = false, + localOffset = 1; return function iterator() { if (wantForward && !forwardExhausted) { if (backwardExhausted) { localOffset++; } else { wantForward = false; - } // Check if trying to fit beyond text length, and if not, check it fits - // after offset location (or desired location on first iteration) - + } + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) if (start + localOffset <= maxLine) { - return localOffset; + return start + localOffset; } - forwardExhausted = true; } - if (!backwardExhausted) { if (!forwardExhausted) { wantForward = true; - } // Check if trying to fit before text beginning, and if not, check it fits - // before offset location - + } + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location if (minLine <= start - localOffset) { - return -localOffset++; + return start - localOffset++; } - backwardExhausted = true; return iterator(); - } // We tried to fit hunk before text beginning and beyond text length, then - // hunk can't fit on the text. Return undefined + } + // We tried to fit hunk before text beginning and beyond text length, then + // hunk can't fit on the text. Return undefined }; } function applyPatch(source, uniDiff) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - if (typeof uniDiff === 'string') { uniDiff = parsePatch(uniDiff); } - if (Array.isArray(uniDiff)) { if (uniDiff.length > 1) { throw new Error('applyPatch only works with a single input.'); } - uniDiff = uniDiff[0]; - } // Apply the diff to the input - - - var lines = source.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], - hunks = uniDiff.hunks, - compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) { - return line === patchContent; - }, - errorCount = 0, - fuzzFactor = options.fuzzFactor || 0, - minLine = 0, - offset = 0, - removeEOFNL, - addEOFNL; - /** - * Checks if the hunk exactly fits on the provided location - */ + } + if (options.autoConvertLineEndings || options.autoConvertLineEndings == null) { + if (hasOnlyWinLineEndings(source) && isUnix(uniDiff)) { + uniDiff = unixToWin(uniDiff); + } else if (hasOnlyUnixLineEndings(source) && isWin(uniDiff)) { + uniDiff = winToUnix(uniDiff); + } + } + // Apply the diff to the input + var lines = source.split('\n'), + hunks = uniDiff.hunks, + compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) { + return line === patchContent; + }, + fuzzFactor = options.fuzzFactor || 0, + minLine = 0; + if (fuzzFactor < 0 || !Number.isInteger(fuzzFactor)) { + throw new Error('fuzzFactor must be a non-negative integer'); + } - function hunkFits(hunk, toPos) { - for (var j = 0; j < hunk.lines.length; j++) { - var line = hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line; + // Special case for empty patch. + if (!hunks.length) { + return source; + } - if (operation === ' ' || operation === '-') { - // Context sanity check - if (!compareLine(toPos + 1, lines[toPos], operation, content)) { - errorCount++; + // Before anything else, handle EOFNL insertion/removal. If the patch tells us to make a change + // to the EOFNL that is redundant/impossible - i.e. to remove a newline that's not there, or add a + // newline that already exists - then we either return false and fail to apply the patch (if + // fuzzFactor is 0) or simply ignore the problem and do nothing (if fuzzFactor is >0). + // If we do need to remove/add a newline at EOF, this will always be in the final hunk: + var prevLine = '', + removeEOFNL = false, + addEOFNL = false; + for (var i = 0; i < hunks[hunks.length - 1].lines.length; i++) { + var line = hunks[hunks.length - 1].lines[i]; + if (line[0] == '\\') { + if (prevLine[0] == '+') { + removeEOFNL = true; + } else if (prevLine[0] == '-') { + addEOFNL = true; + } + } + prevLine = line; + } + if (removeEOFNL) { + if (addEOFNL) { + // This means the final line gets changed but doesn't have a trailing newline in either the + // original or patched version. In that case, we do nothing if fuzzFactor > 0, and if + // fuzzFactor is 0, we simply validate that the source file has no trailing newline. + if (!fuzzFactor && lines[lines.length - 1] == '') { + return false; + } + } else if (lines[lines.length - 1] == '') { + lines.pop(); + } else if (!fuzzFactor) { + return false; + } + } else if (addEOFNL) { + if (lines[lines.length - 1] != '') { + lines.push(''); + } else if (!fuzzFactor) { + return false; + } + } - if (errorCount > fuzzFactor) { - return false; + /** + * Checks if the hunk can be made to fit at the provided location with at most `maxErrors` + * insertions, substitutions, or deletions, while ensuring also that: + * - lines deleted in the hunk match exactly, and + * - wherever an insertion operation or block of insertion operations appears in the hunk, the + * immediately preceding and following lines of context match exactly + * + * `toPos` should be set such that lines[toPos] is meant to match hunkLines[0]. + * + * If the hunk can be applied, returns an object with properties `oldLineLastI` and + * `replacementLines`. Otherwise, returns null. + */ + function applyHunk(hunkLines, toPos, maxErrors) { + var hunkLinesI = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; + var lastContextLineMatched = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; + var patchedLines = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : []; + var patchedLinesLength = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0; + var nConsecutiveOldContextLines = 0; + var nextContextLineMustMatch = false; + for (; hunkLinesI < hunkLines.length; hunkLinesI++) { + var hunkLine = hunkLines[hunkLinesI], + operation = hunkLine.length > 0 ? hunkLine[0] : ' ', + content = hunkLine.length > 0 ? hunkLine.substr(1) : hunkLine; + if (operation === '-') { + if (compareLine(toPos + 1, lines[toPos], operation, content)) { + toPos++; + nConsecutiveOldContextLines = 0; + } else { + if (!maxErrors || lines[toPos] == null) { + return null; } + patchedLines[patchedLinesLength] = lines[toPos]; + return applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1); } + } + if (operation === '+') { + if (!lastContextLineMatched) { + return null; + } + patchedLines[patchedLinesLength] = content; + patchedLinesLength++; + nConsecutiveOldContextLines = 0; + nextContextLineMustMatch = true; + } + if (operation === ' ') { + nConsecutiveOldContextLines++; + patchedLines[patchedLinesLength] = lines[toPos]; + if (compareLine(toPos + 1, lines[toPos], operation, content)) { + patchedLinesLength++; + lastContextLineMatched = true; + nextContextLineMustMatch = false; + toPos++; + } else { + if (nextContextLineMustMatch || !maxErrors) { + return null; + } - toPos++; + // Consider 3 possibilities in sequence: + // 1. lines contains a *substitution* not included in the patch context, or + // 2. lines contains an *insertion* not included in the patch context, or + // 3. lines contains a *deletion* not included in the patch context + // The first two options are of course only possible if the line from lines is non-null - + // i.e. only option 3 is possible if we've overrun the end of the old file. + return lines[toPos] && (applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength + 1) || applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1)) || applyHunk(hunkLines, toPos, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength); + } } } - return true; - } // Search best fit offsets for each hunk based on the previous ones - - - for (var i = 0; i < hunks.length; i++) { - var hunk = hunks[i], - maxLine = lines.length - hunk.oldLines, - localOffset = 0, - toPos = offset + hunk.oldStart - 1; - var iterator = distanceIterator(toPos, minLine, maxLine); + // Before returning, trim any unmodified context lines off the end of patchedLines and reduce + // toPos (and thus oldLineLastI) accordingly. This allows later hunks to be applied to a region + // that starts in this hunk's trailing context. + patchedLinesLength -= nConsecutiveOldContextLines; + toPos -= nConsecutiveOldContextLines; + patchedLines.length = patchedLinesLength; + return { + patchedLines: patchedLines, + oldLineLastI: toPos - 1 + }; + } + var resultLines = []; - for (; localOffset !== undefined; localOffset = iterator()) { - if (hunkFits(hunk, toPos + localOffset)) { - hunk.offset = offset += localOffset; + // Search best fit offsets for each hunk based on the previous ones + var prevHunkOffset = 0; + for (var _i = 0; _i < hunks.length; _i++) { + var hunk = hunks[_i]; + var hunkResult = void 0; + var maxLine = lines.length - hunk.oldLines + fuzzFactor; + var toPos = void 0; + for (var maxErrors = 0; maxErrors <= fuzzFactor; maxErrors++) { + toPos = hunk.oldStart + prevHunkOffset - 1; + var iterator = distanceIterator(toPos, minLine, maxLine); + for (; toPos !== undefined; toPos = iterator()) { + hunkResult = applyHunk(hunk.lines, toPos, maxErrors); + if (hunkResult) { + break; + } + } + if (hunkResult) { break; } } - - if (localOffset === undefined) { + if (!hunkResult) { return false; - } // Set lower text limit to end of the current hunk, so next ones don't try - // to fit over already patched text - - - minLine = hunk.offset + hunk.oldStart + hunk.oldLines; - } // Apply patch hunks - - - var diffOffset = 0; - - for (var _i = 0; _i < hunks.length; _i++) { - var _hunk = hunks[_i], - _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; - - diffOffset += _hunk.newLines - _hunk.oldLines; + } - for (var j = 0; j < _hunk.lines.length; j++) { - var line = _hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line, - delimiter = _hunk.linedelimiters && _hunk.linedelimiters[j] || '\n'; + // Copy everything from the end of where we applied the last hunk to the start of this hunk + for (var _i2 = minLine; _i2 < toPos; _i2++) { + resultLines.push(lines[_i2]); + } - if (operation === ' ') { - _toPos++; - } else if (operation === '-') { - lines.splice(_toPos, 1); - delimiters.splice(_toPos, 1); - /* istanbul ignore else */ - } else if (operation === '+') { - lines.splice(_toPos, 0, content); - delimiters.splice(_toPos, 0, delimiter); - _toPos++; - } else if (operation === '\\') { - var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; - - if (previousOperation === '+') { - removeEOFNL = true; - } else if (previousOperation === '-') { - addEOFNL = true; - } - } + // Add the lines produced by applying the hunk: + for (var _i3 = 0; _i3 < hunkResult.patchedLines.length; _i3++) { + var _line = hunkResult.patchedLines[_i3]; + resultLines.push(_line); } - } // Handle EOFNL insertion/removal + // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text + minLine = hunkResult.oldLineLastI + 1; - if (removeEOFNL) { - while (!lines[lines.length - 1]) { - lines.pop(); - delimiters.pop(); - } - } else if (addEOFNL) { - lines.push(''); - delimiters.push('\n'); + // Note the offset between where the patch said the hunk should've applied and where we + // applied it, so we can adjust future hunks accordingly: + prevHunkOffset = toPos + 1 - hunk.oldStart; } - for (var _k = 0; _k < lines.length - 1; _k++) { - lines[_k] = lines[_k] + delimiters[_k]; + // Copy over the rest of the lines from the old text + for (var _i4 = minLine; _i4 < lines.length; _i4++) { + resultLines.push(lines[_i4]); } + return resultLines.join('\n'); +} - return lines.join(''); -} // Wrapper that supports multiple file patches via callbacks. - +// Wrapper that supports multiple file patches via callbacks. function applyPatches(uniDiff, options) { if (typeof uniDiff === 'string') { uniDiff = parsePatch(uniDiff); } - var currentIndex = 0; - function processIndex() { var index = uniDiff[currentIndex++]; - if (!index) { return options.complete(); } - options.loadFile(index, function (err, data) { if (err) { return options.complete(err); } - var updatedContent = applyPatch(data, index, options); options.patched(index, updatedContent, function (err) { if (err) { return options.complete(err); } - processIndex(); }); }); } - processIndex(); } @@ -1020,206 +1391,238 @@ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, ne if (!options) { options = {}; } - + if (typeof options === 'function') { + options = { + callback: options + }; + } if (typeof options.context === 'undefined') { options.context = 4; } - - var diff = diffLines(oldStr, newStr, options); - - if (!diff) { - return; + if (options.newlineIsToken) { + throw new Error('newlineIsToken may not be used with patch-generation functions, only with diffing functions'); } - - diff.push({ - value: '', - lines: [] - }); // Append an empty value to make cleanup easier - - function contextLines(lines) { - return lines.map(function (entry) { - return ' ' + entry; - }); + if (!options.callback) { + return diffLinesResultToPatch(diffLines(oldStr, newStr, options)); + } else { + var _options = options, + _callback = _options.callback; + diffLines(oldStr, newStr, _objectSpread2(_objectSpread2({}, options), {}, { + callback: function callback(diff) { + var patch = diffLinesResultToPatch(diff); + _callback(patch); + } + })); } + function diffLinesResultToPatch(diff) { + // STEP 1: Build up the patch with no "\ No newline at end of file" lines and with the arrays + // of lines containing trailing newline characters. We'll tidy up later... - var hunks = []; - var oldRangeStart = 0, + if (!diff) { + return; + } + diff.push({ + value: '', + lines: [] + }); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function (entry) { + return ' ' + entry; + }); + } + var hunks = []; + var oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1; - - var _loop = function _loop(i) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - var _curRange; - - // If we have previous context, start with that - if (!oldRangeStart) { - var prev = diff[i - 1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; + var _loop = function _loop() { + var current = diff[i], + lines = current.lines || splitLines(current.value); + current.lines = lines; + if (current.added || current.removed) { + var _curRange; + // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + if (prev) { + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } } - } // Output our changes + // Output our changes + (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) { + return (current.added ? '+' : '-') + entry; + }))); - (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) { - return (current.added ? '+' : '-') + entry; - }))); // Track the updated file position - - - if (current.added) { - newLine += lines.length; + // Track the updated file position + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= options.context * 2 && i < diff.length - 2) { + var _curRange2; + // Overlapping + (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines))); + } else { + var _curRange3; + // end the range and output + var contextSize = Math.min(lines.length, options.context); + (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize)))); + var _hunk = { + oldStart: oldRangeStart, + oldLines: oldLine - oldRangeStart + contextSize, + newStart: newRangeStart, + newLines: newLine - newRangeStart + contextSize, + lines: curRange + }; + hunks.push(_hunk); + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; + } + } oldLine += lines.length; + newLine += lines.length; } - } else { - // Identical context lines. Track line changes - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= options.context * 2 && i < diff.length - 2) { - var _curRange2; - - // Overlapping - (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines))); - } else { - var _curRange3; - - // end the range and output - var contextSize = Math.min(lines.length, options.context); - - (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize)))); - - var hunk = { - oldStart: oldRangeStart, - oldLines: oldLine - oldRangeStart + contextSize, - newStart: newRangeStart, - newLines: newLine - newRangeStart + contextSize, - lines: curRange - }; - - if (i >= diff.length - 2 && lines.length <= options.context) { - // EOF is inside this hunk - var oldEOFNewline = /\n$/.test(oldStr); - var newEOFNewline = /\n$/.test(newStr); - var noNlBeforeAdds = lines.length == 0 && curRange.length > hunk.oldLines; - - if (!oldEOFNewline && noNlBeforeAdds && oldStr.length > 0) { - // special case: old has no eol and no trailing context; no-nl can end up before adds - // however, if the old file is empty, do not output the no-nl line - curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); - } - - if (!oldEOFNewline && !noNlBeforeAdds || !newEOFNewline) { - curRange.push('\\ No newline at end of file'); - } - } + }; + for (var i = 0; i < diff.length; i++) { + _loop(); + } - hunks.push(hunk); - oldRangeStart = 0; - newRangeStart = 0; - curRange = []; + // Step 2: eliminate the trailing `\n` from each line of each hunk, and, where needed, add + // "\ No newline at end of file". + for (var _i = 0, _hunks = hunks; _i < _hunks.length; _i++) { + var hunk = _hunks[_i]; + for (var _i2 = 0; _i2 < hunk.lines.length; _i2++) { + if (hunk.lines[_i2].endsWith('\n')) { + hunk.lines[_i2] = hunk.lines[_i2].slice(0, -1); + } else { + hunk.lines.splice(_i2 + 1, 0, '\\ No newline at end of file'); + _i2++; // Skip the line we just added, then continue iterating } } - - oldLine += lines.length; - newLine += lines.length; } - }; - - for (var i = 0; i < diff.length; i++) { - _loop(i); + return { + oldFileName: oldFileName, + newFileName: newFileName, + oldHeader: oldHeader, + newHeader: newHeader, + hunks: hunks + }; } - - return { - oldFileName: oldFileName, - newFileName: newFileName, - oldHeader: oldHeader, - newHeader: newHeader, - hunks: hunks - }; } function formatPatch(diff) { if (Array.isArray(diff)) { return diff.map(formatPatch).join('\n'); } - var ret = []; - if (diff.oldFileName == diff.newFileName) { ret.push('Index: ' + diff.oldFileName); } - ret.push('==================================================================='); ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); - for (var i = 0; i < diff.hunks.length; i++) { - var hunk = diff.hunks[i]; // Unified Diff Format quirk: If the chunk size is 0, + var hunk = diff.hunks[i]; + // Unified Diff Format quirk: If the chunk size is 0, // the first number is one lower than one would expect. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 - if (hunk.oldLines === 0) { hunk.oldStart -= 1; } - if (hunk.newLines === 0) { hunk.newStart -= 1; } - ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); ret.push.apply(ret, hunk.lines); } - return ret.join('\n') + '\n'; } function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { - return formatPatch(structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)); + var _options2; + if (typeof options === 'function') { + options = { + callback: options + }; + } + if (!((_options2 = options) !== null && _options2 !== void 0 && _options2.callback)) { + var patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); + if (!patchObj) { + return; + } + return formatPatch(patchObj); + } else { + var _options3 = options, + _callback2 = _options3.callback; + structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, _objectSpread2(_objectSpread2({}, options), {}, { + callback: function callback(patchObj) { + if (!patchObj) { + _callback2(); + } else { + _callback2(formatPatch(patchObj)); + } + } + })); + } } function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); } +/** + * Split `text` into an array of lines, including the trailing newline character (where present) + */ +function splitLines(text) { + var hasTrailingNl = text.endsWith('\n'); + var result = text.split('\n').map(function (line) { + return line + '\n'; + }); + if (hasTrailingNl) { + result.pop(); + } else { + result.push(result.pop().slice(0, -1)); + } + return result; +} + function arrayEqual(a, b) { if (a.length !== b.length) { return false; } - return arrayStartsWith(a, b); } function arrayStartsWith(array, start) { if (start.length > array.length) { return false; } - for (var i = 0; i < start.length; i++) { if (start[i] !== array[i]) { return false; } } - return true; } function calcLineCount(hunk) { var _calcOldNewLineCount = calcOldNewLineCount(hunk.lines), - oldLines = _calcOldNewLineCount.oldLines, - newLines = _calcOldNewLineCount.newLines; - + oldLines = _calcOldNewLineCount.oldLines, + newLines = _calcOldNewLineCount.newLines; if (oldLines !== undefined) { hunk.oldLines = oldLines; } else { delete hunk.oldLines; } - if (newLines !== undefined) { hunk.newLines = newLines; } else { @@ -1229,14 +1632,14 @@ function calcLineCount(hunk) { function merge(mine, theirs, base) { mine = loadPatch(mine, base); theirs = loadPatch(theirs, base); - var ret = {}; // For index we just let it pass through as it doesn't have any necessary meaning. + var ret = {}; + + // For index we just let it pass through as it doesn't have any necessary meaning. // Leaving sanity checks on this to the API consumer that may know more about the // meaning in their own context. - if (mine.index || theirs.index) { ret.index = mine.index || theirs.index; } - if (mine.newFileName || theirs.newFileName) { if (!fileNameChanged(mine)) { // No header or no change in ours, use theirs (and ours if theirs does not exist) @@ -1258,21 +1661,18 @@ function merge(mine, theirs, base) { ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); } } - ret.hunks = []; var mineIndex = 0, - theirsIndex = 0, - mineOffset = 0, - theirsOffset = 0; - + theirsIndex = 0, + mineOffset = 0, + theirsOffset = 0; while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { var mineCurrent = mine.hunks[mineIndex] || { - oldStart: Infinity - }, - theirsCurrent = theirs.hunks[theirsIndex] || { - oldStart: Infinity - }; - + oldStart: Infinity + }, + theirsCurrent = theirs.hunks[theirsIndex] || { + oldStart: Infinity + }; if (hunkBefore(mineCurrent, theirsCurrent)) { // This patch does not overlap with any of the others, yay. ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); @@ -1298,30 +1698,23 @@ function merge(mine, theirs, base) { ret.hunks.push(mergedHunk); } } - return ret; } - function loadPatch(param, base) { if (typeof param === 'string') { if (/^@@/m.test(param) || /^Index:/m.test(param)) { return parsePatch(param)[0]; } - if (!base) { throw new Error('Must provide a base reference or pass in a patch'); } - return structuredPatch(undefined, undefined, base, param); } - return param; } - function fileNameChanged(patch) { return patch.newFileName && patch.newFileName !== patch.oldFileName; } - function selectField(index, mine, theirs) { if (mine === theirs) { return mine; @@ -1333,11 +1726,9 @@ function selectField(index, mine, theirs) { }; } } - function hunkBefore(test, check) { return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; } - function cloneHunk(hunk, offset) { return { oldStart: hunk.oldStart, @@ -1347,39 +1738,37 @@ function cloneHunk(hunk, offset) { lines: hunk.lines }; } - function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { // This will generally result in a conflicted hunk, but there are cases where the context // is the only overlap where we can successfully merge the content here. var mine = { - offset: mineOffset, - lines: mineLines, - index: 0 - }, - their = { - offset: theirOffset, - lines: theirLines, - index: 0 - }; // Handle any leading content + offset: mineOffset, + lines: mineLines, + index: 0 + }, + their = { + offset: theirOffset, + lines: theirLines, + index: 0 + }; + // Handle any leading content insertLeading(hunk, mine, their); - insertLeading(hunk, their, mine); // Now in the overlap content. Scan through and select the best changes from each. + insertLeading(hunk, their, mine); + // Now in the overlap content. Scan through and select the best changes from each. while (mine.index < mine.lines.length && their.index < their.lines.length) { var mineCurrent = mine.lines[mine.index], - theirCurrent = their.lines[their.index]; - + theirCurrent = their.lines[their.index]; if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { // Both modified ... mutualChange(hunk, mine, their); } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { var _hunk$lines; - // Mine inserted (_hunk$lines = hunk.lines).push.apply(_hunk$lines, _toConsumableArray(collectChange(mine))); } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { var _hunk$lines2; - // Theirs inserted (_hunk$lines2 = hunk.lines).push.apply(_hunk$lines2, _toConsumableArray(collectChange(their))); } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') { @@ -1397,57 +1786,44 @@ function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { // Context mismatch conflict(hunk, collectChange(mine), collectChange(their)); } - } // Now push anything that may be remaining - + } + // Now push anything that may be remaining insertTrailing(hunk, mine); insertTrailing(hunk, their); calcLineCount(hunk); } - function mutualChange(hunk, mine, their) { var myChanges = collectChange(mine), - theirChanges = collectChange(their); - + theirChanges = collectChange(their); if (allRemoves(myChanges) && allRemoves(theirChanges)) { // Special case for remove changes that are supersets of one another if (arrayStartsWith(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { var _hunk$lines3; - (_hunk$lines3 = hunk.lines).push.apply(_hunk$lines3, _toConsumableArray(myChanges)); - return; } else if (arrayStartsWith(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { var _hunk$lines4; - (_hunk$lines4 = hunk.lines).push.apply(_hunk$lines4, _toConsumableArray(theirChanges)); - return; } } else if (arrayEqual(myChanges, theirChanges)) { var _hunk$lines5; - (_hunk$lines5 = hunk.lines).push.apply(_hunk$lines5, _toConsumableArray(myChanges)); - return; } - conflict(hunk, myChanges, theirChanges); } - function removal(hunk, mine, their, swap) { var myChanges = collectChange(mine), - theirChanges = collectContext(their, myChanges); - + theirChanges = collectContext(their, myChanges); if (theirChanges.merged) { var _hunk$lines6; - (_hunk$lines6 = hunk.lines).push.apply(_hunk$lines6, _toConsumableArray(theirChanges.merged)); } else { conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); } } - function conflict(hunk, mine, their) { hunk.conflict = true; hunk.lines.push({ @@ -1456,7 +1832,6 @@ function conflict(hunk, mine, their) { theirs: their }); } - function insertLeading(hunk, insert, their) { while (insert.offset < their.offset && insert.index < insert.lines.length) { var line = insert.lines[insert.index++]; @@ -1464,25 +1839,22 @@ function insertLeading(hunk, insert, their) { insert.offset++; } } - function insertTrailing(hunk, insert) { while (insert.index < insert.lines.length) { var line = insert.lines[insert.index++]; hunk.lines.push(line); } } - function collectChange(state) { var ret = [], - operation = state.lines[state.index][0]; - + operation = state.lines[state.index][0]; while (state.index < state.lines.length) { - var line = state.lines[state.index]; // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. + var line = state.lines[state.index]; + // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. if (operation === '-' && line[0] === '+') { operation = '+'; } - if (operation === line[0]) { ret.push(line); state.index++; @@ -1490,39 +1862,35 @@ function collectChange(state) { break; } } - return ret; } - function collectContext(state, matchChanges) { var changes = [], - merged = [], - matchIndex = 0, - contextChanges = false, - conflicted = false; - + merged = [], + matchIndex = 0, + contextChanges = false, + conflicted = false; while (matchIndex < matchChanges.length && state.index < state.lines.length) { var change = state.lines[state.index], - match = matchChanges[matchIndex]; // Once we've hit our add, then we are done + match = matchChanges[matchIndex]; + // Once we've hit our add, then we are done if (match[0] === '+') { break; } - contextChanges = contextChanges || change[0] !== ' '; merged.push(match); - matchIndex++; // Consume any additions in the other block as a conflict to attempt - // to pull in the remaining context after this + matchIndex++; + // Consume any additions in the other block as a conflict to attempt + // to pull in the remaining context after this if (change[0] === '+') { conflicted = true; - while (change[0] === '+') { changes.push(change); change = state.lines[++state.index]; } } - if (match.substr(1) === change.substr(1)) { changes.push(change); state.index++; @@ -1530,44 +1898,35 @@ function collectContext(state, matchChanges) { conflicted = true; } } - if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { conflicted = true; } - if (conflicted) { return changes; } - while (matchIndex < matchChanges.length) { merged.push(matchChanges[matchIndex++]); } - return { merged: merged, changes: changes }; } - function allRemoves(changes) { return changes.reduce(function (prev, change) { return prev && change[0] === '-'; }, true); } - function skipRemoveSuperset(state, removeChanges, delta) { for (var i = 0; i < delta; i++) { var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); - if (state.lines[state.index + i] !== ' ' + changeContent) { return false; } } - state.index += delta; return true; } - function calcOldNewLineCount(lines) { var oldLines = 0; var newLines = 0; @@ -1575,7 +1934,6 @@ function calcOldNewLineCount(lines) { if (typeof line !== 'string') { var myCount = calcOldNewLineCount(line.mine); var theirCount = calcOldNewLineCount(line.theirs); - if (oldLines !== undefined) { if (myCount.oldLines === theirCount.oldLines) { oldLines += myCount.oldLines; @@ -1583,7 +1941,6 @@ function calcOldNewLineCount(lines) { oldLines = undefined; } } - if (newLines !== undefined) { if (myCount.newLines === theirCount.newLines) { newLines += myCount.newLines; @@ -1595,7 +1952,6 @@ function calcOldNewLineCount(lines) { if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { newLines++; } - if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { oldLines++; } @@ -1611,7 +1967,6 @@ function reversePatch(structuredPatch) { if (Array.isArray(structuredPatch)) { return structuredPatch.map(reversePatch).reverse(); } - return _objectSpread2(_objectSpread2({}, structuredPatch), {}, { oldFileName: structuredPatch.newFileName, oldHeader: structuredPatch.newHeader, @@ -1623,16 +1978,13 @@ function reversePatch(structuredPatch) { oldStart: hunk.newStart, newLines: hunk.oldLines, newStart: hunk.oldStart, - linedelimiters: hunk.linedelimiters, lines: hunk.lines.map(function (l) { if (l.startsWith('-')) { return "+".concat(l.slice(1)); } - if (l.startsWith('+')) { return "-".concat(l.slice(1)); } - return l; }) }; @@ -1643,12 +1995,10 @@ function reversePatch(structuredPatch) { // See: http://code.google.com/p/google-diff-match-patch/wiki/API function convertChangesToDMP(changes) { var ret = [], - change, - operation; - + change, + operation; for (var i = 0; i < changes.length; i++) { change = changes[i]; - if (change.added) { operation = 1; } else if (change.removed) { @@ -1656,37 +2006,29 @@ function convertChangesToDMP(changes) { } else { operation = 0; } - ret.push([operation, change.value]); } - return ret; } function convertChangesToXML(changes) { var ret = []; - for (var i = 0; i < changes.length; i++) { var change = changes[i]; - if (change.added) { ret.push(''); } else if (change.removed) { ret.push(''); } - ret.push(escapeHTML(change.value)); - if (change.added) { ret.push(''); } else if (change.removed) { ret.push(''); } } - return ret.join(''); } - function escapeHTML(s) { var n = s; n = n.replace(/&/g, '&'); diff --git a/deps/npm/node_modules/diff/lib/index.js b/deps/npm/node_modules/diff/lib/index.js index 09d885e1182926..518b3dee33d30c 100644 --- a/deps/npm/node_modules/diff/lib/index.js +++ b/deps/npm/node_modules/diff/lib/index.js @@ -10,225 +10,208 @@ Object.defineProperty(exports, "Diff", { return _base["default"]; } }); -Object.defineProperty(exports, "diffChars", { +Object.defineProperty(exports, "applyPatch", { enumerable: true, get: function get() { - return _character.diffChars; + return _apply.applyPatch; } }); -Object.defineProperty(exports, "diffWords", { +Object.defineProperty(exports, "applyPatches", { enumerable: true, get: function get() { - return _word.diffWords; + return _apply.applyPatches; } }); -Object.defineProperty(exports, "diffWordsWithSpace", { +Object.defineProperty(exports, "canonicalize", { enumerable: true, get: function get() { - return _word.diffWordsWithSpace; + return _json.canonicalize; } }); -Object.defineProperty(exports, "diffLines", { +Object.defineProperty(exports, "convertChangesToDMP", { enumerable: true, get: function get() { - return _line.diffLines; + return _dmp.convertChangesToDMP; } }); -Object.defineProperty(exports, "diffTrimmedLines", { +Object.defineProperty(exports, "convertChangesToXML", { enumerable: true, get: function get() { - return _line.diffTrimmedLines; + return _xml.convertChangesToXML; } }); -Object.defineProperty(exports, "diffSentences", { +Object.defineProperty(exports, "createPatch", { enumerable: true, get: function get() { - return _sentence.diffSentences; + return _create.createPatch; } }); -Object.defineProperty(exports, "diffCss", { +Object.defineProperty(exports, "createTwoFilesPatch", { enumerable: true, get: function get() { - return _css.diffCss; + return _create.createTwoFilesPatch; } }); -Object.defineProperty(exports, "diffJson", { +Object.defineProperty(exports, "diffArrays", { enumerable: true, get: function get() { - return _json.diffJson; + return _array.diffArrays; } }); -Object.defineProperty(exports, "canonicalize", { +Object.defineProperty(exports, "diffChars", { enumerable: true, get: function get() { - return _json.canonicalize; + return _character.diffChars; } }); -Object.defineProperty(exports, "diffArrays", { +Object.defineProperty(exports, "diffCss", { enumerable: true, get: function get() { - return _array.diffArrays; + return _css.diffCss; } }); -Object.defineProperty(exports, "applyPatch", { +Object.defineProperty(exports, "diffJson", { enumerable: true, get: function get() { - return _apply.applyPatch; + return _json.diffJson; } }); -Object.defineProperty(exports, "applyPatches", { +Object.defineProperty(exports, "diffLines", { enumerable: true, get: function get() { - return _apply.applyPatches; + return _line.diffLines; } }); -Object.defineProperty(exports, "parsePatch", { +Object.defineProperty(exports, "diffSentences", { enumerable: true, get: function get() { - return _parse.parsePatch; + return _sentence.diffSentences; } }); -Object.defineProperty(exports, "merge", { +Object.defineProperty(exports, "diffTrimmedLines", { enumerable: true, get: function get() { - return _merge.merge; + return _line.diffTrimmedLines; } }); -Object.defineProperty(exports, "reversePatch", { +Object.defineProperty(exports, "diffWords", { enumerable: true, get: function get() { - return _reverse.reversePatch; + return _word.diffWords; } }); -Object.defineProperty(exports, "structuredPatch", { +Object.defineProperty(exports, "diffWordsWithSpace", { enumerable: true, get: function get() { - return _create.structuredPatch; + return _word.diffWordsWithSpace; } }); -Object.defineProperty(exports, "createTwoFilesPatch", { +Object.defineProperty(exports, "formatPatch", { enumerable: true, get: function get() { - return _create.createTwoFilesPatch; + return _create.formatPatch; } }); -Object.defineProperty(exports, "createPatch", { +Object.defineProperty(exports, "merge", { enumerable: true, get: function get() { - return _create.createPatch; + return _merge.merge; } }); -Object.defineProperty(exports, "formatPatch", { +Object.defineProperty(exports, "parsePatch", { enumerable: true, get: function get() { - return _create.formatPatch; + return _parse.parsePatch; } }); -Object.defineProperty(exports, "convertChangesToDMP", { +Object.defineProperty(exports, "reversePatch", { enumerable: true, get: function get() { - return _dmp.convertChangesToDMP; + return _reverse.reversePatch; } }); -Object.defineProperty(exports, "convertChangesToXML", { +Object.defineProperty(exports, "structuredPatch", { enumerable: true, get: function get() { - return _xml.convertChangesToXML; + return _create.structuredPatch; } }); - /*istanbul ignore end*/ var /*istanbul ignore start*/ _base = _interopRequireDefault(require("./diff/base")) /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _character = require("./diff/character") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _word = require("./diff/word") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _line = require("./diff/line") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _sentence = require("./diff/sentence") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _css = require("./diff/css") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _json = require("./diff/json") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _array = require("./diff/array") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _apply = require("./patch/apply") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _parse = require("./patch/parse") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _merge = require("./patch/merge") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _reverse = require("./patch/reverse") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _create = require("./patch/create") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _dmp = require("./convert/dmp") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _xml = require("./convert/xml") /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /*istanbul ignore end*/ -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWdCQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQUNBO0FBQUE7QUFBQTtBQUFBO0FBQUE7O0FBQ0E7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQUNBO0FBQUE7QUFBQTtBQUFBO0FBQUE7O0FBRUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQUVBO0FBQUE7QUFBQTtBQUFBO0FBQUE7O0FBRUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQUNBO0FBQUE7QUFBQTtBQUFBO0FBQUE7O0FBQ0E7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQUVBO0FBQUE7QUFBQTtBQUFBO0FBQUE7O0FBQ0E7QUFBQTtBQUFBO0FBQUE7QUFBQSIsInNvdXJjZXNDb250ZW50IjpbIi8qIFNlZSBMSUNFTlNFIGZpbGUgZm9yIHRlcm1zIG9mIHVzZSAqL1xuXG4vKlxuICogVGV4dCBkaWZmIGltcGxlbWVudGF0aW9uLlxuICpcbiAqIFRoaXMgbGlicmFyeSBzdXBwb3J0cyB0aGUgZm9sbG93aW5nIEFQSXM6XG4gKiBEaWZmLmRpZmZDaGFyczogQ2hhcmFjdGVyIGJ5IGNoYXJhY3RlciBkaWZmXG4gKiBEaWZmLmRpZmZXb3JkczogV29yZCAoYXMgZGVmaW5lZCBieSBcXGIgcmVnZXgpIGRpZmYgd2hpY2ggaWdub3JlcyB3aGl0ZXNwYWNlXG4gKiBEaWZmLmRpZmZMaW5lczogTGluZSBiYXNlZCBkaWZmXG4gKlxuICogRGlmZi5kaWZmQ3NzOiBEaWZmIHRhcmdldGVkIGF0IENTUyBjb250ZW50XG4gKlxuICogVGhlc2UgbWV0aG9kcyBhcmUgYmFzZWQgb24gdGhlIGltcGxlbWVudGF0aW9uIHByb3Bvc2VkIGluXG4gKiBcIkFuIE8oTkQpIERpZmZlcmVuY2UgQWxnb3JpdGhtIGFuZCBpdHMgVmFyaWF0aW9uc1wiIChNeWVycywgMTk4NikuXG4gKiBodHRwOi8vY2l0ZXNlZXJ4LmlzdC5wc3UuZWR1L3ZpZXdkb2Mvc3VtbWFyeT9kb2k9MTAuMS4xLjQuNjkyN1xuICovXG5pbXBvcnQgRGlmZiBmcm9tICcuL2RpZmYvYmFzZSc7XG5pbXBvcnQge2RpZmZDaGFyc30gZnJvbSAnLi9kaWZmL2NoYXJhY3Rlcic7XG5pbXBvcnQge2RpZmZXb3JkcywgZGlmZldvcmRzV2l0aFNwYWNlfSBmcm9tICcuL2RpZmYvd29yZCc7XG5pbXBvcnQge2RpZmZMaW5lcywgZGlmZlRyaW1tZWRMaW5lc30gZnJvbSAnLi9kaWZmL2xpbmUnO1xuaW1wb3J0IHtkaWZmU2VudGVuY2VzfSBmcm9tICcuL2RpZmYvc2VudGVuY2UnO1xuXG5pbXBvcnQge2RpZmZDc3N9IGZyb20gJy4vZGlmZi9jc3MnO1xuaW1wb3J0IHtkaWZmSnNvbiwgY2Fub25pY2FsaXplfSBmcm9tICcuL2RpZmYvanNvbic7XG5cbmltcG9ydCB7ZGlmZkFycmF5c30gZnJvbSAnLi9kaWZmL2FycmF5JztcblxuaW1wb3J0IHthcHBseVBhdGNoLCBhcHBseVBhdGNoZXN9IGZyb20gJy4vcGF0Y2gvYXBwbHknO1xuaW1wb3J0IHtwYXJzZVBhdGNofSBmcm9tICcuL3BhdGNoL3BhcnNlJztcbmltcG9ydCB7bWVyZ2V9IGZyb20gJy4vcGF0Y2gvbWVyZ2UnO1xuaW1wb3J0IHtyZXZlcnNlUGF0Y2h9IGZyb20gJy4vcGF0Y2gvcmV2ZXJzZSc7XG5pbXBvcnQge3N0cnVjdHVyZWRQYXRjaCwgY3JlYXRlVHdvRmlsZXNQYXRjaCwgY3JlYXRlUGF0Y2gsIGZvcm1hdFBhdGNofSBmcm9tICcuL3BhdGNoL2NyZWF0ZSc7XG5cbmltcG9ydCB7Y29udmVydENoYW5nZXNUb0RNUH0gZnJvbSAnLi9jb252ZXJ0L2RtcCc7XG5pbXBvcnQge2NvbnZlcnRDaGFuZ2VzVG9YTUx9IGZyb20gJy4vY29udmVydC94bWwnO1xuXG5leHBvcnQge1xuICBEaWZmLFxuXG4gIGRpZmZDaGFycyxcbiAgZGlmZldvcmRzLFxuICBkaWZmV29yZHNXaXRoU3BhY2UsXG4gIGRpZmZMaW5lcyxcbiAgZGlmZlRyaW1tZWRMaW5lcyxcbiAgZGlmZlNlbnRlbmNlcyxcblxuICBkaWZmQ3NzLFxuICBkaWZmSnNvbixcblxuICBkaWZmQXJyYXlzLFxuXG4gIHN0cnVjdHVyZWRQYXRjaCxcbiAgY3JlYXRlVHdvRmlsZXNQYXRjaCxcbiAgY3JlYXRlUGF0Y2gsXG4gIGZvcm1hdFBhdGNoLFxuICBhcHBseVBhdGNoLFxuICBhcHBseVBhdGNoZXMsXG4gIHBhcnNlUGF0Y2gsXG4gIG1lcmdlLFxuICByZXZlcnNlUGF0Y2gsXG4gIGNvbnZlcnRDaGFuZ2VzVG9ETVAsXG4gIGNvbnZlcnRDaGFuZ2VzVG9YTUwsXG4gIGNhbm9uaWNhbGl6ZVxufTtcbiJdfQ== +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX2NoYXJhY3RlciIsIl93b3JkIiwiX2xpbmUiLCJfc2VudGVuY2UiLCJfY3NzIiwiX2pzb24iLCJfYXJyYXkiLCJfYXBwbHkiLCJfcGFyc2UiLCJfbWVyZ2UiLCJfcmV2ZXJzZSIsIl9jcmVhdGUiLCJfZG1wIiwiX3htbCIsIm9iaiIsIl9fZXNNb2R1bGUiXSwic291cmNlcyI6WyIuLi9zcmMvaW5kZXguanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyogU2VlIExJQ0VOU0UgZmlsZSBmb3IgdGVybXMgb2YgdXNlICovXG5cbi8qXG4gKiBUZXh0IGRpZmYgaW1wbGVtZW50YXRpb24uXG4gKlxuICogVGhpcyBsaWJyYXJ5IHN1cHBvcnRzIHRoZSBmb2xsb3dpbmcgQVBJczpcbiAqIERpZmYuZGlmZkNoYXJzOiBDaGFyYWN0ZXIgYnkgY2hhcmFjdGVyIGRpZmZcbiAqIERpZmYuZGlmZldvcmRzOiBXb3JkIChhcyBkZWZpbmVkIGJ5IFxcYiByZWdleCkgZGlmZiB3aGljaCBpZ25vcmVzIHdoaXRlc3BhY2VcbiAqIERpZmYuZGlmZkxpbmVzOiBMaW5lIGJhc2VkIGRpZmZcbiAqXG4gKiBEaWZmLmRpZmZDc3M6IERpZmYgdGFyZ2V0ZWQgYXQgQ1NTIGNvbnRlbnRcbiAqXG4gKiBUaGVzZSBtZXRob2RzIGFyZSBiYXNlZCBvbiB0aGUgaW1wbGVtZW50YXRpb24gcHJvcG9zZWQgaW5cbiAqIFwiQW4gTyhORCkgRGlmZmVyZW5jZSBBbGdvcml0aG0gYW5kIGl0cyBWYXJpYXRpb25zXCIgKE15ZXJzLCAxOTg2KS5cbiAqIGh0dHA6Ly9jaXRlc2VlcnguaXN0LnBzdS5lZHUvdmlld2RvYy9zdW1tYXJ5P2RvaT0xMC4xLjEuNC42OTI3XG4gKi9cbmltcG9ydCBEaWZmIGZyb20gJy4vZGlmZi9iYXNlJztcbmltcG9ydCB7ZGlmZkNoYXJzfSBmcm9tICcuL2RpZmYvY2hhcmFjdGVyJztcbmltcG9ydCB7ZGlmZldvcmRzLCBkaWZmV29yZHNXaXRoU3BhY2V9IGZyb20gJy4vZGlmZi93b3JkJztcbmltcG9ydCB7ZGlmZkxpbmVzLCBkaWZmVHJpbW1lZExpbmVzfSBmcm9tICcuL2RpZmYvbGluZSc7XG5pbXBvcnQge2RpZmZTZW50ZW5jZXN9IGZyb20gJy4vZGlmZi9zZW50ZW5jZSc7XG5cbmltcG9ydCB7ZGlmZkNzc30gZnJvbSAnLi9kaWZmL2Nzcyc7XG5pbXBvcnQge2RpZmZKc29uLCBjYW5vbmljYWxpemV9IGZyb20gJy4vZGlmZi9qc29uJztcblxuaW1wb3J0IHtkaWZmQXJyYXlzfSBmcm9tICcuL2RpZmYvYXJyYXknO1xuXG5pbXBvcnQge2FwcGx5UGF0Y2gsIGFwcGx5UGF0Y2hlc30gZnJvbSAnLi9wYXRjaC9hcHBseSc7XG5pbXBvcnQge3BhcnNlUGF0Y2h9IGZyb20gJy4vcGF0Y2gvcGFyc2UnO1xuaW1wb3J0IHttZXJnZX0gZnJvbSAnLi9wYXRjaC9tZXJnZSc7XG5pbXBvcnQge3JldmVyc2VQYXRjaH0gZnJvbSAnLi9wYXRjaC9yZXZlcnNlJztcbmltcG9ydCB7c3RydWN0dXJlZFBhdGNoLCBjcmVhdGVUd29GaWxlc1BhdGNoLCBjcmVhdGVQYXRjaCwgZm9ybWF0UGF0Y2h9IGZyb20gJy4vcGF0Y2gvY3JlYXRlJztcblxuaW1wb3J0IHtjb252ZXJ0Q2hhbmdlc1RvRE1QfSBmcm9tICcuL2NvbnZlcnQvZG1wJztcbmltcG9ydCB7Y29udmVydENoYW5nZXNUb1hNTH0gZnJvbSAnLi9jb252ZXJ0L3htbCc7XG5cbmV4cG9ydCB7XG4gIERpZmYsXG5cbiAgZGlmZkNoYXJzLFxuICBkaWZmV29yZHMsXG4gIGRpZmZXb3Jkc1dpdGhTcGFjZSxcbiAgZGlmZkxpbmVzLFxuICBkaWZmVHJpbW1lZExpbmVzLFxuICBkaWZmU2VudGVuY2VzLFxuXG4gIGRpZmZDc3MsXG4gIGRpZmZKc29uLFxuXG4gIGRpZmZBcnJheXMsXG5cbiAgc3RydWN0dXJlZFBhdGNoLFxuICBjcmVhdGVUd29GaWxlc1BhdGNoLFxuICBjcmVhdGVQYXRjaCxcbiAgZm9ybWF0UGF0Y2gsXG4gIGFwcGx5UGF0Y2gsXG4gIGFwcGx5UGF0Y2hlcyxcbiAgcGFyc2VQYXRjaCxcbiAgbWVyZ2UsXG4gIHJldmVyc2VQYXRjaCxcbiAgY29udmVydENoYW5nZXNUb0RNUCxcbiAgY29udmVydENoYW5nZXNUb1hNTCxcbiAgY2Fub25pY2FsaXplXG59O1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBZ0JBO0FBQUE7QUFBQUEsS0FBQSxHQUFBQyxzQkFBQSxDQUFBQyxPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQUMsVUFBQSxHQUFBRCxPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQUUsS0FBQSxHQUFBRixPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQUcsS0FBQSxHQUFBSCxPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQUksU0FBQSxHQUFBSixPQUFBO0FBQUE7QUFBQTtBQUVBO0FBQUE7QUFBQUssSUFBQSxHQUFBTCxPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQU0sS0FBQSxHQUFBTixPQUFBO0FBQUE7QUFBQTtBQUVBO0FBQUE7QUFBQU8sTUFBQSxHQUFBUCxPQUFBO0FBQUE7QUFBQTtBQUVBO0FBQUE7QUFBQVEsTUFBQSxHQUFBUixPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQVMsTUFBQSxHQUFBVCxPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQVUsTUFBQSxHQUFBVixPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQVcsUUFBQSxHQUFBWCxPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQVksT0FBQSxHQUFBWixPQUFBO0FBQUE7QUFBQTtBQUVBO0FBQUE7QUFBQWEsSUFBQSxHQUFBYixPQUFBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQWMsSUFBQSxHQUFBZCxPQUFBO0FBQUE7QUFBQTtBQUFrRCxtQ0FBQUQsdUJBQUFnQixHQUFBLFdBQUFBLEdBQUEsSUFBQUEsR0FBQSxDQUFBQyxVQUFBLEdBQUFELEdBQUEsZ0JBQUFBLEdBQUE7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== diff --git a/deps/npm/node_modules/diff/lib/index.mjs b/deps/npm/node_modules/diff/lib/index.mjs index a0ace0182ab14e..6e872723d85817 100644 --- a/deps/npm/node_modules/diff/lib/index.mjs +++ b/deps/npm/node_modules/diff/lib/index.mjs @@ -2,59 +2,52 @@ function Diff() {} Diff.prototype = { diff: function diff(oldString, newString) { var _options$timeout; - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var callback = options.callback; - if (typeof options === 'function') { callback = options; options = {}; } - - this.options = options; var self = this; - function done(value) { + value = self.postProcess(value, options); if (callback) { setTimeout(function () { - callback(undefined, value); + callback(value); }, 0); return true; } else { return value; } - } // Allow subclasses to massage the input prior to running - + } - oldString = this.castInput(oldString); - newString = this.castInput(newString); - oldString = this.removeEmpty(this.tokenize(oldString)); - newString = this.removeEmpty(this.tokenize(newString)); + // Allow subclasses to massage the input prior to running + oldString = this.castInput(oldString, options); + newString = this.castInput(newString, options); + oldString = this.removeEmpty(this.tokenize(oldString, options)); + newString = this.removeEmpty(this.tokenize(newString, options)); var newLen = newString.length, - oldLen = oldString.length; + oldLen = oldString.length; var editLength = 1; var maxEditLength = newLen + oldLen; - - if (options.maxEditLength) { + if (options.maxEditLength != null) { maxEditLength = Math.min(maxEditLength, options.maxEditLength); } - var maxExecutionTime = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : Infinity; var abortAfterTimestamp = Date.now() + maxExecutionTime; var bestPath = [{ oldPos: -1, lastComponent: undefined - }]; // Seed editLength = 0, i.e. the content starts with the same values - - var newPos = this.extractCommon(bestPath[0], newString, oldString, 0); + }]; + // Seed editLength = 0, i.e. the content starts with the same values + var newPos = this.extractCommon(bestPath[0], newString, oldString, 0, options); if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) { // Identity per the equality and tokenizer - return done([{ - value: this.join(newString), - count: newString.length - }]); - } // Once we hit the right edge of the edit graph on some diagonal k, we can + return done(buildValues(self, bestPath[0].lastComponent, newString, oldString, self.useLongestToken)); + } + + // Once we hit the right edge of the edit graph on some diagonal k, we can // definitely reach the end of the edit graph in no more than k edits, so // there's no point in considering any moves to diagonal k+1 any more (from // which we're guaranteed to need at least k+1 more edits). @@ -71,81 +64,67 @@ Diff.prototype = { // where the new text simply appends d characters on the end of the // original text of length n, the true Myers algorithm will take O(n+d^2) // time while this optimization needs only O(n+d) time. - - var minDiagonalToConsider = -Infinity, - maxDiagonalToConsider = Infinity; // Main worker method. checks all permutations of a given edit length for acceptance. + maxDiagonalToConsider = Infinity; + // Main worker method. checks all permutations of a given edit length for acceptance. function execEditLength() { for (var diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) { var basePath = void 0; var removePath = bestPath[diagonalPath - 1], - addPath = bestPath[diagonalPath + 1]; - + addPath = bestPath[diagonalPath + 1]; if (removePath) { // No one else is going to attempt to use this value, clear it bestPath[diagonalPath - 1] = undefined; } - var canAdd = false; - if (addPath) { // what newPos will be after we do an insertion: var addPathNewPos = addPath.oldPos - diagonalPath; canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen; } - var canRemove = removePath && removePath.oldPos + 1 < oldLen; - if (!canAdd && !canRemove) { // If this path is a terminal then prune bestPath[diagonalPath] = undefined; continue; - } // Select the diagonal that we want to branch from. We select the prior + } + + // Select the diagonal that we want to branch from. We select the prior // path whose position in the old string is the farthest from the origin // and does not pass the bounds of the diff graph - // TODO: Remove the `+ 1` here to make behavior match Myers algorithm - // and prefer to order removals before insertions. - - - if (!canRemove || canAdd && removePath.oldPos + 1 < addPath.oldPos) { - basePath = self.addToPath(addPath, true, undefined, 0); + if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) { + basePath = self.addToPath(addPath, true, false, 0, options); } else { - basePath = self.addToPath(removePath, undefined, true, 1); + basePath = self.addToPath(removePath, false, true, 1, options); } - - newPos = self.extractCommon(basePath, newString, oldString, diagonalPath); - + newPos = self.extractCommon(basePath, newString, oldString, diagonalPath, options); if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) { // If we have hit the end of both strings, then we are done return done(buildValues(self, basePath.lastComponent, newString, oldString, self.useLongestToken)); } else { bestPath[diagonalPath] = basePath; - if (basePath.oldPos + 1 >= oldLen) { maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1); } - if (newPos + 1 >= newLen) { minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1); } } } - editLength++; - } // Performs the length of edit iteration. Is a bit fugly as this has to support the + } + + // Performs the length of edit iteration. Is a bit fugly as this has to support the // sync and async mode which is never fun. Loops over execEditLength until a value // is produced, or until the edit length exceeds options.maxEditLength (if given), // in which case it will return undefined. - - if (callback) { (function exec() { setTimeout(function () { if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) { return callback(); } - if (!execEditLength()) { exec(); } @@ -154,17 +133,15 @@ Diff.prototype = { } else { while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) { var ret = execEditLength(); - if (ret) { return ret; } } } }, - addToPath: function addToPath(path, added, removed, oldPosInc) { + addToPath: function addToPath(path, added, removed, oldPosInc, options) { var last = path.lastComponent; - - if (last && last.added === added && last.removed === removed) { + if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) { return { oldPos: path.oldPos + oldPosInc, lastComponent: { @@ -186,80 +163,83 @@ Diff.prototype = { }; } }, - extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { + extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath, options) { var newLen = newString.length, - oldLen = oldString.length, - oldPos = basePath.oldPos, - newPos = oldPos - diagonalPath, - commonCount = 0; - - while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + oldLen = oldString.length, + oldPos = basePath.oldPos, + newPos = oldPos - diagonalPath, + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldString[oldPos + 1], newString[newPos + 1], options)) { newPos++; oldPos++; commonCount++; + if (options.oneChangePerToken) { + basePath.lastComponent = { + count: 1, + previousComponent: basePath.lastComponent, + added: false, + removed: false + }; + } } - - if (commonCount) { + if (commonCount && !options.oneChangePerToken) { basePath.lastComponent = { count: commonCount, - previousComponent: basePath.lastComponent + previousComponent: basePath.lastComponent, + added: false, + removed: false }; } - basePath.oldPos = oldPos; return newPos; }, - equals: function equals(left, right) { - if (this.options.comparator) { - return this.options.comparator(left, right); + equals: function equals(left, right, options) { + if (options.comparator) { + return options.comparator(left, right); } else { - return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); + return left === right || options.ignoreCase && left.toLowerCase() === right.toLowerCase(); } }, removeEmpty: function removeEmpty(array) { var ret = []; - for (var i = 0; i < array.length; i++) { if (array[i]) { ret.push(array[i]); } } - return ret; }, castInput: function castInput(value) { return value; }, tokenize: function tokenize(value) { - return value.split(''); + return Array.from(value); }, join: function join(chars) { return chars.join(''); + }, + postProcess: function postProcess(changeObjects) { + return changeObjects; } }; - function buildValues(diff, lastComponent, newString, oldString, useLongestToken) { // First we convert our linked list of components in reverse order to an // array in the right order: var components = []; var nextComponent; - while (lastComponent) { components.push(lastComponent); nextComponent = lastComponent.previousComponent; delete lastComponent.previousComponent; lastComponent = nextComponent; } - components.reverse(); var componentPos = 0, - componentLen = components.length, - newPos = 0, - oldPos = 0; - + componentLen = components.length, + newPos = 0, + oldPos = 0; for (; componentPos < componentLen; componentPos++) { var component = components[componentPos]; - if (!component.removed) { if (!component.added && useLongestToken) { var value = newString.slice(newPos, newPos + component.count); @@ -271,36 +251,17 @@ function buildValues(diff, lastComponent, newString, oldString, useLongestToken) } else { component.value = diff.join(newString.slice(newPos, newPos + component.count)); } + newPos += component.count; - newPos += component.count; // Common case - + // Common case if (!component.added) { oldPos += component.count; } } else { component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); - oldPos += component.count; // Reverse add and remove so removes are output first to match common convention - // The diffing algorithm is tied to add then remove output and this is the simplest - // route to get the desired output with minimal overhead. - - if (componentPos && components[componentPos - 1].added) { - var tmp = components[componentPos - 1]; - components[componentPos - 1] = components[componentPos]; - components[componentPos] = tmp; - } + oldPos += component.count; } - } // Special case handle for when one terminal is ignored (i.e. whitespace). - // For this case we merge the terminal into the prior string and drop the change. - // This is only available for string mode. - - - var finalComponent = components[componentLen - 1]; - - if (componentLen > 1 && typeof finalComponent.value === 'string' && (finalComponent.added || finalComponent.removed) && diff.equals('', finalComponent.value)) { - components[componentLen - 2].value += finalComponent.value; - components.pop(); } - return components; } @@ -309,21 +270,114 @@ function diffChars(oldStr, newStr, options) { return characterDiff.diff(oldStr, newStr, options); } -function generateOptions(options, defaults) { - if (typeof options === 'function') { - defaults.callback = options; - } else if (options) { - for (var name in options) { - /* istanbul ignore else */ - if (options.hasOwnProperty(name)) { - defaults[name] = options[name]; - } +function longestCommonPrefix(str1, str2) { + var i; + for (i = 0; i < str1.length && i < str2.length; i++) { + if (str1[i] != str2[i]) { + return str1.slice(0, i); } } + return str1.slice(0, i); +} +function longestCommonSuffix(str1, str2) { + var i; - return defaults; + // Unlike longestCommonPrefix, we need a special case to handle all scenarios + // where we return the empty string since str1.slice(-0) will return the + // entire string. + if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) { + return ''; + } + for (i = 0; i < str1.length && i < str2.length; i++) { + if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) { + return str1.slice(-i); + } + } + return str1.slice(-i); +} +function replacePrefix(string, oldPrefix, newPrefix) { + if (string.slice(0, oldPrefix.length) != oldPrefix) { + throw Error("string ".concat(JSON.stringify(string), " doesn't start with prefix ").concat(JSON.stringify(oldPrefix), "; this is a bug")); + } + return newPrefix + string.slice(oldPrefix.length); +} +function replaceSuffix(string, oldSuffix, newSuffix) { + if (!oldSuffix) { + return string + newSuffix; + } + if (string.slice(-oldSuffix.length) != oldSuffix) { + throw Error("string ".concat(JSON.stringify(string), " doesn't end with suffix ").concat(JSON.stringify(oldSuffix), "; this is a bug")); + } + return string.slice(0, -oldSuffix.length) + newSuffix; +} +function removePrefix(string, oldPrefix) { + return replacePrefix(string, oldPrefix, ''); +} +function removeSuffix(string, oldSuffix) { + return replaceSuffix(string, oldSuffix, ''); +} +function maximumOverlap(string1, string2) { + return string2.slice(0, overlapCount(string1, string2)); +} + +// Nicked from https://stackoverflow.com/a/60422853/1709587 +function overlapCount(a, b) { + // Deal with cases where the strings differ in length + var startA = 0; + if (a.length > b.length) { + startA = a.length - b.length; + } + var endB = b.length; + if (a.length < b.length) { + endB = a.length; + } + // Create a back-reference for each index + // that should be followed in case of a mismatch. + // We only need B to make these references: + var map = Array(endB); + var k = 0; // Index that lags behind j + map[0] = 0; + for (var j = 1; j < endB; j++) { + if (b[j] == b[k]) { + map[j] = map[k]; // skip over the same character (optional optimisation) + } else { + map[j] = k; + } + while (k > 0 && b[j] != b[k]) { + k = map[k]; + } + if (b[j] == b[k]) { + k++; + } + } + // Phase 2: use these references while iterating over A + k = 0; + for (var i = startA; i < a.length; i++) { + while (k > 0 && a[i] != b[k]) { + k = map[k]; + } + if (a[i] == b[k]) { + k++; + } + } + return k; +} + +/** + * Returns true if the string consistently uses Windows line endings. + */ +function hasOnlyWinLineEndings(string) { + return string.includes('\r\n') && !string.startsWith('\n') && !string.match(/[^\r]\n/); +} + +/** + * Returns true if the string consistently uses Unix line endings. + */ +function hasOnlyUnixLineEndings(string) { + return !string.includes('\r\n') && string.includes('\n'); } +// Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode // // Ranges and exceptions: // Latin-1 Supplement, 0080–00FF @@ -341,82 +395,330 @@ function generateOptions(options, defaults) { // - U+02DC ˜ ˜ Small Tilde // - U+02DD ˝ ˝ Double Acute Accent // Latin Extended Additional, 1E00–1EFF +var extendedWordChars = "a-zA-Z0-9_\\u{C0}-\\u{FF}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}"; -var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; -var reWhitespace = /\S/; +// Each token is one of the following: +// - A punctuation mark plus the surrounding whitespace +// - A word plus the surrounding whitespace +// - Pure whitespace (but only in the special case where this the entire text +// is just whitespace) +// +// We have to include surrounding whitespace in the tokens because the two +// alternative approaches produce horribly broken results: +// * If we just discard the whitespace, we can't fully reproduce the original +// text from the sequence of tokens and any attempt to render the diff will +// get the whitespace wrong. +// * If we have separate tokens for whitespace, then in a typical text every +// second token will be a single space character. But this often results in +// the optimal diff between two texts being a perverse one that preserves +// the spaces between words but deletes and reinserts actual common words. +// See https://github.com/kpdecker/jsdiff/issues/160#issuecomment-1866099640 +// for an example. +// +// Keeping the surrounding whitespace of course has implications for .equals +// and .join, not just .tokenize. + +// This regex does NOT fully implement the tokenization rules described above. +// Instead, it gives runs of whitespace their own "token". The tokenize method +// then handles stitching whitespace tokens onto adjacent word or punctuation +// tokens. +var tokenizeIncludingWhitespace = new RegExp("[".concat(extendedWordChars, "]+|\\s+|[^").concat(extendedWordChars, "]"), 'ug'); var wordDiff = new Diff(); - -wordDiff.equals = function (left, right) { - if (this.options.ignoreCase) { +wordDiff.equals = function (left, right, options) { + if (options.ignoreCase) { left = left.toLowerCase(); right = right.toLowerCase(); } - - return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); + return left.trim() === right.trim(); }; - wordDiff.tokenize = function (value) { - // All whitespace symbols except newline group into one token, each newline - in separate token - var tokens = value.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/); // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. - - for (var i = 0; i < tokens.length - 1; i++) { - // If we have an empty string in the next field and we have only word chars before and after, merge - if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { - tokens[i] += tokens[i + 2]; - tokens.splice(i + 1, 2); - i--; + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var parts; + if (options.intlSegmenter) { + if (options.intlSegmenter.resolvedOptions().granularity != 'word') { + throw new Error('The segmenter passed must have a granularity of "word"'); } + parts = Array.from(options.intlSegmenter.segment(value), function (segment) { + return segment.segment; + }); + } else { + parts = value.match(tokenizeIncludingWhitespace) || []; } - + var tokens = []; + var prevPart = null; + parts.forEach(function (part) { + if (/\s/.test(part)) { + if (prevPart == null) { + tokens.push(part); + } else { + tokens.push(tokens.pop() + part); + } + } else if (/\s/.test(prevPart)) { + if (tokens[tokens.length - 1] == prevPart) { + tokens.push(tokens.pop() + part); + } else { + tokens.push(prevPart + part); + } + } else { + tokens.push(part); + } + prevPart = part; + }); return tokens; }; - -function diffWords(oldStr, newStr, options) { - options = generateOptions(options, { - ignoreWhitespace: true +wordDiff.join = function (tokens) { + // Tokens being joined here will always have appeared consecutively in the + // same text, so we can simply strip off the leading whitespace from all the + // tokens except the first (and except any whitespace-only tokens - but such + // a token will always be the first and only token anyway) and then join them + // and the whitespace around words and punctuation will end up correct. + return tokens.map(function (token, i) { + if (i == 0) { + return token; + } else { + return token.replace(/^\s+/, ''); + } + }).join(''); +}; +wordDiff.postProcess = function (changes, options) { + if (!changes || options.oneChangePerToken) { + return changes; + } + var lastKeep = null; + // Change objects representing any insertion or deletion since the last + // "keep" change object. There can be at most one of each. + var insertion = null; + var deletion = null; + changes.forEach(function (change) { + if (change.added) { + insertion = change; + } else if (change.removed) { + deletion = change; + } else { + if (insertion || deletion) { + // May be false at start of text + dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, change); + } + lastKeep = change; + insertion = null; + deletion = null; + } }); + if (insertion || deletion) { + dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, null); + } + return changes; +}; +function diffWords(oldStr, newStr, options) { + // This option has never been documented and never will be (it's clearer to + // just call `diffWordsWithSpace` directly if you need that behavior), but + // has existed in jsdiff for a long time, so we retain support for it here + // for the sake of backwards compatibility. + if ((options === null || options === void 0 ? void 0 : options.ignoreWhitespace) != null && !options.ignoreWhitespace) { + return diffWordsWithSpace(oldStr, newStr, options); + } return wordDiff.diff(oldStr, newStr, options); } +function dedupeWhitespaceInChangeObjects(startKeep, deletion, insertion, endKeep) { + // Before returning, we tidy up the leading and trailing whitespace of the + // change objects to eliminate cases where trailing whitespace in one object + // is repeated as leading whitespace in the next. + // Below are examples of the outcomes we want here to explain the code. + // I=insert, K=keep, D=delete + // 1. diffing 'foo bar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz' + // After cleanup, we want: K:'foo ' D:'bar ' K:'baz' + // + // 2. Diffing 'foo bar baz' vs 'foo qux baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' I:' qux ' K:' baz' + // After cleanup, we want K:'foo ' D:'bar' I:'qux' K:' baz' + // + // 3. Diffing 'foo\nbar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:'\nbar ' K:' baz' + // After cleanup, we want K'foo' D:'\nbar' K:' baz' + // + // 4. Diffing 'foo baz' vs 'foo\nbar baz' + // Prior to cleanup, we have K:'foo\n' I:'\nbar ' K:' baz' + // After cleanup, we ideally want K'foo' I:'\nbar' K:' baz' + // but don't actually manage this currently (the pre-cleanup change + // objects don't contain enough information to make it possible). + // + // 5. Diffing 'foo bar baz' vs 'foo baz' + // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz' + // After cleanup, we want K:'foo ' D:' bar ' K:'baz' + // + // Our handling is unavoidably imperfect in the case where there's a single + // indel between keeps and the whitespace has changed. For instance, consider + // diffing 'foo\tbar\nbaz' vs 'foo baz'. Unless we create an extra change + // object to represent the insertion of the space character (which isn't even + // a token), we have no way to avoid losing information about the texts' + // original whitespace in the result we return. Still, we do our best to + // output something that will look sensible if we e.g. print it with + // insertions in green and deletions in red. + + // Between two "keep" change objects (or before the first or after the last + // change object), we can have either: + // * A "delete" followed by an "insert" + // * Just an "insert" + // * Just a "delete" + // We handle the three cases separately. + if (deletion && insertion) { + var oldWsPrefix = deletion.value.match(/^\s*/)[0]; + var oldWsSuffix = deletion.value.match(/\s*$/)[0]; + var newWsPrefix = insertion.value.match(/^\s*/)[0]; + var newWsSuffix = insertion.value.match(/\s*$/)[0]; + if (startKeep) { + var commonWsPrefix = longestCommonPrefix(oldWsPrefix, newWsPrefix); + startKeep.value = replaceSuffix(startKeep.value, newWsPrefix, commonWsPrefix); + deletion.value = removePrefix(deletion.value, commonWsPrefix); + insertion.value = removePrefix(insertion.value, commonWsPrefix); + } + if (endKeep) { + var commonWsSuffix = longestCommonSuffix(oldWsSuffix, newWsSuffix); + endKeep.value = replacePrefix(endKeep.value, newWsSuffix, commonWsSuffix); + deletion.value = removeSuffix(deletion.value, commonWsSuffix); + insertion.value = removeSuffix(insertion.value, commonWsSuffix); + } + } else if (insertion) { + // The whitespaces all reflect what was in the new text rather than + // the old, so we essentially have no information about whitespace + // insertion or deletion. We just want to dedupe the whitespace. + // We do that by having each change object keep its trailing + // whitespace and deleting duplicate leading whitespace where + // present. + if (startKeep) { + insertion.value = insertion.value.replace(/^\s*/, ''); + } + if (endKeep) { + endKeep.value = endKeep.value.replace(/^\s*/, ''); + } + // otherwise we've got a deletion and no insertion + } else if (startKeep && endKeep) { + var newWsFull = endKeep.value.match(/^\s*/)[0], + delWsStart = deletion.value.match(/^\s*/)[0], + delWsEnd = deletion.value.match(/\s*$/)[0]; + + // Any whitespace that comes straight after startKeep in both the old and + // new texts, assign to startKeep and remove from the deletion. + var newWsStart = longestCommonPrefix(newWsFull, delWsStart); + deletion.value = removePrefix(deletion.value, newWsStart); + + // Any whitespace that comes straight before endKeep in both the old and + // new texts, and hasn't already been assigned to startKeep, assign to + // endKeep and remove from the deletion. + var newWsEnd = longestCommonSuffix(removePrefix(newWsFull, newWsStart), delWsEnd); + deletion.value = removeSuffix(deletion.value, newWsEnd); + endKeep.value = replacePrefix(endKeep.value, newWsFull, newWsEnd); + + // If there's any whitespace from the new text that HASN'T already been + // assigned, assign it to the start: + startKeep.value = replaceSuffix(startKeep.value, newWsFull, newWsFull.slice(0, newWsFull.length - newWsEnd.length)); + } else if (endKeep) { + // We are at the start of the text. Preserve all the whitespace on + // endKeep, and just remove whitespace from the end of deletion to the + // extent that it overlaps with the start of endKeep. + var endKeepWsPrefix = endKeep.value.match(/^\s*/)[0]; + var deletionWsSuffix = deletion.value.match(/\s*$/)[0]; + var overlap = maximumOverlap(deletionWsSuffix, endKeepWsPrefix); + deletion.value = removeSuffix(deletion.value, overlap); + } else if (startKeep) { + // We are at the END of the text. Preserve all the whitespace on + // startKeep, and just remove whitespace from the start of deletion to + // the extent that it overlaps with the end of startKeep. + var startKeepWsSuffix = startKeep.value.match(/\s*$/)[0]; + var deletionWsPrefix = deletion.value.match(/^\s*/)[0]; + var _overlap = maximumOverlap(startKeepWsSuffix, deletionWsPrefix); + deletion.value = removePrefix(deletion.value, _overlap); + } +} +var wordWithSpaceDiff = new Diff(); +wordWithSpaceDiff.tokenize = function (value) { + // Slightly different to the tokenizeIncludingWhitespace regex used above in + // that this one treats each individual newline as a distinct tokens, rather + // than merging them into other surrounding whitespace. This was requested + // in https://github.com/kpdecker/jsdiff/issues/180 & + // https://github.com/kpdecker/jsdiff/issues/211 + var regex = new RegExp("(\\r?\\n)|[".concat(extendedWordChars, "]+|[^\\S\\n\\r]+|[^").concat(extendedWordChars, "]"), 'ug'); + return value.match(regex) || []; +}; function diffWordsWithSpace(oldStr, newStr, options) { - return wordDiff.diff(oldStr, newStr, options); + return wordWithSpaceDiff.diff(oldStr, newStr, options); } -var lineDiff = new Diff(); +function generateOptions(options, defaults) { + if (typeof options === 'function') { + defaults.callback = options; + } else if (options) { + for (var name in options) { + /* istanbul ignore else */ + if (options.hasOwnProperty(name)) { + defaults[name] = options[name]; + } + } + } + return defaults; +} -lineDiff.tokenize = function (value) { - if (this.options.stripTrailingCr) { +var lineDiff = new Diff(); +lineDiff.tokenize = function (value, options) { + if (options.stripTrailingCr) { // remove one \r before \n to match GNU diff's --strip-trailing-cr behavior value = value.replace(/\r\n/g, '\n'); } - var retLines = [], - linesAndNewlines = value.split(/(\n|\r\n)/); // Ignore the final empty token that occurs if the string ends with a new line + linesAndNewlines = value.split(/(\n|\r\n)/); + // Ignore the final empty token that occurs if the string ends with a new line if (!linesAndNewlines[linesAndNewlines.length - 1]) { linesAndNewlines.pop(); - } // Merge the content and line separators into single tokens - + } + // Merge the content and line separators into single tokens for (var i = 0; i < linesAndNewlines.length; i++) { var line = linesAndNewlines[i]; - - if (i % 2 && !this.options.newlineIsToken) { + if (i % 2 && !options.newlineIsToken) { retLines[retLines.length - 1] += line; } else { - if (this.options.ignoreWhitespace) { - line = line.trim(); - } - retLines.push(line); } } - return retLines; }; - +lineDiff.equals = function (left, right, options) { + // If we're ignoring whitespace, we need to normalise lines by stripping + // whitespace before checking equality. (This has an annoying interaction + // with newlineIsToken that requires special handling: if newlines get their + // own token, then we DON'T want to trim the *newline* tokens down to empty + // strings, since this would cause us to treat whitespace-only line content + // as equal to a separator between lines, which would be weird and + // inconsistent with the documented behavior of the options.) + if (options.ignoreWhitespace) { + if (!options.newlineIsToken || !left.includes('\n')) { + left = left.trim(); + } + if (!options.newlineIsToken || !right.includes('\n')) { + right = right.trim(); + } + } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) { + if (left.endsWith('\n')) { + left = left.slice(0, -1); + } + if (right.endsWith('\n')) { + right = right.slice(0, -1); + } + } + return Diff.prototype.equals.call(this, left, right, options); +}; function diffLines(oldStr, newStr, callback) { return lineDiff.diff(oldStr, newStr, callback); } + +// Kept for backwards compatibility. This is a rather arbitrary wrapper method +// that just calls `diffLines` with `ignoreWhitespace: true`. It's confusing to +// have two ways to do exactly the same thing in the API, so we no longer +// document this one (library users should explicitly use `diffLines` with +// `ignoreWhitespace: true` instead) but we keep it around to maintain +// compatibility with code that used old versions. function diffTrimmedLines(oldStr, newStr, callback) { var options = generateOptions(callback, { ignoreWhitespace: true @@ -425,42 +727,67 @@ function diffTrimmedLines(oldStr, newStr, callback) { } var sentenceDiff = new Diff(); - sentenceDiff.tokenize = function (value) { return value.split(/(\S.+?[.!?])(?=\s+|$)/); }; - function diffSentences(oldStr, newStr, callback) { return sentenceDiff.diff(oldStr, newStr, callback); } var cssDiff = new Diff(); - cssDiff.tokenize = function (value) { return value.split(/([{}:;,]|\s+)/); }; - function diffCss(oldStr, newStr, callback) { return cssDiff.diff(oldStr, newStr, callback); } -function _typeof(obj) { - "@babel/helpers - typeof"; - - if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { - _typeof = function (obj) { - return typeof obj; - }; - } else { - _typeof = function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }; +function ownKeys(e, r) { + var t = Object.keys(e); + if (Object.getOwnPropertySymbols) { + var o = Object.getOwnPropertySymbols(e); + r && (o = o.filter(function (r) { + return Object.getOwnPropertyDescriptor(e, r).enumerable; + })), t.push.apply(t, o); } - - return _typeof(obj); + return t; +} +function _objectSpread2(e) { + for (var r = 1; r < arguments.length; r++) { + var t = null != arguments[r] ? arguments[r] : {}; + r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { + _defineProperty(e, r, t[r]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { + Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); + }); + } + return e; +} +function _toPrimitive(t, r) { + if ("object" != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != typeof i) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return ("string" === r ? String : Number)(t); +} +function _toPropertyKey(t) { + var i = _toPrimitive(t, "string"); + return "symbol" == typeof i ? i : i + ""; } +function _typeof(o) { + "@babel/helpers - typeof"; + return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { + return typeof o; + } : function (o) { + return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; + }, _typeof(o); +} function _defineProperty(obj, key, value) { + key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, @@ -471,56 +798,17 @@ function _defineProperty(obj, key, value) { } else { obj[key] = value; } - return obj; } - -function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - if (enumerableOnly) symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - }); - keys.push.apply(keys, symbols); - } - - return keys; -} - -function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i] != null ? arguments[i] : {}; - - if (i % 2) { - ownKeys(Object(source), true).forEach(function (key) { - _defineProperty(target, key, source[key]); - }); - } else if (Object.getOwnPropertyDescriptors) { - Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); - } else { - ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - } - - return target; -} - function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } - function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } - function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); + if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } - function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); @@ -529,238 +817,263 @@ function _unsupportedIterableToArray(o, minLen) { if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; } - function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } -var objectPrototypeToString = Object.prototype.toString; -var jsonDiff = new Diff(); // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a +var jsonDiff = new Diff(); +// Discriminate between two lines of pretty-printed, serialized JSON where one of them has a // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: - jsonDiff.useLongestToken = true; jsonDiff.tokenize = lineDiff.tokenize; - -jsonDiff.castInput = function (value) { - var _this$options = this.options, - undefinedReplacement = _this$options.undefinedReplacement, - _this$options$stringi = _this$options.stringifyReplacer, - stringifyReplacer = _this$options$stringi === void 0 ? function (k, v) { - return typeof v === 'undefined' ? undefinedReplacement : v; - } : _this$options$stringi; +jsonDiff.castInput = function (value, options) { + var undefinedReplacement = options.undefinedReplacement, + _options$stringifyRep = options.stringifyReplacer, + stringifyReplacer = _options$stringifyRep === void 0 ? function (k, v) { + return typeof v === 'undefined' ? undefinedReplacement : v; + } : _options$stringifyRep; return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); }; - -jsonDiff.equals = function (left, right) { - return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); +jsonDiff.equals = function (left, right, options) { + return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'), options); }; - function diffJson(oldObj, newObj, options) { return jsonDiff.diff(oldObj, newObj, options); -} // This function handles the presence of circular references by bailing out when encountering an -// object that is already on the "stack" of items being processed. Accepts an optional replacer +} +// This function handles the presence of circular references by bailing out when encountering an +// object that is already on the "stack" of items being processed. Accepts an optional replacer function canonicalize(obj, stack, replacementStack, replacer, key) { stack = stack || []; replacementStack = replacementStack || []; - if (replacer) { obj = replacer(key, obj); } - var i; - for (i = 0; i < stack.length; i += 1) { if (stack[i] === obj) { return replacementStack[i]; } } - var canonicalizedObj; - - if ('[object Array]' === objectPrototypeToString.call(obj)) { + if ('[object Array]' === Object.prototype.toString.call(obj)) { stack.push(obj); canonicalizedObj = new Array(obj.length); replacementStack.push(canonicalizedObj); - for (i = 0; i < obj.length; i += 1) { canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); } - stack.pop(); replacementStack.pop(); return canonicalizedObj; } - if (obj && obj.toJSON) { obj = obj.toJSON(); } - if (_typeof(obj) === 'object' && obj !== null) { stack.push(obj); canonicalizedObj = {}; replacementStack.push(canonicalizedObj); - var sortedKeys = [], - _key; - + _key; for (_key in obj) { /* istanbul ignore else */ - if (obj.hasOwnProperty(_key)) { + if (Object.prototype.hasOwnProperty.call(obj, _key)) { sortedKeys.push(_key); } } - sortedKeys.sort(); - for (i = 0; i < sortedKeys.length; i += 1) { _key = sortedKeys[i]; canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); } - stack.pop(); replacementStack.pop(); } else { canonicalizedObj = obj; } - return canonicalizedObj; } var arrayDiff = new Diff(); - arrayDiff.tokenize = function (value) { return value.slice(); }; - arrayDiff.join = arrayDiff.removeEmpty = function (value) { return value; }; - function diffArrays(oldArr, newArr, callback) { return arrayDiff.diff(oldArr, newArr, callback); } -function parsePatch(uniDiff) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], - list = [], - i = 0; +function unixToWin(patch) { + if (Array.isArray(patch)) { + return patch.map(unixToWin); + } + return _objectSpread2(_objectSpread2({}, patch), {}, { + hunks: patch.hunks.map(function (hunk) { + return _objectSpread2(_objectSpread2({}, hunk), {}, { + lines: hunk.lines.map(function (line, i) { + var _hunk$lines; + return line.startsWith('\\') || line.endsWith('\r') || (_hunk$lines = hunk.lines[i + 1]) !== null && _hunk$lines !== void 0 && _hunk$lines.startsWith('\\') ? line : line + '\r'; + }) + }); + }) + }); +} +function winToUnix(patch) { + if (Array.isArray(patch)) { + return patch.map(winToUnix); + } + return _objectSpread2(_objectSpread2({}, patch), {}, { + hunks: patch.hunks.map(function (hunk) { + return _objectSpread2(_objectSpread2({}, hunk), {}, { + lines: hunk.lines.map(function (line) { + return line.endsWith('\r') ? line.substring(0, line.length - 1) : line; + }) + }); + }) + }); +} + +/** + * Returns true if the patch consistently uses Unix line endings (or only involves one line and has + * no line endings). + */ +function isUnix(patch) { + if (!Array.isArray(patch)) { + patch = [patch]; + } + return !patch.some(function (index) { + return index.hunks.some(function (hunk) { + return hunk.lines.some(function (line) { + return !line.startsWith('\\') && line.endsWith('\r'); + }); + }); + }); +} + +/** + * Returns true if the patch uses Windows line endings and only Windows line endings. + */ +function isWin(patch) { + if (!Array.isArray(patch)) { + patch = [patch]; + } + return patch.some(function (index) { + return index.hunks.some(function (hunk) { + return hunk.lines.some(function (line) { + return line.endsWith('\r'); + }); + }); + }) && patch.every(function (index) { + return index.hunks.every(function (hunk) { + return hunk.lines.every(function (line, i) { + var _hunk$lines2; + return line.startsWith('\\') || line.endsWith('\r') || ((_hunk$lines2 = hunk.lines[i + 1]) === null || _hunk$lines2 === void 0 ? void 0 : _hunk$lines2.startsWith('\\')); + }); + }); + }); +} +function parsePatch(uniDiff) { + var diffstr = uniDiff.split(/\n/), + list = [], + i = 0; function parseIndex() { var index = {}; - list.push(index); // Parse diff metadata + list.push(index); + // Parse diff metadata while (i < diffstr.length) { - var line = diffstr[i]; // File header found, end parsing diff metadata + var line = diffstr[i]; + // File header found, end parsing diff metadata if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { break; - } // Diff index - + } + // Diff index var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); - if (header) { index.index = header[1]; } - i++; - } // Parse file headers if they are defined. Unified diff requires them, but - // there's no technical issues to have an isolated hunk without file header - + } + // Parse file headers if they are defined. Unified diff requires them, but + // there's no technical issues to have an isolated hunk without file header + parseFileHeader(index); parseFileHeader(index); - parseFileHeader(index); // Parse hunks + // Parse hunks index.hunks = []; - while (i < diffstr.length) { var _line = diffstr[i]; - - if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { + if (/^(Index:\s|diff\s|\-\-\-\s|\+\+\+\s|===================================================================)/.test(_line)) { break; } else if (/^@@/.test(_line)) { index.hunks.push(parseHunk()); - } else if (_line && options.strict) { - // Ignore unexpected content unless in strict mode + } else if (_line) { throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); } else { i++; } } - } // Parses the --- and +++ headers, if none are found, no lines - // are consumed. - + } + // Parses the --- and +++ headers, if none are found, no lines + // are consumed. function parseFileHeader(index) { - var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); - + var fileHeader = /^(---|\+\+\+)\s+(.*)\r?$/.exec(diffstr[i]); if (fileHeader) { var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; var data = fileHeader[2].split('\t', 2); var fileName = data[0].replace(/\\\\/g, '\\'); - if (/^".*"$/.test(fileName)) { fileName = fileName.substr(1, fileName.length - 2); } - index[keyPrefix + 'FileName'] = fileName; index[keyPrefix + 'Header'] = (data[1] || '').trim(); i++; } - } // Parses a hunk - // This assumes that we are at the start of a hunk. - + } + // Parses a hunk + // This assumes that we are at the start of a hunk. function parseHunk() { var chunkHeaderIndex = i, - chunkHeaderLine = diffstr[i++], - chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); + chunkHeaderLine = diffstr[i++], + chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); var hunk = { oldStart: +chunkHeader[1], oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2], newStart: +chunkHeader[3], newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4], - lines: [], - linedelimiters: [] - }; // Unified Diff Format quirk: If the chunk size is 0, + lines: [] + }; + + // Unified Diff Format quirk: If the chunk size is 0, // the first number is one lower than one would expect. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 - if (hunk.oldLines === 0) { hunk.oldStart += 1; } - if (hunk.newLines === 0) { hunk.newStart += 1; } - var addCount = 0, - removeCount = 0; - - for (; i < diffstr.length; i++) { - // Lines starting with '---' could be mistaken for the "remove line" operation - // But they could be the header for the next file. Therefore prune such cases out. - if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { - break; - } - + removeCount = 0; + for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || (_diffstr$i = diffstr[i]) !== null && _diffstr$i !== void 0 && _diffstr$i.startsWith('\\')); i++) { + var _diffstr$i; var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; - if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { hunk.lines.push(diffstr[i]); - hunk.linedelimiters.push(delimiters[i] || '\n'); - if (operation === '+') { addCount++; } else if (operation === '-') { @@ -770,37 +1083,30 @@ function parsePatch(uniDiff) { removeCount++; } } else { - break; + throw new Error("Hunk at line ".concat(chunkHeaderIndex + 1, " contained invalid line ").concat(diffstr[i])); } - } // Handle the empty block count case - + } + // Handle the empty block count case if (!addCount && hunk.newLines === 1) { hunk.newLines = 0; } - if (!removeCount && hunk.oldLines === 1) { hunk.oldLines = 0; - } // Perform optional sanity checking - - - if (options.strict) { - if (addCount !== hunk.newLines) { - throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } - - if (removeCount !== hunk.oldLines) { - throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } } + // Perform sanity checking + if (addCount !== hunk.newLines) { + throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + if (removeCount !== hunk.oldLines) { + throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } return hunk; } - while (i < diffstr.length) { parseIndex(); } - return list; } @@ -809,210 +1115,275 @@ function parsePatch(uniDiff) { // start of 2, this will iterate 2, 3, 1, 4, 0. function distanceIterator (start, minLine, maxLine) { var wantForward = true, - backwardExhausted = false, - forwardExhausted = false, - localOffset = 1; + backwardExhausted = false, + forwardExhausted = false, + localOffset = 1; return function iterator() { if (wantForward && !forwardExhausted) { if (backwardExhausted) { localOffset++; } else { wantForward = false; - } // Check if trying to fit beyond text length, and if not, check it fits - // after offset location (or desired location on first iteration) - + } + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) if (start + localOffset <= maxLine) { - return localOffset; + return start + localOffset; } - forwardExhausted = true; } - if (!backwardExhausted) { if (!forwardExhausted) { wantForward = true; - } // Check if trying to fit before text beginning, and if not, check it fits - // before offset location - + } + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location if (minLine <= start - localOffset) { - return -localOffset++; + return start - localOffset++; } - backwardExhausted = true; return iterator(); - } // We tried to fit hunk before text beginning and beyond text length, then - // hunk can't fit on the text. Return undefined + } + // We tried to fit hunk before text beginning and beyond text length, then + // hunk can't fit on the text. Return undefined }; } function applyPatch(source, uniDiff) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - if (typeof uniDiff === 'string') { uniDiff = parsePatch(uniDiff); } - if (Array.isArray(uniDiff)) { if (uniDiff.length > 1) { throw new Error('applyPatch only works with a single input.'); } - uniDiff = uniDiff[0]; - } // Apply the diff to the input - - - var lines = source.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], - hunks = uniDiff.hunks, - compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) { - return line === patchContent; - }, - errorCount = 0, - fuzzFactor = options.fuzzFactor || 0, - minLine = 0, - offset = 0, - removeEOFNL, - addEOFNL; - /** - * Checks if the hunk exactly fits on the provided location - */ + } + if (options.autoConvertLineEndings || options.autoConvertLineEndings == null) { + if (hasOnlyWinLineEndings(source) && isUnix(uniDiff)) { + uniDiff = unixToWin(uniDiff); + } else if (hasOnlyUnixLineEndings(source) && isWin(uniDiff)) { + uniDiff = winToUnix(uniDiff); + } + } + // Apply the diff to the input + var lines = source.split('\n'), + hunks = uniDiff.hunks, + compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) { + return line === patchContent; + }, + fuzzFactor = options.fuzzFactor || 0, + minLine = 0; + if (fuzzFactor < 0 || !Number.isInteger(fuzzFactor)) { + throw new Error('fuzzFactor must be a non-negative integer'); + } - function hunkFits(hunk, toPos) { - for (var j = 0; j < hunk.lines.length; j++) { - var line = hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line; + // Special case for empty patch. + if (!hunks.length) { + return source; + } - if (operation === ' ' || operation === '-') { - // Context sanity check - if (!compareLine(toPos + 1, lines[toPos], operation, content)) { - errorCount++; + // Before anything else, handle EOFNL insertion/removal. If the patch tells us to make a change + // to the EOFNL that is redundant/impossible - i.e. to remove a newline that's not there, or add a + // newline that already exists - then we either return false and fail to apply the patch (if + // fuzzFactor is 0) or simply ignore the problem and do nothing (if fuzzFactor is >0). + // If we do need to remove/add a newline at EOF, this will always be in the final hunk: + var prevLine = '', + removeEOFNL = false, + addEOFNL = false; + for (var i = 0; i < hunks[hunks.length - 1].lines.length; i++) { + var line = hunks[hunks.length - 1].lines[i]; + if (line[0] == '\\') { + if (prevLine[0] == '+') { + removeEOFNL = true; + } else if (prevLine[0] == '-') { + addEOFNL = true; + } + } + prevLine = line; + } + if (removeEOFNL) { + if (addEOFNL) { + // This means the final line gets changed but doesn't have a trailing newline in either the + // original or patched version. In that case, we do nothing if fuzzFactor > 0, and if + // fuzzFactor is 0, we simply validate that the source file has no trailing newline. + if (!fuzzFactor && lines[lines.length - 1] == '') { + return false; + } + } else if (lines[lines.length - 1] == '') { + lines.pop(); + } else if (!fuzzFactor) { + return false; + } + } else if (addEOFNL) { + if (lines[lines.length - 1] != '') { + lines.push(''); + } else if (!fuzzFactor) { + return false; + } + } - if (errorCount > fuzzFactor) { - return false; + /** + * Checks if the hunk can be made to fit at the provided location with at most `maxErrors` + * insertions, substitutions, or deletions, while ensuring also that: + * - lines deleted in the hunk match exactly, and + * - wherever an insertion operation or block of insertion operations appears in the hunk, the + * immediately preceding and following lines of context match exactly + * + * `toPos` should be set such that lines[toPos] is meant to match hunkLines[0]. + * + * If the hunk can be applied, returns an object with properties `oldLineLastI` and + * `replacementLines`. Otherwise, returns null. + */ + function applyHunk(hunkLines, toPos, maxErrors) { + var hunkLinesI = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; + var lastContextLineMatched = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; + var patchedLines = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : []; + var patchedLinesLength = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0; + var nConsecutiveOldContextLines = 0; + var nextContextLineMustMatch = false; + for (; hunkLinesI < hunkLines.length; hunkLinesI++) { + var hunkLine = hunkLines[hunkLinesI], + operation = hunkLine.length > 0 ? hunkLine[0] : ' ', + content = hunkLine.length > 0 ? hunkLine.substr(1) : hunkLine; + if (operation === '-') { + if (compareLine(toPos + 1, lines[toPos], operation, content)) { + toPos++; + nConsecutiveOldContextLines = 0; + } else { + if (!maxErrors || lines[toPos] == null) { + return null; } + patchedLines[patchedLinesLength] = lines[toPos]; + return applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1); } + } + if (operation === '+') { + if (!lastContextLineMatched) { + return null; + } + patchedLines[patchedLinesLength] = content; + patchedLinesLength++; + nConsecutiveOldContextLines = 0; + nextContextLineMustMatch = true; + } + if (operation === ' ') { + nConsecutiveOldContextLines++; + patchedLines[patchedLinesLength] = lines[toPos]; + if (compareLine(toPos + 1, lines[toPos], operation, content)) { + patchedLinesLength++; + lastContextLineMatched = true; + nextContextLineMustMatch = false; + toPos++; + } else { + if (nextContextLineMustMatch || !maxErrors) { + return null; + } - toPos++; + // Consider 3 possibilities in sequence: + // 1. lines contains a *substitution* not included in the patch context, or + // 2. lines contains an *insertion* not included in the patch context, or + // 3. lines contains a *deletion* not included in the patch context + // The first two options are of course only possible if the line from lines is non-null - + // i.e. only option 3 is possible if we've overrun the end of the old file. + return lines[toPos] && (applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength + 1) || applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1)) || applyHunk(hunkLines, toPos, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength); + } } } - return true; - } // Search best fit offsets for each hunk based on the previous ones - - - for (var i = 0; i < hunks.length; i++) { - var hunk = hunks[i], - maxLine = lines.length - hunk.oldLines, - localOffset = 0, - toPos = offset + hunk.oldStart - 1; - var iterator = distanceIterator(toPos, minLine, maxLine); + // Before returning, trim any unmodified context lines off the end of patchedLines and reduce + // toPos (and thus oldLineLastI) accordingly. This allows later hunks to be applied to a region + // that starts in this hunk's trailing context. + patchedLinesLength -= nConsecutiveOldContextLines; + toPos -= nConsecutiveOldContextLines; + patchedLines.length = patchedLinesLength; + return { + patchedLines: patchedLines, + oldLineLastI: toPos - 1 + }; + } + var resultLines = []; - for (; localOffset !== undefined; localOffset = iterator()) { - if (hunkFits(hunk, toPos + localOffset)) { - hunk.offset = offset += localOffset; + // Search best fit offsets for each hunk based on the previous ones + var prevHunkOffset = 0; + for (var _i = 0; _i < hunks.length; _i++) { + var hunk = hunks[_i]; + var hunkResult = void 0; + var maxLine = lines.length - hunk.oldLines + fuzzFactor; + var toPos = void 0; + for (var maxErrors = 0; maxErrors <= fuzzFactor; maxErrors++) { + toPos = hunk.oldStart + prevHunkOffset - 1; + var iterator = distanceIterator(toPos, minLine, maxLine); + for (; toPos !== undefined; toPos = iterator()) { + hunkResult = applyHunk(hunk.lines, toPos, maxErrors); + if (hunkResult) { + break; + } + } + if (hunkResult) { break; } } - - if (localOffset === undefined) { + if (!hunkResult) { return false; - } // Set lower text limit to end of the current hunk, so next ones don't try - // to fit over already patched text - - - minLine = hunk.offset + hunk.oldStart + hunk.oldLines; - } // Apply patch hunks - - - var diffOffset = 0; - - for (var _i = 0; _i < hunks.length; _i++) { - var _hunk = hunks[_i], - _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; - - diffOffset += _hunk.newLines - _hunk.oldLines; + } - for (var j = 0; j < _hunk.lines.length; j++) { - var line = _hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line, - delimiter = _hunk.linedelimiters && _hunk.linedelimiters[j] || '\n'; + // Copy everything from the end of where we applied the last hunk to the start of this hunk + for (var _i2 = minLine; _i2 < toPos; _i2++) { + resultLines.push(lines[_i2]); + } - if (operation === ' ') { - _toPos++; - } else if (operation === '-') { - lines.splice(_toPos, 1); - delimiters.splice(_toPos, 1); - /* istanbul ignore else */ - } else if (operation === '+') { - lines.splice(_toPos, 0, content); - delimiters.splice(_toPos, 0, delimiter); - _toPos++; - } else if (operation === '\\') { - var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; - - if (previousOperation === '+') { - removeEOFNL = true; - } else if (previousOperation === '-') { - addEOFNL = true; - } - } + // Add the lines produced by applying the hunk: + for (var _i3 = 0; _i3 < hunkResult.patchedLines.length; _i3++) { + var _line = hunkResult.patchedLines[_i3]; + resultLines.push(_line); } - } // Handle EOFNL insertion/removal + // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text + minLine = hunkResult.oldLineLastI + 1; - if (removeEOFNL) { - while (!lines[lines.length - 1]) { - lines.pop(); - delimiters.pop(); - } - } else if (addEOFNL) { - lines.push(''); - delimiters.push('\n'); + // Note the offset between where the patch said the hunk should've applied and where we + // applied it, so we can adjust future hunks accordingly: + prevHunkOffset = toPos + 1 - hunk.oldStart; } - for (var _k = 0; _k < lines.length - 1; _k++) { - lines[_k] = lines[_k] + delimiters[_k]; + // Copy over the rest of the lines from the old text + for (var _i4 = minLine; _i4 < lines.length; _i4++) { + resultLines.push(lines[_i4]); } + return resultLines.join('\n'); +} - return lines.join(''); -} // Wrapper that supports multiple file patches via callbacks. - +// Wrapper that supports multiple file patches via callbacks. function applyPatches(uniDiff, options) { if (typeof uniDiff === 'string') { uniDiff = parsePatch(uniDiff); } - var currentIndex = 0; - function processIndex() { var index = uniDiff[currentIndex++]; - if (!index) { return options.complete(); } - options.loadFile(index, function (err, data) { if (err) { return options.complete(err); } - var updatedContent = applyPatch(data, index, options); options.patched(index, updatedContent, function (err) { if (err) { return options.complete(err); } - processIndex(); }); }); } - processIndex(); } @@ -1020,206 +1391,238 @@ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, ne if (!options) { options = {}; } - + if (typeof options === 'function') { + options = { + callback: options + }; + } if (typeof options.context === 'undefined') { options.context = 4; } - - var diff = diffLines(oldStr, newStr, options); - - if (!diff) { - return; + if (options.newlineIsToken) { + throw new Error('newlineIsToken may not be used with patch-generation functions, only with diffing functions'); } - - diff.push({ - value: '', - lines: [] - }); // Append an empty value to make cleanup easier - - function contextLines(lines) { - return lines.map(function (entry) { - return ' ' + entry; - }); + if (!options.callback) { + return diffLinesResultToPatch(diffLines(oldStr, newStr, options)); + } else { + var _options = options, + _callback = _options.callback; + diffLines(oldStr, newStr, _objectSpread2(_objectSpread2({}, options), {}, { + callback: function callback(diff) { + var patch = diffLinesResultToPatch(diff); + _callback(patch); + } + })); } + function diffLinesResultToPatch(diff) { + // STEP 1: Build up the patch with no "\ No newline at end of file" lines and with the arrays + // of lines containing trailing newline characters. We'll tidy up later... - var hunks = []; - var oldRangeStart = 0, + if (!diff) { + return; + } + diff.push({ + value: '', + lines: [] + }); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function (entry) { + return ' ' + entry; + }); + } + var hunks = []; + var oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1; - - var _loop = function _loop(i) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - var _curRange; - - // If we have previous context, start with that - if (!oldRangeStart) { - var prev = diff[i - 1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; + var _loop = function _loop() { + var current = diff[i], + lines = current.lines || splitLines(current.value); + current.lines = lines; + if (current.added || current.removed) { + var _curRange; + // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + if (prev) { + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } } - } // Output our changes + // Output our changes + (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) { + return (current.added ? '+' : '-') + entry; + }))); - (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) { - return (current.added ? '+' : '-') + entry; - }))); // Track the updated file position - - - if (current.added) { - newLine += lines.length; + // Track the updated file position + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= options.context * 2 && i < diff.length - 2) { + var _curRange2; + // Overlapping + (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines))); + } else { + var _curRange3; + // end the range and output + var contextSize = Math.min(lines.length, options.context); + (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize)))); + var _hunk = { + oldStart: oldRangeStart, + oldLines: oldLine - oldRangeStart + contextSize, + newStart: newRangeStart, + newLines: newLine - newRangeStart + contextSize, + lines: curRange + }; + hunks.push(_hunk); + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; + } + } oldLine += lines.length; + newLine += lines.length; } - } else { - // Identical context lines. Track line changes - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= options.context * 2 && i < diff.length - 2) { - var _curRange2; - - // Overlapping - (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines))); - } else { - var _curRange3; - - // end the range and output - var contextSize = Math.min(lines.length, options.context); - - (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize)))); - - var hunk = { - oldStart: oldRangeStart, - oldLines: oldLine - oldRangeStart + contextSize, - newStart: newRangeStart, - newLines: newLine - newRangeStart + contextSize, - lines: curRange - }; - - if (i >= diff.length - 2 && lines.length <= options.context) { - // EOF is inside this hunk - var oldEOFNewline = /\n$/.test(oldStr); - var newEOFNewline = /\n$/.test(newStr); - var noNlBeforeAdds = lines.length == 0 && curRange.length > hunk.oldLines; - - if (!oldEOFNewline && noNlBeforeAdds && oldStr.length > 0) { - // special case: old has no eol and no trailing context; no-nl can end up before adds - // however, if the old file is empty, do not output the no-nl line - curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); - } - - if (!oldEOFNewline && !noNlBeforeAdds || !newEOFNewline) { - curRange.push('\\ No newline at end of file'); - } - } + }; + for (var i = 0; i < diff.length; i++) { + _loop(); + } - hunks.push(hunk); - oldRangeStart = 0; - newRangeStart = 0; - curRange = []; + // Step 2: eliminate the trailing `\n` from each line of each hunk, and, where needed, add + // "\ No newline at end of file". + for (var _i = 0, _hunks = hunks; _i < _hunks.length; _i++) { + var hunk = _hunks[_i]; + for (var _i2 = 0; _i2 < hunk.lines.length; _i2++) { + if (hunk.lines[_i2].endsWith('\n')) { + hunk.lines[_i2] = hunk.lines[_i2].slice(0, -1); + } else { + hunk.lines.splice(_i2 + 1, 0, '\\ No newline at end of file'); + _i2++; // Skip the line we just added, then continue iterating } } - - oldLine += lines.length; - newLine += lines.length; } - }; - - for (var i = 0; i < diff.length; i++) { - _loop(i); + return { + oldFileName: oldFileName, + newFileName: newFileName, + oldHeader: oldHeader, + newHeader: newHeader, + hunks: hunks + }; } - - return { - oldFileName: oldFileName, - newFileName: newFileName, - oldHeader: oldHeader, - newHeader: newHeader, - hunks: hunks - }; } function formatPatch(diff) { if (Array.isArray(diff)) { return diff.map(formatPatch).join('\n'); } - var ret = []; - if (diff.oldFileName == diff.newFileName) { ret.push('Index: ' + diff.oldFileName); } - ret.push('==================================================================='); ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); - for (var i = 0; i < diff.hunks.length; i++) { - var hunk = diff.hunks[i]; // Unified Diff Format quirk: If the chunk size is 0, + var hunk = diff.hunks[i]; + // Unified Diff Format quirk: If the chunk size is 0, // the first number is one lower than one would expect. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 - if (hunk.oldLines === 0) { hunk.oldStart -= 1; } - if (hunk.newLines === 0) { hunk.newStart -= 1; } - ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); ret.push.apply(ret, hunk.lines); } - return ret.join('\n') + '\n'; } function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { - return formatPatch(structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)); + var _options2; + if (typeof options === 'function') { + options = { + callback: options + }; + } + if (!((_options2 = options) !== null && _options2 !== void 0 && _options2.callback)) { + var patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); + if (!patchObj) { + return; + } + return formatPatch(patchObj); + } else { + var _options3 = options, + _callback2 = _options3.callback; + structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, _objectSpread2(_objectSpread2({}, options), {}, { + callback: function callback(patchObj) { + if (!patchObj) { + _callback2(); + } else { + _callback2(formatPatch(patchObj)); + } + } + })); + } } function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); } +/** + * Split `text` into an array of lines, including the trailing newline character (where present) + */ +function splitLines(text) { + var hasTrailingNl = text.endsWith('\n'); + var result = text.split('\n').map(function (line) { + return line + '\n'; + }); + if (hasTrailingNl) { + result.pop(); + } else { + result.push(result.pop().slice(0, -1)); + } + return result; +} + function arrayEqual(a, b) { if (a.length !== b.length) { return false; } - return arrayStartsWith(a, b); } function arrayStartsWith(array, start) { if (start.length > array.length) { return false; } - for (var i = 0; i < start.length; i++) { if (start[i] !== array[i]) { return false; } } - return true; } function calcLineCount(hunk) { var _calcOldNewLineCount = calcOldNewLineCount(hunk.lines), - oldLines = _calcOldNewLineCount.oldLines, - newLines = _calcOldNewLineCount.newLines; - + oldLines = _calcOldNewLineCount.oldLines, + newLines = _calcOldNewLineCount.newLines; if (oldLines !== undefined) { hunk.oldLines = oldLines; } else { delete hunk.oldLines; } - if (newLines !== undefined) { hunk.newLines = newLines; } else { @@ -1229,14 +1632,14 @@ function calcLineCount(hunk) { function merge(mine, theirs, base) { mine = loadPatch(mine, base); theirs = loadPatch(theirs, base); - var ret = {}; // For index we just let it pass through as it doesn't have any necessary meaning. + var ret = {}; + + // For index we just let it pass through as it doesn't have any necessary meaning. // Leaving sanity checks on this to the API consumer that may know more about the // meaning in their own context. - if (mine.index || theirs.index) { ret.index = mine.index || theirs.index; } - if (mine.newFileName || theirs.newFileName) { if (!fileNameChanged(mine)) { // No header or no change in ours, use theirs (and ours if theirs does not exist) @@ -1258,21 +1661,18 @@ function merge(mine, theirs, base) { ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); } } - ret.hunks = []; var mineIndex = 0, - theirsIndex = 0, - mineOffset = 0, - theirsOffset = 0; - + theirsIndex = 0, + mineOffset = 0, + theirsOffset = 0; while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { var mineCurrent = mine.hunks[mineIndex] || { - oldStart: Infinity - }, - theirsCurrent = theirs.hunks[theirsIndex] || { - oldStart: Infinity - }; - + oldStart: Infinity + }, + theirsCurrent = theirs.hunks[theirsIndex] || { + oldStart: Infinity + }; if (hunkBefore(mineCurrent, theirsCurrent)) { // This patch does not overlap with any of the others, yay. ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); @@ -1298,30 +1698,23 @@ function merge(mine, theirs, base) { ret.hunks.push(mergedHunk); } } - return ret; } - function loadPatch(param, base) { if (typeof param === 'string') { if (/^@@/m.test(param) || /^Index:/m.test(param)) { return parsePatch(param)[0]; } - if (!base) { throw new Error('Must provide a base reference or pass in a patch'); } - return structuredPatch(undefined, undefined, base, param); } - return param; } - function fileNameChanged(patch) { return patch.newFileName && patch.newFileName !== patch.oldFileName; } - function selectField(index, mine, theirs) { if (mine === theirs) { return mine; @@ -1333,11 +1726,9 @@ function selectField(index, mine, theirs) { }; } } - function hunkBefore(test, check) { return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; } - function cloneHunk(hunk, offset) { return { oldStart: hunk.oldStart, @@ -1347,39 +1738,37 @@ function cloneHunk(hunk, offset) { lines: hunk.lines }; } - function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { // This will generally result in a conflicted hunk, but there are cases where the context // is the only overlap where we can successfully merge the content here. var mine = { - offset: mineOffset, - lines: mineLines, - index: 0 - }, - their = { - offset: theirOffset, - lines: theirLines, - index: 0 - }; // Handle any leading content + offset: mineOffset, + lines: mineLines, + index: 0 + }, + their = { + offset: theirOffset, + lines: theirLines, + index: 0 + }; + // Handle any leading content insertLeading(hunk, mine, their); - insertLeading(hunk, their, mine); // Now in the overlap content. Scan through and select the best changes from each. + insertLeading(hunk, their, mine); + // Now in the overlap content. Scan through and select the best changes from each. while (mine.index < mine.lines.length && their.index < their.lines.length) { var mineCurrent = mine.lines[mine.index], - theirCurrent = their.lines[their.index]; - + theirCurrent = their.lines[their.index]; if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { // Both modified ... mutualChange(hunk, mine, their); } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { var _hunk$lines; - // Mine inserted (_hunk$lines = hunk.lines).push.apply(_hunk$lines, _toConsumableArray(collectChange(mine))); } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { var _hunk$lines2; - // Theirs inserted (_hunk$lines2 = hunk.lines).push.apply(_hunk$lines2, _toConsumableArray(collectChange(their))); } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') { @@ -1397,57 +1786,44 @@ function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { // Context mismatch conflict(hunk, collectChange(mine), collectChange(their)); } - } // Now push anything that may be remaining - + } + // Now push anything that may be remaining insertTrailing(hunk, mine); insertTrailing(hunk, their); calcLineCount(hunk); } - function mutualChange(hunk, mine, their) { var myChanges = collectChange(mine), - theirChanges = collectChange(their); - + theirChanges = collectChange(their); if (allRemoves(myChanges) && allRemoves(theirChanges)) { // Special case for remove changes that are supersets of one another if (arrayStartsWith(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { var _hunk$lines3; - (_hunk$lines3 = hunk.lines).push.apply(_hunk$lines3, _toConsumableArray(myChanges)); - return; } else if (arrayStartsWith(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { var _hunk$lines4; - (_hunk$lines4 = hunk.lines).push.apply(_hunk$lines4, _toConsumableArray(theirChanges)); - return; } } else if (arrayEqual(myChanges, theirChanges)) { var _hunk$lines5; - (_hunk$lines5 = hunk.lines).push.apply(_hunk$lines5, _toConsumableArray(myChanges)); - return; } - conflict(hunk, myChanges, theirChanges); } - function removal(hunk, mine, their, swap) { var myChanges = collectChange(mine), - theirChanges = collectContext(their, myChanges); - + theirChanges = collectContext(their, myChanges); if (theirChanges.merged) { var _hunk$lines6; - (_hunk$lines6 = hunk.lines).push.apply(_hunk$lines6, _toConsumableArray(theirChanges.merged)); } else { conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); } } - function conflict(hunk, mine, their) { hunk.conflict = true; hunk.lines.push({ @@ -1456,7 +1832,6 @@ function conflict(hunk, mine, their) { theirs: their }); } - function insertLeading(hunk, insert, their) { while (insert.offset < their.offset && insert.index < insert.lines.length) { var line = insert.lines[insert.index++]; @@ -1464,25 +1839,22 @@ function insertLeading(hunk, insert, their) { insert.offset++; } } - function insertTrailing(hunk, insert) { while (insert.index < insert.lines.length) { var line = insert.lines[insert.index++]; hunk.lines.push(line); } } - function collectChange(state) { var ret = [], - operation = state.lines[state.index][0]; - + operation = state.lines[state.index][0]; while (state.index < state.lines.length) { - var line = state.lines[state.index]; // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. + var line = state.lines[state.index]; + // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. if (operation === '-' && line[0] === '+') { operation = '+'; } - if (operation === line[0]) { ret.push(line); state.index++; @@ -1490,39 +1862,35 @@ function collectChange(state) { break; } } - return ret; } - function collectContext(state, matchChanges) { var changes = [], - merged = [], - matchIndex = 0, - contextChanges = false, - conflicted = false; - + merged = [], + matchIndex = 0, + contextChanges = false, + conflicted = false; while (matchIndex < matchChanges.length && state.index < state.lines.length) { var change = state.lines[state.index], - match = matchChanges[matchIndex]; // Once we've hit our add, then we are done + match = matchChanges[matchIndex]; + // Once we've hit our add, then we are done if (match[0] === '+') { break; } - contextChanges = contextChanges || change[0] !== ' '; merged.push(match); - matchIndex++; // Consume any additions in the other block as a conflict to attempt - // to pull in the remaining context after this + matchIndex++; + // Consume any additions in the other block as a conflict to attempt + // to pull in the remaining context after this if (change[0] === '+') { conflicted = true; - while (change[0] === '+') { changes.push(change); change = state.lines[++state.index]; } } - if (match.substr(1) === change.substr(1)) { changes.push(change); state.index++; @@ -1530,44 +1898,35 @@ function collectContext(state, matchChanges) { conflicted = true; } } - if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { conflicted = true; } - if (conflicted) { return changes; } - while (matchIndex < matchChanges.length) { merged.push(matchChanges[matchIndex++]); } - return { merged: merged, changes: changes }; } - function allRemoves(changes) { return changes.reduce(function (prev, change) { return prev && change[0] === '-'; }, true); } - function skipRemoveSuperset(state, removeChanges, delta) { for (var i = 0; i < delta; i++) { var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); - if (state.lines[state.index + i] !== ' ' + changeContent) { return false; } } - state.index += delta; return true; } - function calcOldNewLineCount(lines) { var oldLines = 0; var newLines = 0; @@ -1575,7 +1934,6 @@ function calcOldNewLineCount(lines) { if (typeof line !== 'string') { var myCount = calcOldNewLineCount(line.mine); var theirCount = calcOldNewLineCount(line.theirs); - if (oldLines !== undefined) { if (myCount.oldLines === theirCount.oldLines) { oldLines += myCount.oldLines; @@ -1583,7 +1941,6 @@ function calcOldNewLineCount(lines) { oldLines = undefined; } } - if (newLines !== undefined) { if (myCount.newLines === theirCount.newLines) { newLines += myCount.newLines; @@ -1595,7 +1952,6 @@ function calcOldNewLineCount(lines) { if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { newLines++; } - if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { oldLines++; } @@ -1611,7 +1967,6 @@ function reversePatch(structuredPatch) { if (Array.isArray(structuredPatch)) { return structuredPatch.map(reversePatch).reverse(); } - return _objectSpread2(_objectSpread2({}, structuredPatch), {}, { oldFileName: structuredPatch.newFileName, oldHeader: structuredPatch.newHeader, @@ -1623,16 +1978,13 @@ function reversePatch(structuredPatch) { oldStart: hunk.newStart, newLines: hunk.oldLines, newStart: hunk.oldStart, - linedelimiters: hunk.linedelimiters, lines: hunk.lines.map(function (l) { if (l.startsWith('-')) { return "+".concat(l.slice(1)); } - if (l.startsWith('+')) { return "-".concat(l.slice(1)); } - return l; }) }; @@ -1643,12 +1995,10 @@ function reversePatch(structuredPatch) { // See: http://code.google.com/p/google-diff-match-patch/wiki/API function convertChangesToDMP(changes) { var ret = [], - change, - operation; - + change, + operation; for (var i = 0; i < changes.length; i++) { change = changes[i]; - if (change.added) { operation = 1; } else if (change.removed) { @@ -1656,37 +2006,29 @@ function convertChangesToDMP(changes) { } else { operation = 0; } - ret.push([operation, change.value]); } - return ret; } function convertChangesToXML(changes) { var ret = []; - for (var i = 0; i < changes.length; i++) { var change = changes[i]; - if (change.added) { ret.push(''); } else if (change.removed) { ret.push(''); } - ret.push(escapeHTML(change.value)); - if (change.added) { ret.push(''); } else if (change.removed) { ret.push(''); } } - return ret.join(''); } - function escapeHTML(s) { var n = s; n = n.replace(/&/g, '&'); diff --git a/deps/npm/node_modules/diff/lib/patch/apply.js b/deps/npm/node_modules/diff/lib/patch/apply.js index cefea04dae73b0..619def1f48efa5 100644 --- a/deps/npm/node_modules/diff/lib/patch/apply.js +++ b/deps/npm/node_modules/diff/lib/patch/apply.js @@ -6,35 +6,39 @@ Object.defineProperty(exports, "__esModule", { }); exports.applyPatch = applyPatch; exports.applyPatches = applyPatches; - /*istanbul ignore end*/ var /*istanbul ignore start*/ +_string = require("../util/string") +/*istanbul ignore end*/ +; +var +/*istanbul ignore start*/ +_lineEndings = require("./line-endings") +/*istanbul ignore end*/ +; +var +/*istanbul ignore start*/ _parse = require("./parse") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _distanceIterator = _interopRequireDefault(require("../util/distance-iterator")) /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - /*istanbul ignore end*/ function applyPatch(source, uniDiff) { /*istanbul ignore start*/ var /*istanbul ignore end*/ options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - if (typeof uniDiff === 'string') { uniDiff = /*istanbul ignore start*/ (0, /*istanbul ignore end*/ - /*istanbul ignore start*/ _parse /*istanbul ignore end*/ @@ -44,160 +48,318 @@ function applyPatch(source, uniDiff) { /*istanbul ignore end*/ (uniDiff); } - if (Array.isArray(uniDiff)) { if (uniDiff.length > 1) { throw new Error('applyPatch only works with a single input.'); } - uniDiff = uniDiff[0]; - } // Apply the diff to the input - - - var lines = source.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], - hunks = uniDiff.hunks, - compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) - /*istanbul ignore start*/ - { - return ( + } + if (options.autoConvertLineEndings || options.autoConvertLineEndings == null) { + if ( + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + hasOnlyWinLineEndings) + /*istanbul ignore end*/ + (source) && + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _lineEndings + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + isUnix) + /*istanbul ignore end*/ + (uniDiff)) { + uniDiff = + /*istanbul ignore start*/ + (0, /*istanbul ignore end*/ - line === patchContent - ); - }, - errorCount = 0, - fuzzFactor = options.fuzzFactor || 0, - minLine = 0, - offset = 0, - removeEOFNL, - addEOFNL; - /** - * Checks if the hunk exactly fits on the provided location - */ - - - function hunkFits(hunk, toPos) { - for (var j = 0; j < hunk.lines.length; j++) { - var line = hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line; + /*istanbul ignore start*/ + _lineEndings + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + unixToWin) + /*istanbul ignore end*/ + (uniDiff); + } else if ( + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _string + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + hasOnlyUnixLineEndings) + /*istanbul ignore end*/ + (source) && + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _lineEndings + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + isWin) + /*istanbul ignore end*/ + (uniDiff)) { + uniDiff = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _lineEndings + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + winToUnix) + /*istanbul ignore end*/ + (uniDiff); + } + } - if (operation === ' ' || operation === '-') { - // Context sanity check - if (!compareLine(toPos + 1, lines[toPos], operation, content)) { - errorCount++; + // Apply the diff to the input + var lines = source.split('\n'), + hunks = uniDiff.hunks, + compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + line === patchContent + ); + }, + fuzzFactor = options.fuzzFactor || 0, + minLine = 0; + if (fuzzFactor < 0 || !Number.isInteger(fuzzFactor)) { + throw new Error('fuzzFactor must be a non-negative integer'); + } - if (errorCount > fuzzFactor) { - return false; - } - } + // Special case for empty patch. + if (!hunks.length) { + return source; + } - toPos++; + // Before anything else, handle EOFNL insertion/removal. If the patch tells us to make a change + // to the EOFNL that is redundant/impossible - i.e. to remove a newline that's not there, or add a + // newline that already exists - then we either return false and fail to apply the patch (if + // fuzzFactor is 0) or simply ignore the problem and do nothing (if fuzzFactor is >0). + // If we do need to remove/add a newline at EOF, this will always be in the final hunk: + var prevLine = '', + removeEOFNL = false, + addEOFNL = false; + for (var i = 0; i < hunks[hunks.length - 1].lines.length; i++) { + var line = hunks[hunks.length - 1].lines[i]; + if (line[0] == '\\') { + if (prevLine[0] == '+') { + removeEOFNL = true; + } else if (prevLine[0] == '-') { + addEOFNL = true; } } + prevLine = line; + } + if (removeEOFNL) { + if (addEOFNL) { + // This means the final line gets changed but doesn't have a trailing newline in either the + // original or patched version. In that case, we do nothing if fuzzFactor > 0, and if + // fuzzFactor is 0, we simply validate that the source file has no trailing newline. + if (!fuzzFactor && lines[lines.length - 1] == '') { + return false; + } + } else if (lines[lines.length - 1] == '') { + lines.pop(); + } else if (!fuzzFactor) { + return false; + } + } else if (addEOFNL) { + if (lines[lines.length - 1] != '') { + lines.push(''); + } else if (!fuzzFactor) { + return false; + } + } - return true; - } // Search best fit offsets for each hunk based on the previous ones - - - for (var i = 0; i < hunks.length; i++) { - var hunk = hunks[i], - maxLine = lines.length - hunk.oldLines, - localOffset = 0, - toPos = offset + hunk.oldStart - 1; - var iterator = + /** + * Checks if the hunk can be made to fit at the provided location with at most `maxErrors` + * insertions, substitutions, or deletions, while ensuring also that: + * - lines deleted in the hunk match exactly, and + * - wherever an insertion operation or block of insertion operations appears in the hunk, the + * immediately preceding and following lines of context match exactly + * + * `toPos` should be set such that lines[toPos] is meant to match hunkLines[0]. + * + * If the hunk can be applied, returns an object with properties `oldLineLastI` and + * `replacementLines`. Otherwise, returns null. + */ + function applyHunk(hunkLines, toPos, maxErrors) { /*istanbul ignore start*/ - (0, + var /*istanbul ignore end*/ - + hunkLinesI = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; + /*istanbul ignore start*/ + var + /*istanbul ignore end*/ + lastContextLineMatched = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; /*istanbul ignore start*/ - _distanceIterator + var /*istanbul ignore end*/ - [ + patchedLines = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : []; /*istanbul ignore start*/ - "default" + var /*istanbul ignore end*/ - ])(toPos, minLine, maxLine); + patchedLinesLength = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0; + var nConsecutiveOldContextLines = 0; + var nextContextLineMustMatch = false; + for (; hunkLinesI < hunkLines.length; hunkLinesI++) { + var hunkLine = hunkLines[hunkLinesI], + operation = hunkLine.length > 0 ? hunkLine[0] : ' ', + content = hunkLine.length > 0 ? hunkLine.substr(1) : hunkLine; + if (operation === '-') { + if (compareLine(toPos + 1, lines[toPos], operation, content)) { + toPos++; + nConsecutiveOldContextLines = 0; + } else { + if (!maxErrors || lines[toPos] == null) { + return null; + } + patchedLines[patchedLinesLength] = lines[toPos]; + return applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1); + } + } + if (operation === '+') { + if (!lastContextLineMatched) { + return null; + } + patchedLines[patchedLinesLength] = content; + patchedLinesLength++; + nConsecutiveOldContextLines = 0; + nextContextLineMustMatch = true; + } + if (operation === ' ') { + nConsecutiveOldContextLines++; + patchedLines[patchedLinesLength] = lines[toPos]; + if (compareLine(toPos + 1, lines[toPos], operation, content)) { + patchedLinesLength++; + lastContextLineMatched = true; + nextContextLineMustMatch = false; + toPos++; + } else { + if (nextContextLineMustMatch || !maxErrors) { + return null; + } - for (; localOffset !== undefined; localOffset = iterator()) { - if (hunkFits(hunk, toPos + localOffset)) { - hunk.offset = offset += localOffset; - break; + // Consider 3 possibilities in sequence: + // 1. lines contains a *substitution* not included in the patch context, or + // 2. lines contains an *insertion* not included in the patch context, or + // 3. lines contains a *deletion* not included in the patch context + // The first two options are of course only possible if the line from lines is non-null - + // i.e. only option 3 is possible if we've overrun the end of the old file. + return lines[toPos] && (applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength + 1) || applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1)) || applyHunk(hunkLines, toPos, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength); + } } } - if (localOffset === undefined) { - return false; - } // Set lower text limit to end of the current hunk, so next ones don't try - // to fit over already patched text - - - minLine = hunk.offset + hunk.oldStart + hunk.oldLines; - } // Apply patch hunks - - - var diffOffset = 0; + // Before returning, trim any unmodified context lines off the end of patchedLines and reduce + // toPos (and thus oldLineLastI) accordingly. This allows later hunks to be applied to a region + // that starts in this hunk's trailing context. + patchedLinesLength -= nConsecutiveOldContextLines; + toPos -= nConsecutiveOldContextLines; + patchedLines.length = patchedLinesLength; + return { + patchedLines: patchedLines, + oldLineLastI: toPos - 1 + }; + } + var resultLines = []; + // Search best fit offsets for each hunk based on the previous ones + var prevHunkOffset = 0; for (var _i = 0; _i < hunks.length; _i++) { - var _hunk = hunks[_i], - _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; - - diffOffset += _hunk.newLines - _hunk.oldLines; - - for (var j = 0; j < _hunk.lines.length; j++) { - var line = _hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line, - delimiter = _hunk.linedelimiters && _hunk.linedelimiters[j] || '\n'; - - if (operation === ' ') { - _toPos++; - } else if (operation === '-') { - lines.splice(_toPos, 1); - delimiters.splice(_toPos, 1); - /* istanbul ignore else */ - } else if (operation === '+') { - lines.splice(_toPos, 0, content); - delimiters.splice(_toPos, 0, delimiter); - _toPos++; - } else if (operation === '\\') { - var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; - - if (previousOperation === '+') { - removeEOFNL = true; - } else if (previousOperation === '-') { - addEOFNL = true; + var hunk = hunks[_i]; + var hunkResult = + /*istanbul ignore start*/ + void 0 + /*istanbul ignore end*/ + ; + var maxLine = lines.length - hunk.oldLines + fuzzFactor; + var toPos = + /*istanbul ignore start*/ + void 0 + /*istanbul ignore end*/ + ; + for (var maxErrors = 0; maxErrors <= fuzzFactor; maxErrors++) { + toPos = hunk.oldStart + prevHunkOffset - 1; + var iterator = + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _distanceIterator + /*istanbul ignore end*/ + [ + /*istanbul ignore start*/ + "default" + /*istanbul ignore end*/ + ])(toPos, minLine, maxLine); + for (; toPos !== undefined; toPos = iterator()) { + hunkResult = applyHunk(hunk.lines, toPos, maxErrors); + if (hunkResult) { + break; } } + if (hunkResult) { + break; + } + } + if (!hunkResult) { + return false; } - } // Handle EOFNL insertion/removal + // Copy everything from the end of where we applied the last hunk to the start of this hunk + for (var _i2 = minLine; _i2 < toPos; _i2++) { + resultLines.push(lines[_i2]); + } - if (removeEOFNL) { - while (!lines[lines.length - 1]) { - lines.pop(); - delimiters.pop(); + // Add the lines produced by applying the hunk: + for (var _i3 = 0; _i3 < hunkResult.patchedLines.length; _i3++) { + var _line = hunkResult.patchedLines[_i3]; + resultLines.push(_line); } - } else if (addEOFNL) { - lines.push(''); - delimiters.push('\n'); - } - for (var _k = 0; _k < lines.length - 1; _k++) { - lines[_k] = lines[_k] + delimiters[_k]; - } + // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text + minLine = hunkResult.oldLineLastI + 1; - return lines.join(''); -} // Wrapper that supports multiple file patches via callbacks. + // Note the offset between where the patch said the hunk should've applied and where we + // applied it, so we can adjust future hunks accordingly: + prevHunkOffset = toPos + 1 - hunk.oldStart; + } + // Copy over the rest of the lines from the old text + for (var _i4 = minLine; _i4 < lines.length; _i4++) { + resultLines.push(lines[_i4]); + } + return resultLines.join('\n'); +} +// Wrapper that supports multiple file patches via callbacks. function applyPatches(uniDiff, options) { if (typeof uniDiff === 'string') { uniDiff = /*istanbul ignore start*/ (0, /*istanbul ignore end*/ - /*istanbul ignore start*/ _parse /*istanbul ignore end*/ @@ -207,32 +369,25 @@ function applyPatches(uniDiff, options) { /*istanbul ignore end*/ (uniDiff); } - var currentIndex = 0; - function processIndex() { var index = uniDiff[currentIndex++]; - if (!index) { return options.complete(); } - options.loadFile(index, function (err, data) { if (err) { return options.complete(err); } - var updatedContent = applyPatch(data, index, options); options.patched(index, updatedContent, function (err) { if (err) { return options.complete(err); } - processIndex(); }); }); } - processIndex(); } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9hcHBseS5qcyJdLCJuYW1lcyI6WyJhcHBseVBhdGNoIiwic291cmNlIiwidW5pRGlmZiIsIm9wdGlvbnMiLCJwYXJzZVBhdGNoIiwiQXJyYXkiLCJpc0FycmF5IiwibGVuZ3RoIiwiRXJyb3IiLCJsaW5lcyIsInNwbGl0IiwiZGVsaW1pdGVycyIsIm1hdGNoIiwiaHVua3MiLCJjb21wYXJlTGluZSIsImxpbmVOdW1iZXIiLCJsaW5lIiwib3BlcmF0aW9uIiwicGF0Y2hDb250ZW50IiwiZXJyb3JDb3VudCIsImZ1enpGYWN0b3IiLCJtaW5MaW5lIiwib2Zmc2V0IiwicmVtb3ZlRU9GTkwiLCJhZGRFT0ZOTCIsImh1bmtGaXRzIiwiaHVuayIsInRvUG9zIiwiaiIsImNvbnRlbnQiLCJzdWJzdHIiLCJpIiwibWF4TGluZSIsIm9sZExpbmVzIiwibG9jYWxPZmZzZXQiLCJvbGRTdGFydCIsIml0ZXJhdG9yIiwiZGlzdGFuY2VJdGVyYXRvciIsInVuZGVmaW5lZCIsImRpZmZPZmZzZXQiLCJuZXdMaW5lcyIsImRlbGltaXRlciIsImxpbmVkZWxpbWl0ZXJzIiwic3BsaWNlIiwicHJldmlvdXNPcGVyYXRpb24iLCJwb3AiLCJwdXNoIiwiX2siLCJqb2luIiwiYXBwbHlQYXRjaGVzIiwiY3VycmVudEluZGV4IiwicHJvY2Vzc0luZGV4IiwiaW5kZXgiLCJjb21wbGV0ZSIsImxvYWRGaWxlIiwiZXJyIiwiZGF0YSIsInVwZGF0ZWRDb250ZW50IiwicGF0Y2hlZCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7O0FBQ0E7QUFBQTtBQUFBO0FBQUE7QUFBQTs7Ozs7QUFFTyxTQUFTQSxVQUFULENBQW9CQyxNQUFwQixFQUE0QkMsT0FBNUIsRUFBbUQ7QUFBQTtBQUFBO0FBQUE7QUFBZEMsRUFBQUEsT0FBYyx1RUFBSixFQUFJOztBQUN4RCxNQUFJLE9BQU9ELE9BQVAsS0FBbUIsUUFBdkIsRUFBaUM7QUFDL0JBLElBQUFBLE9BQU87QUFBRztBQUFBO0FBQUE7O0FBQUFFO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUE7QUFBQSxLQUFXRixPQUFYLENBQVY7QUFDRDs7QUFFRCxNQUFJRyxLQUFLLENBQUNDLE9BQU4sQ0FBY0osT0FBZCxDQUFKLEVBQTRCO0FBQzFCLFFBQUlBLE9BQU8sQ0FBQ0ssTUFBUixHQUFpQixDQUFyQixFQUF3QjtBQUN0QixZQUFNLElBQUlDLEtBQUosQ0FBVSw0Q0FBVixDQUFOO0FBQ0Q7O0FBRUROLElBQUFBLE9BQU8sR0FBR0EsT0FBTyxDQUFDLENBQUQsQ0FBakI7QUFDRCxHQVh1RCxDQWF4RDs7O0FBQ0EsTUFBSU8sS0FBSyxHQUFHUixNQUFNLENBQUNTLEtBQVAsQ0FBYSxxQkFBYixDQUFaO0FBQUEsTUFDSUMsVUFBVSxHQUFHVixNQUFNLENBQUNXLEtBQVAsQ0FBYSxzQkFBYixLQUF3QyxFQUR6RDtBQUFBLE1BRUlDLEtBQUssR0FBR1gsT0FBTyxDQUFDVyxLQUZwQjtBQUFBLE1BSUlDLFdBQVcsR0FBR1gsT0FBTyxDQUFDVyxXQUFSLElBQXdCLFVBQUNDLFVBQUQsRUFBYUMsSUFBYixFQUFtQkMsU0FBbkIsRUFBOEJDLFlBQTlCO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBK0NGLE1BQUFBLElBQUksS0FBS0U7QUFBeEQ7QUFBQSxHQUoxQztBQUFBLE1BS0lDLFVBQVUsR0FBRyxDQUxqQjtBQUFBLE1BTUlDLFVBQVUsR0FBR2pCLE9BQU8sQ0FBQ2lCLFVBQVIsSUFBc0IsQ0FOdkM7QUFBQSxNQU9JQyxPQUFPLEdBQUcsQ0FQZDtBQUFBLE1BUUlDLE1BQU0sR0FBRyxDQVJiO0FBQUEsTUFVSUMsV0FWSjtBQUFBLE1BV0lDLFFBWEo7QUFhQTs7Ozs7QUFHQSxXQUFTQyxRQUFULENBQWtCQyxJQUFsQixFQUF3QkMsS0FBeEIsRUFBK0I7QUFDN0IsU0FBSyxJQUFJQyxDQUFDLEdBQUcsQ0FBYixFQUFnQkEsQ0FBQyxHQUFHRixJQUFJLENBQUNqQixLQUFMLENBQVdGLE1BQS9CLEVBQXVDcUIsQ0FBQyxFQUF4QyxFQUE0QztBQUMxQyxVQUFJWixJQUFJLEdBQUdVLElBQUksQ0FBQ2pCLEtBQUwsQ0FBV21CLENBQVgsQ0FBWDtBQUFBLFVBQ0lYLFNBQVMsR0FBSUQsSUFBSSxDQUFDVCxNQUFMLEdBQWMsQ0FBZCxHQUFrQlMsSUFBSSxDQUFDLENBQUQsQ0FBdEIsR0FBNEIsR0FEN0M7QUFBQSxVQUVJYSxPQUFPLEdBQUliLElBQUksQ0FBQ1QsTUFBTCxHQUFjLENBQWQsR0FBa0JTLElBQUksQ0FBQ2MsTUFBTCxDQUFZLENBQVosQ0FBbEIsR0FBbUNkLElBRmxEOztBQUlBLFVBQUlDLFNBQVMsS0FBSyxHQUFkLElBQXFCQSxTQUFTLEtBQUssR0FBdkMsRUFBNEM7QUFDMUM7QUFDQSxZQUFJLENBQUNILFdBQVcsQ0FBQ2EsS0FBSyxHQUFHLENBQVQsRUFBWWxCLEtBQUssQ0FBQ2tCLEtBQUQsQ0FBakIsRUFBMEJWLFNBQTFCLEVBQXFDWSxPQUFyQyxDQUFoQixFQUErRDtBQUM3RFYsVUFBQUEsVUFBVTs7QUFFVixjQUFJQSxVQUFVLEdBQUdDLFVBQWpCLEVBQTZCO0FBQzNCLG1CQUFPLEtBQVA7QUFDRDtBQUNGOztBQUNETyxRQUFBQSxLQUFLO0FBQ047QUFDRjs7QUFFRCxXQUFPLElBQVA7QUFDRCxHQWxEdUQsQ0FvRHhEOzs7QUFDQSxPQUFLLElBQUlJLENBQUMsR0FBRyxDQUFiLEVBQWdCQSxDQUFDLEdBQUdsQixLQUFLLENBQUNOLE1BQTFCLEVBQWtDd0IsQ0FBQyxFQUFuQyxFQUF1QztBQUNyQyxRQUFJTCxJQUFJLEdBQUdiLEtBQUssQ0FBQ2tCLENBQUQsQ0FBaEI7QUFBQSxRQUNJQyxPQUFPLEdBQUd2QixLQUFLLENBQUNGLE1BQU4sR0FBZW1CLElBQUksQ0FBQ08sUUFEbEM7QUFBQSxRQUVJQyxXQUFXLEdBQUcsQ0FGbEI7QUFBQSxRQUdJUCxLQUFLLEdBQUdMLE1BQU0sR0FBR0ksSUFBSSxDQUFDUyxRQUFkLEdBQXlCLENBSHJDO0FBS0EsUUFBSUMsUUFBUTtBQUFHO0FBQUE7QUFBQTs7QUFBQUM7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUE7QUFBQUEsT0FBaUJWLEtBQWpCLEVBQXdCTixPQUF4QixFQUFpQ1csT0FBakMsQ0FBZjs7QUFFQSxXQUFPRSxXQUFXLEtBQUtJLFNBQXZCLEVBQWtDSixXQUFXLEdBQUdFLFFBQVEsRUFBeEQsRUFBNEQ7QUFDMUQsVUFBSVgsUUFBUSxDQUFDQyxJQUFELEVBQU9DLEtBQUssR0FBR08sV0FBZixDQUFaLEVBQXlDO0FBQ3ZDUixRQUFBQSxJQUFJLENBQUNKLE1BQUwsR0FBY0EsTUFBTSxJQUFJWSxXQUF4QjtBQUNBO0FBQ0Q7QUFDRjs7QUFFRCxRQUFJQSxXQUFXLEtBQUtJLFNBQXBCLEVBQStCO0FBQzdCLGFBQU8sS0FBUDtBQUNELEtBakJvQyxDQW1CckM7QUFDQTs7O0FBQ0FqQixJQUFBQSxPQUFPLEdBQUdLLElBQUksQ0FBQ0osTUFBTCxHQUFjSSxJQUFJLENBQUNTLFFBQW5CLEdBQThCVCxJQUFJLENBQUNPLFFBQTdDO0FBQ0QsR0EzRXVELENBNkV4RDs7O0FBQ0EsTUFBSU0sVUFBVSxHQUFHLENBQWpCOztBQUNBLE9BQUssSUFBSVIsRUFBQyxHQUFHLENBQWIsRUFBZ0JBLEVBQUMsR0FBR2xCLEtBQUssQ0FBQ04sTUFBMUIsRUFBa0N3QixFQUFDLEVBQW5DLEVBQXVDO0FBQ3JDLFFBQUlMLEtBQUksR0FBR2IsS0FBSyxDQUFDa0IsRUFBRCxDQUFoQjtBQUFBLFFBQ0lKLE1BQUssR0FBR0QsS0FBSSxDQUFDUyxRQUFMLEdBQWdCVCxLQUFJLENBQUNKLE1BQXJCLEdBQThCaUIsVUFBOUIsR0FBMkMsQ0FEdkQ7O0FBRUFBLElBQUFBLFVBQVUsSUFBSWIsS0FBSSxDQUFDYyxRQUFMLEdBQWdCZCxLQUFJLENBQUNPLFFBQW5DOztBQUVBLFNBQUssSUFBSUwsQ0FBQyxHQUFHLENBQWIsRUFBZ0JBLENBQUMsR0FBR0YsS0FBSSxDQUFDakIsS0FBTCxDQUFXRixNQUEvQixFQUF1Q3FCLENBQUMsRUFBeEMsRUFBNEM7QUFDMUMsVUFBSVosSUFBSSxHQUFHVSxLQUFJLENBQUNqQixLQUFMLENBQVdtQixDQUFYLENBQVg7QUFBQSxVQUNJWCxTQUFTLEdBQUlELElBQUksQ0FBQ1QsTUFBTCxHQUFjLENBQWQsR0FBa0JTLElBQUksQ0FBQyxDQUFELENBQXRCLEdBQTRCLEdBRDdDO0FBQUEsVUFFSWEsT0FBTyxHQUFJYixJQUFJLENBQUNULE1BQUwsR0FBYyxDQUFkLEdBQWtCUyxJQUFJLENBQUNjLE1BQUwsQ0FBWSxDQUFaLENBQWxCLEdBQW1DZCxJQUZsRDtBQUFBLFVBR0l5QixTQUFTLEdBQUdmLEtBQUksQ0FBQ2dCLGNBQUwsSUFBdUJoQixLQUFJLENBQUNnQixjQUFMLENBQW9CZCxDQUFwQixDQUF2QixJQUFpRCxJQUhqRTs7QUFLQSxVQUFJWCxTQUFTLEtBQUssR0FBbEIsRUFBdUI7QUFDckJVLFFBQUFBLE1BQUs7QUFDTixPQUZELE1BRU8sSUFBSVYsU0FBUyxLQUFLLEdBQWxCLEVBQXVCO0FBQzVCUixRQUFBQSxLQUFLLENBQUNrQyxNQUFOLENBQWFoQixNQUFiLEVBQW9CLENBQXBCO0FBQ0FoQixRQUFBQSxVQUFVLENBQUNnQyxNQUFYLENBQWtCaEIsTUFBbEIsRUFBeUIsQ0FBekI7QUFDRjtBQUNDLE9BSk0sTUFJQSxJQUFJVixTQUFTLEtBQUssR0FBbEIsRUFBdUI7QUFDNUJSLFFBQUFBLEtBQUssQ0FBQ2tDLE1BQU4sQ0FBYWhCLE1BQWIsRUFBb0IsQ0FBcEIsRUFBdUJFLE9BQXZCO0FBQ0FsQixRQUFBQSxVQUFVLENBQUNnQyxNQUFYLENBQWtCaEIsTUFBbEIsRUFBeUIsQ0FBekIsRUFBNEJjLFNBQTVCO0FBQ0FkLFFBQUFBLE1BQUs7QUFDTixPQUpNLE1BSUEsSUFBSVYsU0FBUyxLQUFLLElBQWxCLEVBQXdCO0FBQzdCLFlBQUkyQixpQkFBaUIsR0FBR2xCLEtBQUksQ0FBQ2pCLEtBQUwsQ0FBV21CLENBQUMsR0FBRyxDQUFmLElBQW9CRixLQUFJLENBQUNqQixLQUFMLENBQVdtQixDQUFDLEdBQUcsQ0FBZixFQUFrQixDQUFsQixDQUFwQixHQUEyQyxJQUFuRTs7QUFDQSxZQUFJZ0IsaUJBQWlCLEtBQUssR0FBMUIsRUFBK0I7QUFDN0JyQixVQUFBQSxXQUFXLEdBQUcsSUFBZDtBQUNELFNBRkQsTUFFTyxJQUFJcUIsaUJBQWlCLEtBQUssR0FBMUIsRUFBK0I7QUFDcENwQixVQUFBQSxRQUFRLEdBQUcsSUFBWDtBQUNEO0FBQ0Y7QUFDRjtBQUNGLEdBN0d1RCxDQStHeEQ7OztBQUNBLE1BQUlELFdBQUosRUFBaUI7QUFDZixXQUFPLENBQUNkLEtBQUssQ0FBQ0EsS0FBSyxDQUFDRixNQUFOLEdBQWUsQ0FBaEIsQ0FBYixFQUFpQztBQUMvQkUsTUFBQUEsS0FBSyxDQUFDb0MsR0FBTjtBQUNBbEMsTUFBQUEsVUFBVSxDQUFDa0MsR0FBWDtBQUNEO0FBQ0YsR0FMRCxNQUtPLElBQUlyQixRQUFKLEVBQWM7QUFDbkJmLElBQUFBLEtBQUssQ0FBQ3FDLElBQU4sQ0FBVyxFQUFYO0FBQ0FuQyxJQUFBQSxVQUFVLENBQUNtQyxJQUFYLENBQWdCLElBQWhCO0FBQ0Q7O0FBQ0QsT0FBSyxJQUFJQyxFQUFFLEdBQUcsQ0FBZCxFQUFpQkEsRUFBRSxHQUFHdEMsS0FBSyxDQUFDRixNQUFOLEdBQWUsQ0FBckMsRUFBd0N3QyxFQUFFLEVBQTFDLEVBQThDO0FBQzVDdEMsSUFBQUEsS0FBSyxDQUFDc0MsRUFBRCxDQUFMLEdBQVl0QyxLQUFLLENBQUNzQyxFQUFELENBQUwsR0FBWXBDLFVBQVUsQ0FBQ29DLEVBQUQsQ0FBbEM7QUFDRDs7QUFDRCxTQUFPdEMsS0FBSyxDQUFDdUMsSUFBTixDQUFXLEVBQVgsQ0FBUDtBQUNELEMsQ0FFRDs7O0FBQ08sU0FBU0MsWUFBVCxDQUFzQi9DLE9BQXRCLEVBQStCQyxPQUEvQixFQUF3QztBQUM3QyxNQUFJLE9BQU9ELE9BQVAsS0FBbUIsUUFBdkIsRUFBaUM7QUFDL0JBLElBQUFBLE9BQU87QUFBRztBQUFBO0FBQUE7O0FBQUFFO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUFBO0FBQUE7QUFBQSxLQUFXRixPQUFYLENBQVY7QUFDRDs7QUFFRCxNQUFJZ0QsWUFBWSxHQUFHLENBQW5COztBQUNBLFdBQVNDLFlBQVQsR0FBd0I7QUFDdEIsUUFBSUMsS0FBSyxHQUFHbEQsT0FBTyxDQUFDZ0QsWUFBWSxFQUFiLENBQW5COztBQUNBLFFBQUksQ0FBQ0UsS0FBTCxFQUFZO0FBQ1YsYUFBT2pELE9BQU8sQ0FBQ2tELFFBQVIsRUFBUDtBQUNEOztBQUVEbEQsSUFBQUEsT0FBTyxDQUFDbUQsUUFBUixDQUFpQkYsS0FBakIsRUFBd0IsVUFBU0csR0FBVCxFQUFjQyxJQUFkLEVBQW9CO0FBQzFDLFVBQUlELEdBQUosRUFBUztBQUNQLGVBQU9wRCxPQUFPLENBQUNrRCxRQUFSLENBQWlCRSxHQUFqQixDQUFQO0FBQ0Q7O0FBRUQsVUFBSUUsY0FBYyxHQUFHekQsVUFBVSxDQUFDd0QsSUFBRCxFQUFPSixLQUFQLEVBQWNqRCxPQUFkLENBQS9CO0FBQ0FBLE1BQUFBLE9BQU8sQ0FBQ3VELE9BQVIsQ0FBZ0JOLEtBQWhCLEVBQXVCSyxjQUF2QixFQUF1QyxVQUFTRixHQUFULEVBQWM7QUFDbkQsWUFBSUEsR0FBSixFQUFTO0FBQ1AsaUJBQU9wRCxPQUFPLENBQUNrRCxRQUFSLENBQWlCRSxHQUFqQixDQUFQO0FBQ0Q7O0FBRURKLFFBQUFBLFlBQVk7QUFDYixPQU5EO0FBT0QsS0FiRDtBQWNEOztBQUNEQSxFQUFBQSxZQUFZO0FBQ2IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge3BhcnNlUGF0Y2h9IGZyb20gJy4vcGFyc2UnO1xuaW1wb3J0IGRpc3RhbmNlSXRlcmF0b3IgZnJvbSAnLi4vdXRpbC9kaXN0YW5jZS1pdGVyYXRvcic7XG5cbmV4cG9ydCBmdW5jdGlvbiBhcHBseVBhdGNoKHNvdXJjZSwgdW5pRGlmZiwgb3B0aW9ucyA9IHt9KSB7XG4gIGlmICh0eXBlb2YgdW5pRGlmZiA9PT0gJ3N0cmluZycpIHtcbiAgICB1bmlEaWZmID0gcGFyc2VQYXRjaCh1bmlEaWZmKTtcbiAgfVxuXG4gIGlmIChBcnJheS5pc0FycmF5KHVuaURpZmYpKSB7XG4gICAgaWYgKHVuaURpZmYubGVuZ3RoID4gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdhcHBseVBhdGNoIG9ubHkgd29ya3Mgd2l0aCBhIHNpbmdsZSBpbnB1dC4nKTtcbiAgICB9XG5cbiAgICB1bmlEaWZmID0gdW5pRGlmZlswXTtcbiAgfVxuXG4gIC8vIEFwcGx5IHRoZSBkaWZmIHRvIHRoZSBpbnB1dFxuICBsZXQgbGluZXMgPSBzb3VyY2Uuc3BsaXQoL1xcclxcbnxbXFxuXFx2XFxmXFxyXFx4ODVdLyksXG4gICAgICBkZWxpbWl0ZXJzID0gc291cmNlLm1hdGNoKC9cXHJcXG58W1xcblxcdlxcZlxcclxceDg1XS9nKSB8fCBbXSxcbiAgICAgIGh1bmtzID0gdW5pRGlmZi5odW5rcyxcblxuICAgICAgY29tcGFyZUxpbmUgPSBvcHRpb25zLmNvbXBhcmVMaW5lIHx8ICgobGluZU51bWJlciwgbGluZSwgb3BlcmF0aW9uLCBwYXRjaENvbnRlbnQpID0+IGxpbmUgPT09IHBhdGNoQ29udGVudCksXG4gICAgICBlcnJvckNvdW50ID0gMCxcbiAgICAgIGZ1enpGYWN0b3IgPSBvcHRpb25zLmZ1enpGYWN0b3IgfHwgMCxcbiAgICAgIG1pbkxpbmUgPSAwLFxuICAgICAgb2Zmc2V0ID0gMCxcblxuICAgICAgcmVtb3ZlRU9GTkwsXG4gICAgICBhZGRFT0ZOTDtcblxuICAvKipcbiAgICogQ2hlY2tzIGlmIHRoZSBodW5rIGV4YWN0bHkgZml0cyBvbiB0aGUgcHJvdmlkZWQgbG9jYXRpb25cbiAgICovXG4gIGZ1bmN0aW9uIGh1bmtGaXRzKGh1bmssIHRvUG9zKSB7XG4gICAgZm9yIChsZXQgaiA9IDA7IGogPCBodW5rLmxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICBsZXQgbGluZSA9IGh1bmsubGluZXNbal0sXG4gICAgICAgICAgb3BlcmF0aW9uID0gKGxpbmUubGVuZ3RoID4gMCA/IGxpbmVbMF0gOiAnICcpLFxuICAgICAgICAgIGNvbnRlbnQgPSAobGluZS5sZW5ndGggPiAwID8gbGluZS5zdWJzdHIoMSkgOiBsaW5lKTtcblxuICAgICAgaWYgKG9wZXJhdGlvbiA9PT0gJyAnIHx8IG9wZXJhdGlvbiA9PT0gJy0nKSB7XG4gICAgICAgIC8vIENvbnRleHQgc2FuaXR5IGNoZWNrXG4gICAgICAgIGlmICghY29tcGFyZUxpbmUodG9Qb3MgKyAxLCBsaW5lc1t0b1Bvc10sIG9wZXJhdGlvbiwgY29udGVudCkpIHtcbiAgICAgICAgICBlcnJvckNvdW50Kys7XG5cbiAgICAgICAgICBpZiAoZXJyb3JDb3VudCA+IGZ1enpGYWN0b3IpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdG9Qb3MrKztcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8vIFNlYXJjaCBiZXN0IGZpdCBvZmZzZXRzIGZvciBlYWNoIGh1bmsgYmFzZWQgb24gdGhlIHByZXZpb3VzIG9uZXNcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBodW5rcy5sZW5ndGg7IGkrKykge1xuICAgIGxldCBodW5rID0gaHVua3NbaV0sXG4gICAgICAgIG1heExpbmUgPSBsaW5lcy5sZW5ndGggLSBodW5rLm9sZExpbmVzLFxuICAgICAgICBsb2NhbE9mZnNldCA9IDAsXG4gICAgICAgIHRvUG9zID0gb2Zmc2V0ICsgaHVuay5vbGRTdGFydCAtIDE7XG5cbiAgICBsZXQgaXRlcmF0b3IgPSBkaXN0YW5jZUl0ZXJhdG9yKHRvUG9zLCBtaW5MaW5lLCBtYXhMaW5lKTtcblxuICAgIGZvciAoOyBsb2NhbE9mZnNldCAhPT0gdW5kZWZpbmVkOyBsb2NhbE9mZnNldCA9IGl0ZXJhdG9yKCkpIHtcbiAgICAgIGlmIChodW5rRml0cyhodW5rLCB0b1BvcyArIGxvY2FsT2Zmc2V0KSkge1xuICAgICAgICBodW5rLm9mZnNldCA9IG9mZnNldCArPSBsb2NhbE9mZnNldDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKGxvY2FsT2Zmc2V0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBTZXQgbG93ZXIgdGV4dCBsaW1pdCB0byBlbmQgb2YgdGhlIGN1cnJlbnQgaHVuaywgc28gbmV4dCBvbmVzIGRvbid0IHRyeVxuICAgIC8vIHRvIGZpdCBvdmVyIGFscmVhZHkgcGF0Y2hlZCB0ZXh0XG4gICAgbWluTGluZSA9IGh1bmsub2Zmc2V0ICsgaHVuay5vbGRTdGFydCArIGh1bmsub2xkTGluZXM7XG4gIH1cblxuICAvLyBBcHBseSBwYXRjaCBodW5rc1xuICBsZXQgZGlmZk9mZnNldCA9IDA7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgaHVua3MubGVuZ3RoOyBpKyspIHtcbiAgICBsZXQgaHVuayA9IGh1bmtzW2ldLFxuICAgICAgICB0b1BvcyA9IGh1bmsub2xkU3RhcnQgKyBodW5rLm9mZnNldCArIGRpZmZPZmZzZXQgLSAxO1xuICAgIGRpZmZPZmZzZXQgKz0gaHVuay5uZXdMaW5lcyAtIGh1bmsub2xkTGluZXM7XG5cbiAgICBmb3IgKGxldCBqID0gMDsgaiA8IGh1bmsubGluZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgIGxldCBsaW5lID0gaHVuay5saW5lc1tqXSxcbiAgICAgICAgICBvcGVyYXRpb24gPSAobGluZS5sZW5ndGggPiAwID8gbGluZVswXSA6ICcgJyksXG4gICAgICAgICAgY29udGVudCA9IChsaW5lLmxlbmd0aCA+IDAgPyBsaW5lLnN1YnN0cigxKSA6IGxpbmUpLFxuICAgICAgICAgIGRlbGltaXRlciA9IGh1bmsubGluZWRlbGltaXRlcnMgJiYgaHVuay5saW5lZGVsaW1pdGVyc1tqXSB8fCAnXFxuJztcblxuICAgICAgaWYgKG9wZXJhdGlvbiA9PT0gJyAnKSB7XG4gICAgICAgIHRvUG9zKys7XG4gICAgICB9IGVsc2UgaWYgKG9wZXJhdGlvbiA9PT0gJy0nKSB7XG4gICAgICAgIGxpbmVzLnNwbGljZSh0b1BvcywgMSk7XG4gICAgICAgIGRlbGltaXRlcnMuc3BsaWNlKHRvUG9zLCAxKTtcbiAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBlbHNlICovXG4gICAgICB9IGVsc2UgaWYgKG9wZXJhdGlvbiA9PT0gJysnKSB7XG4gICAgICAgIGxpbmVzLnNwbGljZSh0b1BvcywgMCwgY29udGVudCk7XG4gICAgICAgIGRlbGltaXRlcnMuc3BsaWNlKHRvUG9zLCAwLCBkZWxpbWl0ZXIpO1xuICAgICAgICB0b1BvcysrO1xuICAgICAgfSBlbHNlIGlmIChvcGVyYXRpb24gPT09ICdcXFxcJykge1xuICAgICAgICBsZXQgcHJldmlvdXNPcGVyYXRpb24gPSBodW5rLmxpbmVzW2ogLSAxXSA/IGh1bmsubGluZXNbaiAtIDFdWzBdIDogbnVsbDtcbiAgICAgICAgaWYgKHByZXZpb3VzT3BlcmF0aW9uID09PSAnKycpIHtcbiAgICAgICAgICByZW1vdmVFT0ZOTCA9IHRydWU7XG4gICAgICAgIH0gZWxzZSBpZiAocHJldmlvdXNPcGVyYXRpb24gPT09ICctJykge1xuICAgICAgICAgIGFkZEVPRk5MID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIEhhbmRsZSBFT0ZOTCBpbnNlcnRpb24vcmVtb3ZhbFxuICBpZiAocmVtb3ZlRU9GTkwpIHtcbiAgICB3aGlsZSAoIWxpbmVzW2xpbmVzLmxlbmd0aCAtIDFdKSB7XG4gICAgICBsaW5lcy5wb3AoKTtcbiAgICAgIGRlbGltaXRlcnMucG9wKCk7XG4gICAgfVxuICB9IGVsc2UgaWYgKGFkZEVPRk5MKSB7XG4gICAgbGluZXMucHVzaCgnJyk7XG4gICAgZGVsaW1pdGVycy5wdXNoKCdcXG4nKTtcbiAgfVxuICBmb3IgKGxldCBfayA9IDA7IF9rIDwgbGluZXMubGVuZ3RoIC0gMTsgX2srKykge1xuICAgIGxpbmVzW19rXSA9IGxpbmVzW19rXSArIGRlbGltaXRlcnNbX2tdO1xuICB9XG4gIHJldHVybiBsaW5lcy5qb2luKCcnKTtcbn1cblxuLy8gV3JhcHBlciB0aGF0IHN1cHBvcnRzIG11bHRpcGxlIGZpbGUgcGF0Y2hlcyB2aWEgY2FsbGJhY2tzLlxuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5UGF0Y2hlcyh1bmlEaWZmLCBvcHRpb25zKSB7XG4gIGlmICh0eXBlb2YgdW5pRGlmZiA9PT0gJ3N0cmluZycpIHtcbiAgICB1bmlEaWZmID0gcGFyc2VQYXRjaCh1bmlEaWZmKTtcbiAgfVxuXG4gIGxldCBjdXJyZW50SW5kZXggPSAwO1xuICBmdW5jdGlvbiBwcm9jZXNzSW5kZXgoKSB7XG4gICAgbGV0IGluZGV4ID0gdW5pRGlmZltjdXJyZW50SW5kZXgrK107XG4gICAgaWYgKCFpbmRleCkge1xuICAgICAgcmV0dXJuIG9wdGlvbnMuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICBvcHRpb25zLmxvYWRGaWxlKGluZGV4LCBmdW5jdGlvbihlcnIsIGRhdGEpIHtcbiAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgcmV0dXJuIG9wdGlvbnMuY29tcGxldGUoZXJyKTtcbiAgICAgIH1cblxuICAgICAgbGV0IHVwZGF0ZWRDb250ZW50ID0gYXBwbHlQYXRjaChkYXRhLCBpbmRleCwgb3B0aW9ucyk7XG4gICAgICBvcHRpb25zLnBhdGNoZWQoaW5kZXgsIHVwZGF0ZWRDb250ZW50LCBmdW5jdGlvbihlcnIpIHtcbiAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgIHJldHVybiBvcHRpb25zLmNvbXBsZXRlKGVycik7XG4gICAgICAgIH1cblxuICAgICAgICBwcm9jZXNzSW5kZXgoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG4gIHByb2Nlc3NJbmRleCgpO1xufVxuIl19 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfc3RyaW5nIiwicmVxdWlyZSIsIl9saW5lRW5kaW5ncyIsIl9wYXJzZSIsIl9kaXN0YW5jZUl0ZXJhdG9yIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIm9iaiIsIl9fZXNNb2R1bGUiLCJhcHBseVBhdGNoIiwic291cmNlIiwidW5pRGlmZiIsIm9wdGlvbnMiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJ1bmRlZmluZWQiLCJwYXJzZVBhdGNoIiwiQXJyYXkiLCJpc0FycmF5IiwiRXJyb3IiLCJhdXRvQ29udmVydExpbmVFbmRpbmdzIiwiaGFzT25seVdpbkxpbmVFbmRpbmdzIiwiaXNVbml4IiwidW5peFRvV2luIiwiaGFzT25seVVuaXhMaW5lRW5kaW5ncyIsImlzV2luIiwid2luVG9Vbml4IiwibGluZXMiLCJzcGxpdCIsImh1bmtzIiwiY29tcGFyZUxpbmUiLCJsaW5lTnVtYmVyIiwibGluZSIsIm9wZXJhdGlvbiIsInBhdGNoQ29udGVudCIsImZ1enpGYWN0b3IiLCJtaW5MaW5lIiwiTnVtYmVyIiwiaXNJbnRlZ2VyIiwicHJldkxpbmUiLCJyZW1vdmVFT0ZOTCIsImFkZEVPRk5MIiwiaSIsInBvcCIsInB1c2giLCJhcHBseUh1bmsiLCJodW5rTGluZXMiLCJ0b1BvcyIsIm1heEVycm9ycyIsImh1bmtMaW5lc0kiLCJsYXN0Q29udGV4dExpbmVNYXRjaGVkIiwicGF0Y2hlZExpbmVzIiwicGF0Y2hlZExpbmVzTGVuZ3RoIiwibkNvbnNlY3V0aXZlT2xkQ29udGV4dExpbmVzIiwibmV4dENvbnRleHRMaW5lTXVzdE1hdGNoIiwiaHVua0xpbmUiLCJjb250ZW50Iiwic3Vic3RyIiwib2xkTGluZUxhc3RJIiwicmVzdWx0TGluZXMiLCJwcmV2SHVua09mZnNldCIsImh1bmsiLCJodW5rUmVzdWx0IiwibWF4TGluZSIsIm9sZExpbmVzIiwib2xkU3RhcnQiLCJpdGVyYXRvciIsImRpc3RhbmNlSXRlcmF0b3IiLCJqb2luIiwiYXBwbHlQYXRjaGVzIiwiY3VycmVudEluZGV4IiwicHJvY2Vzc0luZGV4IiwiaW5kZXgiLCJjb21wbGV0ZSIsImxvYWRGaWxlIiwiZXJyIiwiZGF0YSIsInVwZGF0ZWRDb250ZW50IiwicGF0Y2hlZCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9hcHBseS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge2hhc09ubHlXaW5MaW5lRW5kaW5ncywgaGFzT25seVVuaXhMaW5lRW5kaW5nc30gZnJvbSAnLi4vdXRpbC9zdHJpbmcnO1xuaW1wb3J0IHtpc1dpbiwgaXNVbml4LCB1bml4VG9XaW4sIHdpblRvVW5peH0gZnJvbSAnLi9saW5lLWVuZGluZ3MnO1xuaW1wb3J0IHtwYXJzZVBhdGNofSBmcm9tICcuL3BhcnNlJztcbmltcG9ydCBkaXN0YW5jZUl0ZXJhdG9yIGZyb20gJy4uL3V0aWwvZGlzdGFuY2UtaXRlcmF0b3InO1xuXG5leHBvcnQgZnVuY3Rpb24gYXBwbHlQYXRjaChzb3VyY2UsIHVuaURpZmYsIG9wdGlvbnMgPSB7fSkge1xuICBpZiAodHlwZW9mIHVuaURpZmYgPT09ICdzdHJpbmcnKSB7XG4gICAgdW5pRGlmZiA9IHBhcnNlUGF0Y2godW5pRGlmZik7XG4gIH1cblxuICBpZiAoQXJyYXkuaXNBcnJheSh1bmlEaWZmKSkge1xuICAgIGlmICh1bmlEaWZmLmxlbmd0aCA+IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignYXBwbHlQYXRjaCBvbmx5IHdvcmtzIHdpdGggYSBzaW5nbGUgaW5wdXQuJyk7XG4gICAgfVxuXG4gICAgdW5pRGlmZiA9IHVuaURpZmZbMF07XG4gIH1cblxuICBpZiAob3B0aW9ucy5hdXRvQ29udmVydExpbmVFbmRpbmdzIHx8IG9wdGlvbnMuYXV0b0NvbnZlcnRMaW5lRW5kaW5ncyA9PSBudWxsKSB7XG4gICAgaWYgKGhhc09ubHlXaW5MaW5lRW5kaW5ncyhzb3VyY2UpICYmIGlzVW5peCh1bmlEaWZmKSkge1xuICAgICAgdW5pRGlmZiA9IHVuaXhUb1dpbih1bmlEaWZmKTtcbiAgICB9IGVsc2UgaWYgKGhhc09ubHlVbml4TGluZUVuZGluZ3Moc291cmNlKSAmJiBpc1dpbih1bmlEaWZmKSkge1xuICAgICAgdW5pRGlmZiA9IHdpblRvVW5peCh1bmlEaWZmKTtcbiAgICB9XG4gIH1cblxuICAvLyBBcHBseSB0aGUgZGlmZiB0byB0aGUgaW5wdXRcbiAgbGV0IGxpbmVzID0gc291cmNlLnNwbGl0KCdcXG4nKSxcbiAgICAgIGh1bmtzID0gdW5pRGlmZi5odW5rcyxcblxuICAgICAgY29tcGFyZUxpbmUgPSBvcHRpb25zLmNvbXBhcmVMaW5lIHx8ICgobGluZU51bWJlciwgbGluZSwgb3BlcmF0aW9uLCBwYXRjaENvbnRlbnQpID0+IGxpbmUgPT09IHBhdGNoQ29udGVudCksXG4gICAgICBmdXp6RmFjdG9yID0gb3B0aW9ucy5mdXp6RmFjdG9yIHx8IDAsXG4gICAgICBtaW5MaW5lID0gMDtcblxuICBpZiAoZnV6ekZhY3RvciA8IDAgfHwgIU51bWJlci5pc0ludGVnZXIoZnV6ekZhY3RvcikpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2Z1enpGYWN0b3IgbXVzdCBiZSBhIG5vbi1uZWdhdGl2ZSBpbnRlZ2VyJyk7XG4gIH1cblxuICAvLyBTcGVjaWFsIGNhc2UgZm9yIGVtcHR5IHBhdGNoLlxuICBpZiAoIWh1bmtzLmxlbmd0aCkge1xuICAgIHJldHVybiBzb3VyY2U7XG4gIH1cblxuICAvLyBCZWZvcmUgYW55dGhpbmcgZWxzZSwgaGFuZGxlIEVPRk5MIGluc2VydGlvbi9yZW1vdmFsLiBJZiB0aGUgcGF0Y2ggdGVsbHMgdXMgdG8gbWFrZSBhIGNoYW5nZVxuICAvLyB0byB0aGUgRU9GTkwgdGhhdCBpcyByZWR1bmRhbnQvaW1wb3NzaWJsZSAtIGkuZS4gdG8gcmVtb3ZlIGEgbmV3bGluZSB0aGF0J3Mgbm90IHRoZXJlLCBvciBhZGQgYVxuICAvLyBuZXdsaW5lIHRoYXQgYWxyZWFkeSBleGlzdHMgLSB0aGVuIHdlIGVpdGhlciByZXR1cm4gZmFsc2UgYW5kIGZhaWwgdG8gYXBwbHkgdGhlIHBhdGNoIChpZlxuICAvLyBmdXp6RmFjdG9yIGlzIDApIG9yIHNpbXBseSBpZ25vcmUgdGhlIHByb2JsZW0gYW5kIGRvIG5vdGhpbmcgKGlmIGZ1enpGYWN0b3IgaXMgPjApLlxuICAvLyBJZiB3ZSBkbyBuZWVkIHRvIHJlbW92ZS9hZGQgYSBuZXdsaW5lIGF0IEVPRiwgdGhpcyB3aWxsIGFsd2F5cyBiZSBpbiB0aGUgZmluYWwgaHVuazpcbiAgbGV0IHByZXZMaW5lID0gJycsXG4gICAgICByZW1vdmVFT0ZOTCA9IGZhbHNlLFxuICAgICAgYWRkRU9GTkwgPSBmYWxzZTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBodW5rc1todW5rcy5sZW5ndGggLSAxXS5saW5lcy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGxpbmUgPSBodW5rc1todW5rcy5sZW5ndGggLSAxXS5saW5lc1tpXTtcbiAgICBpZiAobGluZVswXSA9PSAnXFxcXCcpIHtcbiAgICAgIGlmIChwcmV2TGluZVswXSA9PSAnKycpIHtcbiAgICAgICAgcmVtb3ZlRU9GTkwgPSB0cnVlO1xuICAgICAgfSBlbHNlIGlmIChwcmV2TGluZVswXSA9PSAnLScpIHtcbiAgICAgICAgYWRkRU9GTkwgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICBwcmV2TGluZSA9IGxpbmU7XG4gIH1cbiAgaWYgKHJlbW92ZUVPRk5MKSB7XG4gICAgaWYgKGFkZEVPRk5MKSB7XG4gICAgICAvLyBUaGlzIG1lYW5zIHRoZSBmaW5hbCBsaW5lIGdldHMgY2hhbmdlZCBidXQgZG9lc24ndCBoYXZlIGEgdHJhaWxpbmcgbmV3bGluZSBpbiBlaXRoZXIgdGhlXG4gICAgICAvLyBvcmlnaW5hbCBvciBwYXRjaGVkIHZlcnNpb24uIEluIHRoYXQgY2FzZSwgd2UgZG8gbm90aGluZyBpZiBmdXp6RmFjdG9yID4gMCwgYW5kIGlmXG4gICAgICAvLyBmdXp6RmFjdG9yIGlzIDAsIHdlIHNpbXBseSB2YWxpZGF0ZSB0aGF0IHRoZSBzb3VyY2UgZmlsZSBoYXMgbm8gdHJhaWxpbmcgbmV3bGluZS5cbiAgICAgIGlmICghZnV6ekZhY3RvciAmJiBsaW5lc1tsaW5lcy5sZW5ndGggLSAxXSA9PSAnJykge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChsaW5lc1tsaW5lcy5sZW5ndGggLSAxXSA9PSAnJykge1xuICAgICAgbGluZXMucG9wKCk7XG4gICAgfSBlbHNlIGlmICghZnV6ekZhY3Rvcikge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfSBlbHNlIGlmIChhZGRFT0ZOTCkge1xuICAgIGlmIChsaW5lc1tsaW5lcy5sZW5ndGggLSAxXSAhPSAnJykge1xuICAgICAgbGluZXMucHVzaCgnJyk7XG4gICAgfSBlbHNlIGlmICghZnV6ekZhY3Rvcikge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIGh1bmsgY2FuIGJlIG1hZGUgdG8gZml0IGF0IHRoZSBwcm92aWRlZCBsb2NhdGlvbiB3aXRoIGF0IG1vc3QgYG1heEVycm9yc2BcbiAgICogaW5zZXJ0aW9ucywgc3Vic3RpdHV0aW9ucywgb3IgZGVsZXRpb25zLCB3aGlsZSBlbnN1cmluZyBhbHNvIHRoYXQ6XG4gICAqIC0gbGluZXMgZGVsZXRlZCBpbiB0aGUgaHVuayBtYXRjaCBleGFjdGx5LCBhbmRcbiAgICogLSB3aGVyZXZlciBhbiBpbnNlcnRpb24gb3BlcmF0aW9uIG9yIGJsb2NrIG9mIGluc2VydGlvbiBvcGVyYXRpb25zIGFwcGVhcnMgaW4gdGhlIGh1bmssIHRoZVxuICAgKiAgIGltbWVkaWF0ZWx5IHByZWNlZGluZyBhbmQgZm9sbG93aW5nIGxpbmVzIG9mIGNvbnRleHQgbWF0Y2ggZXhhY3RseVxuICAgKlxuICAgKiBgdG9Qb3NgIHNob3VsZCBiZSBzZXQgc3VjaCB0aGF0IGxpbmVzW3RvUG9zXSBpcyBtZWFudCB0byBtYXRjaCBodW5rTGluZXNbMF0uXG4gICAqXG4gICAqIElmIHRoZSBodW5rIGNhbiBiZSBhcHBsaWVkLCByZXR1cm5zIGFuIG9iamVjdCB3aXRoIHByb3BlcnRpZXMgYG9sZExpbmVMYXN0SWAgYW5kXG4gICAqIGByZXBsYWNlbWVudExpbmVzYC4gT3RoZXJ3aXNlLCByZXR1cm5zIG51bGwuXG4gICAqL1xuICBmdW5jdGlvbiBhcHBseUh1bmsoXG4gICAgaHVua0xpbmVzLFxuICAgIHRvUG9zLFxuICAgIG1heEVycm9ycyxcbiAgICBodW5rTGluZXNJID0gMCxcbiAgICBsYXN0Q29udGV4dExpbmVNYXRjaGVkID0gdHJ1ZSxcbiAgICBwYXRjaGVkTGluZXMgPSBbXSxcbiAgICBwYXRjaGVkTGluZXNMZW5ndGggPSAwLFxuICApIHtcbiAgICBsZXQgbkNvbnNlY3V0aXZlT2xkQ29udGV4dExpbmVzID0gMDtcbiAgICBsZXQgbmV4dENvbnRleHRMaW5lTXVzdE1hdGNoID0gZmFsc2U7XG4gICAgZm9yICg7IGh1bmtMaW5lc0kgPCBodW5rTGluZXMubGVuZ3RoOyBodW5rTGluZXNJKyspIHtcbiAgICAgIGxldCBodW5rTGluZSA9IGh1bmtMaW5lc1todW5rTGluZXNJXSxcbiAgICAgICAgICBvcGVyYXRpb24gPSAoaHVua0xpbmUubGVuZ3RoID4gMCA/IGh1bmtMaW5lWzBdIDogJyAnKSxcbiAgICAgICAgICBjb250ZW50ID0gKGh1bmtMaW5lLmxlbmd0aCA+IDAgPyBodW5rTGluZS5zdWJzdHIoMSkgOiBodW5rTGluZSk7XG5cbiAgICAgIGlmIChvcGVyYXRpb24gPT09ICctJykge1xuICAgICAgICBpZiAoY29tcGFyZUxpbmUodG9Qb3MgKyAxLCBsaW5lc1t0b1Bvc10sIG9wZXJhdGlvbiwgY29udGVudCkpIHtcbiAgICAgICAgICB0b1BvcysrO1xuICAgICAgICAgIG5Db25zZWN1dGl2ZU9sZENvbnRleHRMaW5lcyA9IDA7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWYgKCFtYXhFcnJvcnMgfHwgbGluZXNbdG9Qb3NdID09IG51bGwpIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICAgIH1cbiAgICAgICAgICBwYXRjaGVkTGluZXNbcGF0Y2hlZExpbmVzTGVuZ3RoXSA9IGxpbmVzW3RvUG9zXTtcbiAgICAgICAgICByZXR1cm4gYXBwbHlIdW5rKFxuICAgICAgICAgICAgaHVua0xpbmVzLFxuICAgICAgICAgICAgdG9Qb3MgKyAxLFxuICAgICAgICAgICAgbWF4RXJyb3JzIC0gMSxcbiAgICAgICAgICAgIGh1bmtMaW5lc0ksXG4gICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgIHBhdGNoZWRMaW5lcyxcbiAgICAgICAgICAgIHBhdGNoZWRMaW5lc0xlbmd0aCArIDEsXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAob3BlcmF0aW9uID09PSAnKycpIHtcbiAgICAgICAgaWYgKCFsYXN0Q29udGV4dExpbmVNYXRjaGVkKSB7XG4gICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgcGF0Y2hlZExpbmVzW3BhdGNoZWRMaW5lc0xlbmd0aF0gPSBjb250ZW50O1xuICAgICAgICBwYXRjaGVkTGluZXNMZW5ndGgrKztcbiAgICAgICAgbkNvbnNlY3V0aXZlT2xkQ29udGV4dExpbmVzID0gMDtcbiAgICAgICAgbmV4dENvbnRleHRMaW5lTXVzdE1hdGNoID0gdHJ1ZTtcbiAgICAgIH1cblxuICAgICAgaWYgKG9wZXJhdGlvbiA9PT0gJyAnKSB7XG4gICAgICAgIG5Db25zZWN1dGl2ZU9sZENvbnRleHRMaW5lcysrO1xuICAgICAgICBwYXRjaGVkTGluZXNbcGF0Y2hlZExpbmVzTGVuZ3RoXSA9IGxpbmVzW3RvUG9zXTtcbiAgICAgICAgaWYgKGNvbXBhcmVMaW5lKHRvUG9zICsgMSwgbGluZXNbdG9Qb3NdLCBvcGVyYXRpb24sIGNvbnRlbnQpKSB7XG4gICAgICAgICAgcGF0Y2hlZExpbmVzTGVuZ3RoKys7XG4gICAgICAgICAgbGFzdENvbnRleHRMaW5lTWF0Y2hlZCA9IHRydWU7XG4gICAgICAgICAgbmV4dENvbnRleHRMaW5lTXVzdE1hdGNoID0gZmFsc2U7XG4gICAgICAgICAgdG9Qb3MrKztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpZiAobmV4dENvbnRleHRMaW5lTXVzdE1hdGNoIHx8ICFtYXhFcnJvcnMpIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIENvbnNpZGVyIDMgcG9zc2liaWxpdGllcyBpbiBzZXF1ZW5jZTpcbiAgICAgICAgICAvLyAxLiBsaW5lcyBjb250YWlucyBhICpzdWJzdGl0dXRpb24qIG5vdCBpbmNsdWRlZCBpbiB0aGUgcGF0Y2ggY29udGV4dCwgb3JcbiAgICAgICAgICAvLyAyLiBsaW5lcyBjb250YWlucyBhbiAqaW5zZXJ0aW9uKiBub3QgaW5jbHVkZWQgaW4gdGhlIHBhdGNoIGNvbnRleHQsIG9yXG4gICAgICAgICAgLy8gMy4gbGluZXMgY29udGFpbnMgYSAqZGVsZXRpb24qIG5vdCBpbmNsdWRlZCBpbiB0aGUgcGF0Y2ggY29udGV4dFxuICAgICAgICAgIC8vIFRoZSBmaXJzdCB0d28gb3B0aW9ucyBhcmUgb2YgY291cnNlIG9ubHkgcG9zc2libGUgaWYgdGhlIGxpbmUgZnJvbSBsaW5lcyBpcyBub24tbnVsbCAtXG4gICAgICAgICAgLy8gaS5lLiBvbmx5IG9wdGlvbiAzIGlzIHBvc3NpYmxlIGlmIHdlJ3ZlIG92ZXJydW4gdGhlIGVuZCBvZiB0aGUgb2xkIGZpbGUuXG4gICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIGxpbmVzW3RvUG9zXSAmJiAoXG4gICAgICAgICAgICAgIGFwcGx5SHVuayhcbiAgICAgICAgICAgICAgICBodW5rTGluZXMsXG4gICAgICAgICAgICAgICAgdG9Qb3MgKyAxLFxuICAgICAgICAgICAgICAgIG1heEVycm9ycyAtIDEsXG4gICAgICAgICAgICAgICAgaHVua0xpbmVzSSArIDEsXG4gICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgcGF0Y2hlZExpbmVzLFxuICAgICAgICAgICAgICAgIHBhdGNoZWRMaW5lc0xlbmd0aCArIDFcbiAgICAgICAgICAgICAgKSB8fCBhcHBseUh1bmsoXG4gICAgICAgICAgICAgICAgaHVua0xpbmVzLFxuICAgICAgICAgICAgICAgIHRvUG9zICsgMSxcbiAgICAgICAgICAgICAgICBtYXhFcnJvcnMgLSAxLFxuICAgICAgICAgICAgICAgIGh1bmtMaW5lc0ksXG4gICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgcGF0Y2hlZExpbmVzLFxuICAgICAgICAgICAgICAgIHBhdGNoZWRMaW5lc0xlbmd0aCArIDFcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKSB8fCBhcHBseUh1bmsoXG4gICAgICAgICAgICAgIGh1bmtMaW5lcyxcbiAgICAgICAgICAgICAgdG9Qb3MsXG4gICAgICAgICAgICAgIG1heEVycm9ycyAtIDEsXG4gICAgICAgICAgICAgIGh1bmtMaW5lc0kgKyAxLFxuICAgICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgICAgcGF0Y2hlZExpbmVzLFxuICAgICAgICAgICAgICBwYXRjaGVkTGluZXNMZW5ndGhcbiAgICAgICAgICAgIClcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQmVmb3JlIHJldHVybmluZywgdHJpbSBhbnkgdW5tb2RpZmllZCBjb250ZXh0IGxpbmVzIG9mZiB0aGUgZW5kIG9mIHBhdGNoZWRMaW5lcyBhbmQgcmVkdWNlXG4gICAgLy8gdG9Qb3MgKGFuZCB0aHVzIG9sZExpbmVMYXN0SSkgYWNjb3JkaW5nbHkuIFRoaXMgYWxsb3dzIGxhdGVyIGh1bmtzIHRvIGJlIGFwcGxpZWQgdG8gYSByZWdpb25cbiAgICAvLyB0aGF0IHN0YXJ0cyBpbiB0aGlzIGh1bmsncyB0cmFpbGluZyBjb250ZXh0LlxuICAgIHBhdGNoZWRMaW5lc0xlbmd0aCAtPSBuQ29uc2VjdXRpdmVPbGRDb250ZXh0TGluZXM7XG4gICAgdG9Qb3MgLT0gbkNvbnNlY3V0aXZlT2xkQ29udGV4dExpbmVzO1xuICAgIHBhdGNoZWRMaW5lcy5sZW5ndGggPSBwYXRjaGVkTGluZXNMZW5ndGg7XG4gICAgcmV0dXJuIHtcbiAgICAgIHBhdGNoZWRMaW5lcyxcbiAgICAgIG9sZExpbmVMYXN0STogdG9Qb3MgLSAxXG4gICAgfTtcbiAgfVxuXG4gIGNvbnN0IHJlc3VsdExpbmVzID0gW107XG5cbiAgLy8gU2VhcmNoIGJlc3QgZml0IG9mZnNldHMgZm9yIGVhY2ggaHVuayBiYXNlZCBvbiB0aGUgcHJldmlvdXMgb25lc1xuICBsZXQgcHJldkh1bmtPZmZzZXQgPSAwO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGh1bmtzLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgaHVuayA9IGh1bmtzW2ldO1xuICAgIGxldCBodW5rUmVzdWx0O1xuICAgIGxldCBtYXhMaW5lID0gbGluZXMubGVuZ3RoIC0gaHVuay5vbGRMaW5lcyArIGZ1enpGYWN0b3I7XG4gICAgbGV0IHRvUG9zO1xuICAgIGZvciAobGV0IG1heEVycm9ycyA9IDA7IG1heEVycm9ycyA8PSBmdXp6RmFjdG9yOyBtYXhFcnJvcnMrKykge1xuICAgICAgdG9Qb3MgPSBodW5rLm9sZFN0YXJ0ICsgcHJldkh1bmtPZmZzZXQgLSAxO1xuICAgICAgbGV0IGl0ZXJhdG9yID0gZGlzdGFuY2VJdGVyYXRvcih0b1BvcywgbWluTGluZSwgbWF4TGluZSk7XG4gICAgICBmb3IgKDsgdG9Qb3MgIT09IHVuZGVmaW5lZDsgdG9Qb3MgPSBpdGVyYXRvcigpKSB7XG4gICAgICAgIGh1bmtSZXN1bHQgPSBhcHBseUh1bmsoaHVuay5saW5lcywgdG9Qb3MsIG1heEVycm9ycyk7XG4gICAgICAgIGlmIChodW5rUmVzdWx0KSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChodW5rUmVzdWx0KSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmICghaHVua1Jlc3VsdCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIENvcHkgZXZlcnl0aGluZyBmcm9tIHRoZSBlbmQgb2Ygd2hlcmUgd2UgYXBwbGllZCB0aGUgbGFzdCBodW5rIHRvIHRoZSBzdGFydCBvZiB0aGlzIGh1bmtcbiAgICBmb3IgKGxldCBpID0gbWluTGluZTsgaSA8IHRvUG9zOyBpKyspIHtcbiAgICAgIHJlc3VsdExpbmVzLnB1c2gobGluZXNbaV0pO1xuICAgIH1cblxuICAgIC8vIEFkZCB0aGUgbGluZXMgcHJvZHVjZWQgYnkgYXBwbHlpbmcgdGhlIGh1bms6XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBodW5rUmVzdWx0LnBhdGNoZWRMaW5lcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgbGluZSA9IGh1bmtSZXN1bHQucGF0Y2hlZExpbmVzW2ldO1xuICAgICAgcmVzdWx0TGluZXMucHVzaChsaW5lKTtcbiAgICB9XG5cbiAgICAvLyBTZXQgbG93ZXIgdGV4dCBsaW1pdCB0byBlbmQgb2YgdGhlIGN1cnJlbnQgaHVuaywgc28gbmV4dCBvbmVzIGRvbid0IHRyeVxuICAgIC8vIHRvIGZpdCBvdmVyIGFscmVhZHkgcGF0Y2hlZCB0ZXh0XG4gICAgbWluTGluZSA9IGh1bmtSZXN1bHQub2xkTGluZUxhc3RJICsgMTtcblxuICAgIC8vIE5vdGUgdGhlIG9mZnNldCBiZXR3ZWVuIHdoZXJlIHRoZSBwYXRjaCBzYWlkIHRoZSBodW5rIHNob3VsZCd2ZSBhcHBsaWVkIGFuZCB3aGVyZSB3ZVxuICAgIC8vIGFwcGxpZWQgaXQsIHNvIHdlIGNhbiBhZGp1c3QgZnV0dXJlIGh1bmtzIGFjY29yZGluZ2x5OlxuICAgIHByZXZIdW5rT2Zmc2V0ID0gdG9Qb3MgKyAxIC0gaHVuay5vbGRTdGFydDtcbiAgfVxuXG4gIC8vIENvcHkgb3ZlciB0aGUgcmVzdCBvZiB0aGUgbGluZXMgZnJvbSB0aGUgb2xkIHRleHRcbiAgZm9yIChsZXQgaSA9IG1pbkxpbmU7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgIHJlc3VsdExpbmVzLnB1c2gobGluZXNbaV0pO1xuICB9XG5cbiAgcmV0dXJuIHJlc3VsdExpbmVzLmpvaW4oJ1xcbicpO1xufVxuXG4vLyBXcmFwcGVyIHRoYXQgc3VwcG9ydHMgbXVsdGlwbGUgZmlsZSBwYXRjaGVzIHZpYSBjYWxsYmFja3MuXG5leHBvcnQgZnVuY3Rpb24gYXBwbHlQYXRjaGVzKHVuaURpZmYsIG9wdGlvbnMpIHtcbiAgaWYgKHR5cGVvZiB1bmlEaWZmID09PSAnc3RyaW5nJykge1xuICAgIHVuaURpZmYgPSBwYXJzZVBhdGNoKHVuaURpZmYpO1xuICB9XG5cbiAgbGV0IGN1cnJlbnRJbmRleCA9IDA7XG4gIGZ1bmN0aW9uIHByb2Nlc3NJbmRleCgpIHtcbiAgICBsZXQgaW5kZXggPSB1bmlEaWZmW2N1cnJlbnRJbmRleCsrXTtcbiAgICBpZiAoIWluZGV4KSB7XG4gICAgICByZXR1cm4gb3B0aW9ucy5jb21wbGV0ZSgpO1xuICAgIH1cblxuICAgIG9wdGlvbnMubG9hZEZpbGUoaW5kZXgsIGZ1bmN0aW9uKGVyciwgZGF0YSkge1xuICAgICAgaWYgKGVycikge1xuICAgICAgICByZXR1cm4gb3B0aW9ucy5jb21wbGV0ZShlcnIpO1xuICAgICAgfVxuXG4gICAgICBsZXQgdXBkYXRlZENvbnRlbnQgPSBhcHBseVBhdGNoKGRhdGEsIGluZGV4LCBvcHRpb25zKTtcbiAgICAgIG9wdGlvbnMucGF0Y2hlZChpbmRleCwgdXBkYXRlZENvbnRlbnQsIGZ1bmN0aW9uKGVycikge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgcmV0dXJuIG9wdGlvbnMuY29tcGxldGUoZXJyKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHByb2Nlc3NJbmRleCgpO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cbiAgcHJvY2Vzc0luZGV4KCk7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztB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 diff --git a/deps/npm/node_modules/diff/lib/patch/create.js b/deps/npm/node_modules/diff/lib/patch/create.js index 45be1512a5a088..10ec2d46ff6e8c 100644 --- a/deps/npm/node_modules/diff/lib/patch/create.js +++ b/deps/npm/node_modules/diff/lib/patch/create.js @@ -4,273 +4,366 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.structuredPatch = structuredPatch; -exports.formatPatch = formatPatch; -exports.createTwoFilesPatch = createTwoFilesPatch; exports.createPatch = createPatch; - +exports.createTwoFilesPatch = createTwoFilesPatch; +exports.formatPatch = formatPatch; +exports.structuredPatch = structuredPatch; /*istanbul ignore end*/ var /*istanbul ignore start*/ _line = require("../diff/line") /*istanbul ignore end*/ ; - -/*istanbul ignore start*/ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } - +/*istanbul ignore start*/ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } - function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } - +function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /*istanbul ignore end*/ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { if (!options) { options = {}; } - + if (typeof options === 'function') { + options = { + callback: options + }; + } if (typeof options.context === 'undefined') { options.context = 4; } - - var diff = - /*istanbul ignore start*/ - (0, - /*istanbul ignore end*/ - - /*istanbul ignore start*/ - _line - /*istanbul ignore end*/ - . - /*istanbul ignore start*/ - diffLines) - /*istanbul ignore end*/ - (oldStr, newStr, options); - - if (!diff) { - return; - } - - diff.push({ - value: '', - lines: [] - }); // Append an empty value to make cleanup easier - - function contextLines(lines) { - return lines.map(function (entry) { - return ' ' + entry; - }); + if (options.newlineIsToken) { + throw new Error('newlineIsToken may not be used with patch-generation functions, only with diffing functions'); } - - var hunks = []; - var oldRangeStart = 0, - newRangeStart = 0, - curRange = [], - oldLine = 1, - newLine = 1; - - /*istanbul ignore start*/ - var _loop = function _loop( - /*istanbul ignore end*/ - i) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - /*istanbul ignore start*/ - var _curRange; - - /*istanbul ignore end*/ - // If we have previous context, start with that - if (!oldRangeStart) { - var prev = diff[i - 1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; - } - } // Output our changes - - - /*istanbul ignore start*/ - - /*istanbul ignore end*/ - + if (!options.callback) { + return diffLinesResultToPatch( + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _line + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + diffLines) + /*istanbul ignore end*/ + (oldStr, newStr, options)); + } else { + var /*istanbul ignore start*/ - (_curRange = + _options = /*istanbul ignore end*/ - curRange).push.apply( + options, /*istanbul ignore start*/ - _curRange /*istanbul ignore end*/ - , + _callback = _options.callback; + /*istanbul ignore start*/ + (0, + /*istanbul ignore end*/ + /*istanbul ignore start*/ + _line + /*istanbul ignore end*/ + . + /*istanbul ignore start*/ + diffLines) + /*istanbul ignore end*/ + (oldStr, newStr, + /*istanbul ignore start*/ + _objectSpread(_objectSpread({}, + /*istanbul ignore end*/ + options), {}, { + callback: function /*istanbul ignore start*/ - _toConsumableArray( + callback /*istanbul ignore end*/ - lines.map(function (entry) { - return (current.added ? '+' : '-') + entry; - }))); // Track the updated file position - - - if (current.added) { - newLine += lines.length; - } else { - oldLine += lines.length; + (diff) { + var patch = diffLinesResultToPatch(diff); + _callback(patch); } - } else { - // Identical context lines. Track line changes - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= options.context * 2 && i < diff.length - 2) { - /*istanbul ignore start*/ - var _curRange2; - - /*istanbul ignore end*/ - // Overlapping - - /*istanbul ignore start*/ + })); + } + function diffLinesResultToPatch(diff) { + // STEP 1: Build up the patch with no "\ No newline at end of file" lines and with the arrays + // of lines containing trailing newline characters. We'll tidy up later... - /*istanbul ignore end*/ + if (!diff) { + return; + } + diff.push({ + value: '', + lines: [] + }); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function (entry) { + return ' ' + entry; + }); + } + var hunks = []; + var oldRangeStart = 0, + newRangeStart = 0, + curRange = [], + oldLine = 1, + newLine = 1; + /*istanbul ignore start*/ + var _loop = function _loop() + /*istanbul ignore end*/ + { + var current = diff[i], + lines = current.lines || splitLines(current.value); + current.lines = lines; + if (current.added || current.removed) { + /*istanbul ignore start*/ + var _curRange; + /*istanbul ignore end*/ + // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + if (prev) { + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } - /*istanbul ignore start*/ - (_curRange2 = - /*istanbul ignore end*/ - curRange).push.apply( - /*istanbul ignore start*/ - _curRange2 - /*istanbul ignore end*/ - , - /*istanbul ignore start*/ - _toConsumableArray( - /*istanbul ignore end*/ - contextLines(lines))); + // Output our changes + /*istanbul ignore start*/ + /*istanbul ignore end*/ + /*istanbul ignore start*/ + (_curRange = + /*istanbul ignore end*/ + curRange).push.apply( + /*istanbul ignore start*/ + _curRange + /*istanbul ignore end*/ + , + /*istanbul ignore start*/ + _toConsumableArray( + /*istanbul ignore end*/ + lines.map(function (entry) { + return (current.added ? '+' : '-') + entry; + }))); + + // Track the updated file position + if (current.added) { + newLine += lines.length; } else { - /*istanbul ignore start*/ - var _curRange3; - - /*istanbul ignore end*/ - // end the range and output - var contextSize = Math.min(lines.length, options.context); - - /*istanbul ignore start*/ - - /*istanbul ignore end*/ - - /*istanbul ignore start*/ - (_curRange3 = - /*istanbul ignore end*/ - curRange).push.apply( - /*istanbul ignore start*/ - _curRange3 - /*istanbul ignore end*/ - , - /*istanbul ignore start*/ - _toConsumableArray( - /*istanbul ignore end*/ - contextLines(lines.slice(0, contextSize)))); - - var hunk = { - oldStart: oldRangeStart, - oldLines: oldLine - oldRangeStart + contextSize, - newStart: newRangeStart, - newLines: newLine - newRangeStart + contextSize, - lines: curRange - }; - - if (i >= diff.length - 2 && lines.length <= options.context) { - // EOF is inside this hunk - var oldEOFNewline = /\n$/.test(oldStr); - var newEOFNewline = /\n$/.test(newStr); - var noNlBeforeAdds = lines.length == 0 && curRange.length > hunk.oldLines; - - if (!oldEOFNewline && noNlBeforeAdds && oldStr.length > 0) { - // special case: old has no eol and no trailing context; no-nl can end up before adds - // however, if the old file is empty, do not output the no-nl line - curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); - } - - if (!oldEOFNewline && !noNlBeforeAdds || !newEOFNewline) { - curRange.push('\\ No newline at end of file'); - } + oldLine += lines.length; + } + } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= options.context * 2 && i < diff.length - 2) { + /*istanbul ignore start*/ + var _curRange2; + /*istanbul ignore end*/ + // Overlapping + /*istanbul ignore start*/ + /*istanbul ignore end*/ + /*istanbul ignore start*/ + (_curRange2 = + /*istanbul ignore end*/ + curRange).push.apply( + /*istanbul ignore start*/ + _curRange2 + /*istanbul ignore end*/ + , + /*istanbul ignore start*/ + _toConsumableArray( + /*istanbul ignore end*/ + contextLines(lines))); + } else { + /*istanbul ignore start*/ + var _curRange3; + /*istanbul ignore end*/ + // end the range and output + var contextSize = Math.min(lines.length, options.context); + /*istanbul ignore start*/ + /*istanbul ignore end*/ + /*istanbul ignore start*/ + (_curRange3 = + /*istanbul ignore end*/ + curRange).push.apply( + /*istanbul ignore start*/ + _curRange3 + /*istanbul ignore end*/ + , + /*istanbul ignore start*/ + _toConsumableArray( + /*istanbul ignore end*/ + contextLines(lines.slice(0, contextSize)))); + var _hunk = { + oldStart: oldRangeStart, + oldLines: oldLine - oldRangeStart + contextSize, + newStart: newRangeStart, + newLines: newLine - newRangeStart + contextSize, + lines: curRange + }; + hunks.push(_hunk); + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; } - - hunks.push(hunk); - oldRangeStart = 0; - newRangeStart = 0; - curRange = []; } + oldLine += lines.length; + newLine += lines.length; } - - oldLine += lines.length; - newLine += lines.length; + }; + for (var i = 0; i < diff.length; i++) + /*istanbul ignore start*/ + { + _loop(); } - }; - for (var i = 0; i < diff.length; i++) { + // Step 2: eliminate the trailing `\n` from each line of each hunk, and, where needed, add + // "\ No newline at end of file". + /*istanbul ignore end*/ + for ( + /*istanbul ignore start*/ + var _i = 0, _hunks = + /*istanbul ignore end*/ + hunks; + /*istanbul ignore start*/ + _i < _hunks.length + /*istanbul ignore end*/ + ; /*istanbul ignore start*/ - _loop( + _i++ /*istanbul ignore end*/ - i); + ) { + var hunk = + /*istanbul ignore start*/ + _hunks[_i] + /*istanbul ignore end*/ + ; + for (var _i2 = 0; _i2 < hunk.lines.length; _i2++) { + if (hunk.lines[_i2].endsWith('\n')) { + hunk.lines[_i2] = hunk.lines[_i2].slice(0, -1); + } else { + hunk.lines.splice(_i2 + 1, 0, '\\ No newline at end of file'); + _i2++; // Skip the line we just added, then continue iterating + } + } + } + return { + oldFileName: oldFileName, + newFileName: newFileName, + oldHeader: oldHeader, + newHeader: newHeader, + hunks: hunks + }; } - - return { - oldFileName: oldFileName, - newFileName: newFileName, - oldHeader: oldHeader, - newHeader: newHeader, - hunks: hunks - }; } - function formatPatch(diff) { if (Array.isArray(diff)) { return diff.map(formatPatch).join('\n'); } - var ret = []; - if (diff.oldFileName == diff.newFileName) { ret.push('Index: ' + diff.oldFileName); } - ret.push('==================================================================='); ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); - for (var i = 0; i < diff.hunks.length; i++) { - var hunk = diff.hunks[i]; // Unified Diff Format quirk: If the chunk size is 0, + var hunk = diff.hunks[i]; + // Unified Diff Format quirk: If the chunk size is 0, // the first number is one lower than one would expect. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 - if (hunk.oldLines === 0) { hunk.oldStart -= 1; } - if (hunk.newLines === 0) { hunk.newStart -= 1; } - ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); ret.push.apply(ret, hunk.lines); } - return ret.join('\n') + '\n'; } - function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { - return formatPatch(structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)); + /*istanbul ignore start*/ + var _options2; + /*istanbul ignore end*/ + if (typeof options === 'function') { + options = { + callback: options + }; + } + if (! + /*istanbul ignore start*/ + ((_options2 = + /*istanbul ignore end*/ + options) !== null && _options2 !== void 0 && + /*istanbul ignore start*/ + _options2 + /*istanbul ignore end*/ + .callback)) { + var patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); + if (!patchObj) { + return; + } + return formatPatch(patchObj); + } else { + var + /*istanbul ignore start*/ + _options3 = + /*istanbul ignore end*/ + options, + /*istanbul ignore start*/ + /*istanbul ignore end*/ + _callback2 = _options3.callback; + structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, + /*istanbul ignore start*/ + _objectSpread(_objectSpread({}, + /*istanbul ignore end*/ + options), {}, { + callback: function + /*istanbul ignore start*/ + callback + /*istanbul ignore end*/ + (patchObj) { + if (!patchObj) { + _callback2(); + } else { + _callback2(formatPatch(patchObj)); + } + } + })); + } } - function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9jcmVhdGUuanMiXSwibmFtZXMiOlsic3RydWN0dXJlZFBhdGNoIiwib2xkRmlsZU5hbWUiLCJuZXdGaWxlTmFtZSIsIm9sZFN0ciIsIm5ld1N0ciIsIm9sZEhlYWRlciIsIm5ld0hlYWRlciIsIm9wdGlvbnMiLCJjb250ZXh0IiwiZGlmZiIsImRpZmZMaW5lcyIsInB1c2giLCJ2YWx1ZSIsImxpbmVzIiwiY29udGV4dExpbmVzIiwibWFwIiwiZW50cnkiLCJodW5rcyIsIm9sZFJhbmdlU3RhcnQiLCJuZXdSYW5nZVN0YXJ0IiwiY3VyUmFuZ2UiLCJvbGRMaW5lIiwibmV3TGluZSIsImkiLCJjdXJyZW50IiwicmVwbGFjZSIsInNwbGl0IiwiYWRkZWQiLCJyZW1vdmVkIiwicHJldiIsInNsaWNlIiwibGVuZ3RoIiwiY29udGV4dFNpemUiLCJNYXRoIiwibWluIiwiaHVuayIsIm9sZFN0YXJ0Iiwib2xkTGluZXMiLCJuZXdTdGFydCIsIm5ld0xpbmVzIiwib2xkRU9GTmV3bGluZSIsInRlc3QiLCJuZXdFT0ZOZXdsaW5lIiwibm9ObEJlZm9yZUFkZHMiLCJzcGxpY2UiLCJmb3JtYXRQYXRjaCIsIkFycmF5IiwiaXNBcnJheSIsImpvaW4iLCJyZXQiLCJhcHBseSIsImNyZWF0ZVR3b0ZpbGVzUGF0Y2giLCJjcmVhdGVQYXRjaCIsImZpbGVOYW1lIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOzs7Ozs7Ozs7Ozs7Ozs7QUFFTyxTQUFTQSxlQUFULENBQXlCQyxXQUF6QixFQUFzQ0MsV0FBdEMsRUFBbURDLE1BQW5ELEVBQTJEQyxNQUEzRCxFQUFtRUMsU0FBbkUsRUFBOEVDLFNBQTlFLEVBQXlGQyxPQUF6RixFQUFrRztBQUN2RyxNQUFJLENBQUNBLE9BQUwsRUFBYztBQUNaQSxJQUFBQSxPQUFPLEdBQUcsRUFBVjtBQUNEOztBQUNELE1BQUksT0FBT0EsT0FBTyxDQUFDQyxPQUFmLEtB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QUNuRixTQUFPNEMsbUJBQW1CLENBQUNFLFFBQUQsRUFBV0EsUUFBWCxFQUFxQmxELE1BQXJCLEVBQTZCQyxNQUE3QixFQUFxQ0MsU0FBckMsRUFBZ0RDLFNBQWhELEVBQTJEQyxPQUEzRCxDQUExQjtBQUNEIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtkaWZmTGluZXN9IGZyb20gJy4uL2RpZmYvbGluZSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBzdHJ1Y3R1cmVkUGF0Y2gob2xkRmlsZU5hbWUsIG5ld0ZpbGVOYW1lLCBvbGRTdHIsIG5ld1N0ciwgb2xkSGVhZGVyLCBuZXdIZWFkZXIsIG9wdGlvbnMpIHtcbiAgaWYgKCFvcHRpb25zKSB7XG4gICAgb3B0aW9ucyA9IHt9O1xuICB9XG4gIGlmICh0eXBlb2Ygb3B0aW9ucy5jb250ZXh0ID09PSAndW5kZWZpbmVkJykge1xuICAgIG9wdGlvbnMuY29udGV4dCA9IDQ7XG4gIH1cblxuICBjb25zdCBkaWZmID0gZGlmZkxpbmVzKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbiAgaWYoIWRpZmYpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICBkaWZmLnB1c2goe3ZhbHVlOiAnJywgbGluZXM6IFtdfSk7IC8vIEFwcGVuZCBhbiBlbXB0eSB2YWx1ZSB0byBtYWtlIGNsZWFudXAgZWFzaWVyXG5cbiAgZnVuY3Rpb24gY29udGV4dExpbmVzKGxpbmVzKSB7XG4gICAgcmV0dXJuIGxpbmVzLm1hcChmdW5jdGlvbihlbnRyeSkgeyByZXR1cm4gJyAnICsgZW50cnk7IH0pO1xuICB9XG5cbiAgbGV0IGh1bmtzID0gW107XG4gIGxldCBvbGRSYW5nZVN0YXJ0ID0gMCwgbmV3UmFuZ2VTdGFydCA9IDAsIGN1clJhbmdlID0gW10sXG4gICAgICBvbGRMaW5lID0gMSwgbmV3TGluZSA9IDE7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZGlmZi5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGN1cnJlbnQgPSBkaWZmW2ldLFxuICAgICAgICAgIGxpbmVzID0gY3VycmVudC5saW5lcyB8fCBjdXJyZW50LnZhbHVlLnJlcGxhY2UoL1xcbiQvLCAnJykuc3BsaXQoJ1xcbicpO1xuICAgIGN1cnJlbnQubGluZXMgPSBsaW5lcztcblxuICAgIGlmIChjdXJyZW50LmFkZGVkIHx8IGN1cnJlbnQucmVtb3ZlZCkge1xuICAgICAgLy8gSWYgd2UgaGF2ZSBwcmV2aW91cyBjb250ZXh0LCBzdGFydCB3aXRoIHRoYXRcbiAgICAgIGlmICghb2xkUmFuZ2VTdGFydCkge1xuICAgICAgICBjb25zdCBwcmV2ID0gZGlmZltpIC0gMV07XG4gICAgICAgIG9sZFJhbmdlU3RhcnQgPSBvbGRMaW5lO1xuICAgICAgICBuZXdSYW5nZVN0YXJ0ID0gbmV3TGluZTtcblxuICAgICAgICBpZiAocHJldikge1xuICAgICAgICAgIGN1clJhbmdlID0gb3B0aW9ucy5jb250ZXh0ID4gMCA/IGNvbnRleHRMaW5lcyhwcmV2LmxpbmVzLnNsaWNlKC1vcHRpb25zLmNvbnRleHQpKSA6IFtdO1xuICAgICAgICAgIG9sZFJhbmdlU3RhcnQgLT0gY3VyUmFuZ2UubGVuZ3RoO1xuICAgICAgICAgIG5ld1JhbmdlU3RhcnQgLT0gY3VyUmFuZ2UubGVuZ3RoO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIE91dHB1dCBvdXIgY2hhbmdlc1xuICAgICAgY3VyUmFuZ2UucHVzaCguLi4gbGluZXMubWFwKGZ1bmN0aW9uKGVudHJ5KSB7XG4gICAgICAgIHJldHVybiAoY3VycmVudC5hZGRlZCA/ICcrJyA6ICctJykgKyBlbnRyeTtcbiAgICAgIH0pKTtcblxuICAgICAgLy8gVHJhY2sgdGhlIHVwZGF0ZWQgZmlsZSBwb3NpdGlvblxuICAgICAgaWYgKGN1cnJlbnQuYWRkZWQpIHtcbiAgICAgICAgbmV3TGluZSArPSBsaW5lcy5sZW5ndGg7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBvbGRMaW5lICs9IGxpbmVzLmxlbmd0aDtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gSWRlbnRpY2FsIGNvbnRleHQgbGluZXMuIFRyYWNrIGxpbmUgY2hhbmdlc1xuICAgICAgaWYgKG9sZFJhbmdlU3RhcnQpIHtcbiAgICAgICAgLy8gQ2xvc2Ugb3V0IGFueSBjaGFuZ2VzIHRoYXQgaGF2ZSBiZWVuIG91dHB1dCAob3Igam9pbiBvdmVybGFwcGluZylcbiAgICAgICAgaWYgKGxpbmVzLmxlbmd0aCA8PSBvcHRpb25zLmNvbnRleHQgKiAyICYmIGkgPCBkaWZmLmxlbmd0aCAtIDIpIHtcbiAgICAgICAgICAvLyBPdmVybGFwcGluZ1xuICAgICAgICAgIGN1clJhbmdlLnB1c2goLi4uIGNvbnRleHRMaW5lcyhsaW5lcykpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIGVuZCB0aGUgcmFuZ2UgYW5kIG91dHB1dFxuICAgICAgICAgIGxldCBjb250ZXh0U2l6ZSA9IE1hdGgubWluKGxpbmVzLmxlbmd0aCwgb3B0aW9ucy5jb250ZXh0KTtcbiAgICAgICAgICBjdXJSYW5nZS5wdXNoKC4uLiBjb250ZXh0TGluZXMobGluZXMuc2xpY2UoMCwgY29udGV4dFNpemUpKSk7XG5cbiAgICAgICAgICBsZXQgaHVuayA9IHtcbiAgICAgICAgICAgIG9sZFN0YXJ0OiBvbGRSYW5nZVN0YXJ0LFxuICAgICAgICAgICAgb2xkTGluZXM6IChvbGRMaW5lIC0gb2xkUmFuZ2VTdGFydCArIGNvbnRleHRTaXplKSxcbiAgICAgICAgICAgIG5ld1N0YXJ0OiBuZXdSYW5nZVN0YXJ0LFxuICAgICAgICAgICAgbmV3TGluZXM6IChuZXdMaW5lIC0gbmV3UmFuZ2VTdGFydCArIGNvbnRleHRTaXplKSxcbiAgICAgICAgICAgIGxpbmVzOiBjdXJSYW5nZVxuICAgICAgICAgIH07XG4gICAgICAgICAgaWYgKGkgPj0gZGlmZi5sZW5ndGggLSAyICYmIGxpbmVzLmxlbmd0aCA8PSBvcHRpb25zLmNvbnRleHQpIHtcbiAgICAgICAgICAgIC8vIEVPRiBpcyBpbnNpZGUgdGhpcyBodW5rXG4gICAgICAgICAgICBsZXQgb2xkRU9GTmV3bGluZSA9ICgoL1xcbiQvKS50ZXN0KG9sZFN0cikpO1xuICAgICAgICAgICAgbGV0IG5ld0VPRk5ld2xpbmUgPSAoKC9cXG4kLykudGVzdChuZXdTdHIpKTtcbiAgICAgICAgICAgIGxldCBub05sQmVmb3JlQWRkcyA9IGxpbmVzLmxlbmd0aCA9PSAwICYmIGN1clJhbmdlLmxlbmd0aCA+IGh1bmsub2xkTGluZXM7XG4gICAgICAgICAgICBpZiAoIW9sZEVPRk5ld2xpbmUgJiYgbm9ObEJlZm9yZUFkZHMgJiYgb2xkU3RyLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgLy8gc3BlY2lhbCBjYXNlOiBvbGQgaGFzIG5vIGVvbCBhbmQgbm8gdHJhaWxpbmcgY29udGV4dDsgbm8tbmwgY2FuIGVuZCB1cCBiZWZvcmUgYWRkc1xuICAgICAgICAgICAgICAvLyBob3dldmVyLCBpZiB0aGUgb2xkIGZpbGUgaXMgZW1wdHksIGRvIG5vdCBvdXRwdXQgdGhlIG5vLW5sIGxpbmVcbiAgICAgICAgICAgICAgY3VyUmFuZ2Uuc3BsaWNlKGh1bmsub2xkTGluZXMsIDAsICdcXFxcIE5vIG5ld2xpbmUgYXQgZW5kIG9mIGZpbGUnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICgoIW9sZEVPRk5ld2xpbmUgJiYgIW5vTmxCZWZvcmVBZGRzKSB8fCAhbmV3RU9GTmV3bGluZSkge1xuICAgICAgICAgICAgICBjdXJSYW5nZS5wdXNoKCdcXFxcIE5vIG5ld2xpbmUgYXQgZW5kIG9mIGZpbGUnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaHVua3MucHVzaChodW5rKTtcblxuICAgICAgICAgIG9sZFJhbmdlU3RhcnQgPSAwO1xuICAgICAgICAgIG5ld1JhbmdlU3RhcnQgPSAwO1xuICAgICAgICAgIGN1clJhbmdlID0gW107XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIG9sZExpbmUgKz0gbGluZXMubGVuZ3RoO1xuICAgICAgbmV3TGluZSArPSBsaW5lcy5sZW5ndGg7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBvbGRGaWxlTmFtZTogb2xkRmlsZU5hbWUsIG5ld0ZpbGVOYW1lOiBuZXdGaWxlTmFtZSxcbiAgICBvbGRIZWFkZXI6IG9sZEhlYWRlciwgbmV3SGVhZGVyOiBuZXdIZWFkZXIsXG4gICAgaHVua3M6IGh1bmtzXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRQYXRjaChkaWZmKSB7XG4gIGlmIChBcnJheS5pc0FycmF5KGRpZmYpKSB7XG4gICAgcmV0dXJuIGRpZmYubWFwKGZvcm1hdFBhdGNoKS5qb2luKCdcXG4nKTtcbiAgfVxuXG4gIGNvbnN0IHJldCA9IFtdO1xuICBpZiAoZGlmZi5vbGRGaWxlTmFtZSA9PSBkaWZmLm5ld0ZpbGVOYW1lKSB7XG4gICAgcmV0LnB1c2goJ0luZGV4OiAnICsgZGlmZi5vbGRGaWxlTmFtZSk7XG4gIH1cbiAgcmV0LnB1c2goJz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0nKTtcbiAgcmV0LnB1c2goJy0tLSAnICsgZGlmZi5vbGRGaWxlTmFtZSArICh0eXBlb2YgZGlmZi5vbGRIZWFkZXIgPT09ICd1bmRlZmluZWQnID8gJycgOiAnXFx0JyArIGRpZmYub2xkSGVhZGVyKSk7XG4gIHJldC5wdXNoKCcrKysgJyArIGRpZmYubmV3RmlsZU5hbWUgKyAodHlwZW9mIGRpZmYubmV3SGVhZGVyID09PSAndW5kZWZpbmVkJyA/ICcnIDogJ1xcdCcgKyBkaWZmLm5ld0hlYWRlcikpO1xuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZGlmZi5odW5rcy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGh1bmsgPSBkaWZmLmh1bmtzW2ldO1xuICAgIC8vIFVuaWZpZWQgRGlmZiBGb3JtYXQgcXVpcms6IElmIHRoZSBjaHVuayBzaXplIGlzIDAsXG4gICAgLy8gdGhlIGZpcnN0IG51bWJlciBpcyBvbmUgbG93ZXIgdGhhbiBvbmUgd291bGQgZXhwZWN0LlxuICAgIC8vIGh0dHBzOi8vd3d3LmFydGltYS5jb20vd2VibG9ncy92aWV3cG9zdC5qc3A/dGhyZWFkPTE2NDI5M1xuICAgIGlmIChodW5rLm9sZExpbmVzID09PSAwKSB7XG4gICAgICBodW5rLm9sZFN0YXJ0IC09IDE7XG4gICAgfVxuICAgIGlmIChodW5rLm5ld0xpbmVzID09PSAwKSB7XG4gICAgICBodW5rLm5ld1N0YXJ0IC09IDE7XG4gICAgfVxuICAgIHJldC5wdXNoKFxuICAgICAgJ0BAIC0nICsgaHVuay5vbGRTdGFydCArICcsJyArIGh1bmsub2xkTGluZXNcbiAgICAgICsgJyArJyArIGh1bmsubmV3U3RhcnQgKyAnLCcgKyBodW5rLm5ld0xpbmVzXG4gICAgICArICcgQEAnXG4gICAgKTtcbiAgICByZXQucHVzaC5hcHBseShyZXQsIGh1bmsubGluZXMpO1xuICB9XG5cbiAgcmV0dXJuIHJldC5qb2luKCdcXG4nKSArICdcXG4nO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlVHdvRmlsZXNQYXRjaChvbGRGaWxlTmFtZSwgbmV3RmlsZU5hbWUsIG9sZFN0ciwgbmV3U3RyLCBvbGRIZWFkZXIsIG5ld0hlYWRlciwgb3B0aW9ucykge1xuICByZXR1cm4gZm9ybWF0UGF0Y2goc3RydWN0dXJlZFBhdGNoKG9sZEZpbGVOYW1lLCBuZXdGaWxlTmFtZSwgb2xkU3RyLCBuZXdTdHIsIG9sZEhlYWRlciwgbmV3SGVhZGVyLCBvcHRpb25zKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVQYXRjaChmaWxlTmFtZSwgb2xkU3RyLCBuZXdTdHIsIG9sZEhlYWRlciwgbmV3SGVhZGVyLCBvcHRpb25zKSB7XG4gIHJldHVybiBjcmVhdGVUd29GaWxlc1BhdGNoKGZpbGVOYW1lLCBmaWxlTmFtZSwgb2xkU3RyLCBuZXdTdHIsIG9sZEhlYWRlciwgbmV3SGVhZGVyLCBvcHRpb25zKTtcbn1cbiJdfQ== + +/** + * Split `text` into an array of lines, including the trailing newline character (where present) + */ +function splitLines(text) { + var hasTrailingNl = text.endsWith('\n'); + var result = text.split('\n').map(function (line) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + line + '\n' + ); + }); + if (hasTrailingNl) { + result.pop(); + } else { + result.push(result.pop().slice(0, -1)); + } + return result; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbGluZSIsInJlcXVpcmUiLCJfdHlwZW9mIiwibyIsIlN5bWJvbCIsIml0ZXJhdG9yIiwiY29uc3RydWN0b3IiLCJwcm90b3R5cGUiLCJfdG9Db25zdW1hYmxlQXJyYXkiLCJhcnIiLCJfYXJyYXlXaXRob3V0SG9sZXMiLCJfaXRlcmFibGVUb0FycmF5IiwiX3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5IiwiX25vbkl0ZXJhYmxlU3ByZWFkIiwiVHlwZUVycm9yIiwibWluTGVuIiwiX2FycmF5TGlrZVRvQXJyYXkiLCJuIiwiT2JqZWN0IiwidG9TdHJpbmciLCJjYWxsIiwic2xpY2UiLCJuYW1lIiwiQXJyYXkiLCJmcm9tIiwidGVzdCIsIml0ZXIiLCJpc0FycmF5IiwibGVuIiwibGVuZ3RoIiwiaSIsImFycjIiLCJvd25LZXlzIiwiZSIsInIiLCJ0Iiwia2V5cyIsImdldE93blByb3BlcnR5U3ltYm9scyIsImZpbHRlciIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiZGVmaW5lUHJvcGVydHkiLCJvYmoiLCJrZXkiLCJ2YWx1ZSIsIl90b1Byb3BlcnR5S2V5IiwiY29uZmlndXJhYmxlIiwid3JpdGFibGUiLCJfdG9QcmltaXRpdmUiLCJ0b1ByaW1pdGl2ZSIsIlN0cmluZyIsIk51bWJlciIsInN0cnVjdHVyZWRQYXRjaCIsIm9sZEZpbGVOYW1lIiwibmV3RmlsZU5hbWUiLCJvbGRTdHIiLCJuZXdTdHIiLCJvbGRIZWFkZXIiLCJuZXdIZWFkZXIiLCJvcHRpb25zIiwiY2FsbGJhY2siLCJjb250ZXh0IiwibmV3bGluZUlzVG9rZW4iLCJFcnJvciIsImRpZmZMaW5lc1Jlc3VsdFRvUGF0Y2giLCJkaWZmTGluZXMiLCJfb3B0aW9ucyIsImRpZmYiLCJwYXRjaCIsImxpbmVzIiwiY29udGV4dExpbmVzIiwibWFwIiwiZW50cnkiLCJodW5rcyIsIm9sZFJhbmdlU3RhcnQiLCJuZXdSYW5nZVN0YXJ0IiwiY3VyUmFuZ2UiLCJvbGRMaW5lIiwibmV3TGluZSIsIl9sb29wIiwiY3VycmVudCIsInNwbGl0TGluZXMiLCJhZGRlZCIsInJlbW92ZWQiLCJfY3VyUmFuZ2UiLCJwcmV2IiwiX2N1clJhbmdlMiIsIl9jdXJSYW5nZTMiLCJjb250ZXh0U2l6ZSIsIk1hdGgiLCJtaW4iLCJodW5rIiwib2xkU3RhcnQiLCJvbGRMaW5lcyIsIm5ld1N0YXJ0IiwibmV3TGluZXMiLCJfaSIsIl9odW5rcyIsImVuZHNXaXRoIiwic3BsaWNlIiwiZm9ybWF0UGF0Y2giLCJqb2luIiwicmV0IiwiY3JlYXRlVHdvRmlsZXNQYXRjaCIsIl9vcHRpb25zMiIsInBhdGNoT2JqIiwiX29wdGlvbnMzIiwiY3JlYXRlUGF0Y2giLCJmaWxlTmFtZSIsInRleHQiLCJoYXNUcmFpbGluZ05sIiwicmVzdWx0Iiwic3BsaXQiLCJsaW5lIiwicG9wIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BhdGNoL2NyZWF0ZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge2RpZmZMaW5lc30gZnJvbSAnLi4vZGlmZi9saW5lJztcblxuZXhwb3J0IGZ1bmN0aW9uIHN0cnVjdHVyZWRQYXRjaChvbGRGaWxlTmFtZSwgbmV3RmlsZU5hbWUsIG9sZFN0ciwgbmV3U3RyLCBvbGRIZWFkZXIsIG5ld0hlYWRlciwgb3B0aW9ucykge1xuICBpZiAoIW9wdGlvbnMpIHtcbiAgICBvcHRpb25zID0ge307XG4gIH1cbiAgaWYgKHR5cGVvZiBvcHRpb25zID09PSAnZnVuY3Rpb24nKSB7XG4gICAgb3B0aW9ucyA9IHtjYWxsYmFjazogb3B0aW9uc307XG4gIH1cbiAgaWYgKHR5cGVvZiBvcHRpb25zLmNvbnRleHQgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgb3B0aW9ucy5jb250ZXh0ID0gNDtcbiAgfVxuICBpZiAob3B0aW9ucy5uZXdsaW5lSXNUb2tlbikge1xuICAgIHRocm93IG5ldyBFcnJvcignbmV3bGluZUlzVG9rZW4gbWF5IG5vdCBiZSB1c2VkIHdpdGggcGF0Y2gtZ2VuZXJhdGlvbiBmdW5jdGlvbnMsIG9ubHkgd2l0aCBkaWZmaW5nIGZ1bmN0aW9ucycpO1xuICB9XG5cbiAgaWYgKCFvcHRpb25zLmNhbGxiYWNrKSB7XG4gICAgcmV0dXJuIGRpZmZMaW5lc1Jlc3VsdFRvUGF0Y2goZGlmZkxpbmVzKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKSk7XG4gIH0gZWxzZSB7XG4gICAgY29uc3Qge2NhbGxiYWNrfSA9IG9wdGlvbnM7XG4gICAgZGlmZkxpbmVzKFxuICAgICAgb2xkU3RyLFxuICAgICAgbmV3U3RyLFxuICAgICAge1xuICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgICBjYWxsYmFjazogKGRpZmYpID0+IHtcbiAgICAgICAgICBjb25zdCBwYXRjaCA9IGRpZmZMaW5lc1Jlc3VsdFRvUGF0Y2goZGlmZik7XG4gICAgICAgICAgY2FsbGJhY2socGF0Y2gpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGRpZmZMaW5lc1Jlc3VsdFRvUGF0Y2goZGlmZikge1xuICAgIC8vIFNURVAgMTogQnVpbGQgdXAgdGhlIHBhdGNoIHdpdGggbm8gXCJcXCBObyBuZXdsaW5lIGF0IGVuZCBvZiBmaWxlXCIgbGluZXMgYW5kIHdpdGggdGhlIGFycmF5c1xuICAgIC8vICAgICAgICAgb2YgbGluZXMgY29udGFpbmluZyB0cmFpbGluZyBuZXdsaW5lIGNoYXJhY3RlcnMuIFdlJ2xsIHRpZHkgdXAgbGF0ZXIuLi5cblxuICAgIGlmKCFkaWZmKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZGlmZi5wdXNoKHt2YWx1ZTogJycsIGxpbmVzOiBbXX0pOyAvLyBBcHBlbmQgYW4gZW1wdHkgdmFsdWUgdG8gbWFrZSBjbGVhbnVwIGVhc2llclxuXG4gICAgZnVuY3Rpb24gY29udGV4dExpbmVzKGxpbmVzKSB7XG4gICAgICByZXR1cm4gbGluZXMubWFwKGZ1bmN0aW9uKGVudHJ5KSB7IHJldHVybiAnICcgKyBlbnRyeTsgfSk7XG4gICAgfVxuXG4gICAgbGV0IGh1bmtzID0gW107XG4gICAgbGV0IG9sZFJhbmdlU3RhcnQgPSAwLCBuZXdSYW5nZVN0YXJ0ID0gMCwgY3VyUmFuZ2UgPSBbXSxcbiAgICAgICAgb2xkTGluZSA9IDEsIG5ld0xpbmUgPSAxO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZGlmZi5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgY3VycmVudCA9IGRpZmZbaV0sXG4gICAgICAgICAgICBsaW5lcyA9IGN1cnJlbnQubGluZXMgfHwgc3BsaXRMaW5lcyhjdXJyZW50LnZhbHVlKTtcbiAgICAgIGN1cnJlbnQubGluZXMgPSBsaW5lcztcblxuICAgICAgaWYgKGN1cnJlbnQuYWRkZWQgfHwgY3VycmVudC5yZW1vdmVkKSB7XG4gICAgICAgIC8vIElmIHdlIGhhdmUgcHJldmlvdXMgY29udGV4dCwgc3RhcnQgd2l0aCB0aGF0XG4gICAgICAgIGlmICghb2xkUmFuZ2VTdGFydCkge1xuICAgICAgICAgIGNvbnN0IHByZXYgPSBkaWZmW2kgLSAxXTtcbiAgICAgICAgICBvbGRSYW5nZVN0YXJ0ID0gb2xkTGluZTtcbiAgICAgICAgICBuZXdSYW5nZVN0YXJ0ID0gbmV3TGluZTtcblxuICAgICAgICAgIGlmIChwcmV2KSB7XG4gICAgICAgICAgICBjdXJSYW5nZSA9IG9wdGlvbnMuY29udGV4dCA+IDAgPyBjb250ZXh0TGluZXMocHJldi5saW5lcy5zbGljZSgtb3B0aW9ucy5jb250ZXh0KSkgOiBbXTtcbiAgICAgICAgICAgIG9sZFJhbmdlU3RhcnQgLT0gY3VyUmFuZ2UubGVuZ3RoO1xuICAgICAgICAgICAgbmV3UmFuZ2VTdGFydCAtPSBjdXJSYW5nZS5sZW5ndGg7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gT3V0cHV0IG91ciBjaGFuZ2VzXG4gICAgICAgIGN1clJhbmdlLnB1c2goLi4uIGxpbmVzLm1hcChmdW5jdGlvbihlbnRyeSkge1xuICAgICAgICAgIHJldHVybiAoY3VycmVudC5hZGRlZCA/ICcrJyA6ICctJykgKyBlbnRyeTtcbiAgICAgICAgfSkpO1xuXG4gICAgICAgIC8vIFRyYWNrIHRoZSB1cGRhdGVkIGZpbGUgcG9zaXRpb25cbiAgICAgICAgaWYgKGN1cnJlbnQuYWRkZWQpIHtcbiAgICAgICAgICBuZXdMaW5lICs9IGxpbmVzLmxlbmd0aDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBvbGRMaW5lICs9IGxpbmVzLmxlbmd0aDtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gSWRlbnRpY2FsIGNvbnRleHQgbGluZXMuIFRyYWNrIGxpbmUgY2hhbmdlc1xuICAgICAgICBpZiAob2xkUmFuZ2VTdGFydCkge1xuICAgICAgICAgIC8vIENsb3NlIG91dCBhbnkgY2hhbmdlcyB0aGF0IGhhdmUgYmVlbiBvdXRwdXQgKG9yIGpvaW4gb3ZlcmxhcHBpbmcpXG4gICAgICAgICAgaWYgKGxpbmVzLmxlbmd0aCA8PSBvcHRpb25zLmNvbnRleHQgKiAyICYmIGkgPCBkaWZmLmxlbmd0aCAtIDIpIHtcbiAgICAgICAgICAgIC8vIE92ZXJsYXBwaW5nXG4gICAgICAgICAgICBjdXJSYW5nZS5wdXNoKC4uLiBjb250ZXh0TGluZXMobGluZXMpKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gZW5kIHRoZSByYW5nZSBhbmQgb3V0cHV0XG4gICAgICAgICAgICBsZXQgY29udGV4dFNpemUgPSBNYXRoLm1pbihsaW5lcy5sZW5ndGgsIG9wdGlvbnMuY29udGV4dCk7XG4gICAgICAgICAgICBjdXJSYW5nZS5wdXNoKC4uLiBjb250ZXh0TGluZXMobGluZXMuc2xpY2UoMCwgY29udGV4dFNpemUpKSk7XG5cbiAgICAgICAgICAgIGxldCBodW5rID0ge1xuICAgICAgICAgICAgICBvbGRTdGFydDogb2xkUmFuZ2VTdGFydCxcbiAgICAgICAgICAgICAgb2xkTGluZXM6IChvbGRMaW5lIC0gb2xkUmFuZ2VTdGFydCArIGNvbnRleHRTaXplKSxcbiAgICAgICAgICAgICAgbmV3U3RhcnQ6IG5ld1JhbmdlU3RhcnQsXG4gICAgICAgICAgICAgIG5ld0xpbmVzOiAobmV3TGluZSAtIG5ld1JhbmdlU3RhcnQgKyBjb250ZXh0U2l6ZSksXG4gICAgICAgICAgICAgIGxpbmVzOiBjdXJSYW5nZVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGh1bmtzLnB1c2goaHVuayk7XG5cbiAgICAgICAgICAgIG9sZFJhbmdlU3RhcnQgPSAwO1xuICAgICAgICAgICAgbmV3UmFuZ2VTdGFydCA9IDA7XG4gICAgICAgICAgICBjdXJSYW5nZSA9IFtdO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBvbGRMaW5lICs9IGxpbmVzLmxlbmd0aDtcbiAgICAgICAgbmV3TGluZSArPSBsaW5lcy5sZW5ndGg7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gU3RlcCAyOiBlbGltaW5hdGUgdGhlIHRyYWlsaW5nIGBcXG5gIGZyb20gZWFjaCBsaW5lIG9mIGVhY2ggaHVuaywgYW5kLCB3aGVyZSBuZWVkZWQsIGFkZFxuICAgIC8vICAgICAgICAgXCJcXCBObyBuZXdsaW5lIGF0IGVuZCBvZiBmaWxlXCIuXG4gICAgZm9yIChjb25zdCBodW5rIG9mIGh1bmtzKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGh1bmsubGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKGh1bmsubGluZXNbaV0uZW5kc1dpdGgoJ1xcbicpKSB7XG4gICAgICAgICAgaHVuay5saW5lc1tpXSA9IGh1bmsubGluZXNbaV0uc2xpY2UoMCwgLTEpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGh1bmsubGluZXMuc3BsaWNlKGkgKyAxLCAwLCAnXFxcXCBObyBuZXdsaW5lIGF0IGVuZCBvZiBmaWxlJyk7XG4gICAgICAgICAgaSsrOyAvLyBTa2lwIHRoZSBsaW5lIHdlIGp1c3QgYWRkZWQsIHRoZW4gY29udGludWUgaXRlcmF0aW5nXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgb2xkRmlsZU5hbWU6IG9sZEZpbGVOYW1lLCBuZXdGaWxlTmFtZTogbmV3RmlsZU5hbWUsXG4gICAgICBvbGRIZWFkZXI6IG9sZEhlYWRlciwgbmV3SGVhZGVyOiBuZXdIZWFkZXIsXG4gICAgICBodW5rczogaHVua3NcbiAgICB9O1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRQYXRjaChkaWZmKSB7XG4gIGlmIChBcnJheS5pc0FycmF5KGRpZmYpKSB7XG4gICAgcmV0dXJuIGRpZmYubWFwKGZvcm1hdFBhdGNoKS5qb2luKCdcXG4nKTtcbiAgfVxuXG4gIGNvbnN0IHJldCA9IFtdO1xuICBpZiAoZGlmZi5vbGRGaWxlTmFtZSA9PSBkaWZmLm5ld0ZpbGVOYW1lKSB7XG4gICAgcmV0LnB1c2goJ0luZGV4OiAnICsgZGlmZi5vbGRGaWxlTmFtZSk7XG4gIH1cbiAgcmV0LnB1c2goJz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0nKTtcbiAgcmV0LnB1c2goJy0tLSAnICsgZGlmZi5vbGRGaWxlTmFtZSArICh0eXBlb2YgZGlmZi5vbGRIZWFkZXIgPT09ICd1bmRlZmluZWQnID8gJycgOiAnXFx0JyArIGRpZmYub2xkSGVhZGVyKSk7XG4gIHJldC5wdXNoKCcrKysgJyArIGRpZmYubmV3RmlsZU5hbWUgKyAodHlwZW9mIGRpZmYubmV3SGVhZGVyID09PSAndW5kZWZpbmVkJyA/ICcnIDogJ1xcdCcgKyBkaWZmLm5ld0hlYWRlcikpO1xuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZGlmZi5odW5rcy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGh1bmsgPSBkaWZmLmh1bmtzW2ldO1xuICAgIC8vIFVuaWZpZWQgRGlmZiBGb3JtYXQgcXVpcms6IElmIHRoZSBjaHVuayBzaXplIGlzIDAsXG4gICAgLy8gdGhlIGZpcnN0IG51bWJlciBpcyBvbmUgbG93ZXIgdGhhbiBvbmUgd291bGQgZXhwZWN0LlxuICAgIC8vIGh0dHBzOi8vd3d3LmFydGltYS5jb20vd2VibG9ncy92aWV3cG9zdC5qc3A/dGhyZWFkPTE2NDI5M1xuICAgIGlmIChodW5rLm9sZExpbmVzID09PSAwKSB7XG4gICAgICBodW5rLm9sZFN0YXJ0IC09IDE7XG4gICAgfVxuICAgIGlmIChodW5rLm5ld0xpbmVzID09PSAwKSB7XG4gICAgICBodW5rLm5ld1N0YXJ0IC09IDE7XG4gICAgfVxuICAgIHJldC5wdXNoKFxuICAgICAgJ0BAIC0nICsgaHVuay5vbGRTdGFydCArICcsJyArIGh1bmsub2xkTGluZXNcbiAgICAgICsgJyArJyArIGh1bmsubmV3U3RhcnQgKyAnLCcgKyBodW5rLm5ld0xpbmVzXG4gICAgICArICcgQEAnXG4gICAgKTtcbiAgICByZXQucHVzaC5hcHBseShyZXQsIGh1bmsubGluZXMpO1xuICB9XG5cbiAgcmV0dXJuIHJldC5qb2luKCdcXG4nKSArICdcXG4nO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlVHdvRmlsZXNQYXRjaChvbGRGaWxlTmFtZSwgbmV3RmlsZU5hbWUsIG9sZFN0ciwgbmV3U3RyLCBvbGRIZWFkZXIsIG5ld0hlYWRlciwgb3B0aW9ucykge1xuICBpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdmdW5jdGlvbicpIHtcbiAgICBvcHRpb25zID0ge2NhbGxiYWNrOiBvcHRpb25zfTtcbiAgfVxuXG4gIGlmICghb3B0aW9ucz8uY2FsbGJhY2spIHtcbiAgICBjb25zdCBwYXRjaE9iaiA9IHN0cnVjdHVyZWRQYXRjaChvbGRGaWxlTmFtZSwgbmV3RmlsZU5hbWUsIG9sZFN0ciwgbmV3U3RyLCBvbGRIZWFkZXIsIG5ld0hlYWRlciwgb3B0aW9ucyk7XG4gICAgaWYgKCFwYXRjaE9iaikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICByZXR1cm4gZm9ybWF0UGF0Y2gocGF0Y2hPYmopO1xuICB9IGVsc2Uge1xuICAgIGNvbnN0IHtjYWxsYmFja30gPSBvcHRpb25zO1xuICAgIHN0cnVjdHVyZWRQYXRjaChcbiAgICAgIG9sZEZpbGVOYW1lLFxuICAgICAgbmV3RmlsZU5hbWUsXG4gICAgICBvbGRTdHIsXG4gICAgICBuZXdTdHIsXG4gICAgICBvbGRIZWFkZXIsXG4gICAgICBuZXdIZWFkZXIsXG4gICAgICB7XG4gICAgICAgIC4uLm9wdGlvbnMsXG4gICAgICAgIGNhbGxiYWNrOiBwYXRjaE9iaiA9PiB7XG4gICAgICAgICAgaWYgKCFwYXRjaE9iaikge1xuICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY2FsbGJhY2soZm9ybWF0UGF0Y2gocGF0Y2hPYmopKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICApO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVQYXRjaChmaWxlTmFtZSwgb2xkU3RyLCBuZXdTdHIsIG9sZEhlYWRlciwgbmV3SGVhZGVyLCBvcHRpb25zKSB7XG4gIHJldHVybiBjcmVhdGVUd29GaWxlc1BhdGNoKGZpbGVOYW1lLCBmaWxlTmFtZSwgb2xkU3RyLCBuZXdTdHIsIG9sZEhlYWRlciwgbmV3SGVhZGVyLCBvcHRpb25zKTtcbn1cblxuLyoqXG4gKiBTcGxpdCBgdGV4dGAgaW50byBhbiBhcnJheSBvZiBsaW5lcywgaW5jbHVkaW5nIHRoZSB0cmFpbGluZyBuZXdsaW5lIGNoYXJhY3RlciAod2hlcmUgcHJlc2VudClcbiAqL1xuZnVuY3Rpb24gc3BsaXRMaW5lcyh0ZXh0KSB7XG4gIGNvbnN0IGhhc1RyYWlsaW5nTmwgPSB0ZXh0LmVuZHNXaXRoKCdcXG4nKTtcbiAgY29uc3QgcmVzdWx0ID0gdGV4dC5zcGxpdCgnXFxuJykubWFwKGxpbmUgPT4gbGluZSArICdcXG4nKTtcbiAgaWYgKGhhc1RyYWlsaW5nTmwpIHtcbiAgICByZXN1bHQucG9wKCk7XG4gIH0gZWxzZSB7XG4gICAgcmVzdWx0LnB1c2gocmVzdWx0LnBvcCgpLnNsaWNlKDAsIC0xKSk7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUFBLEtBQUEsR0FBQUMsT0FBQTtBQUFBO0FBQUE7QUFBdUMsbUNBQUFDLFFBQUFDLENBQUEsc0NBQUFELE9BQUEsd0JBQUFFLE1BQUEsdUJBQUFBLE1BQUEsQ0FBQUMsUUFBQSxhQUFBRixDQUFBLGtCQUFBQSxDQUFBLGdCQUFBQSxDQUFBLFdBQUFBLENBQUEseUJBQUFDLE1BQUEsSUFBQUQsQ0FBQSxDQUFBRyxXQUFBLEtBQUFGLE1BQUEsSUFBQUQsQ0FBQSxLQUFBQyxNQUFBLENBQUFHLFNBQUEscUJBQUFKLENBQUEsS0FBQUQsT0FBQSxDQUFBQyxDQUFBO0FBQUEsU0FBQUssbUJBQUFDLEdBQUEsV0FBQUMsa0JBQUEsQ0FBQUQsR0FBQSxLQUFBRSxnQkFBQSxDQUFBRixHQUFBLEtBQUFHLDJCQUFBLENBQUFILEdBQUEsS0FBQUksa0JBQUE7QUFBQSxTQUFBQSxtQkFBQSxjQUFBQyxTQUFBO0FBQUEsU0FBQUYsNEJBQUFULENBQUEsRUFBQVksTUFBQSxTQUFBWixDQUFBLHFCQUFBQSxDQUFBLHNCQUFBYSxpQkFBQSxDQUFBYixDQUFBLEVBQUFZLE1BQUEsT0FBQUUsQ0FBQSxHQUFBQyxNQUFBLENBQUFYLFNBQUEsQ0FBQVksUUFBQSxDQUFBQyxJQUFBLENBQUFqQixDQUFBLEVBQUFrQixLQUFBLGFBQUFKLENBQUEsaUJBQUFkLENBQUEsQ0FBQUcsV0FBQSxFQUFBVyxDQUFBLEdBQUFkLENBQUEsQ0FBQUcsV0FBQSxDQUFBZ0IsSUFBQSxNQUFBTCxDQUFBLGNBQUFBLENBQUEsbUJBQUFNLEtBQUEsQ0FBQUMsSUFBQSxDQUFBckIsQ0FBQSxPQUFBYyxDQUFBLCtE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 diff --git a/deps/npm/node_modules/diff/lib/patch/line-endings.js b/deps/npm/node_modules/diff/lib/patch/line-endings.js new file mode 100644 index 00000000000000..8d00bd22030ab4 --- /dev/null +++ b/deps/npm/node_modules/diff/lib/patch/line-endings.js @@ -0,0 +1,176 @@ +/*istanbul ignore start*/ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isUnix = isUnix; +exports.isWin = isWin; +exports.unixToWin = unixToWin; +exports.winToUnix = winToUnix; +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +/*istanbul ignore end*/ +function unixToWin(patch) { + if (Array.isArray(patch)) { + return patch.map(unixToWin); + } + return ( + /*istanbul ignore start*/ + _objectSpread(_objectSpread({}, + /*istanbul ignore end*/ + patch), {}, { + hunks: patch.hunks.map(function (hunk) + /*istanbul ignore start*/ + { + return _objectSpread(_objectSpread({}, + /*istanbul ignore end*/ + hunk), {}, { + lines: hunk.lines.map(function (line, i) + /*istanbul ignore start*/ + { + var _hunk$lines; + return ( + /*istanbul ignore end*/ + line.startsWith('\\') || line.endsWith('\r') || + /*istanbul ignore start*/ + (_hunk$lines = + /*istanbul ignore end*/ + hunk.lines[i + 1]) !== null && _hunk$lines !== void 0 && + /*istanbul ignore start*/ + _hunk$lines + /*istanbul ignore end*/ + .startsWith('\\') ? line : line + '\r' + ); + }) + }); + }) + }) + ); +} +function winToUnix(patch) { + if (Array.isArray(patch)) { + return patch.map(winToUnix); + } + return ( + /*istanbul ignore start*/ + _objectSpread(_objectSpread({}, + /*istanbul ignore end*/ + patch), {}, { + hunks: patch.hunks.map(function (hunk) + /*istanbul ignore start*/ + { + return _objectSpread(_objectSpread({}, + /*istanbul ignore end*/ + hunk), {}, { + lines: hunk.lines.map(function (line) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + line.endsWith('\r') ? line.substring(0, line.length - 1) : line + ); + }) + }); + }) + }) + ); +} + +/** + * Returns true if the patch consistently uses Unix line endings (or only involves one line and has + * no line endings). + */ +function isUnix(patch) { + if (!Array.isArray(patch)) { + patch = [patch]; + } + return !patch.some(function (index) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + index.hunks.some(function (hunk) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + hunk.lines.some(function (line) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + !line.startsWith('\\') && line.endsWith('\r') + ); + }) + ); + }) + ); + }); +} + +/** + * Returns true if the patch uses Windows line endings and only Windows line endings. + */ +function isWin(patch) { + if (!Array.isArray(patch)) { + patch = [patch]; + } + return patch.some(function (index) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + index.hunks.some(function (hunk) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + hunk.lines.some(function (line) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + line.endsWith('\r') + ); + }) + ); + }) + ); + }) && patch.every(function (index) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + index.hunks.every(function (hunk) + /*istanbul ignore start*/ + { + return ( + /*istanbul ignore end*/ + hunk.lines.every(function (line, i) + /*istanbul ignore start*/ + { + var _hunk$lines2; + return ( + /*istanbul ignore end*/ + line.startsWith('\\') || line.endsWith('\r') || + /*istanbul ignore start*/ + ((_hunk$lines2 = + /*istanbul ignore end*/ + hunk.lines[i + 1]) === null || _hunk$lines2 === void 0 ? void 0 : + /*istanbul ignore start*/ + _hunk$lines2 + /*istanbul ignore end*/ + .startsWith('\\')) + ); + }) + ); + }) + ); + }); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ1bml4VG9XaW4iLCJwYXRjaCIsIkFycmF5IiwiaXNBcnJheSIsIm1hcCIsIl9vYmplY3RTcHJlYWQiLCJodW5rcyIsImh1bmsiLCJsaW5lcyIsImxpbmUiLCJpIiwiX2h1bmskbGluZXMiLCJzdGFydHNXaXRoIiwiZW5kc1dpdGgiLCJ3aW5Ub1VuaXgiLCJzdWJzdHJpbmciLCJsZW5ndGgiLCJpc1VuaXgiLCJzb21lIiwiaW5kZXgiLCJpc1dpbiIsImV2ZXJ5IiwiX2h1bmskbGluZXMyIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BhdGNoL2xpbmUtZW5kaW5ncy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gdW5peFRvV2luKHBhdGNoKSB7XG4gIGlmIChBcnJheS5pc0FycmF5KHBhdGNoKSkge1xuICAgIHJldHVybiBwYXRjaC5tYXAodW5peFRvV2luKTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgLi4ucGF0Y2gsXG4gICAgaHVua3M6IHBhdGNoLmh1bmtzLm1hcChodW5rID0+ICh7XG4gICAgICAuLi5odW5rLFxuICAgICAgbGluZXM6IGh1bmsubGluZXMubWFwKFxuICAgICAgICAobGluZSwgaSkgPT5cbiAgICAgICAgICAobGluZS5zdGFydHNXaXRoKCdcXFxcJykgfHwgbGluZS5lbmRzV2l0aCgnXFxyJykgfHwgaHVuay5saW5lc1tpICsgMV0/LnN0YXJ0c1dpdGgoJ1xcXFwnKSlcbiAgICAgICAgICAgID8gbGluZVxuICAgICAgICAgICAgOiBsaW5lICsgJ1xccidcbiAgICAgIClcbiAgICB9KSlcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHdpblRvVW5peChwYXRjaCkge1xuICBpZiAoQXJyYXkuaXNBcnJheShwYXRjaCkpIHtcbiAgICByZXR1cm4gcGF0Y2gubWFwKHdpblRvVW5peCk7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIC4uLnBhdGNoLFxuICAgIGh1bmtzOiBwYXRjaC5odW5rcy5tYXAoaHVuayA9PiAoe1xuICAgICAgLi4uaHVuayxcbiAgICAgIGxpbmVzOiBodW5rLmxpbmVzLm1hcChsaW5lID0+IGxpbmUuZW5kc1dpdGgoJ1xccicpID8gbGluZS5zdWJzdHJpbmcoMCwgbGluZS5sZW5ndGggLSAxKSA6IGxpbmUpXG4gICAgfSkpXG4gIH07XG59XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIHRoZSBwYXRjaCBjb25zaXN0ZW50bHkgdXNlcyBVbml4IGxpbmUgZW5kaW5ncyAob3Igb25seSBpbnZvbHZlcyBvbmUgbGluZSBhbmQgaGFzXG4gKiBubyBsaW5lIGVuZGluZ3MpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNVbml4KHBhdGNoKSB7XG4gIGlmICghQXJyYXkuaXNBcnJheShwYXRjaCkpIHsgcGF0Y2ggPSBbcGF0Y2hdOyB9XG4gIHJldHVybiAhcGF0Y2guc29tZShcbiAgICBpbmRleCA9PiBpbmRleC5odW5rcy5zb21lKFxuICAgICAgaHVuayA9PiBodW5rLmxpbmVzLnNvbWUoXG4gICAgICAgIGxpbmUgPT4gIWxpbmUuc3RhcnRzV2l0aCgnXFxcXCcpICYmIGxpbmUuZW5kc1dpdGgoJ1xccicpXG4gICAgICApXG4gICAgKVxuICApO1xufVxuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgcGF0Y2ggdXNlcyBXaW5kb3dzIGxpbmUgZW5kaW5ncyBhbmQgb25seSBXaW5kb3dzIGxpbmUgZW5kaW5ncy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzV2luKHBhdGNoKSB7XG4gIGlmICghQXJyYXkuaXNBcnJheShwYXRjaCkpIHsgcGF0Y2ggPSBbcGF0Y2hdOyB9XG4gIHJldHVybiBwYXRjaC5zb21lKGluZGV4ID0+IGluZGV4Lmh1bmtzLnNvbWUoaHVuayA9PiBodW5rLmxpbmVzLnNvbWUobGluZSA9PiBsaW5lLmVuZHNXaXRoKCdcXHInKSkpKVxuICAgICYmIHBhdGNoLmV2ZXJ5KFxuICAgICAgaW5kZXggPT4gaW5kZXguaHVua3MuZXZlcnkoXG4gICAgICAgIGh1bmsgPT4gaHVuay5saW5lcy5ldmVyeShcbiAgICAgICAgICAobGluZSwgaSkgPT4gbGluZS5zdGFydHNXaXRoKCdcXFxcJykgfHwgbGluZS5lbmRzV2l0aCgnXFxyJykgfHwgaHVuay5saW5lc1tpICsgMV0/LnN0YXJ0c1dpdGgoJ1xcXFwnKVxuICAgICAgICApXG4gICAgICApXG4gICAgKTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBTyxTQUFTQSxTQUFTQSxDQUFDQyxLQUFLLEVBQUU7RUFDL0IsSUFBSUMsS0FBSyxDQUFDQyxPQUFPLENBQUNGLEtBQUssQ0FBQyxFQUFFO0lBQ3hCLE9BQU9BLEtBQUssQ0FBQ0csR0FBRyxDQUFDSixTQUFTLENBQUM7RUFDN0I7RUFFQTtJQUFBO0lBQUFLLGFBQUEsQ0FBQUEsYUFBQTtJQUFBO0lBQ0tKLEtBQUs7TUFDUkssS0FBSyxFQUFFTCxLQUFLLENBQUNLLEtBQUssQ0FBQ0YsR0FBRyxDQUFDLFVBQUFHLElBQUk7TUFBQTtNQUFBO1FBQUEsT0FBQUYsYUFBQSxDQUFBQSxhQUFBO1FBQUE7UUFDdEJFLElBQUk7VUFDUEMsS0FBSyxFQUFFRCxJQUFJLENBQUNDLEtBQUssQ0FBQ0osR0FBRyxDQUNuQixVQUFDSyxJQUFJLEVBQUVDLENBQUM7VUFBQTtVQUFBO1lBQUEsSUFBQUMsV0FBQTtZQUFBO2NBQUE7Y0FDTEYsSUFBSSxDQUFDRyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUlILElBQUksQ0FBQ0ksUUFBUSxDQUFDLElBQUksQ0FBQztjQUFBO2NBQUEsQ0FBQUYsV0FBQTtjQUFBO2NBQUlKLElBQUksQ0FBQ0MsS0FBSyxDQUFDRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLGNBQUFDLFdBQUE7Y0FBakI7Y0FBQUE7Y0FBQTtjQUFBLENBQW1CQyxVQUFVLENBQUMsSUFBSSxDQUFDLEdBQ2hGSCxJQUFJLEdBQ0pBLElBQUksR0FBRztZQUFJO1VBQUEsQ0FDbkI7UUFBQztNQUFBLENBQ0Q7SUFBQztFQUFBO0FBRVA7QUFFTyxTQUFTSyxTQUFTQSxDQUFDYixLQUFL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 diff --git a/deps/npm/node_modules/diff/lib/patch/merge.js b/deps/npm/node_modules/diff/lib/patch/merge.js index b46faaaba8e8b1..fead4e011df0df 100644 --- a/deps/npm/node_modules/diff/lib/patch/merge.js +++ b/deps/npm/node_modules/diff/lib/patch/merge.js @@ -6,71 +6,63 @@ Object.defineProperty(exports, "__esModule", { }); exports.calcLineCount = calcLineCount; exports.merge = merge; - /*istanbul ignore end*/ var /*istanbul ignore start*/ _create = require("./create") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _parse = require("./parse") /*istanbul ignore end*/ ; - var /*istanbul ignore start*/ _array = require("../util/array") /*istanbul ignore end*/ ; - /*istanbul ignore start*/ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } - function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } - function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } - +function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /*istanbul ignore end*/ function calcLineCount(hunk) { - /*istanbul ignore start*/ - var _calcOldNewLineCount = - /*istanbul ignore end*/ - calcOldNewLineCount(hunk.lines), - oldLines = _calcOldNewLineCount.oldLines, - newLines = _calcOldNewLineCount.newLines; - + var + /*istanbul ignore start*/ + _calcOldNewLineCount = + /*istanbul ignore end*/ + calcOldNewLineCount(hunk.lines), + /*istanbul ignore start*/ + /*istanbul ignore end*/ + oldLines = _calcOldNewLineCount.oldLines, + /*istanbul ignore start*/ + /*istanbul ignore end*/ + newLines = _calcOldNewLineCount.newLines; if (oldLines !== undefined) { hunk.oldLines = oldLines; } else { delete hunk.oldLines; } - if (newLines !== undefined) { hunk.newLines = newLines; } else { delete hunk.newLines; } } - function merge(mine, theirs, base) { mine = loadPatch(mine, base); theirs = loadPatch(theirs, base); - var ret = {}; // For index we just let it pass through as it doesn't have any necessary meaning. + var ret = {}; + + // For index we just let it pass through as it doesn't have any necessary meaning. // Leaving sanity checks on this to the API consumer that may know more about the // meaning in their own context. - if (mine.index || theirs.index) { ret.index = mine.index || theirs.index; } - if (mine.newFileName || theirs.newFileName) { if (!fileNameChanged(mine)) { // No header or no change in ours, use theirs (and ours if theirs does not exist) @@ -92,21 +84,18 @@ function merge(mine, theirs, base) { ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); } } - ret.hunks = []; var mineIndex = 0, - theirsIndex = 0, - mineOffset = 0, - theirsOffset = 0; - + theirsIndex = 0, + mineOffset = 0, + theirsOffset = 0; while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { var mineCurrent = mine.hunks[mineIndex] || { - oldStart: Infinity - }, - theirsCurrent = theirs.hunks[theirsIndex] || { - oldStart: Infinity - }; - + oldStart: Infinity + }, + theirsCurrent = theirs.hunks[theirsIndex] || { + oldStart: Infinity + }; if (hunkBefore(mineCurrent, theirsCurrent)) { // This patch does not overlap with any of the others, yay. ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); @@ -132,10 +121,8 @@ function merge(mine, theirs, base) { ret.hunks.push(mergedHunk); } } - return ret; } - function loadPatch(param, base) { if (typeof param === 'string') { if (/^@@/m.test(param) || /^Index:/m.test(param)) { @@ -143,7 +130,6 @@ function loadPatch(param, base) { /*istanbul ignore start*/ (0, /*istanbul ignore end*/ - /*istanbul ignore start*/ _parse /*istanbul ignore end*/ @@ -154,16 +140,13 @@ function loadPatch(param, base) { (param)[0] ); } - if (!base) { throw new Error('Must provide a base reference or pass in a patch'); } - return ( /*istanbul ignore start*/ (0, /*istanbul ignore end*/ - /*istanbul ignore start*/ _create /*istanbul ignore end*/ @@ -174,14 +157,11 @@ function loadPatch(param, base) { (undefined, undefined, base, param) ); } - return param; } - function fileNameChanged(patch) { return patch.newFileName && patch.newFileName !== patch.oldFileName; } - function selectField(index, mine, theirs) { if (mine === theirs) { return mine; @@ -193,11 +173,9 @@ function selectField(index, mine, theirs) { }; } } - function hunkBefore(test, check) { return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; } - function cloneHunk(hunk, offset) { return { oldStart: hunk.oldStart, @@ -207,42 +185,38 @@ function cloneHunk(hunk, offset) { lines: hunk.lines }; } - function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { // This will generally result in a conflicted hunk, but there are cases where the context // is the only overlap where we can successfully merge the content here. var mine = { - offset: mineOffset, - lines: mineLines, - index: 0 - }, - their = { - offset: theirOffset, - lines: theirLines, - index: 0 - }; // Handle any leading content + offset: mineOffset, + lines: mineLines, + index: 0 + }, + their = { + offset: theirOffset, + lines: theirLines, + index: 0 + }; + // Handle any leading content insertLeading(hunk, mine, their); - insertLeading(hunk, their, mine); // Now in the overlap content. Scan through and select the best changes from each. + insertLeading(hunk, their, mine); + // Now in the overlap content. Scan through and select the best changes from each. while (mine.index < mine.lines.length && their.index < their.lines.length) { var mineCurrent = mine.lines[mine.index], - theirCurrent = their.lines[their.index]; - + theirCurrent = their.lines[their.index]; if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { // Both modified ... mutualChange(hunk, mine, their); } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { /*istanbul ignore start*/ var _hunk$lines; - /*istanbul ignore end*/ // Mine inserted - /*istanbul ignore start*/ - /*istanbul ignore end*/ - /*istanbul ignore start*/ (_hunk$lines = /*istanbul ignore end*/ @@ -258,14 +232,10 @@ function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { /*istanbul ignore start*/ var _hunk$lines2; - /*istanbul ignore end*/ // Theirs inserted - /*istanbul ignore start*/ - /*istanbul ignore end*/ - /*istanbul ignore start*/ (_hunk$lines2 = /*istanbul ignore end*/ @@ -293,25 +263,22 @@ function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { // Context mismatch conflict(hunk, collectChange(mine), collectChange(their)); } - } // Now push anything that may be remaining - + } + // Now push anything that may be remaining insertTrailing(hunk, mine); insertTrailing(hunk, their); calcLineCount(hunk); } - function mutualChange(hunk, mine, their) { var myChanges = collectChange(mine), - theirChanges = collectChange(their); - + theirChanges = collectChange(their); if (allRemoves(myChanges) && allRemoves(theirChanges)) { // Special case for remove changes that are supersets of one another if ( /*istanbul ignore start*/ (0, /*istanbul ignore end*/ - /*istanbul ignore start*/ _array /*istanbul ignore end*/ @@ -322,13 +289,9 @@ function mutualChange(hunk, mine, their) { (myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { /*istanbul ignore start*/ var _hunk$lines3; - /*istanbul ignore end*/ - /*istanbul ignore start*/ - /*istanbul ignore end*/ - /*istanbul ignore start*/ (_hunk$lines3 = /*istanbul ignore end*/ @@ -341,13 +304,11 @@ function mutualChange(hunk, mine, their) { _toConsumableArray( /*istanbul ignore end*/ myChanges)); - return; } else if ( /*istanbul ignore start*/ (0, /*istanbul ignore end*/ - /*istanbul ignore start*/ _array /*istanbul ignore end*/ @@ -358,13 +319,9 @@ function mutualChange(hunk, mine, their) { (theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { /*istanbul ignore start*/ var _hunk$lines4; - /*istanbul ignore end*/ - /*istanbul ignore start*/ - /*istanbul ignore end*/ - /*istanbul ignore start*/ (_hunk$lines4 = /*istanbul ignore end*/ @@ -377,14 +334,12 @@ function mutualChange(hunk, mine, their) { _toConsumableArray( /*istanbul ignore end*/ theirChanges)); - return; } } else if ( /*istanbul ignore start*/ (0, /*istanbul ignore end*/ - /*istanbul ignore start*/ _array /*istanbul ignore end*/ @@ -395,13 +350,9 @@ function mutualChange(hunk, mine, their) { (myChanges, theirChanges)) { /*istanbul ignore start*/ var _hunk$lines5; - /*istanbul ignore end*/ - /*istanbul ignore start*/ - /*istanbul ignore end*/ - /*istanbul ignore start*/ (_hunk$lines5 = /*istanbul ignore end*/ @@ -414,27 +365,19 @@ function mutualChange(hunk, mine, their) { _toConsumableArray( /*istanbul ignore end*/ myChanges)); - return; } - conflict(hunk, myChanges, theirChanges); } - function removal(hunk, mine, their, swap) { var myChanges = collectChange(mine), - theirChanges = collectContext(their, myChanges); - + theirChanges = collectContext(their, myChanges); if (theirChanges.merged) { /*istanbul ignore start*/ var _hunk$lines6; - /*istanbul ignore end*/ - /*istanbul ignore start*/ - /*istanbul ignore end*/ - /*istanbul ignore start*/ (_hunk$lines6 = /*istanbul ignore end*/ @@ -451,7 +394,6 @@ function removal(hunk, mine, their, swap) { conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); } } - function conflict(hunk, mine, their) { hunk.conflict = true; hunk.lines.push({ @@ -460,7 +402,6 @@ function conflict(hunk, mine, their) { theirs: their }); } - function insertLeading(hunk, insert, their) { while (insert.offset < their.offset && insert.index < insert.lines.length) { var line = insert.lines[insert.index++]; @@ -468,25 +409,22 @@ function insertLeading(hunk, insert, their) { insert.offset++; } } - function insertTrailing(hunk, insert) { while (insert.index < insert.lines.length) { var line = insert.lines[insert.index++]; hunk.lines.push(line); } } - function collectChange(state) { var ret = [], - operation = state.lines[state.index][0]; - + operation = state.lines[state.index][0]; while (state.index < state.lines.length) { - var line = state.lines[state.index]; // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. + var line = state.lines[state.index]; + // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. if (operation === '-' && line[0] === '+') { operation = '+'; } - if (operation === line[0]) { ret.push(line); state.index++; @@ -494,39 +432,35 @@ function collectChange(state) { break; } } - return ret; } - function collectContext(state, matchChanges) { var changes = [], - merged = [], - matchIndex = 0, - contextChanges = false, - conflicted = false; - + merged = [], + matchIndex = 0, + contextChanges = false, + conflicted = false; while (matchIndex < matchChanges.length && state.index < state.lines.length) { var change = state.lines[state.index], - match = matchChanges[matchIndex]; // Once we've hit our add, then we are done + match = matchChanges[matchIndex]; + // Once we've hit our add, then we are done if (match[0] === '+') { break; } - contextChanges = contextChanges || change[0] !== ' '; merged.push(match); - matchIndex++; // Consume any additions in the other block as a conflict to attempt - // to pull in the remaining context after this + matchIndex++; + // Consume any additions in the other block as a conflict to attempt + // to pull in the remaining context after this if (change[0] === '+') { conflicted = true; - while (change[0] === '+') { changes.push(change); change = state.lines[++state.index]; } } - if (match.substr(1) === change.substr(1)) { changes.push(change); state.index++; @@ -534,44 +468,35 @@ function collectContext(state, matchChanges) { conflicted = true; } } - if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { conflicted = true; } - if (conflicted) { return changes; } - while (matchIndex < matchChanges.length) { merged.push(matchChanges[matchIndex++]); } - return { merged: merged, changes: changes }; } - function allRemoves(changes) { return changes.reduce(function (prev, change) { return prev && change[0] === '-'; }, true); } - function skipRemoveSuperset(state, removeChanges, delta) { for (var i = 0; i < delta; i++) { var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); - if (state.lines[state.index + i] !== ' ' + changeContent) { return false; } } - state.index += delta; return true; } - function calcOldNewLineCount(lines) { var oldLines = 0; var newLines = 0; @@ -579,7 +504,6 @@ function calcOldNewLineCount(lines) { if (typeof line !== 'string') { var myCount = calcOldNewLineCount(line.mine); var theirCount = calcOldNewLineCount(line.theirs); - if (oldLines !== undefined) { if (myCount.oldLines === theirCount.oldLines) { oldLines += myCount.oldLines; @@ -587,7 +511,6 @@ function calcOldNewLineCount(lines) { oldLines = undefined; } } - if (newLines !== undefined) { if (myCount.newLines === theirCount.newLines) { newLines += myCount.newLines; @@ -599,7 +522,6 @@ function calcOldNewLineCount(lines) { if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { newLines++; } - if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { oldLines++; } @@ -610,4 +532,4 @@ function calcOldNewLineCount(lines) { newLines: newLines }; } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9tZXJnZS5qcyJdLCJuYW1lcyI6WyJjYWxjTGluZUNvdW50IiwiaHVuayIsImNhbGNPbGROZXdMaW5lQ291bnQiLCJsaW5lcyIsIm9sZExpbmVzIiwibmV3TGluZXMiLCJ1bmRlZmluZWQiLCJtZXJnZSIsIm1pbmUiLCJ0aGVpcnMiLCJiYXNlIiwibG9hZFBhdGNoIiwicmV0IiwiaW5kZXgiLCJuZXdGaWxlTmFtZSIsImZpbGVOYW1lQ2hhbmdlZCIsIm9sZEZpbGVOYW1lIiwib2xkSGVhZGVyIiwibmV3SGVhZGVyIiwic2VsZWN0RmllbGQiLCJodW5rcyIsIm1pbmVJbmRleCIsInRoZWlyc0luZGV4IiwibWluZU9mZnNldCIsInRoZWlyc09mZnNldCIsImxlbmd0aCIsIm1pbmVDdXJyZW50Iiwib2xkU3RhcnQiLCJJbmZpbml0eSIsInRoZWlyc0N1cnJlbnQiLCJodW5rQmVmb3JlIiwicHVzaCIsImNsb25lSHVuayIsIm1lcmdlZEh1bmsiLCJNYXRoIiwibWluIiwibmV3U3RhcnQiLCJtZXJnZUxpbmVzIiwicGFyYW0iLCJ0ZXN0IiwicGFyc2VQYXRjaCIsIkVycm9yIiwic3RydWN0dXJlZFBhdGNoIiwicGF0Y2giLCJjb25mbGljdCIsImNoZWNrIiwib2Zmc2V0IiwibWluZUxpbmVzIiwidGhlaXJPZmZzZXQiLCJ0aGVpckxpbmVzIiwidGhlaXIiLCJpbnNlcnRMZWFkaW5nIiwidGhlaXJDdXJyZW50IiwibXV0dWFsQ2hhbmdlIiwiY29sbGVjdENoYW5nZSIsInJlbW92YWwiLCJpbnNlcnRUcmFpbGluZyIsIm15Q2hhbmdlcyIsInRoZWlyQ2hhbmdlcyIsImFsbFJlbW92ZXMiLCJhcnJheVN0YXJ0c1dpdGgiLCJza2lwUmVtb3ZlU3VwZXJzZXQiLCJhcnJheUVxdWFsIiwic3dhcCIsImNvbGxlY3RDb250ZXh0IiwibWVyZ2VkIiwiaW5zZXJ0IiwibGluZSIsInN0YXRlIiwib3BlcmF0aW9uIiwibWF0Y2hDaGFuZ2VzIiwiY2hhbmdlcyIsIm1hdGNoSW5kZXgiLCJjb250ZXh0Q2hhbmdlcyIsImNvbmZsaWN0ZWQiLCJjaGFuZ2UiLCJtYXRjaCIsInN1YnN0ciIsInJlZHVjZSIsInByZXYiLCJyZW1vdmVDaGFuZ2VzIiwiZGVsdGEiLCJpIiwiY2hhbmdlQ29udGVudCIsImZvckVhY2giLCJteUNvdW50IiwidGhlaXJDb3VudCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7O0FBQ0E7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFFQTtBQUFBO0FBQUE7QUFBQTtBQUFBOzs7Ozs7Ozs7Ozs7Ozs7QUFFTyxTQUFTQSxhQUFULENBQXVCQyxJQUF2QixFQUE2QjtBQUFBO0FBQUE7QUFBQTtBQUNMQyxFQUFBQSxtQkFBbUIsQ0FBQ0QsSUFBSSxDQUFDRSxLQUFOLENBRGQ7QUFBQSxNQUMzQkMsUUFEMkIsd0JBQzNCQSxRQUQyQjtBQUFBLE1BQ2pCQyxRQURpQix3QkFDakJBLFFBRGlCOztBQUdsQyxNQUFJRCxRQUFRLEtBQUtFLFNBQWpCLEVBQTRCO0FBQzFCTCxJQUFBQSxJQUFJLENBQUNHLFFBQUwsR0FBZ0JBLFFBQWhCO0FBQ0QsR0FGRCxNQUVPO0FBQ0wsV0FBT0gsSUFBSSxDQUFDRyxRQUFaO0FBQ0Q7O0FBRUQsTUFBSUMsUUFBUSxLQUFLQyxTQUFqQixFQUE0QjtBQUMxQkwsSUFBQUEsSUFBSSxDQUFDSSxRQUFMLEdBQWdCQSxRQUFoQjtBQUNELEdBRkQsTUFFTztBQUNMLFdBQU9KLElBQUksQ0FBQ0ksUUFBWjtBQUNEO0FBQ0Y7O0FBRU0sU0FBU0UsS0FBVCxDQUFlQyxJQUFmLEVBQXFCQyxNQUFyQixFQUE2QkMsSUFBN0IsRUFBbUM7QUFDeENGLEVBQUFBLElBQUksR0FBR0csU0FBUyxDQUFDSCxJQUFELEVBQU9FLElBQVAsQ0FBaEI7QUFDQUQsRUFBQUEsTUFBTSxHQUFHRSxTQUFTLENBQUNGLE1BQUQsRUFBU0MsSUFBVCxDQUFsQjtBQUVBLE1BQUlFLEdBQUcsR0FBRyxFQUFWLENBSndD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QUFKLEVBQW9FO0FBQ2xFL0QsUUFBQUEsUUFBUTtBQUNUO0FBQ0Y7QUFDRixHQTVCRDtBQThCQSxTQUFPO0FBQUNBLElBQUFBLFFBQVEsRUFBUkEsUUFBRDtBQUFXQyxJQUFBQSxRQUFRLEVBQVJBO0FBQVgsR0FBUDtBQUNEIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtzdHJ1Y3R1cmVkUGF0Y2h9IGZyb20gJy4vY3JlYXRlJztcbmltcG9ydCB7cGFyc2VQYXRjaH0gZnJvbSAnLi9wYXJzZSc7XG5cbmltcG9ydCB7YXJyYXlFcXVhbCwgYXJyYXlTdGFydHNXaXRofSBmcm9tICcuLi91dGlsL2FycmF5JztcblxuZXhwb3J0IGZ1bmN0aW9uIGNhbGNMaW5lQ291bnQoaHVuaykge1xuICBjb25zdCB7b2xkTGluZXMsIG5ld0xpbmVzfSA9IGNhbGNPbGROZXdMaW5lQ291bnQoaHVuay5saW5lcyk7XG5cbiAgaWYgKG9sZExpbmVzICE9PSB1bmRlZmluZWQpIHtcbiAgICBodW5rLm9sZExpbmVzID0gb2xkTGluZXM7XG4gIH0gZWxzZSB7XG4gICAgZGVsZXRlIGh1bmsub2xkTGluZXM7XG4gIH1cblxuICBpZiAobmV3TGluZXMgIT09IHVuZGVmaW5lZCkge1xuICAgIGh1bmsubmV3TGluZXMgPSBuZXdMaW5lcztcbiAgfSBlbHNlIHtcbiAgICBkZWxldGUgaHVuay5uZXdMaW5lcztcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gbWVyZ2UobWluZSwgdGhlaXJzLCBiYXNlKSB7XG4gIG1pbmUgPSBsb2FkUGF0Y2gobWluZSwgYmFzZSk7XG4gIHRoZWlycyA9IGxvYWRQYXRjaCh0aGVpcnMsIGJhc2UpO1xuXG4gIGxldCByZXQgPSB7fTtcblxuICAvLyBGb3IgaW5kZXggd2UganVzdCBsZXQgaXQgcGFzcyB0aHJvdWdoIGFzIGl0IGRvZXNuJ3QgaGF2ZSBhbnkgbmVjZXNzYXJ5IG1lYW5pbmcuXG4gIC8vIExlYXZpbmcgc2FuaXR5IGNoZWNrcyBvbiB0aGlzIHRvIHRoZSBBUEkgY29uc3VtZXIgdGhhdCBtYXkga25vdyBtb3JlIGFib3V0IHRoZVxuICAvLyBtZWFuaW5nIGluIHRoZWlyIG93biBjb250ZXh0LlxuICBpZiAobWluZS5pbmRleCB8fCB0aGVpcnMuaW5kZXgpIHtcbiAgICByZXQuaW5kZXggPSBtaW5lLmluZGV4IHx8IHRoZWlycy5pbmRleDtcbiAgfVxuXG4gIGlmIChtaW5lLm5ld0ZpbGVOYW1lIHx8IHRoZWlycy5uZXdGaWxlTmFtZSkge1xuICAgIGlmICghZmlsZU5hbWVDaGFuZ2VkKG1pbmUpKSB7XG4gICAgICAvLyBObyBoZWFkZXIgb3Igbm8gY2hhbmdlIGluIG91cnMsIHVzZSB0aGVpcnMgKGFuZCBvdXJzIGlmIHRoZWlycyBkb2VzIG5vdCBleGlzdClcbiAgICAgIHJldC5vbGRGaWxlTmFtZSA9IHRoZWlycy5vbGRGaWxlTmFtZSB8fCBtaW5lLm9sZEZpbGVOYW1lO1xuICAgICAgcmV0Lm5ld0ZpbGVOYW1lID0gdGhlaXJzLm5ld0ZpbGVOYW1lIHx8IG1pbmUubmV3RmlsZU5hbWU7XG4gICAgICByZXQub2xkSGVhZGVyID0gdGhlaXJzLm9sZEhlYWRlciB8fCBtaW5lLm9sZEhlYWRlcjtcbiAgICAgIHJldC5uZXdIZWFkZXIgPSB0aGVpcnMubmV3SGVhZGVyIHx8IG1pbmUubmV3SGVhZGVyO1xuICAgIH0gZWxzZSBpZiAoIWZpbGVOYW1lQ2hhbmdlZCh0aGVpcnMpKSB7XG4gICAgICAvLyBObyBoZWFkZXIgb3Igbm8gY2hhbmdlIGluIHRoZWlycywgdXNlIG91cnNcbiAgICAgIHJldC5vbGRGaWxlTmFtZSA9IG1pbmUub2xkRmlsZU5hbWU7XG4gICAgICByZXQubmV3RmlsZU5hbWUgPSBtaW5lLm5ld0ZpbGVOYW1lO1xuICAgICAgcmV0Lm9sZEhlYWRlciA9IG1pbmUub2xkSGVhZGVyO1xuICAgICAgcmV0Lm5ld0hlYWRlciA9IG1pbmUubmV3SGVhZGVyO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBCb3RoIGNoYW5nZWQuLi4gZmlndXJlIGl0IG91dFxuICAgICAgcmV0Lm9sZEZpbGVOYW1lID0gc2VsZWN0RmllbGQocmV0LCBtaW5lLm9sZEZpbGVOYW1lLCB0aGVpcnMub2xkRmlsZU5hbWUpO1xuICAgICAgcmV0Lm5ld0ZpbGVOYW1lID0gc2VsZWN0RmllbGQocmV0LCBtaW5lLm5ld0ZpbGVOYW1lLCB0aGVpcnMubmV3RmlsZU5hbWUpO1xuICAgICAgcmV0Lm9sZEhlYWRlciA9IHNlbGVjdEZpZWxkKHJldCwgbWluZS5vbGRIZWFkZXIsIHRoZWlycy5vbGRIZWFkZXIpO1xuICAgICAgcmV0Lm5ld0hlYWRlciA9IHNlbGVjdEZpZWxkKHJldCwgbWluZS5uZXdIZWFkZXIsIHRoZWlycy5uZXdIZWFkZXIpO1xuICAgIH1cbiAgfVxuXG4gIHJldC5odW5rcyA9IFtdO1xuXG4gIGxldCBtaW5lSW5kZXggPSAwLFxuICAgICAgdGhlaXJzSW5kZXggPSAwLFxuICAgICAgbWluZU9mZnNldCA9IDAsXG4gICAgICB0aGVpcnNPZmZzZXQgPSAwO1xuXG4gIHdoaWxlIChtaW5lSW5kZXggPCBtaW5lLmh1bmtzLmxlbmd0aCB8fCB0aGVpcnNJbmRleCA8IHRoZWlycy5odW5rcy5sZW5ndGgpIHtcbiAgICBsZXQgbWluZUN1cnJlbnQgPSBtaW5lLmh1bmtzW21pbmVJbmRleF0gfHwge29sZFN0YXJ0OiBJbmZpbml0eX0sXG4gICAgICAgIHRoZWlyc0N1cnJlbnQgPSB0aGVpcnMuaHVua3NbdGhlaXJzSW5kZXhdIHx8IHtvbGRTdGFydDogSW5maW5pdHl9O1xuXG4gICAgaWYgKGh1bmtCZWZvcmUobWluZUN1cnJlbnQsIHRoZWlyc0N1cnJlbnQpKSB7XG4gICAgICAvLyBUaGlzIHBhdGNoIGRvZXMgbm90IG92ZXJsYXAgd2l0aCBhbnkgb2YgdGhlIG90aGVycywgeWF5LlxuICAgICAgcmV0Lmh1bmtzLnB1c2goY2xvbmVIdW5rKG1pbmVDdXJyZW50LCBtaW5lT2Zmc2V0KSk7XG4gICAgICBtaW5lSW5kZXgrKztcbiAgICAgIHRoZWlyc09mZnNldCArPSBtaW5lQ3VycmVudC5uZXdMaW5lcyAtIG1pbmVDdXJyZW50Lm9sZExpbmVzO1xuICAgIH0gZWxzZSBpZiAoaHVua0JlZm9yZSh0aGVpcnNDdXJyZW50LCBtaW5lQ3VycmVudCkpIHtcbiAgICAgIC8vIFRoaXMgcGF0Y2ggZG9lcyBub3Qgb3ZlcmxhcCB3aXRoIGFueSBvZiB0aGUgb3RoZXJzLCB5YXkuXG4gICAgICByZXQuaHVua3MucHVzaChjbG9uZUh1bmsodGhlaXJzQ3VycmVudCwgdGhlaXJzT2Zmc2V0KSk7XG4gICAgICB0aGVpcnNJbmRleCsrO1xuICAgICAgbWluZU9mZnNldCArPSB0aGVpcnNDdXJyZW50Lm5ld0xpbmVzIC0gdGhlaXJzQ3VycmVudC5vbGRMaW5lcztcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gT3ZlcmxhcCwgbWVyZ2UgYXMgYmVzdCB3ZSBjYW5cbiAgICAgIGxldCBtZXJnZWRIdW5rID0ge1xuICAgICAgICBvbGRTdGFydDogTWF0aC5taW4obWluZUN1cnJlbnQub2xkU3RhcnQsIHRoZWlyc0N1cnJlbnQub2xkU3RhcnQpLFxuICAgICAgICBvbGRMaW5lczogMCxcbiAgICAgICAgbmV3U3RhcnQ6IE1hdGgubWluKG1pbmVDdXJyZW50Lm5ld1N0YXJ0ICsgbWluZU9mZnNldCwgdGhlaXJzQ3VycmVudC5vbGRTdGFydCArIHRoZWlyc09mZnNldCksXG4gICAgICAgIG5ld0xpbmVzOiAwLFxuICAgICAgICBsaW5lczogW11cbiAgICAgIH07XG4gICAgICBtZXJnZUxpbmVzKG1lcmdlZEh1bmssIG1pbmVDdXJyZW50Lm9sZFN0YXJ0LCBtaW5lQ3VycmVudC5saW5lcywgdGhlaXJzQ3VycmVudC5vbGRTdGFydCwgdGhlaXJzQ3VycmVudC5saW5lcyk7XG4gICAgICB0aGVpcnNJbmRleCsrO1xuICAgICAgbWluZUluZGV4Kys7XG5cbiAgICAgIHJldC5odW5rcy5wdXNoKG1lcmdlZEh1bmspO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXQ7XG59XG5cbmZ1bmN0aW9uIGxvYWRQYXRjaChwYXJhbSwgYmFzZSkge1xuICBpZiAodHlwZW9mIHBhcmFtID09PSAnc3RyaW5nJykge1xuICAgIGlmICgoL15AQC9tKS50ZXN0KHBhcmFtKSB8fCAoKC9eSW5kZXg6L20pLnRlc3QocGFyYW0pKSkge1xuICAgICAgcmV0dXJuIHBhcnNlUGF0Y2gocGFyYW0pWzBdO1xuICAgIH1cblxuICAgIGlmICghYmFzZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdNdXN0IHByb3ZpZGUgYSBiYXNlIHJlZmVyZW5jZSBvciBwYXNzIGluIGEgcGF0Y2gnKTtcbiAgICB9XG4gICAgcmV0dXJuIHN0cnVjdHVyZWRQYXRjaCh1bmRlZmluZWQsIHVuZGVmaW5lZCwgYmFzZSwgcGFyYW0pO1xuICB9XG5cbiAgcmV0dXJuIHBhcmFtO1xufVxuXG5mdW5jdGlvbiBmaWxlTmFtZUNoYW5nZWQocGF0Y2gpIHtcbiAgcmV0dXJuIHBhdGNoLm5ld0ZpbGVOYW1lICYmIHBhdGNoLm5ld0ZpbGVOYW1lICE9PSBwYXRjaC5vbGRGaWxlTmFtZTtcbn1cblxuZnVuY3Rpb24gc2VsZWN0RmllbGQoaW5kZXgsIG1pbmUsIHRoZWlycykge1xuICBpZiAobWluZSA9PT0gdGhlaXJzKSB7XG4gICAgcmV0dXJuIG1pbmU7XG4gIH0gZWxzZSB7XG4gICAgaW5kZXguY29uZmxpY3QgPSB0cnVlO1xuICAgIHJldHVybiB7bWluZSwgdGhlaXJzfTtcbiAgfVxufVxuXG5mdW5jdGlvbiBodW5rQmVmb3JlKHRlc3QsIGNoZWNrKSB7XG4gIHJldHVybiB0ZXN0Lm9sZFN0YXJ0IDwgY2hlY2sub2xkU3RhcnRcbiAgICAmJiAodGVzdC5vbGRTdGFydCArIHRlc3Qub2xkTGluZXMpIDwgY2hlY2sub2xkU3RhcnQ7XG59XG5cbmZ1bmN0aW9uIGNsb25lSHVuayhodW5rLCBvZmZzZXQpIHtcbiAgcmV0dXJuIHtcbiAgICBvbGRTdGFydDogaHVuay5vbGRTdGFydCwgb2xkTGluZXM6IGh1bmsub2xkTGluZXMsXG4gICAgbmV3U3RhcnQ6IGh1bmsubmV3U3RhcnQgKyBvZmZzZXQsIG5ld0xpbmVzOiBodW5rLm5ld0xpbmVzLFxuICAgIGxpbmVzOiBodW5rLmxpbmVzXG4gIH07XG59XG5cbmZ1bmN0aW9uIG1lcmdlTGluZXMoaHVuaywgbWluZU9mZnNldCwgbWluZUxpbmVzLCB0aGVpck9mZnNldCwgdGhlaXJMaW5lcykge1xuICAvLyBUaGlzIHdpbGwgZ2VuZXJhbGx5IHJlc3VsdCBpbiBhIGNvbmZsaWN0ZWQgaHVuaywgYnV0IHRoZXJlIGFyZSBjYXNlcyB3aGVyZSB0aGUgY29udGV4dFxuICAvLyBpcyB0aGUgb25seSBvdmVybGFwIHdoZXJlIHdlIGNhbiBzdWNjZXNzZnVsbHkgbWVyZ2UgdGhlIGNvbnRlbnQgaGVyZS5cbiAgbGV0IG1pbmUgPSB7b2Zmc2V0OiBtaW5lT2Zmc2V0LCBsaW5lczogbWluZUxpbmVzLCBpbmRleDogMH0sXG4gICAgICB0aGVpciA9IHtvZmZzZXQ6IHRoZWlyT2Zmc2V0LCBsaW5lczogdGhlaXJMaW5lcywgaW5kZXg6IDB9O1xuXG4gIC8vIEhhbmRsZSBhbnkgbGVhZGluZyBjb250ZW50XG4gIGluc2VydExlYWRpbmcoaHVuaywgbWluZSwgdGhlaXIpO1xuICBpbnNlcnRMZWFkaW5nKGh1bmssIHRoZWlyLCBtaW5lKTtcblxuICAvLyBOb3cgaW4gdGhlIG92ZXJsYXAgY29udGVudC4gU2NhbiB0aHJvdWdoIGFuZCBzZWxlY3QgdGhlIGJlc3QgY2hhbmdlcyBmcm9tIGVhY2guXG4gIHdoaWxlIChtaW5lLmluZGV4IDwgbWluZS5saW5lcy5sZW5ndGggJiYgdGhlaXIuaW5kZXggPCB0aGVpci5saW5lcy5sZW5ndGgpIHtcbiAgICBsZXQgbWluZUN1cnJlbnQgPSBtaW5lLmxpbmVzW21pbmUuaW5kZXhdLFxuICAgICAgICB0aGVpckN1cnJlbnQgPSB0aGVpci5saW5lc1t0aGVpci5pbmRleF07XG5cbiAgICBpZiAoKG1pbmVDdXJyZW50WzBdID09PSAnLScgfHwgbWluZUN1cnJlbnRbMF0gPT09ICcrJylcbiAgICAgICAgJiYgKHRoZWlyQ3VycmVudFswXSA9PT0gJy0nIHx8IHRoZWlyQ3VycmVudFswXSA9PT0gJysnKSkge1xuICAgICAgLy8gQm90aCBtb2RpZmllZCAuLi5cbiAgICAgIG11dHVhbENoYW5nZShodW5rLCBtaW5lLCB0aGVpcik7XG4gICAgfSBlbHNlIGlmIChtaW5lQ3VycmVudFswXSA9PT0gJysnICYmIHRoZWlyQ3VycmVudFswXSA9PT0gJyAnKSB7XG4gICAgICAvLyBNaW5lIGluc2VydGVkXG4gICAgICBodW5rLmxpbmVzLnB1c2goLi4uIGNvbGxlY3RDaGFuZ2UobWluZSkpO1xuICAgIH0gZWxzZSBpZiAodGhlaXJDdXJyZW50WzBdID09PSAnKycgJiYgbWluZUN1cnJlbnRbMF0gPT09ICcgJykge1xuICAgICAgLy8gVGhlaXJzIGluc2VydGVkXG4gICAgICBodW5rLmxpbmVzLnB1c2goLi4uIGNvbGxlY3RDaGFuZ2UodGhlaXIpKTtcbiAgICB9IGVsc2UgaWYgKG1pbmVDdXJyZW50WzBdID09PSAnLScgJiYgdGhlaXJDdXJyZW50WzBdID09PSAnICcpIHtcbiAgICAgIC8vIE1pbmUgcmVtb3ZlZCBvciBlZGl0ZWRcbiAgICAgIHJlbW92YWwoaHVuaywgbWluZSwgdGhlaXIpO1xuICAgIH0gZWxzZSBpZiAodGhlaXJDdXJyZW50WzBdID09PSAnLScgJiYgbWluZUN1cnJlbnRbMF0gPT09ICcgJykge1xuICAgICAgLy8gVGhlaXIgcmVtb3ZlZCBvciBlZGl0ZWRcbiAgICAgIHJlbW92YWwoaHVuaywgdGhlaXIsIG1pbmUsIHRydWUpO1xuICAgIH0gZWxzZSBpZiAobWluZUN1cnJlbnQgPT09IHRoZWlyQ3VycmVudCkge1xuICAgICAgLy8gQ29udGV4dCBpZGVudGl0eVxuICAgICAgaHVuay5saW5lcy5wdXNoKG1pbmVDdXJyZW50KTtcbiAgICAgIG1pbmUuaW5kZXgrKztcbiAgICAgIHRoZWlyLmluZGV4Kys7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIENvbnRleHQgbWlzbWF0Y2hcbiAgICAgIGNvbmZsaWN0KGh1bmssIGNvbGxlY3RDaGFuZ2UobWluZSksIGNvbGxlY3RDaGFuZ2UodGhlaXIpKTtcbiAgICB9XG4gIH1cblxuICAvLyBOb3cgcHVzaCBhbnl0aGluZyB0aGF0IG1heSBiZSByZW1haW5pbmdcbiAgaW5zZXJ0VHJhaWxpbmcoaHVuaywgbWluZSk7XG4gIGluc2VydFRyYWlsaW5nKGh1bmssIHRoZWlyKTtcblxuICBjYWxjTGluZUNvdW50KGh1bmspO1xufVxuXG5mdW5jdGlvbiBtdXR1YWxDaGFuZ2UoaHVuaywgbWluZSwgdGhlaXIpIHtcbiAgbGV0IG15Q2hhbmdlcyA9IGNvbGxlY3RDaGFuZ2UobWluZSksXG4gICAgICB0aGVpckNoYW5nZXMgPSBjb2xsZWN0Q2hhbmdlKHRoZWlyKTtcblxuICBpZiAoYWxsUmVtb3ZlcyhteUNoYW5nZXMpICYmIGFsbFJlbW92ZXModGhlaXJDaGFuZ2VzKSkge1xuICAgIC8vIFNwZWNpYWwgY2FzZSBmb3IgcmVtb3ZlIGNoYW5nZXMgdGhhdCBhcmUgc3VwZXJzZXRzIG9mIG9uZSBhbm90aGVyXG4gICAgaWYgKGFycmF5U3RhcnRzV2l0aChteUNoYW5nZXMsIHRoZWlyQ2hhbmdlcylcbiAgICAgICAgJiYgc2tpcFJlbW92ZVN1cGVyc2V0KHRoZWlyLCBteUNoYW5nZXMsIG15Q2hhbmdlcy5sZW5ndGggLSB0aGVpckNoYW5nZXMubGVuZ3RoKSkge1xuICAgICAgaHVuay5saW5lcy5wdXNoKC4uLiBteUNoYW5nZXMpO1xuICAgICAgcmV0dXJuO1xuICAgIH0gZWxzZSBpZiAoYXJyYXlTdGFydHNXaXRoKHRoZWlyQ2hhbmdlcywgbXlDaGFuZ2VzKVxuICAgICAgICAmJiBza2lwUmVtb3ZlU3VwZXJzZXQobWluZSwgdGhlaXJDaGFuZ2VzLCB0aGVpckNoYW5nZXMubGVuZ3RoIC0gbXlDaGFuZ2VzLmxlbmd0aCkpIHtcbiAgICAgIGh1bmsubGluZXMucHVzaCguLi4gdGhlaXJDaGFuZ2VzKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gIH0gZWxzZSBpZiAoYXJyYXlFcXVhbChteUNoYW5nZXMsIHRoZWlyQ2hhbmdlcykpIHtcbiAgICBodW5rLmxpbmVzLnB1c2goLi4uIG15Q2hhbmdlcyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uZmxpY3QoaHVuaywgbXlDaGFuZ2VzLCB0aGVpckNoYW5nZXMpO1xufVxuXG5mdW5jdGlvbiByZW1vdmFsKGh1bmssIG1pbmUsIHRoZWlyLCBzd2FwKSB7XG4gIGxldCBteUNoYW5nZXMgPSBjb2xsZWN0Q2hhbmdlKG1pbmUpLFxuICAgICAgdGhlaXJDaGFuZ2VzID0gY29sbGVjdENvbnRleHQodGhlaXIsIG15Q2hhbmdlcyk7XG4gIGlmICh0aGVpckNoYW5nZXMubWVyZ2VkKSB7XG4gICAgaHVuay5saW5lcy5wdXNoKC4uLiB0aGVpckNoYW5nZXMubWVyZ2VkKTtcbiAgfSBlbHNlIHtcbiAgICBjb25mbGljdChodW5rLCBzd2FwID8gdGhlaXJDaGFuZ2VzIDogbXlDaGFuZ2VzLCBzd2FwID8gbXlDaGFuZ2VzIDogdGhlaXJDaGFuZ2VzKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBjb25mbGljdChodW5rLCBtaW5lLCB0aGVpcikge1xuICBodW5rLmNvbmZsaWN0ID0gdHJ1ZTtcbiAgaHVuay5saW5lcy5wdXNoKHtcbiAgICBjb25mbGljdDogdHJ1ZSxcbiAgICBtaW5lOiBtaW5lLFxuICAgIHRoZWlyczogdGhlaXJcbiAgfSk7XG59XG5cbmZ1bmN0aW9uIGluc2VydExlYWRpbmcoaHVuaywgaW5zZXJ0LCB0aGVpcikge1xuICB3aGlsZSAoaW5zZXJ0Lm9mZnNldCA8IHRoZWlyLm9mZnNldCAmJiBpbnNlcnQuaW5kZXggPCBpbnNlcnQubGluZXMubGVuZ3RoKSB7XG4gICAgbGV0IGxpbmUgPSBpbnNlcnQubGluZXNbaW5zZXJ0LmluZGV4KytdO1xuICAgIGh1bmsubGluZXMucHVzaChsaW5lKTtcbiAgICBpbnNlcnQub2Zmc2V0Kys7XG4gIH1cbn1cbmZ1bmN0aW9uIGluc2VydFRyYWlsaW5nKGh1bmssIGluc2VydCkge1xuICB3aGlsZSAoaW5zZXJ0LmluZGV4IDwgaW5zZXJ0LmxpbmVzLmxlbmd0aCkge1xuICAgIGxldCBsaW5lID0gaW5zZXJ0LmxpbmVzW2luc2VydC5pbmRleCsrXTtcbiAgICBodW5rLmxpbmVzLnB1c2gobGluZSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gY29sbGVjdENoYW5nZShzdGF0ZSkge1xuICBsZXQgcmV0ID0gW10sXG4gICAgICBvcGVyYXRpb24gPSBzdGF0ZS5saW5lc1tzdGF0ZS5pbmRleF1bMF07XG4gIHdoaWxlIChzdGF0ZS5pbmRleCA8IHN0YXRlLmxpbmVzLmxlbmd0aCkge1xuICAgIGxldCBsaW5lID0gc3RhdGUubGluZXNbc3RhdGUuaW5kZXhdO1xuXG4gICAgLy8gR3JvdXAgYWRkaXRpb25zIHRoYXQgYXJlIGltbWVkaWF0ZWx5IGFmdGVyIHN1YnRyYWN0aW9ucyBhbmQgdHJlYXQgdGhlbSBhcyBvbmUgXCJhdG9taWNcIiBtb2RpZnkgY2hhbmdlLlxuICAgIGlmIChvcGVyYXRpb24gPT09ICctJyAmJiBsaW5lWzBdID09PSAnKycpIHtcbiAgICAgIG9wZXJhdGlvbiA9ICcrJztcbiAgICB9XG5cbiAgICBpZiAob3BlcmF0aW9uID09PSBsaW5lWzBdKSB7XG4gICAgICByZXQucHVzaChsaW5lKTtcbiAgICAgIHN0YXRlLmluZGV4Kys7XG4gICAgfSBlbHNlIHtcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXQ7XG59XG5mdW5jdGlvbiBjb2xsZWN0Q29udGV4dChzdGF0ZSwgbWF0Y2hDaGFuZ2VzKSB7XG4gIGxldCBjaGFuZ2VzID0gW10sXG4gICAgICBtZXJnZWQgPSBbXSxcbiAgICAgIG1hdGNoSW5kZXggPSAwLFxuICAgICAgY29udGV4dENoYW5nZXMgPSBmYWxzZSxcbiAgICAgIGNvbmZsaWN0ZWQgPSBmYWxzZTtcbiAgd2hpbGUgKG1hdGNoSW5kZXggPCBtYXRjaENoYW5nZXMubGVuZ3RoXG4gICAgICAgICYmIHN0YXRlLmluZGV4IDwgc3RhdGUubGluZXMubGVuZ3RoKSB7XG4gICAgbGV0IGNoYW5nZSA9IHN0YXRlLmxpbmVzW3N0YXRlLmluZGV4XSxcbiAgICAgICAgbWF0Y2ggPSBtYXRjaENoYW5nZXNbbWF0Y2hJbmRleF07XG5cbiAgICAvLyBPbmNlIHdlJ3ZlIGhpdCBvdXIgYWRkLCB0aGVuIHdlIGFyZSBkb25lXG4gICAgaWYgKG1hdGNoWzBdID09PSAnKycpIHtcbiAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIGNvbnRleHRDaGFuZ2VzID0gY29udGV4dENoYW5nZXMgfHwgY2hhbmdlWzBdICE9PSAnICc7XG5cbiAgICBtZXJnZWQucHVzaChtYXRjaCk7XG4gICAgbWF0Y2hJbmRleCsrO1xuXG4gICAgLy8gQ29uc3VtZSBhbnkgYWRkaXRpb25zIGluIHRoZSBvdGhlciBibG9jayBhcyBhIGNvbmZsaWN0IHRvIGF0dGVtcHRcbiAgICAvLyB0byBwdWxsIGluIHRoZSByZW1haW5pbmcgY29udGV4dCBhZnRlciB0aGlzXG4gICAgaWYgKGNoYW5nZVswXSA9PT0gJysnKSB7XG4gICAgICBjb25mbGljdGVkID0gdHJ1ZTtcblxuICAgICAgd2hpbGUgKGNoYW5nZVswXSA9PT0gJysnKSB7XG4gICAgICAgIGNoYW5nZXMucHVzaChjaGFuZ2UpO1xuICAgICAgICBjaGFuZ2UgPSBzdGF0ZS5saW5lc1srK3N0YXRlLmluZGV4XTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAobWF0Y2guc3Vic3RyKDEpID09PSBjaGFuZ2Uuc3Vic3RyKDEpKSB7XG4gICAgICBjaGFuZ2VzLnB1c2goY2hhbmdlKTtcbiAgICAgIHN0YXRlLmluZGV4Kys7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbmZsaWN0ZWQgPSB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIGlmICgobWF0Y2hDaGFuZ2VzW21hdGNoSW5kZXhdIHx8ICcnKVswXSA9PT0gJysnXG4gICAgICAmJiBjb250ZXh0Q2hhbmdlcykge1xuICAgIGNvbmZsaWN0ZWQgPSB0cnVlO1xuICB9XG5cbiAgaWYgKGNvbmZsaWN0ZWQpIHtcbiAgICByZXR1cm4gY2hhbmdlcztcbiAgfVxuXG4gIHdoaWxlIChtYXRjaEluZGV4IDwgbWF0Y2hDaGFuZ2VzLmxlbmd0aCkge1xuICAgIG1lcmdlZC5wdXNoKG1hdGNoQ2hhbmdlc1ttYXRjaEluZGV4KytdKTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgbWVyZ2VkLFxuICAgIGNoYW5nZXNcbiAgfTtcbn1cblxuZnVuY3Rpb24gYWxsUmVtb3ZlcyhjaGFuZ2VzKSB7XG4gIHJldHVybiBjaGFuZ2VzLnJlZHVjZShmdW5jdGlvbihwcmV2LCBjaGFuZ2UpIHtcbiAgICByZXR1cm4gcHJldiAmJiBjaGFuZ2VbMF0gPT09ICctJztcbiAgfSwgdHJ1ZSk7XG59XG5mdW5jdGlvbiBza2lwUmVtb3ZlU3VwZXJzZXQoc3RhdGUsIHJlbW92ZUNoYW5nZXMsIGRlbHRhKSB7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZGVsdGE7IGkrKykge1xuICAgIGxldCBjaGFuZ2VDb250ZW50ID0gcmVtb3ZlQ2hhbmdlc1tyZW1vdmVDaGFuZ2VzLmxlbmd0aCAtIGRlbHRhICsgaV0uc3Vic3RyKDEpO1xuICAgIGlmIChzdGF0ZS5saW5lc1tzdGF0ZS5pbmRleCArIGldICE9PSAnICcgKyBjaGFuZ2VDb250ZW50KSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgc3RhdGUuaW5kZXggKz0gZGVsdGE7XG4gIHJldHVybiB0cnVlO1xufVxuXG5mdW5jdGlvbiBjYWxjT2xkTmV3TGluZUNvdW50KGxpbmVzKSB7XG4gIGxldCBvbGRMaW5lcyA9IDA7XG4gIGxldCBuZXdMaW5lcyA9IDA7XG5cbiAgbGluZXMuZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgaWYgKHR5cGVvZiBsaW5lICE9PSAnc3RyaW5nJykge1xuICAgICAgbGV0IG15Q291bnQgPSBjYWxjT2xkTmV3TGluZUNvdW50KGxpbmUubWluZSk7XG4gICAgICBsZXQgdGhlaXJDb3VudCA9IGNhbGNPbGROZXdMaW5lQ291bnQobGluZS50aGVpcnMpO1xuXG4gICAgICBpZiAob2xkTGluZXMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAobXlDb3VudC5vbGRMaW5lcyA9PT0gdGhlaXJDb3VudC5vbGRMaW5lcykge1xuICAgICAgICAgIG9sZExpbmVzICs9IG15Q291bnQub2xkTGluZXM7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgb2xkTGluZXMgPSB1bmRlZmluZWQ7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKG5ld0xpbmVzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKG15Q291bnQubmV3TGluZXMgPT09IHRoZWlyQ291bnQubmV3TGluZXMpIHtcbiAgICAgICAgICBuZXdMaW5lcyArPSBteUNvdW50Lm5ld0xpbmVzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG5ld0xpbmVzID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmIChuZXdMaW5lcyAhPT0gdW5kZWZpbmVkICYmIChsaW5lWzBdID09PSAnKycgfHwgbGluZVswXSA9PT0gJyAnKSkge1xuICAgICAgICBuZXdMaW5lcysrO1xuICAgICAgfVxuICAgICAgaWYgKG9sZExpbmVzICE9PSB1bmRlZmluZWQgJiYgKGxpbmVbMF0gPT09ICctJyB8fCBsaW5lWzBdID09PSAnICcpKSB7XG4gICAgICAgIG9sZExpbmVzKys7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxuICByZXR1cm4ge29sZExpbmVzLCBuZXdMaW5lc307XG59XG4iXX0= +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfY3JlYXRlIiwicmVxdWlyZSIsIl9wYXJzZSIsIl9hcnJheSIsIl90b0NvbnN1bWFibGVBcnJheSIsImFyciIsIl9hcnJheVdpdGhvdXRIb2xlcyIsIl9pdGVyYWJsZVRvQXJyYXkiLCJfdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkiLCJfbm9uSXRlcmFibGVTcHJlYWQiLCJUeXBlRXJyb3IiLCJvIiwibWluTGVuIiwiX2FycmF5TGlrZVRvQXJyYXkiLCJuIiwiT2JqZWN0IiwicHJvdG90eXBlIiwidG9TdHJpbmciLCJjYWxsIiwic2xpY2UiLCJjb25zdHJ1Y3RvciIsIm5hbWUiLCJBcnJheSIsImZyb20iLCJ0ZXN0IiwiaXRlciIsIlN5bWJvbCIsIml0ZXJhdG9yIiwiaXNBcnJheSIsImxlbiIsImxlbmd0aCIsImkiLCJhcnIyIiwiY2FsY0xpbmVDb3VudCIsImh1bmsiLCJfY2FsY09sZE5ld0xpbmVDb3VudCIsImNhbGNPbGROZXdMaW5lQ291bnQiLCJsaW5lcyIsIm9sZExpbmVzIiwibmV3TGluZXMiLCJ1bmRlZmluZWQiLCJtZXJnZSIsIm1pbmUiLCJ0aGVpcnMiLCJiYXNlIiwibG9hZFBhdGNoIiwicmV0IiwiaW5kZXgiLCJuZXdGaWxlTmFtZSIsImZpbGVOYW1lQ2hhbmdlZCIsIm9sZEZpbGVOYW1lIiwib2xkSGVhZGVyIiwibmV3SGVhZGVyIiwic2VsZWN0RmllbGQiLCJodW5rcyIsIm1pbmVJbmRleCIsInRoZWlyc0luZGV4IiwibWluZU9mZnNldCIsInRoZWlyc09mZnNldCIsIm1pbmVDdXJyZW50Iiwib2xkU3RhcnQiLCJJbmZpbml0eSIsInRoZWlyc0N1cnJlbnQiLCJodW5rQmVmb3JlIiwicHVzaCIsImNsb25lSHVuayIsIm1lcmdlZEh1bmsiLCJNYXRoIiwibWluIiwibmV3U3RhcnQiLCJtZXJnZUxpbmVzIiwicGFyYW0iLCJwYXJzZVBhdGNoIiwiRXJyb3IiLCJzdHJ1Y3R1cmVkUGF0Y2giLCJwYXRjaCIsImNvbmZsaWN0IiwiY2hlY2siLCJvZmZzZXQiLCJtaW5lTGluZXMiLCJ0aGVpck9mZnNldCIsInRoZWlyTGluZXMiLCJ0aGVpciIsImluc2VydExlYWRpbmciLCJ0aGVpckN1cnJlbnQiLCJtdXR1YWxDaGFuZ2UiLCJfaHVuayRsaW5lcyIsImFwcGx5IiwiY29sbGVjdENoYW5nZSIsIl9odW5rJGxpbmVzMiIsInJlbW92YWwiLCJpbnNlcnRUcmFpbGluZyIsIm15Q2hhbmdlcyIsInRoZWlyQ2hhbmdlcyIsImFsbFJlbW92ZXMiLCJhcnJheVN0YXJ0c1dpdGgiLCJza2lwUmVtb3ZlU3VwZXJzZXQiLCJfaHVuayRsaW5lczMiLCJfaHVuayRsaW5lczQiLCJhcnJheUVxdWFsIiwiX2h1bmskbGluZXM1Iiwic3dhcCIsImNvbGxlY3RDb250ZXh0IiwibWVyZ2VkIiwiX2h1bmskbGluZXM2IiwiaW5zZXJ0IiwibGluZSIsInN0YXRlIiwib3BlcmF0aW9uIiwibWF0Y2hDaGFuZ2VzIiwiY2hhbmdlcyIsIm1hdGNoSW5kZXgiLCJjb250ZXh0Q2hhbmdlcyIsImNvbmZsaWN0ZWQiLCJjaGFuZ2UiLCJtYXRjaCIsInN1YnN0ciIsInJlZHVjZSIsInByZXYiLCJyZW1vdmVDaGFuZ2VzIiwiZGVsdGEiLCJjaGFuZ2VDb250ZW50IiwiZm9yRWFjaCIsIm15Q291bnQiLCJ0aGVpckNvdW50Il0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BhdGNoL21lcmdlLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7c3RydWN0dXJlZFBhdGNofSBmcm9tICcuL2NyZWF0ZSc7XG5pbXBvcnQge3BhcnNlUGF0Y2h9IGZyb20gJy4vcGFyc2UnO1xuXG5pbXBvcnQge2FycmF5RXF1YWwsIGFycmF5U3RhcnRzV2l0aH0gZnJvbSAnLi4vdXRpbC9hcnJheSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBjYWxjTGluZUNvdW50KGh1bmspIHtcbiAgY29uc3Qge29sZExpbmVzLCBuZXdMaW5lc30gPSBjYWxjT2xkTmV3TGluZUNvdW50KGh1bmsubGluZXMpO1xuXG4gIGlmIChvbGRMaW5lcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgaHVuay5vbGRMaW5lcyA9IG9sZExpbmVzO1xuICB9IGVsc2Uge1xuICAgIGRlbGV0ZSBodW5rLm9sZExpbmVzO1xuICB9XG5cbiAgaWYgKG5ld0xpbmVzICE9PSB1bmRlZmluZWQpIHtcbiAgICBodW5rLm5ld0xpbmVzID0gbmV3TGluZXM7XG4gIH0gZWxzZSB7XG4gICAgZGVsZXRlIGh1bmsubmV3TGluZXM7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlKG1pbmUsIHRoZWlycywgYmFzZSkge1xuICBtaW5lID0gbG9hZFBhdGNoKG1pbmUsIGJhc2UpO1xuICB0aGVpcnMgPSBsb2FkUGF0Y2godGhlaXJzLCBiYXNlKTtcblxuICBsZXQgcmV0ID0ge307XG5cbiAgLy8gRm9yIGluZGV4IHdlIGp1c3QgbGV0IGl0IHBhc3MgdGhyb3VnaCBhcyBpdCBkb2Vzbid0IGhhdmUgYW55IG5lY2Vzc2FyeSBtZWFuaW5nLlxuICAvLyBMZWF2aW5nIHNhbml0eSBjaGVja3Mgb24gdGhpcyB0byB0aGUgQVBJIGNvbnN1bWVyIHRoYXQgbWF5IGtub3cgbW9yZSBhYm91dCB0aGVcbiAgLy8gbWVhbmluZyBpbiB0aGVpciBvd24gY29udGV4dC5cbiAgaWYgKG1pbmUuaW5kZXggfHwgdGhlaXJzLmluZGV4KSB7XG4gICAgcmV0LmluZGV4ID0gbWluZS5pbmRleCB8fCB0aGVpcnMuaW5kZXg7XG4gIH1cblxuICBpZiAobWluZS5uZXdGaWxlTmFtZSB8fCB0aGVpcnMubmV3RmlsZU5hbWUpIHtcbiAgICBpZiAoIWZpbGVOYW1lQ2hhbmdlZChtaW5lKSkge1xuICAgICAgLy8gTm8gaGVhZGVyIG9yIG5vIGNoYW5nZSBpbiBvdXJzLCB1c2UgdGhlaXJzIChhbmQgb3VycyBpZiB0aGVpcnMgZG9lcyBub3QgZXhpc3QpXG4gICAgICByZXQub2xkRmlsZU5hbWUgPSB0aGVpcnMub2xkRmlsZU5hbWUgfHwgbWluZS5vbGRGaWxlTmFtZTtcbiAgICAgIHJldC5uZXdGaWxlTmFtZSA9IHRoZWlycy5uZXdGaWxlTmFtZSB8fCBtaW5lLm5ld0ZpbGVOYW1lO1xuICAgICAgcmV0Lm9sZEhlYWRlciA9IHRoZWlycy5vbGRIZWFkZXIgfHwgbWluZS5vbGRIZWFkZXI7XG4gICAgICByZXQubmV3SGVhZGVyID0gdGhlaXJzLm5ld0hlYWRlciB8fCBtaW5lLm5ld0hlYWRlcjtcbiAgICB9IGVsc2UgaWYgKCFmaWxlTmFtZUNoYW5nZWQodGhlaXJzKSkge1xuICAgICAgLy8gTm8gaGVhZGVyIG9yIG5vIGNoYW5nZSBpbiB0aGVpcnMsIHVzZSBvdXJzXG4gICAgICByZXQub2xkRmlsZU5hbWUgPSBtaW5lLm9sZEZpbGVOYW1lO1xuICAgICAgcmV0Lm5ld0ZpbGVOYW1lID0gbWluZS5uZXdGaWxlTmFtZTtcbiAgICAgIHJldC5vbGRIZWFkZXIgPSBtaW5lLm9sZEhlYWRlcjtcbiAgICAgIHJldC5uZXdIZWFkZXIgPSBtaW5lLm5ld0hlYWRlcjtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gQm90aCBjaGFuZ2VkLi4uIGZpZ3VyZSBpdCBvdXRcbiAgICAgIHJldC5vbGRGaWxlTmFtZSA9IHNlbGVjdEZpZWxkKHJldCwgbWluZS5vbGRGaWxlTmFtZSwgdGhlaXJzLm9sZEZpbGVOYW1lKTtcbiAgICAgIHJldC5uZXdGaWxlTmFtZSA9IHNlbGVjdEZpZWxkKHJldCwgbWluZS5uZXdGaWxlTmFtZSwgdGhlaXJzLm5ld0ZpbGVOYW1lKTtcbiAgICAgIHJldC5vbGRIZWFkZXIgPSBzZWxlY3RGaWVsZChyZXQsIG1pbmUub2xkSGVhZGVyLCB0aGVpcnMub2xkSGVhZGVyKTtcbiAgICAgIHJldC5uZXdIZWFkZXIgPSBzZWxlY3RGaWVsZChyZXQsIG1pbmUubmV3SGVhZGVyLCB0aGVpcnMubmV3SGVhZGVyKTtcbiAgICB9XG4gIH1cblxuICByZXQuaHVua3MgPSBbXTtcblxuICBsZXQgbWluZUluZGV4ID0gMCxcbiAgICAgIHRoZWlyc0luZGV4ID0gMCxcbiAgICAgIG1pbmVPZmZzZXQgPSAwLFxuICAgICAgdGhlaXJzT2Zmc2V0ID0gMDtcblxuICB3aGlsZSAobWluZUluZGV4IDwgbWluZS5odW5rcy5sZW5ndGggfHwgdGhlaXJzSW5kZXggPCB0aGVpcnMuaHVua3MubGVuZ3RoKSB7XG4gICAgbGV0IG1pbmVDdXJyZW50ID0gbWluZS5odW5rc1ttaW5lSW5kZXhdIHx8IHtvbGRTdGFydDogSW5maW5pdHl9LFxuICAgICAgICB0aGVpcnNDdXJyZW50ID0gdGhlaXJzLmh1bmtzW3RoZWlyc0luZGV4XSB8fCB7b2xkU3RhcnQ6IEluZmluaXR5fTtcblxuICAgIGlmIChodW5rQmVmb3JlKG1pbmVDdXJyZW50LCB0aGVpcnNDdXJyZW50KSkge1xuICAgICAgLy8gVGhpcyBwYXRjaCBkb2VzIG5vdCBvdmVybGFwIHdpdGggYW55IG9mIHRoZSBvdGhlcnMsIHlheS5cbiAgICAgIHJldC5odW5rcy5wdXNoKGNsb25lSHVuayhtaW5lQ3VycmVudCwgbWluZU9mZnNldCkpO1xuICAgICAgbWluZUluZGV4Kys7XG4gICAgICB0aGVpcnNPZmZzZXQgKz0gbWluZUN1cnJlbnQubmV3TGluZXMgLSBtaW5lQ3VycmVudC5vbGRMaW5lcztcbiAgICB9IGVsc2UgaWYgKGh1bmtCZWZvcmUodGhlaXJzQ3VycmVudCwgbWluZUN1cnJlbnQpKSB7XG4gICAgICAvLyBUaGlzIHBhdGNoIGRvZXMgbm90IG92ZXJsYXAgd2l0aCBhbnkgb2YgdGhlIG90aGVycywgeWF5LlxuICAgICAgcmV0Lmh1bmtzLnB1c2goY2xvbmVIdW5rKHRoZWlyc0N1cnJlbnQsIHRoZWlyc09mZnNldCkpO1xuICAgICAgdGhlaXJzSW5kZXgrKztcbiAgICAgIG1pbmVPZmZzZXQgKz0gdGhlaXJzQ3VycmVudC5uZXdMaW5lcyAtIHRoZWlyc0N1cnJlbnQub2xkTGluZXM7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIE92ZXJsYXAsIG1lcmdlIGFzIGJlc3Qgd2UgY2FuXG4gICAgICBsZXQgbWVyZ2VkSHVuayA9IHtcbiAgICAgICAgb2xkU3RhcnQ6IE1hdGgubWluKG1pbmVDdXJyZW50Lm9sZFN0YXJ0LCB0aGVpcnNDdXJyZW50Lm9sZFN0YXJ0KSxcbiAgICAgICAgb2xkTGluZXM6IDAsXG4gICAgICAgIG5ld1N0YXJ0OiBNYXRoLm1pbihtaW5lQ3VycmVudC5uZXdTdGFydCArIG1pbmVPZmZzZXQsIHRoZWlyc0N1cnJlbnQub2xkU3RhcnQgKyB0aGVpcnNPZmZzZXQpLFxuICAgICAgICBuZXdMaW5lczogMCxcbiAgICAgICAgbGluZXM6IFtdXG4gICAgICB9O1xuICAgICAgbWVyZ2VMaW5lcyhtZXJnZWRIdW5rLCBtaW5lQ3VycmVudC5vbGRTdGFydCwgbWluZUN1cnJlbnQubGluZXMsIHRoZWlyc0N1cnJlbnQub2xkU3RhcnQsIHRoZWlyc0N1cnJlbnQubGluZXMpO1xuICAgICAgdGhlaXJzSW5kZXgrKztcbiAgICAgIG1pbmVJbmRleCsrO1xuXG4gICAgICByZXQuaHVua3MucHVzaChtZXJnZWRIdW5rKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmV0O1xufVxuXG5mdW5jdGlvbiBsb2FkUGF0Y2gocGFyYW0sIGJhc2UpIHtcbiAgaWYgKHR5cGVvZiBwYXJhbSA9PT0gJ3N0cmluZycpIHtcbiAgICBpZiAoKC9eQEAvbSkudGVzdChwYXJhbSkgfHwgKCgvXkluZGV4Oi9tKS50ZXN0KHBhcmFtKSkpIHtcbiAgICAgIHJldHVybiBwYXJzZVBhdGNoKHBhcmFtKVswXTtcbiAgICB9XG5cbiAgICBpZiAoIWJhc2UpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignTXVzdCBwcm92aWRlIGEgYmFzZSByZWZlcmVuY2Ugb3IgcGFzcyBpbiBhIHBhdGNoJyk7XG4gICAgfVxuICAgIHJldHVybiBzdHJ1Y3R1cmVkUGF0Y2godW5kZWZpbmVkLCB1bmRlZmluZWQsIGJhc2UsIHBhcmFtKTtcbiAgfVxuXG4gIHJldHVybiBwYXJhbTtcbn1cblxuZnVuY3Rpb24gZmlsZU5hbWVDaGFuZ2VkKHBhdGNoKSB7XG4gIHJldHVybiBwYXRjaC5uZXdGaWxlTmFtZSAmJiBwYXRjaC5uZXdGaWxlTmFtZSAhPT0gcGF0Y2gub2xkRmlsZU5hbWU7XG59XG5cbmZ1bmN0aW9uIHNlbGVjdEZpZWxkKGluZGV4LCBtaW5lLCB0aGVpcnMpIHtcbiAgaWYgKG1pbmUgPT09IHRoZWlycykge1xuICAgIHJldHVybiBtaW5lO1xuICB9IGVsc2Uge1xuICAgIGluZGV4LmNvbmZsaWN0ID0gdHJ1ZTtcbiAgICByZXR1cm4ge21pbmUsIHRoZWlyc307XG4gIH1cbn1cblxuZnVuY3Rpb24gaHVua0JlZm9yZSh0ZXN0LCBjaGVjaykge1xuICByZXR1cm4gdGVzdC5vbGRTdGFydCA8IGNoZWNrLm9sZFN0YXJ0XG4gICAgJiYgKHRlc3Qub2xkU3RhcnQgKyB0ZXN0Lm9sZExpbmVzKSA8IGNoZWNrLm9sZFN0YXJ0O1xufVxuXG5mdW5jdGlvbiBjbG9uZUh1bmsoaHVuaywgb2Zmc2V0KSB7XG4gIHJldHVybiB7XG4gICAgb2xkU3RhcnQ6IGh1bmsub2xkU3RhcnQsIG9sZExpbmVzOiBodW5rLm9sZExpbmVzLFxuICAgIG5ld1N0YXJ0OiBodW5rLm5ld1N0YXJ0ICsgb2Zmc2V0LCBuZXdMaW5lczogaHVuay5uZXdMaW5lcyxcbiAgICBsaW5lczogaHVuay5saW5lc1xuICB9O1xufVxuXG5mdW5jdGlvbiBtZXJnZUxpbmVzKGh1bmssIG1pbmVPZmZzZXQsIG1pbmVMaW5lcywgdGhlaXJPZmZzZXQsIHRoZWlyTGluZXMpIHtcbiAgLy8gVGhpcyB3aWxsIGdlbmVyYWxseSByZXN1bHQgaW4gYSBjb25mbGljdGVkIGh1bmssIGJ1dCB0aGVyZSBhcmUgY2FzZXMgd2hlcmUgdGhlIGNvbnRleHRcbiAgLy8gaXMgdGhlIG9ubHkgb3ZlcmxhcCB3aGVyZSB3ZSBjYW4gc3VjY2Vzc2Z1bGx5IG1lcmdlIHRoZSBjb250ZW50IGhlcmUuXG4gIGxldCBtaW5lID0ge29mZnNldDogbWluZU9mZnNldCwgbGluZXM6IG1pbmVMaW5lcywgaW5kZXg6IDB9LFxuICAgICAgdGhlaXIgPSB7b2Zmc2V0OiB0aGVpck9mZnNldCwgbGluZXM6IHRoZWlyTGluZXMsIGluZGV4OiAwfTtcblxuICAvLyBIYW5kbGUgYW55IGxlYWRpbmcgY29udGVudFxuICBpbnNlcnRMZWFkaW5nKGh1bmssIG1pbmUsIHRoZWlyKTtcbiAgaW5zZXJ0TGVhZGluZyhodW5rLCB0aGVpciwgbWluZSk7XG5cbiAgLy8gTm93IGluIHRoZSBvdmVybGFwIGNvbnRlbnQuIFNjYW4gdGhyb3VnaCBhbmQgc2VsZWN0IHRoZSBiZXN0IGNoYW5nZXMgZnJvbSBlYWNoLlxuICB3aGlsZSAobWluZS5pbmRleCA8IG1pbmUubGluZXMubGVuZ3RoICYmIHRoZWlyLmluZGV4IDwgdGhlaXIubGluZXMubGVuZ3RoKSB7XG4gICAgbGV0IG1pbmVDdXJyZW50ID0gbWluZS5saW5lc1ttaW5lLmluZGV4XSxcbiAgICAgICAgdGhlaXJDdXJyZW50ID0gdGhlaXIubGluZXNbdGhlaXIuaW5kZXhdO1xuXG4gICAgaWYgKChtaW5lQ3VycmVudFswXSA9PT0gJy0nIHx8IG1pbmVDdXJyZW50WzBdID09PSAnKycpXG4gICAgICAgICYmICh0aGVpckN1cnJlbnRbMF0gPT09ICctJyB8fCB0aGVpckN1cnJlbnRbMF0gPT09ICcrJykpIHtcbiAgICAgIC8vIEJvdGggbW9kaWZpZWQgLi4uXG4gICAgICBtdXR1YWxDaGFuZ2UoaHVuaywgbWluZSwgdGhlaXIpO1xuICAgIH0gZWxzZSBpZiAobWluZUN1cnJlbnRbMF0gPT09ICcrJyAmJiB0aGVpckN1cnJlbnRbMF0gPT09ICcgJykge1xuICAgICAgLy8gTWluZSBpbnNlcnRlZFxuICAgICAgaHVuay5saW5lcy5wdXNoKC4uLiBjb2xsZWN0Q2hhbmdlKG1pbmUpKTtcbiAgICB9IGVsc2UgaWYgKHRoZWlyQ3VycmVudFswXSA9PT0gJysnICYmIG1pbmVDdXJyZW50WzBdID09PSAnICcpIHtcbiAgICAgIC8vIFRoZWlycyBpbnNlcnRlZFxuICAgICAgaHVuay5saW5lcy5wdXNoKC4uLiBjb2xsZWN0Q2hhbmdlKHRoZWlyKSk7XG4gICAgfSBlbHNlIGlmIChtaW5lQ3VycmVudFswXSA9PT0gJy0nICYmIHRoZWlyQ3VycmVudFswXSA9PT0gJyAnKSB7XG4gICAgICAvLyBNaW5lIHJlbW92ZWQgb3IgZWRpdGVkXG4gICAgICByZW1vdmFsKGh1bmssIG1pbmUsIHRoZWlyKTtcbiAgICB9IGVsc2UgaWYgKHRoZWlyQ3VycmVudFswXSA9PT0gJy0nICYmIG1pbmVDdXJyZW50WzBdID09PSAnICcpIHtcbiAgICAgIC8vIFRoZWlyIHJlbW92ZWQgb3IgZWRpdGVkXG4gICAgICByZW1vdmFsKGh1bmssIHRoZWlyLCBtaW5lLCB0cnVlKTtcbiAgICB9IGVsc2UgaWYgKG1pbmVDdXJyZW50ID09PSB0aGVpckN1cnJlbnQpIHtcbiAgICAgIC8vIENvbnRleHQgaWRlbnRpdHlcbiAgICAgIGh1bmsubGluZXMucHVzaChtaW5lQ3VycmVudCk7XG4gICAgICBtaW5lLmluZGV4Kys7XG4gICAgICB0aGVpci5pbmRleCsrO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBDb250ZXh0IG1pc21hdGNoXG4gICAgICBjb25mbGljdChodW5rLCBjb2xsZWN0Q2hhbmdlKG1pbmUpLCBjb2xsZWN0Q2hhbmdlKHRoZWlyKSk7XG4gICAgfVxuICB9XG5cbiAgLy8gTm93IHB1c2ggYW55dGhpbmcgdGhhdCBtYXkgYmUgcmVtYWluaW5nXG4gIGluc2VydFRyYWlsaW5nKGh1bmssIG1pbmUpO1xuICBpbnNlcnRUcmFpbGluZyhodW5rLCB0aGVpcik7XG5cbiAgY2FsY0xpbmVDb3VudChodW5rKTtcbn1cblxuZnVuY3Rpb24gbXV0dWFsQ2hhbmdlKGh1bmssIG1pbmUsIHRoZWlyKSB7XG4gIGxldCBteUNoYW5nZXMgPSBjb2xsZWN0Q2hhbmdlKG1pbmUpLFxuICAgICAgdGhlaXJDaGFuZ2VzID0gY29sbGVjdENoYW5nZSh0aGVpcik7XG5cbiAgaWYgKGFsbFJlbW92ZXMobXlDaGFuZ2VzKSAmJiBhbGxSZW1vdmVzKHRoZWlyQ2hhbmdlcykpIHtcbiAgICAvLyBTcGVjaWFsIGNhc2UgZm9yIHJlbW92ZSBjaGFuZ2VzIHRoYXQgYXJlIHN1cGVyc2V0cyBvZiBvbmUgYW5vdGhlclxuICAgIGlmIChhcnJheVN0YXJ0c1dpdGgobXlDaGFuZ2VzLCB0aGVpckNoYW5nZXMpXG4gICAgICAgICYmIHNraXBSZW1vdmVTdXBlcnNldCh0aGVpciwgbXlDaGFuZ2VzLCBteUNoYW5nZXMubGVuZ3RoIC0gdGhlaXJDaGFuZ2VzLmxlbmd0aCkpIHtcbiAgICAgIGh1bmsubGluZXMucHVzaCguLi4gbXlDaGFuZ2VzKTtcbiAgICAgIHJldHVybjtcbiAgICB9IGVsc2UgaWYgKGFycmF5U3RhcnRzV2l0aCh0aGVpckNoYW5nZXMsIG15Q2hhbmdlcylcbiAgICAgICAgJiYgc2tpcFJlbW92ZVN1cGVyc2V0KG1pbmUsIHRoZWlyQ2hhbmdlcywgdGhlaXJDaGFuZ2VzLmxlbmd0aCAtIG15Q2hhbmdlcy5sZW5ndGgpKSB7XG4gICAgICBodW5rLmxpbmVzLnB1c2goLi4uIHRoZWlyQ2hhbmdlcyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICB9IGVsc2UgaWYgKGFycmF5RXF1YWwobXlDaGFuZ2VzLCB0aGVpckNoYW5nZXMpKSB7XG4gICAgaHVuay5saW5lcy5wdXNoKC4uLiBteUNoYW5nZXMpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbmZsaWN0KGh1bmssIG15Q2hhbmdlcywgdGhlaXJDaGFuZ2VzKTtcbn1cblxuZnVuY3Rpb24gcmVtb3ZhbChodW5rLCBtaW5lLCB0aGVpciwgc3dhcCkge1xuICBsZXQgbXlDaGFuZ2VzID0gY29sbGVjdENoYW5nZShtaW5lKSxcbiAgICAgIHRoZWlyQ2hhbmdlcyA9IGNvbGxlY3RDb250ZXh0KHRoZWlyLCBteUNoYW5nZXMpO1xuICBpZiAodGhlaXJDaGFuZ2VzLm1lcmdlZCkge1xuICAgIGh1bmsubGluZXMucHVzaCguLi4gdGhlaXJDaGFuZ2VzLm1lcmdlZCk7XG4gIH0gZWxzZSB7XG4gICAgY29uZmxpY3QoaHVuaywgc3dhcCA/IHRoZWlyQ2hhbmdlcyA6IG15Q2hhbmdlcywgc3dhcCA/IG15Q2hhbmdlcyA6IHRoZWlyQ2hhbmdlcyk7XG4gIH1cbn1cblxuZnVuY3Rpb24gY29uZmxpY3QoaHVuaywgbWluZSwgdGhlaXIpIHtcbiAgaHVuay5jb25mbGljdCA9IHRydWU7XG4gIGh1bmsubGluZXMucHVzaCh7XG4gICAgY29uZmxpY3Q6IHRydWUsXG4gICAgbWluZTogbWluZSxcbiAgICB0aGVpcnM6IHRoZWlyXG4gIH0pO1xufVxuXG5mdW5jdGlvbiBpbnNlcnRMZWFkaW5nKGh1bmssIGluc2VydCwgdGhlaXIpIHtcbiAgd2hpbGUgKGluc2VydC5vZmZzZXQgPCB0aGVpci5vZmZzZXQgJiYgaW5zZXJ0LmluZGV4IDwgaW5zZXJ0LmxpbmVzLmxlbmd0aCkge1xuICAgIGxldCBsaW5lID0gaW5zZXJ0LmxpbmVzW2luc2VydC5pbmRleCsrXTtcbiAgICBodW5rLmxpbmVzLnB1c2gobGluZSk7XG4gICAgaW5zZXJ0Lm9mZnNldCsrO1xuICB9XG59XG5mdW5jdGlvbiBpbnNlcnRUcmFpbGluZyhodW5rLCBpbnNlcnQpIHtcbiAgd2hpbGUgKGluc2VydC5pbmRleCA8IGluc2VydC5saW5lcy5sZW5ndGgpIHtcbiAgICBsZXQgbGluZSA9IGluc2VydC5saW5lc1tpbnNlcnQuaW5kZXgrK107XG4gICAgaHVuay5saW5lcy5wdXNoKGxpbmUpO1xuICB9XG59XG5cbmZ1bmN0aW9uIGNvbGxlY3RDaGFuZ2Uoc3RhdGUpIHtcbiAgbGV0IHJldCA9IFtdLFxuICAgICAgb3BlcmF0aW9uID0gc3RhdGUubGluZXNbc3RhdGUuaW5kZXhdWzBdO1xuICB3aGlsZSAoc3RhdGUuaW5kZXggPCBzdGF0ZS5saW5lcy5sZW5ndGgpIHtcbiAgICBsZXQgbGluZSA9IHN0YXRlLmxpbmVzW3N0YXRlLmluZGV4XTtcblxuICAgIC8vIEdyb3VwIGFkZGl0aW9ucyB0aGF0IGFyZSBpbW1lZGlhdGVseSBhZnRlciBzdWJ0cmFjdGlvbnMgYW5kIHRyZWF0IHRoZW0gYXMgb25lIFwiYXRvbWljXCIgbW9kaWZ5IGNoYW5nZS5cbiAgICBpZiAob3BlcmF0aW9uID09PSAnLScgJiYgbGluZVswXSA9PT0gJysnKSB7XG4gICAgICBvcGVyYXRpb24gPSAnKyc7XG4gICAgfVxuXG4gICAgaWYgKG9wZXJhdGlvbiA9PT0gbGluZVswXSkge1xuICAgICAgcmV0LnB1c2gobGluZSk7XG4gICAgICBzdGF0ZS5pbmRleCsrO1xuICAgIH0gZWxzZSB7XG4gICAgICBicmVhaztcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmV0O1xufVxuZnVuY3Rpb24gY29sbGVjdENvbnRleHQoc3RhdGUsIG1hdGNoQ2hhbmdlcykge1xuICBsZXQgY2hhbmdlcyA9IFtdLFxuICAgICAgbWVyZ2VkID0gW10sXG4gICAgICBtYXRjaEluZGV4ID0gMCxcbiAgICAgIGNvbnRleHRDaGFuZ2VzID0gZmFsc2UsXG4gICAgICBjb25mbGljdGVkID0gZmFsc2U7XG4gIHdoaWxlIChtYXRjaEluZGV4IDwgbWF0Y2hDaGFuZ2VzLmxlbmd0aFxuICAgICAgICAmJiBzdGF0ZS5pbmRleCA8IHN0YXRlLmxpbmVzLmxlbmd0aCkge1xuICAgIGxldCBjaGFuZ2UgPSBzdGF0ZS5saW5lc1tzdGF0ZS5pbmRleF0sXG4gICAgICAgIG1hdGNoID0gbWF0Y2hDaGFuZ2VzW21hdGNoSW5kZXhdO1xuXG4gICAgLy8gT25jZSB3ZSd2ZSBoaXQgb3VyIGFkZCwgdGhlbiB3ZSBhcmUgZG9uZVxuICAgIGlmIChtYXRjaFswXSA9PT0gJysnKSB7XG4gICAgICBicmVhaztcbiAgICB9XG5cbiAgICBjb250ZXh0Q2hhbmdlcyA9IGNvbnRleHRDaGFuZ2VzIHx8IGNoYW5nZVswXSAhPT0gJyAnO1xuXG4gICAgbWVyZ2VkLnB1c2gobWF0Y2gpO1xuICAgIG1hdGNoSW5kZXgrKztcblxuICAgIC8vIENvbnN1bWUgYW55IGFkZGl0aW9ucyBpbiB0aGUgb3RoZXIgYmxvY2sgYXMgYSBjb25mbGljdCB0byBhdHRlbXB0XG4gICAgLy8gdG8gcHVsbCBpbiB0aGUgcmVtYWluaW5nIGNvbnRleHQgYWZ0ZXIgdGhpc1xuICAgIGlmIChjaGFuZ2VbMF0gPT09ICcrJykge1xuICAgICAgY29uZmxpY3RlZCA9IHRydWU7XG5cbiAgICAgIHdoaWxlIChjaGFuZ2VbMF0gPT09ICcrJykge1xuICAgICAgICBjaGFuZ2VzLnB1c2goY2hhbmdlKTtcbiAgICAgICAgY2hhbmdlID0gc3RhdGUubGluZXNbKytzdGF0ZS5pbmRleF07XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG1hdGNoLnN1YnN0cigxKSA9PT0gY2hhbmdlLnN1YnN0cigxKSkge1xuICAgICAgY2hhbmdlcy5wdXNoKGNoYW5nZSk7XG4gICAgICBzdGF0ZS5pbmRleCsrO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25mbGljdGVkID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBpZiAoKG1hdGNoQ2hhbmdlc1ttYXRjaEluZGV4XSB8fCAnJylbMF0gPT09ICcrJ1xuICAgICAgJiYgY29udGV4dENoYW5nZXMpIHtcbiAgICBjb25mbGljdGVkID0gdHJ1ZTtcbiAgfVxuXG4gIGlmIChjb25mbGljdGVkKSB7XG4gICAgcmV0dXJuIGNoYW5nZXM7XG4gIH1cblxuICB3aGlsZSAobWF0Y2hJbmRleCA8IG1hdGNoQ2hhbmdlcy5sZW5ndGgpIHtcbiAgICBtZXJnZWQucHVzaChtYXRjaENoYW5nZXNbbWF0Y2hJbmRleCsrXSk7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG1lcmdlZCxcbiAgICBjaGFuZ2VzXG4gIH07XG59XG5cbmZ1bmN0aW9uIGFsbFJlbW92ZXMoY2hhbmdlcykge1xuICByZXR1cm4gY2hhbmdlcy5yZWR1Y2UoZnVuY3Rpb24ocHJldiwgY2hhbmdlKSB7XG4gICAgcmV0dXJuIHByZXYgJiYgY2hhbmdlWzBdID09PSAnLSc7XG4gIH0sIHRydWUpO1xufVxuZnVuY3Rpb24gc2tpcFJlbW92ZVN1cGVyc2V0KHN0YXRlLCByZW1vdmVDaGFuZ2VzLCBkZWx0YSkge1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGRlbHRhOyBpKyspIHtcbiAgICBsZXQgY2hhbmdlQ29udGVudCA9IHJlbW92ZUNoYW5nZXNbcmVtb3ZlQ2hhbmdlcy5sZW5ndGggLSBkZWx0YSArIGldLnN1YnN0cigxKTtcbiAgICBpZiAoc3RhdGUubGluZXNbc3RhdGUuaW5kZXggKyBpXSAhPT0gJyAnICsgY2hhbmdlQ29udGVudCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHN0YXRlLmluZGV4ICs9IGRlbHRhO1xuICByZXR1cm4gdHJ1ZTtcbn1cblxuZnVuY3Rpb24gY2FsY09sZE5ld0xpbmVDb3VudChsaW5lcykge1xuICBsZXQgb2xkTGluZXMgPSAwO1xuICBsZXQgbmV3TGluZXMgPSAwO1xuXG4gIGxpbmVzLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgIGlmICh0eXBlb2YgbGluZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIGxldCBteUNvdW50ID0gY2FsY09sZE5ld0xpbmVDb3VudChsaW5lLm1pbmUpO1xuICAgICAgbGV0IHRoZWlyQ291bnQgPSBjYWxjT2xkTmV3TGluZUNvdW50KGxpbmUudGhlaXJzKTtcblxuICAgICAgaWYgKG9sZExpbmVzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKG15Q291bnQub2xkTGluZXMgPT09IHRoZWlyQ291bnQub2xkTGluZXMpIHtcbiAgICAgICAgICBvbGRMaW5lcyArPSBteUNvdW50Lm9sZExpbmVzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG9sZExpbmVzID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChuZXdMaW5lcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGlmIChteUNvdW50Lm5ld0xpbmVzID09PSB0aGVpckNvdW50Lm5ld0xpbmVzKSB7XG4gICAgICAgICAgbmV3TGluZXMgKz0gbXlDb3VudC5uZXdMaW5lcztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBuZXdMaW5lcyA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAobmV3TGluZXMgIT09IHVuZGVmaW5lZCAmJiAobGluZVswXSA9PT0gJysnIHx8IGxpbmVbMF0gPT09ICcgJykpIHtcbiAgICAgICAgbmV3TGluZXMrKztcbiAgICAgIH1cbiAgICAgIGlmIChvbGRMaW5lcyAhPT0gdW5kZWZpbmVkICYmIChsaW5lWzBdID09PSAnLScgfHwgbGluZVswXSA9PT0gJyAnKSkge1xuICAgICAgICBvbGRMaW5lcysrO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgcmV0dXJuIHtvbGRMaW5lcywgbmV3TGluZXN9O1xufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUFBLE9BQUEsR0FBQUMsT0FBQTtBQUFBO0FBQUE7QUFDQTtBQUFBO0FBQUFDLE1BQUEsR0FBQUQsT0FBQTtBQUFBO0FBQUE7QUFFQTtBQUFBO0FBQUFFLE1BQUEsR0FBQUYsT0FBQTtBQUFBO0FBQUE7QUFBMEQsbUNBQUFHLG1CQUFBQyxHQUFBLFdBQUFDLGtCQUFBLENBQUFELEdBQUEsS0FBQUUsZ0JBQUEsQ0FBQUYsR0FBQSxLQUFBRywyQkFBQSxDQUFBSCxHQUFBLEtBQUFJLGtCQUFBO0FBQUEsU0FBQUEsbUJBQUEsY0FBQUMsU0FBQTtBQUFBLFNBQUFGLDRCQUFBRyxDQUFBLEVBQUFDLE1BQUEsU0FBQUQsQ0FBQSxxQkFBQUEsQ0FBQSxzQkFBQUUsaUJBQUEsQ0FBQUYsQ0FBQSxFQUFBQyxNQUFBLE9BQUFFLENBQUEsR0FBQUMsTUFBQSxDQUFBQyxTQUFBLENBQUFDLFFBQUEsQ0FBQUMsSUFBQSxDQUFBUCxDQUFBLEVBQUFRLEtBQUEsYUFBQUwsQ0FBQSxpQkFBQUgsQ0FBQSxDQUFBUyxXQUFBLEVBQUFOLENBQUEsR0FBQUgsQ0FBQSxDQUFBUyxXQUFBLENBQUFDLElBQUEsTUFBQVAsQ0FBQSxjQUFBQSxDQUFBLG1CQUFBUSxLQUFBLENBQUFDLElBQUEsQ0FBQVosQ0FBQSxPQUFBRyxDQUFBLCtEQUFBVSxJQUFBLENB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 diff --git a/deps/npm/node_modules/diff/lib/patch/parse.js b/deps/npm/node_modules/diff/lib/patch/parse.js index f1501048014f1f..15acdd9a0e1c2c 100644 --- a/deps/npm/node_modules/diff/lib/patch/parse.js +++ b/deps/npm/node_modules/diff/lib/patch/parse.js @@ -5,123 +5,110 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.parsePatch = parsePatch; - /*istanbul ignore end*/ function parsePatch(uniDiff) { - /*istanbul ignore start*/ - var - /*istanbul ignore end*/ - options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], - list = [], - i = 0; - + var diffstr = uniDiff.split(/\n/), + list = [], + i = 0; function parseIndex() { var index = {}; - list.push(index); // Parse diff metadata + list.push(index); + // Parse diff metadata while (i < diffstr.length) { - var line = diffstr[i]; // File header found, end parsing diff metadata + var line = diffstr[i]; + // File header found, end parsing diff metadata if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { break; - } // Diff index - + } + // Diff index var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); - if (header) { index.index = header[1]; } - i++; - } // Parse file headers if they are defined. Unified diff requires them, but - // there's no technical issues to have an isolated hunk without file header - + } + // Parse file headers if they are defined. Unified diff requires them, but + // there's no technical issues to have an isolated hunk without file header + parseFileHeader(index); parseFileHeader(index); - parseFileHeader(index); // Parse hunks + // Parse hunks index.hunks = []; - while (i < diffstr.length) { var _line = diffstr[i]; - - if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { + if (/^(Index:\s|diff\s|\-\-\-\s|\+\+\+\s|===================================================================)/.test(_line)) { break; } else if (/^@@/.test(_line)) { index.hunks.push(parseHunk()); - } else if (_line && options.strict) { - // Ignore unexpected content unless in strict mode + } else if (_line) { throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); } else { i++; } } - } // Parses the --- and +++ headers, if none are found, no lines - // are consumed. - + } + // Parses the --- and +++ headers, if none are found, no lines + // are consumed. function parseFileHeader(index) { - var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); - + var fileHeader = /^(---|\+\+\+)\s+(.*)\r?$/.exec(diffstr[i]); if (fileHeader) { var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; var data = fileHeader[2].split('\t', 2); var fileName = data[0].replace(/\\\\/g, '\\'); - if (/^".*"$/.test(fileName)) { fileName = fileName.substr(1, fileName.length - 2); } - index[keyPrefix + 'FileName'] = fileName; index[keyPrefix + 'Header'] = (data[1] || '').trim(); i++; } - } // Parses a hunk - // This assumes that we are at the start of a hunk. - + } + // Parses a hunk + // This assumes that we are at the start of a hunk. function parseHunk() { var chunkHeaderIndex = i, - chunkHeaderLine = diffstr[i++], - chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); + chunkHeaderLine = diffstr[i++], + chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); var hunk = { oldStart: +chunkHeader[1], oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2], newStart: +chunkHeader[3], newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4], - lines: [], - linedelimiters: [] - }; // Unified Diff Format quirk: If the chunk size is 0, + lines: [] + }; + + // Unified Diff Format quirk: If the chunk size is 0, // the first number is one lower than one would expect. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 - if (hunk.oldLines === 0) { hunk.oldStart += 1; } - if (hunk.newLines === 0) { hunk.newStart += 1; } - var addCount = 0, - removeCount = 0; - - for (; i < diffstr.length; i++) { - // Lines starting with '---' could be mistaken for the "remove line" operation - // But they could be the header for the next file. Therefore prune such cases out. - if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { - break; - } - + removeCount = 0; + for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || + /*istanbul ignore start*/ + (_diffstr$i = + /*istanbul ignore end*/ + diffstr[i]) !== null && _diffstr$i !== void 0 && + /*istanbul ignore start*/ + _diffstr$i + /*istanbul ignore end*/ + .startsWith('\\')); i++) { + /*istanbul ignore start*/ + var _diffstr$i; + /*istanbul ignore end*/ var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; - if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { hunk.lines.push(diffstr[i]); - hunk.linedelimiters.push(delimiters[i] || '\n'); - if (operation === '+') { addCount++; } else if (operation === '-') { @@ -131,37 +118,34 @@ function parsePatch(uniDiff) { removeCount++; } } else { - break; + throw new Error( + /*istanbul ignore start*/ + "Hunk at line ".concat( + /*istanbul ignore end*/ + chunkHeaderIndex + 1, " contained invalid line ").concat(diffstr[i])); } - } // Handle the empty block count case - + } + // Handle the empty block count case if (!addCount && hunk.newLines === 1) { hunk.newLines = 0; } - if (!removeCount && hunk.oldLines === 1) { hunk.oldLines = 0; - } // Perform optional sanity checking - - - if (options.strict) { - if (addCount !== hunk.newLines) { - throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } - - if (removeCount !== hunk.oldLines) { - throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } } + // Perform sanity checking + if (addCount !== hunk.newLines) { + throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + if (removeCount !== hunk.oldLines) { + throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } return hunk; } - while (i < diffstr.length) { parseIndex(); } - return list; } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9wYXJzZS5qcyJdLCJuYW1lcyI6WyJwYXJzZVBhdGNoIiwidW5pRGlmZiIsIm9wdGlvbnMiLCJkaWZmc3RyIiwic3BsaXQiLCJkZWxpbWl0ZXJzIiwibWF0Y2giLCJsaXN0IiwiaSIsInBhcnNlSW5kZXgiLCJpbmRleCIsInB1c2giLCJsZW5ndGgiLCJsaW5lIiwidGVzdCIsImhlYWRlciIsImV4ZWMiLCJwYXJzZUZpbGVIZWFkZXIiLCJodW5rcyIsInBhcnNlSHVuayIsInN0cmljdCIsIkVycm9yIiwiSlNPTiIsInN0cmluZ2lmeSIsImZpbGVIZWFkZXIiLCJrZXlQcmVmaXgiLCJkYXRhIiwiZmlsZU5hbWUiLCJyZXBsYWNlIiwic3Vic3RyIiwidHJpbSIsImNodW5rSGVhZGVySW5kZXgiLCJjaHVua0hlYWRlckxpbmUiLCJjaHVua0hlYWRlciIsImh1bmsiLCJvbGRTdGFydCIsIm9sZExpbmVzIiwibmV3U3RhcnQiLCJuZXdMaW5lcyIsImxpbmVzIiwibGluZWRlbGltaXRlcnMiLCJhZGRDb3VudCIsInJlbW92ZUNvdW50IiwiaW5kZXhPZiIsIm9wZXJhdGlvbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQU8sU0FBU0EsVUFBVCxDQUFvQkMsT0FBcEIsRUFBMkM7QUFBQTtBQUFBO0FBQUE7QUFBZEMsRUFBQUEsT0FBYyx1RUFBSixFQUFJO0FBQ2hELE1BQUlDLE9BQU8sR0FBR0YsT0FBTyxDQUFDRyxLQUFSLENBQWMscUJBQWQsQ0FBZDtBQUFBLE1BQ0lDLFVBQVUsR0FBR0osT0FBTyxDQUFDSyxLQUFSLENBQWMsc0JBQWQsS0FBeUMsRUFEMUQ7QUFBQSxNQUVJQyxJQUFJLEdBQUcsRUFGWDtBQUFBLE1BR0lDLENBQUMsR0FBRyxDQUhSOztBQUtBLFdBQVNDLFVBQVQsR0FBc0I7QUFDcEIsUUFBSUMsS0FBSyxHQUFHLEVBQVo7QUFDQUgsSUFBQUEsSUFBSSxDQUFDSSxJQUFMLENBQVVELEtBQVYsRUFGb0IsQ0FJcEI7O0FB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QUwsS0FBa0IsQ0FBbkMsRUFBc0M7QUFDcENKLE1BQUFBLElBQUksQ0FBQ0ksUUFBTCxHQUFnQixDQUFoQjtBQUNEOztBQUNELFFBQUksQ0FBQ0ksV0FBRCxJQUFnQlIsSUFBSSxDQUFDRSxRQUFMLEtBQWtCLENBQXRDLEVBQXlDO0FBQ3ZDRixNQUFBQSxJQUFJLENBQUNFLFFBQUwsR0FBZ0IsQ0FBaEI7QUFDRCxLQTVEa0IsQ0E4RG5COzs7QUFDQSxRQUFJbEMsT0FBTyxDQUFDa0IsTUFBWixFQUFvQjtBQUNsQixVQUFJcUIsUUFBUSxLQUFLUCxJQUFJLENBQUNJLFFBQXRCLEVBQWdDO0FBQzlCLGNBQU0sSUFBSWpCLEtBQUosQ0FBVSxzREFBc0RVLGdCQUFnQixHQUFHLENBQXpFLENBQVYsQ0FBTjtBQUNEOztBQUNELFVBQUlXLFdBQVcsS0FBS1IsSUFBSSxDQUFDRSxRQUF6QixFQUFtQztBQUNqQyxjQUFNLElBQUlmLEtBQUosQ0FBVSx3REFBd0RVLGdCQUFnQixHQUFHLENBQTNFLENBQVYsQ0FBTjtBQUNEO0FBQ0Y7O0FBRUQsV0FBT0csSUFBUDtBQUNEOztBQUVELFNBQU8xQixDQUFDLEdBQUdMLE9BQU8sQ0FBQ1MsTUFBbkIsRUFBMkI7QUFDekJILElBQUFBLFVBQVU7QUFDWDs7QUFFRCxTQUFPRixJQUFQO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gcGFyc2VQYXRjaCh1bmlEaWZmLCBvcHRpb25zID0ge30pIHtcbiAgbGV0IGRpZmZzdHIgPSB1bmlEaWZmLnNwbGl0KC9cXHJcXG58W1xcblxcdlxcZlxcclxceDg1XS8pLFxuICAgICAgZGVsaW1pdGVycyA9IHVuaURpZmYubWF0Y2goL1xcclxcbnxbXFxuXFx2XFxmXFxyXFx4ODVdL2cpIHx8IFtdLFxuICAgICAgbGlzdCA9IFtdLFxuICAgICAgaSA9IDA7XG5cbiAgZnVuY3Rpb24gcGFyc2VJbmRleCgpIHtcbiAgICBsZXQgaW5kZXggPSB7fTtcbiAgICBsaXN0LnB1c2goaW5kZXgpO1xuXG4gICAgLy8gUGFyc2UgZGlmZiBtZXRhZGF0YVxuICAgIHdoaWxlIChpIDwgZGlmZnN0ci5sZW5ndGgpIHtcbiAgICAgIGxldCBsaW5lID0gZGlmZnN0cltpXTtcblxuICAgICAgLy8gRmlsZSBoZWFkZXIgZm91bmQsIGVuZCBwYXJzaW5nIGRpZmYgbWV0YWRhdGFcbiAgICAgIGlmICgoL14oXFwtXFwtXFwtfFxcK1xcK1xcK3xAQClcXHMvKS50ZXN0KGxpbmUpKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuXG4gICAgICAvLyBEaWZmIGluZGV4XG4gICAgICBsZXQgaGVhZGVyID0gKC9eKD86SW5kZXg6fGRpZmYoPzogLXIgXFx3KykrKVxccysoLis/KVxccyokLykuZXhlYyhsaW5lKTtcbiAgICAgIGlmIChoZWFkZXIpIHtcbiAgICAgICAgaW5kZXguaW5kZXggPSBoZWFkZXJbMV07XG4gICAgICB9XG5cbiAgICAgIGkrKztcbiAgICB9XG5cbiAgICAvLyBQYXJzZSBmaWxlIGhlYWRlcnMgaWYgdGhleSBhcmUgZGVmaW5lZC4gVW5pZmllZCBkaWZmIHJlcXVpcmVzIHRoZW0sIGJ1dFxuICAgIC8vIHRoZXJlJ3Mgbm8gdGVjaG5pY2FsIGlzc3VlcyB0byBoYXZlIGFuIGlzb2xhdGVkIGh1bmsgd2l0aG91dCBmaWxlIGhlYWRlclxuICAgIHBhcnNlRmlsZUhlYWRlcihpbmRleCk7XG4gICAgcGFyc2VGaWxlSGVhZGVyKGluZGV4KTtcblxuICAgIC8vIFBhcnNlIGh1bmtzXG4gICAgaW5kZXguaHVua3MgPSBbXTtcblxuICAgIHdoaWxlIChpIDwgZGlmZnN0ci5sZW5ndGgpIHtcbiAgICAgIGxldCBsaW5lID0gZGlmZnN0cltpXTtcblxuICAgICAgaWYgKCgvXihJbmRleDp8ZGlmZnxcXC1cXC1cXC18XFwrXFwrXFwrKVxccy8pLnRlc3QobGluZSkpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9IGVsc2UgaWYgKCgvXkBALykudGVzdChsaW5lKSkge1xuICAgICAgICBpbmRleC5odW5rcy5wdXNoKHBhcnNlSHVuaygpKTtcbiAgICAgIH0gZWxzZSBpZiAobGluZSAmJiBvcHRpb25zLnN0cmljdCkge1xuICAgICAgICAvLyBJZ25vcmUgdW5leHBlY3RlZCBjb250ZW50IHVubGVzcyBpbiBzdHJpY3QgbW9kZVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Vua25vd24gbGluZSAnICsgKGkgKyAxKSArICcgJyArIEpTT04uc3RyaW5naWZ5KGxpbmUpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGkrKztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBQYXJzZXMgdGhlIC0tLSBhbmQgKysrIGhlYWRlcnMsIGlmIG5vbmUgYXJlIGZvdW5kLCBubyBsaW5lc1xuICAvLyBhcmUgY29uc3VtZWQuXG4gIGZ1bmN0aW9uIHBhcnNlRmlsZUhlYWRlcihpbmRleCkge1xuICAgIGNvbnN0IGZpbGVIZWFkZXIgPSAoL14oLS0tfFxcK1xcK1xcKylcXHMrKC4qKSQvKS5leGVjKGRpZmZzdHJbaV0pO1xuICAgIGlmIChmaWxlSGVhZGVyKSB7XG4gICAgICBsZXQga2V5UHJlZml4ID0gZmlsZUhlYWRlclsxXSA9PT0gJy0tLScgPyAnb2xkJyA6ICduZXcnO1xuICAgICAgY29uc3QgZGF0YSA9IGZpbGVIZWFkZXJbMl0uc3BsaXQoJ1xcdCcsIDIpO1xuICAgICAgbGV0IGZpbGVOYW1lID0gZGF0YVswXS5yZXBsYWNlKC9cXFxcXFxcXC9nLCAnXFxcXCcpO1xuICAgICAgaWYgKCgvXlwiLipcIiQvKS50ZXN0KGZpbGVOYW1lKSkge1xuICAgICAgICBmaWxlTmFtZSA9IGZpbGVOYW1lLnN1YnN0cigxLCBmaWxlTmFtZS5sZW5ndGggLSAyKTtcbiAgICAgIH1cbiAgICAgIGluZGV4W2tleVByZWZpeCArICdGaWxlTmFtZSddID0gZmlsZU5hbWU7XG4gICAgICBpbmRleFtrZXlQcmVmaXggKyAnSGVhZGVyJ10gPSAoZGF0YVsxXSB8fCAnJykudHJpbSgpO1xuXG4gICAgICBpKys7XG4gICAgfVxuICB9XG5cbiAgLy8gUGFyc2VzIGEgaHVua1xuICAvLyBUaGlzIGFzc3VtZXMgdGhhdCB3ZSBhcmUgYXQgdGhlIHN0YXJ0IG9mIGEgaHVuay5cbiAgZnVuY3Rpb24gcGFyc2VIdW5rKCkge1xuICAgIGxldCBjaHVua0hlYWRlckluZGV4ID0gaSxcbiAgICAgICAgY2h1bmtIZWFkZXJMaW5lID0gZGlmZnN0cltpKytdLFxuICAgICAgICBjaHVua0hlYWRlciA9IGNodW5rSGVhZGVyTGluZS5zcGxpdCgvQEAgLShcXGQrKSg/OiwoXFxkKykpPyBcXCsoXFxkKykoPzosKFxcZCspKT8gQEAvKTtcblxuICAgIGxldCBodW5rID0ge1xuICAgICAgb2xkU3RhcnQ6ICtjaHVua0hlYWRlclsxXSxcbiAgICAgIG9sZExpbmVzOiB0eXBlb2YgY2h1bmtIZWFkZXJbMl0gPT09ICd1bmRlZmluZWQnID8gMSA6ICtjaHVua0hlYWRlclsyXSxcbiAgICAgIG5ld1N0YXJ0OiArY2h1bmtIZWFkZXJbM10sXG4gICAgICBuZXdMaW5lczogdHlwZW9mIGNodW5rSGVhZGVyWzRdID09PSAndW5kZWZpbmVkJyA/IDEgOiArY2h1bmtIZWFkZXJbNF0sXG4gICAgICBsaW5lczogW10sXG4gICAgICBsaW5lZGVsaW1pdGVyczogW11cbiAgICB9O1xuXG4gICAgLy8gVW5pZmllZCBEaWZmIEZvcm1hdCBxdWlyazogSWYgdGhlIGNodW5rIHNpemUgaXMgMCxcbiAgICAvLyB0aGUgZmlyc3QgbnVtYmVyIGlzIG9uZSBsb3dlciB0aGFuIG9uZSB3b3VsZCBleHBlY3QuXG4gICAgLy8gaHR0cHM6Ly93d3cuYXJ0aW1hLmNvbS93ZWJsb2dzL3ZpZXdwb3N0LmpzcD90aHJlYWQ9MTY0MjkzXG4gICAgaWYgKGh1bmsub2xkTGluZXMgPT09IDApIHtcbiAgICAgIGh1bmsub2xkU3RhcnQgKz0gMTtcbiAgICB9XG4gICAgaWYgKGh1bmsubmV3TGluZXMgPT09IDApIHtcbiAgICAgIGh1bmsubmV3U3RhcnQgKz0gMTtcbiAgICB9XG5cbiAgICBsZXQgYWRkQ291bnQgPSAwLFxuICAgICAgICByZW1vdmVDb3VudCA9IDA7XG4gICAgZm9yICg7IGkgPCBkaWZmc3RyLmxlbmd0aDsgaSsrKSB7XG4gICAgICAvLyBMaW5lcyBzdGFydGluZyB3aXRoICctLS0nIGNvdWxkIGJlIG1pc3Rha2VuIGZvciB0aGUgXCJyZW1vdmUgbGluZVwiIG9wZXJhdGlvblxuICAgICAgLy8gQnV0IHRoZXkgY291bGQgYmUgdGhlIGhlYWRlciBmb3IgdGhlIG5leHQgZmlsZS4gVGhlcmVmb3JlIHBydW5lIHN1Y2ggY2FzZXMgb3V0LlxuICAgICAgaWYgKGRpZmZzdHJbaV0uaW5kZXhPZignLS0tICcpID09PSAwXG4gICAgICAgICAgICAmJiAoaSArIDIgPCBkaWZmc3RyLmxlbmd0aClcbiAgICAgICAgICAgICYmIGRpZmZzdHJbaSArIDFdLmluZGV4T2YoJysrKyAnKSA9PT0gMFxuICAgICAgICAgICAgJiYgZGlmZnN0cltpICsgMl0uaW5kZXhPZignQEAnKSA9PT0gMCkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgbGV0IG9wZXJhdGlvbiA9IChkaWZmc3RyW2ldLmxlbmd0aCA9PSAwICYmIGkgIT0gKGRpZmZzdHIubGVuZ3RoIC0gMSkpID8gJyAnIDogZGlmZnN0cltpXVswXTtcblxuICAgICAgaWYgKG9wZXJhdGlvbiA9PT0gJysnIHx8IG9wZXJhdGlvbiA9PT0gJy0nIHx8IG9wZXJhdGlvbiA9PT0gJyAnIHx8IG9wZXJhdGlvbiA9PT0gJ1xcXFwnKSB7XG4gICAgICAgIGh1bmsubGluZXMucHVzaChkaWZmc3RyW2ldKTtcbiAgICAgICAgaHVuay5saW5lZGVsaW1pdGVycy5wdXNoKGRlbGltaXRlcnNbaV0gfHwgJ1xcbicpO1xuXG4gICAgICAgIGlmIChvcGVyYXRpb24gPT09ICcrJykge1xuICAgICAgICAgIGFkZENvdW50Kys7XG4gICAgICAgIH0gZWxzZSBpZiAob3BlcmF0aW9uID09PSAnLScpIHtcbiAgICAgICAgICByZW1vdmVDb3VudCsrO1xuICAgICAgICB9IGVsc2UgaWYgKG9wZXJhdGlvbiA9PT0gJyAnKSB7XG4gICAgICAgICAgYWRkQ291bnQrKztcbiAgICAgICAgICByZW1vdmVDb3VudCsrO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgdGhlIGVtcHR5IGJsb2NrIGNvdW50IGNhc2VcbiAgICBpZiAoIWFkZENvdW50ICYmIGh1bmsubmV3TGluZXMgPT09IDEpIHtcbiAgICAgIGh1bmsubmV3TGluZXMgPSAwO1xuICAgIH1cbiAgICBpZiAoIXJlbW92ZUNvdW50ICYmIGh1bmsub2xkTGluZXMgPT09IDEpIHtcbiAgICAgIGh1bmsub2xkTGluZXMgPSAwO1xuICAgIH1cblxuICAgIC8vIFBlcmZvcm0gb3B0aW9uYWwgc2FuaXR5IGNoZWNraW5nXG4gICAgaWYgKG9wdGlvbnMuc3RyaWN0KSB7XG4gICAgICBpZiAoYWRkQ291bnQgIT09IGh1bmsubmV3TGluZXMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdBZGRlZCBsaW5lIGNvdW50IGRpZCBub3QgbWF0Y2ggZm9yIGh1bmsgYXQgbGluZSAnICsgKGNodW5rSGVhZGVySW5kZXggKyAxKSk7XG4gICAgICB9XG4gICAgICBpZiAocmVtb3ZlQ291bnQgIT09IGh1bmsub2xkTGluZXMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdSZW1vdmVkIGxpbmUgY291bnQgZGlkIG5vdCBtYXRjaCBmb3IgaHVuayBhdCBsaW5lICcgKyAoY2h1bmtIZWFkZXJJbmRleCArIDEpKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gaHVuaztcbiAgfVxuXG4gIHdoaWxlIChpIDwgZGlmZnN0ci5sZW5ndGgpIHtcbiAgICBwYXJzZUluZGV4KCk7XG4gIH1cblxuICByZXR1cm4gbGlzdDtcbn1cbiJdfQ== +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJwYXJzZVBhdGNoIiwidW5pRGlmZiIsImRpZmZzdHIiLCJzcGxpdCIsImxpc3QiLCJpIiwicGFyc2VJbmRleCIsImluZGV4IiwicHVzaCIsImxlbmd0aCIsImxpbmUiLCJ0ZXN0IiwiaGVhZGVyIiwiZXhlYyIsInBhcnNlRmlsZUhlYWRlciIsImh1bmtzIiwicGFyc2VIdW5rIiwiRXJyb3IiLCJKU09OIiwic3RyaW5naWZ5IiwiZmlsZUhlYWRlciIsImtleVByZWZpeCIsImRhdGEiLCJmaWxlTmFtZSIsInJlcGxhY2UiLCJzdWJzdHIiLCJ0cmltIiwiY2h1bmtIZWFkZXJJbmRleCIsImNodW5rSGVhZGVyTGluZSIsImNodW5rSGVhZGVyIiwiaHVuayIsIm9sZFN0YXJ0Iiwib2xkTGluZXMiLCJuZXdTdGFydCIsIm5ld0xpbmVzIiwibGluZXMiLCJhZGRDb3VudCIsInJlbW92ZUNvdW50IiwiX2RpZmZzdHIkaSIsInN0YXJ0c1dpdGgiLCJvcGVyYXRpb24iLCJjb25jYXQiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvcGF0Y2gvcGFyc2UuanMiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIHBhcnNlUGF0Y2godW5pRGlmZikge1xuICBsZXQgZGlmZnN0ciA9IHVuaURpZmYuc3BsaXQoL1xcbi8pLFxuICAgICAgbGlzdCA9IFtdLFxuICAgICAgaSA9IDA7XG5cbiAgZnVuY3Rpb24gcGFyc2VJbmRleCgpIHtcbiAgICBsZXQgaW5kZXggPSB7fTtcbiAgICBsaXN0LnB1c2goaW5kZXgpO1xuXG4gICAgLy8gUGFyc2UgZGlmZiBtZXRhZGF0YVxuICAgIHdoaWxlIChpIDwgZGlmZnN0ci5sZW5ndGgpIHtcbiAgICAgIGxldCBsaW5lID0gZGlmZnN0cltpXTtcblxuICAgICAgLy8gRmlsZSBoZWFkZXIgZm91bmQsIGVuZCBwYXJzaW5nIGRpZmYgbWV0YWRhdGFcbiAgICAgIGlmICgoL14oXFwtXFwtXFwtfFxcK1xcK1xcK3xAQClcXHMvKS50ZXN0KGxpbmUpKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuXG4gICAgICAvLyBEaWZmIGluZGV4XG4gICAgICBsZXQgaGVhZGVyID0gKC9eKD86SW5kZXg6fGRpZmYoPzogLXIgXFx3KykrKVxccysoLis/KVxccyokLykuZXhlYyhsaW5lKTtcbiAgICAgIGlmIChoZWFkZXIpIHtcbiAgICAgICAgaW5kZXguaW5kZXggPSBoZWFkZXJbMV07XG4gICAgICB9XG5cbiAgICAgIGkrKztcbiAgICB9XG5cbiAgICAvLyBQYXJzZSBmaWxlIGhlYWRlcnMgaWYgdGhleSBhcmUgZGVmaW5lZC4gVW5pZmllZCBkaWZmIHJlcXVpcmVzIHRoZW0sIGJ1dFxuICAgIC8vIHRoZXJlJ3Mgbm8gdGVjaG5pY2FsIGlzc3VlcyB0byBoYXZlIGFuIGlzb2xhdGVkIGh1bmsgd2l0aG91dCBmaWxlIGhlYWRlclxuICAgIHBhcnNlRmlsZUhlYWRlcihpbmRleCk7XG4gICAgcGFyc2VGaWxlSGVhZGVyKGluZGV4KTtcblxuICAgIC8vIFBhcnNlIGh1bmtzXG4gICAgaW5kZXguaHVua3MgPSBbXTtcblxuICAgIHdoaWxlIChpIDwgZGlmZnN0ci5sZW5ndGgpIHtcbiAgICAgIGxldCBsaW5lID0gZGlmZnN0cltpXTtcbiAgICAgIGlmICgoL14oSW5kZXg6XFxzfGRpZmZcXHN8XFwtXFwtXFwtXFxzfFxcK1xcK1xcK1xcc3w9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09KS8pLnRlc3QobGluZSkpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9IGVsc2UgaWYgKCgvXkBALykudGVzdChsaW5lKSkge1xuICAgICAgICBpbmRleC5odW5rcy5wdXNoKHBhcnNlSHVuaygpKTtcbiAgICAgIH0gZWxzZSBpZiAobGluZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Vua25vd24gbGluZSAnICsgKGkgKyAxKSArICcgJyArIEpTT04uc3RyaW5naWZ5KGxpbmUpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGkrKztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBQYXJzZXMgdGhlIC0tLSBhbmQgKysrIGhlYWRlcnMsIGlmIG5vbmUgYXJlIGZvdW5kLCBubyBsaW5lc1xuICAvLyBhcmUgY29uc3VtZWQuXG4gIGZ1bmN0aW9uIHBhcnNlRmlsZUhlYWRlcihpbmRleCkge1xuICAgIGNvbnN0IGZpbGVIZWFkZXIgPSAoL14oLS0tfFxcK1xcK1xcKylcXHMrKC4qKVxccj8kLykuZXhlYyhkaWZmc3RyW2ldKTtcbiAgICBpZiAoZmlsZUhlYWRlcikge1xuICAgICAgbGV0IGtleVByZWZpeCA9IGZpbGVIZWFkZXJbMV0gPT09ICctLS0nID8gJ29sZCcgOiAnbmV3JztcbiAgICAgIGNvbnN0IGRhdGEgPSBmaWxlSGVhZGVyWzJdLnNwbGl0KCdcXHQnLCAyKTtcbiAgICAgIGxldCBmaWxlTmFtZSA9IGRhdGFbMF0ucmVwbGFjZSgvXFxcXFxcXFwvZywgJ1xcXFwnKTtcbiAgICAgIGlmICgoL15cIi4qXCIkLykudGVzdChmaWxlTmFtZSkpIHtcbiAgICAgICAgZmlsZU5hbWUgPSBmaWxlTmFtZS5zdWJzdHIoMSwgZmlsZU5hbWUubGVuZ3RoIC0gMik7XG4gICAgICB9XG4gICAgICBpbmRleFtrZXlQcmVmaXggKyAnRmlsZU5hbWUnXSA9IGZpbGVOYW1lO1xuICAgICAgaW5kZXhba2V5UHJlZml4ICsgJ0hlYWRlciddID0gKGRhdGFbMV0gfHwgJycpLnRyaW0oKTtcblxuICAgICAgaSsrO1xuICAgIH1cbiAgfVxuXG4gIC8vIFBhcnNlcyBhIGh1bmtcbiAgLy8gVGhpcyBhc3N1bWVzIHRoYXQgd2UgYXJlIGF0IHRoZSBzdGFydCBvZiBhIGh1bmsuXG4gIGZ1bmN0aW9uIHBhcnNlSHVuaygpIHtcbiAgICBsZXQgY2h1bmtIZWFkZXJJbmRleCA9IGksXG4gICAgICAgIGNodW5rSGVhZGVyTGluZSA9IGRpZmZzdHJbaSsrXSxcbiAgICAgICAgY2h1bmtIZWFkZXIgPSBjaHVua0hlYWRlckxpbmUuc3BsaXQoL0BAIC0oXFxkKykoPzosKFxcZCspKT8gXFwrKFxcZCspKD86LChcXGQrKSk/IEBALyk7XG5cbiAgICBsZXQgaHVuayA9IHtcbiAgICAgIG9sZFN0YXJ0OiArY2h1bmtIZWFkZXJbMV0sXG4gICAgICBvbGRMaW5lczogdHlwZW9mIGNodW5rSGVhZGVyWzJdID09PSAndW5kZWZpbmVkJyA/IDEgOiArY2h1bmtIZWFkZXJbMl0sXG4gICAgICBuZXdTdGFydDogK2NodW5rSGVhZGVyWzNdLFxuICAgICAgbmV3TGluZXM6IHR5cGVvZiBjaHVua0hlYWRlcls0XSA9PT0gJ3VuZGVmaW5lZCcgPyAxIDogK2NodW5rSGVhZGVyWzRdLFxuICAgICAgbGluZXM6IFtdXG4gICAgfTtcblxuICAgIC8vIFVuaWZpZWQgRGlmZiBGb3JtYXQgcXVpcms6IElmIHRoZSBjaHVuayBzaXplIGlzIDAsXG4gICAgLy8gdGhlIGZpcnN0IG51bWJlciBpcyBvbmUgbG93ZXIgdGhhbiBvbmUgd291bGQgZXhwZWN0LlxuICAgIC8vIGh0dHBzOi8vd3d3LmFydGltYS5jb20vd2VibG9ncy92aWV3cG9zdC5qc3A/dGhyZWFkPTE2NDI5M1xuICAgIGlmIChodW5rLm9sZExpbmVzID09PSAwKSB7XG4gICAgICBodW5rLm9sZFN0YXJ0ICs9IDE7XG4gICAgfVxuICAgIGlmIChodW5rLm5ld0xpbmVzID09PSAwKSB7XG4gICAgICBodW5rLm5ld1N0YXJ0ICs9IDE7XG4gICAgfVxuXG4gICAgbGV0IGFkZENvdW50ID0gMCxcbiAgICAgICAgcmVtb3ZlQ291bnQgPSAwO1xuICAgIGZvciAoXG4gICAgICA7XG4gICAgICBpIDwgZGlmZnN0ci5sZW5ndGggJiYgKHJlbW92ZUNvdW50IDwgaHVuay5vbGRMaW5lcyB8fCBhZGRDb3VudCA8IGh1bmsubmV3TGluZXMgfHwgZGlmZnN0cltpXT8uc3RhcnRzV2l0aCgnXFxcXCcpKTtcbiAgICAgIGkrK1xuICAgICkge1xuICAgICAgbGV0IG9wZXJhdGlvbiA9IChkaWZmc3RyW2ldLmxlbmd0aCA9PSAwICYmIGkgIT0gKGRpZmZzdHIubGVuZ3RoIC0gMSkpID8gJyAnIDogZGlmZnN0cltpXVswXTtcbiAgICAgIGlmIChvcGVyYXRpb24gPT09ICcrJyB8fCBvcGVyYXRpb24gPT09ICctJyB8fCBvcGVyYXRpb24gPT09ICcgJyB8fCBvcGVyYXRpb24gPT09ICdcXFxcJykge1xuICAgICAgICBodW5rLmxpbmVzLnB1c2goZGlmZnN0cltpXSk7XG5cbiAgICAgICAgaWYgKG9wZXJhdGlvbiA9PT0gJysnKSB7XG4gICAgICAgICAgYWRkQ291bnQrKztcbiAgICAgICAgfSBlbHNlIGlmIChvcGVyYXRpb24gPT09ICctJykge1xuICAgICAgICAgIHJlbW92ZUNvdW50Kys7XG4gICAgICAgIH0gZWxzZSBpZiAob3BlcmF0aW9uID09PSAnICcpIHtcbiAgICAgICAgICBhZGRDb3VudCsrO1xuICAgICAgICAgIHJlbW92ZUNvdW50Kys7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSHVuayBhdCBsaW5lICR7Y2h1bmtIZWFkZXJJbmRleCArIDF9IGNvbnRhaW5lZCBpbnZhbGlkIGxpbmUgJHtkaWZmc3RyW2ldfWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEhhbmRsZSB0aGUgZW1wdHkgYmxvY2sgY291bnQgY2FzZVxuICAgIGlmICghYWRkQ291bnQgJiYgaHVuay5uZXdMaW5lcyA9PT0gMSkge1xuICAgICAgaHVuay5uZXdMaW5lcyA9IDA7XG4gICAgfVxuICAgIGlmICghcmVtb3ZlQ291bnQgJiYgaHVuay5vbGRMaW5lcyA9PT0gMSkge1xuICAgICAgaHVuay5vbGRMaW5lcyA9IDA7XG4gICAgfVxuXG4gICAgLy8gUGVyZm9ybSBzYW5pdHkgY2hlY2tpbmdcbiAgICBpZiAoYWRkQ291bnQgIT09IGh1bmsubmV3TGluZXMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQWRkZWQgbGluZSBjb3VudCBkaWQgbm90IG1hdGNoIGZvciBodW5rIGF0IGxpbmUgJyArIChjaHVua0hlYWRlckluZGV4ICsgMSkpO1xuICAgIH1cbiAgICBpZiAocmVtb3ZlQ291bnQgIT09IGh1bmsub2xkTGluZXMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignUmVtb3ZlZCBsaW5lIGNvdW50IGRpZCBub3QgbWF0Y2ggZm9yIGh1bmsgYXQgbGluZSAnICsgKGNodW5rSGVhZGVySW5kZXggKyAxKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGh1bms7XG4gIH1cblxuICB3aGlsZSAoaSA8IGRpZmZzdHIubGVuZ3RoKSB7XG4gICAgcGFyc2VJbmRleCgpO1xuICB9XG5cbiAgcmV0dXJuIGxpc3Q7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBQU8sU0FBU0EsVUFBVUEsQ0FBQ0MsT0FBTyxFQUFFO0VBQ2xDLElBQUlDLE9BQU8sR0FBR0QsT0FBTyxDQUFDRSxLQUFLLENBQUMsSUFBSSxDQUFDO0lBQzdCQyxJQUFJLEdBQUcsRUFBRTtJQUNUQyxDQUFDLEdBQUcsQ0FBQztFQUVULFNBQVNDLFVBQVVBLENBQUEsRUFBRztJQUNwQixJQUFJQyxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBQ2RILElBQUksQ0FBQ0ksSUFBSSxDQUFDRCxLQUFLLENBQUM7O0lBRWhCO0lBQ0EsT0FBT0YsQ0FBQyxHQUFHSCxPQUFPLENBQUNPLE1BQU0sRUFBRTtNQUN6QixJQUFJQyxJQUFJLEdBQUdSLE9BQU8sQ0FBQ0csQ0FBQyxDQUFDOztNQUVyQjtNQUNBLElBQUssdUJBQXVCLENBQUVNLElBQUksQ0FBQ0QsSUFBSSxDQUFDLEVBQUU7UUFDeEM7TUFDRjs7TUFFQTtNQUNBLElBQUlFLE1BQU0sR0FBSSwwQ0FBMEMsQ0FBRUMsSUFBSSxDQUFDSCxJQUFJLENBQUM7TUFDcEUsSUFBSUUsTUFBTSxFQUFFO1FBQ1ZMLEtBQUssQ0FBQ0EsS0FBSyxHQUFHSyxNQUFNLENBQUMsQ0FBQyxDQUFDO01BQ3pCO01BRUFQLENBQUMsRUFBRTtJQUNMOztJQUVBO0lBQ0E7SUFDQVMsZUFB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 diff --git a/deps/npm/node_modules/diff/lib/patch/reverse.js b/deps/npm/node_modules/diff/lib/patch/reverse.js index 6e4be99af8ac32..3c8723e4d5fe66 100644 --- a/deps/npm/node_modules/diff/lib/patch/reverse.js +++ b/deps/npm/node_modules/diff/lib/patch/reverse.js @@ -5,19 +5,17 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.reversePatch = reversePatch; - -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /*istanbul ignore end*/ function reversePatch(structuredPatch) { if (Array.isArray(structuredPatch)) { return structuredPatch.map(reversePatch).reverse(); } - return ( /*istanbul ignore start*/ _objectSpread(_objectSpread({}, @@ -33,7 +31,6 @@ function reversePatch(structuredPatch) { oldStart: hunk.newStart, newLines: hunk.oldLines, newStart: hunk.oldStart, - linedelimiters: hunk.linedelimiters, lines: hunk.lines.map(function (l) { if (l.startsWith('-')) { return ( @@ -43,7 +40,6 @@ function reversePatch(structuredPatch) { l.slice(1)) ); } - if (l.startsWith('+')) { return ( /*istanbul ignore start*/ @@ -52,7 +48,6 @@ function reversePatch(structuredPatch) { l.slice(1)) ); } - return l; }) }; @@ -60,4 +55,4 @@ function reversePatch(structuredPatch) { }) ); } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9yZXZlcnNlLmpzIl0sIm5hbWVzIjpbInJldmVyc2VQYXRjaCIsInN0cnVjdHVyZWRQYXRjaCIsIkFycmF5IiwiaXNBcnJheSIsIm1hcCIsInJldmVyc2UiLCJvbGRGaWxlTmFtZSIsIm5ld0ZpbGVOYW1lIiwib2xkSGVhZGVyIiwibmV3SGVhZGVyIiwiaHVua3MiLCJodW5rIiwib2xkTGluZXMiLCJuZXdMaW5lcyIsIm9sZFN0YXJ0IiwibmV3U3RhcnQiLCJsaW5lZGVsaW1pdGVycyIsImxpbmVzIiwibCIsInN0YXJ0c1dpdGgiLCJzbGljZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7O0FBQU8sU0FBU0EsWUFBVCxDQUFzQkMsZUFBdEIsRUFBdUM7QUFDNUMsTUFBSUMsS0FBSyxDQUFDQyxPQUFOLENBQWNGLGVBQWQsQ0FBSixFQUFvQztBQUNsQyxXQUFPQSxlQUFlLENBQUNHLEdBQWhCLENBQW9CSixZQUFwQixFQUFrQ0ssT0FBbEMsRUFBUDtBQUNEOztBQUVEO0FBQUE7QUFBQTtBQUFBO0FBQ0tKLElBQUFBLGVBREw7QUFFRUssTUFBQUEsV0FBVyxFQUFFTCxlQUFlLENBQUNNLFdBRi9CO0FBR0VDLE1BQUFBLFNBQVMsRUFBRVAsZUFBZSxDQUFDUSxTQUg3QjtBQUlFRixNQUFBQSxXQUFXLEVBQUVOLGVBQWUsQ0FBQ0ssV0FKL0I7QUFLRUcsTUFBQUEsU0FBUyxFQUFFUixlQUFlLENBQUNPLFNBTDdCO0FBTUVFLE1BQUFBLEtBQUssRUFBRVQsZUFBZSxDQUFDUyxLQUFoQixDQUFzQk4sR0FBdEIsQ0FBMEIsVUFBQU8sSUFBSSxFQUFJO0FBQ3ZDLGVBQU87QUFDTEMsVUFBQUEsUUFBUSxFQUFFRCxJQUFJLENBQUNFLFFBRFY7QUFFTEMsVUFBQUEsUUFBUSxFQUFFSCxJQUFJLENBQUNJLFFBRlY7QUFHTEYsVUFBQUEsUUFBUSxFQUFFRixJQUFJLENBQUNDLFFBSFY7QUFJTEcsVUFBQUEsUUFBUSxFQUFFSixJQUFJLENBQUNHLFFBSlY7QUFLTEUsVUFBQUEsY0FBYyxFQUFFTCxJQUFJLENBQUNLLGNBTGhCO0FBTUxDLFVBQUFBLEtBQUssRUFBRU4sSUFBSSxDQUFDTSxLQUFMLENBQVdiLEdBQVgsQ0FBZSxVQUFBYyxDQUFDLEVBQUk7QUFDekIsZ0JBQUlBLENBQUMsQ0FBQ0MsVUFBRixDQUFhLEdBQWIsQ0FBSixFQUF1QjtBQUFFO0FBQUE7QUFBQTtBQUFBO0FBQVdELGdCQUFBQSxDQUFDLENBQUNFLEtBQUYsQ0FBUSxDQUFSLENBQVg7QUFBQTtBQUEwQjs7QUFDbkQsZ0JBQUlGLENBQUMsQ0FBQ0MsVUFBRixDQUFhLEdBQWIsQ0FBSixFQUF1QjtBQUFFO0FBQUE7QUFBQTtBQUFBO0FBQVdELGdCQUFBQSxDQUFDLENBQUNFLEtBQUYsQ0FBUSxDQUFSLENBQVg7QUFBQTtBQUEwQjs7QUFDbkQsbUJBQU9GLENBQVA7QUFDRCxXQUpNO0FBTkYsU0FBUDtBQVlELE9BYk07QUFOVDtBQUFBO0FBcUJEIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIHJldmVyc2VQYXRjaChzdHJ1Y3R1cmVkUGF0Y2gpIHtcbiAgaWYgKEFycmF5LmlzQXJyYXkoc3RydWN0dXJlZFBhdGNoKSkge1xuICAgIHJldHVybiBzdHJ1Y3R1cmVkUGF0Y2gubWFwKHJldmVyc2VQYXRjaCkucmV2ZXJzZSgpO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICAuLi5zdHJ1Y3R1cmVkUGF0Y2gsXG4gICAgb2xkRmlsZU5hbWU6IHN0cnVjdHVyZWRQYXRjaC5uZXdGaWxlTmFtZSxcbiAgICBvbGRIZWFkZXI6IHN0cnVjdHVyZWRQYXRjaC5uZXdIZWFkZXIsXG4gICAgbmV3RmlsZU5hbWU6IHN0cnVjdHVyZWRQYXRjaC5vbGRGaWxlTmFtZSxcbiAgICBuZXdIZWFkZXI6IHN0cnVjdHVyZWRQYXRjaC5vbGRIZWFkZXIsXG4gICAgaHVua3M6IHN0cnVjdHVyZWRQYXRjaC5odW5rcy5tYXAoaHVuayA9PiB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBvbGRMaW5lczogaHVuay5uZXdMaW5lcyxcbiAgICAgICAgb2xkU3RhcnQ6IGh1bmsubmV3U3RhcnQsXG4gICAgICAgIG5ld0xpbmVzOiBodW5rLm9sZExpbmVzLFxuICAgICAgICBuZXdTdGFydDogaHVuay5vbGRTdGFydCxcbiAgICAgICAgbGluZWRlbGltaXRlcnM6IGh1bmsubGluZWRlbGltaXRlcnMsXG4gICAgICAgIGxpbmVzOiBodW5rLmxpbmVzLm1hcChsID0+IHtcbiAgICAgICAgICBpZiAobC5zdGFydHNXaXRoKCctJykpIHsgcmV0dXJuIGArJHtsLnNsaWNlKDEpfWA7IH1cbiAgICAgICAgICBpZiAobC5zdGFydHNXaXRoKCcrJykpIHsgcmV0dXJuIGAtJHtsLnNsaWNlKDEpfWA7IH1cbiAgICAgICAgICByZXR1cm4gbDtcbiAgICAgICAgfSlcbiAgICAgIH07XG4gICAgfSlcbiAgfTtcbn1cbiJdfQ== +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyZXZlcnNlUGF0Y2giLCJzdHJ1Y3R1cmVkUGF0Y2giLCJBcnJheSIsImlzQXJyYXkiLCJtYXAiLCJyZXZlcnNlIiwiX29iamVjdFNwcmVhZCIsIm9sZEZpbGVOYW1lIiwibmV3RmlsZU5hbWUiLCJvbGRIZWFkZXIiLCJuZXdIZWFkZXIiLCJodW5rcyIsImh1bmsiLCJvbGRMaW5lcyIsIm5ld0xpbmVzIiwib2xkU3RhcnQiLCJuZXdTdGFydCIsImxpbmVzIiwibCIsInN0YXJ0c1dpdGgiLCJjb25jYXQiLCJzbGljZSJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9yZXZlcnNlLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBmdW5jdGlvbiByZXZlcnNlUGF0Y2goc3RydWN0dXJlZFBhdGNoKSB7XG4gIGlmIChBcnJheS5pc0FycmF5KHN0cnVjdHVyZWRQYXRjaCkpIHtcbiAgICByZXR1cm4gc3RydWN0dXJlZFBhdGNoLm1hcChyZXZlcnNlUGF0Y2gpLnJldmVyc2UoKTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgLi4uc3RydWN0dXJlZFBhdGNoLFxuICAgIG9sZEZpbGVOYW1lOiBzdHJ1Y3R1cmVkUGF0Y2gubmV3RmlsZU5hbWUsXG4gICAgb2xkSGVhZGVyOiBzdHJ1Y3R1cmVkUGF0Y2gubmV3SGVhZGVyLFxuICAgIG5ld0ZpbGVOYW1lOiBzdHJ1Y3R1cmVkUGF0Y2gub2xkRmlsZU5hbWUsXG4gICAgbmV3SGVhZGVyOiBzdHJ1Y3R1cmVkUGF0Y2gub2xkSGVhZGVyLFxuICAgIGh1bmtzOiBzdHJ1Y3R1cmVkUGF0Y2guaHVua3MubWFwKGh1bmsgPT4ge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgb2xkTGluZXM6IGh1bmsubmV3TGluZXMsXG4gICAgICAgIG9sZFN0YXJ0OiBodW5rLm5ld1N0YXJ0LFxuICAgICAgICBuZXdMaW5lczogaHVuay5vbGRMaW5lcyxcbiAgICAgICAgbmV3U3RhcnQ6IGh1bmsub2xkU3RhcnQsXG4gICAgICAgIGxpbmVzOiBodW5rLmxpbmVzLm1hcChsID0+IHtcbiAgICAgICAgICBpZiAobC5zdGFydHNXaXRoKCctJykpIHsgcmV0dXJuIGArJHtsLnNsaWNlKDEpfWA7IH1cbiAgICAgICAgICBpZiAobC5zdGFydHNXaXRoKCcrJykpIHsgcmV0dXJuIGAtJHtsLnNsaWNlKDEpfWA7IH1cbiAgICAgICAgICByZXR1cm4gbDtcbiAgICAgICAgfSlcbiAgICAgIH07XG4gICAgfSlcbiAgfTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7QUFBTyxTQUFTQSxZQUFZQSxDQUFDQyxlQUFlLEVBQUU7RUFDNUMsSUFBSUMsS0FBSyxDQUFDQyxPQUFPLENBQUNGLGVBQWUsQ0FBQyxFQUFFO0lBQ2xDLE9BQU9BLGVBQWUsQ0FBQ0csR0FBRyxDQUFDSixZQUFZLENBQUMsQ0FBQ0ssT0FBTyxDQUFDLENBQUM7RUFDcEQ7RUFFQTtJQUFBO0lBQUFDLGFBQUEsQ0FBQUEsYUFBQTtJQUFBO0lBQ0tMLGVBQWU7TUFDbEJNLFdBQVcsRUFBRU4sZUFBZSxDQUFDTyxXQUFXO01BQ3hDQyxTQUFTLEVBQUVSLGVBQWUsQ0FBQ1MsU0FBUztNQUNwQ0YsV0FBVyxFQUFFUCxlQUFlLENBQUNNLFdBQVc7TUFDeENHLFNBQVMsRUFBRVQsZUFBZSxDQUFDUSxTQUFTO01BQ3BDRSxLQUFLLEVBQUVWLGVBQWUsQ0FBQ1UsS0FBSyxDQUFDUCxHQUFHLENBQUMsVUFBQVEsSUFBSSxFQUFJO1FBQ3ZDLE9BQU87VUFDTEMsUUFBUSxFQUFFRCxJQUFJLENBQUNFLFFBQVE7VUFDdkJDLFFBQVEsRUFBRUgsSUFBSSxDQUFDSSxRQUFRO1VBQ3ZCRixRQUFRLEVBQUVGLElBQUksQ0FBQ0MsUUFBUTtVQUN2QkcsUUFBUSxFQUFFSixJQUFJLENBQUNHLFFBQVE7VUFDdkJFLEtBQUssRUFBRUwsSUFBSSxDQUFDSyxLQUFLLENBQUNiLEdBQUcsQ0FBQyxVQUFBYyxDQUFDLEVBQUk7WUFDekIsSUFBSUEsQ0FBQyxDQUFDQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUU7Y0FBRTtnQkFBQTtnQkFBQSxJQUFBQyxNQUFBO2dCQUFBO2dCQUFXRixDQUFDLENBQUNHLEtBQUssQ0FBQyxDQUFDLENBQUM7Y0FBQTtZQUFJO1lBQ2xELElBQUlILENBQUMsQ0FBQ0MsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFO2NBQUU7Z0JBQUE7Z0JBQUEsSUFBQUMsTUFBQTtnQkFBQTtnQkFBV0YsQ0FBQyxDQUFDRyxLQUFLLENBQUMsQ0FBQyxDQUFDO2NBQUE7WUFBSTtZQUNsRCxPQUFPSCxDQUFDO1VBQ1YsQ0FBQztRQUNILENBQUM7TUFDSCxDQUFDO0lBQUM7RUFBQTtBQUVOIiwiaWdub3JlTGlzdCI6W119 diff --git a/deps/npm/node_modules/diff/lib/util/array.js b/deps/npm/node_modules/diff/lib/util/array.js index aecf67ac817c16..af10977a70ac66 100644 --- a/deps/npm/node_modules/diff/lib/util/array.js +++ b/deps/npm/node_modules/diff/lib/util/array.js @@ -6,27 +6,22 @@ Object.defineProperty(exports, "__esModule", { }); exports.arrayEqual = arrayEqual; exports.arrayStartsWith = arrayStartsWith; - /*istanbul ignore end*/ function arrayEqual(a, b) { if (a.length !== b.length) { return false; } - return arrayStartsWith(a, b); } - function arrayStartsWith(array, start) { if (start.length > array.length) { return false; } - for (var i = 0; i < start.length; i++) { if (start[i] !== array[i]) { return false; } } - return true; } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL2FycmF5LmpzIl0sIm5hbWVzIjpbImFycmF5RXF1YWwiLCJhIiwiYiIsImxlbmd0aCIsImFycmF5U3RhcnRzV2l0aCIsImFycmF5Iiwic3RhcnQiLCJpIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQU8sU0FBU0EsVUFBVCxDQUFvQkMsQ0FBcEIsRUFBdUJDLENBQXZCLEVBQTBCO0FBQy9CLE1BQUlELENBQUMsQ0FBQ0UsTUFBRixLQUFhRCxDQUFDLENBQUNDLE1BQW5CLEVBQTJCO0FBQ3pCLFdBQU8sS0FBUDtBQUNEOztBQUVELFNBQU9DLGVBQWUsQ0FBQ0gsQ0FBRCxFQUFJQyxDQUFKLENBQXRCO0FBQ0Q7O0FBRU0sU0FBU0UsZUFBVCxDQUF5QkMsS0FBekIsRUFBZ0NDLEtBQWhDLEVBQXVDO0FBQzVDLE1BQUlBLEtBQUssQ0FBQ0gsTUFBTixHQUFlRSxLQUFLLENBQUNGLE1BQXpCLEVBQWlDO0FBQy9CLFdBQU8sS0FBUDtBQUNEOztBQUVELE9BQUssSUFBSUksQ0FBQyxHQUFHLENBQWIsRUFBZ0JBLENBQUMsR0FBR0QsS0FBSyxDQUFDSCxNQUExQixFQUFrQ0ksQ0FBQyxFQUFuQyxFQUF1QztBQUNyQyxRQUFJRCxLQUFLLENBQUNDLENBQUQsQ0FBTCxLQUFhRixLQUFLLENBQUNFLENBQUQsQ0FBdEIsRUFBMkI7QUFDekIsYUFBTyxLQUFQO0FBQ0Q7QUFDRjs7QUFFRCxTQUFPLElBQVA7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBmdW5jdGlvbiBhcnJheUVxdWFsKGEsIGIpIHtcbiAgaWYgKGEubGVuZ3RoICE9PSBiLmxlbmd0aCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHJldHVybiBhcnJheVN0YXJ0c1dpdGgoYSwgYik7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhcnJheVN0YXJ0c1dpdGgoYXJyYXksIHN0YXJ0KSB7XG4gIGlmIChzdGFydC5sZW5ndGggPiBhcnJheS5sZW5ndGgpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBmb3IgKGxldCBpID0gMDsgaSA8IHN0YXJ0Lmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKHN0YXJ0W2ldICE9PSBhcnJheVtpXSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufVxuIl19 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhcnJheUVxdWFsIiwiYSIsImIiLCJsZW5ndGgiLCJhcnJheVN0YXJ0c1dpdGgiLCJhcnJheSIsInN0YXJ0IiwiaSJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL2FycmF5LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBmdW5jdGlvbiBhcnJheUVxdWFsKGEsIGIpIHtcbiAgaWYgKGEubGVuZ3RoICE9PSBiLmxlbmd0aCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHJldHVybiBhcnJheVN0YXJ0c1dpdGgoYSwgYik7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhcnJheVN0YXJ0c1dpdGgoYXJyYXksIHN0YXJ0KSB7XG4gIGlmIChzdGFydC5sZW5ndGggPiBhcnJheS5sZW5ndGgpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBmb3IgKGxldCBpID0gMDsgaSA8IHN0YXJ0Lmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKHN0YXJ0W2ldICE9PSBhcnJheVtpXSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBTyxTQUFTQSxVQUFVQSxDQUFDQyxDQUFDLEVBQUVDLENBQUMsRUFBRTtFQUMvQixJQUFJRCxDQUFDLENBQUNFLE1BQU0sS0FBS0QsQ0FBQyxDQUFDQyxNQUFNLEVBQUU7SUFDekIsT0FBTyxLQUFLO0VBQ2Q7RUFFQSxPQUFPQyxlQUFlLENBQUNILENBQUMsRUFBRUMsQ0FBQyxDQUFDO0FBQzlCO0FBRU8sU0FBU0UsZUFBZUEsQ0FBQ0MsS0FBSyxFQUFFQyxLQUFLLEVBQUU7RUFDNUMsSUFBSUEsS0FBSyxDQUFDSCxNQUFNLEdBQUdFLEtBQUssQ0FBQ0YsTUFBTSxFQUFFO0lBQy9CLE9BQU8sS0FBSztFQUNkO0VBRUEsS0FBSyxJQUFJSSxDQUFDLEdBQUcsQ0FBQyxFQUFFQSxDQUFDLEdBQUdELEtBQUssQ0FBQ0gsTUFBTSxFQUFFSSxDQUFDLEVBQUUsRUFBRTtJQUNyQyxJQUFJRCxLQUFLLENBQUNDLENBQUMsQ0FBQyxLQUFLRixLQUFLLENBQUNFLENBQUMsQ0FBQyxFQUFFO01BQ3pCLE9BQU8sS0FBSztJQUNkO0VBQ0Y7RUFFQSxPQUFPLElBQUk7QUFDYiIsImlnbm9yZUxpc3QiOltdfQ== diff --git a/deps/npm/node_modules/diff/lib/util/distance-iterator.js b/deps/npm/node_modules/diff/lib/util/distance-iterator.js index 57c06a3f9cf1ce..63893731fb1509 100644 --- a/deps/npm/node_modules/diff/lib/util/distance-iterator.js +++ b/deps/npm/node_modules/diff/lib/util/distance-iterator.js @@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = _default; - /*istanbul ignore end*/ // Iterator that traverses in the range of [min, max], stepping // by distance from a given start position. I.e. for [0, 4], with @@ -16,42 +15,40 @@ _default /*istanbul ignore end*/ (start, minLine, maxLine) { var wantForward = true, - backwardExhausted = false, - forwardExhausted = false, - localOffset = 1; + backwardExhausted = false, + forwardExhausted = false, + localOffset = 1; return function iterator() { if (wantForward && !forwardExhausted) { if (backwardExhausted) { localOffset++; } else { wantForward = false; - } // Check if trying to fit beyond text length, and if not, check it fits - // after offset location (or desired location on first iteration) - + } + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) if (start + localOffset <= maxLine) { - return localOffset; + return start + localOffset; } - forwardExhausted = true; } - if (!backwardExhausted) { if (!forwardExhausted) { wantForward = true; - } // Check if trying to fit before text beginning, and if not, check it fits - // before offset location - + } + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location if (minLine <= start - localOffset) { - return -localOffset++; + return start - localOffset++; } - backwardExhausted = true; return iterator(); - } // We tried to fit hunk before text beginning and beyond text length, then - // hunk can't fit on the text. Return undefined + } + // We tried to fit hunk before text beginning and beyond text length, then + // hunk can't fit on the text. Return undefined }; } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL2Rpc3RhbmNlLWl0ZXJhdG9yLmpzIl0sIm5hbWVzIjpbInN0YXJ0IiwibWluTGluZSIsIm1heExpbmUiLCJ3YW50Rm9yd2FyZCIsImJhY2t3YXJkRXhoYXVzdGVkIiwiZm9yd2FyZEV4aGF1c3RlZCIsImxvY2FsT2Zmc2V0IiwiaXRlcmF0b3IiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBO0FBQ0E7QUFDQTtBQUNlO0FBQUE7QUFBQTtBQUFBO0FBQUEsQ0FBU0EsS0FBVCxFQUFnQkMsT0FBaEIsRUFBeUJDLE9BQXpCLEVBQWtDO0FBQy9DLE1BQUlDLFdBQVcsR0FBRyxJQUFsQjtBQUFBLE1BQ0lDLGlCQUFpQixHQUFHLEtBRHhCO0FBQUEsTUFFSUMsZ0JBQWdCLEdBQUcsS0FGdkI7QUFBQSxNQUdJQyxXQUFXLEdBQUcsQ0FIbEI7QUFLQSxTQUFPLFNBQVNDLFFBQVQsR0FBb0I7QUFDekIsUUFBSUosV0FBVyxJQUFJLENBQUNFLGdCQUFwQixFQUFzQztBQUNwQyxVQUFJRCxpQkFBSixFQUF1QjtBQUNyQkUsUUFBQUEsV0FBVztBQUNaLE9BRkQsTUFFTztBQUNMSCxRQUFBQSxXQUFXLEdBQUcsS0FBZDtBQUNELE9BTG1DLENBT3BDO0FBQ0E7OztBQUNBLFVBQUlILEtBQUssR0FBR00sV0FBUixJQUF1QkosT0FBM0IsRUFBb0M7QUFDbEMsZUFBT0ksV0FBUDtBQUNEOztBQUVERCxNQUFBQSxnQkFBZ0IsR0FBRyxJQUFuQjtBQUNEOztBQUVELFFBQUksQ0FBQ0QsaUJBQUwsRUFBd0I7QUFDdEIsVUFBSSxDQUFDQyxnQkFBTCxFQUF1QjtBQUNyQkYsUUFBQUEsV0FBVyxHQUFHLElBQWQ7QUFDRCxPQUhxQixDQUt0QjtBQUNBOzs7QUFDQSxVQUFJRixPQUFPLElBQUlELEtBQUssR0FBR00sV0FBdkIsRUFBb0M7QUFDbEMsZUFBTyxDQUFDQSxXQUFXLEVBQW5CO0FBQ0Q7O0FBRURGLE1BQUFBLGlCQUFpQixHQUFHLElBQXBCO0FBQ0EsYUFBT0csUUFBUSxFQUFmO0FBQ0QsS0E5QndCLENBZ0N6QjtBQUNBOztBQUNELEdBbENEO0FBbUNEIiwic291cmNlc0NvbnRlbnQiOlsiLy8gSXRlcmF0b3IgdGhhdCB0cmF2ZXJzZXMgaW4gdGhlIHJhbmdlIG9mIFttaW4sIG1heF0sIHN0ZXBwaW5nXG4vLyBieSBkaXN0YW5jZSBmcm9tIGEgZ2l2ZW4gc3RhcnQgcG9zaXRpb24uIEkuZS4gZm9yIFswLCA0XSwgd2l0aFxuLy8gc3RhcnQgb2YgMiwgdGhpcyB3aWxsIGl0ZXJhdGUgMiwgMywgMSwgNCwgMC5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uKHN0YXJ0LCBtaW5MaW5lLCBtYXhMaW5lKSB7XG4gIGxldCB3YW50Rm9yd2FyZCA9IHRydWUsXG4gICAgICBiYWNrd2FyZEV4aGF1c3RlZCA9IGZhbHNlLFxuICAgICAgZm9yd2FyZEV4aGF1c3RlZCA9IGZhbHNlLFxuICAgICAgbG9jYWxPZmZzZXQgPSAxO1xuXG4gIHJldHVybiBmdW5jdGlvbiBpdGVyYXRvcigpIHtcbiAgICBpZiAod2FudEZvcndhcmQgJiYgIWZvcndhcmRFeGhhdXN0ZWQpIHtcbiAgICAgIGlmIChiYWNrd2FyZEV4aGF1c3RlZCkge1xuICAgICAgICBsb2NhbE9mZnNldCsrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgd2FudEZvcndhcmQgPSBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgaWYgdHJ5aW5nIHRvIGZpdCBiZXlvbmQgdGV4dCBsZW5ndGgsIGFuZCBpZiBub3QsIGNoZWNrIGl0IGZpdHNcbiAgICAgIC8vIGFmdGVyIG9mZnNldCBsb2NhdGlvbiAob3IgZGVzaXJlZCBsb2NhdGlvbiBvbiBmaXJzdCBpdGVyYXRpb24pXG4gICAgICBpZiAoc3RhcnQgKyBsb2NhbE9mZnNldCA8PSBtYXhMaW5lKSB7XG4gICAgICAgIHJldHVybiBsb2NhbE9mZnNldDtcbiAgICAgIH1cblxuICAgICAgZm9yd2FyZEV4aGF1c3RlZCA9IHRydWU7XG4gICAgfVxuXG4gICAgaWYgKCFiYWNrd2FyZEV4aGF1c3RlZCkge1xuICAgICAgaWYgKCFmb3J3YXJkRXhoYXVzdGVkKSB7XG4gICAgICAgIHdhbnRGb3J3YXJkID0gdHJ1ZTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgaWYgdHJ5aW5nIHRvIGZpdCBiZWZvcmUgdGV4dCBiZWdpbm5pbmcsIGFuZCBpZiBub3QsIGNoZWNrIGl0IGZpdHNcbiAgICAgIC8vIGJlZm9yZSBvZmZzZXQgbG9jYXRpb25cbiAgICAgIGlmIChtaW5MaW5lIDw9IHN0YXJ0IC0gbG9jYWxPZmZzZXQpIHtcbiAgICAgICAgcmV0dXJuIC1sb2NhbE9mZnNldCsrO1xuICAgICAgfVxuXG4gICAgICBiYWNrd2FyZEV4aGF1c3RlZCA9IHRydWU7XG4gICAgICByZXR1cm4gaXRlcmF0b3IoKTtcbiAgICB9XG5cbiAgICAvLyBXZSB0cmllZCB0byBmaXQgaHVuayBiZWZvcmUgdGV4dCBiZWdpbm5pbmcgYW5kIGJleW9uZCB0ZXh0IGxlbmd0aCwgdGhlblxuICAgIC8vIGh1bmsgY2FuJ3QgZml0IG9uIHRoZSB0ZXh0LiBSZXR1cm4gdW5kZWZpbmVkXG4gIH07XG59XG4iXX0= +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfZGVmYXVsdCIsInN0YXJ0IiwibWluTGluZSIsIm1heExpbmUiLCJ3YW50Rm9yd2FyZCIsImJhY2t3YXJkRXhoYXVzdGVkIiwiZm9yd2FyZEV4aGF1c3RlZCIsImxvY2FsT2Zmc2V0IiwiaXRlcmF0b3IiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbC9kaXN0YW5jZS1pdGVyYXRvci5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBJdGVyYXRvciB0aGF0IHRyYXZlcnNlcyBpbiB0aGUgcmFuZ2Ugb2YgW21pbiwgbWF4XSwgc3RlcHBpbmdcbi8vIGJ5IGRpc3RhbmNlIGZyb20gYSBnaXZlbiBzdGFydCBwb3NpdGlvbi4gSS5lLiBmb3IgWzAsIDRdLCB3aXRoXG4vLyBzdGFydCBvZiAyLCB0aGlzIHdpbGwgaXRlcmF0ZSAyLCAzLCAxLCA0LCAwLlxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24oc3RhcnQsIG1pbkxpbmUsIG1heExpbmUpIHtcbiAgbGV0IHdhbnRGb3J3YXJkID0gdHJ1ZSxcbiAgICAgIGJhY2t3YXJkRXhoYXVzdGVkID0gZmFsc2UsXG4gICAgICBmb3J3YXJkRXhoYXVzdGVkID0gZmFsc2UsXG4gICAgICBsb2NhbE9mZnNldCA9IDE7XG5cbiAgcmV0dXJuIGZ1bmN0aW9uIGl0ZXJhdG9yKCkge1xuICAgIGlmICh3YW50Rm9yd2FyZCAmJiAhZm9yd2FyZEV4aGF1c3RlZCkge1xuICAgICAgaWYgKGJhY2t3YXJkRXhoYXVzdGVkKSB7XG4gICAgICAgIGxvY2FsT2Zmc2V0Kys7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB3YW50Rm9yd2FyZCA9IGZhbHNlO1xuICAgICAgfVxuXG4gICAgICAvLyBDaGVjayBpZiB0cnlpbmcgdG8gZml0IGJleW9uZCB0ZXh0IGxlbmd0aCwgYW5kIGlmIG5vdCwgY2hlY2sgaXQgZml0c1xuICAgICAgLy8gYWZ0ZXIgb2Zmc2V0IGxvY2F0aW9uIChvciBkZXNpcmVkIGxvY2F0aW9uIG9uIGZpcnN0IGl0ZXJhdGlvbilcbiAgICAgIGlmIChzdGFydCArIGxvY2FsT2Zmc2V0IDw9IG1heExpbmUpIHtcbiAgICAgICAgcmV0dXJuIHN0YXJ0ICsgbG9jYWxPZmZzZXQ7XG4gICAgICB9XG5cbiAgICAgIGZvcndhcmRFeGhhdXN0ZWQgPSB0cnVlO1xuICAgIH1cblxuICAgIGlmICghYmFja3dhcmRFeGhhdXN0ZWQpIHtcbiAgICAgIGlmICghZm9yd2FyZEV4aGF1c3RlZCkge1xuICAgICAgICB3YW50Rm9yd2FyZCA9IHRydWU7XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGlmIHRyeWluZyB0byBmaXQgYmVmb3JlIHRleHQgYmVnaW5uaW5nLCBhbmQgaWYgbm90LCBjaGVjayBpdCBmaXRzXG4gICAgICAvLyBiZWZvcmUgb2Zmc2V0IGxvY2F0aW9uXG4gICAgICBpZiAobWluTGluZSA8PSBzdGFydCAtIGxvY2FsT2Zmc2V0KSB7XG4gICAgICAgIHJldHVybiBzdGFydCAtIGxvY2FsT2Zmc2V0Kys7XG4gICAgICB9XG5cbiAgICAgIGJhY2t3YXJkRXhoYXVzdGVkID0gdHJ1ZTtcbiAgICAgIHJldHVybiBpdGVyYXRvcigpO1xuICAgIH1cblxuICAgIC8vIFdlIHRyaWVkIHRvIGZpdCBodW5rIGJlZm9yZSB0ZXh0IGJlZ2lubmluZyBhbmQgYmV5b25kIHRleHQgbGVuZ3RoLCB0aGVuXG4gICAgLy8gaHVuayBjYW4ndCBmaXQgb24gdGhlIHRleHQuIFJldHVybiB1bmRlZmluZWRcbiAgfTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFBQTtBQUNBO0FBQ0E7QUFDZTtBQUFBO0FBQUFBO0FBQUFBO0FBQUEsQ0FBU0MsS0FBSyxFQUFFQyxPQUFPLEVBQUVDLE9BQU8sRUFBRTtFQUMvQyxJQUFJQyxXQUFXLEdBQUcsSUFBSTtJQUNsQkMsaUJBQWlCLEdBQUcsS0FBSztJQUN6QkMsZ0JBQWdCLEdBQUcsS0FBSztJQUN4QkMsV0FBVyxHQUFHLENBQUM7RUFFbkIsT0FBTyxTQUFTQyxRQUFRQSxDQUFBLEVBQUc7SUFDekIsSUFBSUosV0FBVyxJQUFJLENBQUNFLGdCQUFnQixFQUFFO01BQ3BDLElBQUlELGlCQUFpQixFQUFFO1FBQ3JCRSxXQUFXLEVBQUU7TUFDZixDQUFDLE1BQU07UUFDTEgsV0FBVyxHQUFHLEtBQUs7TUFDckI7O01BRUE7TUFDQTtNQUNBLElBQUlILEtBQUssR0FBR00sV0FBVyxJQUFJSixPQUFPLEVBQUU7UUFDbEMsT0FBT0YsS0FBSyxHQUFHTSxXQUFXO01BQzVCO01BRUFELGdCQUFnQixHQUFHLElBQUk7SUFDekI7SUFFQSxJQUFJLENBQUNELGlCQUFpQixFQUFFO01BQ3RCLElBQUksQ0FBQ0MsZ0JBQWdCLEVBQUU7UUFDckJGLFdBQVcsR0FBRyxJQUFJO01BQ3BCOztNQUVBO01BQ0E7TUFDQSxJQUFJRixPQUFPLElBQUlELEtBQUssR0FBR00sV0FBVyxFQUFFO1FBQ2xDLE9BQU9OLEtBQUssR0FBR00sV0FBVyxFQUFFO01BQzlCO01BRUFGLGlCQUFpQixHQUFHLElBQUk7TUFDeEIsT0FBT0csUUFBUSxDQUFDLENBQUM7SUFDbkI7O0lBRUE7SUFDQTtFQUNGLENBQUM7QUFDSCIsImlnbm9yZUxpc3QiOltdfQ== diff --git a/deps/npm/node_modules/diff/lib/util/params.js b/deps/npm/node_modules/diff/lib/util/params.js index e838eb2f42d157..283c2472bc601e 100644 --- a/deps/npm/node_modules/diff/lib/util/params.js +++ b/deps/npm/node_modules/diff/lib/util/params.js @@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.generateOptions = generateOptions; - /*istanbul ignore end*/ function generateOptions(options, defaults) { if (typeof options === 'function') { @@ -18,7 +17,6 @@ function generateOptions(options, defaults) { } } } - return defaults; } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL3BhcmFtcy5qcyJdLCJuYW1lcyI6WyJnZW5lcmF0ZU9wdGlvbnMiLCJvcHRpb25zIiwiZGVmYXVsdHMiLCJjYWxsYmFjayIsIm5hbWUiLCJoYXNPd25Qcm9wZXJ0eSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQU8sU0FBU0EsZUFBVCxDQUF5QkMsT0FBekIsRUFBa0NDLFFBQWxDLEVBQTRDO0FBQ2pELE1BQUksT0FBT0QsT0FBUCxLQUFtQixVQUF2QixFQUFtQztBQUNqQ0MsSUFBQUEsUUFBUSxDQUFDQyxRQUFULEdBQW9CRixPQUFwQjtBQUNELEdBRkQsTUFFTyxJQUFJQSxPQUFKLEVBQWE7QUFDbEIsU0FBSyxJQUFJRyxJQUFULElBQWlCSCxPQUFqQixFQUEwQjtBQUN4QjtBQUNBLFVBQUlBLE9BQU8sQ0FBQ0ksY0FBUixDQUF1QkQsSUFBdkIsQ0FBSixFQUFrQztBQUNoQ0YsUUFBQUEsUUFBUSxDQUFDRSxJQUFELENBQVIsR0FBaUJILE9BQU8sQ0FBQ0csSUFBRCxDQUF4QjtBQUNEO0FBQ0Y7QUFDRjs7QUFDRCxTQUFPRixRQUFQO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVPcHRpb25zKG9wdGlvbnMsIGRlZmF1bHRzKSB7XG4gIGlmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIGRlZmF1bHRzLmNhbGxiYWNrID0gb3B0aW9ucztcbiAgfSBlbHNlIGlmIChvcHRpb25zKSB7XG4gICAgZm9yIChsZXQgbmFtZSBpbiBvcHRpb25zKSB7XG4gICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgZWxzZSAqL1xuICAgICAgaWYgKG9wdGlvbnMuaGFzT3duUHJvcGVydHkobmFtZSkpIHtcbiAgICAgICAgZGVmYXVsdHNbbmFtZV0gPSBvcHRpb25zW25hbWVdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gZGVmYXVsdHM7XG59XG4iXX0= +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJnZW5lcmF0ZU9wdGlvbnMiLCJvcHRpb25zIiwiZGVmYXVsdHMiLCJjYWxsYmFjayIsIm5hbWUiLCJoYXNPd25Qcm9wZXJ0eSJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL3BhcmFtcy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVPcHRpb25zKG9wdGlvbnMsIGRlZmF1bHRzKSB7XG4gIGlmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIGRlZmF1bHRzLmNhbGxiYWNrID0gb3B0aW9ucztcbiAgfSBlbHNlIGlmIChvcHRpb25zKSB7XG4gICAgZm9yIChsZXQgbmFtZSBpbiBvcHRpb25zKSB7XG4gICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgZWxzZSAqL1xuICAgICAgaWYgKG9wdGlvbnMuaGFzT3duUHJvcGVydHkobmFtZSkpIHtcbiAgICAgICAgZGVmYXVsdHNbbmFtZV0gPSBvcHRpb25zW25hbWVdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gZGVmYXVsdHM7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBQU8sU0FBU0EsZUFBZUEsQ0FBQ0MsT0FBTyxFQUFFQyxRQUFRLEVBQUU7RUFDakQsSUFBSSxPQUFPRCxPQUFPLEtBQUssVUFBVSxFQUFFO0lBQ2pDQyxRQUFRLENBQUNDLFFBQVEsR0FBR0YsT0FBTztFQUM3QixDQUFDLE1BQU0sSUFBSUEsT0FBTyxFQUFFO0lBQ2xCLEtBQUssSUFBSUcsSUFBSSxJQUFJSCxPQUFPLEVBQUU7TUFDeEI7TUFDQSxJQUFJQSxPQUFPLENBQUNJLGNBQWMsQ0FBQ0QsSUFBSSxDQUFDLEVBQUU7UUFDaENGLFFBQVEsQ0FBQ0UsSUFBSSxDQUFDLEdBQUdILE9BQU8sQ0FBQ0csSUFBSSxDQUFDO01BQ2hDO0lBQ0Y7RUFDRjtFQUNBLE9BQU9GLFFBQVE7QUFDakIiLCJpZ25vcmVMaXN0IjpbXX0= diff --git a/deps/npm/node_modules/diff/lib/util/string.js b/deps/npm/node_modules/diff/lib/util/string.js new file mode 100644 index 00000000000000..f81c6827be731b --- /dev/null +++ b/deps/npm/node_modules/diff/lib/util/string.js @@ -0,0 +1,131 @@ +/*istanbul ignore start*/ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.hasOnlyUnixLineEndings = hasOnlyUnixLineEndings; +exports.hasOnlyWinLineEndings = hasOnlyWinLineEndings; +exports.longestCommonPrefix = longestCommonPrefix; +exports.longestCommonSuffix = longestCommonSuffix; +exports.maximumOverlap = maximumOverlap; +exports.removePrefix = removePrefix; +exports.removeSuffix = removeSuffix; +exports.replacePrefix = replacePrefix; +exports.replaceSuffix = replaceSuffix; +/*istanbul ignore end*/ +function longestCommonPrefix(str1, str2) { + var i; + for (i = 0; i < str1.length && i < str2.length; i++) { + if (str1[i] != str2[i]) { + return str1.slice(0, i); + } + } + return str1.slice(0, i); +} +function longestCommonSuffix(str1, str2) { + var i; + + // Unlike longestCommonPrefix, we need a special case to handle all scenarios + // where we return the empty string since str1.slice(-0) will return the + // entire string. + if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) { + return ''; + } + for (i = 0; i < str1.length && i < str2.length; i++) { + if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) { + return str1.slice(-i); + } + } + return str1.slice(-i); +} +function replacePrefix(string, oldPrefix, newPrefix) { + if (string.slice(0, oldPrefix.length) != oldPrefix) { + throw Error( + /*istanbul ignore start*/ + "string ".concat( + /*istanbul ignore end*/ + JSON.stringify(string), " doesn't start with prefix ").concat(JSON.stringify(oldPrefix), "; this is a bug")); + } + return newPrefix + string.slice(oldPrefix.length); +} +function replaceSuffix(string, oldSuffix, newSuffix) { + if (!oldSuffix) { + return string + newSuffix; + } + if (string.slice(-oldSuffix.length) != oldSuffix) { + throw Error( + /*istanbul ignore start*/ + "string ".concat( + /*istanbul ignore end*/ + JSON.stringify(string), " doesn't end with suffix ").concat(JSON.stringify(oldSuffix), "; this is a bug")); + } + return string.slice(0, -oldSuffix.length) + newSuffix; +} +function removePrefix(string, oldPrefix) { + return replacePrefix(string, oldPrefix, ''); +} +function removeSuffix(string, oldSuffix) { + return replaceSuffix(string, oldSuffix, ''); +} +function maximumOverlap(string1, string2) { + return string2.slice(0, overlapCount(string1, string2)); +} + +// Nicked from https://stackoverflow.com/a/60422853/1709587 +function overlapCount(a, b) { + // Deal with cases where the strings differ in length + var startA = 0; + if (a.length > b.length) { + startA = a.length - b.length; + } + var endB = b.length; + if (a.length < b.length) { + endB = a.length; + } + // Create a back-reference for each index + // that should be followed in case of a mismatch. + // We only need B to make these references: + var map = Array(endB); + var k = 0; // Index that lags behind j + map[0] = 0; + for (var j = 1; j < endB; j++) { + if (b[j] == b[k]) { + map[j] = map[k]; // skip over the same character (optional optimisation) + } else { + map[j] = k; + } + while (k > 0 && b[j] != b[k]) { + k = map[k]; + } + if (b[j] == b[k]) { + k++; + } + } + // Phase 2: use these references while iterating over A + k = 0; + for (var i = startA; i < a.length; i++) { + while (k > 0 && a[i] != b[k]) { + k = map[k]; + } + if (a[i] == b[k]) { + k++; + } + } + return k; +} + +/** + * Returns true if the string consistently uses Windows line endings. + */ +function hasOnlyWinLineEndings(string) { + return string.includes('\r\n') && !string.startsWith('\n') && !string.match(/[^\r]\n/); +} + +/** + * Returns true if the string consistently uses Unix line endings. + */ +function hasOnlyUnixLineEndings(string) { + return !string.includes('\r\n') && string.includes('\n'); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJsb25nZXN0Q29tbW9uUHJlZml4Iiwic3RyMSIsInN0cjIiLCJpIiwibGVuZ3RoIiwic2xpY2UiLCJsb25nZXN0Q29tbW9uU3VmZml4IiwicmVwbGFjZVByZWZpeCIsInN0cmluZyIsIm9sZFByZWZpeCIsIm5ld1ByZWZpeCIsIkVycm9yIiwiY29uY2F0IiwiSlNPTiIsInN0cmluZ2lmeSIsInJlcGxhY2VTdWZmaXgiLCJvbGRTdWZmaXgiLCJuZXdTdWZmaXgiLCJyZW1vdmVQcmVmaXgiLCJyZW1vdmVTdWZmaXgiLCJtYXhpbXVtT3ZlcmxhcCIsInN0cmluZzEiLCJzdHJpbmcyIiwib3ZlcmxhcENvdW50IiwiYSIsImIiLCJzdGFydEEiLCJlbmRCIiwibWFwIiwiQXJyYXkiLCJrIiwiaiIsImhhc09ubHlXaW5MaW5lRW5kaW5ncyIsImluY2x1ZGVzIiwic3RhcnRzV2l0aCIsIm1hdGNoIiwiaGFzT25seVVuaXhMaW5lRW5kaW5ncyJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL3N0cmluZy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gbG9uZ2VzdENvbW1vblByZWZpeChzdHIxLCBzdHIyKSB7XG4gIGxldCBpO1xuICBmb3IgKGkgPSAwOyBpIDwgc3RyMS5sZW5ndGggJiYgaSA8IHN0cjIubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAoc3RyMVtpXSAhPSBzdHIyW2ldKSB7XG4gICAgICByZXR1cm4gc3RyMS5zbGljZSgwLCBpKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHN0cjEuc2xpY2UoMCwgaSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb25nZXN0Q29tbW9uU3VmZml4KHN0cjEsIHN0cjIpIHtcbiAgbGV0IGk7XG5cbiAgLy8gVW5saWtlIGxvbmdlc3RDb21tb25QcmVmaXgsIHdlIG5lZWQgYSBzcGVjaWFsIGNhc2UgdG8gaGFuZGxlIGFsbCBzY2VuYXJpb3NcbiAgLy8gd2hlcmUgd2UgcmV0dXJuIHRoZSBlbXB0eSBzdHJpbmcgc2luY2Ugc3RyMS5zbGljZSgtMCkgd2lsbCByZXR1cm4gdGhlXG4gIC8vIGVudGlyZSBzdHJpbmcuXG4gIGlmICghc3RyMSB8fCAhc3RyMiB8fCBzdHIxW3N0cjEubGVuZ3RoIC0gMV0gIT0gc3RyMltzdHIyLmxlbmd0aCAtIDFdKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG5cbiAgZm9yIChpID0gMDsgaSA8IHN0cjEubGVuZ3RoICYmIGkgPCBzdHIyLmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKHN0cjFbc3RyMS5sZW5ndGggLSAoaSArIDEpXSAhPSBzdHIyW3N0cjIubGVuZ3RoIC0gKGkgKyAxKV0pIHtcbiAgICAgIHJldHVybiBzdHIxLnNsaWNlKC1pKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHN0cjEuc2xpY2UoLWkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVwbGFjZVByZWZpeChzdHJpbmcsIG9sZFByZWZpeCwgbmV3UHJlZml4KSB7XG4gIGlmIChzdHJpbmcuc2xpY2UoMCwgb2xkUHJlZml4Lmxlbmd0aCkgIT0gb2xkUHJlZml4KSB7XG4gICAgdGhyb3cgRXJyb3IoYHN0cmluZyAke0pTT04uc3RyaW5naWZ5KHN0cmluZyl9IGRvZXNuJ3Qgc3RhcnQgd2l0aCBwcmVmaXggJHtKU09OLnN0cmluZ2lmeShvbGRQcmVmaXgpfTsgdGhpcyBpcyBhIGJ1Z2ApO1xuICB9XG4gIHJldHVybiBuZXdQcmVmaXggKyBzdHJpbmcuc2xpY2Uob2xkUHJlZml4Lmxlbmd0aCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXBsYWNlU3VmZml4KHN0cmluZywgb2xkU3VmZml4LCBuZXdTdWZmaXgpIHtcbiAgaWYgKCFvbGRTdWZmaXgpIHtcbiAgICByZXR1cm4gc3RyaW5nICsgbmV3U3VmZml4O1xuICB9XG5cbiAgaWYgKHN0cmluZy5zbGljZSgtb2xkU3VmZml4Lmxlbmd0aCkgIT0gb2xkU3VmZml4KSB7XG4gICAgdGhyb3cgRXJyb3IoYHN0cmluZyAke0pTT04uc3RyaW5naWZ5KHN0cmluZyl9IGRvZXNuJ3QgZW5kIHdpdGggc3VmZml4ICR7SlNPTi5zdHJpbmdpZnkob2xkU3VmZml4KX07IHRoaXMgaXMgYSBidWdgKTtcbiAgfVxuICByZXR1cm4gc3RyaW5nLnNsaWNlKDAsIC1vbGRTdWZmaXgubGVuZ3RoKSArIG5ld1N1ZmZpeDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbW92ZVByZWZpeChzdHJpbmcsIG9sZFByZWZpeCkge1xuICByZXR1cm4gcmVwbGFjZVByZWZpeChzdHJpbmcsIG9sZFByZWZpeCwgJycpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVtb3ZlU3VmZml4KHN0cmluZywgb2xkU3VmZml4KSB7XG4gIHJldHVybiByZXBsYWNlU3VmZml4KHN0cmluZywgb2xkU3VmZml4LCAnJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtYXhpbXVtT3ZlcmxhcChzdHJpbmcxLCBzdHJpbmcyKSB7XG4gIHJldHVybiBzdHJpbmcyLnNsaWNlKDAsIG92ZXJsYXBDb3VudChzdHJpbmcxLCBzdHJpbmcyKSk7XG59XG5cbi8vIE5pY2tlZCBmcm9tIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS82MDQyMjg1My8xNzA5NTg3XG5mdW5jdGlvbiBvdmVybGFwQ291bnQoYSwgYikge1xuICAvLyBEZWFsIHdpdGggY2FzZXMgd2hlcmUgdGhlIHN0cmluZ3MgZGlmZmVyIGluIGxlbmd0aFxuICBsZXQgc3RhcnRBID0gMDtcbiAgaWYgKGEubGVuZ3RoID4gYi5sZW5ndGgpIHsgc3RhcnRBID0gYS5sZW5ndGggLSBiLmxlbmd0aDsgfVxuICBsZXQgZW5kQiA9IGIubGVuZ3RoO1xuICBpZiAoYS5sZW5ndGggPCBiLmxlbmd0aCkgeyBlbmRCID0gYS5sZW5ndGg7IH1cbiAgLy8gQ3JlYXRlIGEgYmFjay1yZWZlcmVuY2UgZm9yIGVhY2ggaW5kZXhcbiAgLy8gICB0aGF0IHNob3VsZCBiZSBmb2xsb3dlZCBpbiBjYXNlIG9mIGEgbWlzbWF0Y2guXG4gIC8vICAgV2Ugb25seSBuZWVkIEIgdG8gbWFrZSB0aGVzZSByZWZlcmVuY2VzOlxuICBsZXQgbWFwID0gQXJyYXkoZW5kQik7XG4gIGxldCBrID0gMDsgLy8gSW5kZXggdGhhdCBsYWdzIGJlaGluZCBqXG4gIG1hcFswXSA9IDA7XG4gIGZvciAobGV0IGogPSAxOyBqIDwgZW5kQjsgaisrKSB7XG4gICAgICBpZiAoYltqXSA9PSBiW2tdKSB7XG4gICAgICAgICAgbWFwW2pdID0gbWFwW2tdOyAvLyBza2lwIG92ZXIgdGhlIHNhbWUgY2hhcmFjdGVyIChvcHRpb25hbCBvcHRpbWlzYXRpb24pXG4gICAgICB9IGVsc2Uge1xuICAgICAgICAgIG1hcFtqXSA9IGs7XG4gICAgICB9XG4gICAgICB3aGlsZSAoayA+IDAgJiYgYltqXSAhPSBiW2tdKSB7IGsgPSBtYXBba107IH1cbiAgICAgIGlmIChiW2pdID09IGJba10pIHsgaysrOyB9XG4gIH1cbiAgLy8gUGhhc2UgMjogdXNlIHRoZXNlIHJlZmVyZW5jZXMgd2hpbGUgaXRlcmF0aW5nIG92ZXIgQVxuICBrID0gMDtcbiAgZm9yIChsZXQgaSA9IHN0YXJ0QTsgaSA8IGEubGVuZ3RoOyBpKyspIHtcbiAgICAgIHdoaWxlIChrID4gMCAmJiBhW2ldICE9IGJba10pIHsgayA9IG1hcFtrXTsgfVxuICAgICAgaWYgKGFbaV0gPT0gYltrXSkgeyBrKys7IH1cbiAgfVxuICByZXR1cm4gaztcbn1cblxuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgc3RyaW5nIGNvbnNpc3RlbnRseSB1c2VzIFdpbmRvd3MgbGluZSBlbmRpbmdzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFzT25seVdpbkxpbmVFbmRpbmdzKHN0cmluZykge1xuICByZXR1cm4gc3RyaW5nLmluY2x1ZGVzKCdcXHJcXG4nKSAmJiAhc3RyaW5nLnN0YXJ0c1dpdGgoJ1xcbicpICYmICFzdHJpbmcubWF0Y2goL1teXFxyXVxcbi8pO1xufVxuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgc3RyaW5nIGNvbnNpc3RlbnRseSB1c2VzIFVuaXggbGluZSBlbmRpbmdzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFzT25seVVuaXhMaW5lRW5kaW5ncyhzdHJpbmcpIHtcbiAgcmV0dXJuICFzdHJpbmcuaW5jbHVkZXMoJ1xcclxcbicpICYmIHN0cmluZy5pbmNsdWRlcygnXFxuJyk7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBTyxTQUFTQSxtQkFBbUJBLENBQUNDLElBQUksRUFBRUMsSUFBSSxFQUFFO0VBQzlDLElBQUlDLENBQUM7RUFDTCxLQUFLQSxDQUFDLEdBQUcsQ0FBQyxFQUFFQSxDQUFDLEdBQUdGLElBQUksQ0FBQ0csTUFBTSxJQUFJRCxDQUFDLEdBQUdELElBQUksQ0FBQ0UsTUFBTSxFQUFFRCxDQUFDLEVBQUUsRUFBRTtJQUNuRCxJQUFJRixJQUFJLENBQUNFLENBQUMsQ0FBQyxJQUFJRCxJQUFJLENBQUNDLENBQUMsQ0FBQyxFQUFFO01BQ3RCLE9BQU9GLElBQUksQ0FBQ0ksS0FBSyxDQUFDLENBQUMsRUFBRUYsQ0FBQyxDQUFDO0lBQ3pCO0VBQ0Y7RUFDQSxPQUFPRixJQUFJLENBQUNJLEtBQUssQ0FBQyxDQUFDLEVBQUVGLENBQUMsQ0FBQztBQUN6QjtBQUVPLFNBQVNHLG1CQUFtQkEsQ0FBQ0wsSUFBSSxFQUFFQyxJQUFJLEVBQUU7RUFDOUMsSUFBSUMsQ0FBQzs7RUFFTDtFQUNBO0VBQ0E7RUFDQSxJQUFJLENBQUNGLElBQUksSUFBSSxDQUFDQyxJQUFJLElBQUlELElBQUksQ0FBQ0EsSUFBSSxDQUFDRyxNQUFNLEdBQUcsQ0FBQyxDQUFDLElBQUlGLElBQUksQ0FBQ0EsSUFBSSxDQUFDRSxNQUFNLEdBQUcsQ0FBQyxDQUFDLEVBQUU7SUFDcEUsT0FBTyxFQUFFO0VBQ1g7RUFFQSxLQUFLRCxDQUFDLEdBQUcsQ0FBQyxFQUFFQSxDQUFDLEdBQUdGLElBQUksQ0FBQ0csTUFBTSxJQUFJRCxDQUFDLEdBQUdELElBQUksQ0FBQ0UsTUFBTSxFQUFFRCxDQUFDLEVBQUUsRUFBRTtJQUNuRCxJQUFJRixJQUFJ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 diff --git a/deps/npm/node_modules/diff/package.json b/deps/npm/node_modules/diff/package.json index dcffb9474baefc..400c8dd8fe9b3e 100644 --- a/deps/npm/node_modules/diff/package.json +++ b/deps/npm/node_modules/diff/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "5.2.0", + "version": "7.0.0", "description": "A JavaScript text diff implementation.", "keywords": [ "diff", @@ -47,43 +47,42 @@ "test": "grunt" }, "devDependencies": { - "@babel/cli": "^7.2.3", - "@babel/core": "^7.2.2", - "@babel/plugin-transform-modules-commonjs": "^7.2.0", - "@babel/preset-env": "^7.2.3", - "@babel/register": "^7.0.0", - "@colors/colors": "^1.3.3", + "@babel/cli": "^7.24.1", + "@babel/core": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/preset-env": "^7.24.1", + "@babel/register": "^7.23.7", + "@colors/colors": "^1.6.0", "babel-eslint": "^10.0.1", - "babel-loader": "^8.0.5", + "babel-loader": "^9.1.3", "chai": "^4.2.0", "eslint": "^5.12.0", - "grunt": "^1.0.3", + "grunt": "^1.6.1", "grunt-babel": "^8.0.0", - "grunt-cli": "^1.3.2", - "grunt-contrib-clean": "^2.0.0", + "grunt-cli": "^1.4.3", + "grunt-contrib-clean": "^2.0.1", "grunt-contrib-copy": "^1.0.0", - "grunt-contrib-uglify": "^5.0.0", + "grunt-contrib-uglify": "^5.2.2", "grunt-contrib-watch": "^1.1.0", - "grunt-eslint": "^23.0.0", + "grunt-eslint": "^24.3.0", "grunt-exec": "^3.0.0", - "grunt-karma": "^4.0.0", + "grunt-karma": "^4.0.2", "grunt-mocha-istanbul": "^5.0.2", "grunt-mocha-test": "^0.13.3", - "grunt-webpack": "^3.1.3", + "grunt-webpack": "^6.0.0", "istanbul": "github:kpdecker/istanbul", - "karma": "^6.3.16", - "karma-chrome-launcher": "^3.1.0", + "karma": "^6.4.3", + "karma-chrome-launcher": "^3.2.0", "karma-mocha": "^2.0.1", - "karma-mocha-reporter": "^2.0.0", - "karma-sauce-launcher": "^4.1.5", - "karma-sourcemap-loader": "^0.3.6", - "karma-webpack": "^4.0.2", - "mocha": "^6.0.0", - "rollup": "^1.0.2", + "karma-mocha-reporter": "^2.2.5", + "karma-sourcemap-loader": "^0.4.0", + "karma-webpack": "^5.0.1", + "mocha": "^7.0.0", + "rollup": "^4.13.0", "rollup-plugin-babel": "^4.2.0", - "semver": "^7.3.2", - "webpack": "^4.28.3", - "webpack-dev-server": "^3.1.14" + "semver": "^7.6.0", + "webpack": "^5.90.3", + "webpack-dev-server": "^5.0.3" }, "optionalDependencies": {} } diff --git a/deps/npm/node_modules/diff/release-notes.md b/deps/npm/node_modules/diff/release-notes.md index fe98f22d239e7e..21b5d41d6188b1 100644 --- a/deps/npm/node_modules/diff/release-notes.md +++ b/deps/npm/node_modules/diff/release-notes.md @@ -1,8 +1,52 @@ # Release Notes +## 7.0.0 + +Just a single (breaking) bugfix, undoing a behaviour change introduced accidentally in 6.0.0: + +- [#554](https://github.com/kpdecker/jsdiff/pull/554) **`diffWords` treats numbers and underscores as word characters again.** This behaviour was broken in v6.0.0. + +## 6.0.0 + +This is a release containing many, *many* breaking changes. The objective of this release was to carry out a mass fix, in one go, of all the open bugs and design problems that required breaking changes to fix. A substantial, but exhaustive, changelog is below. + +[Commits](https://github.com/kpdecker/jsdiff/compare/v5.2.0...v6.0.0) + +- [#497](https://github.com/kpdecker/jsdiff/pull/497) **`diffWords` behavior has been radically changed.** Previously, even with `ignoreWhitespace: true`, runs of whitespace were tokens, which led to unhelpful and unintuitive diffing behavior in typical texts. Specifically, even when two texts contained overlapping passages, `diffWords` would sometimes choose to delete all the words from the old text and insert them anew in their new positions in order to avoid having to delete or insert whitespace tokens. Whitespace sequences are no longer tokens as of this release, which affects both the generated diffs and the `count`s. + + Runs of whitespace are still tokens in `diffWordsWithSpace`. + + As part of the changes to `diffWords`, **a new `.postProcess` method has been added on the base `Diff` type**, which can be overridden in custom `Diff` implementations. + + **`diffLines` with `ignoreWhitespace: true` will no longer ignore the insertion or deletion of entire extra lines of whitespace at the end of the text**. Previously, these would not show up as insertions or deletions, as a side effect of a hack in the base diffing algorithm meant to help ignore whitespace in `diffWords`. More generally, **the undocumented special handling in the core algorithm for ignored terminals has been removed entirely.** (This special case behavior used to rewrite the final two change objects in a scenario where the final change object was an addition or deletion and its `value` was treated as equal to the empty string when compared using the diff object's `.equals` method.) + +- [#500](https://github.com/kpdecker/jsdiff/pull/500) **`diffChars` now diffs Unicode code points** instead of UTF-16 code units. +- [#508](https://github.com/kpdecker/jsdiff/pull/508) **`parsePatch` now always runs in what was previously "strict" mode; the undocumented `strict` option has been removed.** Previously, by default, `parsePatch` (and other patch functions that use it under the hood to parse patches) would accept a patch where the line counts in the headers were inconsistent with the actual patch content - e.g. where a hunk started with the header `@@ -1,3 +1,6 @@`, indicating that the content below spanned 3 lines in the old file and 6 lines in the new file, but then the actual content below the header consisted of some different number of lines, say 10 lines of context, 5 deletions, and 1 insertion. Actually trying to work with these patches using `applyPatch` or `merge`, however, would produce incorrect results instead of just ignoring the incorrect headers, making this "feature" more of a trap than something actually useful. It's been ripped out, and now we are always "strict" and will reject patches where the line counts in the headers aren't consistent with the actual patch content. +- [#435](https://github.com/kpdecker/jsdiff/pull/435) **Fix `parsePatch` handling of control characters.** `parsePatch` used to interpret various unusual control characters - namely vertical tabs, form feeds, lone carriage returns without a line feed, and EBCDIC NELs - as line breaks when parsing a patch file. This was inconsistent with the behavior of both JsDiff's own `diffLines` method and also the Unix `diff` and `patch` utils, which all simply treat those control characters as ordinary characters. The result of this discrepancy was that some well-formed patches - produced either by `diff` or by JsDiff itself and handled properly by the `patch` util - would be wrongly parsed by `parsePatch`, with the effect that it would disregard the remainder of a hunk after encountering one of these control characters. +- [#439](https://github.com/kpdecker/jsdiff/pull/439) **Prefer diffs that order deletions before insertions.** When faced with a choice between two diffs with an equal total edit distance, the Myers diff algorithm generally prefers one that does deletions before insertions rather than insertions before deletions. For instance, when diffing `abcd` against `acbd`, it will prefer a diff that says to delete the `b` and then insert a new `b` after the `c`, over a diff that says to insert a `c` before the `b` and then delete the existing `c`. JsDiff deviated from the published Myers algorithm in a way that led to it having the opposite preference in many cases, including that example. This is now fixed, meaning diffs output by JsDiff will more accurately reflect what the published Myers diff algorithm would output. +- [#455](https://github.com/kpdecker/jsdiff/pull/455) **The `added` and `removed` properties of change objects are now guaranteed to be set to a boolean value.** (Previously, they would be set to `undefined` or omitted entirely instead of setting them to false.) +- [#464](https://github.com/kpdecker/jsdiff/pull/464) Specifying `{maxEditLength: 0}` now sets a max edit length of 0 instead of no maximum. +- [#460](https://github.com/kpdecker/jsdiff/pull/460) **Added `oneChangePerToken` option.** +- [#467](https://github.com/kpdecker/jsdiff/pull/467) **Consistent ordering of arguments to `comparator(left, right)`.** Values from the old array will now consistently be passed as the first argument (`left`) and values from the new array as the second argument (`right`). Previously this was almost (but not quite) always the other way round. +- [#480](https://github.com/kpdecker/jsdiff/pull/480) **Passing `maxEditLength` to `createPatch` & `createTwoFilesPatch` now works properly** (i.e. returns undefined if the max edit distance is exceeded; previous behavior was to crash with a `TypeError` if the edit distance was exceeded). +- [#486](https://github.com/kpdecker/jsdiff/pull/486) **The `ignoreWhitespace` option of `diffLines` behaves more sensibly now.** `value`s in returned change objects now include leading/trailing whitespace even when `ignoreWhitespace` is used, just like how with `ignoreCase` the `value`s still reflect the case of one of the original texts instead of being all-lowercase. `ignoreWhitespace` is also now compatible with `newlineIsToken`. Finally, **`diffTrimmedLines` is deprecated** (and removed from the docs) in favour of using `diffLines` with `ignoreWhitespace: true`; the two are, and always have been, equivalent. +- [#490](https://github.com/kpdecker/jsdiff/pull/490) **When calling diffing functions in async mode by passing a `callback` option, the diff result will now be passed as the *first* argument to the callback instead of the second.** (Previously, the first argument was never used at all and would always have value `undefined`.) +- [#489](github.com/kpdecker/jsdiff/pull/489) **`this.options` no longer exists on `Diff` objects.** Instead, `options` is now passed as an argument to methods that rely on options, like `equals(left, right, options)`. This fixes a race condition in async mode, where diffing behaviour could be changed mid-execution if a concurrent usage of the same `Diff` instances overwrote its `options`. +- [#518](https://github.com/kpdecker/jsdiff/pull/518) **`linedelimiters` no longer exists** on patch objects; instead, when a patch with Windows-style CRLF line endings is parsed, **the lines in `lines` will end with `\r`**. There is now a **new `autoConvertLineEndings` option, on by default**, which makes it so that when a patch with Windows-style line endings is applied to a source file with Unix style line endings, the patch gets autoconverted to use Unix-style line endings, and when a patch with Unix-style line endings is applied to a source file with Windows-style line endings, it gets autoconverted to use Windows-style line endings. +- [#521](https://github.com/kpdecker/jsdiff/pull/521) **the `callback` option is now supported by `structuredPatch`, `createPatch +- [#529](https://github.com/kpdecker/jsdiff/pull/529) **`parsePatch` can now parse patches where lines starting with `--` or `++` are deleted/inserted**; previously, there were edge cases where the parser would choke on valid patches or give wrong results. +- [#530](https://github.com/kpdecker/jsdiff/pull/530) **Added `ignoreNewlineAtEof` option` to `diffLines`** +- [#533](https://github.com/kpdecker/jsdiff/pull/533) **`applyPatch` uses an entirely new algorithm for fuzzy matching.** Differences between the old and new algorithm are as follows: + * The `fuzzFactor` now indicates the maximum [*Levenshtein* distance](https://en.wikipedia.org/wiki/Levenshtein_distance) that there can be between the context shown in a hunk and the actual file content at a location where we try to apply the hunk. (Previously, it represented a maximum [*Hamming* distance](https://en.wikipedia.org/wiki/Hamming_distance), meaning that a single insertion or deletion in the source file could stop a hunk from applying even with a high `fuzzFactor`.) + * A hunk containing a deletion can now only be applied in a context where the line to be deleted actually appears verbatim. (Previously, as long as enough context lines in the hunk matched, `applyPatch` would apply the hunk anyway and delete a completely different line.) + * The context line immediately before and immediately after an insertion must match exactly between the hunk and the file for a hunk to apply. (Previously this was not required.) +- [#535](https://github.com/kpdecker/jsdiff/pull/535) **A bug in patch generation functions is now fixed** that would sometimes previously cause `\ No newline at end of file` to appear in the wrong place in the generated patch, resulting in the patch being invalid. +- [#535](https://github.com/kpdecker/jsdiff/pull/535) **Passing `newlineIsToken: true` to *patch*-generation functions is no longer allowed.** (Passing it to `diffLines` is still supported - it's only functions like `createPatch` where passing `newlineIsToken` is now an error.) Allowing it to be passed never really made sense, since in cases where the option had any effect on the output at all, the effect tended to be causing a garbled patch to be created that couldn't actually be applied to the source file. +- [#539](https://github.com/kpdecker/jsdiff/pull/539) **`diffWords` now takes an optional `intlSegmenter` option** which should be an `Intl.Segmenter` with word-level granularity. This provides better tokenization of text into words than the default behaviour, even for English but especially for some other languages for which the default behaviour is poor. + ## v5.2.0 -[Commits](https://github.com/kpdecker/jsdiff/compare/v5.1.0...master) +[Commits](https://github.com/kpdecker/jsdiff/compare/v5.1.0...v5.2.0) - [#411](https://github.com/kpdecker/jsdiff/pull/411) Big performance improvement. Previously an O(n) array-copying operation inside the innermost loop of jsdiff's base diffing code increased the overall worst-case time complexity of computing a diff from O(n²) to O(n³). This is now fixed, bringing the worst-case time complexity down to what it theoretically should be for a Myers diff implementation. - [#448](https://github.com/kpdecker/jsdiff/pull/411) Performance improvement. Diagonals whose furthest-reaching D-path would go off the edge of the edit graph are now skipped, rather than being pointlessly considered as called for by the original Myers diff algorithm. This dramatically speeds up computing diffs where the new text just appends or truncates content at the end of the old text. diff --git a/deps/npm/node_modules/https-proxy-agent/dist/index.js b/deps/npm/node_modules/https-proxy-agent/dist/index.js index 0c91722035f07e..1857f464724e20 100644 --- a/deps/npm/node_modules/https-proxy-agent/dist/index.js +++ b/deps/npm/node_modules/https-proxy-agent/dist/index.js @@ -35,6 +35,17 @@ const agent_base_1 = require("agent-base"); const url_1 = require("url"); const parse_proxy_response_1 = require("./parse-proxy-response"); const debug = (0, debug_1.default)('https-proxy-agent'); +const setServernameFromNonIpHost = (options) => { + if (options.servername === undefined && + options.host && + !net.isIP(options.host)) { + return { + ...options, + servername: options.host, + }; + } + return options; +}; /** * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to * the specified "HTTP(s) proxy server" in order to proxy HTTPS requests. @@ -82,11 +93,7 @@ class HttpsProxyAgent extends agent_base_1.Agent { let socket; if (proxy.protocol === 'https:') { debug('Creating `tls.Socket`: %o', this.connectOpts); - const servername = this.connectOpts.servername || this.connectOpts.host; - socket = tls.connect({ - ...this.connectOpts, - servername, - }); + socket = tls.connect(setServernameFromNonIpHost(this.connectOpts)); } else { debug('Creating `net.Socket`: %o', this.connectOpts); @@ -122,11 +129,9 @@ class HttpsProxyAgent extends agent_base_1.Agent { // The proxy is connecting to a TLS server, so upgrade // this socket connection to a TLS connection. debug('Upgrading socket connection to TLS'); - const servername = opts.servername || opts.host; return tls.connect({ - ...omit(opts, 'host', 'path', 'port'), + ...omit(setServernameFromNonIpHost(opts), 'host', 'path', 'port'), socket, - servername, }); } return socket; diff --git a/deps/npm/node_modules/https-proxy-agent/package.json b/deps/npm/node_modules/https-proxy-agent/package.json index 3c793b769dc5d9..51b7e1175ff51b 100644 --- a/deps/npm/node_modules/https-proxy-agent/package.json +++ b/deps/npm/node_modules/https-proxy-agent/package.json @@ -1,6 +1,6 @@ { "name": "https-proxy-agent", - "version": "7.0.5", + "version": "7.0.6", "description": "An HTTP(s) proxy `http.Agent` implementation for HTTPS", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -21,7 +21,7 @@ "author": "Nathan Rajlich (http://n8.io/)", "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "devDependencies": { diff --git a/deps/npm/node_modules/indent-string/index.js b/deps/npm/node_modules/indent-string/index.js deleted file mode 100644 index e1ab804f2fd8a1..00000000000000 --- a/deps/npm/node_modules/indent-string/index.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -module.exports = (string, count = 1, options) => { - options = { - indent: ' ', - includeEmptyLines: false, - ...options - }; - - if (typeof string !== 'string') { - throw new TypeError( - `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` - ); - } - - if (typeof count !== 'number') { - throw new TypeError( - `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` - ); - } - - if (typeof options.indent !== 'string') { - throw new TypeError( - `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` - ); - } - - if (count === 0) { - return string; - } - - const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; - - return string.replace(regex, options.indent.repeat(count)); -}; diff --git a/deps/npm/node_modules/indent-string/license b/deps/npm/node_modules/indent-string/license deleted file mode 100644 index e7af2f77107d73..00000000000000 --- a/deps/npm/node_modules/indent-string/license +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) Sindre Sorhus (sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/npm/node_modules/indent-string/package.json b/deps/npm/node_modules/indent-string/package.json deleted file mode 100644 index 497bb83bbd9b7f..00000000000000 --- a/deps/npm/node_modules/indent-string/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "indent-string", - "version": "4.0.0", - "description": "Indent each line in a string", - "license": "MIT", - "repository": "sindresorhus/indent-string", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "indent", - "string", - "pad", - "align", - "line", - "text", - "each", - "every" - ], - "devDependencies": { - "ava": "^1.4.1", - "tsd": "^0.7.2", - "xo": "^0.24.0" - } -} diff --git a/deps/npm/node_modules/init-package-json/lib/default-input.js b/deps/npm/node_modules/init-package-json/lib/default-input.js index 0a01bfa05fa45c..3b38c77606e328 100644 --- a/deps/npm/node_modules/init-package-json/lib/default-input.js +++ b/deps/npm/node_modules/init-package-json/lib/default-input.js @@ -261,3 +261,8 @@ exports.license = yes ? license : prompt('license', license, (data) => { const errors = (its.errors || []).concat(its.warnings || []) return invalid(`Sorry, ${errors.join(' and ')}.`) }) + +const type = package.type || getConfig('type') || 'commonjs' +exports.type = yes ? type : prompt('type', type, (data) => { + return data +}) diff --git a/deps/npm/node_modules/init-package-json/lib/init-package-json.js b/deps/npm/node_modules/init-package-json/lib/init-package-json.js index 51cbd21a1ebe6a..b67ae418f7abd7 100644 --- a/deps/npm/node_modules/init-package-json/lib/init-package-json.js +++ b/deps/npm/node_modules/init-package-json/lib/init-package-json.js @@ -139,7 +139,7 @@ async function init (dir, return } - await pkg.save() + await pkg.save({ sort: true }) return pkg.content } diff --git a/deps/npm/node_modules/init-package-json/package.json b/deps/npm/node_modules/init-package-json/package.json index d1de96476a9f14..c264eb44f97495 100644 --- a/deps/npm/node_modules/init-package-json/package.json +++ b/deps/npm/node_modules/init-package-json/package.json @@ -1,6 +1,6 @@ { "name": "init-package-json", - "version": "7.0.2", + "version": "8.0.0", "main": "lib/init-package-json.js", "scripts": { "test": "tap", @@ -20,7 +20,7 @@ "license": "ISC", "description": "A node module to get your node module started", "dependencies": { - "@npmcli/package-json": "^6.0.0", + "@npmcli/package-json": "^6.1.0", "npm-package-arg": "^12.0.0", "promzard": "^2.0.0", "read": "^4.0.0", @@ -29,13 +29,13 @@ "validate-npm-package-name": "^6.0.0" }, "devDependencies": { - "@npmcli/config": "^8.2.0", + "@npmcli/config": "^9.0.0", "@npmcli/eslint-config": "^5.0.0", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.4", "tap": "^16.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "tap": { "test-ignore": "fixtures/", @@ -61,7 +61,7 @@ ], "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.4", "publish": true } } diff --git a/deps/npm/node_modules/libnpmaccess/package.json b/deps/npm/node_modules/libnpmaccess/package.json index 0022437adadc60..ff63233c488716 100644 --- a/deps/npm/node_modules/libnpmaccess/package.json +++ b/deps/npm/node_modules/libnpmaccess/package.json @@ -1,6 +1,6 @@ { "name": "libnpmaccess", - "version": "9.0.0", + "version": "10.0.0", "description": "programmatic library for `npm access` commands", "author": "GitHub Inc.", "license": "ISC", @@ -18,8 +18,7 @@ "devDependencies": { "@npmcli/eslint-config": "^5.0.1", "@npmcli/mock-registry": "^1.0.0", - "@npmcli/template-oss": "4.23.3", - "nock": "^13.3.3", + "@npmcli/template-oss": "4.23.6", "tap": "^16.3.8" }, "repository": { @@ -34,7 +33,7 @@ "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "files": [ "bin/", @@ -42,7 +41,7 @@ ], "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" }, "tap": { diff --git a/deps/npm/node_modules/libnpmdiff/lib/format-diff.js b/deps/npm/node_modules/libnpmdiff/lib/format-diff.js index a6606d94b8b302..f50738207c854a 100644 --- a/deps/npm/node_modules/libnpmdiff/lib/format-diff.js +++ b/deps/npm/node_modules/libnpmdiff/lib/format-diff.js @@ -19,7 +19,7 @@ const color = (colorStr, colorId) => { return colorStr.replace(/[^\n\r]+/g, open + '$&' + close) } -const formatDiff = ({ files, opts = {}, refs, versions }) => { +const formatDiff = async ({ files, opts = {}, refs, versions }) => { let res = '' const srcPrefix = opts.diffNoPrefix ? '' : opts.diffSrcPrefix || 'a/' const dstPrefix = opts.diffNoPrefix ? '' : opts.diffDstPrefix || 'b/' @@ -77,7 +77,7 @@ const formatDiff = ({ files, opts = {}, refs, versions }) => { /* eslint-disable-next-line max-len */ header(`index ${opts.tagVersionPrefix || 'v'}${versions.a}..${opts.tagVersionPrefix || 'v'}${versions.b} ${fileMode}`) - if (shouldPrintPatch(filename)) { + if (await shouldPrintPatch(filename)) { patch += jsDiff.createTwoFilesPatch( names.a, names.b, diff --git a/deps/npm/node_modules/libnpmdiff/lib/should-print-patch.js b/deps/npm/node_modules/libnpmdiff/lib/should-print-patch.js index 8000fc5e6afc16..c63cdee87c1fab 100644 --- a/deps/npm/node_modules/libnpmdiff/lib/should-print-patch.js +++ b/deps/npm/node_modules/libnpmdiff/lib/should-print-patch.js @@ -1,14 +1,14 @@ const { basename, extname } = require('node:path') -const binaryExtensions = require('binary-extensions') - // we should try to print patches as long as the // extension is not identified as binary files -const shouldPrintPatch = (path, opts = {}) => { +const shouldPrintPatch = async (path, opts = {}) => { if (opts.diffText) { return true } + const { default: binaryExtensions } = await import('binary-extensions') + const filename = basename(path) const extension = ( filename.startsWith('.') diff --git a/deps/npm/node_modules/libnpmdiff/package.json b/deps/npm/node_modules/libnpmdiff/package.json index ccb499e78ff66d..48673c03ff4c73 100644 --- a/deps/npm/node_modules/libnpmdiff/package.json +++ b/deps/npm/node_modules/libnpmdiff/package.json @@ -1,6 +1,6 @@ { "name": "libnpmdiff", - "version": "7.0.0", + "version": "8.0.0", "description": "The registry diff", "repository": { "type": "git", @@ -13,7 +13,7 @@ "lib/" ], "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "keywords": [ "npm", @@ -43,22 +43,22 @@ }, "devDependencies": { "@npmcli/eslint-config": "^5.0.1", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.6", "tap": "^16.3.8" }, "dependencies": { - "@npmcli/arborist": "^8.0.0", + "@npmcli/arborist": "^9.0.0", "@npmcli/installed-package-contents": "^3.0.0", - "binary-extensions": "^2.3.0", - "diff": "^5.1.0", + "binary-extensions": "^3.0.0", + "diff": "^7.0.0", "minimatch": "^9.0.4", "npm-package-arg": "^12.0.0", - "pacote": "^19.0.0", + "pacote": "^21.0.0", "tar": "^6.2.1" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" }, "tap": { diff --git a/deps/npm/node_modules/libnpmexec/lib/index.js b/deps/npm/node_modules/libnpmexec/lib/index.js index 8344471696e25d..78633a8cadb3cf 100644 --- a/deps/npm/node_modules/libnpmexec/lib/index.js +++ b/deps/npm/node_modules/libnpmexec/lib/index.js @@ -24,7 +24,7 @@ const manifests = new Map() const getManifest = async (spec, flatOptions) => { if (!manifests.has(spec.raw)) { - const manifest = await pacote.manifest(spec, { ...flatOptions, preferOnline: true }) + const manifest = await pacote.manifest(spec, { ...flatOptions, preferOnline: true, Arborist }) manifests.set(spec.raw, manifest) } return manifests.get(spec.raw) diff --git a/deps/npm/node_modules/libnpmexec/package.json b/deps/npm/node_modules/libnpmexec/package.json index 497b971a0841bc..5009d76d12fe54 100644 --- a/deps/npm/node_modules/libnpmexec/package.json +++ b/deps/npm/node_modules/libnpmexec/package.json @@ -1,13 +1,13 @@ { "name": "libnpmexec", - "version": "9.0.0", + "version": "10.0.0", "files": [ "bin/", "lib/" ], "main": "lib/index.js", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "description": "npm exec (npx) programmatic API", "repository": { @@ -52,7 +52,7 @@ "devDependencies": { "@npmcli/eslint-config": "^5.0.1", "@npmcli/mock-registry": "^1.0.0", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.6", "bin-links": "^5.0.0", "chalk": "^5.2.0", "just-extend": "^6.2.0", @@ -60,20 +60,20 @@ "tap": "^16.3.8" }, "dependencies": { - "@npmcli/arborist": "^8.0.0", + "@npmcli/arborist": "^9.0.0", "@npmcli/run-script": "^9.0.1", "ci-info": "^4.0.0", "npm-package-arg": "^12.0.0", - "pacote": "^19.0.0", + "pacote": "^21.0.0", "proc-log": "^5.0.0", "read": "^4.0.0", "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", - "walk-up-path": "^3.0.1" + "walk-up-path": "^4.0.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" } } diff --git a/deps/npm/node_modules/libnpmfund/package.json b/deps/npm/node_modules/libnpmfund/package.json index 07c1e33f2a7c38..8d458259abd6b3 100644 --- a/deps/npm/node_modules/libnpmfund/package.json +++ b/deps/npm/node_modules/libnpmfund/package.json @@ -1,6 +1,6 @@ { "name": "libnpmfund", - "version": "6.0.0", + "version": "7.0.0", "main": "lib/index.js", "files": [ "bin/", @@ -42,18 +42,18 @@ }, "devDependencies": { "@npmcli/eslint-config": "^5.0.1", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.6", "tap": "^16.3.8" }, "dependencies": { - "@npmcli/arborist": "^8.0.0" + "@npmcli/arborist": "^9.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" }, "tap": { diff --git a/deps/npm/node_modules/libnpmhook/LICENSE.md b/deps/npm/node_modules/libnpmhook/LICENSE.md deleted file mode 100644 index 8d28acf866d932..00000000000000 --- a/deps/npm/node_modules/libnpmhook/LICENSE.md +++ /dev/null @@ -1,16 +0,0 @@ -ISC License - -Copyright (c) npm, Inc. - -Permission to use, copy, modify, and/or distribute this software for -any purpose with or without fee is hereby granted, provided that the -above copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE COPYRIGHT HOLDER DISCLAIMS -ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR -CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE -USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/deps/npm/node_modules/libnpmhook/README.md b/deps/npm/node_modules/libnpmhook/README.md deleted file mode 100644 index 309f8041da08b2..00000000000000 --- a/deps/npm/node_modules/libnpmhook/README.md +++ /dev/null @@ -1,271 +0,0 @@ -# libnpmhook - -[![npm version](https://img.shields.io/npm/v/libnpmhook.svg)](https://npm.im/libnpmhook) -[![license](https://img.shields.io/npm/l/libnpmhook.svg)](https://npm.im/libnpmhook) -[![CI - libnpmhook](https://github.com/npm/cli/actions/workflows/ci-libnpmhook.yml/badge.svg)](https://github.com/npm/cli/actions/workflows/ci-libnpmhook.yml) - -[`libnpmhook`](https://github.com/npm/libnpmhook) is a Node.js library for -programmatically managing the npm registry's server-side hooks. - -For a more general introduction to managing hooks, see [the introductory blog -post](https://blog.npmjs.org/post/145260155635/introducing-hooks-get-notifications-of-npm). - -## Table of Contents - -* [Example](#example) -* [Install](#install) -* [Contributing](#contributing) -* [API](#api) - * [hook opts](#opts) - * [`add()`](#add) - * [`rm()`](#rm) - * [`ls()`](#ls) - * [`ls.stream()`](#ls-stream) - * [`update()`](#update) - -## Example - -```js -const hooks = require('libnpmhook') - -console.log(await hooks.ls('mypkg', {token: 'deadbeef'})) -// array of hook objects on `mypkg`. -``` - -## Install - -`$ npm install libnpmhook` - -### API - -#### `opts` for `libnpmhook` commands - -`libnpmhook` uses [`npm-registry-fetch`](https://npm.im/npm-registry-fetch). -All options are passed through directly to that library, so please refer to [its -own `opts` -documentation](https://www.npmjs.com/package/npm-registry-fetch#fetch-options) -for options that can be passed in. - -A couple of options of note for those in a hurry: - -* `opts.token` - can be passed in and will be used as the authentication token for the registry. For other ways to pass in auth details, see the n-r-f docs. -* `opts.otp` - certain operations will require an OTP token to be passed in. If a `libnpmhook` command fails with `err.code === EOTP`, please retry the request with `{otp: <2fa token>}` - -#### `> hooks.add(name, endpoint, secret, [opts]) -> Promise` - -`name` is the name of the package, org, or user/org scope to watch. The type is -determined by the name syntax: `'@foo/bar'` and `'foo'` are treated as packages, -`@foo` is treated as a scope, and `~user` is treated as an org name or scope. -Each type will attach to different events. - -The `endpoint` should be a fully-qualified http URL for the endpoint the hook -will send its payload to when it fires. `secret` is a shared secret that the -hook will send to that endpoint to verify that it's actually coming from the -registry hook. - -The returned Promise resolves to the full hook object that was created, -including its generated `id`. - -See also: [`POST -/v1/hooks/hook`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#post-v1hookshook) - -##### Example - -```javascript -await hooks.add('~zkat', 'https://example.com/api/added', 'supersekrit', { - token: 'myregistrytoken', - otp: '694207' -}) - -=> - -{ id: '16f7xoal', - username: 'zkat', - name: 'zkat', - endpoint: 'https://example.com/api/added', - secret: 'supersekrit', - type: 'owner', - created: '2018-08-21T20:05:25.125Z', - updated: '2018-08-21T20:05:25.125Z', - deleted: false, - delivered: false, - last_delivery: null, - response_code: 0, - status: 'active' } -``` - -#### `> hooks.find(id, [opts]) -> Promise` - -Returns the hook identified by `id`. - -The returned Promise resolves to the full hook object that was found, or error -with `err.code` of `'E404'` if it didn't exist. - -See also: [`GET -/v1/hooks/hook/:id`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#get-v1hookshookid) - -##### Example - -```javascript -await hooks.find('16f7xoal', {token: 'myregistrytoken'}) - -=> - -{ id: '16f7xoal', - username: 'zkat', - name: 'zkat', - endpoint: 'https://example.com/api/added', - secret: 'supersekrit', - type: 'owner', - created: '2018-08-21T20:05:25.125Z', - updated: '2018-08-21T20:05:25.125Z', - deleted: false, - delivered: false, - last_delivery: null, - response_code: 0, - status: 'active' } -``` - -#### `> hooks.rm(id, [opts]) -> Promise` - -Removes the hook identified by `id`. - -The returned Promise resolves to the full hook object that was removed, if it -existed, or `null` if no such hook was there (instead of erroring). - -See also: [`DELETE -/v1/hooks/hook/:id`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#delete-v1hookshookid) - -##### Example - -```javascript -await hooks.rm('16f7xoal', { - token: 'myregistrytoken', - otp: '694207' -}) - -=> - -{ id: '16f7xoal', - username: 'zkat', - name: 'zkat', - endpoint: 'https://example.com/api/added', - secret: 'supersekrit', - type: 'owner', - created: '2018-08-21T20:05:25.125Z', - updated: '2018-08-21T20:05:25.125Z', - deleted: true, - delivered: false, - last_delivery: null, - response_code: 0, - status: 'active' } - -// Repeat it... -await hooks.rm('16f7xoal', { - token: 'myregistrytoken', - otp: '694207' -}) - -=> null -``` - -#### `> hooks.update(id, endpoint, secret, [opts]) -> Promise` - -The `id` should be a hook ID from a previously-created hook. - -The `endpoint` should be a fully-qualified http URL for the endpoint the hook -will send its payload to when it fires. `secret` is a shared secret that the -hook will send to that endpoint to verify that it's actually coming from the -registry hook. - -The returned Promise resolves to the full hook object that was updated, if it -existed. Otherwise, it will error with an `'E404'` error code. - -See also: [`PUT -/v1/hooks/hook/:id`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#put-v1hookshookid) - -##### Example - -```javascript -await hooks.update('16fxoal', 'https://example.com/api/other', 'newsekrit', { - token: 'myregistrytoken', - otp: '694207' -}) - -=> - -{ id: '16f7xoal', - username: 'zkat', - name: 'zkat', - endpoint: 'https://example.com/api/other', - secret: 'newsekrit', - type: 'owner', - created: '2018-08-21T20:05:25.125Z', - updated: '2018-08-21T20:14:41.964Z', - deleted: false, - delivered: false, - last_delivery: null, - response_code: 0, - status: 'active' } -``` - -#### `> hooks.ls([opts]) -> Promise` - -Resolves to an array of hook objects associated with the account you're -authenticated as. - -Results can be further filtered with three values that can be passed in through -`opts`: - -* `opts.package` - filter results by package name -* `opts.limit` - maximum number of hooks to return -* `opts.offset` - pagination offset for results (use with `opts.limit`) - -See also: - * [`hooks.ls.stream()`](#ls-stream) - * [`GET -/v1/hooks`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#get-v1hooks) - -##### Example - -```javascript -await hooks.ls({token: 'myregistrytoken'}) - -=> -[ - { id: '16f7xoal', ... }, - { id: 'wnyf98a1', ... }, - ... -] -``` - -#### `> hooks.ls.stream([opts]) -> Stream` - -Returns a stream of hook objects associated with the account you're -authenticated as. The returned stream is a valid `Symbol.asyncIterator` on -`node@>=10`. - -Results can be further filtered with three values that can be passed in through -`opts`: - -* `opts.package` - filter results by package name -* `opts.limit` - maximum number of hooks to return -* `opts.offset` - pagination offset for results (use with `opts.limit`) - -See also: - * [`hooks.ls()`](#ls) - * [`GET -/v1/hooks`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#get-v1hooks) - -##### Example - -```javascript -for await (let hook of hooks.ls.stream({token: 'myregistrytoken'})) { - console.log('found hook:', hook.id) -} - -=> -// outputs: -// found hook: 16f7xoal -// found hook: wnyf98a1 -``` diff --git a/deps/npm/node_modules/libnpmhook/lib/index.js b/deps/npm/node_modules/libnpmhook/lib/index.js deleted file mode 100644 index 091cdc49a80d1a..00000000000000 --- a/deps/npm/node_modules/libnpmhook/lib/index.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict' - -const fetch = require('npm-registry-fetch') -const validate = require('aproba') - -const eu = encodeURIComponent -const cmd = module.exports = {} -cmd.add = (name, endpoint, secret, opts = {}) => { - validate('SSSO', [name, endpoint, secret, opts]) - let type = 'package' - if (name.match(/^@[^/]+$/)) { - type = 'scope' - } - if (name[0] === '~') { - type = 'owner' - name = name.slice(1) - } - return fetch.json('/-/npm/v1/hooks/hook', { - ...opts, - method: 'POST', - body: { type, name, endpoint, secret }, - }) -} - -cmd.rm = (id, opts = {}) => { - validate('SO', [id, opts]) - return fetch.json(`/-/npm/v1/hooks/hook/${eu(id)}`, { - ...opts, - method: 'DELETE', - }).catch(err => { - if (err.code === 'E404') { - return null - } else { - throw err - } - }) -} - -cmd.update = (id, endpoint, secret, opts = {}) => { - validate('SSSO', [id, endpoint, secret, opts]) - return fetch.json(`/-/npm/v1/hooks/hook/${eu(id)}`, { - ...opts, - method: 'PUT', - body: { endpoint, secret }, - }) -} - -cmd.find = (id, opts = {}) => { - validate('SO', [id, opts]) - return fetch.json(`/-/npm/v1/hooks/hook/${eu(id)}`, opts) -} - -cmd.ls = (opts = {}) => { - return cmd.ls.stream(opts).collect() -} - -cmd.ls.stream = (opts = {}) => { - const { package: pkg, limit, offset } = opts - validate('S|Z', [pkg]) - validate('N|Z', [limit]) - validate('N|Z', [offset]) - return fetch.json.stream('/-/npm/v1/hooks', 'objects.*', { - ...opts, - query: { - package: pkg, - limit, - offset, - }, - }) -} diff --git a/deps/npm/node_modules/libnpmhook/package.json b/deps/npm/node_modules/libnpmhook/package.json deleted file mode 100644 index 09157ab08cb209..00000000000000 --- a/deps/npm/node_modules/libnpmhook/package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "libnpmhook", - "version": "11.0.0", - "description": "programmatic API for managing npm registry hooks", - "main": "lib/index.js", - "files": [ - "bin/", - "lib/" - ], - "scripts": { - "test": "tap", - "lint": "npm run eslint", - "postlint": "template-oss-check", - "lintfix": "npm run eslint -- --fix", - "snap": "tap", - "posttest": "npm run lint", - "template-oss-apply": "template-oss-apply --force", - "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/npm/cli.git", - "directory": "workspaces/libnpmhook" - }, - "keywords": [ - "npm", - "hooks", - "registry", - "npm api" - ], - "author": "GitHub Inc.", - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^18.0.1" - }, - "devDependencies": { - "@npmcli/eslint-config": "^5.0.1", - "@npmcli/template-oss": "4.23.3", - "nock": "^13.3.3", - "tap": "^16.3.8" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - }, - "templateOSS": { - "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", - "content": "../../scripts/template-oss/index.js" - }, - "tap": { - "nyc-arg": [ - "--exclude", - "tap-snapshots/**" - ] - } -} diff --git a/deps/npm/node_modules/libnpmorg/lib/index.js b/deps/npm/node_modules/libnpmorg/lib/index.js index 4684b516d2b4ad..f3d361b8be6d75 100644 --- a/deps/npm/node_modules/libnpmorg/lib/index.js +++ b/deps/npm/node_modules/libnpmorg/lib/index.js @@ -1,7 +1,7 @@ 'use strict' const eu = encodeURIComponent -const fetch = require('npm-registry-fetch') +const npmFetch = require('npm-registry-fetch') const validate = require('aproba') // From https://github.com/npm/registry/blob/master/docs/orgs/memberships.md @@ -19,7 +19,7 @@ cmd.set = (org, user, role, opts = {}) => { validate('SSSO|SSZO', [org, user, role, opts]) user = user.replace(/^@?/, '') org = org.replace(/^@?/, '') - return fetch.json(`/-/org/${eu(org)}/user`, { + return npmFetch.json(`/-/org/${eu(org)}/user`, { ...opts, method: 'PUT', body: { user, role }, @@ -30,7 +30,7 @@ cmd.rm = (org, user, opts = {}) => { validate('SSO', [org, user, opts]) user = user.replace(/^@?/, '') org = org.replace(/^@?/, '') - return fetch(`/-/org/${eu(org)}/user`, { + return npmFetch(`/-/org/${eu(org)}/user`, { ...opts, method: 'DELETE', body: { user }, @@ -55,7 +55,7 @@ cmd.ls = (org, opts = {}) => { cmd.ls.stream = (org, opts = {}) => { validate('SO', [org, opts]) org = org.replace(/^@?/, '') - return fetch.json.stream(`/-/org/${eu(org)}/user`, '*', { + return npmFetch.json.stream(`/-/org/${eu(org)}/user`, '*', { ...opts, mapJSON: (value, [key]) => { return [key, value] diff --git a/deps/npm/node_modules/libnpmorg/package.json b/deps/npm/node_modules/libnpmorg/package.json index 38800308a31a4e..aec1ef79791c80 100644 --- a/deps/npm/node_modules/libnpmorg/package.json +++ b/deps/npm/node_modules/libnpmorg/package.json @@ -1,6 +1,6 @@ { "name": "libnpmorg", - "version": "7.0.0", + "version": "8.0.0", "description": "Programmatic api for `npm org` commands", "author": "GitHub Inc.", "main": "lib/index.js", @@ -29,7 +29,7 @@ ], "devDependencies": { "@npmcli/eslint-config": "^5.0.1", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.6", "minipass": "^7.1.1", "nock": "^13.3.3", "tap": "^16.3.8" @@ -46,11 +46,11 @@ "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" }, "tap": { diff --git a/deps/npm/node_modules/libnpmpack/lib/index.js b/deps/npm/node_modules/libnpmpack/lib/index.js index b026ad1a935c79..bd3e0c7bd7232a 100644 --- a/deps/npm/node_modules/libnpmpack/lib/index.js +++ b/deps/npm/node_modules/libnpmpack/lib/index.js @@ -12,7 +12,7 @@ async function pack (spec = 'file:.', opts = {}) { // gets spec spec = npa(spec) - const manifest = await pacote.manifest(spec, opts) + const manifest = await pacote.manifest(spec, { ...opts, Arborist }) const stdio = opts.foregroundScripts ? 'inherit' : 'pipe' diff --git a/deps/npm/node_modules/libnpmpack/package.json b/deps/npm/node_modules/libnpmpack/package.json index 35d12425b02ff7..eba99bd38a9bc5 100644 --- a/deps/npm/node_modules/libnpmpack/package.json +++ b/deps/npm/node_modules/libnpmpack/package.json @@ -1,6 +1,6 @@ { "name": "libnpmpack", - "version": "8.0.0", + "version": "9.0.0", "description": "Programmatic API for the bits behind npm pack", "author": "GitHub Inc.", "main": "lib/index.js", @@ -24,7 +24,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^5.0.1", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.6", "nock": "^13.3.3", "spawk": "^1.7.1", "tap": "^16.3.8" @@ -37,17 +37,17 @@ "bugs": "https://github.com/npm/libnpmpack/issues", "homepage": "https://npmjs.com/package/libnpmpack", "dependencies": { - "@npmcli/arborist": "^8.0.0", + "@npmcli/arborist": "^9.0.0", "@npmcli/run-script": "^9.0.1", "npm-package-arg": "^12.0.0", - "pacote": "^19.0.0" + "pacote": "^21.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" }, "tap": { diff --git a/deps/npm/node_modules/libnpmpublish/lib/publish.js b/deps/npm/node_modules/libnpmpublish/lib/publish.js index 2bcab4f3ba304e..93d546efb5f0e1 100644 --- a/deps/npm/node_modules/libnpmpublish/lib/publish.js +++ b/deps/npm/node_modules/libnpmpublish/lib/publish.js @@ -137,7 +137,7 @@ const buildMetadata = async (registry, manifest, tarballData, spec, opts) => { if (provenance === true) { await ensureProvenanceGeneration(registry, spec, opts) - provenanceBundle = await generateProvenance([subject], { legacyCompatibility: true, ...opts }) + provenanceBundle = await generateProvenance([subject], opts) /* eslint-disable-next-line max-len */ log.notice('publish', `Signed provenance statement with source and build information from ${ciInfo.name}`) diff --git a/deps/npm/node_modules/libnpmpublish/package.json b/deps/npm/node_modules/libnpmpublish/package.json index 594f5041480b4a..87526bdd88ca0d 100644 --- a/deps/npm/node_modules/libnpmpublish/package.json +++ b/deps/npm/node_modules/libnpmpublish/package.json @@ -1,6 +1,6 @@ { "name": "libnpmpublish", - "version": "10.0.1", + "version": "11.0.0", "description": "Programmatic API for the bits behind npm publish and unpublish", "author": "GitHub Inc.", "main": "lib/index.js", @@ -27,8 +27,7 @@ "@npmcli/eslint-config": "^5.0.1", "@npmcli/mock-globals": "^1.0.0", "@npmcli/mock-registry": "^1.0.0", - "@npmcli/template-oss": "4.23.3", - "nock": "^13.3.3", + "@npmcli/template-oss": "4.23.6", "tap": "^16.3.8" }, "repository": { @@ -49,11 +48,11 @@ "ssri": "^12.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" }, "tap": { diff --git a/deps/npm/node_modules/libnpmsearch/package.json b/deps/npm/node_modules/libnpmsearch/package.json index a5d2ae424913ef..ca021f24144401 100644 --- a/deps/npm/node_modules/libnpmsearch/package.json +++ b/deps/npm/node_modules/libnpmsearch/package.json @@ -1,6 +1,6 @@ { "name": "libnpmsearch", - "version": "8.0.0", + "version": "9.0.0", "description": "Programmatic API for searching in npm and compatible registries.", "author": "GitHub Inc.", "main": "lib/index.js", @@ -27,7 +27,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^5.0.1", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.6", "nock": "^13.3.3", "tap": "^16.3.8" }, @@ -42,11 +42,11 @@ "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" }, "tap": { diff --git a/deps/npm/node_modules/libnpmteam/package.json b/deps/npm/node_modules/libnpmteam/package.json index fd8f69669f15cf..72858f25c2f164 100644 --- a/deps/npm/node_modules/libnpmteam/package.json +++ b/deps/npm/node_modules/libnpmteam/package.json @@ -1,7 +1,7 @@ { "name": "libnpmteam", "description": "npm Team management APIs", - "version": "7.0.0", + "version": "8.0.0", "author": "GitHub Inc.", "license": "ISC", "main": "lib/index.js", @@ -17,7 +17,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^5.0.1", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.6", "nock": "^13.3.3", "tap": "^16.3.8" }, @@ -36,11 +36,11 @@ "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" }, "tap": { diff --git a/deps/npm/node_modules/libnpmversion/package.json b/deps/npm/node_modules/libnpmversion/package.json index cdc9e7bbdf718a..b319156a18fc98 100644 --- a/deps/npm/node_modules/libnpmversion/package.json +++ b/deps/npm/node_modules/libnpmversion/package.json @@ -1,6 +1,6 @@ { "name": "libnpmversion", - "version": "7.0.0", + "version": "8.0.0", "main": "lib/index.js", "files": [ "bin/", @@ -33,7 +33,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^5.0.1", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.6", "require-inject": "^1.4.4", "tap": "^16.3.8" }, @@ -45,11 +45,11 @@ "semver": "^7.3.7" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "../../scripts/template-oss/index.js" } } diff --git a/deps/npm/node_modules/npm-package-arg/lib/npa.js b/deps/npm/node_modules/npm-package-arg/lib/npa.js index 8094b3e732cd98..e92605811ae3e6 100644 --- a/deps/npm/node_modules/npm-package-arg/lib/npa.js +++ b/deps/npm/node_modules/npm-package-arg/lib/npa.js @@ -17,6 +17,7 @@ const hasSlashes = isWindows ? /\\|[/]/ : /[/]/ const isURL = /^(?:git[+])?[a-z]+:/i const isGit = /^[^@]+@[^:.]+\.[^:]+:.+$/i const isFilename = /[.](?:tgz|tar.gz|tar)$/i +const isPortNumber = /:[0-9]+(\/|$)/i function npa (arg, where) { let name @@ -324,7 +325,9 @@ function fromURL (res) { // git+ssh://git@my.custom.git.com:username/project.git#deadbeef // ...and various combinations. The username in the beginning is *required*. const matched = rawSpec.match(/^git\+ssh:\/\/([^:#]+:[^#]+(?:\.git)?)(?:#(.*))?$/i) - if (matched && !matched[1].match(/:[0-9]+\/?.*$/i)) { + // Filter out all-number "usernames" which are really port numbers + // They can either be :1234 :1234/ or :1234/path but not :12abc + if (matched && !matched[1].match(isPortNumber)) { res.type = 'git' setGitAttrs(res, matched[2]) res.fetchSpec = matched[1] diff --git a/deps/npm/node_modules/npm-package-arg/package.json b/deps/npm/node_modules/npm-package-arg/package.json index 80baa3d32a52fe..ab285eb6c610c6 100644 --- a/deps/npm/node_modules/npm-package-arg/package.json +++ b/deps/npm/node_modules/npm-package-arg/package.json @@ -1,6 +1,6 @@ { "name": "npm-package-arg", - "version": "12.0.0", + "version": "12.0.1", "description": "Parse the things that can be arguments to `npm install`", "main": "./lib/npa.js", "directories": { @@ -18,7 +18,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^5.0.0", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.4", "tap": "^16.0.1" }, "scripts": { @@ -55,7 +55,7 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.4", "publish": true } } diff --git a/deps/npm/node_modules/npm-packlist/lib/index.js b/deps/npm/node_modules/npm-packlist/lib/index.js index 985f11ee3f7384..9e4e7db07c01cb 100644 --- a/deps/npm/node_modules/npm-packlist/lib/index.js +++ b/deps/npm/node_modules/npm-packlist/lib/index.js @@ -290,6 +290,7 @@ class PackWalker extends IgnoreWalker { '/package-lock.json', '/yarn.lock', '/pnpm-lock.yaml', + '/bun.lockb', ] // if we have a files array in our package, we need to pull rules from it diff --git a/deps/npm/node_modules/npm-packlist/package.json b/deps/npm/node_modules/npm-packlist/package.json index d7e0a4fd5a8452..b25864612030f9 100644 --- a/deps/npm/node_modules/npm-packlist/package.json +++ b/deps/npm/node_modules/npm-packlist/package.json @@ -1,6 +1,6 @@ { "name": "npm-packlist", - "version": "9.0.0", + "version": "10.0.0", "description": "Get a list of the files to add from a folder into an npm package", "directories": { "test": "test" @@ -16,9 +16,9 @@ "lib/" ], "devDependencies": { - "@npmcli/arborist": "^7.5.4", - "@npmcli/eslint-config": "^4.0.0", - "@npmcli/template-oss": "4.23.3", + "@npmcli/arborist": "^8.0.0", + "@npmcli/eslint-config": "^5.0.1", + "@npmcli/template-oss": "4.23.4", "mutate-fs": "^2.1.1", "tap": "^16.0.1" }, @@ -51,11 +51,11 @@ ] }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.4", "publish": true } } diff --git a/deps/npm/node_modules/p-map/index.js b/deps/npm/node_modules/p-map/index.js index c11a28512a4733..10558008a77283 100644 --- a/deps/npm/node_modules/p-map/index.js +++ b/deps/npm/node_modules/p-map/index.js @@ -1,49 +1,107 @@ -'use strict'; -const AggregateError = require('aggregate-error'); - -module.exports = async ( +export default async function pMap( iterable, mapper, { - concurrency = Infinity, - stopOnError = true - } = {} -) => { - return new Promise((resolve, reject) => { + concurrency = Number.POSITIVE_INFINITY, + stopOnError = true, + signal, + } = {}, +) { + return new Promise((resolve_, reject_) => { + if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) { + throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`); + } + if (typeof mapper !== 'function') { throw new TypeError('Mapper function is required'); } - if (!((Number.isSafeInteger(concurrency) || concurrency === Infinity) && concurrency >= 1)) { + if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) { throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`); } const result = []; const errors = []; - const iterator = iterable[Symbol.iterator](); + const skippedIndexesMap = new Map(); let isRejected = false; + let isResolved = false; let isIterableDone = false; let resolvingCount = 0; let currentIndex = 0; + const iterator = iterable[Symbol.iterator] === undefined ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator](); + + const signalListener = () => { + reject(signal.reason); + }; - const next = () => { - if (isRejected) { + const cleanup = () => { + signal?.removeEventListener('abort', signalListener); + }; + + const resolve = value => { + resolve_(value); + cleanup(); + }; + + const reject = reason => { + isRejected = true; + isResolved = true; + reject_(reason); + cleanup(); + }; + + if (signal) { + if (signal.aborted) { + reject(signal.reason); + } + + signal.addEventListener('abort', signalListener, {once: true}); + } + + const next = async () => { + if (isResolved) { return; } - const nextItem = iterator.next(); + const nextItem = await iterator.next(); + const index = currentIndex; currentIndex++; + // Note: `iterator.next()` can be called many times in parallel. + // This can cause multiple calls to this `next()` function to + // receive a `nextItem` with `done === true`. + // The shutdown logic that rejects/resolves must be protected + // so it runs only one time as the `skippedIndex` logic is + // non-idempotent. if (nextItem.done) { isIterableDone = true; - if (resolvingCount === 0) { - if (!stopOnError && errors.length !== 0) { - reject(new AggregateError(errors)); - } else { + if (resolvingCount === 0 && !isResolved) { + if (!stopOnError && errors.length > 0) { + reject(new AggregateError(errors)); // eslint-disable-line unicorn/error-message + return; + } + + isResolved = true; + + if (skippedIndexesMap.size === 0) { resolve(result); + return; + } + + const pureResult = []; + + // Support multiple `pMapSkip`'s. + for (const [index, value] of result.entries()) { + if (skippedIndexesMap.get(index) === pMapSkip) { + continue; + } + + pureResult.push(value); } + + resolve(pureResult); } return; @@ -51,31 +109,173 @@ module.exports = async ( resolvingCount++; + // Intentionally detached (async () => { try { const element = await nextItem.value; - result[index] = await mapper(element, index); + + if (isResolved) { + return; + } + + const value = await mapper(element, index); + + // Use Map to stage the index of the element. + if (value === pMapSkip) { + skippedIndexesMap.set(index, value); + } + + result[index] = value; + resolvingCount--; - next(); + await next(); } catch (error) { if (stopOnError) { - isRejected = true; reject(error); } else { errors.push(error); resolvingCount--; - next(); + + // In that case we can't really continue regardless of `stopOnError` state + // since an iterable is likely to continue throwing after it throws once. + // If we continue calling `next()` indefinitely we will likely end up + // in an infinite loop of failed iteration. + try { + await next(); + } catch (error) { + reject(error); + } } } })(); }; - for (let i = 0; i < concurrency; i++) { - next(); + // Create the concurrent runners in a detached (non-awaited) + // promise. We need this so we can await the `next()` calls + // to stop creating runners before hitting the concurrency limit + // if the iterable has already been marked as done. + // NOTE: We *must* do this for async iterators otherwise we'll spin up + // infinite `next()` calls by default and never start the event loop. + (async () => { + for (let index = 0; index < concurrency; index++) { + try { + // eslint-disable-next-line no-await-in-loop + await next(); + } catch (error) { + reject(error); + break; + } - if (isIterableDone) { - break; + if (isIterableDone || isRejected) { + break; + } } - } + })(); }); -}; +} + +export function pMapIterable( + iterable, + mapper, + { + concurrency = Number.POSITIVE_INFINITY, + backpressure = concurrency, + } = {}, +) { + if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) { + throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`); + } + + if (typeof mapper !== 'function') { + throw new TypeError('Mapper function is required'); + } + + if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) { + throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`); + } + + if (!((Number.isSafeInteger(backpressure) && backpressure >= concurrency) || backpressure === Number.POSITIVE_INFINITY)) { + throw new TypeError(`Expected \`backpressure\` to be an integer from \`concurrency\` (${concurrency}) and up or \`Infinity\`, got \`${backpressure}\` (${typeof backpressure})`); + } + + return { + async * [Symbol.asyncIterator]() { + const iterator = iterable[Symbol.asyncIterator] === undefined ? iterable[Symbol.iterator]() : iterable[Symbol.asyncIterator](); + + const promises = []; + let runningMappersCount = 0; + let isDone = false; + let index = 0; + + function trySpawn() { + if (isDone || !(runningMappersCount < concurrency && promises.length < backpressure)) { + return; + } + + const promise = (async () => { + const {done, value} = await iterator.next(); + + if (done) { + return {done: true}; + } + + runningMappersCount++; + + // Spawn if still below concurrency and backpressure limit + trySpawn(); + + try { + const returnValue = await mapper(await value, index++); + + runningMappersCount--; + + if (returnValue === pMapSkip) { + const index = promises.indexOf(promise); + + if (index > 0) { + promises.splice(index, 1); + } + } + + // Spawn if still below backpressure limit and just dropped below concurrency limit + trySpawn(); + + return {done: false, value: returnValue}; + } catch (error) { + isDone = true; + return {error}; + } + })(); + + promises.push(promise); + } + + trySpawn(); + + while (promises.length > 0) { + const {error, done, value} = await promises[0]; // eslint-disable-line no-await-in-loop + + promises.shift(); + + if (error) { + throw error; + } + + if (done) { + return; + } + + // Spawn if just dropped below backpressure limit and below the concurrency limit + trySpawn(); + + if (value === pMapSkip) { + continue; + } + + yield value; + } + }, + }; +} + +export const pMapSkip = Symbol('skip'); diff --git a/deps/npm/node_modules/p-map/package.json b/deps/npm/node_modules/p-map/package.json index 042b1af553f2de..b7b6594c855d8c 100644 --- a/deps/npm/node_modules/p-map/package.json +++ b/deps/npm/node_modules/p-map/package.json @@ -1,6 +1,6 @@ { "name": "p-map", - "version": "4.0.0", + "version": "7.0.3", "description": "Map over promises concurrently", "license": "MIT", "repository": "sindresorhus/p-map", @@ -10,8 +10,14 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", + "exports": { + "types": "./index.d.ts", + "default": "./index.js" + }, + "sideEffects": false, "engines": { - "node": ">=10" + "node": ">=18" }, "scripts": { "test": "xo && ava && tsd" @@ -38,16 +44,14 @@ "parallel", "bluebird" ], - "dependencies": { - "aggregate-error": "^3.0.0" - }, "devDependencies": { - "ava": "^2.2.0", - "delay": "^4.1.0", - "in-range": "^2.0.0", - "random-int": "^2.0.0", - "time-span": "^3.1.0", - "tsd": "^0.7.4", - "xo": "^0.27.2" + "ava": "^5.2.0", + "chalk": "^5.3.0", + "delay": "^6.0.0", + "in-range": "^3.0.0", + "random-int": "^3.0.0", + "time-span": "^5.1.0", + "tsd": "^0.29.0", + "xo": "^0.56.0" } } diff --git a/deps/npm/node_modules/pacote/lib/dir.js b/deps/npm/node_modules/pacote/lib/dir.js index 4ae97c216fe64f..04846eb8a6e221 100644 --- a/deps/npm/node_modules/pacote/lib/dir.js +++ b/deps/npm/node_modules/pacote/lib/dir.js @@ -32,6 +32,9 @@ class DirFetcher extends Fetcher { if (!mani.scripts || !mani.scripts.prepare) { return } + if (this.opts.ignoreScripts) { + return + } // we *only* run prepare. // pre/post-pack is run by the npm CLI for publish and pack, diff --git a/deps/npm/node_modules/pacote/package.json b/deps/npm/node_modules/pacote/package.json index 71c9aa1ce32572..422be5f5452dc8 100644 --- a/deps/npm/node_modules/pacote/package.json +++ b/deps/npm/node_modules/pacote/package.json @@ -1,6 +1,6 @@ { "name": "pacote", - "version": "19.0.1", + "version": "21.0.0", "description": "JavaScript package downloader", "author": "GitHub Inc.", "bin": { @@ -26,13 +26,14 @@ ] }, "devDependencies": { - "@npmcli/arborist": "^7.1.0", + "@npmcli/arborist": "^8.0.0", "@npmcli/eslint-config": "^5.0.0", - "@npmcli/template-oss": "4.23.3", + "@npmcli/template-oss": "4.23.4", "hosted-git-info": "^8.0.0", "mutate-fs": "^2.1.1", "nock": "^13.2.4", "npm-registry-mock": "^1.3.2", + "rimraf": "^6.0.1", "tap": "^16.0.1" }, "files": [ @@ -54,7 +55,7 @@ "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^12.0.0", - "npm-packlist": "^9.0.0", + "npm-packlist": "^10.0.0", "npm-pick-manifest": "^10.0.0", "npm-registry-fetch": "^18.0.0", "proc-log": "^5.0.0", @@ -64,7 +65,7 @@ "tar": "^6.1.11" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "repository": { "type": "git", @@ -72,7 +73,7 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.4", "windowsCI": false, "publish": "true" } diff --git a/deps/npm/node_modules/socks-proxy-agent/dist/index.js b/deps/npm/node_modules/socks-proxy-agent/dist/index.js index a9b5db2d61f573..15e06e8f431765 100644 --- a/deps/npm/node_modules/socks-proxy-agent/dist/index.js +++ b/deps/npm/node_modules/socks-proxy-agent/dist/index.js @@ -31,9 +31,21 @@ const socks_1 = require("socks"); const agent_base_1 = require("agent-base"); const debug_1 = __importDefault(require("debug")); const dns = __importStar(require("dns")); +const net = __importStar(require("net")); const tls = __importStar(require("tls")); const url_1 = require("url"); const debug = (0, debug_1.default)('socks-proxy-agent'); +const setServernameFromNonIpHost = (options) => { + if (options.servername === undefined && + options.host && + !net.isIP(options.host)) { + return { + ...options, + servername: options.host, + }; + } + return options; +}; function parseSocksURL(url) { let lookup = false; let type = 5; @@ -149,11 +161,9 @@ class SocksProxyAgent extends agent_base_1.Agent { // The proxy is connecting to a TLS server, so upgrade // this socket connection to a TLS connection. debug('Upgrading socket connection to TLS'); - const servername = opts.servername || opts.host; const tlsSocket = tls.connect({ - ...omit(opts, 'host', 'path', 'port'), + ...omit(setServernameFromNonIpHost(opts), 'host', 'path', 'port'), socket, - servername, }); tlsSocket.once('error', (error) => { debug('Socket TLS error', error.message); diff --git a/deps/npm/node_modules/socks-proxy-agent/package.json b/deps/npm/node_modules/socks-proxy-agent/package.json index ae0e373fa77381..0f330a73106778 100644 --- a/deps/npm/node_modules/socks-proxy-agent/package.json +++ b/deps/npm/node_modules/socks-proxy-agent/package.json @@ -1,6 +1,6 @@ { "name": "socks-proxy-agent", - "version": "8.0.4", + "version": "8.0.5", "description": "A SOCKS proxy `http.Agent` implementation for HTTP and HTTPS", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -107,7 +107,7 @@ "socks5h" ], "dependencies": { - "agent-base": "^7.1.1", + "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, diff --git a/deps/npm/node_modules/sprintf-js/bower.json b/deps/npm/node_modules/sprintf-js/bower.json deleted file mode 100644 index d90a75989f7b05..00000000000000 --- a/deps/npm/node_modules/sprintf-js/bower.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "sprintf", - "description": "JavaScript sprintf implementation", - "version": "1.0.3", - "main": "src/sprintf.js", - "license": "BSD-3-Clause-Clear", - "keywords": ["sprintf", "string", "formatting"], - "authors": ["Alexandru Marasteanu (http://alexei.ro/)"], - "homepage": "https://github.com/alexei/sprintf.js", - "repository": { - "type": "git", - "url": "git://github.com/alexei/sprintf.js.git" - } -} diff --git a/deps/npm/node_modules/sprintf-js/demo/angular.html b/deps/npm/node_modules/sprintf-js/demo/angular.html deleted file mode 100644 index 3559efd7635634..00000000000000 --- a/deps/npm/node_modules/sprintf-js/demo/angular.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - -
      {{ "%+010d"|sprintf:-123 }}
      -
      {{ "%+010d"|vsprintf:[-123] }}
      -
      {{ "%+010d"|fmt:-123 }}
      -
      {{ "%+010d"|vfmt:[-123] }}
      -
      {{ "I've got %2$d apples and %1$d oranges."|fmt:4:2 }}
      -
      {{ "I've got %(apples)d apples and %(oranges)d oranges."|fmt:{apples: 2, oranges: 4} }}
      - - - - diff --git a/deps/npm/node_modules/sprintf-js/gruntfile.js b/deps/npm/node_modules/sprintf-js/gruntfile.js deleted file mode 100644 index 246e1c3b9801fc..00000000000000 --- a/deps/npm/node_modules/sprintf-js/gruntfile.js +++ /dev/null @@ -1,36 +0,0 @@ -module.exports = function(grunt) { - grunt.initConfig({ - pkg: grunt.file.readJSON("package.json"), - - uglify: { - options: { - banner: "/*! <%= pkg.name %> | <%= pkg.author %> | <%= pkg.license %> */\n", - sourceMap: true - }, - build: { - files: [ - { - src: "src/sprintf.js", - dest: "dist/sprintf.min.js" - }, - { - src: "src/angular-sprintf.js", - dest: "dist/angular-sprintf.min.js" - } - ] - } - }, - - watch: { - js: { - files: "src/*.js", - tasks: ["uglify"] - } - } - }) - - grunt.loadNpmTasks("grunt-contrib-uglify") - grunt.loadNpmTasks("grunt-contrib-watch") - - grunt.registerTask("default", ["uglify", "watch"]) -} diff --git a/deps/npm/node_modules/sprintf-js/test/test.js b/deps/npm/node_modules/sprintf-js/test/test.js deleted file mode 100644 index 6f57b2538c8522..00000000000000 --- a/deps/npm/node_modules/sprintf-js/test/test.js +++ /dev/null @@ -1,82 +0,0 @@ -var assert = require("assert"), - sprintfjs = require("../src/sprintf.js"), - sprintf = sprintfjs.sprintf, - vsprintf = sprintfjs.vsprintf - -describe("sprintfjs", function() { - var pi = 3.141592653589793 - - it("should return formated strings for simple placeholders", function() { - assert.equal("%", sprintf("%%")) - assert.equal("10", sprintf("%b", 2)) - assert.equal("A", sprintf("%c", 65)) - assert.equal("2", sprintf("%d", 2)) - assert.equal("2", sprintf("%i", 2)) - assert.equal("2", sprintf("%d", "2")) - assert.equal("2", sprintf("%i", "2")) - assert.equal('{"foo":"bar"}', sprintf("%j", {foo: "bar"})) - assert.equal('["foo","bar"]', sprintf("%j", ["foo", "bar"])) - assert.equal("2e+0", sprintf("%e", 2)) - assert.equal("2", sprintf("%u", 2)) - assert.equal("4294967294", sprintf("%u", -2)) - assert.equal("2.2", sprintf("%f", 2.2)) - assert.equal("3.141592653589793", sprintf("%g", pi)) - assert.equal("10", sprintf("%o", 8)) - assert.equal("%s", sprintf("%s", "%s")) - assert.equal("ff", sprintf("%x", 255)) - assert.equal("FF", sprintf("%X", 255)) - assert.equal("Polly wants a cracker", sprintf("%2$s %3$s a %1$s", "cracker", "Polly", "wants")) - assert.equal("Hello world!", sprintf("Hello %(who)s!", {"who": "world"})) - }) - - it("should return formated strings for complex placeholders", function() { - // sign - assert.equal("2", sprintf("%d", 2)) - assert.equal("-2", sprintf("%d", -2)) - assert.equal("+2", sprintf("%+d", 2)) - assert.equal("-2", sprintf("%+d", -2)) - assert.equal("2", sprintf("%i", 2)) - assert.equal("-2", sprintf("%i", -2)) - assert.equal("+2", sprintf("%+i", 2)) - assert.equal("-2", sprintf("%+i", -2)) - assert.equal("2.2", sprintf("%f", 2.2)) - assert.equal("-2.2", sprintf("%f", -2.2)) - assert.equal("+2.2", sprintf("%+f", 2.2)) - assert.equal("-2.2", sprintf("%+f", -2.2)) - assert.equal("-2.3", sprintf("%+.1f", -2.34)) - assert.equal("-0.0", sprintf("%+.1f", -0.01)) - assert.equal("3.14159", sprintf("%.6g", pi)) - assert.equal("3.14", sprintf("%.3g", pi)) - assert.equal("3", sprintf("%.1g", pi)) - assert.equal("-000000123", sprintf("%+010d", -123)) - assert.equal("______-123", sprintf("%+'_10d", -123)) - assert.equal("-234.34 123.2", sprintf("%f %f", -234.34, 123.2)) - - // padding - assert.equal("-0002", sprintf("%05d", -2)) - assert.equal("-0002", sprintf("%05i", -2)) - assert.equal(" <", sprintf("%5s", "<")) - assert.equal("0000<", sprintf("%05s", "<")) - assert.equal("____<", sprintf("%'_5s", "<")) - assert.equal("> ", sprintf("%-5s", ">")) - assert.equal(">0000", sprintf("%0-5s", ">")) - assert.equal(">____", sprintf("%'_-5s", ">")) - assert.equal("xxxxxx", sprintf("%5s", "xxxxxx")) - assert.equal("1234", sprintf("%02u", 1234)) - assert.equal(" -10.235", sprintf("%8.3f", -10.23456)) - assert.equal("-12.34 xxx", sprintf("%f %s", -12.34, "xxx")) - assert.equal('{\n "foo": "bar"\n}', sprintf("%2j", {foo: "bar"})) - assert.equal('[\n "foo",\n "bar"\n]', sprintf("%2j", ["foo", "bar"])) - - // precision - assert.equal("2.3", sprintf("%.1f", 2.345)) - assert.equal("xxxxx", sprintf("%5.5s", "xxxxxx")) - assert.equal(" x", sprintf("%5.1s", "xxxxxx")) - - }) - - it("should return formated strings for callbacks", function() { - assert.equal("foobar", sprintf("%s", function() { return "foobar" })) - assert.equal(Date.now(), sprintf("%s", Date.now)) // should pass... - }) -}) diff --git a/deps/npm/node_modules/walk-up-path/LICENSE b/deps/npm/node_modules/walk-up-path/LICENSE index 05eeeb88c2ef4c..d710582667b8bc 100644 --- a/deps/npm/node_modules/walk-up-path/LICENSE +++ b/deps/npm/node_modules/walk-up-path/LICENSE @@ -1,6 +1,6 @@ The ISC License -Copyright (c) Isaac Z. Schlueter +Copyright (c) 2020-2023 Isaac Z. Schlueter Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/deps/npm/node_modules/walk-up-path/dist/cjs/index.js b/deps/npm/node_modules/walk-up-path/dist/commonjs/index.js similarity index 100% rename from deps/npm/node_modules/walk-up-path/dist/cjs/index.js rename to deps/npm/node_modules/walk-up-path/dist/commonjs/index.js diff --git a/deps/npm/node_modules/walk-up-path/dist/cjs/package.json b/deps/npm/node_modules/walk-up-path/dist/commonjs/package.json similarity index 100% rename from deps/npm/node_modules/walk-up-path/dist/cjs/package.json rename to deps/npm/node_modules/walk-up-path/dist/commonjs/package.json diff --git a/deps/npm/node_modules/walk-up-path/dist/mjs/index.js b/deps/npm/node_modules/walk-up-path/dist/esm/index.js similarity index 100% rename from deps/npm/node_modules/walk-up-path/dist/mjs/index.js rename to deps/npm/node_modules/walk-up-path/dist/esm/index.js diff --git a/deps/npm/node_modules/walk-up-path/dist/mjs/package.json b/deps/npm/node_modules/walk-up-path/dist/esm/package.json similarity index 100% rename from deps/npm/node_modules/walk-up-path/dist/mjs/package.json rename to deps/npm/node_modules/walk-up-path/dist/esm/package.json diff --git a/deps/npm/node_modules/walk-up-path/package.json b/deps/npm/node_modules/walk-up-path/package.json index 0931c670a8d044..4f6d95363297e7 100644 --- a/deps/npm/node_modules/walk-up-path/package.json +++ b/deps/npm/node_modules/walk-up-path/package.json @@ -1,24 +1,9 @@ { "name": "walk-up-path", - "version": "3.0.1", + "version": "4.0.0", "files": [ "dist" ], - "main": "./dist/cjs/index.js", - "module": "./dist/mjs/index.js", - "types": "./dist/mjs/index.d.ts", - "exports": { - ".": { - "require": { - "types": "./dist/cjs/index.d.ts", - "default": "./dist/cjs/index.js" - }, - "import": { - "types": "./dist/mjs/index.d.ts", - "default": "./dist/mjs/index.js" - } - } - }, "description": "Given a path string, return a generator that walks up the path, emitting each dirname.", "repository": { "type": "git", @@ -30,15 +15,16 @@ "preversion": "npm test", "postversion": "npm publish", "prepublishOnly": "git push origin --follow-tags", - "prepare": "tsc -p tsconfig.json && tsc -p tsconfig-esm.json && bash ./scripts/fixup.sh", + "prepare": "tshy", "pretest": "npm run prepare", "presnap": "npm run prepare", - "test": "c8 tap", - "snap": "c8 tap", - "format": "prettier --write . --loglevel warn", + "test": "tap", + "snap": "tap", + "format": "prettier --write . --log-level warn", "typedoc": "typedoc --tsconfig tsconfig-esm.json ./src/*.ts" }, "prettier": { + "experimentalTernaries": true, "semi": false, "printWidth": 75, "tabWidth": 2, @@ -49,24 +35,37 @@ "arrowParens": "avoid", "endOfLine": "lf" }, - "tap": { - "coverage": false, - "node-arg": [ - "--no-warnings", - "--loader", - "ts-node/esm" - ], - "ts": false - }, "devDependencies": { - "@types/node": "^18.15.5", - "@types/tap": "^15.0.8", - "c8": "^7.13.0", - "eslint-config-prettier": "^8.8.0", - "prettier": "^2.8.6", - "tap": "^16.3.4", - "ts-node": "^10.9.1", - "typedoc": "^0.23.28", - "typescript": "^5.0.2" + "@types/node": "^20.14.10", + "prettier": "^3.3.2", + "tap": "^20.0.3", + "tshy": "^3.0.0", + "typedoc": "^0.26.3" + }, + "type": "module", + "tshy": { + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "main": "./dist/commonjs/index.js", + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js", + "engines": { + "node": "20 || >=22" } } diff --git a/deps/npm/package.json b/deps/npm/package.json index 8d01af4a7ce8d0..82c5a193b88c02 100644 --- a/deps/npm/package.json +++ b/deps/npm/package.json @@ -1,5 +1,5 @@ { - "version": "10.9.2", + "version": "11.0.0", "name": "npm", "description": "a package manager for JavaScript", "workspaces": [ @@ -52,8 +52,8 @@ }, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^8.0.0", - "@npmcli/config": "^9.0.0", + "@npmcli/arborist": "^9.0.0", + "@npmcli/config": "^10.0.0", "@npmcli/fs": "^4.0.0", "@npmcli/map-workspaces": "^4.0.2", "@npmcli/package-json": "^6.1.0", @@ -73,20 +73,19 @@ "graceful-fs": "^4.2.11", "hosted-git-info": "^8.0.2", "ini": "^5.0.0", - "init-package-json": "^7.0.2", + "init-package-json": "^8.0.0", "is-cidr": "^5.1.0", "json-parse-even-better-errors": "^4.0.0", - "libnpmaccess": "^9.0.0", - "libnpmdiff": "^7.0.0", - "libnpmexec": "^9.0.0", - "libnpmfund": "^6.0.0", - "libnpmhook": "^11.0.0", - "libnpmorg": "^7.0.0", - "libnpmpack": "^8.0.0", - "libnpmpublish": "^10.0.1", - "libnpmsearch": "^8.0.0", - "libnpmteam": "^7.0.0", - "libnpmversion": "^7.0.0", + "libnpmaccess": "^10.0.0", + "libnpmdiff": "^8.0.0", + "libnpmexec": "^10.0.0", + "libnpmfund": "^7.0.0", + "libnpmorg": "^8.0.0", + "libnpmpack": "^9.0.0", + "libnpmpublish": "^11.0.0", + "libnpmsearch": "^9.0.0", + "libnpmteam": "^8.0.0", + "libnpmversion": "^8.0.0", "make-fetch-happen": "^14.0.3", "minimatch": "^9.0.5", "minipass": "^7.1.1", @@ -97,13 +96,13 @@ "normalize-package-data": "^7.0.0", "npm-audit-report": "^6.0.0", "npm-install-checks": "^7.1.1", - "npm-package-arg": "^12.0.0", + "npm-package-arg": "^12.0.1", "npm-pick-manifest": "^10.0.0", "npm-profile": "^11.0.1", "npm-registry-fetch": "^18.0.2", "npm-user-validate": "^3.0.0", - "p-map": "^4.0.0", - "pacote": "^19.0.1", + "p-map": "^7.0.3", + "pacote": "^21.0.0", "parse-conflict-json": "^4.0.0", "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", @@ -117,8 +116,7 @@ "tiny-relative-date": "^1.3.0", "treeverse": "^3.0.0", "validate-npm-package-name": "^6.0.0", - "which": "^5.0.0", - "write-file-atomic": "^6.0.0" + "which": "^5.0.0" }, "bundleDependencies": [ "@isaacs/string-locale-compare", @@ -150,7 +148,6 @@ "libnpmdiff", "libnpmexec", "libnpmfund", - "libnpmhook", "libnpmorg", "libnpmpack", "libnpmpublish", @@ -187,8 +184,7 @@ "tiny-relative-date", "treeverse", "validate-npm-package-name", - "which", - "write-file-atomic" + "which" ], "devDependencies": { "@npmcli/docs": "^1.0.0", @@ -196,15 +192,15 @@ "@npmcli/git": "^6.0.1", "@npmcli/mock-globals": "^1.0.0", "@npmcli/mock-registry": "^1.0.0", - "@npmcli/template-oss": "4.23.3", - "@tufjs/repo-mock": "^2.0.0", + "@npmcli/template-oss": "4.23.6", + "@tufjs/repo-mock": "^3.0.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "ajv-formats-draft2019": "^1.6.1", "cli-table3": "^0.6.4", - "diff": "^5.2.0", + "diff": "^7.0.0", "nock": "^13.4.0", - "npm-packlist": "^9.0.0", + "npm-packlist": "^10.0.0", "remark": "^14.0.2", "remark-gfm": "^3.0.1", "remark-github": "^11.2.4", @@ -254,11 +250,11 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.23.3", + "version": "4.23.6", "content": "./scripts/template-oss/root.js" }, "license": "Artistic-2.0", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } } diff --git a/deps/npm/tap-snapshots/test/lib/commands/audit.js.test.cjs b/deps/npm/tap-snapshots/test/lib/commands/audit.js.test.cjs index 21c22b26c12e6b..843cf7c8dd3708 100644 --- a/deps/npm/tap-snapshots/test/lib/commands/audit.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/commands/audit.js.test.cjs @@ -331,21 +331,6 @@ audited 2 packages in xxx 2 packages have verified registry signatures ` -exports[`test/lib/commands/audit.js TAP fallback audit > must match snapshot 1`] = ` -# npm audit report - -test-dep-a 1.0.0 -Severity: high -Test advisory 100 - https://github.com/advisories/GHSA-100 -fix available via \`npm audit fix\` -node_modules/test-dep-a - -1 high severity vulnerability - -To address all issues, run: - npm audit fix -` - exports[`test/lib/commands/audit.js TAP json audit > must match snapshot 1`] = ` { "auditReportVersion": 2, diff --git a/deps/npm/tap-snapshots/test/lib/commands/completion.js.test.cjs b/deps/npm/tap-snapshots/test/lib/commands/completion.js.test.cjs index a538e3c0688633..9b6f1ba51c7871 100644 --- a/deps/npm/tap-snapshots/test/lib/commands/completion.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/commands/completion.js.test.cjs @@ -64,7 +64,6 @@ Array [ get help help-search - hook init install install-ci-test diff --git a/deps/npm/tap-snapshots/test/lib/commands/publish.js.test.cjs b/deps/npm/tap-snapshots/test/lib/commands/publish.js.test.cjs index 78395c5cbca637..c0dc06b568180a 100644 --- a/deps/npm/tap-snapshots/test/lib/commands/publish.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/commands/publish.js.test.cjs @@ -174,7 +174,6 @@ Object { "man/man1/npm-fund.1", "man/man1/npm-help-search.1", "man/man1/npm-help.1", - "man/man1/npm-hook.1", "man/man1/npm-init.1", "man/man1/npm-install-ci-test.1", "man/man1/npm-install-test.1", diff --git a/deps/npm/tap-snapshots/test/lib/commands/search.js.test.cjs b/deps/npm/tap-snapshots/test/lib/commands/search.js.test.cjs index d5485853545882..eb2bf419dd2186 100644 --- a/deps/npm/tap-snapshots/test/lib/commands/search.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/commands/search.js.test.cjs @@ -45,12 +45,6 @@ npm Team management APIs Version 2.0.2 published 2020-11-03 by nlf Maintainers: nlf ruyadorno darcyclarke isaacs https://npm.im/libnpmteam -libnpmhook -programmatic API for managing npm registry hooks -Version 6.0.1 published 2020-11-03 by nlf -Maintainers: nlf ruyadorno darcyclarke isaacs -Keywords: npm hooks registry npm api -https://npm.im/libnpmhook libnpmpublish Programmatic API for the bits behind npm publish and unpublish Version 4.0.0 published 2020-11-03 by nlf @@ -124,12 +118,6 @@ npm Team management APIs Version 2.0.2 published 2020-11-03 by nlf Maintainers: nlf ruyadorno darcyclarke isaacs https://npm.im/libnpmteam -libnpmhook -programmatic API for managing npm registry hooks -Version 6.0.1 published 2020-11-03 by nlf -Maintainers: nlf ruyadorno darcyclarke isaacs -Keywords: npm hooks registry npm api -https://npm.im/libnpmhook libnpmpublish Programmatic API for the bits behind npm publish and unpublish Version 4.0.0 published 2020-11-03 by nlf @@ -171,7 +159,6 @@ libnpmaccess programmatic library for \`npm access\` commands 2020-11-03 4.0.1 l libnpmorg Programmatic api for \`npm org\` commands 2020-11-03 2.0.1 libnpm,npm,package manager,api,orgs,teams libnpmsearch Programmatic API for searching in npm and compatible registries. 2020-12-08 3.1.0 npm,search,api,libnpm libnpmteam npm Team management APIs 2020-11-03 2.0.2 -libnpmhook programmatic API for managing npm registry hooks 2020-11-03 6.0.1 npm,hooks,registry,npm api libnpmpublish Programmatic API for the bits behind npm publish and unpublish 2020-11-03 4.0.0 libnpmfund Programmatic API for npm fund 2020-12-08 1.0.2 npm,npmcli,libnpm,cli,git,fund,gitfund @npmcli/map-workspaces Retrieves a name:pathname Map for a given workspaces config 2020-09-30 1.0.1 npm,,bad map,npmcli,libnpm,cli,workspaces,map-workspaces @@ -235,12 +222,6 @@ npm Team management APIs Version 2.0.2 published 2020-11-03 by nlf Maintainers: nlf ruyadorno darcyclarke isaacs https://npm.im/libnpmteam -libnpmhook -programmatic API for managing npm registry hooks -Version 6.0.1 published 2020-11-03 by nlf -Maintainers: nlf ruyadorno darcyclarke isaacs -Keywords: npm hooks registry npm api -https://npm.im/libnpmhook libnpmpublish Programmatic API for the bits behind npm publish and unpublish Version 4.0.0 published 2020-11-03 by nlf @@ -318,12 +299,6 @@ npm Team management APIs Version 2.0.2 published 2020-11-03 by nlf Maintainers: nlf ruyadorno darcyclarke isaacs https://npm.im/libnpmteam -libnpmhook -programmatic API for managing npm registry hooks -Version 6.0.1 published 2020-11-03 by nlf -Maintainers: nlf ruyadorno darcyclarke isaacs -Keywords: npm hooks registry npm api -https://npm.im/libnpmhook libnpmpublish Programmatic API for the bits behind npm publish and unpublish Version 4.0.0 published 2020-11-03 by nlf @@ -392,12 +367,6 @@ npm Team management APIs Version 2.0.2 published 2020-11-03 by nlf Maintainers: nlf ruyadorno darcyclarke isaacs https://npm.im/libnpmteam -libnpmhook -programmatic API for managing npm registry hooks -Version 6.0.1 published 2020-11-03 by nlf -Maintainers: nlf ruyadorno darcyclarke isaacs -Keywords: npm hooks registry npm api -https://npm.im/libnpmhook libnpmpublish Programmatic API for the bits behind npm publish and unpublish Version 4.0.0 published 2020-11-03 by nlf @@ -466,12 +435,6 @@ npm Team management APIs Version 2.0.2 published 2020-11-03 by nlf Maintainers: nlf ruyadorno darcyclarke isaacs https://npm.im/libnpmteam -libnpmhook -programmatic API for managing npm registry hooks -Version 6.0.1 published 2020-11-03 by nlf -Maintainers: nlf ruyadorno darcyclarke isaacs -Keywords: npm hooks registry npm api -https://npm.im/libnpmhook libnpmpublish Programmatic API for the bits behind npm publish and unpublish Version 4.0.0 published 2020-11-03 by nlf @@ -772,51 +735,6 @@ Array [ "scope": "unscoped", "version": "2.0.2", }, - Object { - "author": Object { - "email": "kzm@sykosomatic.org", - "name": "Kat Marchán", - }, - "date": "2020-11-03T19:20:45.818Z", - "description": "programmatic API for managing npm registry hooks", - "keywords": Array [ - "npm", - "hooks", - "registry", - "npm api", - ], - "links": Object { - "bugs": "https://github.com/npm/libnpmhook/issues", - "homepage": "https://github.com/npm/libnpmhook#readme", - "npm": "https://www.npmjs.com/package/libnpmhook", - "repository": "https://github.com/npm/libnpmhook", - }, - "maintainers": Array [ - Object { - "email": "quitlahok@gmail.com", - "username": "nlf", - }, - Object { - "email": "ruyadorno@hotmail.com", - "username": "ruyadorno", - }, - Object { - "email": "darcy@darcyclarke.me", - "username": "darcyclarke", - }, - Object { - "email": "i@izs.me", - "username": "isaacs", - }, - ], - "name": "libnpmhook", - "publisher": Object { - "email": "quitlahok@gmail.com", - "username": "nlf", - }, - "scope": "unscoped", - "version": "6.0.1", - }, Object { "author": Object { "email": "support@npmjs.com", diff --git a/deps/npm/tap-snapshots/test/lib/commands/view.js.test.cjs b/deps/npm/tap-snapshots/test/lib/commands/view.js.test.cjs index e6cd42d0d32a50..051313c59ef9a4 100644 --- a/deps/npm/tap-snapshots/test/lib/commands/view.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/commands/view.js.test.cjs @@ -102,6 +102,18 @@ dist dist-tags: latest: 1.0.0 +z: 1.0.0 +y: 1.0.0 +v1: 1.0.0 +prev: 1.0.0 +d: 1.0.0 +c: 1.0.0 +b: 1.0.0 +a: 1.0.0 +x: 1.0.1 +next: 1.0.1 +h: 1.0.1 +(...and 3 more.) published {TIME} ago ` @@ -116,6 +128,18 @@ dist dist-tags: latest: 1.0.0 +z: 1.0.0 +y: 1.0.0 +v1: 1.0.0 +prev: 1.0.0 +d: 1.0.0 +c: 1.0.0 +b: 1.0.0 +a: 1.0.0 +x: 1.0.1 +next: 1.0.1 +h: 1.0.1 +(...and 3 more.) published {TIME} ago ` @@ -130,6 +154,18 @@ dist dist-tags: latest: 1.0.0 +z: 1.0.0 +y: 1.0.0 +v1: 1.0.0 +prev: 1.0.0 +d: 1.0.0 +c: 1.0.0 +b: 1.0.0 +a: 1.0.0 +x: 1.0.1 +next: 1.0.1 +h: 1.0.1 +(...and 3 more.) published {TIME} ago ` @@ -269,6 +305,18 @@ dist dist-tags: latest: 1.0.0 +z: 1.0.0 +y: 1.0.0 +v1: 1.0.0 +prev: 1.0.0 +d: 1.0.0 +c: 1.0.0 +b: 1.0.0 +a: 1.0.0 +x: 1.0.1 +next: 1.0.1 +h: 1.0.1 +(...and 3 more.) published {TIME} ago ` @@ -283,6 +331,18 @@ dist dist-tags: latest: 1.0.0 +z: 1.0.0 +y: 1.0.0 +v1: 1.0.0 +prev: 1.0.0 +d: 1.0.0 +c: 1.0.0 +b: 1.0.0 +a: 1.0.0 +x: 1.0.1 +next: 1.0.1 +h: 1.0.1 +(...and 3 more.) published {TIME} ago @@ -296,8 +356,20 @@ dist dist-tags: latest: 1.0.0 +z: 1.0.0 +y: 1.0.0 +v1: 1.0.0 +prev: 1.0.0 +d: 1.0.0 +c: 1.0.0 +b: 1.0.0 +a: 1.0.0 +x: 1.0.1 +next: 1.0.1 +h: 1.0.1 +(...and 3 more.) -published over a year from now +published {TIME} ago ` exports[`test/lib/commands/view.js TAP package with single version full json > must match snapshot 1`] = ` diff --git a/deps/npm/tap-snapshots/test/lib/docs.js.test.cjs b/deps/npm/tap-snapshots/test/lib/docs.js.test.cjs index 68eee1542d9352..8e8d9a9ba4b815 100644 --- a/deps/npm/tap-snapshots/test/lib/docs.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/docs.js.test.cjs @@ -119,7 +119,6 @@ Array [ "get", "help", "help-search", - "hook", "init", "install", "install-ci-test", @@ -1878,9 +1877,9 @@ When set to \`dev\` or \`development\`, this is an alias for \`--include=dev\`. * Default: null * Type: null or String * DEPRECATED: \`key\` and \`cert\` are no longer used for most registry - operations. Use registry scoped \`keyfile\` and \`certfile\` instead. Example: + operations. Use registry scoped \`keyfile\` and \`cafile\` instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem - //other-registry.tld/:certfile=/path/to/cert.crt + //other-registry.tld/:cafile=/path/to/cert.crt A client certificate to pass when accessing the registry. Values should be in PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with @@ -1891,8 +1890,8 @@ cert="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----" \`\`\` It is _not_ the path to a certificate file, though you can set a -registry-scoped "certfile" path like -"//other-registry.tld/:certfile=/path/to/cert.pem". +registry-scoped "cafile" path like +"//other-registry.tld/:cafile=/path/to/cert.pem". @@ -1983,9 +1982,9 @@ Alias for \`--init-version\` * Default: null * Type: null or String * DEPRECATED: \`key\` and \`cert\` are no longer used for most registry - operations. Use registry scoped \`keyfile\` and \`certfile\` instead. Example: + operations. Use registry scoped \`keyfile\` and \`cafile\` instead. Example: //other-registry.tld/:keyfile=/path/to/key.pem - //other-registry.tld/:certfile=/path/to/cert.crt + //other-registry.tld/:cafile=/path/to/cert.crt A client key to pass when accessing the registry. Values should be in PEM format with newlines replaced by the string "\\n". For example: @@ -3200,33 +3199,6 @@ Note: This command is unaware of workspaces. #### \`long\` ` -exports[`test/lib/docs.js TAP usage hook > must match snapshot 1`] = ` -Manage registry hooks - -Usage: -npm hook add [--type=] -npm hook ls [pkg] -npm hook rm -npm hook update - -Options: -[--registry ] [--otp ] - -Run "npm help hook" for more info - -\`\`\`bash -npm hook add [--type=] -npm hook ls [pkg] -npm hook rm -npm hook update -\`\`\` - -Note: This command is unaware of workspaces. - -#### \`registry\` -#### \`otp\` -` - exports[`test/lib/docs.js TAP usage init > must match snapshot 1`] = ` Create a package.json file @@ -3708,7 +3680,7 @@ npm pack Options: [--dry-run] [--json] [--pack-destination ] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] [--include-workspace-root] +[-ws|--workspaces] [--include-workspace-root] [--ignore-scripts] Run "npm help pack" for more info @@ -3722,6 +3694,7 @@ npm pack #### \`workspace\` #### \`workspaces\` #### \`include-workspace-root\` +#### \`ignore-scripts\` ` exports[`test/lib/docs.js TAP usage ping > must match snapshot 1`] = ` @@ -3781,7 +3754,7 @@ exports[`test/lib/docs.js TAP usage prefix > must match snapshot 1`] = ` Display prefix Usage: -npm prefix [-g] +npm prefix Options: [-g|--global] @@ -3789,7 +3762,7 @@ Options: Run "npm help prefix" for more info \`\`\`bash -npm prefix [-g] +npm prefix \`\`\` Note: This command is unaware of workspaces. diff --git a/deps/npm/tap-snapshots/test/lib/npm.js.test.cjs b/deps/npm/tap-snapshots/test/lib/npm.js.test.cjs index 32ab47ef06b18e..0864ffe37d297c 100644 --- a/deps/npm/tap-snapshots/test/lib/npm.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/npm.js.test.cjs @@ -34,13 +34,12 @@ All commands: access, adduser, audit, bugs, cache, ci, completion, config, dedupe, deprecate, diff, dist-tag, docs, doctor, edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, install, install-ci-test, - install-test, link, ll, login, logout, ls, org, outdated, - owner, pack, ping, pkg, prefix, profile, prune, publish, - query, rebuild, repo, restart, root, run-script, sbom, - search, set, shrinkwrap, star, stars, start, stop, team, - test, token, uninstall, unpublish, unstar, update, version, - view, whoami + help-search, init, install, install-ci-test, install-test, + link, ll, login, logout, ls, org, outdated, owner, pack, + ping, pkg, prefix, profile, prune, publish, query, rebuild, + repo, restart, root, run-script, sbom, search, set, + shrinkwrap, star, stars, start, stop, team, test, token, + uninstall, unpublish, unstar, update, version, view, whoami Specify configs in the ini-formatted file: {USERCONFIG} @@ -76,7 +75,7 @@ All commands: edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, + help-search, init, install, install-ci-test, install-test, link, ll, @@ -128,7 +127,7 @@ All commands: edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, + help-search, init, install, install-ci-test, install-test, link, ll, @@ -175,13 +174,12 @@ All commands: access, adduser, audit, bugs, cache, ci, completion, config, dedupe, deprecate, diff, dist-tag, docs, doctor, edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, install, install-ci-test, - install-test, link, ll, login, logout, ls, org, outdated, - owner, pack, ping, pkg, prefix, profile, prune, publish, - query, rebuild, repo, restart, root, run-script, sbom, - search, set, shrinkwrap, star, stars, start, stop, team, - test, token, uninstall, unpublish, unstar, update, version, - view, whoami + help-search, init, install, install-ci-test, install-test, + link, ll, login, logout, ls, org, outdated, owner, pack, + ping, pkg, prefix, profile, prune, publish, query, rebuild, + repo, restart, root, run-script, sbom, search, set, + shrinkwrap, star, stars, start, stop, team, test, token, + uninstall, unpublish, unstar, update, version, view, whoami Specify configs in the ini-formatted file: {USERCONFIG} @@ -217,7 +215,7 @@ All commands: edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, + help-search, init, install, install-ci-test, install-test, link, ll, @@ -269,7 +267,7 @@ All commands: edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, + help-search, init, install, install-ci-test, install-test, link, ll, @@ -321,7 +319,7 @@ All commands: edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, + help-search, init, install, install-ci-test, install-test, link, ll, login, logout, ls, org, @@ -367,13 +365,13 @@ All commands: access, adduser, audit, bugs, cache, ci, completion, config, dedupe, deprecate, diff, dist-tag, docs, doctor, edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, install, install-ci-test, - install-test, link, ll, login, logout, ls, org, outdated, - owner, pack, ping, pkg, prefix, profile, prune, publish, - query, rebuild, repo, restart, root, run-script, sbom, - search, set, shrinkwrap, star, stars, start, stop, team, - test, token, uninstall, unpublish, unstar, update, version, - view, whoami + help-search, init, install, install-ci-test, install-test, + link, ll, login, logout, ls, org, outdated, owner, pack, + ping, pkg, prefix, profile, prune, publish, query, rebuild, + repo, restart, root, run-script, sbom, search, set, + shrinkwrap, star, stars, start, stop, team, test, token, + uninstall, unpublish, unstar, update, version, view, + whoami Specify configs in the ini-formatted file: {USERCONFIG} @@ -404,13 +402,12 @@ All commands: access, adduser, audit, bugs, cache, ci, completion, config, dedupe, deprecate, diff, dist-tag, docs, doctor, edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, install, install-ci-test, - install-test, link, ll, login, logout, ls, org, outdated, - owner, pack, ping, pkg, prefix, profile, prune, publish, - query, rebuild, repo, restart, root, run-script, sbom, - search, set, shrinkwrap, star, stars, start, stop, team, - test, token, uninstall, unpublish, unstar, update, version, - view, whoami + help-search, init, install, install-ci-test, install-test, + link, ll, login, logout, ls, org, outdated, owner, pack, + ping, pkg, prefix, profile, prune, publish, query, rebuild, + repo, restart, root, run-script, sbom, search, set, + shrinkwrap, star, stars, start, stop, team, test, token, + uninstall, unpublish, unstar, update, version, view, whoami Specify configs in the ini-formatted file: {USERCONFIG} @@ -441,13 +438,12 @@ All commands: access, adduser, audit, bugs, cache, ci, completion, config, dedupe, deprecate, diff, dist-tag, docs, doctor, edit, exec, explain, explore, find-dupes, fund, get, help, - help-search, hook, init, install, install-ci-test, - install-test, link, ll, login, logout, ls, org, outdated, - owner, pack, ping, pkg, prefix, profile, prune, publish, - query, rebuild, repo, restart, root, run-script, sbom, - search, set, shrinkwrap, star, stars, start, stop, team, - test, token, uninstall, unpublish, unstar, update, version, - view, whoami + help-search, init, install, install-ci-test, install-test, + link, ll, login, logout, ls, org, outdated, owner, pack, + ping, pkg, prefix, profile, prune, publish, query, rebuild, + repo, restart, root, run-script, sbom, search, set, + shrinkwrap, star, stars, start, stop, team, test, token, + uninstall, unpublish, unstar, update, version, view, whoami Specify configs in the ini-formatted file: {USERCONFIG} diff --git a/deps/npm/test/fixtures/git-test.tgz b/deps/npm/test/fixtures/git-test.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c06592708c2a0995543a3716238ea7b803e7a22e GIT binary patch literal 268 zcmV+n0rUPJiwFP!00002|Lv8}O2a@9hxfcsF~-BT&?K{(KcIKPgC~7~bUVt{W)pXk zS`pt}TALn<-i%V%?=tY4VPFm)(^lrWdZN_21#OZgixEJ?GSA_=e2dm4h|9b{t&0LP z5U8=dC_$k31A6W%FA#s>NJNA~JrT~&(w==fw=N~O2G=Y|N>4An^)ykBtftAUw;kQs z72Ur%Gf}RsW7?33JK`?p-WXz5Go_~2Qi{N7(eC}<*JR0!8^Qk~%YOF1#AE+;zMT*L zkI%aV0Ek>wQ*;H { const mock = await setupMockNpm(t, opts) + return { + ...mock, + ...loadRegistry(t, mock, opts), + ...loadFsAssertions(t, mock), + } +} + +const loadRegistry = (t, mock, opts) => { const registry = new MockRegistry({ tap: t, - registry: mock.npm.config.get('registry'), - strict: true, + registry: opts.registry ?? mock.npm.config.get('registry'), + authorization: opts.authorization, + basic: opts.basic, + debug: opts.debugRegistry ?? false, + strict: opts.strictRegistryNock ?? true, }) + return { registry } +} +const loadFsAssertions = (t, mock) => { const fileShouldExist = (filePath) => { t.equal( fsSync.existsSync(path.join(mock.npm.prefix, filePath)), true, `${filePath} should exist` @@ -352,7 +366,7 @@ const loadNpmWithRegistry = async (t, opts) => { packageDirty, } - return { registry, assert, ...mock } + return { assert } } /** breaks down a spec "abbrev@1.1.1" into different parts for mocking */ diff --git a/deps/npm/test/lib/cli/entry.js b/deps/npm/test/lib/cli/entry.js index 900e3ab7943177..369927dace0f24 100644 --- a/deps/npm/test/lib/cli/entry.js +++ b/deps/npm/test/lib/cli/entry.js @@ -35,16 +35,16 @@ const cliMock = async (t, opts) => { } } -t.test('print the version, and treat npm_g as npm -g', async t => { +t.test('print the version ', async t => { const { logs, cli, Npm, outputs, exitHandlerCalled } = await cliMock(t, { - globals: { 'process.argv': ['node', 'npm_g', 'root'] }, + globals: { 'process.argv': ['node', 'npm', 'root'] }, }) await cli(process) - t.strictSame(process.argv, ['node', 'npm', '-g', 'root'], 'system process.argv was rewritten') + t.strictSame(process.argv, ['node', 'npm', 'root'], 'system process.argv was rewritten') t.strictSame(logs.verbose.byTitle('cli'), ['cli node npm']) t.strictSame(logs.verbose.byTitle('title'), ['title npm root']) - t.match(logs.verbose.byTitle('argv'), ['argv "--global" "root"']) + t.match(logs.verbose.byTitle('argv'), ['argv "root"']) t.strictSame(logs.info, [`using npm@${Npm.version}`, `using node@${process.version}`]) t.equal(outputs.length, 1) t.match(outputs[0], dirname(process.cwd())) diff --git a/deps/npm/test/lib/commands/audit.js b/deps/npm/test/lib/commands/audit.js index 4b239116188cae..e3bf40ee613732 100644 --- a/deps/npm/test/lib/commands/audit.js +++ b/deps/npm/test/lib/commands/audit.js @@ -90,54 +90,6 @@ t.test('normal audit', async t => { t.matchSnapshot(joinedOutput()) }) -t.test('fallback audit ', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { - prefixDir: tree, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), - }) - const manifest = registry.manifest({ - name: 'test-dep-a', - packuments: [{ version: '1.0.0' }, { version: '1.0.1' }], - }) - await registry.package({ manifest }) - const advisory = registry.advisory({ - id: 100, - module_name: 'test-dep-a', - vulnerable_versions: '<1.0.1', - findings: [{ version: '1.0.0', paths: ['test-dep-a'] }], - }) - registry.nock - .post('/-/npm/v1/security/advisories/bulk').reply(404) - .post('/-/npm/v1/security/audits/quick', body => { - const unzipped = JSON.parse(gunzip(Buffer.from(body, 'hex'))) - return t.match(unzipped, { - name: 'test-dep', - version: '1.0.0', - requires: { 'test-dep-a': '*' }, - dependencies: { 'test-dep-a': { version: '1.0.0' } }, - }) - }).reply(200, { - actions: [], - muted: [], - advisories: { - 100: advisory, - }, - metadata: { - vulnerabilities: { info: 0, low: 0, moderate: 0, high: 1, critical: 0 }, - dependencies: 1, - devDependencies: 0, - optionalDependencies: 0, - totalDependencies: 1, - }, - }) - await npm.exec('audit', []) - t.ok(process.exitCode, 'would have exited uncleanly') - t.matchSnapshot(joinedOutput()) -}) - t.test('json audit', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { prefixDir: tree, diff --git a/deps/npm/test/lib/commands/exec.js b/deps/npm/test/lib/commands/exec.js index c2977a2f577cb3..db2a8641708d31 100644 --- a/deps/npm/test/lib/commands/exec.js +++ b/deps/npm/test/lib/commands/exec.js @@ -254,3 +254,27 @@ t.test('npx --no-install @npmcli/npx-test', async t => { ) } }) + +t.test('packs from git spec', async t => { + const spec = 'test/test#111111aaaaaaaabbbbbbbbccccccdddddddeeeee' + const pkgPath = path.resolve(__dirname, '../../fixtures/git-test.tgz') + + const srv = MockRegistry.tnock(t, 'https://codeload.github.com') + srv.get('/test/test/tar.gz/111111aaaaaaaabbbbbbbbccccccdddddddeeeee') + .times(2) + .reply(200, await fs.readFile(pkgPath)) + + const { npm } = await loadMockNpm(t, { + config: { + audit: false, + yes: true, + }, + }) + try { + await npm.exec('exec', [spec]) + const exists = await fs.stat(path.join(npm.prefix, 'npm-exec-test-success')) + t.ok(exists.isFile(), 'bin ran, creating file') + } catch (err) { + t.fail(err, "shouldn't throw") + } +}) diff --git a/deps/npm/test/lib/commands/hook.js b/deps/npm/test/lib/commands/hook.js deleted file mode 100644 index 003dae647a35a2..00000000000000 --- a/deps/npm/test/lib/commands/hook.js +++ /dev/null @@ -1,640 +0,0 @@ -const t = require('tap') -const mockNpm = require('../../fixtures/mock-npm') - -const mockHook = async (t, { hookResponse, ...npmOpts } = {}) => { - const now = Date.now() - - let hookArgs = null - - const pkgTypes = { - semver: 'package', - '@npmcli': 'scope', - npm: 'owner', - } - - const libnpmhook = { - add: async (pkg, uri, secret, opts) => { - hookArgs = { pkg, uri, secret, opts } - return { id: 1, name: pkg, type: pkgTypes[pkg], endpoint: uri } - }, - ls: async opts => { - hookArgs = opts - let id = 0 - if (hookResponse) { - return hookResponse - } - - return Object.keys(pkgTypes).map(name => ({ - id: ++id, - name, - type: pkgTypes[name], - endpoint: 'https://google.com', - last_delivery: id % 2 === 0 ? now : undefined, - response_code: 200, - })) - }, - rm: async (id, opts) => { - hookArgs = { id, opts } - const pkg = Object.keys(pkgTypes)[0] - return { - id: 1, - name: pkg, - type: pkgTypes[pkg], - endpoint: 'https://google.com', - } - }, - update: async (id, uri, secret, opts) => { - hookArgs = { id, uri, secret, opts } - const pkg = Object.keys(pkgTypes)[0] - return { id, name: pkg, type: pkgTypes[pkg], endpoint: uri } - }, - } - - const mock = await mockNpm(t, { - ...npmOpts, - command: 'hook', - mocks: { - libnpmhook, - ...npmOpts.mocks, - }, - }) - - return { - ...mock, - now, - hookArgs: () => hookArgs, - } -} - -t.test('npm hook no args', async t => { - const { hook } = await mockHook(t) - await t.rejects(hook.exec([]), hook.usage, 'throws usage with no arguments') -}) - -t.test('npm hook add', async t => { - const { npm, hook, outputs, hookArgs } = await mockHook(t) - await hook.exec(['add', 'semver', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - pkg: 'semver', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'provided the correct arguments to libnpmhook' - ) - t.strictSame(outputs[0], '+ semver -> https://google.com', 'prints the correct output') -}) - -t.test('npm hook add - correct owner hook output', async t => { - const { npm, hook, outputs, hookArgs } = await mockHook(t) - await hook.exec(['add', '~npm', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - pkg: '~npm', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'provided the correct arguments to libnpmhook' - ) - t.strictSame(outputs[0], '+ ~npm -> https://google.com', 'prints the correct output') -}) - -t.test('npm hook add - correct scope hook output', async t => { - const { npm, hook, outputs, hookArgs } = await mockHook(t) - await hook.exec(['add', '@npmcli', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - pkg: '@npmcli', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'provided the correct arguments to libnpmhook' - ) - t.strictSame(outputs[0], '+ @npmcli -> https://google.com', 'prints the correct output') -}) - -t.test('npm hook add - unicode output', async t => { - const config = { - unicode: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['add', 'semver', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - pkg: 'semver', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'provided the correct arguments to libnpmhook' - ) - t.strictSame(outputs[0], '+ semver ➜ https://google.com', 'prints the correct output') -}) - -t.test('npm hook add - json output', async t => { - const config = { - json: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['add', '@npmcli', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - pkg: '@npmcli', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'provided the correct arguments to libnpmhook' - ) - t.strictSame( - JSON.parse(outputs[0]), - { - id: 1, - name: '@npmcli', - endpoint: 'https://google.com', - type: 'scope', - }, - 'prints the correct json output' - ) -}) - -t.test('npm hook add - parseable output', async t => { - const config = { - parseable: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['add', '@npmcli', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - pkg: '@npmcli', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'provided the correct arguments to libnpmhook' - ) - - t.strictSame( - outputs[0].split(/\t/), - ['id', 'name', 'type', 'endpoint'], - 'prints the correct parseable output headers' - ) - t.strictSame( - outputs[1].split(/\t/), - ['1', '@npmcli', 'scope', 'https://google.com'], - 'prints the correct parseable values' - ) -}) - -t.test('npm hook add - silent output', async t => { - const config = { loglevel: 'silent' } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['add', '@npmcli', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - pkg: '@npmcli', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'provided the correct arguments to libnpmhook' - ) - t.strictSame(outputs, [], 'printed no output') -}) - -t.test('npm hook ls', async t => { - const { npm, hook, outputs, hookArgs } = await mockHook(t) - await hook.exec(['ls']) - - t.match( - hookArgs(), - { - ...npm.flatOptions, - package: undefined, - }, - 'received the correct arguments' - ) - t.strictSame(outputs, [ - 'You have 3 hooks configured.', - 'Hook 1: semver', - 'Endpoint: https://google.com', - 'Never triggered\n', - 'Hook 2: @npmcli', - 'Endpoint: https://google.com', - 'Triggered just now, response code was "200"\n', - 'Hook 3: ~npm', - 'Endpoint: https://google.com', - 'Never triggered\n', - ]) -}) - -t.test('npm hook ls, no results', async t => { - const hookResponse = [] - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - hookResponse, - }) - - await hook.exec(['ls']) - - t.match( - hookArgs(), - { - ...npm.flatOptions, - package: undefined, - }, - 'received the correct arguments' - ) - t.strictSame(outputs, [`You don't have any hooks configured yet.`]) -}) - -t.test('npm hook ls, single result', async t => { - const hookResponse = [ - { - id: 1, - name: 'semver', - type: 'package', - endpoint: 'https://google.com', - }, - ] - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - hookResponse, - }) - - await hook.exec(['ls']) - - t.match( - hookArgs(), - { - ...npm.flatOptions, - package: undefined, - }, - 'received the correct arguments' - ) - t.strictSame(outputs, [ - 'You have 1 hook configured.', - 'Hook 1: semver', - 'Endpoint: https://google.com', - 'Never triggered\n', - ]) -}) - -t.test('npm hook ls - json output', async t => { - const config = { - json: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['ls']) - - t.match( - hookArgs(), - { - ...npm.flatOptions, - package: undefined, - }, - 'received the correct arguments' - ) - const out = JSON.parse(outputs[0]) - t.match( - out, - [ - { - id: 1, - name: 'semver', - type: 'package', - endpoint: 'https://google.com', - }, - { - id: 2, - name: 'npmcli', - type: 'scope', - endpoint: 'https://google.com', - }, - { - id: 3, - name: 'npm', - type: 'owner', - endpoint: 'https://google.com', - }, - ], - 'prints the correct output' - ) -}) - -t.test('npm hook ls - parseable output', async t => { - const config = { - parseable: true, - } - const { npm, hook, outputs, hookArgs, now } = await mockHook(t, { - config, - }) - - await hook.exec(['ls']) - - t.match( - hookArgs(), - { - ...npm.flatOptions, - package: undefined, - }, - 'received the correct arguments' - ) - t.strictSame( - outputs.map(line => line.split(/\t/)), - [ - ['id', 'name', 'type', 'endpoint', 'last_delivery', 'response_code'], - ['1', 'semver', 'package', 'https://google.com', '', '200'], - ['2', '@npmcli', 'scope', 'https://google.com', `${now}`, '200'], - ['3', 'npm', 'owner', 'https://google.com', '', '200'], - ], - 'prints the correct result' - ) -}) - -t.test('npm hook ls - silent output', async t => { - const config = { loglevel: 'silent' } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['ls']) - - t.match( - hookArgs(), - { - ...npm.flatOptions, - package: undefined, - }, - 'received the correct arguments' - ) - t.strictSame(outputs, [], 'printed no output') -}) - -t.test('npm hook rm', async t => { - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - }) - await hook.exec(['rm', '1']) - - t.match( - hookArgs(), - { - id: '1', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame(outputs[0], '- semver X https://google.com', 'printed the correct output') -}) - -t.test('npm hook rm - unicode output', async t => { - const config = { - unicode: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['rm', '1']) - - t.match( - hookArgs(), - { - id: '1', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame(outputs[0], '- semver ✘ https://google.com', 'printed the correct output') -}) - -t.test('npm hook rm - silent output', async t => { - const config = { loglevel: 'silent' } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['rm', '1']) - - t.match( - hookArgs(), - { - id: '1', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame(outputs, [], 'printed no output') -}) - -t.test('npm hook rm - json output', async t => { - const config = { - json: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['rm', '1']) - - t.match( - hookArgs(), - { - id: '1', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame( - JSON.parse(outputs[0]), - { - id: 1, - name: 'semver', - type: 'package', - endpoint: 'https://google.com', - }, - 'printed correct output' - ) -}) - -t.test('npm hook rm - parseable output', async t => { - const config = { - parseable: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['rm', '1']) - - t.match( - hookArgs(), - { - id: '1', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame( - outputs.map(line => line.split(/\t/)), - [ - ['id', 'name', 'type', 'endpoint'], - ['1', 'semver', 'package', 'https://google.com'], - ], - 'printed correct output' - ) -}) - -t.test('npm hook update', async t => { - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - }) - await hook.exec(['update', '1', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - id: '1', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame(outputs[0], '+ semver -> https://google.com', 'printed the correct output') -}) - -t.test('npm hook update - unicode', async t => { - const config = { - unicode: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['update', '1', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - id: '1', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame(outputs[0], '+ semver ➜ https://google.com', 'printed the correct output') -}) - -t.test('npm hook update - json output', async t => { - const config = { - json: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['update', '1', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - id: '1', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame( - JSON.parse(outputs[0]), - { - id: '1', - name: 'semver', - type: 'package', - endpoint: 'https://google.com', - }, - 'printed the correct output' - ) -}) - -t.test('npm hook update - parseable output', async t => { - const config = { - parseable: true, - } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['update', '1', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - id: '1', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame( - outputs.map(line => line.split(/\t/)), - [ - ['id', 'name', 'type', 'endpoint'], - ['1', 'semver', 'package', 'https://google.com'], - ], - 'printed the correct output' - ) -}) - -t.test('npm hook update - silent output', async t => { - const config = { loglevel: 'silent' } - const { npm, hook, outputs, hookArgs } = await mockHook(t, { - config, - }) - - await hook.exec(['update', '1', 'https://google.com', 'some-secret']) - - t.match( - hookArgs(), - { - id: '1', - uri: 'https://google.com', - secret: 'some-secret', - opts: npm.flatOptions, - }, - 'received the correct arguments' - ) - t.strictSame(outputs, [], 'printed no output') -}) diff --git a/deps/npm/test/lib/commands/pkg.js b/deps/npm/test/lib/commands/pkg.js index 2306fe10db0251..f4d0278b04d9b5 100644 --- a/deps/npm/test/lib/commands/pkg.js +++ b/deps/npm/test/lib/commands/pkg.js @@ -108,6 +108,26 @@ t.test('get multiple arg', async t => { ) }) +t.test('get multiple arg with only one arg existing', async t => { + const { pkg, OUTPUT } = await mockNpm(t, { + prefixDir: { + 'package.json': JSON.stringify({ + name: 'foo', + }), + }, + }) + + await pkg('get', 'name', 'version', 'dependencies') + + t.strictSame( + JSON.parse(OUTPUT()), + { + name: 'foo', + }, + 'should print retrieved package.json field' + ) +}) + t.test('get multiple arg with empty value', async t => { const { pkg, OUTPUT } = await mockNpm(t, { prefixDir: { diff --git a/deps/npm/test/lib/commands/publish.js b/deps/npm/test/lib/commands/publish.js index 4dea3e2fa06a09..10dc9b33deda45 100644 --- a/deps/npm/test/lib/commands/publish.js +++ b/deps/npm/test/lib/commands/publish.js @@ -1,12 +1,10 @@ const t = require('tap') -const { load: loadMockNpm } = require('../../fixtures/mock-npm') +const { loadNpmWithRegistry } = require('../../fixtures/mock-npm') const { cleanZlib } = require('../../fixtures/clean-snapshot') -const MockRegistry = require('@npmcli/mock-registry') const pacote = require('pacote') const Arborist = require('@npmcli/arborist') const path = require('node:path') const fs = require('node:fs') -const npa = require('npm-package-arg') const pkg = 'test-package' const token = 'test-auth-token' @@ -23,54 +21,28 @@ const pkgJson = { t.cleanSnapshot = data => cleanZlib(data) t.test('respects publishConfig.registry, runs appropriate scripts', async t => { - const { npm, joinedOutput, prefix } = await loadMockNpm(t, { + const packageJson = { + ...pkgJson, + scripts: { + prepublishOnly: 'touch scripts-prepublishonly', + prepublish: 'touch scripts-prepublish', // should NOT run this one + publish: 'touch scripts-publish', + postpublish: 'touch scripts-postpublish', + }, + publishConfig: { registry: alternateRegistry }, + } + const { npm, joinedOutput, prefix, registry } = await loadNpmWithRegistry(t, { config: { loglevel: 'silent', [`${alternateRegistry.slice(6)}/:_authToken`]: 'test-other-token', }, prefixDir: { - 'package.json': JSON.stringify({ - ...pkgJson, - scripts: { - prepublishOnly: 'touch scripts-prepublishonly', - prepublish: 'touch scripts-prepublish', // should NOT run this one - publish: 'touch scripts-publish', - postpublish: 'touch scripts-postpublish', - }, - publishConfig: { registry: alternateRegistry }, - }, null, 2), + 'package.json': JSON.stringify(packageJson, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, registry: alternateRegistry, authorization: 'test-other-token', }) - registry.nock.put(`/${pkg}`, body => { - return t.match(body, { - _id: pkg, - name: pkg, - 'dist-tags': { latest: '1.0.0' }, - access: null, - versions: { - '1.0.0': { - name: pkg, - version: '1.0.0', - _id: `${pkg}@1.0.0`, - dist: { - shasum: /\.*/, - tarball: `http:${alternateRegistry.slice(6)}/test-package/-/test-package-1.0.0.tgz`, - }, - publishConfig: { - registry: alternateRegistry, - }, - }, - }, - _attachments: { - [`${pkg}-1.0.0.tgz`]: {}, - }, - }) - }).reply(200, {}) + registry.publish(pkg, { packageJson }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') t.equal(fs.existsSync(path.join(prefix, 'scripts-prepublishonly')), true, 'ran prepublishOnly') @@ -80,115 +52,66 @@ t.test('respects publishConfig.registry, runs appropriate scripts', async t => { }) t.test('re-loads publishConfig.registry if added during script process', async t => { - const { joinedOutput, npm } = await loadMockNpm(t, { + const initPackageJson = { + ...pkgJson, + scripts: { + prepare: 'cp new.json package.json', + }, + } + const packageJson = { + ...initPackageJson, + publishConfig: { registry: alternateRegistry }, + } + const { joinedOutput, npm, registry } = await loadNpmWithRegistry(t, { config: { [`${alternateRegistry.slice(6)}/:_authToken`]: 'test-other-token', // Keep output from leaking into tap logs for readability 'foreground-scripts': false, }, prefixDir: { - 'package.json': JSON.stringify({ - ...pkgJson, - scripts: { - prepare: 'cp new.json package.json', - }, - }, null, 2), - 'new.json': JSON.stringify({ - ...pkgJson, - publishConfig: { registry: alternateRegistry }, - }), + 'package.json': JSON.stringify(initPackageJson, null, 2), + 'new.json': JSON.stringify(packageJson, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, registry: alternateRegistry, authorization: 'test-other-token', }) - registry.nock.put(`/${pkg}`, body => { - return t.match(body, { - _id: pkg, - name: pkg, - 'dist-tags': { latest: '1.0.0' }, - access: null, - versions: { - '1.0.0': { - name: pkg, - version: '1.0.0', - _id: `${pkg}@1.0.0`, - dist: { - shasum: /\.*/, - tarball: `http:${alternateRegistry.slice(6)}/test-package/-/test-package-1.0.0.tgz`, - }, - publishConfig: { - registry: alternateRegistry, - }, - }, - }, - _attachments: { - [`${pkg}-1.0.0.tgz`]: {}, - }, - }) - }).reply(200, {}) + registry.publish(pkg, { packageJson }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') }) t.test('prioritize CLI flags over publishConfig', async t => { - const publishConfig = { registry: 'http://publishconfig' } - const { joinedOutput, npm } = await loadMockNpm(t, { + const initPackageJson = { + ...pkgJson, + scripts: { + prepare: 'cp new.json package.json', + }, + } + const packageJson = { + ...initPackageJson, + publishConfig: { registry: alternateRegistry }, + } + const { joinedOutput, npm, registry } = await loadNpmWithRegistry(t, { config: { [`${alternateRegistry.slice(6)}/:_authToken`]: 'test-other-token', // Keep output from leaking into tap logs for readability 'foreground-scripts': false, }, prefixDir: { - 'package.json': JSON.stringify({ - ...pkgJson, - scripts: { - prepare: 'cp new.json package.json', - }, - }, null, 2), - 'new.json': JSON.stringify({ - ...pkgJson, - publishConfig, - }), + 'package.json': JSON.stringify(initPackageJson, null, 2), + 'new.json': JSON.stringify(packageJson, null, 2), }, argv: ['--registry', alternateRegistry], - }) - const registry = new MockRegistry({ - tap: t, - registry: alternateRegistry, + registryUrl: alternateRegistry, authorization: 'test-other-token', }) - registry.nock.put(`/${pkg}`, body => { - return t.match(body, { - _id: pkg, - name: pkg, - 'dist-tags': { latest: '1.0.0' }, - access: null, - versions: { - '1.0.0': { - name: pkg, - version: '1.0.0', - _id: `${pkg}@1.0.0`, - dist: { - shasum: /\.*/, - tarball: `http:${alternateRegistry.slice(6)}/test-package/-/test-package-1.0.0.tgz`, - }, - publishConfig, - }, - }, - _attachments: { - [`${pkg}-1.0.0.tgz`]: {}, - }, - }) - }).reply(200, {}) + registry.publish(pkg, { packageJson }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') }) t.test('json', async t => { - const { joinedOutput, npm, logs } = await loadMockNpm(t, { + const { joinedOutput, npm, logs, registry } = await loadNpmWithRegistry(t, { config: { json: true, ...auth, @@ -196,20 +119,16 @@ t.test('json', async t => { prefixDir: { 'package.json': JSON.stringify(pkgJson, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock.put(`/${pkg}`).reply(200, {}) + registry.publish(pkg) await npm.exec('publish', []) t.matchSnapshot(logs.notice) t.matchSnapshot(joinedOutput(), 'new package json') }) t.test('dry-run', async t => { - const { joinedOutput, npm, logs } = await loadMockNpm(t, { + const { joinedOutput, npm, logs, registry } = await loadNpmWithRegistry(t, { config: { 'dry-run': true, ...auth, @@ -217,14 +136,16 @@ t.test('dry-run', async t => { prefixDir: { 'package.json': JSON.stringify(pkgJson, null, 2), }, + authorization: token, }) + registry.publish(pkg, { noPut: true }) await npm.exec('publish', []) t.equal(joinedOutput(), `+ ${pkg}@1.0.0`) t.matchSnapshot(logs.notice) }) t.test('foreground-scripts defaults to true', async t => { - const { outputs, npm, logs } = await loadMockNpm(t, { + const { outputs, npm, logs, registry } = await loadNpmWithRegistry(t, { config: { 'dry-run': true, ...auth, @@ -241,11 +162,9 @@ t.test('foreground-scripts defaults to true', async t => { ), }, }) - + registry.publish('test-fg-scripts', { noPut: true }) await npm.exec('publish', []) - t.matchSnapshot(logs.notice) - t.strictSame( outputs, [ @@ -257,7 +176,7 @@ t.test('foreground-scripts defaults to true', async t => { }) t.test('foreground-scripts can still be set to false', async t => { - const { outputs, npm, logs } = await loadMockNpm(t, { + const { outputs, npm, logs, registry } = await loadNpmWithRegistry(t, { config: { 'dry-run': true, 'foreground-scripts': false, @@ -276,6 +195,7 @@ t.test('foreground-scripts can still be set to false', async t => { }, }) + registry.publish('test-fg-scripts', { noPut: true }) await npm.exec('publish', []) t.matchSnapshot(logs.notice) @@ -287,12 +207,12 @@ t.test('foreground-scripts can still be set to false', async t => { }) t.test('shows usage with wrong set of arguments', async t => { - const { publish } = await loadMockNpm(t, { command: 'publish' }) + const { publish } = await loadNpmWithRegistry(t, { command: 'publish' }) await t.rejects(publish.exec(['a', 'b', 'c']), publish.usage) }) t.test('throws when invalid tag is semver', async t => { - const { npm } = await loadMockNpm(t, { + const { npm } = await loadNpmWithRegistry(t, { config: { tag: '0.0.13', }, @@ -307,7 +227,7 @@ t.test('throws when invalid tag is semver', async t => { }) t.test('throws when invalid tag when not url encodable', async t => { - const { npm } = await loadMockNpm(t, { + const { npm } = await loadNpmWithRegistry(t, { config: { tag: '@test', }, @@ -325,7 +245,7 @@ t.test('throws when invalid tag when not url encodable', async t => { }) t.test('tarball', async t => { - const { npm, joinedOutput, logs, home } = await loadMockNpm(t, { + const { npm, joinedOutput, logs, home, registry } = await loadNpmWithRegistry(t, { config: { 'fetch-retries': 0, ...auth, @@ -338,27 +258,19 @@ t.test('tarball', async t => { }, null, 2), 'index.js': 'console.log("hello world"}', }, + authorization: token, }) const tarball = await pacote.tarball(home, { Arborist }) const tarFilename = path.join(home, 'tarball.tgz') fs.writeFileSync(tarFilename, tarball) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), - authorization: token, - }) - registry.nock.put('/test-tar-package', body => { - return t.match(body, { - name: 'test-tar-package', - }) - }).reply(200, {}) + registry.publish('test-tar-package') await npm.exec('publish', [tarFilename]) t.matchSnapshot(logs.notice) t.matchSnapshot(joinedOutput(), 'new package json') }) t.test('no auth default registry', async t => { - const { npm } = await loadMockNpm(t, { + const { npm } = await loadNpmWithRegistry(t, { prefixDir: { 'package.json': JSON.stringify(pkgJson, null, 2), }, @@ -373,7 +285,7 @@ t.test('no auth default registry', async t => { }) t.test('no auth dry-run', async t => { - const { npm, joinedOutput, logs } = await loadMockNpm(t, { + const { npm, joinedOutput, logs, registry } = await loadNpmWithRegistry(t, { config: { 'dry-run': true, }, @@ -381,13 +293,14 @@ t.test('no auth dry-run', async t => { 'package.json': JSON.stringify(pkgJson, null, 2), }, }) + registry.publish(pkg, { noPut: true }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput()) t.matchSnapshot(logs.warn, 'warns about auth being needed') }) t.test('no auth for configured registry', async t => { - const { npm } = await loadMockNpm(t, { + const { npm } = await loadNpmWithRegistry(t, { config: { registry: alternateRegistry, ...auth, @@ -406,7 +319,7 @@ t.test('no auth for configured registry', async t => { }) t.test('no auth for scope configured registry', async t => { - const { npm } = await loadMockNpm(t, { + const { npm } = await loadNpmWithRegistry(t, { config: { scope: '@npm', registry: alternateRegistry, @@ -429,8 +342,7 @@ t.test('no auth for scope configured registry', async t => { }) t.test('has token auth for scope configured registry', async t => { - const spec = npa('@npm/test-package') - const { npm, joinedOutput } = await loadMockNpm(t, { + const { npm, joinedOutput, registry } = await loadNpmWithRegistry(t, { config: { scope: '@npm', registry: alternateRegistry, @@ -442,22 +354,16 @@ t.test('has token auth for scope configured registry', async t => { version: '1.0.0', }, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, registry: alternateRegistry, authorization: 'test-scope-token', }) - registry.nock.put(`/${spec.escapedName}`, body => { - return t.match(body, { name: '@npm/test-package' }) - }).reply(200, {}) + registry.publish('@npm/test-package') await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') }) t.test('has mTLS auth for scope configured registry', async t => { - const spec = npa('@npm/test-package') - const { npm, joinedOutput } = await loadMockNpm(t, { + const { npm, joinedOutput, registry } = await loadNpmWithRegistry(t, { config: { scope: '@npm', registry: alternateRegistry, @@ -470,14 +376,9 @@ t.test('has mTLS auth for scope configured registry', async t => { version: '1.0.0', }, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, registry: alternateRegistry, }) - registry.nock.put(`/${spec.escapedName}`, body => { - return t.match(body, { name: '@npm/test-package' }) - }).reply(200, {}) + registry.publish('@npm/test-package') await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') }) @@ -519,101 +420,71 @@ t.test('workspaces', t => { } t.test('all workspaces - no color', async t => { - const { npm, joinedOutput, logs } = await loadMockNpm(t, { + const { npm, joinedOutput, logs, registry } = await loadNpmWithRegistry(t, { config: { + tag: 'latest', color: false, ...auth, workspaces: true, }, prefixDir: dir, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock - .put('/workspace-a', body => { - return t.match(body, { name: 'workspace-a' }) - }).reply(200, {}) - .put('/workspace-b', body => { - return t.match(body, { name: 'workspace-b' }) - }).reply(200, {}) - .put('/workspace-n', body => { - return t.match(body, { name: 'workspace-n' }) - }).reply(200, {}) + ;['workspace-a', 'workspace-b', 'workspace-n'].forEach(name => { + registry.publish(name) + }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'all public workspaces') t.matchSnapshot(logs.warn, 'warns about skipped private workspace') }) t.test('all workspaces - color', async t => { - const { npm, joinedOutput, logs } = await loadMockNpm(t, { + const { npm, joinedOutput, logs, registry } = await loadNpmWithRegistry(t, { config: { ...auth, + tag: 'latest', color: 'always', workspaces: true, }, prefixDir: dir, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock - .put('/workspace-a', body => { - return t.match(body, { name: 'workspace-a' }) - }).reply(200, {}) - .put('/workspace-b', body => { - return t.match(body, { name: 'workspace-b' }) - }).reply(200, {}) - .put('/workspace-n', body => { - return t.match(body, { name: 'workspace-n' }) - }).reply(200, {}) + ;['workspace-a', 'workspace-b', 'workspace-n'].forEach(name => { + registry.publish(name) + }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'all public workspaces') t.matchSnapshot(logs.warn, 'warns about skipped private workspace in color') }) t.test('one workspace - success', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { + const { npm, joinedOutput, registry } = await loadNpmWithRegistry(t, { config: { ...auth, + tag: 'latest', workspace: ['workspace-a'], }, prefixDir: dir, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock - .put('/workspace-a', body => { - return t.match(body, { name: 'workspace-a' }) - }).reply(200, {}) + ;['workspace-a'].forEach(name => { + registry.publish(name) + }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'single workspace') }) t.test('one workspace - failure', async t => { - const { npm } = await loadMockNpm(t, { + const { npm, registry } = await loadNpmWithRegistry(t, { config: { ...auth, + tag: 'latest', workspace: ['workspace-a'], }, prefixDir: dir, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock - .put('/workspace-a', body => { - return t.match(body, { name: 'workspace-a' }) - }).reply(404, {}) + registry.publish('workspace-a', { putCode: 404 }) await t.rejects(npm.exec('publish', []), { code: 'E404' }) }) @@ -639,30 +510,25 @@ t.test('workspaces', t => { }, } - const { npm, joinedOutput } = await loadMockNpm(t, { + const { npm, joinedOutput, registry } = await loadNpmWithRegistry(t, { config: { ...auth, + tag: 'latest', workspaces: true, }, prefixDir: testDir, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock - .put('/workspace-a', body => { - return t.match(body, { name: 'workspace-a' }) - }).reply(200, {}) + registry.publish('workspace-a') await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'one marked private') }) t.test('invalid workspace', async t => { - const { npm } = await loadMockNpm(t, { + const { npm } = await loadNpmWithRegistry(t, { config: { ...auth, + tag: 'latest', workspace: ['workspace-x'], }, prefixDir: dir, @@ -674,29 +540,19 @@ t.test('workspaces', t => { }) t.test('json', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { + const { npm, joinedOutput, registry } = await loadNpmWithRegistry(t, { config: { ...auth, + tag: 'latest', workspaces: true, json: true, }, prefixDir: dir, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock - .put('/workspace-a', body => { - return t.match(body, { name: 'workspace-a' }) - }).reply(200, {}) - .put('/workspace-b', body => { - return t.match(body, { name: 'workspace-b' }) - }).reply(200, {}) - .put('/workspace-n', body => { - return t.match(body, { name: 'workspace-n' }) - }).reply(200, {}) + ;['workspace-a', 'workspace-b', 'workspace-n'].forEach(name => { + registry.publish(name) + }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'all workspaces in json') }) @@ -722,22 +578,16 @@ t.test('workspaces', t => { }, } - const { npm, joinedOutput } = await loadMockNpm(t, { + const { npm, joinedOutput, registry } = await loadNpmWithRegistry(t, { config: { ...auth, + tag: 'latest', }, prefixDir: testDir, chdir: ({ prefix }) => path.resolve(prefix, './workspace-a'), - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock - .put('/pkg', body => { - return t.match(body, { name: 'pkg' }) - }).reply(200, {}) + registry.publish('pkg') await npm.exec('publish', ['../dir/pkg']) t.matchSnapshot(joinedOutput(), 'publish different package spec') }) @@ -746,7 +596,7 @@ t.test('workspaces', t => { }) t.test('ignore-scripts', async t => { - const { npm, joinedOutput, prefix } = await loadMockNpm(t, { + const { npm, joinedOutput, prefix, registry } = await loadNpmWithRegistry(t, { config: { ...auth, 'ignore-scripts': true, @@ -762,13 +612,9 @@ t.test('ignore-scripts', async t => { }, }, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock.put(`/${pkg}`).reply(200, {}) + registry.publish(pkg) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') t.equal( @@ -794,27 +640,22 @@ t.test('ignore-scripts', async t => { }) t.test('_auth config default registry', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { + const { npm, joinedOutput, registry } = await loadNpmWithRegistry(t, { config: { '//registry.npmjs.org/:_auth': basic, }, prefixDir: { 'package.json': JSON.stringify(pkgJson), }, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), basic, }) - registry.nock.put(`/${pkg}`).reply(200, {}) + registry.publish(pkg) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') }) t.test('bare _auth and registry config', async t => { - const spec = npa('@npm/test-package') - const { npm, joinedOutput } = await loadMockNpm(t, { + const { npm, joinedOutput, registry } = await loadNpmWithRegistry(t, { config: { registry: alternateRegistry, '//other.registry.npmjs.org/:_auth': basic, @@ -825,19 +666,16 @@ t.test('bare _auth and registry config', async t => { version: '1.0.0', }, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, registry: alternateRegistry, basic, }) - registry.nock.put(`/${spec.escapedName}`).reply(200, {}) + registry.publish('@npm/test-package') await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') }) t.test('bare _auth config scoped registry', async t => { - const { npm } = await loadMockNpm(t, { + const { npm } = await loadNpmWithRegistry(t, { config: { scope: '@npm', registry: alternateRegistry, @@ -857,8 +695,7 @@ t.test('bare _auth config scoped registry', async t => { }) t.test('scoped _auth config scoped registry', async t => { - const spec = npa('@npm/test-package') - const { npm, joinedOutput } = await loadMockNpm(t, { + const { npm, joinedOutput, registry } = await loadNpmWithRegistry(t, { config: { scope: '@npm', registry: alternateRegistry, @@ -870,48 +707,37 @@ t.test('scoped _auth config scoped registry', async t => { version: '1.0.0', }, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, registry: alternateRegistry, basic, }) - registry.nock.put(`/${spec.escapedName}`).reply(200, {}) + registry.publish('@npm/test-package') await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') }) t.test('restricted access', async t => { - const spec = npa('@npm/test-package') - const { npm, joinedOutput, logs } = await loadMockNpm(t, { + const packageJson = { + name: '@npm/test-package', + version: '1.0.0', + } + const { npm, joinedOutput, logs, registry } = await loadNpmWithRegistry(t, { config: { ...auth, access: 'restricted', }, prefixDir: { - 'package.json': JSON.stringify({ - name: '@npm/test-package', - version: '1.0.0', - }, null, 2), + 'package.json': JSON.stringify(packageJson, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock.put(`/${spec.escapedName}`, body => { - t.equal(body.access, 'restricted', 'access is explicitly set to restricted') - return true - }).reply(200, {}) + registry.publish('@npm/test-package', { packageJson, access: 'restricted' }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') t.matchSnapshot(logs.notice) }) t.test('public access', async t => { - const spec = npa('@npm/test-package') - const { npm, joinedOutput, logs } = await loadMockNpm(t, { + const { npm, joinedOutput, logs, registry } = await loadNpmWithRegistry(t, { config: { ...auth, access: 'public', @@ -922,16 +748,9 @@ t.test('public access', async t => { version: '1.0.0', }, null, 2), }, - }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), authorization: token, }) - registry.nock.put(`/${spec.escapedName}`, body => { - t.equal(body.access, 'public', 'access is explicitly set to public') - return true - }).reply(200, {}) + registry.publish('@npm/test-package', { access: 'public' }) await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') t.matchSnapshot(logs.notice) @@ -951,9 +770,10 @@ t.test('manifest', async t => { t.cleanSnapshot = (s) => s.replace(new RegExp(npmPkg.version, 'g'), '{VERSION}') let manifest = null - const { npm } = await loadMockNpm(t, { + const { npm, registry } = await loadNpmWithRegistry(t, { config: { ...auth, + tag: 'latest', 'foreground-scripts': false, }, chdir: () => root, @@ -963,6 +783,9 @@ t.test('manifest', async t => { }, }, }) + + registry.publish('npm', { noPut: true }) + await npm.exec('publish', []) const okKeys = [ @@ -988,3 +811,114 @@ t.test('manifest', async t => { t.matchSnapshot(manifest, 'manifest') }) + +t.test('prerelease dist tag', (t) => { + t.test('aborts when prerelease and no tag', async t => { + const { npm } = await loadNpmWithRegistry(t, { + config: { + loglevel: 'silent', + [`${alternateRegistry.slice(6)}/:_authToken`]: 'test-other-token', + }, + prefixDir: { + 'package.json': JSON.stringify({ + ...pkgJson, + version: '1.0.0-0', + publishConfig: { registry: alternateRegistry }, + }, null, 2), + }, + }) + await t.rejects(async () => { + await npm.exec('publish', []) + }, new Error('You must specify a tag using --tag when publishing a prerelease version')) + }) + + t.test('does not abort when prerelease and authored tag latest', async t => { + const packageJson = { + ...pkgJson, + version: '1.0.0-0', + publishConfig: { registry: alternateRegistry }, + } + const { npm, registry } = await loadNpmWithRegistry(t, { + config: { + loglevel: 'silent', + tag: 'latest', + [`${alternateRegistry.slice(6)}/:_authToken`]: 'test-other-token', + }, + prefixDir: { + 'package.json': JSON.stringify(packageJson, null, 2), + }, + registry: alternateRegistry, + authorization: 'test-other-token', + }) + registry.publish(pkg, { packageJson }) + await npm.exec('publish', []) + }) + + t.end() +}) + +t.test('latest dist tag', (t) => { + const init = (version) => ({ + config: { + loglevel: 'silent', + ...auth, + }, + prefixDir: { + 'package.json': JSON.stringify({ + ...pkgJson, + version, + }, null, 2), + }, + authorization: token, + }) + + const packuments = [ + // this needs more than one item in it to cover the sort logic + { version: '50.0.0' }, + { version: '100.0.0' }, + { version: '105.0.0-pre' }, + ] + + t.test('PREVENTS publish when latest version is HIGHER than publishing version', async t => { + const version = '99.0.0' + const { npm, registry } = await loadNpmWithRegistry(t, init(version)) + registry.publish(pkg, { noPut: true, packuments }) + await t.rejects(async () => { + await npm.exec('publish', []) + /* eslint-disable-next-line max-len */ + }, new Error('Cannot implicitly apply the "latest" tag because published version 100.0.0 is higher than the new version 99.0.0. You must specify a tag using --tag.')) + }) + + t.test('ALLOWS publish when latest is HIGHER than publishing version and flag', async t => { + const version = '99.0.0' + const { npm, registry } = await loadNpmWithRegistry(t, { + ...init(version), + argv: ['--tag', 'latest'], + }) + registry.publish(pkg, { packuments }) + await npm.exec('publish', []) + }) + + t.test('ALLOWS publish when latest versions are LOWER than publishing version', async t => { + const version = '101.0.0' + const { npm, registry } = await loadNpmWithRegistry(t, init(version)) + registry.publish(pkg, { packuments }) + await npm.exec('publish', []) + }) + + t.test('ALLOWS publish when packument has empty versions (for coverage)', async t => { + const version = '1.0.0' + const { npm, registry } = await loadNpmWithRegistry(t, init(version)) + registry.publish(pkg, { manifest: { versions: { } } }) + await npm.exec('publish', []) + }) + + t.test('ALLOWS publish when packument has empty manifest (for coverage)', async t => { + const version = '1.0.0' + const { npm, registry } = await loadNpmWithRegistry(t, init(version)) + registry.publish(pkg, { manifest: {} }) + await npm.exec('publish', []) + }) + + t.end() +}) diff --git a/deps/npm/test/lib/commands/view.js b/deps/npm/test/lib/commands/view.js index 5da9182ddd55ef..f25b3da005b969 100644 --- a/deps/npm/test/lib/commands/view.js +++ b/deps/npm/test/lib/commands/view.js @@ -36,10 +36,25 @@ const packument = (nv, opts) => { _id: 'blue', name: 'blue', 'dist-tags': { + v1: '1.0.0', + next: '1.0.1', + prev: '1.0.0', latest: '1.0.0', + a: '1.0.0', + c: '1.0.0', + b: '1.0.0', + d: '1.0.0', + f: '1.0.1', + g: '1.0.1', + h: '1.0.1', + e: '1.0.1', + z: '1.0.0', + x: '1.0.1', + y: '1.0.0', }, time: { '1.0.0': yesterday, + '1.0.1': '2012-12-20T00:00:00.000Z', }, versions: { '1.0.0': { From 649da3b8377e030ea7b9a1bc0308451e26e28740 Mon Sep 17 00:00:00 2001 From: Rafael Gonzaga Date: Fri, 10 Jan 2025 14:02:12 -0300 Subject: [PATCH 020/208] doc: include CVE to EOL lines as sec release process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/nodejs/security-wg/issues/1401 PR-URL: https://github.com/nodejs/node/pull/56520 Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Marco Ippolito Reviewed-By: Trivikram Kamat Reviewed-By: Ulises Gascón --- doc/contributing/security-release-process.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/contributing/security-release-process.md b/doc/contributing/security-release-process.md index 3508180e0d5687..d8a871bd96922c 100644 --- a/doc/contributing/security-release-process.md +++ b/doc/contributing/security-release-process.md @@ -65,6 +65,8 @@ The current security stewards are documented in the main Node.js * [ ] 4\. **Requesting CVEs:** * Request CVEs for the reports with `git node security --request-cve`. * Make sure to have a green CI before requesting a CVE. + * Check if there is a need to issue a CVE for any version that became + EOL after the last security release through [this issue](https://github.com/nodejs/security-wg/issues/1419). * [ ] 5\. **Choosing or Updating Release Date:** * Get agreement on the planned date for the release. From ad68d088a31c9d607d1eeeadc5cbf3aec78c31bf Mon Sep 17 00:00:00 2001 From: Colin Ihrig Date: Fri, 10 Jan 2025 19:03:08 -0500 Subject: [PATCH 021/208] doc: fix location of NO_COLOR in CLI docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'coverage output' and 'source map cache' sections were appearing under the NO_COLOR environment variable instead of the NODE_V8_COVERAGE enviroment variable where they were intended to be. This commit fixes that issue. PR-URL: https://github.com/nodejs/node/pull/56525 Reviewed-By: Antoine du Hamel Reviewed-By: Luigi Pinca Reviewed-By: Ulises Gascón Reviewed-By: James M Snell --- doc/api/cli.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index 2960055468c47a..d3c1fc6a6e6867 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -3378,11 +3378,6 @@ easier to instrument applications that call the `child_process.spawn()` family of functions. `NODE_V8_COVERAGE` can be set to an empty string, to prevent propagation. -### `NO_COLOR=` - -[`NO_COLOR`][] is an alias for `NODE_DISABLE_COLORS`. The value of the -environment variable is arbitrary. - #### Coverage output Coverage is output as an array of [ScriptCoverage][] objects on the top-level @@ -3448,6 +3443,11 @@ and the line lengths of the source file (in the key `lineLengths`). } ``` +### `NO_COLOR=` + +[`NO_COLOR`][] is an alias for `NODE_DISABLE_COLORS`. The value of the +environment variable is arbitrary. + ### `OPENSSL_CONF=file` + +> Stability: 1.2 - Release candidate + +Disable the ability of starting a debugging session by sending a +`SIGUSR1` signal to the process. + ### `--disable-warning=code-or-type` > Stability: 1.1 - Active development @@ -1458,6 +1469,7 @@ added: v7.6.0 Set the `host:port` to be used when the inspector is activated. Useful when activating the inspector by sending the `SIGUSR1` signal. +Except when [`--disable-sigusr1`][] is passed. Default host is `127.0.0.1`. If port `0` is specified, a random available port will be used. @@ -3099,6 +3111,7 @@ one is included in the list below. * `--conditions`, `-C` * `--diagnostic-dir` * `--disable-proto` +* `--disable-sigusr1` * `--disable-warning` * `--disable-wasm-trap-handler` * `--dns-result-order` @@ -3677,6 +3690,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12 [`--build-snapshot`]: #--build-snapshot [`--cpu-prof-dir`]: #--cpu-prof-dir [`--diagnostic-dir`]: #--diagnostic-dirdirectory +[`--disable-sigusr1`]: #--disable-sigusr1 [`--env-file-if-exists`]: #--env-file-if-existsconfig [`--env-file`]: #--env-fileconfig [`--experimental-addon-modules`]: #--experimental-addon-modules diff --git a/src/env-inl.h b/src/env-inl.h index 79496fab1a7528..1ea46d79787866 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -666,7 +666,8 @@ inline bool Environment::no_global_search_paths() const { } inline bool Environment::should_start_debug_signal_handler() const { - return (flags_ & EnvironmentFlags::kNoStartDebugSignalHandler) == 0; + return ((flags_ & EnvironmentFlags::kNoStartDebugSignalHandler) == 0) && + !options_->disable_sigusr1; } inline bool Environment::no_browser_globals() const { diff --git a/src/node_options.cc b/src/node_options.cc index 8d529651342ba6..21ee002d12de3b 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -386,6 +386,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { " (default: current working directory)", &EnvironmentOptions::diagnostic_dir, kAllowedInEnvvar); + AddOption("--disable-sigusr1", + "Disable inspector thread to be listening for SIGUSR1 signal", + &EnvironmentOptions::disable_sigusr1, + kAllowedInEnvvar, + false); AddOption("--dns-result-order", "set default value of verbatim in dns.lookup. Options are " "'ipv4first' (IPv4 addresses are placed before IPv6 addresses) " diff --git a/src/node_options.h b/src/node_options.h index 9563f90f41f7d8..d2738e2510605a 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -116,6 +116,7 @@ class EnvironmentOptions : public Options { bool abort_on_uncaught_exception = false; std::vector conditions; bool detect_module = true; + bool disable_sigusr1 = false; bool print_required_tla = false; bool require_module = true; std::string dns_result_order; diff --git a/test/fixtures/disable-signal/sigusr1.js b/test/fixtures/disable-signal/sigusr1.js new file mode 100644 index 00000000000000..b4deb246c8cc45 --- /dev/null +++ b/test/fixtures/disable-signal/sigusr1.js @@ -0,0 +1,2 @@ +console.log('pid is', process.pid); +setInterval(() => {}, 1000); \ No newline at end of file diff --git a/test/parallel/test-disable-sigusr1.js b/test/parallel/test-disable-sigusr1.js new file mode 100644 index 00000000000000..e1d15a25ee6505 --- /dev/null +++ b/test/parallel/test-disable-sigusr1.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const { it } = require('node:test'); +const assert = require('node:assert'); +const { NodeInstance } = require('../common/inspector-helper.js'); + +common.skipIfInspectorDisabled(); + +it('should not attach a debugger with SIGUSR1', { skip: common.isWindows }, async () => { + const file = fixtures.path('disable-signal/sigusr1.js'); + const instance = new NodeInstance(['--disable-sigusr1'], undefined, file); + + instance.on('stderr', common.mustNotCall()); + const loggedPid = await new Promise((resolve) => { + instance.on('stdout', (data) => { + const matches = data.match(/pid is (\d+)/); + if (matches) resolve(Number(matches[1])); + }); + }); + + assert.ok(process.kill(instance.pid, 'SIGUSR1')); + assert.strictEqual(loggedPid, instance.pid); + assert.ok(await instance.kill()); +}); From e799722f1a0bf43fe4d47e4824b9524363fe0d62 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Fri, 6 Dec 2024 18:24:41 +0100 Subject: [PATCH 039/208] test: make test-crypto-hash compatible with OpenSSL > 3.4.0 OpenSSL 3.4 has a breaking change where the outputLength is now mandatory for shake* hash algorithms. https://github.com/openssl/openssl/commit/b911fef216d1386210ec24e201d54d709528abb4 PR-URL: https://github.com/nodejs/node/pull/56160 Refs: https://github.com/nodejs/node/issues/56159 Reviewed-By: Antoine du Hamel Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Richard Lau --- test/parallel/test-crypto-hash.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/test/parallel/test-crypto-hash.js b/test/parallel/test-crypto-hash.js index 83218c105a4596..ca8f630b4bb7e7 100644 --- a/test/parallel/test-crypto-hash.js +++ b/test/parallel/test-crypto-hash.js @@ -7,6 +7,7 @@ const assert = require('assert'); const crypto = require('crypto'); const fs = require('fs'); +const { hasOpenSSL } = common; const fixtures = require('../common/fixtures'); let cryptoType; @@ -182,19 +183,21 @@ assert.throws( // Test XOF hash functions and the outputLength option. { - // Default outputLengths. - assert.strictEqual(crypto.createHash('shake128').digest('hex'), - '7f9c2ba4e88f827d616045507605853e'); - assert.strictEqual(crypto.createHash('shake128', null).digest('hex'), - '7f9c2ba4e88f827d616045507605853e'); - assert.strictEqual(crypto.createHash('shake256').digest('hex'), - '46b9dd2b0ba88d13233b3feb743eeb24' + - '3fcd52ea62b81b82b50c27646ed5762f'); - assert.strictEqual(crypto.createHash('shake256', { outputLength: 0 }) - .copy() // Default outputLength. - .digest('hex'), - '46b9dd2b0ba88d13233b3feb743eeb24' + - '3fcd52ea62b81b82b50c27646ed5762f'); + // Default outputLengths. Since OpenSSL 3.4 an outputLength is mandatory + if (!hasOpenSSL(3, 4)) { + assert.strictEqual(crypto.createHash('shake128').digest('hex'), + '7f9c2ba4e88f827d616045507605853e'); + assert.strictEqual(crypto.createHash('shake128', null).digest('hex'), + '7f9c2ba4e88f827d616045507605853e'); + assert.strictEqual(crypto.createHash('shake256').digest('hex'), + '46b9dd2b0ba88d13233b3feb743eeb24' + + '3fcd52ea62b81b82b50c27646ed5762f'); + assert.strictEqual(crypto.createHash('shake256', { outputLength: 0 }) + .copy() // Default outputLength. + .digest('hex'), + '46b9dd2b0ba88d13233b3feb743eeb24' + + '3fcd52ea62b81b82b50c27646ed5762f'); + } // Short outputLengths. assert.strictEqual(crypto.createHash('shake128', { outputLength: 0 }) From e6a988dbdee47b3412094a90d35d6bd8207c750d Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Fri, 6 Dec 2024 19:14:16 +0100 Subject: [PATCH 040/208] test: disable openssl 3.4.0 incompatible tests The shake128/shake256 hashing algorithms broke due to an OpenSSL 3.4 incompatible change and now throws an Error. PR-URL: https://github.com/nodejs/node/pull/56160 Refs: https://github.com/nodejs/node/issues/56159 Reviewed-By: Antoine du Hamel Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Richard Lau --- test/parallel/test-crypto-oneshot-hash.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/parallel/test-crypto-oneshot-hash.js b/test/parallel/test-crypto-oneshot-hash.js index 56b4c04a65a1c1..69051c43d9e882 100644 --- a/test/parallel/test-crypto-oneshot-hash.js +++ b/test/parallel/test-crypto-oneshot-hash.js @@ -31,6 +31,9 @@ const methods = crypto.getHashes(); const input = fs.readFileSync(fixtures.path('utf8_test_text.txt')); for (const method of methods) { + // Skip failing tests on OpenSSL 3.4.0 + if (method.startsWith('shake') && common.hasOpenSSL(3, 4)) + continue; for (const outputEncoding of ['buffer', 'hex', 'base64', undefined]) { const oldDigest = crypto.createHash(method).update(input).digest(outputEncoding || 'hex'); const digestFromBuffer = crypto.hash(method, input, outputEncoding); From 93c15bfbc79c42244b11f99ca931b5745e508612 Mon Sep 17 00:00:00 2001 From: Colin Ihrig Date: Mon, 13 Jan 2025 15:51:55 -0500 Subject: [PATCH 041/208] test: update test-child-process-bad-stdio to use node:test This commit updates test/parallel/test-child-process-bad-stdio.js to use node:test. This change prevents multiple child processes from being spawned in parallel, which can be problematic in the CI. PR-URL: https://github.com/nodejs/node/pull/56562 Reviewed-By: Jake Yuesong Li Reviewed-By: James M Snell Reviewed-By: Yagiz Nizipli --- test/parallel/test-child-process-bad-stdio.js | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/test/parallel/test-child-process-bad-stdio.js b/test/parallel/test-child-process-bad-stdio.js index 90e8ddd0215a2b..b612fc832281a6 100644 --- a/test/parallel/test-child-process-bad-stdio.js +++ b/test/parallel/test-child-process-bad-stdio.js @@ -1,21 +1,23 @@ 'use strict'; // Flags: --expose-internals const common = require('../common'); -const assert = require('assert'); -const cp = require('child_process'); if (process.argv[2] === 'child') { setTimeout(() => {}, common.platformTimeout(100)); return; } +const assert = require('node:assert'); +const cp = require('node:child_process'); +const { mock, test } = require('node:test'); +const { ChildProcess } = require('internal/child_process'); + // Monkey patch spawn() to create a child process normally, but destroy the // stdout and stderr streams. This replicates the conditions where the streams // cannot be properly created. -const ChildProcess = require('internal/child_process').ChildProcess; const original = ChildProcess.prototype.spawn; -ChildProcess.prototype.spawn = function() { +mock.method(ChildProcess.prototype, 'spawn', function() { const err = original.apply(this, arguments); this.stdout.destroy(); @@ -24,7 +26,7 @@ ChildProcess.prototype.spawn = function() { this.stderr = null; return err; -}; +}); function createChild(options, callback) { const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" "${__filename}" child`; @@ -33,32 +35,32 @@ function createChild(options, callback) { return cp.exec(cmd, options, common.mustCall(callback)); } -// Verify that normal execution of a child process is handled. -{ +test('normal execution of a child process is handled', (_, done) => { createChild({}, (err, stdout, stderr) => { assert.strictEqual(err, null); assert.strictEqual(stdout, ''); assert.strictEqual(stderr, ''); + done(); }); -} +}); -// Verify that execution with an error event is handled. -{ +test('execution with an error event is handled', (_, done) => { const error = new Error('foo'); const child = createChild({}, (err, stdout, stderr) => { assert.strictEqual(err, error); assert.strictEqual(stdout, ''); assert.strictEqual(stderr, ''); + done(); }); child.emit('error', error); -} +}); -// Verify that execution with a killed process is handled. -{ +test('execution with a killed process is handled', (_, done) => { createChild({ timeout: 1 }, (err, stdout, stderr) => { assert.strictEqual(err.killed, true); assert.strictEqual(stdout, ''); assert.strictEqual(stderr, ''); + done(); }); -} +}); From 187007a5ecdd154a4f587237473b0c14284339f7 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 13 Jan 2025 22:39:25 +0100 Subject: [PATCH 042/208] doc: document CLI way to open the nodejs/bluesky PR PR-URL: https://github.com/nodejs/node/pull/56506 Reviewed-By: James M Snell Reviewed-By: Michael Dawson Reviewed-By: Ruy Adorno --- doc/contributing/releases.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/contributing/releases.md b/doc/contributing/releases.md index b3b20b8ae5589e..40ba96da602033 100644 --- a/doc/contributing/releases.md +++ b/doc/contributing/releases.md @@ -1102,6 +1102,22 @@ The post content can be as simple as: > … > something here about notable changes +You can create the PR for the release post on nodejs/bluesky with the following: + +```bash +# Create a PR for a post: +gh workflow run create-pr.yml --repo "https://github.com/nodejs/bluesky" \ + -F prTitle='vx.x.x release announcement' \ + -F richText='Node.js vx.x.x is out. Check the blog post at https://nodejs.org/…. TL;DR is + +- New feature +- …' + +# Create a PR for a retweet: +gh workflow run create-pr.yml --repo "https://github.com/nodejs/bluesky" \ + -F prTitle='Retweet vx.x.x release announcement' -F postURL=… +``` +
      Security release From 7a9d78306b68b15e06eecbf30370c551da18bd90 Mon Sep 17 00:00:00 2001 From: Santiago Gimeno Date: Sat, 11 Jan 2025 14:17:38 +0100 Subject: [PATCH 043/208] crypto: fix checkPrime crash with large buffers Fixes: https://github.com/nodejs/node/issues/56512 PR-URL: https://github.com/nodejs/node/pull/56559 Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Joyee Cheung --- src/crypto/crypto_random.cc | 5 +++++ test/parallel/test-crypto-prime.js | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc index a6a206455b52c3..0c26834de3126c 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -176,6 +176,11 @@ Maybe CheckPrimeTraits::AdditionalConfig( ArrayBufferOrViewContents candidate(args[offset]); params->candidate = BignumPointer(candidate.data(), candidate.size()); + if (!params->candidate) { + ThrowCryptoError( + Environment::GetCurrent(args), ERR_get_error(), "BignumPointer"); + return Nothing(); + } CHECK(args[offset + 1]->IsInt32()); // Checks params->checks = args[offset + 1].As()->Value(); diff --git a/test/parallel/test-crypto-prime.js b/test/parallel/test-crypto-prime.js index 2e7edb9074d090..5ffdc1394282be 100644 --- a/test/parallel/test-crypto-prime.js +++ b/test/parallel/test-crypto-prime.js @@ -254,6 +254,19 @@ for (const checks of [-(2 ** 31), -1, 2 ** 31, 2 ** 32 - 1, 2 ** 32, 2 ** 50]) { }); } +{ + const bytes = Buffer.alloc(67108864); + bytes[0] = 0x1; + assert.throws(() => checkPrime(bytes, common.mustNotCall()), { + code: 'ERR_OSSL_BN_BIGNUM_TOO_LONG', + message: /bignum too long/ + }); + assert.throws(() => checkPrimeSync(bytes), { + code: 'ERR_OSSL_BN_BIGNUM_TOO_LONG', + message: /bignum too long/ + }); +} + assert(!checkPrimeSync(Buffer.from([0x1]))); assert(checkPrimeSync(Buffer.from([0x2]))); assert(checkPrimeSync(Buffer.from([0x3]))); From 4ef77497fb6e4eb4dfcc55a6c21f35ec707d38a5 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Mon, 13 Jan 2025 19:55:57 -0500 Subject: [PATCH 044/208] crypto: update root certificates to NSS 3.107 This is the certdata.txt[0] from NSS 3.107. This is the version of NSS that shipped in Firefox 134.0 on 2025-01-07. Certificates removed: - SecureSign RootCA11 - Entrust Root Certification Authority - G4 - Security Communication RootCA3 [0] https://raw.githubusercontent.com/nss-dev/nss/refs/tags/NSS_3_107_RTM/lib/ckfw/builtins/certdata.txt PR-URL: https://github.com/nodejs/node/pull/56566 Reviewed-By: Luigi Pinca Reviewed-By: Richard Lau Reviewed-By: James M Snell --- src/node_root_certs.h | 86 ----------- tools/certdata.txt | 343 ++++-------------------------------------- 2 files changed, 33 insertions(+), 396 deletions(-) diff --git a/src/node_root_certs.h b/src/node_root_certs.h index 2c8670be39e586..ee229fc7740627 100644 --- a/src/node_root_certs.h +++ b/src/node_root_certs.h @@ -569,27 +569,6 @@ "dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=\n" "-----END CERTIFICATE-----", -/* SecureSign RootCA11 */ -"-----BEGIN CERTIFICATE-----\n" -"MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UE\n" -"ChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJl\n" -"U2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNV\n" -"BAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRww\n" -"GgYDVQQDExNTZWN1cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" -"CgKCAQEA/XeqpRyQBTvLTJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1y\n" -"fIw/XwFndBWW4wI8h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyK\n" -"yiyhFTOVMdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9\n" -"UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V\n" -"1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsCh8U+iQIDAQABo0Iw\n" -"QDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud\n" -"EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKChOBZmLqdWHyGcBvod7bkixTgm2E5P\n" -"7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI\n" -"6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAY\n" -"ga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR\n" -"7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN\n" -"QSdJQO7e5iNEOdyhIta6A/I=\n" -"-----END CERTIFICATE-----", - /* Microsec e-Szigno Root CA 2009 */ "-----BEGIN CERTIFICATE-----\n" "MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJI\n" @@ -2310,40 +2289,6 @@ "UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEGmpv0\n" "-----END CERTIFICATE-----", -/* Entrust Root Certification Authority - G4 */ -"-----BEGIN CERTIFICATE-----\n" -"MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJ\n" -"BgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVu\n" -"dHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMu\n" -"IC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0\n" -"aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDEx\n" -"Nlowgb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9T\n" -"ZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRy\n" -"dXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg\n" -"Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOC\n" -"Ag8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJT\n" -"meH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3ET+iq4qA7ec2/a0My3dl0ELn3\n" -"9GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1\n" -"NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc\n" -"0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh64\n" -"3IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmO\n" -"eX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm\n" -"nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8dWbrAuMI\n" -"NClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mWHv0l\n" -"iqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T\n" -"AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6\n" -"sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ\n" -"9POrYs4QjbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5\n" -"ZDIBf9PD3Vht7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0g\n" -"kLpHZPt/B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI\n" -"AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS\n" -"02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m\n" -"9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLl\n" -"YsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuI\n" -"jnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh\n" -"7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw==\n" -"-----END CERTIFICATE-----", - /* Microsoft ECC Root Certificate Authority 2017 */ "-----BEGIN CERTIFICATE-----\n" "MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQG\n" @@ -3161,37 +3106,6 @@ "Nzf43TNRnXCve1XYAS59BWQOhriR\n" "-----END CERTIFICATE-----", -/* Security Communication RootCA3 */ -"-----BEGIN CERTIFICATE-----\n" -"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\n" -"16/9M51NZg==\n" -"-----END CERTIFICATE-----", - /* Security Communication ECC RootCA1 */ "-----BEGIN CERTIFICATE-----\n" "MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAkpQMSUw\n" diff --git a/tools/certdata.txt b/tools/certdata.txt index 110a814718cfd7..e0f60abcd6cf62 100644 --- a/tools/certdata.txt +++ b/tools/certdata.txt @@ -323,7 +323,10 @@ CKA_VALUE MULTILINE_OCTAL \174\136\232\166\351\131\220\305\174\203\065\021\145\121 END CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE +# For Server Distrust After: Sat Nov 30 23:59:59 2024 +CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL +\062\064\061\061\063\060\062\063\065\071\065\071\132 +END CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE # Trust for "Entrust.net Premium 2048 Secure Server CA" @@ -627,7 +630,10 @@ CKA_VALUE MULTILINE_OCTAL \036\177\132\264\074 END CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE +# For Server Distrust After: Sat Nov 30 23:59:59 2024 +CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL +\062\064\061\061\063\060\062\063\065\071\065\071\132 +END CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE # Trust for "Entrust Root Certification Authority" @@ -3808,140 +3814,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE -# -# Certificate "SecureSign RootCA11" -# -# Issuer: CN=SecureSign RootCA11,O="Japan Certification Services, Inc.",C=JP -# Serial Number: 1 (0x1) -# Subject: CN=SecureSign RootCA11,O="Japan Certification Services, Inc.",C=JP -# Not Valid Before: Wed Apr 08 04:56:47 2009 -# Not Valid After : Sun Apr 08 04:56:47 2029 -# Fingerprint (SHA-256): BF:0F:EE:FB:9E:3A:58:1A:D5:F9:E9:DB:75:89:98:57:43:D2:61:08:5C:4D:31:4F:6F:5D:72:59:AA:42:16:12 -# Fingerprint (SHA1): 3B:C4:9F:48:F8:F3:73:A0:9C:1E:BD:F8:5B:B1:C3:65:C7:D8:11:B3 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "SecureSign RootCA11" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\130\061\013\060\011\006\003\125\004\006\023\002\112\120\061 -\053\060\051\006\003\125\004\012\023\042\112\141\160\141\156\040 -\103\145\162\164\151\146\151\143\141\164\151\157\156\040\123\145 -\162\166\151\143\145\163\054\040\111\156\143\056\061\034\060\032 -\006\003\125\004\003\023\023\123\145\143\165\162\145\123\151\147 -\156\040\122\157\157\164\103\101\061\061 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\130\061\013\060\011\006\003\125\004\006\023\002\112\120\061 -\053\060\051\006\003\125\004\012\023\042\112\141\160\141\156\040 -\103\145\162\164\151\146\151\143\141\164\151\157\156\040\123\145 -\162\166\151\143\145\163\054\040\111\156\143\056\061\034\060\032 -\006\003\125\004\003\023\023\123\145\143\165\162\145\123\151\147 -\156\040\122\157\157\164\103\101\061\061 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\001 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\003\155\060\202\002\125\240\003\002\001\002\002\001\001 -\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060 -\130\061\013\060\011\006\003\125\004\006\023\002\112\120\061\053 -\060\051\006\003\125\004\012\023\042\112\141\160\141\156\040\103 -\145\162\164\151\146\151\143\141\164\151\157\156\040\123\145\162 -\166\151\143\145\163\054\040\111\156\143\056\061\034\060\032\006 -\003\125\004\003\023\023\123\145\143\165\162\145\123\151\147\156 -\040\122\157\157\164\103\101\061\061\060\036\027\015\060\071\060 -\064\060\070\060\064\065\066\064\067\132\027\015\062\071\060\064 -\060\070\060\064\065\066\064\067\132\060\130\061\013\060\011\006 -\003\125\004\006\023\002\112\120\061\053\060\051\006\003\125\004 -\012\023\042\112\141\160\141\156\040\103\145\162\164\151\146\151 -\143\141\164\151\157\156\040\123\145\162\166\151\143\145\163\054 -\040\111\156\143\056\061\034\060\032\006\003\125\004\003\023\023 -\123\145\143\165\162\145\123\151\147\156\040\122\157\157\164\103 -\101\061\061\060\202\001\042\060\015\006\011\052\206\110\206\367 -\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002 -\202\001\001\000\375\167\252\245\034\220\005\073\313\114\233\063 -\213\132\024\105\244\347\220\026\321\337\127\322\041\020\244\027 -\375\337\254\326\037\247\344\333\174\367\354\337\270\003\332\224 -\130\375\135\162\174\214\077\137\001\147\164\025\226\343\002\074 -\207\333\256\313\001\216\302\363\146\306\205\105\364\002\306\072 -\265\142\262\257\372\234\277\244\346\324\200\060\230\363\015\266 -\223\217\251\324\330\066\362\260\374\212\312\054\241\025\063\225 -\061\332\300\033\362\356\142\231\206\143\077\277\335\223\052\203 -\250\166\271\023\037\267\316\116\102\205\217\042\347\056\032\362 -\225\011\262\005\265\104\116\167\241\040\275\251\362\116\012\175 -\120\255\365\005\015\105\117\106\161\375\050\076\123\373\004\330 -\055\327\145\035\112\033\372\317\073\260\061\232\065\156\310\213 -\006\323\000\221\362\224\010\145\114\261\064\006\000\172\211\342 -\360\307\003\131\317\325\326\350\247\062\263\346\230\100\206\305 -\315\047\022\213\314\173\316\267\021\074\142\140\007\043\076\053 -\100\156\224\200\011\155\266\263\157\167\157\065\010\120\373\002 -\207\305\076\211\002\003\001\000\001\243\102\060\100\060\035\006 -\003\125\035\016\004\026\004\024\133\370\115\117\262\245\206\324 -\072\322\361\143\232\240\276\011\366\127\267\336\060\016\006\003 -\125\035\017\001\001\377\004\004\003\002\001\006\060\017\006\003 -\125\035\023\001\001\377\004\005\060\003\001\001\377\060\015\006 -\011\052\206\110\206\367\015\001\001\005\005\000\003\202\001\001 -\000\240\241\070\026\146\056\247\126\037\041\234\006\372\035\355 -\271\042\305\070\046\330\116\117\354\243\177\171\336\106\041\241 -\207\167\217\007\010\232\262\244\305\257\017\062\230\013\174\146 -\051\266\233\175\045\122\111\103\253\114\056\053\156\172\160\257 -\026\016\343\002\154\373\102\346\030\235\105\330\125\310\350\073 -\335\347\341\364\056\013\034\064\134\154\130\112\373\214\210\120 -\137\225\034\277\355\253\042\265\145\263\205\272\236\017\270\255 -\345\172\033\212\120\072\035\275\015\274\173\124\120\013\271\102 -\257\125\240\030\201\255\145\231\357\276\344\234\277\304\205\253 -\101\262\124\157\334\045\315\355\170\342\216\014\215\011\111\335 -\143\173\132\151\226\002\041\250\275\122\131\351\175\065\313\310 -\122\312\177\201\376\331\153\323\367\021\355\045\337\370\347\371 -\244\372\162\227\204\123\015\245\320\062\030\121\166\131\024\154 -\017\353\354\137\200\214\165\103\203\303\205\230\377\114\236\055 -\015\344\167\203\223\116\265\226\007\213\050\023\233\214\031\215 -\101\047\111\100\356\336\346\043\104\071\334\241\042\326\272\003 -\362 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE -CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE - -# Trust for "SecureSign RootCA11" -# Issuer: CN=SecureSign RootCA11,O="Japan Certification Services, Inc.",C=JP -# Serial Number: 1 (0x1) -# Subject: CN=SecureSign RootCA11,O="Japan Certification Services, Inc.",C=JP -# Not Valid Before: Wed Apr 08 04:56:47 2009 -# Not Valid After : Sun Apr 08 04:56:47 2029 -# Fingerprint (SHA-256): BF:0F:EE:FB:9E:3A:58:1A:D5:F9:E9:DB:75:89:98:57:43:D2:61:08:5C:4D:31:4F:6F:5D:72:59:AA:42:16:12 -# Fingerprint (SHA1): 3B:C4:9F:48:F8:F3:73:A0:9C:1E:BD:F8:5B:B1:C3:65:C7:D8:11:B3 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "SecureSign RootCA11" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\073\304\237\110\370\363\163\240\234\036\275\370\133\261\303\145 -\307\330\021\263 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\267\122\164\342\222\264\200\223\362\165\344\314\327\362\352\046 -END -CKA_ISSUER MULTILINE_OCTAL -\060\130\061\013\060\011\006\003\125\004\006\023\002\112\120\061 -\053\060\051\006\003\125\004\012\023\042\112\141\160\141\156\040 -\103\145\162\164\151\146\151\143\141\164\151\157\156\040\123\145 -\162\166\151\143\145\163\054\040\111\156\143\056\061\034\060\032 -\006\003\125\004\003\023\023\123\145\143\165\162\145\123\151\147 -\156\040\122\157\157\164\103\101\061\061 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\001 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - # # Certificate "Microsec e-Szigno Root CA 2009" # @@ -4939,7 +4811,10 @@ CKA_VALUE MULTILINE_OCTAL \007\072\027\144\265\004\265\043\041\231\012\225\073\227\174\357 END CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE +# For Server Distrust After: Sat Nov 30 23:59:59 2024 +CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL +\062\064\061\061\063\060\062\063\065\071\065\071\132 +END CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE # Trust for "AffirmTrust Commercial" @@ -5067,7 +4942,10 @@ CKA_VALUE MULTILINE_OCTAL \355\132\000\124\205\034\026\066\222\014\134\372\246\255\277\333 END CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE +# For Server Distrust After: Sat Nov 30 23:59:59 2024 +CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL +\062\064\061\061\063\060\062\063\065\071\065\071\132 +END CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE # Trust for "AffirmTrust Networking" @@ -5227,7 +5105,10 @@ CKA_VALUE MULTILINE_OCTAL \051\340\266\270\011\150\031\034\030\103 END CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE +# For Server Distrust After: Sat Nov 30 23:59:59 2024 +CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL +\062\064\061\061\063\060\062\063\065\071\065\071\132 +END CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE # Trust for "AffirmTrust Premium" @@ -5335,7 +5216,10 @@ CKA_VALUE MULTILINE_OCTAL \214\171 END CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE +# For Server Distrust After: Sat Nov 30 23:59:59 2024 +CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL +\062\064\061\061\063\060\062\063\065\071\065\071\132 +END CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE # Trust for "AffirmTrust Premium ECC" @@ -10269,7 +10153,10 @@ CKA_VALUE MULTILINE_OCTAL \105\366 END CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE +# For Server Distrust After: Sat Nov 30 23:59:59 2024 +CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL +\062\064\061\061\063\060\062\063\065\071\065\071\132 +END CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE # Trust for "Entrust Root Certification Authority - G2" @@ -10416,7 +10303,10 @@ CKA_VALUE MULTILINE_OCTAL \231\267\046\101\133\045\140\256\320\110\032\356\006 END CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE +# For Server Distrust After: Sat Nov 30 23:59:59 2024 +CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL +\062\064\061\061\063\060\062\063\065\071\065\071\132 +END CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE # Trust for "Entrust Root Certification Authority - EC1" @@ -15014,7 +14904,7 @@ CKA_SERIAL_NUMBER MULTILINE_OCTAL \002\021\000\331\265\103\177\257\251\071\017\000\000\000\000\125 \145\255\130 END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE @@ -21228,173 +21118,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE -# -# Certificate "Security Communication RootCA3" -# -# Issuer: CN=Security Communication RootCA3,O="SECOM Trust Systems CO.,LTD.",C=JP -# Serial Number:00:e1:7c:37:40:fd:1b:fe:67 -# Subject: CN=Security Communication RootCA3,O="SECOM Trust Systems CO.,LTD.",C=JP -# Not Valid Before: Thu Jun 16 06:17:16 2016 -# Not Valid After : Mon Jan 18 06:17:16 2038 -# Fingerprint (SHA-256): 24:A5:5C:2A:B0:51:44:2D:06:17:76:65:41:23:9A:4A:D0:32:D7:C5:51:75:AA:34:FF:DE:2F:BC:4F:5C:52:94 -# Fingerprint (SHA1): C3:03:C8:22:74:92:E5:61:A2:9C:5F:79:91:2B:1E:44:13:91:30:3A -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Security Communication RootCA3" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\135\061\013\060\011\006\003\125\004\006\023\002\112\120\061 -\045\060\043\006\003\125\004\012\023\034\123\105\103\117\115\040 -\124\162\165\163\164\040\123\171\163\164\145\155\163\040\103\117 -\056\054\114\124\104\056\061\047\060\045\006\003\125\004\003\023 -\036\123\145\143\165\162\151\164\171\040\103\157\155\155\165\156 -\151\143\141\164\151\157\156\040\122\157\157\164\103\101\063 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\135\061\013\060\011\006\003\125\004\006\023\002\112\120\061 -\045\060\043\006\003\125\004\012\023\034\123\105\103\117\115\040 -\124\162\165\163\164\040\123\171\163\164\145\155\163\040\103\117 -\056\054\114\124\104\056\061\047\060\045\006\003\125\004\003\023 -\036\123\145\143\165\162\151\164\171\040\103\157\155\155\165\156 -\151\143\141\164\151\157\156\040\122\157\157\164\103\101\063 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\011\000\341\174\067\100\375\033\376\147 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\005\177\060\202\003\147\240\003\002\001\002\002\011\000 -\341\174\067\100\375\033\376\147\060\015\006\011\052\206\110\206 -\367\015\001\001\014\005\000\060\135\061\013\060\011\006\003\125 -\004\006\023\002\112\120\061\045\060\043\006\003\125\004\012\023 -\034\123\105\103\117\115\040\124\162\165\163\164\040\123\171\163 -\164\145\155\163\040\103\117\056\054\114\124\104\056\061\047\060 -\045\006\003\125\004\003\023\036\123\145\143\165\162\151\164\171 -\040\103\157\155\155\165\156\151\143\141\164\151\157\156\040\122 -\157\157\164\103\101\063\060\036\027\015\061\066\060\066\061\066 -\060\066\061\067\061\066\132\027\015\063\070\060\061\061\070\060 -\066\061\067\061\066\132\060\135\061\013\060\011\006\003\125\004 -\006\023\002\112\120\061\045\060\043\006\003\125\004\012\023\034 -\123\105\103\117\115\040\124\162\165\163\164\040\123\171\163\164 -\145\155\163\040\103\117\056\054\114\124\104\056\061\047\060\045 -\006\003\125\004\003\023\036\123\145\143\165\162\151\164\171\040 -\103\157\155\155\165\156\151\143\141\164\151\157\156\040\122\157 -\157\164\103\101\063\060\202\002\042\060\015\006\011\052\206\110 -\206\367\015\001\001\001\005\000\003\202\002\017\000\060\202\002 -\012\002\202\002\001\000\343\311\162\111\367\060\336\011\174\251 -\100\201\130\323\264\072\335\272\141\017\223\120\156\151\074\065 -\302\356\133\163\220\033\147\114\041\354\137\065\273\071\076\053 -\012\140\357\273\155\053\206\373\161\242\310\254\344\126\224\371 -\311\257\261\162\324\040\254\164\322\270\025\255\121\376\205\164 -\241\271\020\376\005\200\371\122\223\263\100\075\165\020\254\300 -\226\267\247\176\166\274\343\033\122\031\316\021\037\013\004\064 -\365\330\365\151\074\167\363\144\364\015\252\205\336\340\011\120 -\004\027\226\204\267\310\212\274\115\162\374\034\273\317\363\006 -\115\371\237\144\367\176\246\146\206\065\161\310\021\200\114\301 -\161\100\130\036\276\240\163\366\374\076\120\341\340\057\046\075 -\176\134\043\265\171\160\336\372\340\321\245\326\014\101\161\173 -\367\352\214\034\210\307\354\213\365\321\057\125\226\106\174\132 -\073\130\073\373\272\330\055\265\045\332\172\116\317\104\256\041 -\246\236\230\312\040\156\174\273\210\205\133\373\300\020\142\273 -\362\371\047\107\357\321\211\071\103\304\337\336\341\101\277\124 -\163\040\227\055\154\332\363\324\007\243\346\271\330\157\256\374 -\214\031\056\323\147\147\053\225\333\130\134\265\152\002\363\270 -\203\136\264\153\276\101\176\127\011\165\104\120\125\315\132\021 -\141\041\012\141\302\251\210\375\023\274\055\211\057\315\141\340 -\225\276\312\265\173\341\173\064\147\013\037\266\014\307\174\036 -\031\123\312\247\261\112\025\040\126\024\160\075\053\202\054\017 -\235\025\035\107\200\107\377\170\231\016\061\257\157\076\217\355 -\206\151\036\173\030\210\024\262\302\374\202\063\056\234\113\055 -\373\160\073\161\252\053\173\046\047\363\032\302\334\373\027\270 -\241\352\313\240\264\256\323\224\176\172\320\253\303\354\070\055 -\021\056\210\277\324\077\255\022\073\102\254\217\002\156\175\314 -\321\137\141\276\241\274\072\152\110\352\046\125\042\026\135\137 -\015\377\047\063\237\030\003\164\212\133\122\040\107\153\105\115 -\042\167\214\125\047\360\257\036\214\311\203\042\124\267\232\320 -\117\331\316\374\331\056\034\226\050\261\002\323\003\275\045\122 -\034\064\146\117\043\253\364\167\202\226\035\321\127\060\010\021 -\005\375\127\321\331\307\002\003\001\000\001\243\102\060\100\060 -\035\006\003\125\035\016\004\026\004\024\144\024\174\374\130\162 -\026\246\012\051\064\025\157\052\313\274\374\257\250\253\060\016 -\006\003\125\035\017\001\001\377\004\004\003\002\001\006\060\017 -\006\003\125\035\023\001\001\377\004\005\060\003\001\001\377\060 -\015\006\011\052\206\110\206\367\015\001\001\014\005\000\003\202 -\002\001\000\334\002\043\010\342\357\041\072\307\015\267\046\322 -\142\223\247\245\043\162\007\040\202\140\337\030\327\124\255\151 -\045\222\236\331\024\317\231\271\122\201\317\256\154\212\073\132 -\071\310\154\001\103\302\042\155\002\360\142\315\116\143\103\300 -\024\332\364\143\360\352\364\161\356\116\207\343\161\251\364\311 -\127\345\056\137\034\171\273\043\252\207\104\127\351\275\065\115 -\101\273\113\050\243\230\262\033\331\013\027\007\345\367\352\235 -\365\166\327\277\304\266\201\130\377\310\377\144\151\142\171\255 -\156\016\037\177\356\035\151\345\267\162\161\263\376\245\001\065 -\224\124\053\300\122\155\217\125\304\311\322\270\313\312\064\010 -\121\205\240\365\274\264\027\130\352\012\134\172\275\143\306\072 -\057\377\226\111\031\204\352\147\330\004\261\141\364\000\133\112 -\267\234\161\067\031\205\171\277\201\260\307\023\016\166\161\076 -\072\200\006\256\006\026\247\215\265\302\304\313\377\100\245\134 -\215\245\311\072\355\162\201\312\134\230\074\322\064\003\167\010 -\375\360\051\131\135\041\010\307\140\277\244\161\173\270\331\036 -\202\276\011\257\145\157\050\253\277\113\265\356\076\010\107\047 -\240\017\157\017\213\077\254\225\030\363\271\016\334\147\125\156 -\142\236\106\016\321\004\170\312\162\256\166\331\245\370\262\337 -\210\011\141\213\357\044\116\321\131\077\132\324\075\311\223\074 -\053\144\365\201\015\026\226\367\222\303\376\061\157\350\052\062 -\164\016\364\114\230\112\030\016\060\124\325\305\353\274\305\025 -\236\350\231\041\353\047\053\011\012\333\361\346\160\030\126\273 -\014\344\276\371\350\020\244\023\222\270\034\340\333\147\035\123 -\003\244\042\247\334\135\222\020\074\352\377\374\033\020\032\303 -\330\320\234\235\145\313\320\053\047\061\003\036\066\341\075\166 -\165\014\377\105\046\271\335\121\274\043\307\137\330\330\207\020 -\100\022\015\075\070\067\347\104\074\030\300\123\011\144\217\377 -\325\232\246\174\160\056\163\125\041\350\337\377\203\271\035\076 -\062\036\326\246\175\054\361\146\351\134\035\247\243\316\136\045 -\062\053\343\225\254\052\007\316\264\050\170\206\074\055\246\235 -\115\322\164\060\335\144\121\025\333\203\203\121\327\257\375\063 -\235\115\146 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE -CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE -CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE - -# Trust for "Security Communication RootCA3" -# Issuer: CN=Security Communication RootCA3,O="SECOM Trust Systems CO.,LTD.",C=JP -# Serial Number:00:e1:7c:37:40:fd:1b:fe:67 -# Subject: CN=Security Communication RootCA3,O="SECOM Trust Systems CO.,LTD.",C=JP -# Not Valid Before: Thu Jun 16 06:17:16 2016 -# Not Valid After : Mon Jan 18 06:17:16 2038 -# Fingerprint (SHA-256): 24:A5:5C:2A:B0:51:44:2D:06:17:76:65:41:23:9A:4A:D0:32:D7:C5:51:75:AA:34:FF:DE:2F:BC:4F:5C:52:94 -# Fingerprint (SHA1): C3:03:C8:22:74:92:E5:61:A2:9C:5F:79:91:2B:1E:44:13:91:30:3A -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Security Communication RootCA3" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\303\003\310\042\164\222\345\141\242\234\137\171\221\053\036\104 -\023\221\060\072 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\034\232\026\377\236\134\340\115\212\024\001\364\065\135\051\046 -END -CKA_ISSUER MULTILINE_OCTAL -\060\135\061\013\060\011\006\003\125\004\006\023\002\112\120\061 -\045\060\043\006\003\125\004\012\023\034\123\105\103\117\115\040 -\124\162\165\163\164\040\123\171\163\164\145\155\163\040\103\117 -\056\054\114\124\104\056\061\047\060\045\006\003\125\004\003\023 -\036\123\145\143\165\162\151\164\171\040\103\157\155\155\165\156 -\151\143\141\164\151\157\156\040\122\157\157\164\103\101\063 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\011\000\341\174\067\100\375\033\376\147 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - # # Certificate "Security Communication ECC RootCA1" # From f537efd1dda870baac652321445ce5944b7e09c5 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Mon, 13 Jan 2025 19:56:11 -0500 Subject: [PATCH 045/208] deps: update simdutf to 6.0.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56567 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Ulises Gascón --- deps/simdutf/simdutf.cpp | 495 +++++++++++++++--- deps/simdutf/simdutf.h | 1044 +++++++++++++++++++++++++++++++++++--- 2 files changed, 1403 insertions(+), 136 deletions(-) diff --git a/deps/simdutf/simdutf.cpp b/deps/simdutf/simdutf.cpp index 12a2f494e0a7aa..21962c3bad378d 100644 --- a/deps/simdutf/simdutf.cpp +++ b/deps/simdutf/simdutf.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2024-12-26 12:42:33 -0500. Do not edit! */ +/* auto-generated on 2025-01-08 17:51:07 -0500. Do not edit! */ /* begin file src/simdutf.cpp */ #include "simdutf.h" // We include base64_tables once. @@ -17142,8 +17142,33 @@ size_t convert_masked_utf8_to_utf16(const char *input, for (int k = 0; k < 6; k++) { utf16_output[k] = buffer[k]; } // the loop might compiler to a couple of instructions. - utf16_output += 6; // We wrote 3 32-bit surrogate pairs. - return 12; // We consumed 12 bytes. + // We need some validation. See + // https://github.com/simdutf/simdutf/pull/631 +#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO + uint8x16_t expected_mask = simdutf_make_uint8x16_t( + 0xf8, 0xc0, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, + 0xc0, 0x0, 0x0, 0x0, 0x0); +#else + uint8x16_t expected_mask = {0xf8, 0xc0, 0xc0, 0xc0, 0xf8, 0xc0, + 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xc0, + 0x0, 0x0, 0x0, 0x0}; +#endif +#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO + uint8x16_t expected = simdutf_make_uint8x16_t( + 0xf0, 0x80, 0x80, 0x80, 0xf0, 0x80, 0x80, 0x80, 0xf0, 0x80, 0x80, + 0x80, 0x0, 0x0, 0x0, 0x0); +#else + uint8x16_t expected = {0xf0, 0x80, 0x80, 0x80, 0xf0, 0x80, 0x80, 0x80, + 0xf0, 0x80, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0}; +#endif + uint8x16_t check = vceqq_u8(vandq_u8(in, expected_mask), expected); + bool correct = (vminvq_u32(vreinterpretq_u32_u8(check)) == 0xFFFFFFFF); + // The validation is just three instructions and it is not on a critical + // path. + if (correct) { + utf16_output += 6; // We wrote 3 32-bit surrogate pairs. + } + return 12; // We consumed 12 bytes. } // 3 1-4 byte sequences uint8x16_t sh = vld1q_u8(reinterpret_cast( @@ -18634,6 +18659,12 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, } if (srclen == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; @@ -22881,6 +22912,12 @@ simdutf_warn_unused result implementation::base64_to_binary( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation}; } return {SUCCESS, 0}; @@ -22926,6 +22963,12 @@ simdutf_warn_unused full_result implementation::base64_to_binary_details( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; @@ -22977,6 +23020,12 @@ simdutf_warn_unused result implementation::base64_to_binary( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation}; } return {SUCCESS, 0}; @@ -23022,6 +23071,12 @@ simdutf_warn_unused full_result implementation::base64_to_binary_details( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; @@ -23058,6 +23113,8 @@ size_t implementation::binary_to_base64(const char *input, size_t length, #endif #if SIMDUTF_IMPLEMENTATION_ICELAKE /* begin file src/icelake/implementation.cpp */ +#include +#include /* begin file src/simdutf/icelake/begin.h */ @@ -26106,17 +26163,17 @@ bool validate_ascii(const char *buf, size_t len) { /* begin file src/icelake/icelake_utf32_validation.inl.cpp */ // file included directly -const char32_t *validate_utf32(const char32_t *buf, size_t len) { - if (len < 16) { - return buf; +bool validate_utf32(const char32_t *buf, size_t len) { + if (len == 0) { + return true; } - const char32_t *end = buf + len - 16; + const char32_t *end = buf + len; const __m512i offset = _mm512_set1_epi32((uint32_t)0xffff2000); __m512i currentmax = _mm512_setzero_si512(); __m512i currentoffsetmax = _mm512_setzero_si512(); - while (buf <= end) { + while (buf < end - 16) { __m512i utf32 = _mm512_loadu_si512((const __m512i *)buf); buf += 16; currentoffsetmax = @@ -26124,20 +26181,26 @@ const char32_t *validate_utf32(const char32_t *buf, size_t len) { currentmax = _mm512_max_epu32(utf32, currentmax); } + __m512i utf32 = + _mm512_maskz_loadu_epi32(__mmask16((1 << (end - buf)) - 1), buf); + currentoffsetmax = + _mm512_max_epu32(_mm512_add_epi32(utf32, offset), currentoffsetmax); + currentmax = _mm512_max_epu32(utf32, currentmax); + const __m512i standardmax = _mm512_set1_epi32((uint32_t)0x10ffff); const __m512i standardoffsetmax = _mm512_set1_epi32((uint32_t)0xfffff7ff); __m512i is_zero = _mm512_xor_si512(_mm512_max_epu32(currentmax, standardmax), standardmax); if (_mm512_test_epi8_mask(is_zero, is_zero) != 0) { - return nullptr; + return false; } is_zero = _mm512_xor_si512( _mm512_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); if (_mm512_test_epi8_mask(is_zero, is_zero) != 0) { - return nullptr; + return false; } - return buf; + return true; } /* end file src/icelake/icelake_utf32_validation.inl.cpp */ /* begin file src/icelake/icelake_convert_latin1_to_utf8.inl.cpp */ @@ -26556,6 +26619,12 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, } if (srclen == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; @@ -26753,24 +26822,76 @@ implementation::detect_encodings(const char *input, size_t length) const noexcept { // If there is a BOM, then we trust it. auto bom_encoding = simdutf::BOM::check_bom(input, length); - // todo: convert to a one-pass algorithm if (bom_encoding != encoding_type::unspecified) { return bom_encoding; } + int out = 0; - if (validate_utf8(input, length)) { + uint32_t utf16_err = (length % 2); + uint32_t utf32_err = (length % 4); + uint32_t ends_with_high = 0; + avx512_utf8_checker checker{}; + const __m512i offset = _mm512_set1_epi32((uint32_t)0xffff2000); + __m512i currentmax = _mm512_setzero_si512(); + __m512i currentoffsetmax = _mm512_setzero_si512(); + const char *ptr = input; + const char *end = ptr + length; + for (; end - ptr >= 64; ptr += 64) { + // utf8 checks + const __m512i data = _mm512_loadu_si512((const __m512i *)ptr); + checker.check_next_input(data); + + // utf16le_checks + __m512i diff = _mm512_sub_epi16(data, _mm512_set1_epi16(uint16_t(0xD800))); + __mmask32 surrogates = + _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); + __mmask32 highsurrogates = + _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); + __mmask32 lowsurrogates = surrogates ^ highsurrogates; + utf16_err |= (((highsurrogates << 1) | ends_with_high) != lowsurrogates); + ends_with_high = ((highsurrogates & 0x80000000) != 0); + + // utf32le checks + currentoffsetmax = + _mm512_max_epu32(_mm512_add_epi32(data, offset), currentoffsetmax); + currentmax = _mm512_max_epu32(data, currentmax); + } + + // last block with 0 <= len < 64 + __mmask64 read_mask = (__mmask64(1) << (end - ptr)) - 1; + const __m512i data = _mm512_maskz_loadu_epi8(read_mask, (const __m512i *)ptr); + checker.check_next_input(data); + + __m512i diff = _mm512_sub_epi16(data, _mm512_set1_epi16(uint16_t(0xD800))); + __mmask32 surrogates = + _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800))); + __mmask32 highsurrogates = + _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400))); + __mmask32 lowsurrogates = surrogates ^ highsurrogates; + utf16_err |= (((highsurrogates << 1) | ends_with_high) != lowsurrogates); + + currentoffsetmax = + _mm512_max_epu32(_mm512_add_epi32(data, offset), currentoffsetmax); + currentmax = _mm512_max_epu32(data, currentmax); + + const __m512i standardmax = _mm512_set1_epi32((uint32_t)0x10ffff); + const __m512i standardoffsetmax = _mm512_set1_epi32((uint32_t)0xfffff7ff); + __m512i is_zero = + _mm512_xor_si512(_mm512_max_epu32(currentmax, standardmax), standardmax); + utf32_err |= (_mm512_test_epi8_mask(is_zero, is_zero) != 0); + is_zero = _mm512_xor_si512( + _mm512_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); + utf32_err |= (_mm512_test_epi8_mask(is_zero, is_zero) != 0); + checker.check_eof(); + bool is_valid_utf8 = !checker.errors(); + if (is_valid_utf8) { out |= encoding_type::UTF8; } - if ((length % 2) == 0) { - if (validate_utf16le(reinterpret_cast(input), - length / 2)) { - out |= encoding_type::UTF16_LE; - } + if (utf16_err == 0) { + out |= encoding_type::UTF16_LE; } - if ((length % 4) == 0) { - if (validate_utf32(reinterpret_cast(input), length / 4)) { - out |= encoding_type::UTF32_LE; - } + if (utf32_err == 0) { + out |= encoding_type::UTF32_LE; } return out; } @@ -27092,14 +27213,7 @@ simdutf_warn_unused result implementation::validate_utf16be_with_errors( simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept { - const char32_t *tail = icelake::validate_utf32(buf, len); - if (tail) { - return scalar::utf32::validate(tail, len - (tail - buf)); - } else { - // we come here if there was an error, or buf was nullptr which may happen - // for empty input. - return len == 0; - } + return icelake::validate_utf32(buf, len); } simdutf_warn_unused result implementation::validate_utf32_with_errors( @@ -27980,16 +28094,7 @@ implementation::count_utf8(const char *input, size_t length) const noexcept { } } - __m256i first_half = _mm512_extracti64x4_epi64(unrolled_popcount, 0); - __m256i second_half = _mm512_extracti64x4_epi64(unrolled_popcount, 1); - answer -= (size_t)_mm256_extract_epi64(first_half, 0) + - (size_t)_mm256_extract_epi64(first_half, 1) + - (size_t)_mm256_extract_epi64(first_half, 2) + - (size_t)_mm256_extract_epi64(first_half, 3) + - (size_t)_mm256_extract_epi64(second_half, 0) + - (size_t)_mm256_extract_epi64(second_half, 1) + - (size_t)_mm256_extract_epi64(second_half, 2) + - (size_t)_mm256_extract_epi64(second_half, 3); + answer -= _mm512_reduce_add_epi64(unrolled_popcount); return answer + scalar::utf8::count_code_points( reinterpret_cast(str + i), length - i); @@ -28175,16 +28280,7 @@ simdutf_warn_unused size_t implementation::utf8_length_from_latin1( eight_64bits, _mm512_sad_epu8(runner, _mm512_setzero_si512())); } - __m256i first_half = _mm512_extracti64x4_epi64(eight_64bits, 0); - __m256i second_half = _mm512_extracti64x4_epi64(eight_64bits, 1); - answer += (size_t)_mm256_extract_epi64(first_half, 0) + - (size_t)_mm256_extract_epi64(first_half, 1) + - (size_t)_mm256_extract_epi64(first_half, 2) + - (size_t)_mm256_extract_epi64(first_half, 3) + - (size_t)_mm256_extract_epi64(second_half, 0) + - (size_t)_mm256_extract_epi64(second_half, 1) + - (size_t)_mm256_extract_epi64(second_half, 2) + - (size_t)_mm256_extract_epi64(second_half, 3); + answer += _mm512_reduce_add_epi64(eight_64bits); } else if (answer > 0) { for (; i + sizeof(__m512i) <= length; i += sizeof(__m512i)) { __m512i latin = _mm512_loadu_si512((const __m512i *)(str + i)); @@ -31471,6 +31567,12 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, } if (srclen == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; @@ -33426,20 +33528,103 @@ implementation::detect_encodings(const char *input, if (bom_encoding != encoding_type::unspecified) { return bom_encoding; } + int out = 0; - if (validate_utf8(input, length)) { + uint32_t utf16_err = (length % 2); + uint32_t utf32_err = (length % 4); + uint32_t ends_with_high = 0; + const auto v_d8 = simd8::splat(0xd8); + const auto v_f8 = simd8::splat(0xf8); + const auto v_fc = simd8::splat(0xfc); + const auto v_dc = simd8::splat(0xdc); + const __m256i standardmax = _mm256_set1_epi32(0x10ffff); + const __m256i offset = _mm256_set1_epi32(0xffff2000); + const __m256i standardoffsetmax = _mm256_set1_epi32(0xfffff7ff); + __m256i currentmax = _mm256_setzero_si256(); + __m256i currentoffsetmax = _mm256_setzero_si256(); + + utf8_checker c{}; + buf_block_reader<64> reader(reinterpret_cast(input), length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + // utf8 checks + c.check_next_input(in); + + // utf16le checks + auto in0 = simd16(in.chunks[0]); + auto in1 = simd16(in.chunks[1]); + const auto t0 = in0.shr<8>(); + const auto t1 = in1.shr<8>(); + const auto in2 = simd16::pack(t0, t1); + const auto surrogates_wordmask = (in2 & v_f8) == v_d8; + const uint32_t surrogates_bitmask = surrogates_wordmask.to_bitmask(); + const auto vL = (in2 & v_fc) == v_dc; + const uint32_t L = vL.to_bitmask(); + const uint32_t H = L ^ surrogates_bitmask; + utf16_err |= (((H << 1) | ends_with_high) != L); + ends_with_high = (H & 0x80000000) != 0; + + // utf32le checks + currentmax = _mm256_max_epu32(in.chunks[0], currentmax); + currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in.chunks[0], offset), + currentoffsetmax); + currentmax = _mm256_max_epu32(in.chunks[1], currentmax); + currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in.chunks[1], offset), + currentoffsetmax); + + reader.advance(); + } + + uint8_t block[64]{}; + size_t idx = reader.block_index(); + std::memcpy(block, &input[idx], length - idx); + simd::simd8x64 in(block); + c.check_next_input(in); + + // utf16le last block check + auto in0 = simd16(in.chunks[0]); + auto in1 = simd16(in.chunks[1]); + const auto t0 = in0.shr<8>(); + const auto t1 = in1.shr<8>(); + const auto in2 = simd16::pack(t0, t1); + const auto surrogates_wordmask = (in2 & v_f8) == v_d8; + const uint32_t surrogates_bitmask = surrogates_wordmask.to_bitmask(); + const auto vL = (in2 & v_fc) == v_dc; + const uint32_t L = vL.to_bitmask(); + const uint32_t H = L ^ surrogates_bitmask; + utf16_err |= (((H << 1) | ends_with_high) != L); + // this is required to check for last byte ending in high and end of input + // is reached + ends_with_high = (H & 0x80000000) != 0; + utf16_err |= ends_with_high; + + // utf32le last block check + currentmax = _mm256_max_epu32(in.chunks[0], currentmax); + currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in.chunks[0], offset), + currentoffsetmax); + currentmax = _mm256_max_epu32(in.chunks[1], currentmax); + currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in.chunks[1], offset), + currentoffsetmax); + + reader.advance(); + + c.check_eof(); + bool is_valid_utf8 = !c.errors(); + __m256i is_zero = + _mm256_xor_si256(_mm256_max_epu32(currentmax, standardmax), standardmax); + utf32_err |= (_mm256_testz_si256(is_zero, is_zero) == 0); + + is_zero = _mm256_xor_si256( + _mm256_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax); + utf32_err |= (_mm256_testz_si256(is_zero, is_zero) == 0); + if (is_valid_utf8) { out |= encoding_type::UTF8; } - if ((length % 2) == 0) { - if (validate_utf16le(reinterpret_cast(input), - length / 2)) { - out |= encoding_type::UTF16_LE; - } + if (utf16_err == 0) { + out |= encoding_type::UTF16_LE; } - if ((length % 4) == 0) { - if (validate_utf32(reinterpret_cast(input), length / 4)) { - out |= encoding_type::UTF32_LE; - } + if (utf32_err == 0) { + out |= encoding_type::UTF32_LE; } return out; } @@ -36317,6 +36502,12 @@ simdutf_warn_unused result implementation::base64_to_binary( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation}; } return {SUCCESS, 0}; @@ -36368,6 +36559,12 @@ simdutf_warn_unused result implementation::base64_to_binary( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation}; } return {SUCCESS, 0}; @@ -38120,6 +38317,12 @@ simdutf_warn_unused result implementation::base64_to_binary( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation}; } return {SUCCESS, 0}; @@ -38165,6 +38368,12 @@ simdutf_warn_unused full_result implementation::base64_to_binary_details( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; @@ -38216,6 +38425,12 @@ simdutf_warn_unused result implementation::base64_to_binary( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation}; } return {SUCCESS, 0}; @@ -38261,6 +38476,12 @@ simdutf_warn_unused full_result implementation::base64_to_binary_details( } if (length == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; @@ -41328,6 +41549,12 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, } if (srclen == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; @@ -43282,24 +43509,138 @@ implementation::detect_encodings(const char *input, size_t length) const noexcept { // If there is a BOM, then we trust it. auto bom_encoding = simdutf::BOM::check_bom(input, length); - // todo: reimplement as a one-pass algorithm. if (bom_encoding != encoding_type::unspecified) { return bom_encoding; } + int out = 0; - if (validate_utf8(input, length)) { + uint32_t utf16_err = (length % 2); + uint32_t utf32_err = (length % 4); + uint32_t ends_with_high = 0; + const auto v_d8 = simd8::splat(0xd8); + const auto v_f8 = simd8::splat(0xf8); + const auto v_fc = simd8::splat(0xfc); + const auto v_dc = simd8::splat(0xdc); + const __m128i standardmax = _mm_set1_epi32(0x10ffff); + const __m128i offset = _mm_set1_epi32(0xffff2000); + const __m128i standardoffsetmax = _mm_set1_epi32(0xfffff7ff); + __m128i currentmax = _mm_setzero_si128(); + __m128i currentoffsetmax = _mm_setzero_si128(); + + utf8_checker c{}; + buf_block_reader<64> reader(reinterpret_cast(input), length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + // utf8 checks + c.check_next_input(in); + + // utf16le checks + auto in0 = simd16(in.chunks[0]); + auto in1 = simd16(in.chunks[1]); + const auto t0 = in0.shr<8>(); + const auto t1 = in1.shr<8>(); + const auto packed1 = simd16::pack(t0, t1); + auto in2 = simd16(in.chunks[2]); + auto in3 = simd16(in.chunks[3]); + const auto t2 = in2.shr<8>(); + const auto t3 = in3.shr<8>(); + const auto packed2 = simd16::pack(t2, t3); + + const auto surrogates_wordmask_lo = (packed1 & v_f8) == v_d8; + const auto surrogates_wordmask_hi = (packed2 & v_f8) == v_d8; + const uint32_t surrogates_bitmask = + (surrogates_wordmask_hi.to_bitmask() << 16) | + surrogates_wordmask_lo.to_bitmask(); + const auto vL_lo = (packed1 & v_fc) == v_dc; + const auto vL_hi = (packed2 & v_fc) == v_dc; + const uint32_t L = (vL_hi.to_bitmask() << 16) | vL_lo.to_bitmask(); + const uint32_t H = L ^ surrogates_bitmask; + utf16_err |= (((H << 1) | ends_with_high) != L); + ends_with_high = (H & 0x80000000) != 0; + + // utf32le checks + currentmax = _mm_max_epu32(in.chunks[0], currentmax); + currentoffsetmax = + _mm_max_epu32(_mm_add_epi32(in.chunks[0], offset), currentoffsetmax); + currentmax = _mm_max_epu32(in.chunks[1], currentmax); + currentoffsetmax = + _mm_max_epu32(_mm_add_epi32(in.chunks[1], offset), currentoffsetmax); + currentmax = _mm_max_epu32(in.chunks[2], currentmax); + currentoffsetmax = + _mm_max_epu32(_mm_add_epi32(in.chunks[2], offset), currentoffsetmax); + currentmax = _mm_max_epu32(in.chunks[3], currentmax); + currentoffsetmax = + _mm_max_epu32(_mm_add_epi32(in.chunks[3], offset), currentoffsetmax); + + reader.advance(); + } + + uint8_t block[64]{}; + size_t idx = reader.block_index(); + std::memcpy(block, &input[idx], length - idx); + simd::simd8x64 in(block); + c.check_next_input(in); + + // utf16le last block check + auto in0 = simd16(in.chunks[0]); + auto in1 = simd16(in.chunks[1]); + const auto t0 = in0.shr<8>(); + const auto t1 = in1.shr<8>(); + const auto packed1 = simd16::pack(t0, t1); + auto in2 = simd16(in.chunks[2]); + auto in3 = simd16(in.chunks[3]); + const auto t2 = in2.shr<8>(); + const auto t3 = in3.shr<8>(); + const auto packed2 = simd16::pack(t2, t3); + + const auto surrogates_wordmask_lo = (packed1 & v_f8) == v_d8; + const auto surrogates_wordmask_hi = (packed2 & v_f8) == v_d8; + const uint32_t surrogates_bitmask = + (surrogates_wordmask_hi.to_bitmask() << 16) | + surrogates_wordmask_lo.to_bitmask(); + const auto vL_lo = (packed1 & v_fc) == v_dc; + const auto vL_hi = (packed2 & v_fc) == v_dc; + const uint32_t L = (vL_hi.to_bitmask() << 16) | vL_lo.to_bitmask(); + const uint32_t H = L ^ surrogates_bitmask; + utf16_err |= (((H << 1) | ends_with_high) != L); + // this is required to check for last byte ending in high and end of input + // is reached + ends_with_high = (H & 0x80000000) != 0; + utf16_err |= ends_with_high; + + // utf32le last block check + currentmax = _mm_max_epu32(in.chunks[0], currentmax); + currentoffsetmax = + _mm_max_epu32(_mm_add_epi32(in.chunks[0], offset), currentoffsetmax); + currentmax = _mm_max_epu32(in.chunks[1], currentmax); + currentoffsetmax = + _mm_max_epu32(_mm_add_epi32(in.chunks[1], offset), currentoffsetmax); + currentmax = _mm_max_epu32(in.chunks[2], currentmax); + currentoffsetmax = + _mm_max_epu32(_mm_add_epi32(in.chunks[2], offset), currentoffsetmax); + currentmax = _mm_max_epu32(in.chunks[3], currentmax); + currentoffsetmax = + _mm_max_epu32(_mm_add_epi32(in.chunks[3], offset), currentoffsetmax); + + reader.advance(); + + c.check_eof(); + bool is_valid_utf8 = !c.errors(); + __m128i is_zero = + _mm_xor_si128(_mm_max_epu32(currentmax, standardmax), standardmax); + utf32_err |= (_mm_test_all_zeros(is_zero, is_zero) == 0); + + is_zero = _mm_xor_si128(_mm_max_epu32(currentoffsetmax, standardoffsetmax), + standardoffsetmax); + utf32_err |= (_mm_test_all_zeros(is_zero, is_zero) == 0); + if (is_valid_utf8) { out |= encoding_type::UTF8; } - if ((length % 2) == 0) { - if (validate_utf16le(reinterpret_cast(input), - length / 2)) { - out |= encoding_type::UTF16_LE; - } + if (utf16_err == 0) { + out |= encoding_type::UTF16_LE; } - if ((length % 4) == 0) { - if (validate_utf32(reinterpret_cast(input), length / 4)) { - out |= encoding_type::UTF32_LE; - } + if (utf32_err == 0) { + out |= encoding_type::UTF32_LE; } return out; } @@ -47336,6 +47677,12 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, } if (srclen == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; @@ -53668,6 +54015,12 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, } if (srclen == 0) { if (!ignore_garbage && equalsigns > 0) { + if (last_chunk_options == last_chunk_handling_options::strict) { + return {BASE64_INPUT_REMAINDER, 0, 0}; + } else if (last_chunk_options == + last_chunk_handling_options::stop_before_partial) { + return {SUCCESS, 0, 0}; + } return {INVALID_BASE64_CHARACTER, equallocation, 0}; } return {SUCCESS, 0, 0}; diff --git a/deps/simdutf/simdutf.h b/deps/simdutf/simdutf.h index 9a4b4580da91a1..4bec0cf300292a 100644 --- a/deps/simdutf/simdutf.h +++ b/deps/simdutf/simdutf.h @@ -1,4 +1,4 @@ -/* auto-generated on 2024-12-26 12:42:33 -0500. Do not edit! */ +/* auto-generated on 2025-01-08 17:51:07 -0500. Do not edit! */ /* begin file include/simdutf.h */ #ifndef SIMDUTF_H #define SIMDUTF_H @@ -55,21 +55,35 @@ #ifndef SIMDUTF_COMMON_DEFS_H #define SIMDUTF_COMMON_DEFS_H -#include /* begin file include/simdutf/portability.h */ #ifndef SIMDUTF_PORTABILITY_H #define SIMDUTF_PORTABILITY_H + +#include #include #include #include -#include -#include #ifndef _WIN32 // strcasecmp, strncasecmp #include #endif +#if defined(__apple_build_version__) + #if __apple_build_version__ < 14000000 + #define SIMDUTF_SPAN_DISABLED \ + 1 // apple-clang/13 doesn't support std::convertible_to + #endif +#endif + +#if SIMDUTF_CPLUSPLUS20 + #include + #if __cpp_concepts >= 201907L && __cpp_lib_span >= 202002L && \ + !defined(SIMDUTF_SPAN_DISABLED) + #define SIMDUTF_SPAN 1 + #endif +#endif + /** * We want to check that it is actually a little endian system at * compile-time. @@ -291,27 +305,6 @@ #define simdutf_strncasecmp strncasecmp #endif -#ifdef NDEBUG - - #ifdef SIMDUTF_VISUAL_STUDIO - #define SIMDUTF_UNREACHABLE() __assume(0) - #define SIMDUTF_ASSUME(COND) __assume(COND) - #else - #define SIMDUTF_UNREACHABLE() __builtin_unreachable(); - #define SIMDUTF_ASSUME(COND) \ - do { \ - if (!(COND)) \ - __builtin_unreachable(); \ - } while (0) - #endif - -#else // NDEBUG - - #define SIMDUTF_UNREACHABLE() assert(0); - #define SIMDUTF_ASSUME(COND) assert(COND) - -#endif - #if defined(__GNUC__) && !defined(__clang__) #if __GNUC__ >= 11 #define SIMDUTF_GCC11ORMORE 1 @@ -402,27 +395,6 @@ #endif // SIMDUTF_AVX512_H_ /* end file include/simdutf/avx512.h */ -#if defined(__GNUC__) - // Marks a block with a name so that MCA analysis can see it. - #define SIMDUTF_BEGIN_DEBUG_BLOCK(name) \ - __asm volatile("# LLVM-MCA-BEGIN " #name); - #define SIMDUTF_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name); - #define SIMDUTF_DEBUG_BLOCK(name, block) \ - BEGIN_DEBUG_BLOCK(name); \ - block; \ - END_DEBUG_BLOCK(name); -#else - #define SIMDUTF_BEGIN_DEBUG_BLOCK(name) - #define SIMDUTF_END_DEBUG_BLOCK(name) - #define SIMDUTF_DEBUG_BLOCK(name, block) -#endif - -// Align to N-byte boundary -#define SIMDUTF_ROUNDUP_N(a, n) (((a) + ((n) - 1)) & ~((n) - 1)) -#define SIMDUTF_ROUNDDOWN_N(a, n) ((a) & ~((n) - 1)) - -#define SIMDUTF_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n) - 1)) == 0) - #if defined(SIMDUTF_REGULAR_VISUAL_STUDIO) #define SIMDUTF_DEPRECATED __declspec(deprecated) @@ -536,18 +508,11 @@ #endif #endif -/// If EXPR is an error, returns it. -#define SIMDUTF_TRY(EXPR) \ - { \ - auto _err = (EXPR); \ - if (_err) { \ - return _err; \ - } \ - } - #endif // SIMDUTF_COMMON_DEFS_H /* end file include/simdutf/common_defs.h */ /* begin file include/simdutf/encoding_types.h */ +#ifndef SIMDUTF_ENCODING_TYPES_H +#define SIMDUTF_ENCODING_TYPES_H #include namespace simdutf { @@ -591,6 +556,7 @@ size_t bom_byte_size(encoding_type bom); } // namespace BOM } // namespace simdutf +#endif /* end file include/simdutf/encoding_types.h */ /* begin file include/simdutf/error.h */ #ifndef SIMDUTF_ERROR_H @@ -675,22 +641,22 @@ SIMDUTF_DISABLE_UNDESIRED_WARNINGS #define SIMDUTF_SIMDUTF_VERSION_H /** The version of simdutf being used (major.minor.revision) */ -#define SIMDUTF_VERSION "5.7.2" +#define SIMDUTF_VERSION "6.0.3" namespace simdutf { enum { /** * The major version (MAJOR.minor.revision) of simdutf being used. */ - SIMDUTF_VERSION_MAJOR = 5, + SIMDUTF_VERSION_MAJOR = 6, /** * The minor version (major.MINOR.revision) of simdutf being used. */ - SIMDUTF_VERSION_MINOR = 7, + SIMDUTF_VERSION_MINOR = 0, /** * The revision (major.minor.REVISION) of simdutf being used. */ - SIMDUTF_VERSION_REVISION = 2 + SIMDUTF_VERSION_REVISION = 3 }; } // namespace simdutf @@ -699,11 +665,10 @@ enum { /* begin file include/simdutf/implementation.h */ #ifndef SIMDUTF_IMPLEMENTATION_H #define SIMDUTF_IMPLEMENTATION_H -#include #if !defined(SIMDUTF_NO_THREADS) #include #endif -#include +#include #include /* begin file include/simdutf/internal/isadetection.h */ /* From @@ -1031,8 +996,61 @@ static inline uint32_t detect_supported_architectures() { #endif // SIMDutf_INTERNAL_ISADETECTION_H /* end file include/simdutf/internal/isadetection.h */ +#if SIMDUTF_SPAN + #include + #include + #include +#endif + namespace simdutf { +#if SIMDUTF_SPAN +/// helpers placed in namespace detail are not a part of the public API +namespace detail { +/** + * matches a byte, in the many ways C++ allows. note that these + * are all distinct types. + */ +template +concept byte_like = std::is_same_v || // + std::is_same_v || // + std::is_same_v || // + std::is_same_v; + +template +concept is_byte_like = byte_like>; + +template +concept is_pointer = std::is_pointer_v; + +/** + * matches anything that behaves like std::span and points to character-like + * data such as: std::byte, char, unsigned char, signed char, std::int8_t, + * std::uint8_t + */ +template +concept input_span_of_byte_like = requires(const T &t) { + { t.size() } noexcept -> std::convertible_to; + { t.data() } noexcept -> is_pointer; + { *t.data() } noexcept -> is_byte_like; +}; + +template +concept is_mutable = !std::is_const_v>; + +/** + * like span_of_byte_like, but for an output span (intended to be written to) + */ +template +concept output_span_of_byte_like = requires(T &t) { + { t.size() } noexcept -> std::convertible_to; + { t.data() } noexcept -> is_pointer; + { *t.data() } noexcept -> is_byte_like; + { *t.data() } noexcept -> is_mutable; +}; +} // namespace detail +#endif + /** * Autodetect the encoding of the input, a single encoding is recommended. * E.g., the function might return simdutf::encoding_type::UTF8, @@ -1049,6 +1067,25 @@ simdutf_really_inline simdutf_warn_unused simdutf::encoding_type autodetect_encoding(const uint8_t *input, size_t length) noexcept { return autodetect_encoding(reinterpret_cast(input), length); } +#if SIMDUTF_SPAN +/** + * Autodetect the encoding of the input, a single encoding is recommended. + * E.g., the function might return simdutf::encoding_type::UTF8, + * simdutf::encoding_type::UTF16_LE, simdutf::encoding_type::UTF16_BE, or + * simdutf::encoding_type::UTF32_LE. + * + * @param input the string to analyze. can be a anything span-like that has a + * data() and size() that points to character data: std::string, + * std::string_view, std::vector, std::span etc. + * @return the detected encoding type + */ +simdutf_really_inline simdutf_warn_unused simdutf::encoding_type +autodetect_encoding( + const detail::input_span_of_byte_like auto &input) noexcept { + return autodetect_encoding(reinterpret_cast(input.data()), + input.size()); +} +#endif /** * Autodetect the possible encodings of the input in one pass. @@ -1067,6 +1104,13 @@ simdutf_really_inline simdutf_warn_unused int detect_encodings(const uint8_t *input, size_t length) noexcept { return detect_encodings(reinterpret_cast(input), length); } +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused int +detect_encodings(const detail::input_span_of_byte_like auto &input) noexcept { + return detect_encodings(reinterpret_cast(input.data()), + input.size()); +} +#endif /** * Validate the UTF-8 string. This function may be best when you expect @@ -1080,6 +1124,13 @@ detect_encodings(const uint8_t *input, size_t length) noexcept { * @return true if and only if the string is valid UTF-8. */ simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused bool +validate_utf8(const detail::input_span_of_byte_like auto &input) noexcept { + return validate_utf8(reinterpret_cast(input.data()), + input.size()); +} +#endif /** * Validate the UTF-8 string and stop on error. @@ -1095,6 +1146,13 @@ simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept; */ simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result validate_utf8_with_errors( + const detail::input_span_of_byte_like auto &input) noexcept { + return validate_utf8_with_errors(reinterpret_cast(input.data()), + input.size()); +} +#endif /** * Validate the ASCII string. @@ -1106,6 +1164,13 @@ simdutf_warn_unused result validate_utf8_with_errors(const char *buf, * @return true if and only if the string is valid ASCII. */ simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused bool +validate_ascii(const detail::input_span_of_byte_like auto &input) noexcept { + return validate_ascii(reinterpret_cast(input.data()), + input.size()); +} +#endif /** * Validate the ASCII string and stop on error. It might be faster than @@ -1122,6 +1187,13 @@ simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) noexcept; */ simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result validate_ascii_with_errors( + const detail::input_span_of_byte_like auto &input) noexcept { + return validate_ascii_with_errors( + reinterpret_cast(input.data()), input.size()); +} +#endif /** * Using native endianness; Validate the UTF-16 string. @@ -1139,6 +1211,12 @@ simdutf_warn_unused result validate_ascii_with_errors(const char *buf, */ simdutf_warn_unused bool validate_utf16(const char16_t *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused bool +validate_utf16(std::span input) noexcept { + return validate_utf16(input.data(), input.size()); +} +#endif /** * Validate the UTF-16LE string. This function may be best when you expect @@ -1156,6 +1234,12 @@ simdutf_warn_unused bool validate_utf16(const char16_t *buf, */ simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused bool +validate_utf16le(std::span input) noexcept { + return validate_utf16le(input.data(), input.size()); +} +#endif /** * Validate the UTF-16BE string. This function may be best when you expect @@ -1173,6 +1257,12 @@ simdutf_warn_unused bool validate_utf16le(const char16_t *buf, */ simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused bool +validate_utf16be(std::span input) noexcept { + return validate_utf16be(input.data(), input.size()); +} +#endif /** * Using native endianness; Validate the UTF-16 string and stop on error. @@ -1193,6 +1283,12 @@ simdutf_warn_unused bool validate_utf16be(const char16_t *buf, */ simdutf_warn_unused result validate_utf16_with_errors(const char16_t *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +validate_utf16_with_errors(std::span input) noexcept { + return validate_utf16_with_errors(input.data(), input.size()); +} +#endif /** * Validate the UTF-16LE string and stop on error. It might be faster than @@ -1212,6 +1308,12 @@ simdutf_warn_unused result validate_utf16_with_errors(const char16_t *buf, */ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +validate_utf16le_with_errors(std::span input) noexcept { + return validate_utf16le_with_errors(input.data(), input.size()); +} +#endif /** * Validate the UTF-16BE string and stop on error. It might be faster than @@ -1231,6 +1333,12 @@ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, */ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +validate_utf16be_with_errors(std::span input) noexcept { + return validate_utf16be_with_errors(input.data(), input.size()); +} +#endif /** * Validate the UTF-32 string. This function may be best when you expect @@ -1248,6 +1356,12 @@ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, */ simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused bool +validate_utf32(std::span input) noexcept { + return validate_utf32(input.data(), input.size()); +} +#endif /** * Validate the UTF-32 string and stop on error. It might be faster than @@ -1267,6 +1381,12 @@ simdutf_warn_unused bool validate_utf32(const char32_t *buf, */ simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +validate_utf32_with_errors(std::span input) noexcept { + return validate_utf32_with_errors(input.data(), input.size()); +} +#endif /** * Convert Latin1 string into UTF8 string. @@ -1281,6 +1401,15 @@ simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, simdutf_warn_unused size_t convert_latin1_to_utf8(const char *input, size_t length, char *utf8_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_latin1_to_utf8( + const detail::input_span_of_byte_like auto &latin1_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_latin1_to_utf8( + reinterpret_cast(latin1_input.data()), latin1_input.size(), + utf8_output.data()); +} +#endif /** * Convert Latin1 string into UTF8 string with output limit. @@ -1296,6 +1425,21 @@ simdutf_warn_unused size_t convert_latin1_to_utf8(const char *input, simdutf_warn_unused size_t convert_latin1_to_utf8_safe(const char *input, size_t length, char *utf8_output, size_t utf8_len) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_latin1_to_utf8_safe( + const detail::input_span_of_byte_like auto &input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + // implementation note: outputspan is a forwarding ref to avoid copying and + // allow both lvalues and rvalues. std::span can be copied without problems, + // but std::vector should not, and this function should accept both. it will + // allow using an owning rvalue ref (example: passing a temporary std::string) + // as output, but the user will quickly find out that he has no way of getting + // the data out of the object in that case. + return convert_latin1_to_utf8_safe( + input.data(), input.size(), reinterpret_cast(utf8_output.data()), + utf8_output.size()); +} +#endif /** * Convert possibly Latin1 string into UTF-16LE string. @@ -1309,6 +1453,15 @@ convert_latin1_to_utf8_safe(const char *input, size_t length, char *utf8_output, */ simdutf_warn_unused size_t convert_latin1_to_utf16le( const char *input, size_t length, char16_t *utf16_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_latin1_to_utf16le( + const detail::input_span_of_byte_like auto &latin1_input, + std::span utf16_output) noexcept { + return convert_latin1_to_utf16le( + reinterpret_cast(latin1_input.data()), latin1_input.size(), + utf16_output.data()); +} +#endif /** * Convert Latin1 string into UTF-16BE string. @@ -1322,6 +1475,14 @@ simdutf_warn_unused size_t convert_latin1_to_utf16le( */ simdutf_warn_unused size_t convert_latin1_to_utf16be( const char *input, size_t length, char16_t *utf16_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_latin1_to_utf16be(const detail::input_span_of_byte_like auto &input, + std::span output) noexcept { + return convert_latin1_to_utf16be(reinterpret_cast(input.data()), + input.size(), output.data()); +} +#endif /** * Convert Latin1 string into UTF-32 string. @@ -1335,6 +1496,15 @@ simdutf_warn_unused size_t convert_latin1_to_utf16be( */ simdutf_warn_unused size_t convert_latin1_to_utf32( const char *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_latin1_to_utf32( + const detail::input_span_of_byte_like auto &latin1_input, + std::span utf32_output) noexcept { + return convert_latin1_to_utf32( + reinterpret_cast(latin1_input.data()), latin1_input.size(), + utf32_output.data()); +} +#endif /** * Convert possibly broken UTF-8 string into latin1 string. @@ -1351,6 +1521,15 @@ simdutf_warn_unused size_t convert_latin1_to_utf32( simdutf_warn_unused size_t convert_utf8_to_latin1(const char *input, size_t length, char *latin1_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_utf8_to_latin1( + const detail::input_span_of_byte_like auto &input, + detail::output_span_of_byte_like auto &&output) noexcept { + return convert_utf8_to_latin1(reinterpret_cast(input.data()), + input.size(), + reinterpret_cast(output.data())); +} +#endif /** * Using native endianness, convert possibly broken UTF-8 string into a UTF-16 @@ -1367,6 +1546,14 @@ simdutf_warn_unused size_t convert_utf8_to_latin1(const char *input, */ simdutf_warn_unused size_t convert_utf8_to_utf16( const char *input, size_t length, char16_t *utf16_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf8_to_utf16(const detail::input_span_of_byte_like auto &input, + std::span output) noexcept { + return convert_utf8_to_utf16(reinterpret_cast(input.data()), + input.size(), output.data()); +} +#endif /** * Using native endianness, convert a Latin1 string into a UTF-16 string. @@ -1378,6 +1565,14 @@ simdutf_warn_unused size_t convert_utf8_to_utf16( */ simdutf_warn_unused size_t convert_latin1_to_utf16( const char *input, size_t length, char16_t *utf16_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_latin1_to_utf16(const detail::input_span_of_byte_like auto &input, + std::span output) noexcept { + return convert_latin1_to_utf16(reinterpret_cast(input.data()), + input.size(), output.data()); +} +#endif /** * Convert possibly broken UTF-8 string into UTF-16LE string. @@ -1393,6 +1588,15 @@ simdutf_warn_unused size_t convert_latin1_to_utf16( */ simdutf_warn_unused size_t convert_utf8_to_utf16le( const char *input, size_t length, char16_t *utf16_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf8_to_utf16le(const detail::input_span_of_byte_like auto &utf8_input, + std::span utf16_output) noexcept { + return convert_utf8_to_utf16le( + reinterpret_cast(utf8_input.data()), utf8_input.size(), + utf16_output.data()); +} +#endif /** * Convert possibly broken UTF-8 string into UTF-16BE string. @@ -1408,6 +1612,15 @@ simdutf_warn_unused size_t convert_utf8_to_utf16le( */ simdutf_warn_unused size_t convert_utf8_to_utf16be( const char *input, size_t length, char16_t *utf16_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf8_to_utf16be(const detail::input_span_of_byte_like auto &utf8_input, + std::span utf16_output) noexcept { + return convert_utf8_to_utf16be( + reinterpret_cast(utf8_input.data()), utf8_input.size(), + utf16_output.data()); +} +#endif /** * Convert possibly broken UTF-8 string into latin1 string with errors. @@ -1427,6 +1640,16 @@ simdutf_warn_unused size_t convert_utf8_to_utf16be( */ simdutf_warn_unused result convert_utf8_to_latin1_with_errors( const char *input, size_t length, char *latin1_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf8_to_latin1_with_errors( + const detail::input_span_of_byte_like auto &utf8_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_utf8_to_latin1_with_errors( + reinterpret_cast(utf8_input.data()), utf8_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Using native endianness, convert possibly broken UTF-8 string into UTF-16 @@ -1445,6 +1668,16 @@ simdutf_warn_unused result convert_utf8_to_latin1_with_errors( */ simdutf_warn_unused result convert_utf8_to_utf16_with_errors( const char *input, size_t length, char16_t *utf16_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf8_to_utf16_with_errors( + const detail::input_span_of_byte_like auto &utf8_input, + std::span utf16_output) noexcept { + return convert_utf8_to_utf16_with_errors( + reinterpret_cast(utf8_input.data()), utf8_input.size(), + utf16_output.data()); +} +#endif /** * Convert possibly broken UTF-8 string into UTF-16LE string and stop on error. @@ -1462,6 +1695,16 @@ simdutf_warn_unused result convert_utf8_to_utf16_with_errors( */ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors( const char *input, size_t length, char16_t *utf16_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf8_to_utf16le_with_errors( + const detail::input_span_of_byte_like auto &utf8_input, + std::span utf16_output) noexcept { + return convert_utf8_to_utf16le_with_errors( + reinterpret_cast(utf8_input.data()), utf8_input.size(), + utf16_output.data()); +} +#endif /** * Convert possibly broken UTF-8 string into UTF-16BE string and stop on error. @@ -1479,6 +1722,16 @@ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors( */ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors( const char *input, size_t length, char16_t *utf16_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf8_to_utf16be_with_errors( + const detail::input_span_of_byte_like auto &utf8_input, + std::span utf16_output) noexcept { + return convert_utf8_to_utf16be_with_errors( + reinterpret_cast(utf8_input.data()), utf8_input.size(), + utf16_output.data()); +} +#endif /** * Convert possibly broken UTF-8 string into UTF-32 string. @@ -1494,6 +1747,15 @@ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors( */ simdutf_warn_unused size_t convert_utf8_to_utf32( const char *input, size_t length, char32_t *utf32_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf8_to_utf32(const detail::input_span_of_byte_like auto &utf8_input, + std::span utf32_output) noexcept { + return convert_utf8_to_utf32( + reinterpret_cast(utf8_input.data()), utf8_input.size(), + utf32_output.data()); +} +#endif /** * Convert possibly broken UTF-8 string into UTF-32 string and stop on error. @@ -1511,6 +1773,16 @@ simdutf_warn_unused size_t convert_utf8_to_utf32( */ simdutf_warn_unused result convert_utf8_to_utf32_with_errors( const char *input, size_t length, char32_t *utf32_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf8_to_utf32_with_errors( + const detail::input_span_of_byte_like auto &utf8_input, + std::span utf32_output) noexcept { + return convert_utf8_to_utf32_with_errors( + reinterpret_cast(utf8_input.data()), utf8_input.size(), + utf32_output.data()); +} +#endif /** * Convert valid UTF-8 string into latin1 string. @@ -1533,6 +1805,15 @@ simdutf_warn_unused result convert_utf8_to_utf32_with_errors( */ simdutf_warn_unused size_t convert_valid_utf8_to_latin1( const char *input, size_t length, char *latin1_output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf8_to_latin1( + const detail::input_span_of_byte_like auto &valid_utf8_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_valid_utf8_to_latin1( + reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size(), latin1_output.data()); +} +#endif /** * Using native endianness, convert valid UTF-8 string into a UTF-16 string. @@ -1546,6 +1827,15 @@ simdutf_warn_unused size_t convert_valid_utf8_to_latin1( */ simdutf_warn_unused size_t convert_valid_utf8_to_utf16( const char *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf8_to_utf16( + const detail::input_span_of_byte_like auto &valid_utf8_input, + std::span utf16_output) noexcept { + return convert_valid_utf8_to_utf16( + reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size(), utf16_output.data()); +} +#endif /** * Convert valid UTF-8 string into UTF-16LE string. @@ -1559,6 +1849,15 @@ simdutf_warn_unused size_t convert_valid_utf8_to_utf16( */ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le( const char *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf8_to_utf16le( + const detail::input_span_of_byte_like auto &valid_utf8_input, + std::span utf16_output) noexcept { + return convert_valid_utf8_to_utf16le( + reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size(), utf16_output.data()); +} +#endif /** * Convert valid UTF-8 string into UTF-16BE string. @@ -1572,6 +1871,15 @@ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le( */ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be( const char *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf8_to_utf16be( + const detail::input_span_of_byte_like auto &valid_utf8_input, + std::span utf16_output) noexcept { + return convert_valid_utf8_to_utf16be( + reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size(), utf16_output.data()); +} +#endif /** * Convert valid UTF-8 string into UTF-32 string. @@ -1585,6 +1893,15 @@ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be( */ simdutf_warn_unused size_t convert_valid_utf8_to_utf32( const char *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf8_to_utf32( + const detail::input_span_of_byte_like auto &valid_utf8_input, + std::span utf32_output) noexcept { + return convert_valid_utf8_to_utf32( + reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size(), utf32_output.data()); +} +#endif /** * Return the number of bytes that this Latin1 string would require in UTF-8 @@ -1596,6 +1913,13 @@ simdutf_warn_unused size_t convert_valid_utf8_to_utf32( */ simdutf_warn_unused size_t utf8_length_from_latin1(const char *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t utf8_length_from_latin1( + const detail::input_span_of_byte_like auto &latin1_input) noexcept { + return utf8_length_from_latin1( + reinterpret_cast(latin1_input.data()), latin1_input.size()); +} +#endif /** * Compute the number of bytes that this UTF-8 string would require in Latin1 @@ -1612,6 +1936,14 @@ simdutf_warn_unused size_t utf8_length_from_latin1(const char *input, */ simdutf_warn_unused size_t latin1_length_from_utf8(const char *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t latin1_length_from_utf8( + const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept { + return latin1_length_from_utf8( + reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size()); +} +#endif /** * Compute the number of 2-byte code units that this UTF-8 string would require @@ -1629,6 +1961,14 @@ simdutf_warn_unused size_t latin1_length_from_utf8(const char *input, */ simdutf_warn_unused size_t utf16_length_from_utf8(const char *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t utf16_length_from_utf8( + const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept { + return utf16_length_from_utf8( + reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size()); +} +#endif /** * Compute the number of 4-byte code units that this UTF-8 string would require @@ -1648,6 +1988,14 @@ simdutf_warn_unused size_t utf16_length_from_utf8(const char *input, */ simdutf_warn_unused size_t utf32_length_from_utf8(const char *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t utf32_length_from_utf8( + const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept { + return utf32_length_from_utf8( + reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size()); +} +#endif /** * Using native endianness, convert possibly broken UTF-16 string into UTF-8 @@ -1667,6 +2015,14 @@ simdutf_warn_unused size_t utf32_length_from_utf8(const char *input, simdutf_warn_unused size_t convert_utf16_to_utf8(const char16_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_utf16_to_utf8( + std::span utf16_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_utf16_to_utf8(utf16_input.data(), utf16_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Using native endianness, convert possibly broken UTF-16 string into Latin1 @@ -1685,6 +2041,15 @@ simdutf_warn_unused size_t convert_utf16_to_utf8(const char16_t *input, */ simdutf_warn_unused size_t convert_utf16_to_latin1( const char16_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_utf16_to_latin1( + std::span utf16_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_utf16_to_latin1( + utf16_input.data(), utf16_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert possibly broken UTF-16LE string into Latin1 string. @@ -1704,6 +2069,15 @@ simdutf_warn_unused size_t convert_utf16_to_latin1( */ simdutf_warn_unused size_t convert_utf16le_to_latin1( const char16_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_utf16le_to_latin1( + std::span utf16_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_utf16le_to_latin1( + utf16_input.data(), utf16_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert possibly broken UTF-16BE string into Latin1 string. @@ -1721,6 +2095,15 @@ simdutf_warn_unused size_t convert_utf16le_to_latin1( */ simdutf_warn_unused size_t convert_utf16be_to_latin1( const char16_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_utf16be_to_latin1( + std::span utf16_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_utf16be_to_latin1( + utf16_input.data(), utf16_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert possibly broken UTF-16LE string into UTF-8 string. @@ -1739,6 +2122,14 @@ simdutf_warn_unused size_t convert_utf16be_to_latin1( simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_utf16le_to_utf8( + std::span utf16_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_utf16le_to_utf8(utf16_input.data(), utf16_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Convert possibly broken UTF-16BE string into UTF-8 string. @@ -1757,6 +2148,14 @@ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t *input, simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_utf16be_to_utf8( + std::span utf16_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_utf16be_to_utf8(utf16_input.data(), utf16_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Using native endianness, convert possibly broken UTF-16 string into Latin1 @@ -1776,6 +2175,16 @@ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t *input, */ simdutf_warn_unused result convert_utf16_to_latin1_with_errors( const char16_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf16_to_latin1_with_errors( + std::span utf16_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_utf16_to_latin1_with_errors( + utf16_input.data(), utf16_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert possibly broken UTF-16LE string into Latin1 string. @@ -1794,6 +2203,16 @@ simdutf_warn_unused result convert_utf16_to_latin1_with_errors( */ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors( const char16_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf16le_to_latin1_with_errors( + std::span utf16_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_utf16le_to_latin1_with_errors( + utf16_input.data(), utf16_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert possibly broken UTF-16BE string into Latin1 string. @@ -1814,6 +2233,16 @@ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors( */ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors( const char16_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf16be_to_latin1_with_errors( + std::span utf16_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_utf16be_to_latin1_with_errors( + utf16_input.data(), utf16_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Using native endianness, convert possibly broken UTF-16 string into UTF-8 @@ -1834,6 +2263,16 @@ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors( */ simdutf_warn_unused result convert_utf16_to_utf8_with_errors( const char16_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf16_to_utf8_with_errors( + std::span utf16_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_utf16_to_utf8_with_errors( + utf16_input.data(), utf16_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Convert possibly broken UTF-16LE string into UTF-8 string and stop on error. @@ -1853,6 +2292,16 @@ simdutf_warn_unused result convert_utf16_to_utf8_with_errors( */ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors( const char16_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf16le_to_utf8_with_errors( + std::span utf16_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_utf16le_to_utf8_with_errors( + utf16_input.data(), utf16_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Convert possibly broken UTF-16BE string into UTF-8 string and stop on error. @@ -1872,6 +2321,16 @@ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors( */ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors( const char16_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf16be_to_utf8_with_errors( + std::span utf16_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_utf16be_to_utf8_with_errors( + utf16_input.data(), utf16_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Using native endianness, convert valid UTF-16 string into UTF-8 string. @@ -1888,6 +2347,15 @@ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors( */ simdutf_warn_unused size_t convert_valid_utf16_to_utf8( const char16_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf16_to_utf8( + std::span valid_utf16_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_valid_utf16_to_utf8( + valid_utf16_input.data(), valid_utf16_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Using native endianness, convert UTF-16 string into Latin1 string. @@ -1910,6 +2378,15 @@ simdutf_warn_unused size_t convert_valid_utf16_to_utf8( */ simdutf_warn_unused size_t convert_valid_utf16_to_latin1( const char16_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf16_to_latin1( + std::span valid_utf16_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_valid_utf16_to_latin1( + valid_utf16_input.data(), valid_utf16_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert valid UTF-16LE string into Latin1 string. @@ -1932,6 +2409,16 @@ simdutf_warn_unused size_t convert_valid_utf16_to_latin1( */ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1( const char16_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_valid_utf16le_to_latin1( + std::span valid_utf16_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_valid_utf16le_to_latin1( + valid_utf16_input.data(), valid_utf16_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert valid UTF-16BE string into Latin1 string. @@ -1954,6 +2441,16 @@ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1( */ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1( const char16_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_valid_utf16be_to_latin1( + std::span valid_utf16_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_valid_utf16be_to_latin1( + valid_utf16_input.data(), valid_utf16_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert valid UTF-16LE string into UTF-8 string. @@ -1971,6 +2468,15 @@ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1( */ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8( const char16_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf16le_to_utf8( + std::span valid_utf16_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_valid_utf16le_to_utf8( + valid_utf16_input.data(), valid_utf16_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Convert valid UTF-16BE string into UTF-8 string. @@ -1987,6 +2493,15 @@ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8( */ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8( const char16_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf16be_to_utf8( + std::span valid_utf16_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_valid_utf16be_to_utf8( + valid_utf16_input.data(), valid_utf16_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Using native endianness, convert possibly broken UTF-16 string into UTF-32 @@ -2005,6 +2520,14 @@ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8( */ simdutf_warn_unused size_t convert_utf16_to_utf32( const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf16_to_utf32(std::span utf16_input, + std::span utf32_output) noexcept { + return convert_utf16_to_utf32(utf16_input.data(), utf16_input.size(), + utf32_output.data()); +} +#endif /** * Convert possibly broken UTF-16LE string into UTF-32 string. @@ -2022,6 +2545,14 @@ simdutf_warn_unused size_t convert_utf16_to_utf32( */ simdutf_warn_unused size_t convert_utf16le_to_utf32( const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf16le_to_utf32(std::span utf16_input, + std::span utf32_output) noexcept { + return convert_utf16le_to_utf32(utf16_input.data(), utf16_input.size(), + utf32_output.data()); +} +#endif /** * Convert possibly broken UTF-16BE string into UTF-32 string. @@ -2039,6 +2570,14 @@ simdutf_warn_unused size_t convert_utf16le_to_utf32( */ simdutf_warn_unused size_t convert_utf16be_to_utf32( const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf16be_to_utf32(std::span utf16_input, + std::span utf32_output) noexcept { + return convert_utf16be_to_utf32(utf16_input.data(), utf16_input.size(), + utf32_output.data()); +} +#endif /** * Using native endianness, convert possibly broken UTF-16 string into @@ -2059,6 +2598,14 @@ simdutf_warn_unused size_t convert_utf16be_to_utf32( */ simdutf_warn_unused result convert_utf16_to_utf32_with_errors( const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf16_to_utf32_with_errors(std::span utf16_input, + std::span utf32_output) noexcept { + return convert_utf16_to_utf32_with_errors( + utf16_input.data(), utf16_input.size(), utf32_output.data()); +} +#endif /** * Convert possibly broken UTF-16LE string into UTF-32 string and stop on error. @@ -2078,6 +2625,15 @@ simdutf_warn_unused result convert_utf16_to_utf32_with_errors( */ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors( const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf16le_to_utf32_with_errors( + std::span utf16_input, + std::span utf32_output) noexcept { + return convert_utf16le_to_utf32_with_errors( + utf16_input.data(), utf16_input.size(), utf32_output.data()); +} +#endif /** * Convert possibly broken UTF-16BE string into UTF-32 string and stop on error. @@ -2097,6 +2653,15 @@ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors( */ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors( const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf16be_to_utf32_with_errors( + std::span utf16_input, + std::span utf32_output) noexcept { + return convert_utf16be_to_utf32_with_errors( + utf16_input.data(), utf16_input.size(), utf32_output.data()); +} +#endif /** * Using native endianness, convert valid UTF-16 string into UTF-32 string. @@ -2114,6 +2679,14 @@ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors( */ simdutf_warn_unused size_t convert_valid_utf16_to_utf32( const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_valid_utf16_to_utf32(std::span valid_utf16_input, + std::span utf32_output) noexcept { + return convert_valid_utf16_to_utf32( + valid_utf16_input.data(), valid_utf16_input.size(), utf32_output.data()); +} +#endif /** * Convert valid UTF-16LE string into UTF-32 string. @@ -2130,6 +2703,14 @@ simdutf_warn_unused size_t convert_valid_utf16_to_utf32( */ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32( const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_valid_utf16le_to_utf32(std::span valid_utf16_input, + std::span utf32_output) noexcept { + return convert_valid_utf16le_to_utf32( + valid_utf16_input.data(), valid_utf16_input.size(), utf32_output.data()); +} +#endif /** * Convert valid UTF-16BE string into UTF-32 string. @@ -2146,8 +2727,16 @@ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32( */ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32( const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_valid_utf16be_to_utf32(std::span valid_utf16_input, + std::span utf32_output) noexcept { + return convert_valid_utf16be_to_utf32( + valid_utf16_input.data(), valid_utf16_input.size(), utf32_output.data()); +} +#endif -/* +/** * Compute the number of bytes that this UTF-16LE/BE string would require in * Latin1 format. * @@ -2174,6 +2763,13 @@ simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) noexcept; */ simdutf_warn_unused size_t utf8_length_from_utf16(const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +utf8_length_from_utf16(std::span valid_utf16_input) noexcept { + return utf8_length_from_utf16(valid_utf16_input.data(), + valid_utf16_input.size()); +} +#endif /** * Compute the number of bytes that this UTF-16LE string would require in UTF-8 @@ -2188,6 +2784,13 @@ simdutf_warn_unused size_t utf8_length_from_utf16(const char16_t *input, */ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +utf8_length_from_utf16le(std::span valid_utf16_input) noexcept { + return utf8_length_from_utf16le(valid_utf16_input.data(), + valid_utf16_input.size()); +} +#endif /** * Compute the number of bytes that this UTF-16BE string would require in UTF-8 @@ -2202,6 +2805,13 @@ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t *input, */ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +utf8_length_from_utf16be(std::span valid_utf16_input) noexcept { + return utf8_length_from_utf16be(valid_utf16_input.data(), + valid_utf16_input.size()); +} +#endif /** * Convert possibly broken UTF-32 string into UTF-8 string. @@ -2219,6 +2829,14 @@ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t *input, simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_utf32_to_utf8( + std::span utf32_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_utf32_to_utf8(utf32_input.data(), utf32_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Convert possibly broken UTF-32 string into UTF-8 string and stop on error. @@ -2238,6 +2856,16 @@ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t *input, */ simdutf_warn_unused result convert_utf32_to_utf8_with_errors( const char32_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf32_to_utf8_with_errors( + std::span utf32_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_utf32_to_utf8_with_errors( + utf32_input.data(), utf32_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Convert valid UTF-32 string into UTF-8 string. @@ -2254,6 +2882,15 @@ simdutf_warn_unused result convert_utf32_to_utf8_with_errors( */ simdutf_warn_unused size_t convert_valid_utf32_to_utf8( const char32_t *input, size_t length, char *utf8_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf32_to_utf8( + std::span valid_utf32_input, + detail::output_span_of_byte_like auto &&utf8_output) noexcept { + return convert_valid_utf32_to_utf8( + valid_utf32_input.data(), valid_utf32_input.size(), + reinterpret_cast(utf8_output.data())); +} +#endif /** * Using native endianness, convert possibly broken UTF-32 string into a UTF-16 @@ -2271,6 +2908,14 @@ simdutf_warn_unused size_t convert_valid_utf32_to_utf8( */ simdutf_warn_unused size_t convert_utf32_to_utf16( const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf32_to_utf16(std::span utf32_input, + std::span utf16_output) noexcept { + return convert_utf32_to_utf16(utf32_input.data(), utf32_input.size(), + utf16_output.data()); +} +#endif /** * Convert possibly broken UTF-32 string into UTF-16LE string. @@ -2287,6 +2932,14 @@ simdutf_warn_unused size_t convert_utf32_to_utf16( */ simdutf_warn_unused size_t convert_utf32_to_utf16le( const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf32_to_utf16le(std::span utf32_input, + std::span utf16_output) noexcept { + return convert_utf32_to_utf16le(utf32_input.data(), utf32_input.size(), + utf16_output.data()); +} +#endif /** * Convert possibly broken UTF-32 string into Latin1 string. @@ -2304,6 +2957,15 @@ simdutf_warn_unused size_t convert_utf32_to_utf16le( */ simdutf_warn_unused size_t convert_utf32_to_latin1( const char32_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_utf32_to_latin1( + std::span utf32_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_utf32_to_latin1( + utf32_input.data(), utf32_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert possibly broken UTF-32 string into Latin1 string and stop on error. @@ -2324,6 +2986,16 @@ simdutf_warn_unused size_t convert_utf32_to_latin1( */ simdutf_warn_unused result convert_utf32_to_latin1_with_errors( const char32_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf32_to_latin1_with_errors( + std::span utf32_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_utf32_to_latin1_with_errors( + utf32_input.data(), utf32_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert valid UTF-32 string into Latin1 string. @@ -2347,6 +3019,15 @@ simdutf_warn_unused result convert_utf32_to_latin1_with_errors( */ simdutf_warn_unused size_t convert_valid_utf32_to_latin1( const char32_t *input, size_t length, char *latin1_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t convert_valid_utf32_to_latin1( + std::span valid_utf32_input, + detail::output_span_of_byte_like auto &&latin1_output) noexcept { + return convert_valid_utf32_to_latin1( + valid_utf32_input.data(), valid_utf32_input.size(), + reinterpret_cast(latin1_output.data())); +} +#endif /** * Convert possibly broken UTF-32 string into UTF-16BE string. @@ -2363,6 +3044,14 @@ simdutf_warn_unused size_t convert_valid_utf32_to_latin1( */ simdutf_warn_unused size_t convert_utf32_to_utf16be( const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_utf32_to_utf16be(std::span utf32_input, + std::span utf16_output) noexcept { + return convert_utf32_to_utf16be(utf32_input.data(), utf32_input.size(), + utf16_output.data()); +} +#endif /** * Using native endianness, convert possibly broken UTF-32 string into UTF-16 @@ -2383,6 +3072,14 @@ simdutf_warn_unused size_t convert_utf32_to_utf16be( */ simdutf_warn_unused result convert_utf32_to_utf16_with_errors( const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf32_to_utf16_with_errors(std::span utf32_input, + std::span utf16_output) noexcept { + return convert_utf32_to_utf16_with_errors( + utf32_input.data(), utf32_input.size(), utf16_output.data()); +} +#endif /** * Convert possibly broken UTF-32 string into UTF-16LE string and stop on error. @@ -2402,6 +3099,15 @@ simdutf_warn_unused result convert_utf32_to_utf16_with_errors( */ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors( const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf32_to_utf16le_with_errors( + std::span utf32_input, + std::span utf16_output) noexcept { + return convert_utf32_to_utf16le_with_errors( + utf32_input.data(), utf32_input.size(), utf16_output.data()); +} +#endif /** * Convert possibly broken UTF-32 string into UTF-16BE string and stop on error. @@ -2421,6 +3127,15 @@ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors( */ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors( const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result +convert_utf32_to_utf16be_with_errors( + std::span utf32_input, + std::span utf16_output) noexcept { + return convert_utf32_to_utf16be_with_errors( + utf32_input.data(), utf32_input.size(), utf16_output.data()); +} +#endif /** * Using native endianness, convert valid UTF-32 string into a UTF-16 string. @@ -2437,6 +3152,14 @@ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors( */ simdutf_warn_unused size_t convert_valid_utf32_to_utf16( const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_valid_utf32_to_utf16(std::span valid_utf32_input, + std::span utf16_output) noexcept { + return convert_valid_utf32_to_utf16( + valid_utf32_input.data(), valid_utf32_input.size(), utf16_output.data()); +} +#endif /** * Convert valid UTF-32 string into UTF-16LE string. @@ -2453,6 +3176,14 @@ simdutf_warn_unused size_t convert_valid_utf32_to_utf16( */ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le( const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_valid_utf32_to_utf16le(std::span valid_utf32_input, + std::span utf16_output) noexcept { + return convert_valid_utf32_to_utf16le( + valid_utf32_input.data(), valid_utf32_input.size(), utf16_output.data()); +} +#endif /** * Convert valid UTF-32 string into UTF-16BE string. @@ -2469,6 +3200,14 @@ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le( */ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be( const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +convert_valid_utf32_to_utf16be(std::span valid_utf32_input, + std::span utf16_output) noexcept { + return convert_valid_utf32_to_utf16be( + valid_utf32_input.data(), valid_utf32_input.size(), utf16_output.data()); +} +#endif /** * Change the endianness of the input. Can be used to go from UTF-16LE to @@ -2485,6 +3224,14 @@ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be( */ void change_endianness_utf16(const char16_t *input, size_t length, char16_t *output) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline void +change_endianness_utf16(std::span utf16_input, + std::span utf16_output) noexcept { + return change_endianness_utf16(utf16_input.data(), utf16_input.size(), + utf16_output.data()); +} +#endif /** * Compute the number of bytes that this UTF-32 string would require in UTF-8 @@ -2499,6 +3246,13 @@ void change_endianness_utf16(const char16_t *input, size_t length, */ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +utf8_length_from_utf32(std::span valid_utf32_input) noexcept { + return utf8_length_from_utf32(valid_utf32_input.data(), + valid_utf32_input.size()); +} +#endif /** * Compute the number of two-byte code units that this UTF-32 string would @@ -2513,6 +3267,13 @@ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t *input, */ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +utf16_length_from_utf32(std::span valid_utf32_input) noexcept { + return utf16_length_from_utf32(valid_utf32_input.data(), + valid_utf32_input.size()); +} +#endif /** * Using native endianness; Compute the number of bytes that this UTF-16 @@ -2531,6 +3292,13 @@ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t *input, */ simdutf_warn_unused size_t utf32_length_from_utf16(const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +utf32_length_from_utf16(std::span valid_utf16_input) noexcept { + return utf32_length_from_utf16(valid_utf16_input.data(), + valid_utf16_input.size()); +} +#endif /** * Compute the number of bytes that this UTF-16LE string would require in UTF-32 @@ -2549,6 +3317,13 @@ simdutf_warn_unused size_t utf32_length_from_utf16(const char16_t *input, */ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t utf32_length_from_utf16le( + std::span valid_utf16_input) noexcept { + return utf32_length_from_utf16le(valid_utf16_input.data(), + valid_utf16_input.size()); +} +#endif /** * Compute the number of bytes that this UTF-16BE string would require in UTF-32 @@ -2567,6 +3342,13 @@ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t *input, */ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t utf32_length_from_utf16be( + std::span valid_utf16_input) noexcept { + return utf32_length_from_utf16be(valid_utf16_input.data(), + valid_utf16_input.size()); +} +#endif /** * Count the number of code points (characters) in the string assuming that @@ -2584,6 +3366,12 @@ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t *input, */ simdutf_warn_unused size_t count_utf16(const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +count_utf16(std::span valid_utf16_input) noexcept { + return count_utf16(valid_utf16_input.data(), valid_utf16_input.size()); +} +#endif /** * Count the number of code points (characters) in the string assuming that @@ -2601,6 +3389,12 @@ simdutf_warn_unused size_t count_utf16(const char16_t *input, */ simdutf_warn_unused size_t count_utf16le(const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +count_utf16le(std::span valid_utf16_input) noexcept { + return count_utf16le(valid_utf16_input.data(), valid_utf16_input.size()); +} +#endif /** * Count the number of code points (characters) in the string assuming that @@ -2618,6 +3412,12 @@ simdutf_warn_unused size_t count_utf16le(const char16_t *input, */ simdutf_warn_unused size_t count_utf16be(const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +count_utf16be(std::span valid_utf16_input) noexcept { + return count_utf16be(valid_utf16_input.data(), valid_utf16_input.size()); +} +#endif /** * Count the number of code points (characters) in the string assuming that @@ -2633,6 +3433,13 @@ simdutf_warn_unused size_t count_utf16be(const char16_t *input, */ simdutf_warn_unused size_t count_utf8(const char *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t count_utf8( + const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept { + return count_utf8(reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size()); +} +#endif /** * Given a valid UTF-8 string having a possibly truncated last character, @@ -2649,6 +3456,14 @@ simdutf_warn_unused size_t count_utf8(const char *input, * @return the length of the string in bytes, possibly shorter by 1 to 3 bytes */ simdutf_warn_unused size_t trim_partial_utf8(const char *input, size_t length); +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t trim_partial_utf8( + const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept { + return trim_partial_utf8( + reinterpret_cast(valid_utf8_input.data()), + valid_utf8_input.size()); +} +#endif /** * Given a valid UTF-16BE string having a possibly truncated last character, @@ -2666,6 +3481,13 @@ simdutf_warn_unused size_t trim_partial_utf8(const char *input, size_t length); */ simdutf_warn_unused size_t trim_partial_utf16be(const char16_t *input, size_t length); +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +trim_partial_utf16be(std::span valid_utf16_input) noexcept { + return trim_partial_utf16be(valid_utf16_input.data(), + valid_utf16_input.size()); +} +#endif /** * Given a valid UTF-16LE string having a possibly truncated last character, @@ -2683,6 +3505,13 @@ simdutf_warn_unused size_t trim_partial_utf16be(const char16_t *input, */ simdutf_warn_unused size_t trim_partial_utf16le(const char16_t *input, size_t length); +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +trim_partial_utf16le(std::span valid_utf16_input) noexcept { + return trim_partial_utf16le(valid_utf16_input.data(), + valid_utf16_input.size()); +} +#endif /** * Given a valid UTF-16 string having a possibly truncated last character, @@ -2700,6 +3529,12 @@ simdutf_warn_unused size_t trim_partial_utf16le(const char16_t *input, */ simdutf_warn_unused size_t trim_partial_utf16(const char16_t *input, size_t length); +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +trim_partial_utf16(std::span valid_utf16_input) noexcept { + return trim_partial_utf16(valid_utf16_input.data(), valid_utf16_input.size()); +} +#endif // base64_options are used to specify the base64 encoding options. // ASCII spaces are ' ', '\t', '\n', '\r', '\f' @@ -2742,6 +3577,14 @@ enum last_chunk_handling_options : uint64_t { */ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +maximal_binary_length_from_base64( + const detail::input_span_of_byte_like auto &input) noexcept { + return maximal_binary_length_from_base64( + reinterpret_cast(input.data()), input.size()); +} +#endif /** * Provide the maximal binary length in bytes given the base64 input. @@ -2755,6 +3598,12 @@ maximal_binary_length_from_base64(const char *input, size_t length) noexcept; */ simdutf_warn_unused size_t maximal_binary_length_from_base64( const char16_t *input, size_t length) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +maximal_binary_length_from_base64(std::span input) noexcept { + return maximal_binary_length_from_base64(input.data(), input.size()); +} +#endif /** * Convert a base64 input to a binary output. @@ -2814,6 +3663,18 @@ simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options = base64_default, last_chunk_handling_options last_chunk_options = loose) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result base64_to_binary( + const detail::input_span_of_byte_like auto &input, + detail::output_span_of_byte_like auto &&binary_output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = loose) noexcept { + return base64_to_binary(reinterpret_cast(input.data()), + input.size(), + reinterpret_cast(binary_output.data()), + options, last_chunk_options); +} +#endif /** * Provide the base64 length in bytes given the length of a binary input. @@ -2847,6 +3708,16 @@ simdutf_warn_unused size_t base64_length_from_binary( */ size_t binary_to_base64(const char *input, size_t length, char *output, base64_options options = base64_default) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused size_t +binary_to_base64(const detail::input_span_of_byte_like auto &input, + detail::output_span_of_byte_like auto &&binary_output, + base64_options options = base64_default) noexcept { + return binary_to_base64( + reinterpret_cast(input.data()), input.size(), + reinterpret_cast(binary_output.data()), options); +} +#endif /** * Convert a base64 input to a binary output. @@ -2909,6 +3780,17 @@ base64_to_binary(const char16_t *input, size_t length, char *output, base64_options options = base64_default, last_chunk_handling_options last_chunk_options = last_chunk_handling_options::loose) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result base64_to_binary( + std::span input, + detail::output_span_of_byte_like auto &&binary_output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = loose) noexcept { + return base64_to_binary(input.data(), input.size(), + reinterpret_cast(binary_output.data()), + options, last_chunk_options); +} +#endif /** * Convert a base64 input to a binary output. @@ -2976,11 +3858,43 @@ base64_to_binary_safe(const char *input, size_t length, char *output, size_t &outlen, base64_options options = base64_default, last_chunk_handling_options last_chunk_options = last_chunk_handling_options::loose) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result base64_to_binary_safe( + const detail::input_span_of_byte_like auto &input, + detail::output_span_of_byte_like auto &&binary_output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = loose) noexcept { + // we can't write the outlen to the provided output span, the user will have + // to pick it up from the returned value instead (assuming success). we still + // get the benefit of providing info of how long the output buffer is. + size_t outlen = binary_output.size(); + return base64_to_binary_safe(reinterpret_cast(input.data()), + input.size(), + reinterpret_cast(binary_output.data()), + outlen, options, last_chunk_options); +} +#endif + simdutf_warn_unused result base64_to_binary_safe(const char16_t *input, size_t length, char *output, size_t &outlen, base64_options options = base64_default, last_chunk_handling_options last_chunk_options = last_chunk_handling_options::loose) noexcept; +#if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused result base64_to_binary_safe( + std::span input, + detail::output_span_of_byte_like auto &&binary_output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = loose) noexcept { + // we can't write the outlen to the provided output span, the user will have + // to pick it up from the returned value instead (assuming success). we still + // get the benefit of providing info of how long the output buffer is. + size_t outlen = binary_output.size(); + return base64_to_binary_safe(input.data(), input.size(), + reinterpret_cast(binary_output.data()), + outlen, options, last_chunk_options); +} +#endif /** * An implementation of simdutf for a particular CPU architecture. @@ -4243,7 +5157,7 @@ class implementation { simdutf_warn_unused virtual size_t latin1_length_from_utf8(const char *input, size_t length) const noexcept = 0; - /* + /** * Compute the number of bytes that this UTF-16LE/BE string would require in * Latin1 format. * @@ -4289,7 +5203,7 @@ class implementation { simdutf_warn_unused virtual size_t utf32_length_from_latin1(size_t length) const noexcept = 0; - /* + /** * Compute the number of bytes that this UTF-16LE string would require in * UTF-32 format. * @@ -4310,7 +5224,7 @@ class implementation { utf32_length_from_utf16le(const char16_t *input, size_t length) const noexcept = 0; - /* + /** * Compute the number of bytes that this UTF-16BE string would require in * UTF-32 format. * From 5770972dc6c0458af5458b8056b16d70905ea9d8 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Mon, 13 Jan 2025 19:56:47 -0500 Subject: [PATCH 046/208] deps: update undici to 7.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56569 Reviewed-By: Rafael Gonzaga Reviewed-By: Antoine du Hamel Reviewed-By: James M Snell Reviewed-By: Ulises Gascón --- .../src/docs/docs/api/DiagnosticsChannel.md | 2 +- deps/undici/src/docs/docs/api/Dispatcher.md | 2 +- deps/undici/src/lib/handler/retry-handler.js | 4 +- deps/undici/src/lib/interceptor/dns.js | 2 +- deps/undici/src/lib/llhttp/wasm_build_env.txt | 2 +- deps/undici/src/lib/web/websocket/receiver.js | 88 ++-- deps/undici/src/lib/web/websocket/util.js | 2 +- deps/undici/src/package-lock.json | 436 +++++++++++------- deps/undici/src/package.json | 4 +- deps/undici/src/scripts/release.js | 2 +- deps/undici/src/types/errors.d.ts | 16 + deps/undici/undici.js | 79 ++-- src/undici_version.h | 2 +- 13 files changed, 396 insertions(+), 245 deletions(-) diff --git a/deps/undici/src/docs/docs/api/DiagnosticsChannel.md b/deps/undici/src/docs/docs/api/DiagnosticsChannel.md index 099c072f6c6ca7..a3635cbc05e5c5 100644 --- a/deps/undici/src/docs/docs/api/DiagnosticsChannel.md +++ b/deps/undici/src/docs/docs/api/DiagnosticsChannel.md @@ -40,7 +40,7 @@ diagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) => ## `undici:request:headers` -This message is published after the response headers have been received, i.e. the response has been completed. +This message is published after the response headers have been received. ```js import diagnosticsChannel from 'diagnostics_channel' diff --git a/deps/undici/src/docs/docs/api/Dispatcher.md b/deps/undici/src/docs/docs/api/Dispatcher.md index d9f66d17d81ab1..fb7e87d4e5404a 100644 --- a/deps/undici/src/docs/docs/api/Dispatcher.md +++ b/deps/undici/src/docs/docs/api/Dispatcher.md @@ -652,7 +652,7 @@ return null A faster version of `Dispatcher.request`. This method expects the second argument `factory` to return a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream which the response will be written to. This improves performance by avoiding creating an intermediate [`stream.Readable`](https://nodejs.org/api/stream.html#stream_readable_streams) stream when the user expects to directly pipe the response body to a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream. -As demonstrated in [Example 1 - Basic GET stream request](/docs/docs/api/Dispatcher.md#example-1---basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](/docs/docs/api/Dispatch.md#example-2---stream-to-fastify-response) for more details. +As demonstrated in [Example 1 - Basic GET stream request](/docs/docs/api/Dispatcher.md#example-1-basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](/docs/docs/api/Dispatch.md#example-2-stream-to-fastify-response) for more details. Arguments: diff --git a/deps/undici/src/lib/handler/retry-handler.js b/deps/undici/src/lib/handler/retry-handler.js index f469f9df343d53..d929b0c2ae0da5 100644 --- a/deps/undici/src/lib/handler/retry-handler.js +++ b/deps/undici/src/lib/handler/retry-handler.js @@ -133,7 +133,7 @@ class RetryHandler { ? Math.min(retryAfterHeader, maxTimeout) : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout) - setTimeout(() => cb(null), retryTimeout).unref() + setTimeout(() => cb(null), retryTimeout) } onResponseStart (controller, statusCode, headers, statusMessage) { @@ -277,7 +277,7 @@ class RetryHandler { } onResponseError (controller, err) { - if (!controller || controller.aborted || isDisturbed(this.opts.body)) { + if (controller?.aborted || isDisturbed(this.opts.body)) { this.handler.onResponseError?.(controller, err) return } diff --git a/deps/undici/src/lib/interceptor/dns.js b/deps/undici/src/lib/interceptor/dns.js index 98ef376ecbb22e..c8d56c2cf77562 100644 --- a/deps/undici/src/lib/interceptor/dns.js +++ b/deps/undici/src/lib/interceptor/dns.js @@ -353,7 +353,7 @@ module.exports = interceptorOpts => { instance.runLookup(origin, origDispatchOpts, (err, newOrigin) => { if (err) { - return handler.onError(err) + return handler.onResponseError(null, err) } let dispatchOpts = null diff --git a/deps/undici/src/lib/llhttp/wasm_build_env.txt b/deps/undici/src/lib/llhttp/wasm_build_env.txt index 704f369e94b550..b921c749fab2ac 100644 --- a/deps/undici/src/lib/llhttp/wasm_build_env.txt +++ b/deps/undici/src/lib/llhttp/wasm_build_env.txt @@ -1,5 +1,5 @@ -> undici@7.2.0 build:wasm +> undici@7.2.1 build:wasm > node build/wasm.js --docker > docker run --rm --platform=linux/x86_64 --user 1001:128 --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/lib/llhttp,target=/home/node/build/lib/llhttp --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/build,target=/home/node/build/build --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/deps,target=/home/node/build/deps -t ghcr.io/nodejs/wasm-builder@sha256:975f391d907e42a75b8c72eb77c782181e941608687d4d8694c3e9df415a0970 node build/wasm.js diff --git a/deps/undici/src/lib/web/websocket/receiver.js b/deps/undici/src/lib/web/websocket/receiver.js index dac9122a40833b..3f5bc544b7fa90 100644 --- a/deps/undici/src/lib/web/websocket/receiver.js +++ b/deps/undici/src/lib/web/websocket/receiver.js @@ -24,6 +24,7 @@ const { PerMessageDeflate } = require('./permessage-deflate') class ByteParser extends Writable { #buffers = [] + #fragmentsBytes = 0 #byteOffset = 0 #loop = false @@ -208,16 +209,14 @@ class ByteParser extends Writable { this.#state = parserStates.INFO } else { if (!this.#info.compressed) { - this.#fragments.push(body) + this.writeFragments(body) // If the frame is not fragmented, a message has been received. // If the frame is fragmented, it will terminate with a fin bit set // and an opcode of 0 (continuation), therefore we handle that when // parsing continuation frames, not here. if (!this.#info.fragmented && this.#info.fin) { - const fullMessage = Buffer.concat(this.#fragments) - websocketMessageReceived(this.#handler, this.#info.binaryType, fullMessage) - this.#fragments.length = 0 + websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments()) } this.#state = parserStates.INFO @@ -228,7 +227,7 @@ class ByteParser extends Writable { return } - this.#fragments.push(data) + this.writeFragments(data) if (!this.#info.fin) { this.#state = parserStates.INFO @@ -237,11 +236,10 @@ class ByteParser extends Writable { return } - websocketMessageReceived(this.#handler, this.#info.binaryType, Buffer.concat(this.#fragments)) + websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments()) this.#loop = true this.#state = parserStates.INFO - this.#fragments.length = 0 this.run(callback) }) @@ -265,34 +263,70 @@ class ByteParser extends Writable { return emptyBuffer } - if (this.#buffers[0].length === n) { - this.#byteOffset -= this.#buffers[0].length + this.#byteOffset -= n + + const first = this.#buffers[0] + + if (first.length > n) { + // replace with remaining buffer + this.#buffers[0] = first.subarray(n, first.length) + return first.subarray(0, n) + } else if (first.length === n) { + // prefect match return this.#buffers.shift() + } else { + let offset = 0 + // If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero. + const buffer = Buffer.allocUnsafeSlow(n) + while (offset !== n) { + const next = this.#buffers[0] + const length = next.length + + if (length + offset === n) { + buffer.set(this.#buffers.shift(), offset) + break + } else if (length + offset > n) { + buffer.set(next.subarray(0, n - offset), offset) + this.#buffers[0] = next.subarray(n - offset) + break + } else { + buffer.set(this.#buffers.shift(), offset) + offset += length + } + } + + return buffer + } + } + + writeFragments (fragment) { + this.#fragmentsBytes += fragment.length + this.#fragments.push(fragment) + } + + consumeFragments () { + const fragments = this.#fragments + + if (fragments.length === 1) { + // single fragment + this.#fragmentsBytes = 0 + return fragments.shift() } - const buffer = Buffer.allocUnsafe(n) let offset = 0 + // If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero. + const output = Buffer.allocUnsafeSlow(this.#fragmentsBytes) - while (offset !== n) { - const next = this.#buffers[0] - const { length } = next - - if (length + offset === n) { - buffer.set(this.#buffers.shift(), offset) - break - } else if (length + offset > n) { - buffer.set(next.subarray(0, n - offset), offset) - this.#buffers[0] = next.subarray(n - offset) - break - } else { - buffer.set(this.#buffers.shift(), offset) - offset += next.length - } + for (let i = 0; i < fragments.length; ++i) { + const buffer = fragments[i] + output.set(buffer, offset) + offset += buffer.length } - this.#byteOffset -= n + this.#fragments = [] + this.#fragmentsBytes = 0 - return buffer + return output } parseCloseBody (data) { diff --git a/deps/undici/src/lib/web/websocket/util.js b/deps/undici/src/lib/web/websocket/util.js index e544ac7681936c..45e74498568456 100644 --- a/deps/undici/src/lib/web/websocket/util.js +++ b/deps/undici/src/lib/web/websocket/util.js @@ -87,7 +87,7 @@ function toArrayBuffer (buffer) { if (buffer.byteLength === buffer.buffer.byteLength) { return buffer.buffer } - return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) + return new Uint8Array(buffer).buffer } /** diff --git a/deps/undici/src/package-lock.json b/deps/undici/src/package-lock.json index 79bbfc1c03ddce..17428ee198e8fe 100644 --- a/deps/undici/src/package-lock.json +++ b/deps/undici/src/package-lock.json @@ -1,15 +1,15 @@ { "name": "undici", - "version": "7.2.0", + "version": "7.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "undici", - "version": "7.2.0", + "version": "7.2.1", "license": "MIT", "devDependencies": { - "@fastify/busboy": "3.1.0", + "@fastify/busboy": "3.1.1", "@matteo.collina/tspl": "^0.1.1", "@sinonjs/fake-timers": "^12.0.0", "@types/node": "^18.19.50", @@ -104,9 +104,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", "dev": true, "license": "MIT", "engines": { @@ -145,14 +145,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -162,13 +162,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -211,9 +211,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, "license": "MIT", "engines": { @@ -265,13 +265,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -535,17 +535,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", + "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.5", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/types": "^7.26.5", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -564,9 +564,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -1070,9 +1070,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1107,9 +1107,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", + "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", "dev": true, "license": "MIT", "engines": { @@ -1127,12 +1127,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { @@ -1140,9 +1141,9 @@ } }, "node_modules/@fastify/busboy": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.0.tgz", - "integrity": "sha512-yHmUtGwEbW6HsKpPqT140/L6GpHtquHogRLgtanJFep3UAfDkE0fQfC49U+F9irCAoJVlv3M7VSp4rrtO4LnfA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", + "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", "dev": true, "license": "MIT" }, @@ -2094,9 +2095,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.68", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.68.tgz", - "integrity": "sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==", + "version": "18.19.70", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz", + "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2145,21 +2146,21 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.1.tgz", - "integrity": "sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz", + "integrity": "sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.18.1", - "@typescript-eslint/type-utils": "8.18.1", - "@typescript-eslint/utils": "8.18.1", - "@typescript-eslint/visitor-keys": "8.18.1", + "@typescript-eslint/scope-manager": "8.19.1", + "@typescript-eslint/type-utils": "8.19.1", + "@typescript-eslint/utils": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2175,16 +2176,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.1.tgz", - "integrity": "sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.1.tgz", + "integrity": "sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.18.1", - "@typescript-eslint/types": "8.18.1", - "@typescript-eslint/typescript-estree": "8.18.1", - "@typescript-eslint/visitor-keys": "8.18.1", + "@typescript-eslint/scope-manager": "8.19.1", + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/typescript-estree": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1", "debug": "^4.3.4" }, "engines": { @@ -2200,14 +2201,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.1.tgz", - "integrity": "sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.1.tgz", + "integrity": "sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.18.1", - "@typescript-eslint/visitor-keys": "8.18.1" + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2218,16 +2219,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.1.tgz", - "integrity": "sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.1.tgz", + "integrity": "sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.18.1", - "@typescript-eslint/utils": "8.18.1", + "@typescript-eslint/typescript-estree": "8.19.1", + "@typescript-eslint/utils": "8.19.1", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2242,9 +2243,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.1.tgz", - "integrity": "sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.1.tgz", + "integrity": "sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==", "dev": true, "license": "MIT", "engines": { @@ -2256,20 +2257,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.1.tgz", - "integrity": "sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.1.tgz", + "integrity": "sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.18.1", - "@typescript-eslint/visitor-keys": "8.18.1", + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2322,16 +2323,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.1.tgz", - "integrity": "sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.1.tgz", + "integrity": "sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.18.1", - "@typescript-eslint/types": "8.18.1", - "@typescript-eslint/typescript-estree": "8.18.1" + "@typescript-eslint/scope-manager": "8.19.1", + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/typescript-estree": "8.19.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2346,13 +2347,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.1.tgz", - "integrity": "sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.1.tgz", + "integrity": "sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/types": "8.19.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2977,9 +2978,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -3208,9 +3209,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "version": "1.0.30001692", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", "dev": true, "funding": [ { @@ -3723,9 +3724,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.75", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.75.tgz", - "integrity": "sha512-Lf3++DumRE/QmweGjU+ZcKqQ+3bKkU/qjaKYhIJKEOhgIO9Xs6IiAQFkfFoj+RhgDk4LUeNsLo6plExHqSyu6Q==", + "version": "1.5.80", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz", + "integrity": "sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==", "dev": true, "license": "ISC" }, @@ -3774,9 +3775,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.7.tgz", - "integrity": "sha512-OygGC8kIcDhXX+6yAZRGLqwi2CmEXCbLQixeGUgYeR+Qwlppqmo7DIDr8XibtEBZp+fJcoYpoatp5qwLMEdcqQ==", + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dev": true, "license": "MIT", "dependencies": { @@ -3791,10 +3792,11 @@ "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", + "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.6", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -3815,9 +3817,12 @@ "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.7", + "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -3897,15 +3902,16 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4004,19 +4010,19 @@ } }, "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", + "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", + "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/js": "9.18.0", + "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", @@ -4405,29 +4411,29 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", - "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", + "version": "7.37.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz", + "integrity": "sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==", "dev": true, "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", + "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", + "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", + "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "engines": { @@ -4736,9 +4742,9 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -4746,7 +4752,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -4780,9 +4786,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "dev": true, "license": "ISC", "dependencies": { @@ -4991,22 +4997,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", - "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", - "dunder-proto": "^1.0.0", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", + "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "math-intrinsics": "^1.0.0" + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5025,6 +5031,20 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", @@ -5529,13 +5549,16 @@ "license": "MIT" }, "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", + "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5711,13 +5734,16 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -6062,17 +6088,17 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz", - "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", "has-symbols": "^1.1.0", - "reflect.getprototypeof": "^1.0.8", "set-function-name": "^2.0.2" }, "engines": { @@ -7645,6 +7671,24 @@ "node": ">= 0.8.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8319,19 +8363,19 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz", - "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "dunder-proto": "^1.0.1", - "es-abstract": "^1.23.6", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" }, "engines": { @@ -8342,15 +8386,17 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "set-function-name": "^2.0.2" }, "engines": { @@ -8499,6 +8545,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", @@ -8575,6 +8638,21 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -9208,16 +9286,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/tsd": { @@ -9384,9 +9462,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -9398,15 +9476,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.18.1.tgz", - "integrity": "sha512-Mlaw6yxuaDEPQvb/2Qwu3/TfgeBHy9iTJ3mTwe7OvpPmF6KPQjVOfGyEJpPv6Ez2C34OODChhXrzYw/9phI0MQ==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.1.tgz", + "integrity": "sha512-LKPUQpdEMVOeKluHi8md7rwLcoXHhwvWp3x+sJkMuq3gGm9yaYJtPo8sRZSblMFJ5pcOGCAak/scKf1mvZDlQw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.18.1", - "@typescript-eslint/parser": "8.18.1", - "@typescript-eslint/utils": "8.18.1" + "@typescript-eslint/eslint-plugin": "8.19.1", + "@typescript-eslint/parser": "8.19.1", + "@typescript-eslint/utils": "8.19.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9483,9 +9561,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -9504,7 +9582,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -9836,9 +9914,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", - "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true, "license": "ISC", "bin": { diff --git a/deps/undici/src/package.json b/deps/undici/src/package.json index 5ce5b97f9352f8..95cb149f0d1dc9 100644 --- a/deps/undici/src/package.json +++ b/deps/undici/src/package.json @@ -1,6 +1,6 @@ { "name": "undici", - "version": "7.2.0", + "version": "7.2.1", "description": "An HTTP/1.1 client, written from scratch for Node.js", "homepage": "https://undici.nodejs.org", "bugs": { @@ -107,7 +107,7 @@ "prepare": "husky && node ./scripts/platform-shell.js" }, "devDependencies": { - "@fastify/busboy": "3.1.0", + "@fastify/busboy": "3.1.1", "@matteo.collina/tspl": "^0.1.1", "@sinonjs/fake-timers": "^12.0.0", "@types/node": "^18.19.50", diff --git a/deps/undici/src/scripts/release.js b/deps/undici/src/scripts/release.js index ad8e84686697ae..dd3e86eb5dc760 100644 --- a/deps/undici/src/scripts/release.js +++ b/deps/undici/src/scripts/release.js @@ -8,7 +8,7 @@ const generateReleaseNotes = async ({ github, owner, repo, versionTag, defaultBr repo }) - const previousRelease = releases.find((r) => r.tag_name.startsWith('v6')) + const previousRelease = releases.find((r) => r.tag_name.startsWith('v7')) const { data: { body } } = await github.rest.repos.generateReleaseNotes({ owner, diff --git a/deps/undici/src/types/errors.d.ts b/deps/undici/src/types/errors.d.ts index fdf806e90a13c9..387420db040bd6 100644 --- a/deps/undici/src/types/errors.d.ts +++ b/deps/undici/src/types/errors.d.ts @@ -33,6 +33,22 @@ declare namespace Errors { code: 'UND_ERR_BODY_TIMEOUT' } + export class ResponseError extends UndiciError { + constructor ( + message: string, + code: number, + options: { + headers?: IncomingHttpHeaders | string[] | null, + body?: null | Record | string + } + ) + name: 'ResponseError' + code: 'UND_ERR_RESPONSE' + statusCode: number + body: null | Record | string + headers: IncomingHttpHeaders | string[] | null + } + export class ResponseStatusCodeError extends UndiciError { constructor ( message?: string, diff --git a/deps/undici/undici.js b/deps/undici/undici.js index 993e0734f28c1c..caf0e5247cbd82 100644 --- a/deps/undici/undici.js +++ b/deps/undici/undici.js @@ -12468,7 +12468,7 @@ var require_util3 = __commonJS({ if (buffer.byteLength === buffer.buffer.byteLength) { return buffer.buffer; } - return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); + return new Uint8Array(buffer).buffer; } __name(toArrayBuffer, "toArrayBuffer"); function isValidSubprotocol(protocol) { @@ -12717,6 +12717,7 @@ var require_receiver = __commonJS({ __name(this, "ByteParser"); } #buffers = []; + #fragmentsBytes = 0; #byteOffset = 0; #loop = false; #state = parserStates.INFO; @@ -12846,11 +12847,9 @@ var require_receiver = __commonJS({ this.#state = parserStates.INFO; } else { if (!this.#info.compressed) { - this.#fragments.push(body); + this.writeFragments(body); if (!this.#info.fragmented && this.#info.fin) { - const fullMessage = Buffer.concat(this.#fragments); - websocketMessageReceived(this.#handler, this.#info.binaryType, fullMessage); - this.#fragments.length = 0; + websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments()); } this.#state = parserStates.INFO; } else { @@ -12859,17 +12858,16 @@ var require_receiver = __commonJS({ failWebsocketConnection(this.#handler, 1007, error.message); return; } - this.#fragments.push(data); + this.writeFragments(data); if (!this.#info.fin) { this.#state = parserStates.INFO; this.#loop = true; this.run(callback); return; } - websocketMessageReceived(this.#handler, this.#info.binaryType, Buffer.concat(this.#fragments)); + websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments()); this.#loop = true; this.#state = parserStates.INFO; - this.#fragments.length = 0; this.run(callback); }); this.#loop = false; @@ -12890,29 +12888,54 @@ var require_receiver = __commonJS({ } else if (n === 0) { return emptyBuffer; } - if (this.#buffers[0].length === n) { - this.#byteOffset -= this.#buffers[0].length; + this.#byteOffset -= n; + const first = this.#buffers[0]; + if (first.length > n) { + this.#buffers[0] = first.subarray(n, first.length); + return first.subarray(0, n); + } else if (first.length === n) { return this.#buffers.shift(); - } - const buffer = Buffer.allocUnsafe(n); - let offset = 0; - while (offset !== n) { - const next = this.#buffers[0]; - const { length } = next; - if (length + offset === n) { - buffer.set(this.#buffers.shift(), offset); - break; - } else if (length + offset > n) { - buffer.set(next.subarray(0, n - offset), offset); - this.#buffers[0] = next.subarray(n - offset); - break; - } else { - buffer.set(this.#buffers.shift(), offset); - offset += next.length; + } else { + let offset = 0; + const buffer = Buffer.allocUnsafeSlow(n); + while (offset !== n) { + const next = this.#buffers[0]; + const length = next.length; + if (length + offset === n) { + buffer.set(this.#buffers.shift(), offset); + break; + } else if (length + offset > n) { + buffer.set(next.subarray(0, n - offset), offset); + this.#buffers[0] = next.subarray(n - offset); + break; + } else { + buffer.set(this.#buffers.shift(), offset); + offset += length; + } } + return buffer; } - this.#byteOffset -= n; - return buffer; + } + writeFragments(fragment) { + this.#fragmentsBytes += fragment.length; + this.#fragments.push(fragment); + } + consumeFragments() { + const fragments = this.#fragments; + if (fragments.length === 1) { + this.#fragmentsBytes = 0; + return fragments.shift(); + } + let offset = 0; + const output = Buffer.allocUnsafeSlow(this.#fragmentsBytes); + for (let i = 0; i < fragments.length; ++i) { + const buffer = fragments[i]; + output.set(buffer, offset); + offset += buffer.length; + } + this.#fragments = []; + this.#fragmentsBytes = 0; + return output; } parseCloseBody(data) { assert(data.length !== 1); diff --git a/src/undici_version.h b/src/undici_version.h index 91bc8aabba1cce..6295756809ea68 100644 --- a/src/undici_version.h +++ b/src/undici_version.h @@ -2,5 +2,5 @@ // Refer to tools/dep_updaters/update-undici.sh #ifndef SRC_UNDICI_VERSION_H_ #define SRC_UNDICI_VERSION_H_ -#define UNDICI_VERSION "7.2.0" +#define UNDICI_VERSION "7.2.1" #endif // SRC_UNDICI_VERSION_H_ From fc11189cbd2708811570f3db0b2be32c55f8f05e Mon Sep 17 00:00:00 2001 From: Jacob Smith <3012099+JakobJingleheimer@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:24:41 +0100 Subject: [PATCH 047/208] doc: correct customization hook types & clarify descriptions PR-URL: https://github.com/nodejs/node/pull/56454 Reviewed-By: Geoffrey Booth Reviewed-By: James M Snell Reviewed-By: Matteo Collina --- doc/api/module.md | 15 ++++++---- lib/internal/modules/customization_hooks.js | 33 +++++++++++++++++---- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/doc/api/module.md b/doc/api/module.md index 3ce481fbf6b3b4..6f399fb07a44ee 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -1036,13 +1036,14 @@ changes: * `nextResolve` {Function} The subsequent `resolve` hook in the chain, or the Node.js default `resolve` hook after the last user-supplied `resolve` hook * `specifier` {string} - * `context` {Object} + * `context` {Object|undefined} When omitted, the defaults are provided. When provided, defaults + are merged in with preference to the provided properties. * Returns: {Object|Promise} The asynchronous version takes either an object containing the following properties, or a `Promise` that will resolve to such an object. The synchronous version only accepts an object returned synchronously. - * `format` {string|null|undefined} A hint to the load hook (it might be - ignored) - `'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'` + * `format` {string|null|undefined} A hint to the `load` hook (it might be ignored). It can be a + module format (such as `'commonjs'` or `'module'`) or an arbitrary value like `'css'` or + `'yaml'`. * `importAttributes` {Object|undefined} The import attributes to use when caching the module (optional; if excluded the input will be used) * `shortCircuit` {undefined|boolean} A signal that this hook intends to @@ -1145,12 +1146,14 @@ changes: * `context` {Object} * `conditions` {string\[]} Export conditions of the relevant `package.json` * `format` {string|null|undefined} The format optionally supplied by the - `resolve` hook chain + `resolve` hook chain. This can be any string value as an input; input values do not need to + conform to the list of acceptable return values described below. * `importAttributes` {Object} * `nextLoad` {Function} The subsequent `load` hook in the chain, or the Node.js default `load` hook after the last user-supplied `load` hook * `url` {string} - * `context` {Object} + * `context` {Object|undefined} When omitted, defaults are provided. When provided, defaults are + merged in with preference to the provided properties. * Returns: {Object|Promise} The asynchronous version takes either an object containing the following properties, or a `Promise` that will resolve to such an object. The synchronous version only accepts an object returned synchronously. diff --git a/lib/internal/modules/customization_hooks.js b/lib/internal/modules/customization_hooks.js index c7a7a6d53dffd8..9570f52ddc5884 100644 --- a/lib/internal/modules/customization_hooks.js +++ b/lib/internal/modules/customization_hooks.js @@ -25,17 +25,40 @@ let debug = require('internal/util/debuglog').debuglog('module_hooks', (fn) => { debug = fn; }); -/** @typedef {import('internal/modules/cjs/loader.js').Module} Module */ /** - * @typedef {(specifier: string, context: ModuleResolveContext, nextResolve: ResolveHook) - * => ModuleResolveResult} ResolveHook - * @typedef {(url: string, context: ModuleLoadContext, nextLoad: LoadHook) - * => ModuleLoadResult} LoadHook + * @typedef {import('internal/modules/cjs/loader.js').Module} Module + */ +/** + * @typedef {( + * specifier: string, + * context: Partial, + * ) => ModuleResolveResult + * } NextResolve + * @typedef {( + * specifier: string, + * context: ModuleResolveContext, + * nextResolve: NextResolve, + * ) => ModuleResolveResult + * } ResolveHook + * @typedef {( + * url: string, + * context: Partial, + * ) => ModuleLoadResult + * } NextLoad + * @typedef {( + * url: string, + * context: ModuleLoadContext, + * nextLoad: NextLoad, + * ) => ModuleLoadResult + * } LoadHook */ // Use arrays for better insertion and iteration performance, we don't care // about deletion performance as much. + +/** @type {ResolveHook[]} */ const resolveHooks = []; +/** @type {LoadHook[]} */ const loadHooks = []; const hookId = Symbol('kModuleHooksIdKey'); let nextHookId = 0; From 732744cc7667ef33289516dd3ecbd24ffbadc2e1 Mon Sep 17 00:00:00 2001 From: Carlos Espa <43477095+Ceres6@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:24:30 +0100 Subject: [PATCH 048/208] src,worker: add isInternalWorker PR-URL: https://github.com/nodejs/node/pull/56469 Reviewed-By: Jacob Smith Reviewed-By: James M Snell Reviewed-By: Bryan English --- doc/api/worker_threads.md | 38 ++++++++++++++++++++++ lib/internal/worker.js | 2 ++ lib/worker_threads.js | 2 ++ src/node_worker.cc | 19 +++++++++-- src/node_worker.h | 5 ++- test/fixtures/loader-is-internal-thread.js | 3 ++ test/fixtures/worker-is-internal-thread.js | 3 ++ test/parallel/test-is-internal-thread.mjs | 36 ++++++++++++++++++++ 8 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/loader-is-internal-thread.js create mode 100644 test/fixtures/worker-is-internal-thread.js create mode 100644 test/parallel/test-is-internal-thread.mjs diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index c1ac8fdfc7717c..91e8b6d49f1482 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -100,6 +100,44 @@ if (isMainThread) { } ``` +## `worker.isInternalThread` + + + +* {boolean} + +Is `true` if this code is running inside of an internal [`Worker`][] thread (e.g the loader thread). + +```bash +node --experimental-loader ./loader.js main.js +``` + +```cjs +// loader.js +const { isInternalThread } = require('node:worker_threads'); +console.log(isInternalThread); // true +``` + +```mjs +// loader.js +import { isInternalThread } from 'node:worker_threads'; +console.log(isInternalThread); // true +``` + +```cjs +// main.js +const { isInternalThread } = require('node:worker_threads'); +console.log(isInternalThread); // false +``` + +```mjs +// main.js +import { isInternalThread } from 'node:worker_threads'; +console.log(isInternalThread); // false +``` + ## `worker.isMainThread` + +* `condition` {Function|AsyncFunction} An assertion function that is invoked + periodically until it completes successfully or the defined polling timeout + elapses. Successful completion is defined as not throwing or rejecting. This + function does not accept any arguments, and is allowed to return any value. +* `options` {Object} An optional configuration object for the polling operation. + The following properties are supported: + * `interval` {number} The number of milliseconds to wait after an unsuccessful + invocation of `condition` before trying again. **Default:** `50`. + * `timeout` {number} The poll timeout in milliseconds. If `condition` has not + succeeded by the time this elapses, an error occurs. **Default:** `1000`. +* Returns: {Promise} Fulfilled with the value returned by `condition`. + +This method polls a `condition` function until that function either returns +successfully or the operation times out. + ## Class: `SuiteContext` + +* +* +* +* +* + + + +#### Project contacts + +* @marco-ippolito From 00d49649dabac5dd11e05d2558b2cb0edb607c1b Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Thu, 16 Jan 2025 17:44:06 -0500 Subject: [PATCH 071/208] doc: tweak info on reposts in ambassador program Signed-off-by: Michael Dawson PR-URL: https://github.com/nodejs/node/pull/56589 Reviewed-By: Rafael Gonzaga Reviewed-By: James M Snell Reviewed-By: Marco Ippolito --- doc/contributing/advocacy-ambassador-program.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/contributing/advocacy-ambassador-program.md b/doc/contributing/advocacy-ambassador-program.md index 058a4d7c45cd69..76f29b73586691 100644 --- a/doc/contributing/advocacy-ambassador-program.md +++ b/doc/contributing/advocacy-ambassador-program.md @@ -94,11 +94,14 @@ process. An ambassador can request promotion of content in the following ways: * Posting a link to the content in the "what's new" issue in nodejs/ambassadors so that it goes out on the news feed. -Foundation staff will repost the social media post -without any need for validation based on the request coming from -an ambassador. These requests can be made through the existing social channel -in the OpenJS Slack. For that reason and for communication purposes and -collaboration opportunities, ambassadors should be members of the +For accounts managed by foundation staff, the staff will repost the social +media post without any need for validation based on the request coming from +an ambassador. For accounts managed by the project with an approval process, +(for example bluesky) documentation for the approval process will indicate +that repost requests from ambassadors should generally be approved. These +requests can be made through the existing social channel in the OpenJS Slack. +For that reason and for communication purposes and collaboration opportunities, +ambassadors should be members of the [OpenJS Slack](https://slack-invite.openjsf.org/). ## Messages and topics to promote From 22f1518d2f03a10ebac3b95e7ec1dd61a566c3f8 Mon Sep 17 00:00:00 2001 From: Pietro Marchini Date: Fri, 17 Jan 2025 11:34:55 +0100 Subject: [PATCH 072/208] test_runner: remove unused errors PR-URL: https://github.com/nodejs/node/pull/56607 Reviewed-By: Colin Ihrig Reviewed-By: Jacob Smith --- doc/api/errors.md | 38 +++++++++++++++++++------------------- lib/internal/errors.js | 15 --------------- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index 3deab7397c6ec2..24e2290e472b6b 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -2829,25 +2829,6 @@ An unspecified or non-specific system error has occurred within the Node.js process. The error object will have an `err.info` object property with additional details. - - -### `ERR_TAP_LEXER_ERROR` - -An error representing a failing lexer state. - - - -### `ERR_TAP_PARSER_ERROR` - -An error representing a failing parser state. Additional information about -the token causing the error is available via the `cause` property. - - - -### `ERR_TAP_VALIDATION_ERROR` - -This error represents a failed TAP validation. - ### `ERR_TEST_FAILURE` @@ -3883,6 +3864,25 @@ removed: v10.0.0 Used when an attempt is made to use a readable stream that has not implemented [`readable._read()`][]. + + +### `ERR_TAP_LEXER_ERROR` + +An error representing a failing lexer state. + + + +### `ERR_TAP_PARSER_ERROR` + +An error representing a failing parser state. Additional information about +the token causing the error is available via the `cause` property. + + + +### `ERR_TAP_VALIDATION_ERROR` + +This error represents a failed TAP validation. + ### `ERR_TLS_RENEGOTIATION_FAILED` diff --git a/lib/internal/errors.js b/lib/internal/errors.js index d990f8d5a106aa..bda50797124758 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1739,21 +1739,6 @@ E('ERR_STREAM_WRAP', 'Stream has StringDecoder set or is in objectMode', Error); E('ERR_STREAM_WRITE_AFTER_END', 'write after end', Error); E('ERR_SYNTHETIC', 'JavaScript Callstack', Error); E('ERR_SYSTEM_ERROR', 'A system error occurred', SystemError, HideStackFramesError); -E('ERR_TAP_LEXER_ERROR', function(errorMsg) { - hideInternalStackFrames(this); - return errorMsg; -}, Error); -E('ERR_TAP_PARSER_ERROR', function(errorMsg, details, tokenCausedError, source) { - hideInternalStackFrames(this); - this.cause = tokenCausedError; - const { column, line, start, end } = tokenCausedError.location; - const errorDetails = `${details} at line ${line}, column ${column} (start ${start}, end ${end})`; - return errorMsg + errorDetails; -}, SyntaxError); -E('ERR_TAP_VALIDATION_ERROR', function(errorMsg) { - hideInternalStackFrames(this); - return errorMsg; -}, Error); E('ERR_TEST_FAILURE', function(error, failureType) { hideInternalStackFrames(this); assert(typeof failureType === 'string' || typeof failureType === 'symbol', From cee63dcf35aef94c5e423218f979f53bad4e7db3 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Fri, 17 Jan 2025 13:42:50 +0100 Subject: [PATCH 073/208] module: add ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX PR-URL: https://github.com/nodejs/node/pull/56610 Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater Reviewed-By: Geoffrey Booth Reviewed-By: Ethan Arrowood Reviewed-By: Chengzhong Wu --- doc/api/cli.md | 4 +- doc/api/errors.md | 20 ++++++++-- lib/internal/errors.js | 1 + lib/internal/modules/typescript.js | 17 +++++++- lib/internal/process/execution.js | 40 +++++++------------ test/es-module/test-typescript-eval.mjs | 36 +++++++++++++---- test/es-module/test-typescript.mjs | 10 +++++ .../typescript/ts/test-invalid-syntax.ts | 3 ++ 8 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 test/fixtures/typescript/ts/test-invalid-syntax.ts diff --git a/doc/api/cli.md b/doc/api/cli.md index da2506326fc5bd..dd058306aa26f7 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1390,7 +1390,8 @@ Node.js will try to detect the syntax with the following steps: 1. Run the input as CommonJS. 2. If step 1 fails, run the input as an ES module. 3. If step 2 fails with a SyntaxError, strip the types. -4. If step 3 fails with an error code [`ERR_INVALID_TYPESCRIPT_SYNTAX`][], +4. If step 3 fails with an error code [`ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`][] + or [`ERR_INVALID_TYPESCRIPT_SYNTAX`][], throw the error from step 2, including the TypeScript error in the message, else run as CommonJS. 5. If step 4 fails, run the input as an ES module. @@ -3708,6 +3709,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12 [`Buffer`]: buffer.md#class-buffer [`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html [`ERR_INVALID_TYPESCRIPT_SYNTAX`]: errors.md#err_invalid_typescript_syntax +[`ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`]: errors.md#err_unsupported_typescript_syntax [`NODE_OPTIONS`]: #node_optionsoptions [`NO_COLOR`]: https://no-color.org [`SlowBuffer`]: buffer.md#class-slowbuffer diff --git a/doc/api/errors.md b/doc/api/errors.md index 24e2290e472b6b..fcd351b6993cb5 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -2095,11 +2095,13 @@ does not consist of exactly two elements. added: - v23.0.0 - v22.10.0 +changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/56610 + description: This error is no longer thrown on valid yet unsupported syntax. --> -The provided TypeScript syntax is not valid or unsupported. -This could happen when using TypeScript syntax that requires -transformation with [type-stripping][]. +The provided TypeScript syntax is not valid. @@ -3116,6 +3118,18 @@ try { } ``` + + +### `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX` + + + +The provided TypeScript syntax is unsupported. +This could happen when using TypeScript syntax that requires +transformation with [type-stripping][]. + ### `ERR_USE_AFTER_CLOSE` diff --git a/lib/internal/errors.js b/lib/internal/errors.js index bda50797124758..d6b2ceb5962351 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1838,6 +1838,7 @@ E('ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING', E('ERR_UNSUPPORTED_RESOLVE_REQUEST', 'Failed to resolve module specifier "%s" from "%s": Invalid relative URL or base scheme is not hierarchical.', TypeError); +E('ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX', '%s', SyntaxError); E('ERR_USE_AFTER_CLOSE', '%s was closed', Error); // This should probably be a `TypeError`. diff --git a/lib/internal/modules/typescript.js b/lib/internal/modules/typescript.js index 993fd3ff72d74d..689788b09853c4 100644 --- a/lib/internal/modules/typescript.js +++ b/lib/internal/modules/typescript.js @@ -12,8 +12,10 @@ const { assertTypeScript, isUnderNodeModules, kEmptyObject } = require('internal/util'); const { + ERR_INTERNAL_ASSERTION, ERR_INVALID_TYPESCRIPT_SYNTAX, ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING, + ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, } = require('internal/errors').codes; const { getOptionValue } = require('internal/options'); const assert = require('internal/assert'); @@ -49,7 +51,20 @@ function parseTypeScript(source, options) { try { return parse(source, options); } catch (error) { - throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message); + /** + * Amaro v0.3.0 (from SWC v1.10.7) throws an object with `message` and `code` properties. + * It allows us to distinguish between invalid syntax and unsupported syntax. + */ + switch (error.code) { + case 'UnsupportedSyntax': + throw new ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX(error.message); + case 'InvalidSyntax': + throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message); + default: + // SWC will throw strings when something goes wrong. + // Check if has the `message` property or treat it as a string. + throw new ERR_INTERNAL_ASSERTION(error.message ?? error); + } } } diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js index f5b19d5a7e8c9c..d4d7a604851ef1 100644 --- a/lib/internal/process/execution.js +++ b/lib/internal/process/execution.js @@ -35,7 +35,7 @@ const { getOptionValue } = require('internal/options'); const { makeContextifyScript, runScriptInThisContext, } = require('internal/vm'); -const { emitExperimentalWarning, isError } = require('internal/util'); +const { emitExperimentalWarning } = require('internal/util'); // shouldAbortOnUncaughtToggle is a typed array for faster // communication with JS. const { shouldAbortOnUncaughtToggle } = internalBinding('util'); @@ -254,10 +254,6 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal try { compiledScript = compileScript(name, source, baseUrl); } catch (originalError) { - // If it's not a SyntaxError, rethrow it. - if (!isError(originalError) || originalError.name !== 'SyntaxError') { - throw originalError; - } try { sourceToRun = stripTypeScriptModuleTypes(source, name, false); // Retry the CJS/ESM syntax detection after stripping the types. @@ -270,15 +266,14 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal // Emit the experimental warning after the code was successfully evaluated. emitExperimentalWarning('Type Stripping'); } catch (tsError) { - // If its not an error, or it's not an invalid typescript syntax error, rethrow it. - if (!isError(tsError) || tsError?.code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX') { - throw tsError; + // If it's invalid or unsupported TypeScript syntax, rethrow the original error + // with the TypeScript error message added to the stack. + if (tsError.code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' || tsError.code === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') { + originalError.stack = decorateCJSErrorWithTSMessage(originalError.stack, tsError.message); + throw originalError; } - try { - originalError.stack = decorateCJSErrorWithTSMessage(originalError.stack, tsError.message); - } catch { /* Ignore potential errors coming from `stack` getter/setter */ } - throw originalError; + throw tsError; } } @@ -322,28 +317,23 @@ function evalTypeScriptModuleEntryPoint(source, print) { // Compile the module to check for syntax errors. moduleWrap = loader.createModuleWrap(source, url); } catch (originalError) { - // If it's not a SyntaxError, rethrow it. - if (!isError(originalError) || originalError.name !== 'SyntaxError') { - throw originalError; - } - let strippedSource; try { - strippedSource = stripTypeScriptModuleTypes(source, url, false); + const strippedSource = stripTypeScriptModuleTypes(source, url, false); // If the moduleWrap was successfully created, execute the module job. // outside the try-catch block to avoid catching runtime errors. moduleWrap = loader.createModuleWrap(strippedSource, url); // Emit the experimental warning after the code was successfully compiled. emitExperimentalWarning('Type Stripping'); } catch (tsError) { - // If its not an error, or it's not an invalid typescript syntax error, rethrow it. - if (!isError(tsError) || tsError?.code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX') { - throw tsError; - } - try { + // If it's invalid or unsupported TypeScript syntax, rethrow the original error + // with the TypeScript error message added to the stack. + if (tsError.code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' || + tsError.code === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') { originalError.stack = `${tsError.message}\n\n${originalError.stack}`; - } catch { /* Ignore potential errors coming from `stack` getter/setter */ } + throw originalError; + } - throw originalError; + throw tsError; } } // If the moduleWrap was successfully created either with by just compiling diff --git a/test/es-module/test-typescript-eval.mjs b/test/es-module/test-typescript-eval.mjs index 5c6f25bec4df7d..bbbed8863de25a 100644 --- a/test/es-module/test-typescript-eval.mjs +++ b/test/es-module/test-typescript-eval.mjs @@ -102,33 +102,33 @@ test('expect fail eval TypeScript ESM syntax with input-type commonjs-typescript strictEqual(result.code, 1); }); -test('check syntax error is thrown when passing invalid syntax', async () => { +test('check syntax error is thrown when passing unsupported syntax', async () => { const result = await spawnPromisified(process.execPath, [ '--eval', 'enum Foo { A, B, C }']); strictEqual(result.stdout, ''); match(result.stderr, /SyntaxError/); - doesNotMatch(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/); + doesNotMatch(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/); strictEqual(result.code, 1); }); -test('check syntax error is thrown when passing invalid syntax with --input-type=module-typescript', async () => { +test('check syntax error is thrown when passing unsupported syntax with --input-type=module-typescript', async () => { const result = await spawnPromisified(process.execPath, [ '--input-type=module-typescript', '--eval', 'enum Foo { A, B, C }']); strictEqual(result.stdout, ''); - match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/); + match(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/); strictEqual(result.code, 1); }); -test('check syntax error is thrown when passing invalid syntax with --input-type=commonjs-typescript', async () => { +test('check syntax error is thrown when passing unsupported syntax with --input-type=commonjs-typescript', async () => { const result = await spawnPromisified(process.execPath, [ '--input-type=commonjs-typescript', '--eval', 'enum Foo { A, B, C }']); strictEqual(result.stdout, ''); - match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/); + match(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/); strictEqual(result.code, 1); }); @@ -140,7 +140,7 @@ test('should not parse TypeScript with --type-module=commonjs', async () => { strictEqual(result.stdout, ''); match(result.stderr, /SyntaxError/); - doesNotMatch(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/); + doesNotMatch(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/); strictEqual(result.code, 1); }); @@ -152,7 +152,7 @@ test('should not parse TypeScript with --type-module=module', async () => { strictEqual(result.stdout, ''); match(result.stderr, /SyntaxError/); - doesNotMatch(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/); + doesNotMatch(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/); strictEqual(result.code, 1); }); @@ -222,3 +222,23 @@ test('typescript CJS code is throwing a syntax error at runtime', async () => { strictEqual(result.stdout, ''); strictEqual(result.code, 1); }); + +test('check syntax error is thrown when passing invalid syntax with --input-type=commonjs-typescript', async () => { + const result = await spawnPromisified(process.execPath, [ + '--input-type=commonjs-typescript', + '--eval', + 'function foo(){ await Promise.resolve(1); }']); + strictEqual(result.stdout, ''); + match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/); + strictEqual(result.code, 1); +}); + +test('check syntax error is thrown when passing invalid syntax with --input-type=module-typescript', async () => { + const result = await spawnPromisified(process.execPath, [ + '--input-type=module-typescript', + '--eval', + 'function foo(){ await Promise.resolve(1); }']); + strictEqual(result.stdout, ''); + match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/); + strictEqual(result.code, 1); +}); diff --git a/test/es-module/test-typescript.mjs b/test/es-module/test-typescript.mjs index 81aed880bdcf51..74c4a0f120b758 100644 --- a/test/es-module/test-typescript.mjs +++ b/test/es-module/test-typescript.mjs @@ -321,3 +321,13 @@ test('execute a TypeScript loader and a .js file', async () => { match(result.stdout, /Hello, TypeScript!/); strictEqual(result.code, 0); }); + +test('execute invalid TypeScript syntax', async () => { + const result = await spawnPromisified(process.execPath, [ + fixtures.path('typescript/ts/test-invalid-syntax.ts'), + ]); + + match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/); + strictEqual(result.stdout, ''); + strictEqual(result.code, 1); +}); diff --git a/test/fixtures/typescript/ts/test-invalid-syntax.ts b/test/fixtures/typescript/ts/test-invalid-syntax.ts new file mode 100644 index 00000000000000..031bce938d27dc --- /dev/null +++ b/test/fixtures/typescript/ts/test-invalid-syntax.ts @@ -0,0 +1,3 @@ +function foo(): string { + await Promise.resolve(1); +} From 5d93002a14d506d202ce0fc93584022ee54ccc12 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 14 Jan 2025 13:35:54 +0100 Subject: [PATCH 074/208] test: add maxCount and gcOptions to gcUntil() PR-URL: https://github.com/nodejs/node/pull/56522 Reviewed-By: James M Snell Reviewed-By: Chengzhong Wu --- test/common/gc.js | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/test/common/gc.js b/test/common/gc.js index 82cc4c79edc3dd..87625068c2cbca 100644 --- a/test/common/gc.js +++ b/test/common/gc.js @@ -3,6 +3,8 @@ const wait = require('timers/promises').setTimeout; const assert = require('assert'); const common = require('../common'); +// TODO(joyeecheung): rewrite checkIfCollectable to use this too. +const { setImmediate: setImmediatePromisified } = require('timers/promises'); const gcTrackerMap = new WeakMap(); const gcTrackerTag = 'NODE_TEST_COMMON_GC_TRACKER'; @@ -40,32 +42,26 @@ function onGC(obj, gcListener) { /** * Repeatedly triggers garbage collection until a specified condition is met or a maximum number of attempts is reached. + * This utillity must be run in a Node.js instance that enables --expose-gc. * @param {string|Function} [name] - Optional name, used in the rejection message if the condition is not met. * @param {Function} condition - A function that returns true when the desired condition is met. + * @param {number} maxCount - Maximum number of garbage collections that should be tried. + * @param {object} gcOptions - Options to pass into the global gc() function. * @returns {Promise} A promise that resolves when the condition is met, or rejects after 10 failed attempts. */ -function gcUntil(name, condition) { - if (typeof name === 'function') { - condition = name; - name = undefined; - } - return new Promise((resolve, reject) => { - let count = 0; - function gcAndCheck() { - setImmediate(() => { - count++; - global.gc(); - if (condition()) { - resolve(); - } else if (count < 10) { - gcAndCheck(); - } else { - reject(name === undefined ? undefined : 'Test ' + name + ' failed'); - } - }); +async function gcUntil(name, condition, maxCount = 10, gcOptions) { + for (let count = 0; count < maxCount; ++count) { + await setImmediatePromisified(); + if (gcOptions) { + await global.gc(gcOptions); + } else { + await global.gc(); // Passing in undefined is not the same as empty. } - gcAndCheck(); - }); + if (condition()) { + return; + } + } + throw new Error(`Test ${name} failed`); } // This function can be used to check if an object factor leaks or not, From 74717cb7fa21eb7d7c2abc579334f28c66d96fb0 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 9 Jan 2025 01:21:23 +0100 Subject: [PATCH 075/208] src: use cppgc to manage ContextifyContext This simplifies the memory management of ContextifyContext, making all references visible to V8. The destructors don't need to do anything because when the wrapper is going away, the context is already going away or otherwise it would've been holding the wrapper alive, so there's no need to reset the pointers in the context. Also, any global handles to the context would've been empty at this point, and the per-Environment context tracking code is capable of dealing with empty handles from contexts purged elsewhere. To this end, the context tracking code also purges empty handles from the list now, to prevent keeping too many empty handles around. PR-URL: https://github.com/nodejs/node/pull/56522 Reviewed-By: James M Snell Reviewed-By: Chengzhong Wu --- src/env.cc | 7 +- src/env.h | 1 + src/node_contextify.cc | 69 ++++++++++--------- src/node_contextify.h | 84 ++++++++++++++++++++---- test/parallel/test-inspector-contexts.js | 14 ++-- 5 files changed, 115 insertions(+), 60 deletions(-) diff --git a/src/env.cc b/src/env.cc index f0f97244fdef63..0eda889802710d 100644 --- a/src/env.cc +++ b/src/env.cc @@ -223,7 +223,12 @@ void AsyncHooks::InstallPromiseHooks(Local ctx) { : PersistentToLocal::Strong(js_promise_hooks_[3])); } +void Environment::PurgeTrackedEmptyContexts() { + std::erase_if(contexts_, [&](auto&& el) { return el.IsEmpty(); }); +} + void Environment::TrackContext(Local context) { + PurgeTrackedEmptyContexts(); size_t id = contexts_.size(); contexts_.resize(id + 1); contexts_[id].Reset(isolate_, context); @@ -232,7 +237,7 @@ void Environment::TrackContext(Local context) { void Environment::UntrackContext(Local context) { HandleScope handle_scope(isolate_); - std::erase_if(contexts_, [&](auto&& el) { return el.IsEmpty(); }); + PurgeTrackedEmptyContexts(); for (auto it = contexts_.begin(); it != contexts_.end(); it++) { if (Local saved_context = PersistentToLocal::Weak(isolate_, *it); saved_context == context) { diff --git a/src/env.h b/src/env.h index ec5b608cede6a1..1929450b8fe393 100644 --- a/src/env.h +++ b/src/env.h @@ -1085,6 +1085,7 @@ class Environment final : public MemoryRetainer { const char* errmsg); void TrackContext(v8::Local context); void UntrackContext(v8::Local context); + void PurgeTrackedEmptyContexts(); std::list loaded_addons_; v8::Isolate* const isolate_; diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 77d35675827c67..ab6659d8cdccc6 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -118,8 +118,9 @@ Local Uint32ToName(Local context, uint32_t index) { } // anonymous namespace -BaseObjectPtr ContextifyContext::New( - Environment* env, Local sandbox_obj, ContextOptions* options) { +ContextifyContext* ContextifyContext::New(Environment* env, + Local sandbox_obj, + ContextOptions* options) { Local object_template; HandleScope scope(env->isolate()); CHECK_IMPLIES(sandbox_obj.IsEmpty(), options->vanilla); @@ -140,21 +141,25 @@ BaseObjectPtr ContextifyContext::New( if (!(CreateV8Context(env->isolate(), object_template, snapshot_data, queue) .ToLocal(&v8_context))) { // Allocation failure, maximum call stack size reached, termination, etc. - return BaseObjectPtr(); + return {}; } return New(v8_context, env, sandbox_obj, options); } -void ContextifyContext::MemoryInfo(MemoryTracker* tracker) const {} +void ContextifyContext::Trace(cppgc::Visitor* visitor) const { + CppgcMixin::Trace(visitor); + visitor->Trace(context_); +} ContextifyContext::ContextifyContext(Environment* env, Local wrapper, Local v8_context, ContextOptions* options) - : BaseObject(env, wrapper), - microtask_queue_(options->own_microtask_queue + : microtask_queue_(options->own_microtask_queue ? options->own_microtask_queue.release() : nullptr) { + CppgcMixin::Wrap(this, env, wrapper); + context_.Reset(env->isolate(), v8_context); // This should only be done after the initial initializations of the context // global object is finished. @@ -162,19 +167,6 @@ ContextifyContext::ContextifyContext(Environment* env, ContextEmbedderIndex::kContextifyContext)); v8_context->SetAlignedPointerInEmbedderData( ContextEmbedderIndex::kContextifyContext, this); - // It's okay to make this reference weak - V8 would create an internal - // reference to this context via the constructor of the wrapper. - // As long as the wrapper is alive, it's constructor is alive, and so - // is the context. - context_.SetWeak(); -} - -ContextifyContext::~ContextifyContext() { - Isolate* isolate = env()->isolate(); - HandleScope scope(isolate); - - env()->UnassignFromContext(PersistentToLocal::Weak(isolate, context_)); - context_.Reset(); } void ContextifyContext::InitializeGlobalTemplates(IsolateData* isolate_data) { @@ -251,11 +243,10 @@ MaybeLocal ContextifyContext::CreateV8Context( return scope.Escape(ctx); } -BaseObjectPtr ContextifyContext::New( - Local v8_context, - Environment* env, - Local sandbox_obj, - ContextOptions* options) { +ContextifyContext* ContextifyContext::New(Local v8_context, + Environment* env, + Local sandbox_obj, + ContextOptions* options) { HandleScope scope(env->isolate()); CHECK_IMPLIES(sandbox_obj.IsEmpty(), options->vanilla); // This only initializes part of the context. The primordials are @@ -263,7 +254,7 @@ BaseObjectPtr ContextifyContext::New( // things down significantly and they are only needed in rare occasions // in the vm contexts. if (InitializeContextRuntime(v8_context).IsNothing()) { - return BaseObjectPtr(); + return {}; } Local main_context = env->context(); @@ -300,7 +291,7 @@ BaseObjectPtr ContextifyContext::New( info.origin = *origin_val; } - BaseObjectPtr result; + ContextifyContext* result; Local wrapper; { Context::Scope context_scope(v8_context); @@ -315,7 +306,7 @@ BaseObjectPtr ContextifyContext::New( ctor_name, static_cast(v8::DontEnum)) .IsNothing()) { - return BaseObjectPtr(); + return {}; } } @@ -328,7 +319,7 @@ BaseObjectPtr ContextifyContext::New( env->host_defined_option_symbol(), options->host_defined_options_id) .IsNothing()) { - return BaseObjectPtr(); + return {}; } env->AssignToContext(v8_context, nullptr, info); @@ -336,13 +327,15 @@ BaseObjectPtr ContextifyContext::New( if (!env->contextify_wrapper_template() ->NewInstance(v8_context) .ToLocal(&wrapper)) { - return BaseObjectPtr(); + return {}; } - result = - MakeBaseObject(env, wrapper, v8_context, options); - // The only strong reference to the wrapper will come from the sandbox. - result->MakeWeak(); + result = cppgc::MakeGarbageCollected( + env->isolate()->GetCppHeap()->GetAllocationHandle(), + env, + wrapper, + v8_context, + options); } Local wrapper_holder = @@ -352,7 +345,7 @@ BaseObjectPtr ContextifyContext::New( ->SetPrivate( v8_context, env->contextify_context_private_symbol(), wrapper) .IsNothing()) { - return BaseObjectPtr(); + return {}; } // Assign host_defined_options_id to the sandbox object or the global object @@ -364,7 +357,7 @@ BaseObjectPtr ContextifyContext::New( env->host_defined_option_symbol(), options->host_defined_options_id) .IsNothing()) { - return BaseObjectPtr(); + return {}; } return result; } @@ -438,7 +431,7 @@ void ContextifyContext::MakeContext(const FunctionCallbackInfo& args) { options.host_defined_options_id = args[6].As(); TryCatchScope try_catch(env); - BaseObjectPtr context_ptr = + ContextifyContext* context_ptr = ContextifyContext::New(env, sandbox, &options); if (try_catch.HasCaught()) { @@ -469,6 +462,10 @@ ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox( template ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo& args) { + // TODO(joyeecheung): it should be fine to simply use + // args.GetIsolate()->GetCurrentContext() and take the pointer at + // ContextEmbedderIndex::kContextifyContext, as V8 is supposed to + // push the creation context before invoking these callbacks. return Get(args.This()); } diff --git a/src/node_contextify.h b/src/node_contextify.h index d67968406d7b74..de69c22b0ebaed 100644 --- a/src/node_contextify.h +++ b/src/node_contextify.h @@ -23,17 +23,73 @@ struct ContextOptions { bool vanilla = false; }; -class ContextifyContext : public BaseObject { +/** + * The memory management of a vm context is as follows: + * + * user code + * │ + * As global proxy or ▼ + * ┌──────────────┐ kSandboxObject embedder data ┌────────────────┐ + * ┌─► │ V8 Context │────────────────────────────────►│ Wrapper holder │ + * │ └──────────────┘ └───────┬────────┘ + * │ ▲ Object constructor/creation context │ + * │ │ │ + * │ ┌──────┴────────────┐ contextify_context_private_symbol │ + * │ │ ContextifyContext │◄────────────────────────────────────┘ + * │ │ JS Wrapper │◄──────────► ┌─────────────────────────┐ + * │ └───────────────────┘ cppgc │ node::ContextifyContext │ + * │ │ C++ Object │ + * └──────────────────────────────────► └─────────────────────────┘ + * v8::TracedReference / ContextEmbedderIndex::kContextifyContext + * + * There are two possibilities for the "wrapper holder": + * + * 1. When vm.constants.DONT_CONTEXTIFY is used, the wrapper holder is the V8 + * context's global proxy object + * 2. Otherwise it's the arbitrary "sandbox object" that users pass into + * vm.createContext() or a new empty object created internally if they pass + * undefined. + * + * In 2, the global object of the new V8 context is created using + * global_object_template with interceptors that perform any requested + * operations on the global object in the context first on the sandbox object + * living outside of the new context, then fall back to the global proxy of the + * new context. + * + * It's critical for the user-accessible wrapper holder to keep the + * ContextifyContext wrapper alive via contextify_context_private_symbol + * so that the V8 context is always available to the user while they still + * hold the vm "context" object alive. + * + * It's also critical for the V8 context to keep the wrapper holder + * (specifically, the "sandbox object" if users pass one) as well as the + * node::ContextifyContext C++ object alive, so that when the code + * runs inside the object and accesses the global object, the interceptors + * can still access the "sandbox object" and perform operations + * on them, even if users already relinquish access to the outer + * "sandbox object". + * + * The v8::TracedReference and the ContextEmbedderIndex::kContextifyContext + * slot in the context only act as shortcuts between + * the node::ContextifyContext C++ object and the V8 context. + */ +class ContextifyContext final : CPPGC_MIXIN(ContextifyContext) { public: + SET_CPPGC_NAME(ContextifyContext) + void Trace(cppgc::Visitor* visitor) const final; + ContextifyContext(Environment* env, v8::Local wrapper, v8::Local v8_context, ContextOptions* options); - ~ContextifyContext(); - void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(ContextifyContext) - SET_SELF_SIZE(ContextifyContext) + // The destructors don't need to do anything because when the wrapper is + // going away, the context is already going away or otherwise it would've + // been holding the wrapper alive, so there's no need to reset the pointers + // in the context. Also, any global handles to the context would've been + // empty at this point, and the per-Environment context tracking code is + // capable of dealing with empty handles from contexts purged elsewhere. + ~ContextifyContext() = default; static v8::MaybeLocal CreateV8Context( v8::Isolate* isolate, @@ -48,7 +104,7 @@ class ContextifyContext : public BaseObject { Environment* env, const v8::Local& wrapper_holder); inline v8::Local context() const { - return PersistentToLocal::Default(env()->isolate(), context_); + return context_.Get(env()->isolate()); } inline v8::Local global_proxy() const { @@ -75,14 +131,14 @@ class ContextifyContext : public BaseObject { static void InitializeGlobalTemplates(IsolateData* isolate_data); private: - static BaseObjectPtr New(Environment* env, - v8::Local sandbox_obj, - ContextOptions* options); + static ContextifyContext* New(Environment* env, + v8::Local sandbox_obj, + ContextOptions* options); // Initialize a context created from CreateV8Context() - static BaseObjectPtr New(v8::Local ctx, - Environment* env, - v8::Local sandbox_obj, - ContextOptions* options); + static ContextifyContext* New(v8::Local ctx, + Environment* env, + v8::Local sandbox_obj, + ContextOptions* options); static bool IsStillInitializing(const ContextifyContext* ctx); static void MakeContext(const v8::FunctionCallbackInfo& args); @@ -140,7 +196,7 @@ class ContextifyContext : public BaseObject { static void IndexedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo& args); - v8::Global context_; + v8::TracedReference context_; std::unique_ptr microtask_queue_; }; diff --git a/test/parallel/test-inspector-contexts.js b/test/parallel/test-inspector-contexts.js index 9cdf2d0017c4be..3d6ee4d460e863 100644 --- a/test/parallel/test-inspector-contexts.js +++ b/test/parallel/test-inspector-contexts.js @@ -8,7 +8,7 @@ common.skipIfInspectorDisabled(); const assert = require('assert'); const vm = require('vm'); const { Session } = require('inspector'); - +const { gcUntil } = require('../common/gc'); const session = new Session(); session.connect(); @@ -66,8 +66,7 @@ async function testContextCreatedAndDestroyed() { // GC is unpredictable... console.log('Checking/waiting for GC.'); - while (!contextDestroyed) - global.gc(); + await gcUntil('context destruction', () => contextDestroyed, Infinity, { type: 'major', execution: 'async' }); console.log('Context destroyed.'); assert.strictEqual(contextDestroyed.params.executionContextId, id, @@ -98,8 +97,7 @@ async function testContextCreatedAndDestroyed() { // GC is unpredictable... console.log('Checking/waiting for GC again.'); - while (!contextDestroyed) - global.gc(); + await gcUntil('context destruction', () => contextDestroyed, Infinity, { type: 'major', execution: 'async' }); console.log('Other context destroyed.'); } @@ -124,8 +122,7 @@ async function testContextCreatedAndDestroyed() { // GC is unpredictable... console.log('Checking/waiting for GC a third time.'); - while (!contextDestroyed) - global.gc(); + await gcUntil('context destruction', () => contextDestroyed, Infinity, { type: 'major', execution: 'async' }); console.log('Context destroyed once again.'); } @@ -148,8 +145,7 @@ async function testContextCreatedAndDestroyed() { // GC is unpredictable... console.log('Checking/waiting for GC a fourth time.'); - while (!contextDestroyed) - global.gc(); + await gcUntil('context destruction', () => contextDestroyed, Infinity, { type: 'major', execution: 'async' }); console.log('Context destroyed a fourth time.'); } } From 90840ccc772974cda34e281ad8e6e3561c1254c8 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 17 Jan 2025 17:43:26 +0100 Subject: [PATCH 076/208] tools: fix permissions in `lint-release-proposal` workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56614 Reviewed-By: Michaël Zasso Reviewed-By: Ruy Adorno Reviewed-By: Luigi Pinca --- .github/workflows/lint-release-proposal.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint-release-proposal.yml b/.github/workflows/lint-release-proposal.yml index ecda2b616c0d02..9d8ba5998a7a5c 100644 --- a/.github/workflows/lint-release-proposal.yml +++ b/.github/workflows/lint-release-proposal.yml @@ -19,6 +19,8 @@ permissions: jobs: lint-release-commit: runs-on: ubuntu-latest + permissions: + pull-requests: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: From 2e45656eb2308f1a0c0b170a593f073e147f5a56 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Fri, 17 Jan 2025 12:58:47 -0500 Subject: [PATCH 077/208] crypto: add missing return value check Add return value check for call to SSL_CTX_add_client_CA to be consistent with other places it is called Fixed unused warning in one of the static analysis tools we use at Red Hat even though it is not being reported by coverity in the configuration we run. Signed-off-by: Michael Dawson PR-URL: https://github.com/nodejs/node/pull/56615 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- src/crypto/crypto_context.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 8f1e6dc7110b11..c7574e67f03f03 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -1164,7 +1164,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo& args) { X509* ca = sk_X509_value(extra_certs.get(), i); X509_STORE_add_cert(sc->GetCertStoreOwnedByThisSecureContext(), ca); - SSL_CTX_add_client_CA(sc->ctx_.get(), ca); + CHECK_EQ(1, SSL_CTX_add_client_CA(sc->ctx_.get(), ca)); } ret = true; From a500382d18793abbe7e301b32c8ac371e97d5460 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Fri, 17 Jan 2025 13:54:52 -0500 Subject: [PATCH 078/208] deps: update libuv to 1.50.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56616 Reviewed-By: Rafael Gonzaga Reviewed-By: Colin Ihrig Reviewed-By: Juan José Arboleda Reviewed-By: Santiago Gimeno Reviewed-By: Luigi Pinca Reviewed-By: Ulises Gascón Reviewed-By: Richard Lau --- deps/uv/.mailmap | 1 + deps/uv/AUTHORS | 4 +- deps/uv/CMakeLists.txt | 5 +- deps/uv/ChangeLog | 83 ++++- deps/uv/LINKS.md | 1 + deps/uv/MAINTAINERS.md | 6 +- deps/uv/Makefile.am | 3 +- deps/uv/SUPPORTED_PLATFORMS.md | 4 +- deps/uv/configure.ac | 2 +- deps/uv/docs/src/fs_event.rst | 5 + deps/uv/docs/src/misc.rst | 11 + deps/uv/docs/src/threading.rst | 25 ++ deps/uv/docs/src/threadpool.rst | 2 + deps/uv/docs/src/timer.rst | 14 +- deps/uv/docs/src/udp.rst | 14 + deps/uv/include/uv.h | 12 + deps/uv/include/uv/errno.h | 6 + deps/uv/include/uv/unix.h | 5 +- deps/uv/include/uv/version.h | 4 +- deps/uv/include/uv/win.h | 10 +- deps/uv/src/fs-poll.c | 3 + deps/uv/src/idna.c | 2 +- deps/uv/src/threadpool.c | 1 + deps/uv/src/unix/async.c | 83 +++++ deps/uv/src/unix/core.c | 50 ++- deps/uv/src/unix/darwin-proctitle.c | 20 +- deps/uv/src/unix/internal.h | 24 ++ deps/uv/src/unix/kqueue.c | 29 +- deps/uv/src/unix/linux.c | 75 +++-- deps/uv/src/unix/pipe.c | 30 +- deps/uv/src/unix/thread.c | 98 ++++++ deps/uv/src/unix/udp.c | 387 ++++++++++++------------ deps/uv/src/uv-common.c | 22 ++ deps/uv/src/uv-common.h | 20 ++ deps/uv/src/win/core.c | 110 +------ deps/uv/src/win/fs-event.c | 4 + deps/uv/src/win/fs.c | 205 ++++++++++++- deps/uv/src/win/pipe.c | 12 +- deps/uv/src/win/thread.c | 74 +++++ deps/uv/src/win/udp.c | 21 +- deps/uv/src/win/util.c | 92 ++++-- deps/uv/src/win/winapi.c | 13 - deps/uv/src/win/winapi.h | 108 +++---- deps/uv/src/win/winsock.h | 41 --- deps/uv/test/runner.c | 14 + deps/uv/test/test-fs-event.c | 9 +- deps/uv/test/test-fs.c | 54 ++++ deps/uv/test/test-idna.c | 26 +- deps/uv/test/test-list.h | 10 + deps/uv/test/test-pipe-getsockname.c | 9 + deps/uv/test/test-platform-output.c | 16 + deps/uv/test/test-spawn.c | 6 +- deps/uv/test/test-thread-name.c | 189 ++++++++++++ deps/uv/test/test-thread.c | 10 + deps/uv/test/test-udp-mmsg.c | 5 +- deps/uv/test/test-udp-multicast-join.c | 19 +- deps/uv/test/test-udp-multicast-join6.c | 1 + deps/uv/test/test-udp-try-send.c | 40 ++- 58 files changed, 1582 insertions(+), 567 deletions(-) create mode 100644 deps/uv/test/test-thread-name.c diff --git a/deps/uv/.mailmap b/deps/uv/.mailmap index 97f5d1f2c004c9..f5d5375e044e18 100644 --- a/deps/uv/.mailmap +++ b/deps/uv/.mailmap @@ -52,6 +52,7 @@ San-Tai Hsu Santiago Gimeno Saúl Ibarra Corretgé Saúl Ibarra Corretgé +Saúl Ibarra Corretgé Shigeki Ohtsu Shuowang (Wayne) Zhang TK-one diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 041b7aff610f57..39550bbc535eb2 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -588,5 +588,7 @@ Raihaan Shouhell Rialbat Adam Poul T Lomholt -dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Thad House +Julian A Avar C <28635807+julian-a-avar-c@users.noreply.github.com> +amcgoogan <105525867+amcgoogan@users.noreply.github.com> +Rafael Gonzaga diff --git a/deps/uv/CMakeLists.txt b/deps/uv/CMakeLists.txt index 28c6df25666967..af89db2dfc2762 100644 --- a/deps/uv/CMakeLists.txt +++ b/deps/uv/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0091) cmake_policy(SET CMP0091 NEW) # Enable MSVC_RUNTIME_LIBRARY setting @@ -186,7 +186,7 @@ set(uv_sources src/version.c) if(WIN32) - list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602 _CRT_DECLARE_NONSTDC_NAMES=0) + list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 _CRT_DECLARE_NONSTDC_NAMES=0) list(APPEND uv_libraries psapi user32 @@ -667,6 +667,7 @@ if(LIBUV_BUILD_TESTS) test/test-thread-affinity.c test/test-thread-equal.c test/test-thread.c + test/test-thread-name.c test/test-thread-priority.c test/test-threadpool-cancel.c test/test-threadpool.c diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index dc2dd2790c57d3..006a9e1b415de9 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,4 +1,85 @@ -2024.10.18, Version 1.49.2 (Stable) +2025.01.15, Version 1.50.0 (Stable) + +Changes since version 1.49.2: + +* ci: run macOS and iOS tests also on macOS 14 (Saúl Ibarra Corretgé) + +* unix,win: map ENOEXEC errno (Saúl Ibarra Corretgé) + +* test: skip multicast join test on ENOEXEC (Saúl Ibarra Corretgé) + +* ci: make sure the macOS firewall is disabled (Saúl Ibarra Corretgé) + +* darwin,test: squelch EBUSY error on multicast join (Saúl Ibarra Corretgé) + +* build: update minimum cmake to 3.10 (Ben Noordhuis) + +* kqueue: use EVFILT_USER for async if available (Jameson Nash) + +* unix,win: fix off-by-one in uv_wtf8_to_utf16() (Ben Noordhuis) + +* doc: add scala-native-loop to LINKS.md (Julian A Avar C) + +* unix: fix build breakage on haiku, openbsd, etc (Jeffrey H. Johnson) + +* kqueue: lower overhead in uv__io_check_fd (Andy Pan) + +* doc: move cjihrig back to active maintainers (cjihrig) + +* build(deps): bump actions/checkout from 3 to 4 (dependabot[bot]) + +* unix,pipe: fix handling null buffer in uv_pipe_get{sock,peer}name (Saúl + Ibarra Corretgé) + +* unix,win: harmonize buffer checking (Saúl Ibarra Corretgé) + +* unix,win: add support for detached threads (Juan José Arboleda) + +* src: add uv_thread_set/getname() methods (Santiago Gimeno) + +* build: fix qemu builds (Ben Noordhuis) + +* win: drop support for windows 8 (Ben Noordhuis) + +* linux: fix uv_cpu_info() arm cpu model detection (Ben Noordhuis) + +* linux: always use io_uring for epoll batching (Ben Noordhuis) + +* doc: clarify repeating timer behavior more (Ben Noordhuis) + +* unix,win: handle nbufs=0 in uv_udp_try_send (Ben Noordhuis) + +* win: use GetQueuedCompletionStatusEx directly (Saúl Ibarra Corretgé) + +* win: enable uv_thread_{get,set}name on MinGW (Saúl Ibarra Corretgé) + +* win: drop support for the legacy MinGW (Saúl Ibarra Corretgé) + +* win,fs: get (most) fstat when no permission (Jameson Nash) + +* win: plug uv_fs_event_start memory leak (amcgoogan) + +* test: address FreeBSD kernel bug causing NULL path in fsevents (Juan José + Arboleda) + +* unix: refactor udp sendmsg code (Ben Noordhuis) + +* unix,win: add uv_udp_try_send2 (Ben Noordhuis) + +* test: fix flaky flaky udp_mmsg test (Juan José Arboleda) + +* build: enable fdsan in Android (Juan José Arboleda) + +* test: fix udp-multicast-join for FreeBSD (Juan José Arboleda) + +* win: fix leak processing fs event (Saúl Ibarra Corretgé) + +* src: set a default thread name for workers (Rafael Gonzaga) + +* misc: implement uv_getrusage_thread (Juan José Arboleda) + + +2024.10.18, Version 1.49.2 (Stable), e1095c7a4373ce00cd8874d8e820de5afb25776e Changes since version 1.49.1: diff --git a/deps/uv/LINKS.md b/deps/uv/LINKS.md index 3e5800747bc7dd..743935cebb8532 100644 --- a/deps/uv/LINKS.md +++ b/deps/uv/LINKS.md @@ -37,6 +37,7 @@ * [Pixie-io](https://github.com/pixie-io/pixie): Open-source observability tool for Kubernetes applications. * [potion](https://github.com/perl11/potion)/[p2](https://github.com/perl11/p2): runtime * [racer](https://libraries.io/rubygems/racer): Ruby web server written as an C extension +* [scala-native-loop](https://github.com/scala-native/scala-native-loop): Extensible event loop and async-oriented IO for Scala Native; powered by libuv * [Socket Runtime](https://sockets.sh): A runtime for creating native cross-platform software on mobile and desktop using HTML, CSS, and JavaScript * [spider-gazelle](https://github.com/cotag/spider-gazelle): Ruby web server using libuv bindings * [Suave](http://suave.io/): A simple web development F# library providing a lightweight web server and a set of combinators to manipulate route flow and task composition diff --git a/deps/uv/MAINTAINERS.md b/deps/uv/MAINTAINERS.md index 41c60cb383cfbe..ff8be88b7b7cd5 100644 --- a/deps/uv/MAINTAINERS.md +++ b/deps/uv/MAINTAINERS.md @@ -4,6 +4,9 @@ libuv is currently managed by the following individuals: * **Ben Noordhuis** ([@bnoordhuis](https://github.com/bnoordhuis)) - GPG key: D77B 1E34 243F BAF0 5F8E 9CC3 4F55 C8C8 46AB 89B9 (pubkey-bnoordhuis) +* **Colin Ihrig** ([@cjihrig](https://github.com/cjihrig)) + - GPG key: 94AE 3667 5C46 4D64 BAFA 68DD 7434 390B DBE9 B9C5 (pubkey-cjihrig) + - GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb) * **Jameson Nash** ([@vtjnash](https://github.com/vtjnash)) - GPG key: AEAD 0A4B 6867 6775 1A0E 4AEF 34A2 5FB1 2824 6514 (pubkey-vtjnash) - GPG key: CFBB 9CA9 A5BE AFD7 0E2B 3C5A 79A6 7C55 A367 9C8B (pubkey2022-vtjnash) @@ -24,9 +27,6 @@ libuv is currently managed by the following individuals: * **Anna Henningsen** ([@addaleax](https://github.com/addaleax)) * **Bartosz Sosnowski** ([@bzoz](https://github.com/bzoz)) * **Bert Belder** ([@piscisaureus](https://github.com/piscisaureus)) -* **Colin Ihrig** ([@cjihrig](https://github.com/cjihrig)) - - GPG key: 94AE 3667 5C46 4D64 BAFA 68DD 7434 390B DBE9 B9C5 (pubkey-cjihrig) - - GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb) * **Fedor Indutny** ([@indutny](https://github.com/indutny)) - GPG key: AF2E EA41 EC34 47BF DD86 FED9 D706 3CCE 19B7 E890 (pubkey-indutny) * **Imran Iqbal** ([@imran-iq](https://github.com/imran-iq)) diff --git a/deps/uv/Makefile.am b/deps/uv/Makefile.am index f85a41316c8a43..9b9e6be7178b22 100644 --- a/deps/uv/Makefile.am +++ b/deps/uv/Makefile.am @@ -59,7 +59,7 @@ if WINNT uvinclude_HEADERS += include/uv/win.h include/uv/tree.h AM_CPPFLAGS += -I$(top_srcdir)/src/win \ -DWIN32_LEAN_AND_MEAN \ - -D_WIN32_WINNT=0x0602 + -D_WIN32_WINNT=0x0A00 libuv_la_SOURCES += src/win/async.c \ src/win/atomicops-inl.h \ src/win/core.c \ @@ -294,6 +294,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-thread-equal.c \ test/test-thread.c \ test/test-thread-affinity.c \ + test/test-thread-name.c \ test/test-thread-priority.c \ test/test-threadpool-cancel.c \ test/test-threadpool.c \ diff --git a/deps/uv/SUPPORTED_PLATFORMS.md b/deps/uv/SUPPORTED_PLATFORMS.md index 8a435d2592e47f..9597801b919687 100644 --- a/deps/uv/SUPPORTED_PLATFORMS.md +++ b/deps/uv/SUPPORTED_PLATFORMS.md @@ -4,14 +4,14 @@ |---|---|---|---| | GNU/Linux | Tier 1 | Linux >= 3.10 with glibc >= 2.17 | | | macOS | Tier 1 | macOS >= 11 | Currently supported macOS releases | -| Windows | Tier 1 | >= Windows 8 | VS 2015 and later are supported | +| Windows | Tier 1 | >= Windows 10 | VS 2015 and later are supported | | FreeBSD | Tier 2 | >= 12 | | | AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix | | IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi | | z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos | | Linux with musl | Tier 2 | musl >= 1.0 | | | Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` | -| MinGW | Tier 3 | MinGW32 and MinGW-w64 | | +| MinGW | Tier 3 | MinGW-w64 | | | SunOS | Tier 3 | Solaris 121 and later | | | Other | Tier 3 | N/A | | diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index 98c59363026f86..fc8316b8e8fa75 100644 --- a/deps/uv/configure.ac +++ b/deps/uv/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [1.49.2], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.50.0], [https://github.com/libuv/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/as_case.m4]) diff --git a/deps/uv/docs/src/fs_event.rst b/deps/uv/docs/src/fs_event.rst index 983db1a9d5608a..bfdecdd7329cd2 100644 --- a/deps/uv/docs/src/fs_event.rst +++ b/deps/uv/docs/src/fs_event.rst @@ -47,6 +47,11 @@ Data types The `events` parameter is an ORed mask of :c:enum:`uv_fs_event` elements. +.. note:: + For FreeBSD path could sometimes be `NULL` due to a kernel bug. + + .. _Reference: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=197695 + .. c:enum:: uv_fs_event Event types that :c:type:`uv_fs_event_t` handles monitor. diff --git a/deps/uv/docs/src/misc.rst b/deps/uv/docs/src/misc.rst index 61883b7e21e527..db95e2dde83ea1 100644 --- a/deps/uv/docs/src/misc.rst +++ b/deps/uv/docs/src/misc.rst @@ -360,6 +360,17 @@ API On Windows not all fields are set, the unsupported fields are filled with zeroes. See :c:type:`uv_rusage_t` for more details. +.. c:function:: int uv_getrusage_thread(uv_rusage_t* rusage) + + Gets the resource usage measures for the calling thread. + + .. versionadded:: 1.50.0 + + .. note:: + Not supported on all platforms. May return `UV_ENOTSUP`. + On macOS and Windows not all fields are set, the unsupported fields are filled with zeroes. + See :c:type:`uv_rusage_t` for more details. + .. c:function:: uv_pid_t uv_os_getpid(void) Returns the current process ID. diff --git a/deps/uv/docs/src/threading.rst b/deps/uv/docs/src/threading.rst index 883218fa829ccb..f40cf0a33c8121 100644 --- a/deps/uv/docs/src/threading.rst +++ b/deps/uv/docs/src/threading.rst @@ -78,6 +78,14 @@ Threads .. versionchanged:: 1.4.1 returns a UV_E* error code on failure +.. c:function:: int uv_thread_detach(uv_thread_t* tid) + + Detaches a thread. Detached threads automatically release their + resources upon termination, eliminating the need for the application to + call `uv_thread_join`. + + .. versionadded:: 1.50.0 + .. c:function:: int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, uv_thread_cb entry, void* arg) Like :c:func:`uv_thread_create`, but additionally specifies options for creating a new thread. @@ -132,6 +140,23 @@ Threads .. c:function:: int uv_thread_join(uv_thread_t *tid) .. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) +.. c:function:: int uv_thread_setname(const char* name) + + Sets the name of the current thread. Different platforms define different limits on the max number of characters + a thread name can be: Linux, IBM i (16), macOS (64), Windows (32767), and NetBSD (32), etc. `uv_thread_setname()` + will truncate it in case `name` is larger than the limit of the platform. + + .. versionadded:: 1.50.0 + +.. c:function:: int uv_thread_getname(uv_thread_t* tid, char* name, size_t* size) + + Gets the name of the thread specified by `tid`. The thread name is copied, with the trailing NUL, into the buffer + pointed to by `name`. The `size` parameter specifies the size of the buffer pointed to by `name`. + The buffer should be large enough to hold the name of the thread plus the trailing NUL, or it will be truncated to fit + with the trailing NUL. + + .. versionadded:: 1.50.0 + .. c:function:: int uv_thread_setpriority(uv_thread_t tid, int priority) If the function succeeds, the return value is 0. If the function fails, the return value is less than zero. diff --git a/deps/uv/docs/src/threadpool.rst b/deps/uv/docs/src/threadpool.rst index 7cfa797314ca48..05f31d2ccf30b8 100644 --- a/deps/uv/docs/src/threadpool.rst +++ b/deps/uv/docs/src/threadpool.rst @@ -17,6 +17,8 @@ is 1024). .. versionchanged:: 1.45.0 threads now have an 8 MB stack instead of the (sometimes too low) platform default. +.. versionchanged:: 1.50.0 threads now have a default name of libuv-worker. + The threadpool is global and shared across all event loops. When a particular function makes use of the threadpool (i.e. when using :c:func:`uv_queue_work`) libuv preallocates and initializes the maximum number of threads allowed by diff --git a/deps/uv/docs/src/timer.rst b/deps/uv/docs/src/timer.rst index 070fa79da9d6df..474c6b8c4cd4f6 100644 --- a/deps/uv/docs/src/timer.rst +++ b/deps/uv/docs/src/timer.rst @@ -6,6 +6,15 @@ Timer handles are used to schedule callbacks to be called in the future. +Timers are either single-shot or repeating. Repeating timers do not adjust +for overhead but are rearmed relative to the event loop's idea of "now". + +Libuv updates its idea of "now" right before executing timer callbacks, and +right after waking up from waiting for I/O. See also :c:func:`uv_update_time`. + +Example: a repeating timer with a 50 ms interval whose callback takes 17 ms +to complete, runs again 33 ms later. If other tasks take longer than 33 ms, +the timer callback runs as soon as possible. Data types ---------- @@ -64,11 +73,6 @@ API duration, and will follow normal timer semantics in the case of a time-slice overrun. - For example, if a 50ms repeating timer first runs for 17ms, it will be - scheduled to run again 33ms later. If other tasks consume more than the - 33ms following the first timer callback, then the callback will run as soon - as possible. - .. note:: If the repeat value is set from a timer callback it does not immediately take effect. If the timer was non-repeating before, it will have been stopped. If it was repeating, diff --git a/deps/uv/docs/src/udp.rst b/deps/uv/docs/src/udp.rst index 31f7f7fd71ff47..5f225e5cda4011 100644 --- a/deps/uv/docs/src/udp.rst +++ b/deps/uv/docs/src/udp.rst @@ -426,6 +426,20 @@ API .. versionchanged:: 1.27.0 added support for connected sockets +.. c:function:: int uv_udp_try_send2(uv_udp_t* handle, unsigned int count, uv_buf_t* bufs[/*count*/], unsigned int nbufs[/*count*/], struct sockaddr* addrs[/*count*/], unsigned int flags) + + Like :c:func:`uv_udp_try_send`, but can send multiple datagrams. + Lightweight abstraction around :man:`sendmmsg(2)`, with a :man:`sendmsg(2)` + fallback loop for platforms that do not support the former. The handle must + be fully initialized; call c:func:`uv_udp_bind` first. + + :returns: >= 0: number of datagrams sent. Zero only if `count` was zero. + < 0: negative error code. Only if sending the first datagram fails, + otherwise returns a positive send count. ``UV_EAGAIN`` when datagrams + cannot be sent right now; fall back to :c:func:`uv_udp_send`. + + .. versionadded:: 1.50.0 + .. c:function:: int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) Prepare for receiving data. If the socket has not previously been bound diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 9e450c5110fe57..f0ec376b607c05 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -157,6 +157,7 @@ struct uv__queue { XX(ESOCKTNOSUPPORT, "socket type not supported") \ XX(ENODATA, "no data available") \ XX(EUNATCH, "protocol driver not attached") \ + XX(ENOEXEC, "exec format error") \ #define UV_HANDLE_TYPE_MAP(XX) \ XX(ASYNC, async) \ @@ -775,6 +776,12 @@ UV_EXTERN int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr); +UV_EXTERN int uv_udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/], + unsigned int flags); UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb); @@ -1288,6 +1295,7 @@ typedef struct { } uv_rusage_t; UV_EXTERN int uv_getrusage(uv_rusage_t* rusage); +UV_EXTERN int uv_getrusage_thread(uv_rusage_t* rusage); UV_EXTERN int uv_os_homedir(char* buffer, size_t* size); UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size); @@ -1869,6 +1877,7 @@ UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv); typedef void (*uv_thread_cb)(void* arg); UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg); +UV_EXTERN int uv_thread_detach(uv_thread_t* tid); typedef enum { UV_THREAD_NO_FLAGS = 0x00, @@ -1898,6 +1907,9 @@ UV_EXTERN int uv_thread_getcpu(void); UV_EXTERN uv_thread_t uv_thread_self(void); UV_EXTERN int uv_thread_join(uv_thread_t *tid); UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2); +UV_EXTERN int uv_thread_setname(const char* name); +UV_EXTERN int uv_thread_getname(uv_thread_t* tid, char* name, size_t size); + /* The presence of these unions force similar struct layout. */ #define XX(_, name) uv_ ## name ## _t name; diff --git a/deps/uv/include/uv/errno.h b/deps/uv/include/uv/errno.h index 127278ef916161..ac00778cfc59fb 100644 --- a/deps/uv/include/uv/errno.h +++ b/deps/uv/include/uv/errno.h @@ -474,4 +474,10 @@ # define UV__EUNATCH (-4023) #endif +#if defined(ENOEXEC) && !defined(_WIN32) +# define UV__ENOEXEC UV__ERR(ENOEXEC) +#else +# define UV__ENOEXEC (-4022) +#endif + #endif /* UV_ERRNO_H_ */ diff --git a/deps/uv/include/uv/unix.h b/deps/uv/include/uv/unix.h index 538f98b6c5d657..7c972026f688e8 100644 --- a/deps/uv/include/uv/unix.h +++ b/deps/uv/include/uv/unix.h @@ -271,7 +271,10 @@ typedef struct { #define UV_UDP_SEND_PRIVATE_FIELDS \ struct uv__queue queue; \ - struct sockaddr_storage addr; \ + union { \ + struct sockaddr addr; \ + struct sockaddr_storage storage; \ + } u; \ unsigned int nbufs; \ uv_buf_t* bufs; \ ssize_t status; \ diff --git a/deps/uv/include/uv/version.h b/deps/uv/include/uv/version.h index cfa7871322e690..76eb7d125fe468 100644 --- a/deps/uv/include/uv/version.h +++ b/deps/uv/include/uv/version.h @@ -31,8 +31,8 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 49 -#define UV_VERSION_PATCH 2 +#define UV_VERSION_MINOR 50 +#define UV_VERSION_PATCH 0 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/deps/uv/include/uv/win.h b/deps/uv/include/uv/win.h index 12ac53b4f217d2..58d10b8d07fa0b 100644 --- a/deps/uv/include/uv/win.h +++ b/deps/uv/include/uv/win.h @@ -20,7 +20,7 @@ */ #ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0600 +# define _WIN32_WINNT 0x0A00 #endif #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) @@ -32,14 +32,6 @@ typedef intptr_t ssize_t; #include -#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) -typedef struct pollfd { - SOCKET fd; - short events; - short revents; -} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD; -#endif - #ifndef LOCALE_INVARIANT # define LOCALE_INVARIANT 0x007f #endif diff --git a/deps/uv/src/fs-poll.c b/deps/uv/src/fs-poll.c index 1bac1c568e36ca..44f6263a5832ec 100644 --- a/deps/uv/src/fs-poll.c +++ b/deps/uv/src/fs-poll.c @@ -139,6 +139,9 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) { struct poll_ctx* ctx; size_t required_len; + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + if (!uv_is_active((uv_handle_t*)handle)) { *size = 0; return UV_EINVAL; diff --git a/deps/uv/src/idna.c b/deps/uv/src/idna.c index efc5f283ce2ef9..5fcaf64c974a8a 100644 --- a/deps/uv/src/idna.c +++ b/deps/uv/src/idna.c @@ -393,7 +393,7 @@ void uv_wtf8_to_utf16(const char* source_ptr, code_point = uv__wtf8_decode1(&source_ptr); /* uv_wtf8_length_as_utf16 should have been called and checked first. */ assert(code_point >= 0); - if (code_point > 0x10000) { + if (code_point > 0xFFFF) { assert(code_point < 0x10FFFF); *w_target++ = (((code_point - 0x10000) >> 10) + 0xD800); *w_target++ = ((code_point - 0x10000) & 0x3FF) + 0xDC00; diff --git a/deps/uv/src/threadpool.c b/deps/uv/src/threadpool.c index 45af50dcd04ea6..98d81cc7b6a4ed 100644 --- a/deps/uv/src/threadpool.c +++ b/deps/uv/src/threadpool.c @@ -59,6 +59,7 @@ static void worker(void* arg) { struct uv__queue* q; int is_slow_work; + uv_thread_setname("libuv-worker"); uv_sem_post((uv_sem_t*) arg); arg = NULL; diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c index 0ff2669e30a628..8265a43ab47046 100644 --- a/deps/uv/src/unix/async.c +++ b/deps/uv/src/unix/async.c @@ -38,6 +38,34 @@ #include #endif +#if UV__KQUEUE_EVFILT_USER +static uv_once_t kqueue_runtime_detection_guard = UV_ONCE_INIT; +static int kqueue_evfilt_user_support = 1; + + +static void uv__kqueue_runtime_detection(void) { + int kq; + struct kevent ev[2]; + struct timespec timeout = {0, 0}; + + /* Perform the runtime detection to ensure that kqueue with + * EVFILT_USER actually works. */ + kq = kqueue(); + EV_SET(ev, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER, + EV_ADD | EV_CLEAR, 0, 0, 0); + EV_SET(ev + 1, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER, + 0, NOTE_TRIGGER, 0, 0); + if (kevent(kq, ev, 2, ev, 1, &timeout) < 1 || + ev[0].filter != EVFILT_USER || + ev[0].ident != UV__KQUEUE_EVFILT_USER_IDENT || + ev[0].flags & EV_ERROR) + /* If we wind up here, we can assume that EVFILT_USER is defined but + * broken on the current system. */ + kqueue_evfilt_user_support = 0; + uv__close(kq); +} +#endif + static void uv__async_send(uv_loop_t* loop); static int uv__async_start(uv_loop_t* loop); static void uv__cpu_relax(void); @@ -139,7 +167,11 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { assert(w == &loop->async_io_watcher); +#if UV__KQUEUE_EVFILT_USER + for (;!kqueue_evfilt_user_support;) { +#else for (;;) { +#endif r = read(w->fd, buf, sizeof(buf)); if (r == sizeof(buf)) @@ -195,6 +227,17 @@ static void uv__async_send(uv_loop_t* loop) { len = sizeof(val); fd = loop->async_io_watcher.fd; /* eventfd */ } +#elif UV__KQUEUE_EVFILT_USER + struct kevent ev; + + if (kqueue_evfilt_user_support) { + fd = loop->async_io_watcher.fd; /* magic number for EVFILT_USER */ + EV_SET(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0); + r = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL); + if (r == 0) + return; + abort(); + } #endif do @@ -215,6 +258,9 @@ static void uv__async_send(uv_loop_t* loop) { static int uv__async_start(uv_loop_t* loop) { int pipefd[2]; int err; +#if UV__KQUEUE_EVFILT_USER + struct kevent ev; +#endif if (loop->async_io_watcher.fd != -1) return 0; @@ -226,6 +272,36 @@ static int uv__async_start(uv_loop_t* loop) { pipefd[0] = err; pipefd[1] = -1; +#elif UV__KQUEUE_EVFILT_USER + uv_once(&kqueue_runtime_detection_guard, uv__kqueue_runtime_detection); + if (kqueue_evfilt_user_support) { + /* In order not to break the generic pattern of I/O polling, a valid + * file descriptor is required to take up a room in loop->watchers, + * thus we create one for that, but this fd will not be actually used, + * it's just a placeholder and magic number which is going to be closed + * during the cleanup, as other FDs. */ + err = uv__open_cloexec("/dev/null", O_RDONLY); + if (err < 0) + return err; + + pipefd[0] = err; + pipefd[1] = -1; + + /* When using EVFILT_USER event to wake up the kqueue, this event must be + * registered beforehand. Otherwise, calling kevent() to issue an + * unregistered EVFILT_USER event will get an ENOENT. + * Since uv__async_send() may happen before uv__io_poll() with multi-threads, + * we can't defer this registration of EVFILT_USER event as we did for other + * events, but must perform it right away. */ + EV_SET(&ev, err, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0); + err = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL); + if (err < 0) + return UV__ERR(errno); + } else { + err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); + if (err < 0) + return err; + } #else err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); if (err < 0) @@ -236,6 +312,13 @@ static int uv__async_start(uv_loop_t* loop) { uv__io_start(loop, &loop->async_io_watcher, POLLIN); loop->async_wfd = pipefd[1]; +#if UV__KQUEUE_EVFILT_USER + /* Prevent the EVFILT_USER event from being added to kqueue redundantly + * and mistakenly later in uv__io_poll(). */ + if (kqueue_evfilt_user_support) + loop->async_io_watcher.events = loop->async_io_watcher.pevents; +#endif + return 0; } diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index 0c52ccf2ad7b2d..61cbc0d027f04a 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -52,6 +52,8 @@ #endif #if defined(__APPLE__) +# include +# include # include # include #endif /* defined(__APPLE__) */ @@ -751,7 +753,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { int uv_cwd(char* buffer, size_t* size) { char scratch[1 + UV__PATH_MAX]; - if (buffer == NULL || size == NULL) + if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; /* Try to read directly into the user's buffer first... */ @@ -999,10 +1001,10 @@ int uv__fd_exists(uv_loop_t* loop, int fd) { } -int uv_getrusage(uv_rusage_t* rusage) { +static int uv__getrusage(int who, uv_rusage_t* rusage) { struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage)) + if (getrusage(who, &usage)) return UV__ERR(errno); rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec; @@ -1041,6 +1043,48 @@ int uv_getrusage(uv_rusage_t* rusage) { } +int uv_getrusage(uv_rusage_t* rusage) { + return uv__getrusage(RUSAGE_SELF, rusage); +} + + +int uv_getrusage_thread(uv_rusage_t* rusage) { +#if defined(__APPLE__) + mach_msg_type_number_t count; + thread_basic_info_data_t info; + kern_return_t kr; + thread_t thread; + + thread = mach_thread_self(); + count = THREAD_BASIC_INFO_COUNT; + kr = thread_info(thread, + THREAD_BASIC_INFO, + (thread_info_t)&info, + &count); + + if (kr != KERN_SUCCESS) { + mach_port_deallocate(mach_task_self(), thread); + return UV_EINVAL; + } + + memset(rusage, 0, sizeof(*rusage)); + + rusage->ru_utime.tv_sec = info.user_time.seconds; + rusage->ru_utime.tv_usec = info.user_time.microseconds; + rusage->ru_stime.tv_sec = info.system_time.seconds; + rusage->ru_stime.tv_usec = info.system_time.microseconds; + + mach_port_deallocate(mach_task_self(), thread); + + return 0; + +#elif defined(RUSAGE_THREAD) + return uv__getrusage(RUSAGE_THREAD, rusage); +#endif /* defined(__APPLE__) */ + return UV_ENOTSUP; +} + + int uv__open_cloexec(const char* path, int flags) { #if defined(O_CLOEXEC) int fd; diff --git a/deps/uv/src/unix/darwin-proctitle.c b/deps/uv/src/unix/darwin-proctitle.c index 5288083ef04fd7..5e5642972a4df6 100644 --- a/deps/uv/src/unix/darwin-proctitle.c +++ b/deps/uv/src/unix/darwin-proctitle.c @@ -33,25 +33,9 @@ #include "darwin-stub.h" #endif - -static int uv__pthread_setname_np(const char* name) { - char namebuf[64]; /* MAXTHREADNAMESIZE */ - int err; - - strncpy(namebuf, name, sizeof(namebuf) - 1); - namebuf[sizeof(namebuf) - 1] = '\0'; - - err = pthread_setname_np(namebuf); - if (err) - return UV__ERR(err); - - return 0; -} - - int uv__set_process_title(const char* title) { #if TARGET_OS_IPHONE - return uv__pthread_setname_np(title); + return uv__thread_setname(title); #else CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, const char*, @@ -177,7 +161,7 @@ int uv__set_process_title(const char* title) { goto out; } - uv__pthread_setname_np(title); /* Don't care if it fails. */ + uv__thread_setname(title); /* Don't care if it fails. */ err = 0; out: diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 8d586b0b64a96c..b1d2b21756da36 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -35,6 +35,10 @@ #include #include #include +#if defined(__APPLE__) || defined(__DragonFly__) || \ + defined(__FreeBSD__) || defined(__NetBSD__) +#include +#endif #define uv__msan_unpoison(p, n) \ do { \ @@ -323,6 +327,8 @@ void uv__prepare_close(uv_prepare_t* handle); void uv__process_close(uv_process_t* handle); void uv__stream_close(uv_stream_t* handle); void uv__tcp_close(uv_tcp_t* handle); +int uv__thread_setname(const char* name); +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size); size_t uv__thread_stack_size(void); void uv__udp_close(uv_udp_t* handle); void uv__udp_finish_close(uv_udp_t* handle); @@ -504,4 +510,22 @@ int uv__get_constrained_cpu(uv__cpu_constraint* constraint); #endif #endif +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) +/* EVFILT_USER is available since OS X 10.6, DragonFlyBSD 4.0, + * FreeBSD 8.1, and NetBSD 10.0. + * + * Note that even though EVFILT_USER is defined on the current system, + * it may still fail to work at runtime somehow. In that case, we fall + * back to pipe-based signaling. + */ +#define UV__KQUEUE_EVFILT_USER 1 +/* Magic number of identifier used for EVFILT_USER during runtime detection. + * There are no Google hits for this number when I create it. That way, + * people will be directed here if this number gets printed due to some + * kqueue error and they google for help. */ +#define UV__KQUEUE_EVFILT_USER_IDENT 0x1e7e7711 +#else +#define UV__KQUEUE_EVFILT_USER 0 +#endif + #endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index 66aa166f053f52..e0166c344b05c4 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -97,8 +97,7 @@ int uv__io_fork(uv_loop_t* loop) { int uv__io_check_fd(uv_loop_t* loop, int fd) { - struct kevent ev; - int rc; + struct kevent ev[2]; struct stat sb; #ifdef __APPLE__ char path[MAXPATHLEN]; @@ -133,17 +132,12 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { } #endif - rc = 0; - EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0); - if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) - rc = UV__ERR(errno); - - EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0); - if (rc == 0) - if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) - abort(); + EV_SET(ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0); + EV_SET(ev + 1, fd, EVFILT_READ, EV_DELETE, 0, 0, 0); + if (kevent(loop->backend_fd, ev, 2, NULL, 0, NULL)) + return UV__ERR(errno); - return rc; + return 0; } @@ -367,6 +361,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { continue; } +#if UV__KQUEUE_EVFILT_USER + if (ev->filter == EVFILT_USER) { + w = &loop->async_io_watcher; + assert(fd == w->fd); + uv__metrics_update_idle_time(loop); + w->cb(loop, w, w->events); + nevents++; + continue; + } +#endif + if (ev->filter == EVFILT_VNODE) { assert(w->events == POLLIN); assert(w->pevents == POLLIN); diff --git a/deps/uv/src/unix/linux.c b/deps/uv/src/unix/linux.c index 857a4ef8a6686f..763f5dd5917b44 100644 --- a/deps/uv/src/unix/linux.c +++ b/deps/uv/src/unix/linux.c @@ -455,7 +455,7 @@ int uv__io_uring_register(int fd, unsigned opcode, void* arg, unsigned nargs) { } -static int uv__use_io_uring(void) { +static int uv__use_io_uring(uint32_t flags) { #if defined(__ANDROID_API__) return 0; /* Possibly available but blocked by seccomp. */ #elif defined(__arm__) && __SIZEOF_POINTER__ == 4 @@ -470,25 +470,27 @@ static int uv__use_io_uring(void) { char* val; int use; - use = atomic_load_explicit(&use_io_uring, memory_order_relaxed); - - if (use == 0) { - use = uv__kernel_version() >= #if defined(__hppa__) - /* io_uring first supported on parisc in 6.1, functional in .51 */ - /* https://lore.kernel.org/all/cb912694-b1fe-dbb0-4d8c-d608f3526905@gmx.de/ */ - /* 6.1.51 */ 0x060133 -#else - /* Older kernels have a bug where the sqpoll thread uses 100% CPU. */ - /* 5.10.186 */ 0x050ABA + /* io_uring first supported on parisc in 6.1, functional in .51 + * https://lore.kernel.org/all/cb912694-b1fe-dbb0-4d8c-d608f3526905@gmx.de/ + */ + if (uv__kernel_version() < /*6.1.51*/0x060133) + return 0; #endif - ? 1 : -1; - /* But users can still enable it if they so desire. */ - val = getenv("UV_USE_IO_URING"); - if (val != NULL) - use = atoi(val) ? 1 : -1; + /* SQPOLL is all kinds of buggy but epoll batching should work fine. */ + if (0 == (flags & UV__IORING_SETUP_SQPOLL)) + return 1; + + /* Older kernels have a bug where the sqpoll thread uses 100% CPU. */ + if (uv__kernel_version() < /*5.10.186*/0x050ABA) + return 0; + + use = atomic_load_explicit(&use_io_uring, memory_order_relaxed); + if (use == 0) { + val = getenv("UV_USE_IO_URING"); + use = val != NULL && atoi(val) > 0 ? 1 : -1; atomic_store_explicit(&use_io_uring, use, memory_order_relaxed); } @@ -518,7 +520,7 @@ static void uv__iou_init(int epollfd, sq = MAP_FAILED; sqe = MAP_FAILED; - if (!uv__use_io_uring()) + if (!uv__use_io_uring(flags)) return; kernel_version = uv__kernel_version(); @@ -766,14 +768,13 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou, */ if (iou->ringfd == -2) { /* By default, the SQPOLL is not created. Enable only if the loop is - * configured with UV_LOOP_USE_IO_URING_SQPOLL. + * configured with UV_LOOP_USE_IO_URING_SQPOLL and the UV_USE_IO_URING + * environment variable is unset or a positive number. */ - if ((loop->flags & UV_LOOP_ENABLE_IO_URING_SQPOLL) == 0) { - iou->ringfd = -1; - return NULL; - } + if (loop->flags & UV_LOOP_ENABLE_IO_URING_SQPOLL) + if (uv__use_io_uring(UV__IORING_SETUP_SQPOLL)) + uv__iou_init(loop->backend_fd, iou, 64, UV__IORING_SETUP_SQPOLL); - uv__iou_init(loop->backend_fd, iou, 64, UV__IORING_SETUP_SQPOLL); if (iou->ringfd == -2) iou->ringfd = -1; /* "failed" */ } @@ -1713,16 +1714,22 @@ int uv_uptime(double* uptime) { int uv_cpu_info(uv_cpu_info_t** ci, int* count) { #if defined(__PPC__) static const char model_marker[] = "cpu\t\t: "; + static const char model_marker2[] = ""; #elif defined(__arm__) - static const char model_marker[] = "Processor\t: "; + static const char model_marker[] = "model name\t: "; + static const char model_marker2[] = "Processor\t: "; #elif defined(__aarch64__) static const char model_marker[] = "CPU part\t: "; + static const char model_marker2[] = ""; #elif defined(__mips__) static const char model_marker[] = "cpu model\t\t: "; + static const char model_marker2[] = ""; #elif defined(__loongarch__) static const char model_marker[] = "cpu family\t\t: "; + static const char model_marker2[] = ""; #else static const char model_marker[] = "model name\t: "; + static const char model_marker2[] = ""; #endif static const char parts[] = #ifdef __aarch64__ @@ -1821,14 +1828,22 @@ int uv_cpu_info(uv_cpu_info_t** ci, int* count) { if (1 != fscanf(fp, "processor\t: %u\n", &cpu)) break; /* Parse error. */ - found = 0; - while (!found && fgets(buf, sizeof(buf), fp)) - found = !strncmp(buf, model_marker, sizeof(model_marker) - 1); + while (fgets(buf, sizeof(buf), fp)) { + if (!strncmp(buf, model_marker, sizeof(model_marker) - 1)) { + p = buf + sizeof(model_marker) - 1; + goto parts; + } + if (!*model_marker2) + continue; + if (!strncmp(buf, model_marker2, sizeof(model_marker2) - 1)) { + p = buf + sizeof(model_marker2) - 1; + goto parts; + } + } - if (!found) - goto next; + goto next; /* Not found. */ - p = buf + sizeof(model_marker) - 1; +parts: n = (int) strcspn(p, "\n"); /* arm64: translate CPU part code to model name. */ diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index 1f9acfac41e9c5..bd57b17fb0367a 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -360,6 +360,9 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, char* p; int err; + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + addrlen = sizeof(sa); memset(&sa, 0, addrlen); err = uv__getsockpeername((const uv_handle_t*) handle, @@ -444,7 +447,7 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) { int uv_pipe_chmod(uv_pipe_t* handle, int mode) { unsigned desired_mode; struct stat pipe_stat; - char* name_buffer; + char name_buffer[1 + UV__PATH_MAX]; size_t name_len; int r; @@ -457,26 +460,14 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { return UV_EINVAL; /* Unfortunately fchmod does not work on all platforms, we will use chmod. */ - name_len = 0; - r = uv_pipe_getsockname(handle, NULL, &name_len); - if (r != UV_ENOBUFS) - return r; - - name_buffer = uv__malloc(name_len); - if (name_buffer == NULL) - return UV_ENOMEM; - + name_len = sizeof(name_buffer); r = uv_pipe_getsockname(handle, name_buffer, &name_len); - if (r != 0) { - uv__free(name_buffer); + if (r != 0) return r; - } /* stat must be used as fstat has a bug on Darwin */ - if (uv__stat(name_buffer, &pipe_stat) == -1) { - uv__free(name_buffer); - return -errno; - } + if (uv__stat(name_buffer, &pipe_stat) == -1) + return UV__ERR(errno); desired_mode = 0; if (mode & UV_READABLE) @@ -485,15 +476,12 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH; /* Exit early if pipe already has desired mode. */ - if ((pipe_stat.st_mode & desired_mode) == desired_mode) { - uv__free(name_buffer); + if ((pipe_stat.st_mode & desired_mode) == desired_mode) return 0; - } pipe_stat.st_mode |= desired_mode; r = chmod(name_buffer, pipe_stat.st_mode); - uv__free(name_buffer); return r != -1 ? 0 : UV__ERR(errno); } diff --git a/deps/uv/src/unix/thread.c b/deps/uv/src/unix/thread.c index f05e6fe0f7dd5a..e51c290466d08b 100644 --- a/deps/uv/src/unix/thread.c +++ b/deps/uv/src/unix/thread.c @@ -23,6 +23,9 @@ #include "internal.h" #include +#ifdef __OpenBSD__ +#include +#endif #include #include @@ -126,6 +129,12 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { return uv_thread_create_ex(tid, ¶ms, entry, arg); } + +int uv_thread_detach(uv_thread_t *tid) { + return UV__ERR(pthread_detach(*tid)); +} + + int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, void (*entry)(void *arg), @@ -291,6 +300,18 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) { return pthread_equal(*t1, *t2); } +int uv_thread_setname(const char* name) { + if (name == NULL) + return UV_EINVAL; + return uv__thread_setname(name); +} + +int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) { + if (name == NULL || size == 0) + return UV_EINVAL; + + return uv__thread_getname(tid, name, size); +} int uv_mutex_init(uv_mutex_t* mutex) { #if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK) @@ -875,3 +896,80 @@ void uv_key_set(uv_key_t* key, void* value) { if (pthread_setspecific(*key, value)) abort(); } + +#if defined(_AIX) || defined(__MVS__) || defined(__PASE__) +int uv__thread_setname(const char* name) { + return UV_ENOSYS; +} +#elif defined(__APPLE__) +int uv__thread_setname(const char* name) { + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + int err = pthread_setname_np(namebuf); + if (err) + return UV__ERR(errno); + return 0; +} +#elif defined(__NetBSD__) +int uv__thread_setname(const char* name) { + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + return UV__ERR(pthread_setname_np(pthread_self(), "%s", namebuf)); +} +#elif defined(__OpenBSD__) +int uv__thread_setname(const char* name) { + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + pthread_set_name_np(pthread_self(), namebuf); + return 0; +} +#else +int uv__thread_setname(const char* name) { + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + return UV__ERR(pthread_setname_np(pthread_self(), namebuf)); +} +#endif + +#if (defined(__ANDROID_API__) && __ANDROID_API__ < 26) || \ + defined(_AIX) || \ + defined(__MVS__) || \ + defined(__PASE__) +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) { + return UV_ENOSYS; +} +#elif defined(__OpenBSD__) +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) { + char thread_name[UV_PTHREAD_MAX_NAMELEN_NP]; + pthread_get_name_np(*tid, thread_name, sizeof(thread_name)); + strncpy(name, thread_name, size - 1); + name[size - 1] = '\0'; + return 0; +} +#elif defined(__APPLE__) +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) { + char thread_name[UV_PTHREAD_MAX_NAMELEN_NP]; + if (pthread_getname_np(*tid, thread_name, sizeof(thread_name)) != 0) + return UV__ERR(errno); + + strncpy(name, thread_name, size - 1); + name[size - 1] = '\0'; + return 0; +} +#else +int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) { + int r; + char thread_name[UV_PTHREAD_MAX_NAMELEN_NP]; + r = pthread_getname_np(*tid, thread_name, sizeof(thread_name)); + if (r != 0) + return UV__ERR(r); + + strncpy(name, thread_name, size - 1); + name[size - 1] = '\0'; + return 0; +} +#endif diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index f6640fc7231863..67c01f7dce8e18 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -47,6 +47,10 @@ static void uv__udp_sendmsg(uv_udp_t* handle); static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain, unsigned int flags); +static int uv__udp_sendmsg1(int fd, + const uv_buf_t* bufs, + unsigned int nbufs, + const struct sockaddr* addr); void uv__udp_close(uv_udp_t* handle) { @@ -282,169 +286,6 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { && handle->recv_cb != NULL); } -static void uv__udp_sendmsg_one(uv_udp_t* handle, uv_udp_send_t* req) { - struct uv__queue* q; - struct msghdr h; - ssize_t size; - - for (;;) { - memset(&h, 0, sizeof h); - if (req->addr.ss_family == AF_UNSPEC) { - h.msg_name = NULL; - h.msg_namelen = 0; - } else { - h.msg_name = &req->addr; - if (req->addr.ss_family == AF_INET6) - h.msg_namelen = sizeof(struct sockaddr_in6); - else if (req->addr.ss_family == AF_INET) - h.msg_namelen = sizeof(struct sockaddr_in); - else if (req->addr.ss_family == AF_UNIX) - h.msg_namelen = sizeof(struct sockaddr_un); - else { - assert(0 && "unsupported address family"); - abort(); - } - } - h.msg_iov = (struct iovec*) req->bufs; - h.msg_iovlen = req->nbufs; - - do - size = sendmsg(handle->io_watcher.fd, &h, 0); - while (size == -1 && errno == EINTR); - - if (size == -1) - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) - return; - - req->status = (size == -1 ? UV__ERR(errno) : size); - - /* Sending a datagram is an atomic operation: either all data - * is written or nothing is (and EMSGSIZE is raised). That is - * why we don't handle partial writes. Just pop the request - * off the write queue and onto the completed queue, done. - */ - uv__queue_remove(&req->queue); - uv__queue_insert_tail(&handle->write_completed_queue, &req->queue); - uv__io_feed(handle->loop, &handle->io_watcher); - - if (uv__queue_empty(&handle->write_queue)) - return; - - q = uv__queue_head(&handle->write_queue); - req = uv__queue_data(q, uv_udp_send_t, queue); - } -} - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) -static void uv__udp_sendmsg_many(uv_udp_t* handle) { - uv_udp_send_t* req; - struct mmsghdr h[20]; - struct mmsghdr* p; - struct uv__queue* q; - ssize_t npkts; - size_t pkts; - size_t i; - -write_queue_drain: - for (pkts = 0, q = uv__queue_head(&handle->write_queue); - pkts < ARRAY_SIZE(h) && q != &handle->write_queue; - ++pkts, q = uv__queue_head(q)) { - req = uv__queue_data(q, uv_udp_send_t, queue); - - p = &h[pkts]; - memset(p, 0, sizeof(*p)); - if (req->addr.ss_family == AF_UNSPEC) { - p->msg_hdr.msg_name = NULL; - p->msg_hdr.msg_namelen = 0; - } else { - p->msg_hdr.msg_name = &req->addr; - if (req->addr.ss_family == AF_INET6) - p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in6); - else if (req->addr.ss_family == AF_INET) - p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in); - else if (req->addr.ss_family == AF_UNIX) - p->msg_hdr.msg_namelen = sizeof(struct sockaddr_un); - else { - assert(0 && "unsupported address family"); - abort(); - } - } - h[pkts].msg_hdr.msg_iov = (struct iovec*) req->bufs; - h[pkts].msg_hdr.msg_iovlen = req->nbufs; - } - -#if defined(__APPLE__) - do - npkts = sendmsg_x(handle->io_watcher.fd, h, pkts, MSG_DONTWAIT); - while (npkts == -1 && errno == EINTR); -#else - do - npkts = sendmmsg(handle->io_watcher.fd, h, pkts, 0); - while (npkts == -1 && errno == EINTR); -#endif - - if (npkts < 1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) - return; - for (i = 0, q = uv__queue_head(&handle->write_queue); - i < pkts && q != &handle->write_queue; - ++i, q = uv__queue_head(&handle->write_queue)) { - req = uv__queue_data(q, uv_udp_send_t, queue); - req->status = UV__ERR(errno); - uv__queue_remove(&req->queue); - uv__queue_insert_tail(&handle->write_completed_queue, &req->queue); - } - uv__io_feed(handle->loop, &handle->io_watcher); - return; - } - - /* Safety: npkts known to be >0 below. Hence cast from ssize_t - * to size_t safe. - */ - for (i = 0, q = uv__queue_head(&handle->write_queue); - i < (size_t)npkts && q != &handle->write_queue; - ++i, q = uv__queue_head(&handle->write_queue)) { - req = uv__queue_data(q, uv_udp_send_t, queue); - req->status = req->bufs[0].len; - - /* Sending a datagram is an atomic operation: either all data - * is written or nothing is (and EMSGSIZE is raised). That is - * why we don't handle partial writes. Just pop the request - * off the write queue and onto the completed queue, done. - */ - uv__queue_remove(&req->queue); - uv__queue_insert_tail(&handle->write_completed_queue, &req->queue); - } - - /* couldn't batch everything, continue sending (jump to avoid stack growth) */ - if (!uv__queue_empty(&handle->write_queue)) - goto write_queue_drain; - - uv__io_feed(handle->loop, &handle->io_watcher); -} -#endif /* __linux__ || ____FreeBSD__ || __APPLE__ */ - -static void uv__udp_sendmsg(uv_udp_t* handle) { - struct uv__queue* q; - uv_udp_send_t* req; - - if (uv__queue_empty(&handle->write_queue)) - return; - - q = uv__queue_head(&handle->write_queue); - req = uv__queue_data(q, uv_udp_send_t, queue); - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) - /* Use sendmmsg() if this send request contains more than one datagram OR - * there is more than one send request (because that automatically implies - * there is more than one datagram.) - */ - if (req->nbufs != 1 || &handle->write_queue != uv__queue_next(&req->queue)) - return uv__udp_sendmsg_many(handle); -#endif - - return uv__udp_sendmsg_one(handle, req); -} /* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional * refinements for programs that use multicast. Therefore we preferentially @@ -743,11 +584,11 @@ int uv__udp_send(uv_udp_send_t* req, empty_queue = (handle->send_queue_count == 0); uv__req_init(handle->loop, req, UV_UDP_SEND); - assert(addrlen <= sizeof(req->addr)); + assert(addrlen <= sizeof(req->u.storage)); if (addr == NULL) - req->addr.ss_family = AF_UNSPEC; + req->u.storage.ss_family = AF_UNSPEC; else - memcpy(&req->addr, addr, addrlen); + memcpy(&req->u.storage, addr, addrlen); req->send_cb = send_cb; req->handle = handle; req->nbufs = nbufs; @@ -790,10 +631,9 @@ int uv__udp_try_send(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen) { int err; - struct msghdr h; - ssize_t size; - assert(nbufs > 0); + if (nbufs < 1) + return UV_EINVAL; /* already sending a message */ if (handle->send_queue_count != 0) @@ -807,24 +647,11 @@ int uv__udp_try_send(uv_udp_t* handle, assert(handle->flags & UV_HANDLE_UDP_CONNECTED); } - memset(&h, 0, sizeof h); - h.msg_name = (struct sockaddr*) addr; - h.msg_namelen = addrlen; - h.msg_iov = (struct iovec*) bufs; - h.msg_iovlen = nbufs; + err = uv__udp_sendmsg1(handle->io_watcher.fd, bufs, nbufs, addr); + if (err > 0) + return uv__count_bufs(bufs, nbufs); - do { - size = sendmsg(handle->io_watcher.fd, &h, 0); - } while (size == -1 && errno == EINTR); - - if (size == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) - return UV_EAGAIN; - else - return UV__ERR(errno); - } - - return size; + return err; } @@ -1401,3 +1228,191 @@ int uv__udp_recv_stop(uv_udp_t* handle) { return 0; } + + +static int uv__udp_prep_pkt(struct msghdr* h, + const uv_buf_t* bufs, + const unsigned int nbufs, + const struct sockaddr* addr) { + memset(h, 0, sizeof(*h)); + h->msg_name = (void*) addr; + h->msg_iov = (void*) bufs; + h->msg_iovlen = nbufs; + if (addr == NULL) + return 0; + switch (addr->sa_family) { + case AF_INET: + h->msg_namelen = sizeof(struct sockaddr_in); + return 0; + case AF_INET6: + h->msg_namelen = sizeof(struct sockaddr_in6); + return 0; + case AF_UNIX: + h->msg_namelen = sizeof(struct sockaddr_un); + return 0; + case AF_UNSPEC: + h->msg_name = NULL; + return 0; + } + return UV_EINVAL; +} + + +static int uv__udp_sendmsg1(int fd, + const uv_buf_t* bufs, + unsigned int nbufs, + const struct sockaddr* addr) { + struct msghdr h; + int r; + + if ((r = uv__udp_prep_pkt(&h, bufs, nbufs, addr))) + return r; + + do + r = sendmsg(fd, &h, 0); + while (r == -1 && errno == EINTR); + + if (r < 0) { + r = UV__ERR(errno); + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + r = UV_EAGAIN; + return r; + } + + /* UDP sockets don't EOF so we don't have to handle r=0 specially, + * that only happens when the input was a zero-sized buffer. + */ + return 1; +} + + +static int uv__udp_sendmsgv(int fd, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/]) { + unsigned int i; + int nsent; + int r; + + r = 0; + nsent = 0; + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) + if (count > 1) { + for (i = 0; i < count; /*empty*/) { + struct mmsghdr m[20]; + unsigned int n; + + for (n = 0; i < count && n < ARRAY_SIZE(m); i++, n++) + if ((r = uv__udp_prep_pkt(&m[n].msg_hdr, bufs[i], nbufs[i], addrs[i]))) + goto exit; + + do +#if defined(__APPLE__) + r = sendmsg_x(fd, m, n, MSG_DONTWAIT); +#else + r = sendmmsg(fd, m, n, 0); +#endif + while (r == -1 && errno == EINTR); + + if (r < 1) + goto exit; + + nsent += r; + i += r; + } + + goto exit; + } +#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) */ + + for (i = 0; i < count; i++, nsent++) + if ((r = uv__udp_sendmsg1(fd, bufs[i], nbufs[i], addrs[i]))) + goto exit; /* goto to avoid unused label warning. */ + +exit: + + if (nsent > 0) + return nsent; + + if (r < 0) { + r = UV__ERR(errno); + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + r = UV_EAGAIN; + } + + return r; +} + + +static void uv__udp_sendmsg(uv_udp_t* handle) { + static const int N = 20; + struct sockaddr* addrs[N]; + unsigned int nbufs[N]; + uv_buf_t* bufs[N]; + struct uv__queue* q; + uv_udp_send_t* req; + int n; + + if (uv__queue_empty(&handle->write_queue)) + return; + +again: + n = 0; + q = uv__queue_head(&handle->write_queue); + do { + req = uv__queue_data(q, uv_udp_send_t, queue); + addrs[n] = &req->u.addr; + nbufs[n] = req->nbufs; + bufs[n] = req->bufs; + q = uv__queue_next(q); + n++; + } while (n < N && q != &handle->write_queue); + + n = uv__udp_sendmsgv(handle->io_watcher.fd, n, bufs, nbufs, addrs); + while (n > 0) { + q = uv__queue_head(&handle->write_queue); + req = uv__queue_data(q, uv_udp_send_t, queue); + req->status = uv__count_bufs(req->bufs, req->nbufs); + uv__queue_remove(&req->queue); + uv__queue_insert_tail(&handle->write_completed_queue, &req->queue); + n--; + } + + if (n == 0) { + if (uv__queue_empty(&handle->write_queue)) + goto feed; + goto again; + } + + if (n == UV_EAGAIN) + return; + + /* Register the error against first request in queue because that + * is the request that uv__udp_sendmsgv tried but failed to send, + * because if it did send any requests, it won't return an error. + */ + q = uv__queue_head(&handle->write_queue); + req = uv__queue_data(q, uv_udp_send_t, queue); + req->status = n; + uv__queue_remove(&req->queue); + uv__queue_insert_tail(&handle->write_completed_queue, &req->queue); +feed: + uv__io_feed(handle->loop, &handle->io_watcher); +} + + +int uv__udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/]) { + int fd; + + fd = handle->io_watcher.fd; + if (fd == -1) + return UV_EINVAL; + + return uv__udp_sendmsgv(fd, count, bufs, nbufs, addrs); +} diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index 2200fe3f0a41e2..60ff56b9dd7391 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -514,6 +514,25 @@ int uv_udp_try_send(uv_udp_t* handle, } +int uv_udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/], + unsigned int flags) { + if (count < 1) + return UV_EINVAL; + + if (flags != 0) + return UV_EINVAL; + + if (handle->send_queue_count > 0) + return UV_EAGAIN; + + return uv__udp_try_send2(handle, count, bufs, nbufs, addrs); +} + + int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) { @@ -644,6 +663,9 @@ int uv_send_buffer_size(uv_handle_t* handle, int *value) { int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) { size_t required_len; + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + if (!uv__is_active(handle)) { *size = 0; return UV_EINVAL; diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index 4baede2e506ee1..372f0c4b3ac39e 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -191,6 +191,12 @@ int uv__udp_try_send(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen); +int uv__udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/]); + int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb, uv_udp_recv_cb recv_cb); @@ -428,4 +434,18 @@ struct uv__loop_internal_fields_s { #endif /* __linux__ */ }; +#if defined(_WIN32) +# define UV_PTHREAD_MAX_NAMELEN_NP 32767 +#elif defined(__APPLE__) +# define UV_PTHREAD_MAX_NAMELEN_NP 64 +#elif defined(__NetBSD__) || defined(__illumos__) +# define UV_PTHREAD_MAX_NAMELEN_NP PTHREAD_MAX_NAMELEN_NP +#elif defined (__linux__) +# define UV_PTHREAD_MAX_NAMELEN_NP 16 +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +# define UV_PTHREAD_MAX_NAMELEN_NP (MAXCOMLEN + 1) +#else +# define UV_PTHREAD_MAX_NAMELEN_NP 16 +#endif + #endif /* UV_COMMON_H_ */ diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index e9885a0f1ff389..bc63b06673ac1a 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -423,97 +423,6 @@ int uv_backend_timeout(const uv_loop_t* loop) { } -static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) { - uv__loop_internal_fields_t* lfields; - DWORD bytes; - ULONG_PTR key; - OVERLAPPED* overlapped; - uv_req_t* req; - int repeat; - uint64_t timeout_time; - uint64_t user_timeout; - int reset_timeout; - - lfields = uv__get_internal_fields(loop); - timeout_time = loop->time + timeout; - - if (lfields->flags & UV_METRICS_IDLE_TIME) { - reset_timeout = 1; - user_timeout = timeout; - timeout = 0; - } else { - reset_timeout = 0; - } - - for (repeat = 0; ; repeat++) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - /* Store the current timeout in a location that's globally accessible so - * other locations like uv__work_done() can determine whether the queue - * of events in the callback were waiting when poll was called. - */ - lfields->current_timeout = timeout; - - GetQueuedCompletionStatus(loop->iocp, - &bytes, - &key, - &overlapped, - timeout); - - if (reset_timeout != 0) { - if (overlapped && timeout == 0) - uv__metrics_inc_events_waiting(loop, 1); - timeout = user_timeout; - reset_timeout = 0; - } - - /* Placed here because on success the loop will break whether there is an - * empty package or not, or if GetQueuedCompletionStatus returned early then - * the timeout will be updated and the loop will run again. In either case - * the idle time will need to be updated. - */ - uv__metrics_update_idle_time(loop); - - if (overlapped) { - uv__metrics_inc_events(loop, 1); - - /* Package was dequeued */ - req = uv__overlapped_to_req(overlapped); - uv__insert_pending_req(loop, req); - - /* Some time might have passed waiting for I/O, - * so update the loop time here. - */ - uv_update_time(loop); - } else if (GetLastError() != WAIT_TIMEOUT) { - /* Serious error */ - uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); - } else if (timeout > 0) { - /* GetQueuedCompletionStatus can occasionally return a little early. - * Make sure that the desired timeout target time is reached. - */ - uv_update_time(loop); - if (timeout_time > loop->time) { - timeout = (DWORD)(timeout_time - loop->time); - /* The first call to GetQueuedCompletionStatus should return very - * close to the target time and the second should reach it, but - * this is not stated in the documentation. To make sure a busy - * loop cannot happen, the timeout is increased exponentially - * starting on the third round. - */ - timeout += repeat ? (1 << (repeat - 1)) : 0; - continue; - } - } - break; - } -} - - static void uv__poll(uv_loop_t* loop, DWORD timeout) { uv__loop_internal_fields_t* lfields; BOOL success; @@ -553,12 +462,12 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { */ lfields->current_timeout = timeout; - success = pGetQueuedCompletionStatusEx(loop->iocp, - overlappeds, - ARRAY_SIZE(overlappeds), - &count, - timeout, - FALSE); + success = GetQueuedCompletionStatusEx(loop->iocp, + overlappeds, + ARRAY_SIZE(overlappeds), + &count, + timeout, + FALSE); if (reset_timeout != 0) { timeout = user_timeout; @@ -566,7 +475,7 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { } /* Placed here because on success the loop will break whether there is an - * empty package or not, or if pGetQueuedCompletionStatusEx returned early + * empty package or not, or if GetQueuedCompletionStatusEx returned early * then the timeout will be updated and the loop will run again. In either * case the idle time will need to be updated. */ @@ -647,10 +556,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { uv__metrics_inc_loop_count(loop); - if (pGetQueuedCompletionStatusEx) - uv__poll(loop, timeout); - else - uv__poll_wine(loop, timeout); + uv__poll(loop, timeout); /* Process immediate callbacks (e.g. write_cb) a small fixed number of * times to avoid loop starvation.*/ diff --git a/deps/uv/src/win/fs-event.c b/deps/uv/src/win/fs-event.c index 7ab407e05345f9..1bbb8c52be2d82 100644 --- a/deps/uv/src/win/fs-event.c +++ b/deps/uv/src/win/fs-event.c @@ -253,6 +253,8 @@ int uv_fs_event_start(uv_fs_event_t* handle, } dir_to_watch = dir; + uv__free(short_path); + short_path = NULL; uv__free(pathw); pathw = NULL; } @@ -577,6 +579,8 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req, info.DeletePending) { uv__convert_utf16_to_utf8(handle->dirw, -1, &filename); handle->cb(handle, filename, UV_RENAME, 0); + uv__free(filename); + filename = NULL; } else { handle->cb(handle, NULL, 0, uv_translate_sys_error(err)); } diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index f2215bb3082178..a4742aa2ec13fd 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -58,6 +58,19 @@ #define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x0010 #endif /* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE */ +NTSTATUS uv__RtlUnicodeStringInit( + PUNICODE_STRING DestinationString, + PWSTR SourceString, + size_t SourceStringLen +) { + if (SourceStringLen > 0x7FFF) + return STATUS_INVALID_PARAMETER; + DestinationString->MaximumLength = DestinationString->Length = + SourceStringLen * sizeof(SourceString[0]); + DestinationString->Buffer = SourceString; + return STATUS_SUCCESS; +} + #define INIT(subtype) \ do { \ if (req == NULL) \ @@ -1689,12 +1702,12 @@ INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path, uv_stat_t* statbuf, int do_lstat) { FILE_STAT_BASIC_INFORMATION stat_info; - // Check if the new fast API is available. + /* Check if the new fast API is available. */ if (!pGetFileInformationByName) { return FS__STAT_PATH_TRY_SLOW; } - // Check if the API call fails. + /* Check if the API call fails. */ if (!pGetFileInformationByName(path, FileStatBasicByNameInfo, &stat_info, sizeof(stat_info))) { switch(GetLastError()) { @@ -1708,7 +1721,7 @@ INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path, return FS__STAT_PATH_TRY_SLOW; } - // A file handle is needed to get st_size for links. + /* A file handle is needed to get st_size for links. */ if ((stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { return FS__STAT_PATH_TRY_SLOW; } @@ -1802,7 +1815,6 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, * detect this failure and retry without do_lstat if appropriate. */ if (fs__readlink_handle(handle, NULL, &target_length) != 0) { - fs__stat_assign_statbuf(statbuf, stat_info, do_lstat); return -1; } stat_info.EndOfFile.QuadPart = target_length; @@ -1941,6 +1953,179 @@ INLINE static void fs__stat_prepare_path(WCHAR* pathw) { } } +INLINE static DWORD fs__stat_directory(WCHAR* path, uv_stat_t* statbuf, + int do_lstat, DWORD ret_error) { + HANDLE handle = INVALID_HANDLE_VALUE; + FILE_STAT_BASIC_INFORMATION stat_info; + FILE_ID_FULL_DIR_INFORMATION dir_info; + FILE_FS_VOLUME_INFORMATION volume_info; + FILE_FS_DEVICE_INFORMATION device_info; + IO_STATUS_BLOCK io_status; + NTSTATUS nt_status; + WCHAR* path_dirpath = NULL; + WCHAR* path_filename = NULL; + UNICODE_STRING FileMask; + size_t len; + size_t split; + WCHAR splitchar; + int includes_name; + + /* AKA strtok or wcscspn, in reverse. */ + len = wcslen(path); + split = len; + + includes_name = 0; + while (split > 0 && path[split - 1] != L'\\' && path[split - 1] != L'/' && + path[split - 1] != L':') { + /* check if the path contains a character other than /,\,:,. */ + if (path[split-1] != '.') { + includes_name = 1; + } + split--; + } + /* If the path is a relative path with a file name or a folder name */ + if (split == 0 && includes_name) { + path_dirpath = L"."; + /* If there is a slash or a backslash */ + } else if (path[split - 1] == L'\\' || path[split - 1] == L'/') { + path_dirpath = path; + /* If there is no filename, consider it as a relative folder path */ + if (!includes_name) { + split = len; + /* Else, split it */ + } else { + splitchar = path[split - 1]; + path[split - 1] = L'\0'; + } + /* e.g. "..", "c:" */ + } else { + path_dirpath = path; + split = len; + } + path_filename = &path[split]; + + len = 0; + while (1) { + if (path_filename[len] == L'\0') + break; + if (path_filename[len] == L'*' || path_filename[len] == L'?' || + path_filename[len] == L'>' || path_filename[len] == L'<' || + path_filename[len] == L'"') { + ret_error = ERROR_INVALID_NAME; + goto cleanup; + } + len++; + } + + /* Get directory handle */ + handle = CreateFileW(path_dirpath, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + if (handle == INVALID_HANDLE_VALUE) { + ret_error = GetLastError(); + goto cleanup; + } + + /* Get files in the directory */ + nt_status = uv__RtlUnicodeStringInit(&FileMask, path_filename, len); + if (!NT_SUCCESS(nt_status)) { + ret_error = pRtlNtStatusToDosError(nt_status); + goto cleanup; + } + nt_status = pNtQueryDirectoryFile(handle, + NULL, + NULL, + NULL, + &io_status, + &dir_info, + sizeof(dir_info), + FileIdFullDirectoryInformation, + TRUE, + &FileMask, + TRUE); + + /* Buffer overflow (a warning status code) is expected here since there isn't + * enough space to store the FileName, and actually indicates success. */ + if (!NT_SUCCESS(nt_status) && nt_status != STATUS_BUFFER_OVERFLOW) { + if (nt_status == STATUS_NO_MORE_FILES) + ret_error = ERROR_PATH_NOT_FOUND; + else + ret_error = pRtlNtStatusToDosError(nt_status); + goto cleanup; + } + + /* Assign values to stat_info */ + memset(&stat_info, 0, sizeof(FILE_STAT_BASIC_INFORMATION)); + stat_info.FileAttributes = dir_info.FileAttributes; + stat_info.CreationTime.QuadPart = dir_info.CreationTime.QuadPart; + stat_info.LastAccessTime.QuadPart = dir_info.LastAccessTime.QuadPart; + stat_info.LastWriteTime.QuadPart = dir_info.LastWriteTime.QuadPart; + if (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + /* A file handle is needed to get st_size for the link (from + * FSCTL_GET_REPARSE_POINT), which is required by posix, but we are here + * because getting the file handle failed. We could get just the + * ReparsePointTag by querying FILE_ID_EXTD_DIR_INFORMATION instead to make + * sure this really is a link before giving up here on the uv_fs_stat call, + * but that doesn't seem essential. */ + if (!do_lstat) + goto cleanup; + stat_info.EndOfFile.QuadPart = 0; + stat_info.AllocationSize.QuadPart = 0; + } else { + stat_info.EndOfFile.QuadPart = dir_info.EndOfFile.QuadPart; + stat_info.AllocationSize.QuadPart = dir_info.AllocationSize.QuadPart; + } + stat_info.ChangeTime.QuadPart = dir_info.ChangeTime.QuadPart; + stat_info.FileId.QuadPart = dir_info.FileId.QuadPart; + + /* Finish up by getting device info from the directory handle, + * since files presumably must live on their device. */ + nt_status = pNtQueryVolumeInformationFile(handle, + &io_status, + &volume_info, + sizeof volume_info, + FileFsVolumeInformation); + + /* Buffer overflow (a warning status code) is expected here. */ + if (io_status.Status == STATUS_NOT_IMPLEMENTED) { + stat_info.VolumeSerialNumber.QuadPart = 0; + } else if (NT_ERROR(nt_status)) { + ret_error = pRtlNtStatusToDosError(nt_status); + goto cleanup; + } else { + stat_info.VolumeSerialNumber.QuadPart = volume_info.VolumeSerialNumber; + } + + nt_status = pNtQueryVolumeInformationFile(handle, + &io_status, + &device_info, + sizeof device_info, + FileFsDeviceInformation); + + /* Buffer overflow (a warning status code) is expected here. */ + if (NT_ERROR(nt_status)) { + ret_error = pRtlNtStatusToDosError(nt_status); + goto cleanup; + } + + stat_info.DeviceType = device_info.DeviceType; + stat_info.NumberOfLinks = 1; /* No way to recover this info. */ + + fs__stat_assign_statbuf(statbuf, stat_info, do_lstat); + ret_error = 0; + +cleanup: + if (split != 0) + path[split - 1] = splitchar; + if (handle != INVALID_HANDLE_VALUE) + CloseHandle(handle); + return ret_error; +} INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, int do_lstat, @@ -1949,7 +2134,7 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, DWORD flags; DWORD ret; - // If new API exists, try to use it. + /* If new API exists, try to use it. */ switch (fs__stat_path(path, statbuf, do_lstat)) { case FS__STAT_PATH_SUCCESS: return 0; @@ -1959,7 +2144,7 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, break; } - // If the new API does not exist, use the old API. + /* If the new API does not exist, use the old API. */ flags = FILE_FLAG_BACKUP_SEMANTICS; if (do_lstat) flags |= FILE_FLAG_OPEN_REPARSE_POINT; @@ -1972,8 +2157,12 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, flags, NULL); - if (handle == INVALID_HANDLE_VALUE) - return GetLastError(); + if (handle == INVALID_HANDLE_VALUE) { + ret = GetLastError(); + if (ret != ERROR_ACCESS_DENIED && ret != ERROR_SHARING_VIOLATION) + return ret; + return fs__stat_directory(path, statbuf, do_lstat, ret); + } if (fs__stat_handle(handle, statbuf, do_lstat) != 0) ret = GetLastError(); diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index d46ecb9fc702e6..d05bfd28aec8b9 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -1161,9 +1161,9 @@ int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) { err = uv__tcp_xfer_import( (uv_tcp_t*) client, item->xfer_type, &item->xfer_info); - + uv__free(item); - + if (err != 0) return err; @@ -1738,7 +1738,7 @@ static DWORD uv__pipe_get_ipc_remote_pid(uv_pipe_t* handle) { GetNamedPipeServerProcessId(handle->handle, pid); } } - + return *pid; } @@ -2602,6 +2602,9 @@ int uv_pipe_pending_count(uv_pipe_t* handle) { int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) { + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + if (handle->flags & UV_HANDLE_BOUND) return uv__pipe_getname(handle, buffer, size); @@ -2616,6 +2619,9 @@ int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) { int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) { + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + /* emulate unix behaviour */ if (handle->flags & UV_HANDLE_BOUND) return UV_ENOTCONN; diff --git a/deps/uv/src/win/thread.c b/deps/uv/src/win/thread.c index bf39b88633b0d8..436846a716807e 100644 --- a/deps/uv/src/win/thread.c +++ b/deps/uv/src/win/thread.c @@ -95,6 +95,15 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { return uv_thread_create_ex(tid, ¶ms, entry, arg); } + +int uv_thread_detach(uv_thread_t *tid) { + if (CloseHandle(*tid) == 0) + return uv_translate_sys_error(GetLastError()); + + return 0; +} + + int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, void (*entry)(void *arg), @@ -269,6 +278,71 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) { } +int uv_thread_setname(const char* name) { + HRESULT hr; + WCHAR* namew; + int err; + char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; + + if (name == NULL) + return UV_EINVAL; + + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + + namew = NULL; + err = uv__convert_utf8_to_utf16(namebuf, &namew); + if (err) + return err; + + hr = SetThreadDescription(GetCurrentThread(), namew); + uv__free(namew); + if (FAILED(hr)) + return uv_translate_sys_error(HRESULT_CODE(hr)); + + return 0; +} + + +int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) { + HRESULT hr; + WCHAR* namew; + char* thread_name; + size_t buf_size; + int r; + DWORD exit_code; + + if (name == NULL || size == 0) + return UV_EINVAL; + + if (tid == NULL || *tid == NULL) + return UV_EINVAL; + + /* Check if the thread handle is valid */ + if (!GetExitCodeThread(*tid, &exit_code) || exit_code != STILL_ACTIVE) + return UV_ENOENT; + + namew = NULL; + thread_name = NULL; + hr = GetThreadDescription(*tid, &namew); + if (FAILED(hr)) + return uv_translate_sys_error(HRESULT_CODE(hr)); + + buf_size = size; + r = uv__copy_utf16_to_utf8(namew, -1, name, &buf_size); + if (r == UV_ENOBUFS) { + r = uv__convert_utf16_to_utf8(namew, wcslen(namew), &thread_name); + if (r == 0) { + uv__strscpy(name, thread_name, size); + uv__free(thread_name); + } + } + + LocalFree(namew); + return r; +} + + int uv_mutex_init(uv_mutex_t* mutex) { InitializeCriticalSection(mutex); return 0; diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index 5c8f6e1dd0b449..e0873c2a899c24 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -1101,7 +1101,8 @@ int uv__udp_try_send(uv_udp_t* handle, struct sockaddr_storage converted; int err; - assert(nbufs > 0); + if (nbufs < 1) + return UV_EINVAL; if (addr != NULL) { err = uv__convert_to_localhost_if_unspecified(addr, &converted); @@ -1141,3 +1142,21 @@ int uv__udp_try_send(uv_udp_t* handle, return bytes; } + + +int uv__udp_try_send2(uv_udp_t* handle, + unsigned int count, + uv_buf_t* bufs[/*count*/], + unsigned int nbufs[/*count*/], + struct sockaddr* addrs[/*count*/]) { + unsigned int i; + int r; + + for (i = 0; i < count; i++) { + r = uv_udp_try_send(handle, bufs[i], nbufs[i], addrs[i]); + if (r < 0) + return i > 0 ? i : r; /* Error if first packet, else send count. */ + } + + return i; +} diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c index e0dba1aaa94e28..1d1b2837e1a190 100644 --- a/deps/uv/src/win/util.c +++ b/deps/uv/src/win/util.c @@ -191,7 +191,7 @@ int uv_cwd(char* buffer, size_t* size) { WCHAR *utf16_buffer; int r; - if (buffer == NULL || size == NULL) { + if (buffer == NULL || size == NULL || *size == 0) { return UV_EINVAL; } @@ -874,56 +874,100 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, int uv_getrusage(uv_rusage_t *uv_rusage) { - FILETIME createTime, exitTime, kernelTime, userTime; - SYSTEMTIME kernelSystemTime, userSystemTime; - PROCESS_MEMORY_COUNTERS memCounters; - IO_COUNTERS ioCounters; + FILETIME create_time, exit_time, kernel_time, user_time; + SYSTEMTIME kernel_system_time, user_system_time; + PROCESS_MEMORY_COUNTERS mem_counters; + IO_COUNTERS io_counters; int ret; - ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime); + ret = GetProcessTimes(GetCurrentProcess(), + &create_time, + &exit_time, + &kernel_time, + &user_time); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } - ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime); + ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } - ret = FileTimeToSystemTime(&userTime, &userSystemTime); + ret = FileTimeToSystemTime(&user_time, &user_system_time); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } ret = GetProcessMemoryInfo(GetCurrentProcess(), - &memCounters, - sizeof(memCounters)); + &mem_counters, + sizeof(mem_counters)); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } - ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters); + ret = GetProcessIoCounters(GetCurrentProcess(), &io_counters); if (ret == 0) { return uv_translate_sys_error(GetLastError()); } memset(uv_rusage, 0, sizeof(*uv_rusage)); - uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 + - userSystemTime.wMinute * 60 + - userSystemTime.wSecond; - uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000; + uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 + + user_system_time.wMinute * 60 + + user_system_time.wSecond; + uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000; - uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 + - kernelSystemTime.wMinute * 60 + - kernelSystemTime.wSecond; - uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000; + uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 + + kernel_system_time.wMinute * 60 + + kernel_system_time.wSecond; + uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000; - uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount; - uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024; + uv_rusage->ru_majflt = (uint64_t) mem_counters.PageFaultCount; + uv_rusage->ru_maxrss = (uint64_t) mem_counters.PeakWorkingSetSize / 1024; - uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount; - uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount; + uv_rusage->ru_oublock = (uint64_t) io_counters.WriteOperationCount; + uv_rusage->ru_inblock = (uint64_t) io_counters.ReadOperationCount; + + return 0; +} + + +int uv_getrusage_thread(uv_rusage_t* uv_rusage) { + FILETIME create_time, exit_time, kernel_time, user_time; + SYSTEMTIME kernel_system_time, user_system_time; + int ret; + + ret = GetThreadTimes(GetCurrentThread(), + &create_time, + &exit_time, + &kernel_time, + &user_time); + if (ret == 0) { + return uv_translate_sys_error(GetLastError()); + } + + ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time); + if (ret == 0) { + return uv_translate_sys_error(GetLastError()); + } + + ret = FileTimeToSystemTime(&user_time, &user_system_time); + if (ret == 0) { + return uv_translate_sys_error(GetLastError()); + } + + memset(uv_rusage, 0, sizeof(*uv_rusage)); + + uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 + + user_system_time.wMinute * 60 + + user_system_time.wSecond; + uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000; + + uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 + + kernel_system_time.wMinute * 60 + + kernel_system_time.wSecond; + uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000; return 0; } @@ -1589,7 +1633,7 @@ int uv_os_uname(uv_utsname_t* buffer) { version_size = sizeof(buffer->version) - version_size; r = uv__copy_utf16_to_utf8(os_info.szCSDVersion, -1, - buffer->version + + buffer->version + sizeof(buffer->version) - version_size, &version_size); if (r) diff --git a/deps/uv/src/win/winapi.c b/deps/uv/src/win/winapi.c index a74108db03e701..315a0d49aff50b 100644 --- a/deps/uv/src/win/winapi.c +++ b/deps/uv/src/win/winapi.c @@ -36,9 +36,6 @@ sNtQueryDirectoryFile pNtQueryDirectoryFile; sNtQuerySystemInformation pNtQuerySystemInformation; sNtQueryInformationProcess pNtQueryInformationProcess; -/* Kernel32 function pointers */ -sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; - /* Powrprof.dll function pointer */ sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; @@ -55,7 +52,6 @@ void uv__winapi_init(void) { HMODULE ntdll_module; HMODULE powrprof_module; HMODULE user32_module; - HMODULE kernel32_module; HMODULE ws2_32_module; HMODULE api_win_core_file_module; @@ -121,15 +117,6 @@ void uv__winapi_init(void) { uv_fatal_error(GetLastError(), "GetProcAddress"); } - kernel32_module = GetModuleHandleA("kernel32.dll"); - if (kernel32_module == NULL) { - uv_fatal_error(GetLastError(), "GetModuleHandleA"); - } - - pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress( - kernel32_module, - "GetQueuedCompletionStatusEx"); - powrprof_module = LoadLibraryExA("powrprof.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); if (powrprof_module != NULL) { pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification) diff --git a/deps/uv/src/win/winapi.h b/deps/uv/src/win/winapi.h index 5800e70dfd7d11..4e0ccc61baf225 100644 --- a/deps/uv/src/win/winapi.h +++ b/deps/uv/src/win/winapi.h @@ -4150,40 +4150,35 @@ typedef struct _FILE_STAT_BASIC_INFORMATION { } FILE_STAT_BASIC_INFORMATION; #endif -/* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does - * not. - */ -#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR) - typedef struct _REPARSE_DATA_BUFFER { - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - union { - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - struct { - ULONG StringCount; - WCHAR StringList[1]; - } AppExecLinkReparseBuffer; - }; - } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; -#endif +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + struct { + ULONG StringCount; + WCHAR StringList[1]; + } AppExecLinkReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; typedef struct _IO_STATUS_BLOCK { union { @@ -4292,6 +4287,22 @@ typedef struct _FILE_BOTH_DIR_INFORMATION { WCHAR FileName[1]; } FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; +typedef struct _FILE_ID_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION; + typedef struct _FILE_BASIC_INFORMATION { LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; @@ -4661,15 +4672,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess) # define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 #endif -#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) - typedef struct _OVERLAPPED_ENTRY { - ULONG_PTR lpCompletionKey; - LPOVERLAPPED lpOverlapped; - ULONG_PTR Internal; - DWORD dwNumberOfBytesTransferred; - } OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY; -#endif - /* from wincon.h */ #ifndef ENABLE_INSERT_MODE # define ENABLE_INSERT_MODE 0x20 @@ -4716,14 +4718,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess) # define ERROR_MUI_FILE_NOT_LOADED 15105 #endif -typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx) - (HANDLE CompletionPort, - LPOVERLAPPED_ENTRY lpCompletionPortEntries, - ULONG ulCount, - PULONG ulNumEntriesRemoved, - DWORD dwMilliseconds, - BOOL fAlertable); - /* from powerbase.h */ #ifndef DEVICE_NOTIFY_CALLBACK # define DEVICE_NOTIFY_CALLBACK 2 @@ -4818,9 +4812,6 @@ extern sNtQueryDirectoryFile pNtQueryDirectoryFile; extern sNtQuerySystemInformation pNtQuerySystemInformation; extern sNtQueryInformationProcess pNtQueryInformationProcess; -/* Kernel32 function pointers */ -extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; - /* Powrprof.dll function pointer */ extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; @@ -4837,4 +4828,13 @@ typedef int (WINAPI *uv_sGetHostNameW) int); extern uv_sGetHostNameW pGetHostNameW; +/* processthreadsapi.h */ +#if defined(__MINGW32__) +WINBASEAPI +HRESULT WINAPI GetThreadDescription(HANDLE hThread, + PWSTR *ppszThreadDescription); +WINBASEAPI +HRESULT WINAPI SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription); +#endif + #endif /* UV_WIN_WINAPI_H_ */ diff --git a/deps/uv/src/win/winsock.h b/deps/uv/src/win/winsock.h index 2af958870a7de6..bb3808a35c27e6 100644 --- a/deps/uv/src/win/winsock.h +++ b/deps/uv/src/win/winsock.h @@ -154,47 +154,6 @@ typedef struct _AFD_RECV_INFO { #define IOCTL_AFD_POLL \ _AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED) -#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) -typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP { - /* FIXME: __C89_NAMELESS was removed */ - /* __C89_NAMELESS */ union { - ULONGLONG Alignment; - /* __C89_NAMELESS */ struct { - ULONG Length; - DWORD Flags; - }; - }; - struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next; - SOCKET_ADDRESS Address; - IP_PREFIX_ORIGIN PrefixOrigin; - IP_SUFFIX_ORIGIN SuffixOrigin; - IP_DAD_STATE DadState; - ULONG ValidLifetime; - ULONG PreferredLifetime; - ULONG LeaseLifetime; -} IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP; - -typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH { - union { - ULONGLONG Alignment; - struct { - ULONG Length; - DWORD Flags; - }; - }; - struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next; - SOCKET_ADDRESS Address; - IP_PREFIX_ORIGIN PrefixOrigin; - IP_SUFFIX_ORIGIN SuffixOrigin; - IP_DAD_STATE DadState; - ULONG ValidLifetime; - ULONG PreferredLifetime; - ULONG LeaseLifetime; - UINT8 OnLinkPrefixLength; -} IP_ADAPTER_UNICAST_ADDRESS_LH,*PIP_ADAPTER_UNICAST_ADDRESS_LH; - -#endif - int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr, struct sockaddr_storage* storage); diff --git a/deps/uv/test/runner.c b/deps/uv/test/runner.c index d1dd02f5ce0806..54abb39dd22886 100644 --- a/deps/uv/test/runner.c +++ b/deps/uv/test/runner.c @@ -27,6 +27,11 @@ #include "task.h" #include "uv.h" +/* Refs: https://github.com/libuv/libuv/issues/4369 */ +#if defined(__ANDROID__) +#include +#endif + char executable_path[sizeof(executable_path)]; @@ -142,6 +147,13 @@ void log_tap_result(int test_count, fflush(stdout); } +void enable_fdsan(void) { +/* Refs: https://github.com/libuv/libuv/issues/4369 */ +#if defined(__ANDROID__) + android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS); +#endif +} + int run_test(const char* test, int benchmark_output, @@ -160,6 +172,8 @@ int run_test(const char* test, main_proc = NULL; process_count = 0; + enable_fdsan(); + #ifndef _WIN32 /* Clean up stale socket from previous run. */ remove(TEST_PIPENAME); diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c index bb223a5f654c03..b53057dc25bb22 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -153,7 +153,14 @@ static void fs_event_cb_del_dir(uv_fs_event_t* handle, ASSERT_PTR_EQ(handle, &fs_event); ASSERT_OK(status); ASSERT(events == UV_CHANGE || events == UV_RENAME); + /* There is a bug in the FreeBSD kernel where the filename is sometimes NULL. + * Refs: https://github.com/libuv/libuv/issues/4606 + */ + #if defined(__FreeBSD__) + ASSERT(filename == NULL || strcmp(filename, "watch_del_dir") == 0); + #else ASSERT_OK(strcmp(filename, "watch_del_dir")); + #endif ASSERT_OK(uv_fs_event_stop(handle)); uv_close((uv_handle_t*)handle, close_cb); } @@ -1121,7 +1128,7 @@ TEST_IMPL(fs_event_getpath) { ASSERT_EQ(r, UV_EINVAL); r = uv_fs_event_start(&fs_event, fail_cb, watch_dir[i], 0); ASSERT_OK(r); - len = 0; + len = 1; r = uv_fs_event_getpath(&fs_event, buf, &len); ASSERT_EQ(r, UV_ENOBUFS); ASSERT_LT(len, sizeof buf); /* sanity check */ diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index 33cbd428707c36..423d72dd2f7b84 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -4507,6 +4507,60 @@ TEST_IMPL(fs_open_readonly_acl) { MAKE_VALGRIND_HAPPY(loop); return 0; } + +TEST_IMPL(fs_stat_no_permission) { + uv_passwd_t pwd; + uv_fs_t req; + int r; + char* filename = "test_file_no_permission.txt"; + + /* Setup - clear the ACL and remove the file */ + loop = uv_default_loop(); + r = uv_os_get_passwd(&pwd); + ASSERT_OK(r); + call_icacls("icacls %s /remove *S-1-1-0:(F)", filename); + unlink(filename); + + /* Create the file */ + r = uv_fs_open(loop, + &open_req1, + filename, + UV_FS_O_RDONLY | UV_FS_O_CREAT, + S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT_OK(r); + ASSERT_OK(close_req.result); + uv_fs_req_cleanup(&close_req); + + /* Set up ACL */ + r = call_icacls("icacls %s /deny *S-1-1-0:(F)", filename); + if (r != 0) { + goto acl_cleanup; + } + + /* Read file stats */ + r = uv_fs_stat(NULL, &req, filename, NULL); + if (r != 0) { + goto acl_cleanup; + } + + uv_fs_req_cleanup(&req); + + acl_cleanup: + /* Cleanup */ + call_icacls("icacls %s /reset", filename); + uv_fs_unlink(NULL, &unlink_req, filename, NULL); + uv_fs_req_cleanup(&unlink_req); + unlink(filename); + uv_os_free_passwd(&pwd); + ASSERT_OK(r); + MAKE_VALGRIND_HAPPY(loop); + return 0; +} #endif #ifdef _WIN32 diff --git a/deps/uv/test/test-idna.c b/deps/uv/test/test-idna.c index 28f9eaaae9e77a..46df9f3c581015 100644 --- a/deps/uv/test/test-idna.c +++ b/deps/uv/test/test-idna.c @@ -39,7 +39,7 @@ TEST_IMPL(utf8_decode1) { /* Two-byte sequences. */ p = b; - snprintf(b, sizeof(b), "\xC2\x80\xDF\xBF"); + snprintf(b, sizeof(b), "%s", "\xC2\x80\xDF\xBF"); ASSERT_EQ(128, uv__utf8_decode1(&p, b + sizeof(b))); ASSERT_PTR_EQ(p, b + 2); ASSERT_EQ(0x7FF, uv__utf8_decode1(&p, b + sizeof(b))); @@ -47,7 +47,7 @@ TEST_IMPL(utf8_decode1) { /* Three-byte sequences. */ p = b; - snprintf(b, sizeof(b), "\xE0\xA0\x80\xEF\xBF\xBF"); + snprintf(b, sizeof(b), "%s", "\xE0\xA0\x80\xEF\xBF\xBF"); ASSERT_EQ(0x800, uv__utf8_decode1(&p, b + sizeof(b))); ASSERT_PTR_EQ(p, b + 3); ASSERT_EQ(0xFFFF, uv__utf8_decode1(&p, b + sizeof(b))); @@ -55,7 +55,7 @@ TEST_IMPL(utf8_decode1) { /* Four-byte sequences. */ p = b; - snprintf(b, sizeof(b), "\xF0\x90\x80\x80\xF4\x8F\xBF\xBF"); + snprintf(b, sizeof(b), "%s", "\xF0\x90\x80\x80\xF4\x8F\xBF\xBF"); ASSERT_EQ(0x10000, uv__utf8_decode1(&p, b + sizeof(b))); ASSERT_PTR_EQ(p, b + 4); ASSERT_EQ(0x10FFFF, uv__utf8_decode1(&p, b + sizeof(b))); @@ -63,7 +63,7 @@ TEST_IMPL(utf8_decode1) { /* Four-byte sequences > U+10FFFF; disallowed. */ p = b; - snprintf(b, sizeof(b), "\xF4\x90\xC0\xC0\xF7\xBF\xBF\xBF"); + snprintf(b, sizeof(b), "%s", "\xF4\x90\xC0\xC0\xF7\xBF\xBF\xBF"); ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); ASSERT_PTR_EQ(p, b + 4); ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); @@ -71,7 +71,7 @@ TEST_IMPL(utf8_decode1) { /* Overlong; disallowed. */ p = b; - snprintf(b, sizeof(b), "\xC0\x80\xC1\x80"); + snprintf(b, sizeof(b), "%s", "\xC0\x80\xC1\x80"); ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); ASSERT_PTR_EQ(p, b + 2); ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); @@ -79,7 +79,7 @@ TEST_IMPL(utf8_decode1) { /* Surrogate pairs; disallowed. */ p = b; - snprintf(b, sizeof(b), "\xED\xA0\x80\xED\xA3\xBF"); + snprintf(b, sizeof(b), "%s", "\xED\xA0\x80\xED\xA3\xBF"); ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); ASSERT_PTR_EQ(p, b + 3); ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); @@ -87,7 +87,7 @@ TEST_IMPL(utf8_decode1) { /* Simply illegal. */ p = b; - snprintf(b, sizeof(b), "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"); + snprintf(b, sizeof(b), "%s", "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"); for (i = 1; i <= 8; i++) { ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); @@ -218,3 +218,15 @@ TEST_IMPL(idna_toascii) { #undef T #endif /* __MVS__ */ + +TEST_IMPL(wtf8) { + static const char input[] = "ᜄȺy𐞲:𞢢𘴇𐀀'¥3̞[ + +struct semaphores { + uv_sem_t main; + uv_sem_t worker; +}; + +static void thread_run(void* arg) { + int r; + char thread_name[16]; + struct semaphores* sem; + uv_thread_t thread; + + sem = arg; + +#ifdef _WIN32 + /* uv_thread_self isn't defined for the main thread on Windows. */ + thread = GetCurrentThread(); +#else + thread = uv_thread_self(); +#endif + + r = uv_thread_setname("worker-thread"); + ASSERT_OK(r); + + uv_sem_post(&sem->worker); + + r = uv_thread_getname(&thread, thread_name, sizeof(thread_name)); + ASSERT_OK(r); + + ASSERT_STR_EQ(thread_name, "worker-thread"); + + uv_sem_wait(&sem->main); +} + +TEST_IMPL(thread_name) { + int r; + uv_thread_t threads[2]; + char tn[UV_PTHREAD_MAX_NAMELEN_NP]; + char thread_name[UV_PTHREAD_MAX_NAMELEN_NP]; + char long_thread_name[UV_PTHREAD_MAX_NAMELEN_NP + 1]; + struct semaphores sem; + +#if defined(__ANDROID_API__) && __ANDROID_API__ < 26 || \ + defined(_AIX) || \ + defined(__MVS__) || \ + defined(__PASE__) + RETURN_SKIP("API not available on this platform"); +#endif + + ASSERT_OK(uv_sem_init(&sem.main, 0)); + ASSERT_OK(uv_sem_init(&sem.worker, 0)); + + memset(thread_name, 'a', sizeof(thread_name) - 1); + thread_name[sizeof(thread_name) - 1] = '\0'; + + memset(long_thread_name, 'a', sizeof(long_thread_name) - 1); + long_thread_name[sizeof(long_thread_name) - 1] = '\0'; + +#ifdef _WIN32 + /* uv_thread_self isn't defined for the main thread on Windows. */ + threads[0] = GetCurrentThread(); +#else + threads[0] = uv_thread_self(); +#endif + + r = uv_thread_getname(&threads[0], tn, sizeof(tn)); + ASSERT_OK(r); + + r = uv_thread_setname(long_thread_name); + ASSERT_OK(r); + + r = uv_thread_getname(&threads[0], tn, sizeof(tn)); + ASSERT_OK(r); + ASSERT_STR_EQ(tn, thread_name); + + r = uv_thread_setname(thread_name); + ASSERT_OK(r); + + r = uv_thread_getname(&threads[0], tn, sizeof(tn)); + ASSERT_OK(r); + ASSERT_STR_EQ(tn, thread_name); + + r = uv_thread_getname(&threads[0], tn, 3); + ASSERT_OK(r); + ASSERT_EQ(strlen(tn), 2); + ASSERT_OK(memcmp(thread_name, tn, 2)); + + /* Illumos doesn't support non-ASCII thread names. */ +#ifndef __illumos__ + r = uv_thread_setname("~½¬{½"); + ASSERT_OK(r); + + r = uv_thread_getname(&threads[0], tn, sizeof(tn)); + ASSERT_OK(r); + ASSERT_STR_EQ(tn, "~½¬{½"); +#endif + + ASSERT_OK(uv_thread_create(threads + 1, thread_run, &sem)); + + uv_sem_wait(&sem.worker); + + r = uv_thread_getname(threads + 1, tn, sizeof(tn)); + ASSERT_OK(r); + + ASSERT_STR_EQ(tn, "worker-thread"); + + uv_sem_post(&sem.main); + + ASSERT_OK(uv_thread_join(threads + 1)); + + uv_sem_destroy(&sem.main); + uv_sem_destroy(&sem.worker); + + return 0; +} + +#define MAX_THREADS 4 + +static void* executedThreads[MAX_THREADS] = { NULL }; +static int size; +static uv_loop_t* loop; + +static unsigned short int key_exists(void* key) { + size_t i; + for (i = 0; i < MAX_THREADS; i++) { + if (executedThreads[i] == key) { + return 1; + } + } + return 0; +} + +static void work_cb(uv_work_t* req) { + uv_thread_t thread = uv_thread_self(); + req->data = &thread; + char tn[UV_PTHREAD_MAX_NAMELEN_NP]; + ASSERT_OK(uv_thread_getname(&thread, tn, sizeof(tn))); + ASSERT_STR_EQ(tn, "libuv-worker"); +} + +static void after_work_cb(uv_work_t* req, int status) { + ASSERT_OK(status); + if (!key_exists(req->data)) { + executedThreads[size++] = req->data; + } + + if (size == MAX_THREADS) { + return; + } + + uv_queue_work(loop, req, work_cb, after_work_cb); +} + +TEST_IMPL(thread_name_threadpool) { + uv_work_t req; + loop = uv_default_loop(); + // Just to make sure all workers will be executed + // with the correct thread name + ASSERT_OK(uv_queue_work(loop, &req, work_cb, after_work_cb)); + uv_run(loop, UV_RUN_DEFAULT); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} diff --git a/deps/uv/test/test-thread.c b/deps/uv/test/test-thread.c index d0094e304435bb..819bbd5c92399d 100644 --- a/deps/uv/test/test-thread.c +++ b/deps/uv/test/test-thread.c @@ -294,3 +294,13 @@ TEST_IMPL(thread_stack_size_explicit) { return 0; } + +static void thread_detach_cb(void* arg) {} + +TEST_IMPL(thread_detach) { + uv_thread_t thread; + ASSERT_OK(uv_thread_create(&thread, thread_detach_cb, NULL)); + ASSERT_OK(uv_thread_detach(&thread)); + + return 0; +} diff --git a/deps/uv/test/test-udp-mmsg.c b/deps/uv/test/test-udp-mmsg.c index c0e000b9d92bbf..73213c43d97aa2 100644 --- a/deps/uv/test/test-udp-mmsg.c +++ b/deps/uv/test/test-udp-mmsg.c @@ -32,12 +32,12 @@ #define BUFFER_MULTIPLIER 20 #define MAX_DGRAM_SIZE (64 * 1024) #define NUM_SENDS 40 -#define EXPECTED_MMSG_ALLOCS (NUM_SENDS / BUFFER_MULTIPLIER) static uv_udp_t recver; static uv_udp_t sender; static int recv_cb_called; static int received_datagrams; +static int read_bytes; static int close_cb_called; static int alloc_cb_called; @@ -74,6 +74,7 @@ static void recv_cb(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags) { ASSERT_GE(nread, 0); + read_bytes += nread; /* free and return if this is a mmsg free-only callback invocation */ if (flags & UV_UDP_MMSG_FREE) { @@ -140,7 +141,7 @@ TEST_IMPL(udp_mmsg) { /* On platforms that don't support mmsg, each recv gets its own alloc */ if (uv_udp_using_recvmmsg(&recver)) - ASSERT_EQ(alloc_cb_called, EXPECTED_MMSG_ALLOCS); + ASSERT_EQ(read_bytes, NUM_SENDS * 4); /* we're sending 4 bytes per datagram */ else ASSERT_EQ(alloc_cb_called, recv_cb_called); diff --git a/deps/uv/test/test-udp-multicast-join.c b/deps/uv/test/test-udp-multicast-join.c index 9e322dc579fc33..58b055561c6ded 100644 --- a/deps/uv/test/test-udp-multicast-join.c +++ b/deps/uv/test/test-udp-multicast-join.c @@ -36,10 +36,9 @@ static uv_udp_t client; static uv_udp_send_t req; static uv_udp_send_t req_ss; +static int darwin_ebusy_errors; static int cl_recv_cb_called; - static int sv_send_cb_called; - static int close_cb_called; static void alloc_cb(uv_handle_t* handle, @@ -128,6 +127,13 @@ static void cl_recv_cb(uv_udp_t* handle, #if !defined(__NetBSD__) r = uv_udp_set_source_membership(&server, MULTICAST_ADDR, NULL, source_addr, UV_JOIN_GROUP); +#if defined(__APPLE__) + if (r == UV_EBUSY) { + uv_close((uv_handle_t*) &server, close_cb); + darwin_ebusy_errors++; + return; + } +#endif ASSERT_OK(r); #endif @@ -160,7 +166,13 @@ TEST_IMPL(udp_multicast_join) { r = uv_udp_set_membership(&server, MULTICAST_ADDR, NULL, UV_JOIN_GROUP); if (r == UV_ENODEV) RETURN_SKIP("No multicast support."); + if (r == UV_ENOEXEC) + RETURN_SKIP("No multicast support (likely a firewall issue)."); ASSERT_OK(r); +#if defined(__ANDROID__) + /* It returns an ENOSYS error */ + RETURN_SKIP("Test does not currently work in ANDROID"); +#endif r = uv_udp_recv_start(&server, alloc_cb, cl_recv_cb); ASSERT_OK(r); @@ -175,6 +187,9 @@ TEST_IMPL(udp_multicast_join) { /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); + if (darwin_ebusy_errors > 0) + RETURN_SKIP("Unexplained macOS IP_ADD_SOURCE_MEMBERSHIP EBUSY bug"); + ASSERT_EQ(2, cl_recv_cb_called); ASSERT_EQ(2, sv_send_cb_called); ASSERT_EQ(2, close_cb_called); diff --git a/deps/uv/test/test-udp-multicast-join6.c b/deps/uv/test/test-udp-multicast-join6.c index c6872e4283247d..430e4e3321e859 100644 --- a/deps/uv/test/test-udp-multicast-join6.c +++ b/deps/uv/test/test-udp-multicast-join6.c @@ -33,6 +33,7 @@ #if defined(__APPLE__) || \ defined(_AIX) || \ defined(__MVS__) || \ + defined(__FreeBSD__) || \ defined(__NetBSD__) || \ defined(__OpenBSD__) #define MULTICAST_ADDR "ff02::1%lo0" diff --git a/deps/uv/test/test-udp-try-send.c b/deps/uv/test/test-udp-try-send.c index 0c76fb1c84df68..6181fbbbffca3b 100644 --- a/deps/uv/test/test-udp-try-send.c +++ b/deps/uv/test/test-udp-try-send.c @@ -60,8 +60,6 @@ static void sv_recv_cb(uv_udp_t* handle, const uv_buf_t* rcvbuf, const struct sockaddr* addr, unsigned flags) { - ASSERT_GT(nread, 0); - if (nread == 0) { ASSERT_NULL(addr); return; @@ -70,11 +68,17 @@ static void sv_recv_cb(uv_udp_t* handle, ASSERT_EQ(4, nread); ASSERT_NOT_NULL(addr); - ASSERT_OK(memcmp("EXIT", rcvbuf->base, nread)); - uv_close((uv_handle_t*) handle, close_cb); - uv_close((uv_handle_t*) &client, close_cb); + if (!memcmp("EXIT", rcvbuf->base, nread)) { + uv_close((uv_handle_t*) handle, close_cb); + uv_close((uv_handle_t*) &client, close_cb); + } else { + ASSERT_MEM_EQ(rcvbuf->base, "HELO", 4); + } sv_recv_cb_called++; + + if (sv_recv_cb_called == 2) + uv_udp_recv_stop(handle); } @@ -101,9 +105,33 @@ TEST_IMPL(udp_try_send) { ASSERT_OK(r); buf = uv_buf_init(buffer, sizeof(buffer)); + + r = uv_udp_try_send(&client, &buf, 0, (const struct sockaddr*) &addr); + ASSERT_EQ(r, UV_EINVAL); + r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); ASSERT_EQ(r, UV_EMSGSIZE); + uv_buf_t* bufs[] = {&buf, &buf}; + unsigned int nbufs[] = {1, 1}; + struct sockaddr* addrs[] = { + (struct sockaddr*) &addr, + (struct sockaddr*) &addr, + }; + + ASSERT_EQ(0, sv_recv_cb_called); + + buf = uv_buf_init("HELO", 4); + r = uv_udp_try_send2(&client, 2, bufs, nbufs, addrs, /*flags*/0); + ASSERT_EQ(r, 2); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT_EQ(2, sv_recv_cb_called); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT_OK(r); + buf = uv_buf_init("EXIT", 4); r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); ASSERT_EQ(4, r); @@ -111,7 +139,7 @@ TEST_IMPL(udp_try_send) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT_EQ(2, close_cb_called); - ASSERT_EQ(1, sv_recv_cb_called); + ASSERT_EQ(3, sv_recv_cb_called); ASSERT_OK(client.send_queue_size); ASSERT_OK(server.send_queue_size); From 6f946c95b9da75c70e868637de8161bc8d048379 Mon Sep 17 00:00:00 2001 From: Rafael Gonzaga Date: Fri, 17 Jan 2025 18:50:56 -0300 Subject: [PATCH 079/208] doc: mention prepare --security PR-URL: https://github.com/nodejs/node/pull/56617 Reviewed-By: Marco Ippolito Reviewed-By: Luigi Pinca --- doc/contributing/releases.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/contributing/releases.md b/doc/contributing/releases.md index 40ba96da602033..5b6d2180515565 100644 --- a/doc/contributing/releases.md +++ b/doc/contributing/releases.md @@ -308,6 +308,22 @@ branch. git checkout -b v1.2.3-proposal upstream/v1.x-staging ``` +You can also run: + +```bash +git node release -S --prepare --security --filterLabel vX.x +``` + +Example: + +```bash +git checkout v20.x +git node release -S --prepare --security --filterLabel v20.x +``` + +to automate the remaining steps until step 6 or you can perform it manually +following the below steps. +
      Security release From 840f95226876f86ed04ce2963e6d3dc6492c7d1c Mon Sep 17 00:00:00 2001 From: Colin Ihrig Date: Sat, 18 Jan 2025 13:01:54 -0500 Subject: [PATCH 080/208] punycode: limit deprecation warning DEP0040 is an extremely annoying warning. Most of the people seeing it cannot do anything about it. This commit updates the warning logic to only emit outside of node_modules. This is similar to other warnings such as the Buffer() constructor warning. Ideally, this should be backported to Node 22. Refs: https://github.com/nodejs/node/pull/47202 PR-URL: https://github.com/nodejs/node/pull/56632 Reviewed-By: Jordan Harband Reviewed-By: Richard Lau Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca Reviewed-By: Matteo Collina Reviewed-By: Antoine du Hamel Reviewed-By: Joyee Cheung --- lib/punycode.js | 19 ++++++++++++------- .../errors/core_line_numbers.snapshot | 6 +++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/punycode.js b/lib/punycode.js index 7dfe552a5c9efa..e303a5373b8839 100644 --- a/lib/punycode.js +++ b/lib/punycode.js @@ -1,11 +1,16 @@ 'use strict'; - -process.emitWarning( - 'The `punycode` module is deprecated. Please use a userland ' + - 'alternative instead.', - 'DeprecationWarning', - 'DEP0040', -); +const { + isInsideNodeModules, +} = internalBinding('util'); + +if (!isInsideNodeModules(100, true)) { + process.emitWarning( + 'The `punycode` module is deprecated. Please use a userland ' + + 'alternative instead.', + 'DeprecationWarning', + 'DEP0040', + ); +} /** Highest positive signed 32-bit float value */ const maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1 diff --git a/test/fixtures/errors/core_line_numbers.snapshot b/test/fixtures/errors/core_line_numbers.snapshot index 54cdb52744b29e..9ef06c33af8e28 100644 --- a/test/fixtures/errors/core_line_numbers.snapshot +++ b/test/fixtures/errors/core_line_numbers.snapshot @@ -1,10 +1,10 @@ -node:punycode:49 +node:punycode:54 throw new RangeError(errors[type]); ^ RangeError: Invalid input - at error (node:punycode:49:8) - at Object.decode (node:punycode:242:5) + at error (node:punycode:54:8) + at Object.decode (node:punycode:247:5) at Object. (*core_line_numbers.js:13:10) Node.js * From 009d53ec3c3a411e9ad28eaae419c95b300cb62a Mon Sep 17 00:00:00 2001 From: Maksim Gorkov <33923276+MGorkov@users.noreply.github.com> Date: Sat, 18 Jan 2025 22:39:56 +0300 Subject: [PATCH 081/208] child_process: fix parsing messages with splitted length field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/nodejs/node/issues/55834 PR-URL: https://github.com/nodejs/node/pull/56106 Reviewed-By: Luigi Pinca Reviewed-By: Juan José Arboleda Reviewed-By: James M Snell --- lib/internal/child_process/serialization.js | 7 +++++- ...ced-serialization-splitted-length-field.js | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-child-process-advanced-serialization-splitted-length-field.js diff --git a/lib/internal/child_process/serialization.js b/lib/internal/child_process/serialization.js index 7be39f0d48c3c2..46bb1faaf9fc21 100644 --- a/lib/internal/child_process/serialization.js +++ b/lib/internal/child_process/serialization.js @@ -61,7 +61,12 @@ const advanced = { *parseChannelMessages(channel, readData) { if (readData.length === 0) return; - ArrayPrototypePush(channel[kMessageBuffer], readData); + if (channel[kMessageBufferSize] && channel[kMessageBuffer][0].length < 4) { + // Message length split into two buffers, so let's concatenate it. + channel[kMessageBuffer][0] = Buffer.concat([channel[kMessageBuffer][0], readData]); + } else { + ArrayPrototypePush(channel[kMessageBuffer], readData); + } channel[kMessageBufferSize] += readData.length; // Index 0 should always be present because we just pushed data into it. diff --git a/test/parallel/test-child-process-advanced-serialization-splitted-length-field.js b/test/parallel/test-child-process-advanced-serialization-splitted-length-field.js new file mode 100644 index 00000000000000..5407a56f495c8f --- /dev/null +++ b/test/parallel/test-child-process-advanced-serialization-splitted-length-field.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const child_process = require('child_process'); + +// Regression test for https://github.com/nodejs/node/issues/55834 +const msgLen = 65521; +let cnt = 10; + +if (process.argv[2] === 'child') { + const msg = Buffer.allocUnsafe(msgLen); + (function send() { + if (cnt--) { + process.send(msg, send); + } else { + process.disconnect(); + } + })(); +} else { + const child = child_process.spawn(process.execPath, [__filename, 'child'], { + stdio: ['inherit', 'inherit', 'inherit', 'ipc'], + serialization: 'advanced' + }); + child.on('message', common.mustCall(cnt)); +} From 322056dc327d4a2dd006a90c5fd54075df7f502b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Sun, 19 Jan 2025 17:47:50 +0100 Subject: [PATCH 082/208] src: initialize FSReqWrapSync in path that uses it PR-URL: https://github.com/nodejs/node/pull/56613 Reviewed-By: James M Snell Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca --- src/node_file.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_file.cc b/src/node_file.cc index 1b56d2323c9526..984bc55ee9b941 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -2482,7 +2482,6 @@ static void WriteString(const FunctionCallbackInfo& args) { } } else { // write(fd, string, pos, enc, undefined, ctx) CHECK_EQ(argc, 6); - FSReqWrapSync req_wrap_sync; FSReqBase::FSReqBuffer stack_buffer; if (buf == nullptr) { if (!StringBytes::StorageSize(isolate, value, enc).To(&len)) @@ -2496,6 +2495,7 @@ static void WriteString(const FunctionCallbackInfo& args) { buf = *stack_buffer; } uv_buf_t uvbuf = uv_buf_init(buf, len); + FSReqWrapSync req_wrap_sync("write"); FS_SYNC_TRACE_BEGIN(write); int bytesWritten = SyncCall(env, args[5], &req_wrap_sync, "write", uv_fs_write, fd, &uvbuf, 1, pos); From a5ed762d82bc7cf0b44026d073d60cef3219ed2c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 19 Jan 2025 08:55:54 -0800 Subject: [PATCH 083/208] deps: fixup some minor coverity warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/nodejs/node/issues/56611 PR-URL: https://github.com/nodejs/node/pull/56612 Reviewed-By: Michaël Zasso Reviewed-By: Benjamin Gruenbaum Reviewed-By: Yagiz Nizipli Reviewed-By: Michael Dawson Reviewed-By: Ulises Gascón --- deps/ncrypto/ncrypto.cc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index fa0cf58062d897..ce2e7b384eb198 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -1346,8 +1346,11 @@ DHPointer DHPointer::New(BignumPointer&& p, BignumPointer&& g) { if (DH_set0_pqg(dh.get(), p.get(), nullptr, g.get()) != 1) return {}; // If the call above is successful, the DH object takes ownership of the - // BIGNUMs, so we must release them here. + // BIGNUMs, so we must release them here. Unfortunately coverity does not + // know that so we need to tell it not to complain. + // coverity[resource_leak] p.release(); + // coverity[resource_leak] g.release(); return dh; @@ -1430,7 +1433,10 @@ DataPointer DHPointer::generateKeys() const { size_t DHPointer::size() const { if (!dh_) return 0; - return DH_size(dh_.get()); + int ret = DH_size(dh_.get()); + // DH_size can return a -1 on error but we just want to return a 0 + // in that case so we don't wrap around when returning the size_t. + return ret >= 0 ? static_cast(ret) : 0; } DataPointer DHPointer::computeSecret(const BignumPointer& peer) const { @@ -1459,6 +1465,10 @@ DataPointer DHPointer::computeSecret(const BignumPointer& peer) const { bool DHPointer::setPublicKey(BignumPointer&& key) { if (!dh_) return false; if (DH_set0_key(dh_.get(), key.get(), nullptr) == 1) { + // If DH_set0_key returns successfully, then dh_ takes ownership of the + // BIGNUM, so we must release it here. Unfortunately coverity does not + // know that so we need to tell it not to complain. + // coverity[resource_leak] key.release(); return true; } @@ -1468,6 +1478,10 @@ bool DHPointer::setPublicKey(BignumPointer&& key) { bool DHPointer::setPrivateKey(BignumPointer&& key) { if (!dh_) return false; if (DH_set0_key(dh_.get(), nullptr, key.get()) == 1) { + // If DH_set0_key returns successfully, then dh_ takes ownership of the + // BIGNUM, so we must release it here. Unfortunately coverity does not + // know that so we need to tell it not to complain. + // coverity[resource_leak] key.release(); return true; } From da5f7aca6ac1fac2b7840dc11c0ef8e740cfc414 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Sun, 19 Jan 2025 08:56:09 -0800 Subject: [PATCH 084/208] test: test-stream-compose.js doesn't need internals PR-URL: https://github.com/nodejs/node/pull/56619 Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca Reviewed-By: Jake Yuesong Li Reviewed-By: James M Snell --- test/parallel/test-stream-compose.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/parallel/test-stream-compose.js b/test/parallel/test-stream-compose.js index 1ff8c39b7a2234..d7a54e177668a2 100644 --- a/test/parallel/test-stream-compose.js +++ b/test/parallel/test-stream-compose.js @@ -1,5 +1,3 @@ -// Flags: --expose-internals - 'use strict'; const common = require('../common'); @@ -9,9 +7,9 @@ const { Transform, Writable, finished, + compose, PassThrough } = require('stream'); -const compose = require('internal/streams/compose'); const assert = require('assert'); { From fdad2fa66e77739583591f9991360a87532cb3c7 Mon Sep 17 00:00:00 2001 From: islandryu Date: Sun, 29 Dec 2024 13:41:34 +0900 Subject: [PATCH 085/208] http2: omit server name when HTTP2 host is IP address Fixes: https://github.com/nodejs/node/issues/56189 PR-URL: https://github.com/nodejs/node/pull/56530 Reviewed-By: Matteo Collina Reviewed-By: Yongsheng Zhang Reviewed-By: Luigi Pinca --- lib/internal/http2/core.js | 24 ++++++---- test/parallel/test-http2-ip-address-host.js | 53 +++++++++++++++++++++ 2 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 test/parallel/test-http2-ip-address-host.js diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index b41e1baee24644..4f2392c9829cc3 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -636,15 +636,21 @@ function initOriginSet(session) { if (originSet === undefined) { const socket = session[kSocket]; session[kState].originSet = originSet = new SafeSet(); - if (socket.servername != null) { - let originString = `https://${socket.servername}`; - if (socket.remotePort != null) - originString += `:${socket.remotePort}`; - // We have to ensure that it is a properly serialized - // ASCII origin string. The socket.servername might not - // be properly ASCII encoded. - originSet.add(getURLOrigin(originString)); + let hostName = socket.servername; + if (hostName === null || hostName === false) { + if (socket.remoteFamily === 'IPv6') { + hostName = `[${socket.remoteAddress}]`; + } else { + hostName = socket.remoteAddress; + } } + let originString = `https://${hostName}`; + if (socket.remotePort != null) + originString += `:${socket.remotePort}`; + // We have to ensure that it is a properly serialized + // ASCII origin string. The socket.servername might not + // be properly ASCII encoded. + originSet.add(getURLOrigin(originString)); } return originSet; } @@ -3333,7 +3339,7 @@ function connect(authority, options, listener) { socket = net.connect({ port, host, ...options }); break; case 'https:': - socket = tls.connect(port, host, initializeTLSOptions(options, host)); + socket = tls.connect(port, host, initializeTLSOptions(options, net.isIP(host) ? undefined : host)); break; default: throw new ERR_HTTP2_UNSUPPORTED_PROTOCOL(protocol); diff --git a/test/parallel/test-http2-ip-address-host.js b/test/parallel/test-http2-ip-address-host.js new file mode 100644 index 00000000000000..c0699a89169153 --- /dev/null +++ b/test/parallel/test-http2-ip-address-host.js @@ -0,0 +1,53 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); }; +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const h2 = require('http2'); + +function loadKey(keyname) { + return fixtures.readKey(keyname, 'binary'); +} + +const key = loadKey('agent8-key.pem'); +const cert = fixtures.readKey('agent8-cert.pem'); + +const server = h2.createSecureServer({ key, cert }); +const hasIPv6 = common.hasIPv6; +const testCount = hasIPv6 ? 2 : 1; + +server.on('stream', common.mustCall((stream) => { + const session = stream.session; + assert.strictEqual(session.servername, undefined); + stream.respond({ 'content-type': 'application/json' }); + stream.end(JSON.stringify({ + servername: session.servername, + originSet: session.originSet + }) + ); +}, testCount)); + +let done = 0; + +server.listen(0, common.mustCall(() => { + function handleRequest(url) { + const client = h2.connect(url, + { rejectUnauthorized: false }); + const req = client.request(); + let data = ''; + req.setEncoding('utf8'); + req.on('data', (d) => data += d); + req.on('end', common.mustCall(() => { + const originSet = req.session.originSet; + assert.strictEqual(originSet[0], url); + client.close(); + if (++done === testCount) server.close(); + })); + } + + const ipv4Url = `https://127.0.0.1:${server.address().port}`; + const ipv6Url = `https://[::1]:${server.address().port}`; + handleRequest(ipv4Url); + if (hasIPv6) handleRequest(ipv6Url); +})); From 7bc2946293757389468f1fa09714860f8e1147b7 Mon Sep 17 00:00:00 2001 From: Shreyans Pathak Date: Mon, 20 Jan 2025 15:18:21 -0500 Subject: [PATCH 086/208] doc: `WeakSet` and `WeakMap` comparison details PR-URL: https://github.com/nodejs/node/pull/56648 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- doc/api/assert.md | 84 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index 3f44dffbe7de2d..32740d2f303a8d 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -804,8 +804,10 @@ are recursively evaluated also by the following rules. * [`Map`][] keys and [`Set`][] items are compared unordered. * Recursion stops when both sides differ or both sides encounter a circular reference. -* [`WeakMap`][] and [`WeakSet`][] comparison does not rely on their values. See - below for further details. +* [`WeakMap`][] and [`WeakSet`][] instances are **not** compared structurally. + They are only equal if they reference the same object. Any comparison between + different `WeakMap` or `WeakSet` instances will result in inequality, + even if they contain the same entries. * [`RegExp`][] lastIndex, flags, and source are always compared, even if these are not enumerable properties. @@ -882,23 +884,40 @@ assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 }); // } const weakMap1 = new WeakMap(); -const weakMap2 = new WeakMap([[{}, {}]]); -const weakMap3 = new WeakMap(); -weakMap3.unequal = true; +const weakMap2 = new WeakMap(); +const obj = {}; +weakMap1.set(obj, 'value'); +weakMap2.set(obj, 'value'); + +// Comparing different instances fails, even with same contents assert.deepStrictEqual(weakMap1, weakMap2); -// OK, because it is impossible to compare the entries +// AssertionError: Values have same structure but are not reference-equal: +// +// WeakMap { +// +// } + +// Comparing the same instance to itself succeeds +assert.deepStrictEqual(weakMap1, weakMap1); +// OK -// Fails because weakMap3 has a property that weakMap1 does not contain: -assert.deepStrictEqual(weakMap1, weakMap3); +const weakSet1 = new WeakSet(); +const weakSet2 = new WeakSet(); +weakSet1.add(obj); +weakSet2.add(obj); + +// Comparing different instances fails, even with same contents +assert.deepStrictEqual(weakSet1, weakSet2); // AssertionError: Expected inputs to be strictly deep-equal: // + actual - expected // -// WeakMap { -// + [items unknown] -// - [items unknown], -// - unequal: true -// } +// + WeakSet { } +// - WeakSet { } + +// Comparing the same instance to itself succeeds +assert.deepStrictEqual(weakSet1, weakSet1); +// OK ``` ```cjs @@ -974,23 +993,40 @@ assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 }); // } const weakMap1 = new WeakMap(); -const weakMap2 = new WeakMap([[{}, {}]]); -const weakMap3 = new WeakMap(); -weakMap3.unequal = true; +const weakMap2 = new WeakMap(); +const obj = {}; +weakMap1.set(obj, 'value'); +weakMap2.set(obj, 'value'); + +// Comparing different instances fails, even with same contents assert.deepStrictEqual(weakMap1, weakMap2); -// OK, because it is impossible to compare the entries +// AssertionError: Values have same structure but are not reference-equal: +// +// WeakMap { +// +// } + +// Comparing the same instance to itself succeeds +assert.deepStrictEqual(weakMap1, weakMap1); +// OK -// Fails because weakMap3 has a property that weakMap1 does not contain: -assert.deepStrictEqual(weakMap1, weakMap3); +const weakSet1 = new WeakSet(); +const weakSet2 = new WeakSet(); +weakSet1.add(obj); +weakSet2.add(obj); + +// Comparing different instances fails, even with same contents +assert.deepStrictEqual(weakSet1, weakSet2); // AssertionError: Expected inputs to be strictly deep-equal: // + actual - expected // -// WeakMap { -// + [items unknown] -// - [items unknown], -// - unequal: true -// } +// + WeakSet { } +// - WeakSet { } + +// Comparing the same instance to itself succeeds +assert.deepStrictEqual(weakSet1, weakSet1); +// OK ``` If the values are not equal, an [`AssertionError`][] is thrown with a `message` From b845dc6fbeb0684b83e314dd2233958e95166aa2 Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Wed, 15 Jan 2025 12:03:56 -0300 Subject: [PATCH 087/208] 2025-01-21, Version 23.6.1 (Current) This is a security release. Notable changes: * CVE-2025-23083: throw on InternalWorker use when permission model is enabled (High) * CVE-2025-23084: fix path traversal in normalize() on Windows (Medium) * CVE-2025-23085: fix HTTP2 mem leak on premature close and ERR_PROTO (Medium) * CVE-2025-22150 - Use of Insufficiently Random Values in undici fetch() (Medium) PR-URL: https://github.com/nodejs-private/node-private/pull/654 --- CHANGELOG.md | 3 ++- doc/changelogs/CHANGELOG_V23.md | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7dab220d26dde..7eaeeee7152974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,8 @@ release. -23.6.0
      +23.6.1
      +23.6.0
      23.5.0
      23.4.0
      23.3.0
      diff --git a/doc/changelogs/CHANGELOG_V23.md b/doc/changelogs/CHANGELOG_V23.md index 81e017da2f999e..6fb4f664ead635 100644 --- a/doc/changelogs/CHANGELOG_V23.md +++ b/doc/changelogs/CHANGELOG_V23.md @@ -8,6 +8,7 @@ +23.6.1
      23.6.0
      23.5.0
      23.4.0
      @@ -44,6 +45,29 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2025-01-21, Version 23.6.1 (Current), @RafaelGSS + +This is a security release. + +### Notable Changes + +* CVE-2025-23083 - src,loader,permission: throw on InternalWorker use when permission model is enabled (High) +* CVE-2025-23085 - src: fix HTTP2 mem leak on premature close and ERR\_PROTO (Medium) +* CVE-2025-23084 - path: fix path traversal in normalize() on Windows (Medium) + +Dependency update: + +* CVE-2025-22150 - Use of Insufficiently Random Values in undici fetch() (Medium) + +### Commits + +* \[[`f2ad4d3af8`](https://github.com/nodejs/node/commit/f2ad4d3af8)] - **(CVE-2025-22150)** **deps**: update undici to v6.21.1 (Matteo Collina) [nodejs-private/node-private#654](https://github.com/nodejs-private/node-private/pull/654) +* \[[`0afc6f9600`](https://github.com/nodejs/node/commit/0afc6f9600)] - **(CVE-2025-23084)** **path**: fix path traversal in normalize() on Windows (RafaelGSS) [nodejs-private/node-private#555](https://github.com/nodejs-private/node-private/pull/555) +* \[[`3c7686163e`](https://github.com/nodejs/node/commit/3c7686163e)] - **(CVE-2025-23085)** **src**: fix HTTP2 mem leak on premature close and ERR\_PROTO (RafaelGSS) [nodejs-private/node-private#650](https://github.com/nodejs-private/node-private/pull/650) +* \[[`51938f023a`](https://github.com/nodejs/node/commit/51938f023a)] - **(CVE-2025-23083)** **src,loader,permission**: throw on InternalWorker use (RafaelGSS) [nodejs-private/node-private#629](https://github.com/nodejs-private/node-private/pull/629) + ## 2025-01-07, Version 23.6.0 (Current), @marco-ippolito From e81c809d9d430894d5787f0f366c7377643c2cfc Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Wed, 15 Jan 2025 18:10:39 -0300 Subject: [PATCH 088/208] 2025-01-21, Version 22.13.1 'Jod' (LTS) This is a security release. Notable changes: * CVE-2025-23083: throw on InternalWorker use when permission model is enabled (High) * CVE-2025-23084: fix path traversal in normalize() on Windows (Medium) * CVE-2025-23085: fix HTTP2 mem leak on premature close and ERR_PROTO (Medium) * CVE-2025-22150 - Use of Insufficiently Random Values in undici fetch() (Medium) PR-URL: https://github.com/nodejs-private/node-private/pull/655 Signed-off-by: RafaelGSS --- CHANGELOG.md | 3 ++- doc/changelogs/CHANGELOG_V22.md | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7eaeeee7152974..833e1b72182c1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,8 @@ release. 23.0.0
      -22.13.0
      +22.13.1
      +22.13.0
      22.12.0
      22.11.0
      22.10.0
      diff --git a/doc/changelogs/CHANGELOG_V22.md b/doc/changelogs/CHANGELOG_V22.md index c8ec8fe661c044..301451a8e9e941 100644 --- a/doc/changelogs/CHANGELOG_V22.md +++ b/doc/changelogs/CHANGELOG_V22.md @@ -9,6 +9,7 @@ +22.13.1
      22.13.0
      22.12.0
      22.11.0
      @@ -56,6 +57,29 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2025-01-21, Version 22.13.1 'Jod' (LTS), @RafaelGSS + +This is a security release. + +### Notable Changes + +* CVE-2025-23083 - src,loader,permission: throw on InternalWorker use when permission model is enabled (High) +* CVE-2025-23085 - src: fix HTTP2 mem leak on premature close and ERR\_PROTO (Medium) +* CVE-2025-23084 - path: fix path traversal in normalize() on Windows (Medium) + +Dependency update: + +* CVE-2025-22150 - Use of Insufficiently Random Values in undici fetch() (Medium) + +### Commits + +* \[[`520da342e0`](https://github.com/nodejs/node/commit/520da342e0)] - **(CVE-2025-22150)** **deps**: update undici to v6.21.1 (Matteo Collina) [nodejs-private/node-private#662](https://github.com/nodejs-private/node-private/pull/662) +* \[[`99f217369f`](https://github.com/nodejs/node/commit/99f217369f)] - **(CVE-2025-23084)** **path**: fix path traversal in normalize() on Windows (Tobias Nießen) [nodejs-private/node-private#555](https://github.com/nodejs-private/node-private/pull/555) +* \[[`984f735e35`](https://github.com/nodejs/node/commit/984f735e35)] - **(CVE-2025-23085)** **src**: fix HTTP2 mem leak on premature close and ERR\_PROTO (RafaelGSS) [nodejs-private/node-private#650](https://github.com/nodejs-private/node-private/pull/650) +* \[[`2446870618`](https://github.com/nodejs/node/commit/2446870618)] - **(CVE-2025-23083)** **src,loader,permission**: throw on InternalWorker use (RafaelGSS) [nodejs-private/node-private#651](https://github.com/nodejs-private/node-private/pull/651) + ## 2025-01-07, Version 22.13.0 'Jod' (LTS), @ruyadorno From 3efbb0dfb489d9051c0b765de803f65cecefcdeb Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Thu, 16 Jan 2025 15:09:27 -0300 Subject: [PATCH 089/208] 2025-01-21, Version 20.18.2 'Iron' (LTS) This is a security release. Notable changes: * CVE-2025-23083 - throw on InternalWorker use when permission model is enabled (High) * CVE-2025-23085 - src: fix HTTP2 mem leak on premature close and ERR_PROTO (Medium) * CVE-2025-23084 - path: fix path traversal in normalize() on Windows (Medium) * CVE-2025-22150 - Use of Insufficiently Random Values in undici fetch() (Medium) PR-URL: https://github.com/nodejs-private/node-private/pull/664 --- CHANGELOG.md | 3 ++- doc/changelogs/CHANGELOG_V20.md | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 833e1b72182c1c..f851b4df3c65e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,7 +68,8 @@ release. 22.0.0
      -20.18.1
      +20.18.2
      +20.18.1
      20.18.0
      20.17.0
      20.16.0
      diff --git a/doc/changelogs/CHANGELOG_V20.md b/doc/changelogs/CHANGELOG_V20.md index 5d0660fc5218a0..01b2b3a957fbe0 100644 --- a/doc/changelogs/CHANGELOG_V20.md +++ b/doc/changelogs/CHANGELOG_V20.md @@ -9,6 +9,7 @@ +20.18.2
      20.18.1
      20.18.0
      20.17.0
      @@ -69,6 +70,29 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2025-01-21, Version 20.18.2 'Iron' (LTS), @RafaelGSS + +This is a security release. + +### Notable Changes + +* CVE-2025-23083 - throw on InternalWorker use when permission model is enabled (High) +* CVE-2025-23085 - src: fix HTTP2 mem leak on premature close and ERR\_PROTO (Medium) +* CVE-2025-23084 - path: fix path traversal in normalize() on Windows (Medium) + +Dependency update: + +* CVE-2025-22150 - Use of Insufficiently Random Values in undici fetch() (Medium) + +### Commits + +* \[[`df8b9f2c3e`](https://github.com/nodejs/node/commit/df8b9f2c3e)] - **(CVE-2025-22150)** **deps**: update undici to v6.21.1 (Matteo Collina) [nodejs-private/node-private#663](https://github.com/nodejs-private/node-private/pull/663) +* \[[`42d5821873`](https://github.com/nodejs/node/commit/42d5821873)] - **(CVE-2025-23084)** **path**: fix path traversal in normalize() on Windows (Tobias Nießen) [nodejs-private/node-private#555](https://github.com/nodejs-private/node-private/pull/555) +* \[[`8187a4b9bb`](https://github.com/nodejs/node/commit/8187a4b9bb)] - **src**: fix HTTP2 mem leak on premature close and ERR\_PROTO (RafaelGSS) +* \[[`389f239a28`](https://github.com/nodejs/node/commit/389f239a28)] - **(CVE-2025-23083)** **src,loader,permission**: throw on InternalWorker use (RafaelGSS) [nodejs-private/node-private#652](https://github.com/nodejs-private/node-private/pull/652) + ## 2024-11-20, Version 20.18.1 'Iron' (LTS), @marco-ippolito From d07c60b08fbb293555678a6892b0854b2cd771d0 Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Thu, 16 Jan 2025 10:22:44 -0300 Subject: [PATCH 090/208] 2025-01-21, Version 18.20.6 'Hydrogen' (LTS) This is a security release. Notable changes: * CVE-2025-23084 - fix path traversal in normalize() on Windows (Medium) * CVE-2025-23085 - fix HTTP2 mem leak on premature close and ERR_PROTO * CVE-2025-22150 - Use of Insufficiently Random Values in undici fetch() (Medium) (Medium) PR-URL: https://github.com/nodejs-private/node-private/pull/659 --- CHANGELOG.md | 3 ++- doc/changelogs/CHANGELOG_V18.md | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f851b4df3c65e3..246f126f74c2d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,7 +100,8 @@ release. 20.0.0
      -18.20.5
      +18.20.6
      +18.20.5
      18.20.4
      18.20.3
      18.20.2
      diff --git a/doc/changelogs/CHANGELOG_V18.md b/doc/changelogs/CHANGELOG_V18.md index 4f3cedbc171c4f..c73fcf2d698849 100644 --- a/doc/changelogs/CHANGELOG_V18.md +++ b/doc/changelogs/CHANGELOG_V18.md @@ -9,6 +9,7 @@ +18.20.6
      18.20.5
      18.20.4
      18.20.3
      @@ -75,6 +76,31 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2025-01-21, Version 18.20.6 'Hydrogen' (LTS), @RafaelGSS + +This is a security release. + +### Notable Changes + +* CVE-2025-23085 - src: fix HTTP2 mem leak on premature close and ERR\_PROTO (Medium) +* CVE-2025-23084 - path: fix path traversal in normalize() on Windows (Medium) + +Dependency update: + +* CVE-2025-22150 - Use of Insufficiently Random Values in undici fetch() (Medium) + +### Commits + +* \[[`c03ad5ed63`](https://github.com/nodejs/node/commit/c03ad5ed63)] - **build**: use rclone instead of aws CLI (Michaël Zasso) [#55617](https://github.com/nodejs/node/pull/55617) +* \[[`8232463294`](https://github.com/nodejs/node/commit/8232463294)] - **build, tools**: drop leading `/` from `r2dir` (Richard Lau) [#53951](https://github.com/nodejs/node/pull/53951) +* \[[`b26bcd3394`](https://github.com/nodejs/node/commit/b26bcd3394)] - **build, tools**: copy release assets to staging R2 bucket once built (flakey5) [#51394](https://github.com/nodejs/node/pull/51394) +* \[[`56df127b7b`](https://github.com/nodejs/node/commit/56df127b7b)] - **build,tools**: simplify upload of shasum signatures (Michaël Zasso) [#53892](https://github.com/nodejs/node/pull/53892) +* \[[`a63e9372ed`](https://github.com/nodejs/node/commit/a63e9372ed)] - **(CVE-2025-22150)** **deps**: update undici to v5.28.5 (Matteo Collina) [nodejs-private/node-private#657](https://github.com/nodejs-private/node-private/pull/657) +* \[[`da2d177f91`](https://github.com/nodejs/node/commit/da2d177f91)] - **(CVE-2025-23084)** **path**: fix path traversal in normalize() on Windows (Tobias Nießen) [nodejs-private/node-private#555](https://github.com/nodejs-private/node-private/pull/555) +* \[[`6cc8d58e6f`](https://github.com/nodejs/node/commit/6cc8d58e6f)] - **(CVE-2025-23085)** **src**: fix HTTP2 mem leak on premature close and ERR\_PROTO (RafaelGSS) [nodejs-private/node-private#650](https://github.com/nodejs-private/node-private/pull/650) + ## 2024-11-12, Version 18.20.5 'Hydrogen' (LTS), @aduh95 From 23c2d33592e72f7ba9bb5d30fe3181714bb508d1 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 21 Jan 2025 17:16:17 +0000 Subject: [PATCH 091/208] doc: clarify cjs/esm diff in `queueMicrotask()` vs `process.nextTick()` the section comparing `queueMicrotask()` and `process.nextTick()` doesn't address the different scheduling behavior that the two functions have in cjs and esm modules, the section's introductory mjs example also provides an incorrect output, the changes here address such by explaining the difference between the two module types and updating the example accordingly PR-URL: https://github.com/nodejs/node/pull/56659 Fixes: https://github.com/nodejs/node/issues/45048 Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- doc/api/process.md | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/doc/api/process.md b/doc/api/process.md index 95b35897f9d568..276a12f30e01ef 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -3020,34 +3020,40 @@ function definitelyAsync(arg, cb) { ### When to use `queueMicrotask()` vs. `process.nextTick()` -The [`queueMicrotask()`][] API is an alternative to `process.nextTick()` that -also defers execution of a function using the same microtask queue used to -execute the then, catch, and finally handlers of resolved promises. Within -Node.js, every time the "next tick queue" is drained, the microtask queue +The [`queueMicrotask()`][] API is an alternative to `process.nextTick()` that instead of using the +"next tick queue" defers execution of a function using the same microtask queue used to execute the +then, catch, and finally handlers of resolved promises. + +Within Node.js, every time the "next tick queue" is drained, the microtask queue is drained immediately after. +So in CJS modules `process.nextTick()` callbacks are always run before `queueMicrotask()` ones. +However since ESM modules are processed already as part of the microtask queue, there +`queueMicrotask()` callbacks are always exectued before `process.nextTick()` ones since Node.js +is already in the process of draining the microtask queue. + ```mjs import { nextTick } from 'node:process'; -Promise.resolve().then(() => console.log(2)); -queueMicrotask(() => console.log(3)); -nextTick(() => console.log(1)); +Promise.resolve().then(() => console.log('resolve')); +queueMicrotask(() => console.log('microtask')); +nextTick(() => console.log('nextTick')); // Output: -// 1 -// 2 -// 3 +// resolve +// microtask +// nextTick ``` ```cjs const { nextTick } = require('node:process'); -Promise.resolve().then(() => console.log(2)); -queueMicrotask(() => console.log(3)); -nextTick(() => console.log(1)); +Promise.resolve().then(() => console.log('resolve')); +queueMicrotask(() => console.log('microtask')); +nextTick(() => console.log('nextTick')); // Output: -// 1 -// 2 -// 3 +// nextTick +// resolve +// microtask ``` For _most_ userland use cases, the `queueMicrotask()` API provides a portable From 1b693fa03a0d36bc1dc9ec8d95060e3e5ceeee7b Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Tue, 17 Dec 2024 16:58:03 -0300 Subject: [PATCH 092/208] src: fix HTTP2 mem leak on premature close and ERR_PROTO This commit fixes a memory leak when the socket is suddenly closed by the peer (without GOAWAY notification) and when invalid header (by nghttp2) is identified and the connection is terminated by peer. Refs: https://hackerone.com/reports/2841362 PR-URL: https://github.com/nodejs-private/node-private/pull/650 Reviewed-By: James M Snell CVE-ID: CVE-2025-23085 --- lib/internal/http2/core.js | 15 +++- src/node_http2.cc | 36 ++++++-- ...2-connect-method-extended-cant-turn-off.js | 6 ++ .../test-http2-invalid-last-stream-id.js | 77 ++++++++++++++++ ...-http2-options-max-headers-block-length.js | 4 +- ...tp2-options-max-headers-exceeds-nghttp2.js | 4 +- test/parallel/test-http2-premature-close.js | 88 +++++++++++++++++++ 7 files changed, 220 insertions(+), 10 deletions(-) create mode 100644 test/parallel/test-http2-invalid-last-stream-id.js create mode 100644 test/parallel/test-http2-premature-close.js diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 4f2392c9829cc3..554221ac614636 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -614,11 +614,20 @@ function onFrameError(id, type, code) { return; debugSessionObj(session, 'error sending frame type %d on stream %d, code: %d', type, id, code); - const emitter = session[kState].streams.get(id) || session; + + const stream = session[kState].streams.get(id); + const emitter = stream || session; emitter[kUpdateTimer](); emitter.emit('frameError', type, code, id); - session[kState].streams.get(id).close(code); - session.close(); + + // When a frameError happens is not uncommon that a pending GOAWAY + // package from nghttp2 is on flight with a correct error code. + // We schedule it using setImmediate to give some time for that + // package to arrive. + setImmediate(() => { + stream?.close(code); + session.close(); + }); } function onAltSvc(stream, origin, alt) { diff --git a/src/node_http2.cc b/src/node_http2.cc index bf0ce4fd20a008..b23f4080a6d4e4 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -859,6 +859,7 @@ bool Http2Session::CanAddStream() { } void Http2Session::AddStream(Http2Stream* stream) { + Debug(this, "Adding stream: %d", stream->id()); CHECK_GE(++statistics_.stream_count, 0); streams_[stream->id()] = BaseObjectPtr(stream); size_t size = streams_.size(); @@ -869,6 +870,7 @@ void Http2Session::AddStream(Http2Stream* stream) { BaseObjectPtr Http2Session::RemoveStream(int32_t id) { + Debug(this, "Removing stream: %d", id); BaseObjectPtr stream; if (streams_.empty()) return stream; @@ -1045,6 +1047,7 @@ int Http2Session::OnHeaderCallback(nghttp2_session* handle, if (!stream) [[unlikely]] return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + Debug(session, "handling header key/pair for stream %d", id); // If the stream has already been destroyed, ignore. if (!stream->is_destroyed() && !stream->AddHeader(name, value, flags)) { // This will only happen if the connected peer sends us more @@ -1114,9 +1117,21 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle, return 1; } - // If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error + // If the error is fatal or if error code is one of the following + // we emit and error: + // + // ERR_STREAM_CLOSED: An invalid frame has been received in a closed stream. + // + // ERR_PROTO: The RFC 7540 specifies: + // "An endpoint that encounters a connection error SHOULD first send a GOAWAY + // frame (Section 6.8) with the stream identifier of the last stream that it + // successfully received from its peer. + // The GOAWAY frame includes an error code that indicates the type of error" + // The GOAWAY frame is already sent by nghttp2. We emit the error + // to liberate the Http2Session to destroy. if (nghttp2_is_fatal(lib_error_code) || - lib_error_code == NGHTTP2_ERR_STREAM_CLOSED) { + lib_error_code == NGHTTP2_ERR_STREAM_CLOSED || + lib_error_code == NGHTTP2_ERR_PROTO) { Environment* env = session->env(); Isolate* isolate = env->isolate(); HandleScope scope(isolate); @@ -1179,7 +1194,6 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle, Debug(session, "frame type %d was not sent, code: %d", frame->hd.type, error_code); - // Do not report if the frame was not sent due to the session closing if (error_code == NGHTTP2_ERR_SESSION_CLOSING || error_code == NGHTTP2_ERR_STREAM_CLOSED || error_code == NGHTTP2_ERR_STREAM_CLOSING) { @@ -1188,7 +1202,15 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle, // to destroy the session completely. // Further information see: https://github.com/nodejs/node/issues/35233 session->DecrefHeaders(frame); - return 0; + // Currently, nghttp2 doesn't not inform us when is the best + // time to call session.close(). It relies on a closing connection + // from peer. If that doesn't happen, the nghttp2_session will be + // closed but the Http2Session will still be up causing a memory leak. + // Therefore, if the GOAWAY frame couldn't be send due to + // ERR_SESSION_CLOSING we should force close from our side. + if (frame->hd.type != 0x03) { + return 0; + } } Isolate* isolate = env->isolate(); @@ -1254,12 +1276,15 @@ int Http2Session::OnStreamClose(nghttp2_session* handle, // ignore these. If this callback was not provided, nghttp2 would handle // invalid headers strictly and would shut down the stream. We are intentionally // being more lenient here although we may want to revisit this choice later. -int Http2Session::OnInvalidHeader(nghttp2_session* session, +int Http2Session::OnInvalidHeader(nghttp2_session* handle, const nghttp2_frame* frame, nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags, void* user_data) { + Http2Session* session = static_cast(user_data); + int32_t id = GetFrameID(frame); + Debug(session, "invalid header received for stream %d", id); // Ignore invalid header fields by default. return 0; } @@ -1655,6 +1680,7 @@ void Http2Session::HandlePingFrame(const nghttp2_frame* frame) { // Called by OnFrameReceived when a complete SETTINGS frame has been received. void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) { + Debug(this, "handling settings frame"); bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK; if (!ack) { js_fields_->bitfield &= ~(1 << kSessionRemoteSettingsIsUpToDate); diff --git a/test/parallel/test-http2-connect-method-extended-cant-turn-off.js b/test/parallel/test-http2-connect-method-extended-cant-turn-off.js index f4d033efe65707..456aa1ce10d627 100644 --- a/test/parallel/test-http2-connect-method-extended-cant-turn-off.js +++ b/test/parallel/test-http2-connect-method-extended-cant-turn-off.js @@ -27,4 +27,10 @@ server.listen(0, common.mustCall(() => { server.close(); })); })); + + client.on('error', common.expectsError({ + code: 'ERR_HTTP2_ERROR', + name: 'Error', + message: 'Protocol error' + })); })); diff --git a/test/parallel/test-http2-invalid-last-stream-id.js b/test/parallel/test-http2-invalid-last-stream-id.js new file mode 100644 index 00000000000000..c6e4e78dfb82ed --- /dev/null +++ b/test/parallel/test-http2-invalid-last-stream-id.js @@ -0,0 +1,77 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const h2 = require('http2'); +const net = require('net'); +const assert = require('assert'); +const { ServerHttp2Session } = require('internal/http2/core'); + +async function sendInvalidLastStreamId(server) { + const client = new net.Socket(); + + const address = server.address(); + if (!common.hasIPv6 && address.family === 'IPv6') { + // Necessary to pass CI running inside containers. + client.connect(address.port); + } else { + client.connect(address); + } + + client.on('connect', common.mustCall(function() { + // HTTP/2 preface + client.write(Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n', 'utf8')); + + // Empty SETTINGS frame + client.write(Buffer.from([0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00])); + + // GOAWAY frame with custom debug message + const goAwayFrame = [ + 0x00, 0x00, 0x21, // Length: 33 bytes + 0x07, // Type: GOAWAY + 0x00, // Flags + 0x00, 0x00, 0x00, 0x00, // Stream ID: 0 + 0x00, 0x00, 0x00, 0x01, // Last Stream ID: 1 + 0x00, 0x00, 0x00, 0x00, // Error Code: 0 (No error) + ]; + + // Add the debug message + const debugMessage = 'client transport shutdown'; + const goAwayBuffer = Buffer.concat([ + Buffer.from(goAwayFrame), + Buffer.from(debugMessage, 'utf8'), + ]); + + client.write(goAwayBuffer); + client.destroy(); + })); +} + +const server = h2.createServer(); + +server.on('error', common.mustNotCall()); + +server.on( + 'sessionError', + common.mustCall((err, session) => { + // When destroying the session, on Windows, we would get ECONNRESET + // errors, make sure we take those into account in our tests. + if (err.code !== 'ECONNRESET') { + assert.strictEqual(err.code, 'ERR_HTTP2_ERROR'); + assert.strictEqual(err.name, 'Error'); + assert.strictEqual(err.message, 'Protocol error'); + assert.strictEqual(session instanceof ServerHttp2Session, true); + } + session.close(); + server.close(); + }), +); + +server.listen( + 0, + common.mustCall(async () => { + await sendInvalidLastStreamId(server); + }), +); diff --git a/test/parallel/test-http2-options-max-headers-block-length.js b/test/parallel/test-http2-options-max-headers-block-length.js index af1cc6f9bc4860..15b142ac89b811 100644 --- a/test/parallel/test-http2-options-max-headers-block-length.js +++ b/test/parallel/test-http2-options-max-headers-block-length.js @@ -35,9 +35,11 @@ server.listen(0, common.mustCall(() => { assert.strictEqual(code, h2.constants.NGHTTP2_FRAME_SIZE_ERROR); })); + // NGHTTP2 will automatically send the NGHTTP2_REFUSED_STREAM with + // the GOAWAY frame. req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', name: 'Error', - message: 'Stream closed with error code NGHTTP2_FRAME_SIZE_ERROR' + message: 'Stream closed with error code NGHTTP2_REFUSED_STREAM' })); })); diff --git a/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js b/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js index df3aefff11e7ad..7767dbbc503814 100644 --- a/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js +++ b/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js @@ -59,6 +59,9 @@ server.listen(0, common.mustCall(() => { 'session', common.mustCall((session) => { assert.strictEqual(session instanceof ServerHttp2Session, true); + session.on('close', common.mustCall(() => { + server.close(); + })); }), ); server.on( @@ -80,7 +83,6 @@ server.listen(0, common.mustCall(() => { assert.strictEqual(err.name, 'Error'); assert.strictEqual(err.message, 'Session closed with error code 9'); assert.strictEqual(session instanceof ServerHttp2Session, true); - server.close(); }), ); diff --git a/test/parallel/test-http2-premature-close.js b/test/parallel/test-http2-premature-close.js new file mode 100644 index 00000000000000..a9b08f55d8a3b8 --- /dev/null +++ b/test/parallel/test-http2-premature-close.js @@ -0,0 +1,88 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const h2 = require('http2'); +const net = require('net'); + +async function requestAndClose(server) { + const client = new net.Socket(); + + const address = server.address(); + if (!common.hasIPv6 && address.family === 'IPv6') { + // Necessary to pass CI running inside containers. + client.connect(address.port); + } else { + client.connect(address); + } + + client.on('connect', common.mustCall(function() { + // Send HTTP/2 Preface + client.write(Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n', 'utf8')); + + // Send a SETTINGS frame (empty payload) + client.write(Buffer.from([0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00])); + + const streamId = 1; + // Send a valid HEADERS frame + const headersFrame = Buffer.concat([ + Buffer.from([ + 0x00, 0x00, 0x0c, // Length: 12 bytes + 0x01, // Type: HEADERS + 0x05, // Flags: END_HEADERS + END_STREAM + (streamId >> 24) & 0xFF, // Stream ID: high byte + (streamId >> 16) & 0xFF, + (streamId >> 8) & 0xFF, + streamId & 0xFF, // Stream ID: low byte + ]), + Buffer.from([ + 0x82, // Indexed Header Field Representation (Predefined ":method: GET") + 0x84, // Indexed Header Field Representation (Predefined ":path: /") + 0x86, // Indexed Header Field Representation (Predefined ":scheme: http") + 0x44, 0x0a, // Custom ":authority: localhost" + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + ]), + ]); + client.write(headersFrame); + + // Send a valid DATA frame + const dataFrame = Buffer.concat([ + Buffer.from([ + 0x00, 0x00, 0x05, // Length: 5 bytes + 0x00, // Type: DATA + 0x00, // Flags: No flags + (streamId >> 24) & 0xFF, // Stream ID: high byte + (streamId >> 16) & 0xFF, + (streamId >> 8) & 0xFF, + streamId & 0xFF, // Stream ID: low byte + ]), + Buffer.from('Hello', 'utf8'), // Data payload + ]); + client.write(dataFrame); + + // Does not wait for server to reply. Shutdown the socket + client.end(); + })); +} + +const server = h2.createServer(); + +server.on('error', common.mustNotCall()); + +server.on( + 'session', + common.mustCall((session) => { + session.on('close', common.mustCall(() => { + server.close(); + })); + }), +); + +server.listen( + 0, + common.mustCall(async () => { + await requestAndClose(server); + }), +); From 8306457110ebba648de5979136c39f7abba46ea9 Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Mon, 20 Jan 2025 11:40:16 -0300 Subject: [PATCH 093/208] path: fix path traversal in normalize() on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this patch, on Windows, normalizing a relative path might result in a path that Windows considers absolute. In rare cases, this might lead to path traversal vulnerabilities in user code. We attempt to detect those cases and return a relative path instead. Co-Authored-By: Tobias Nießen PR-URL: https://github.com/nodejs-private/node-private/pull/555 Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/665 CVE-ID: CVE-2025-23084 --- lib/path.js | 18 ++++++++++++++++++ test/parallel/test-path-join.js | 7 +++++++ test/parallel/test-path-normalize.js | 26 ++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/lib/path.js b/lib/path.js index 8b9d6d0b6b9621..1a59a66f66bb2a 100644 --- a/lib/path.js +++ b/lib/path.js @@ -26,6 +26,7 @@ const { ArrayPrototypeSlice, FunctionPrototypeBind, StringPrototypeCharCodeAt, + StringPrototypeIncludes, StringPrototypeIndexOf, StringPrototypeLastIndexOf, StringPrototypeRepeat, @@ -414,6 +415,23 @@ const win32 = { if (tail.length > 0 && isPathSeparator(StringPrototypeCharCodeAt(path, len - 1))) tail += '\\'; + if (!isAbsolute && device === undefined && StringPrototypeIncludes(path, ':')) { + // If the original path was not absolute and if we have not been able to + // resolve it relative to a particular device, we need to ensure that the + // `tail` has not become something that Windows might interpret as an + // absolute path. See CVE-2024-36139. + if (tail.length >= 2 && + isWindowsDeviceRoot(StringPrototypeCharCodeAt(tail, 0)) && + StringPrototypeCharCodeAt(tail, 1) === CHAR_COLON) { + return `.\\${tail}`; + } + let index = StringPrototypeIndexOf(path, ':'); + do { + if (index === len - 1 || isPathSeparator(StringPrototypeCharCodeAt(path, index + 1))) { + return `.\\${tail}`; + } + } while ((index = StringPrototypeIndexOf(path, ':', index + 1)) !== -1); + } if (device === undefined) { return isAbsolute ? `\\${tail}` : tail; } diff --git a/test/parallel/test-path-join.js b/test/parallel/test-path-join.js index d6d18399960d0b..b8d6375989a75e 100644 --- a/test/parallel/test-path-join.js +++ b/test/parallel/test-path-join.js @@ -110,6 +110,13 @@ joinTests.push([ [['c:.', 'file'], 'c:file'], [['c:', '/'], 'c:\\'], [['c:', 'file'], 'c:\\file'], + // Path traversal in previous versions of Node.js. + [['./upload', '/../C:/Windows'], '.\\C:\\Windows'], + [['upload', '../', 'C:foo'], '.\\C:foo'], + [['test/..', '??/D:/Test'], '.\\??\\D:\\Test'], + [['test', '..', 'D:'], '.\\D:'], + [['test', '..', 'D:\\'], '.\\D:\\'], + [['test', '..', 'D:foo'], '.\\D:foo'], ] ), ]); diff --git a/test/parallel/test-path-normalize.js b/test/parallel/test-path-normalize.js index 7a3d02cb7fe126..8b537676dbf45d 100644 --- a/test/parallel/test-path-normalize.js +++ b/test/parallel/test-path-normalize.js @@ -43,6 +43,32 @@ assert.strictEqual(path.win32.normalize('foo/bar\\baz'), 'foo\\bar\\baz'); assert.strictEqual(path.win32.normalize('\\\\.\\foo'), '\\\\.\\foo'); assert.strictEqual(path.win32.normalize('\\\\.\\foo\\'), '\\\\.\\foo\\'); +// Tests related to CVE-2024-36139. Path traversal should not result in changing +// the root directory on Windows. +assert.strictEqual(path.win32.normalize('test/../C:/Windows'), '.\\C:\\Windows'); +assert.strictEqual(path.win32.normalize('test/../C:Windows'), '.\\C:Windows'); +assert.strictEqual(path.win32.normalize('./upload/../C:/Windows'), '.\\C:\\Windows'); +assert.strictEqual(path.win32.normalize('./upload/../C:x'), '.\\C:x'); +assert.strictEqual(path.win32.normalize('test/../??/D:/Test'), '.\\??\\D:\\Test'); +assert.strictEqual(path.win32.normalize('test/C:/../../F:'), '.\\F:'); +assert.strictEqual(path.win32.normalize('test/C:foo/../../F:'), '.\\F:'); +assert.strictEqual(path.win32.normalize('test/C:/../../F:\\'), '.\\F:\\'); +assert.strictEqual(path.win32.normalize('test/C:foo/../../F:\\'), '.\\F:\\'); +assert.strictEqual(path.win32.normalize('test/C:/../../F:x'), '.\\F:x'); +assert.strictEqual(path.win32.normalize('test/C:foo/../../F:x'), '.\\F:x'); +assert.strictEqual(path.win32.normalize('/test/../??/D:/Test'), '\\??\\D:\\Test'); +assert.strictEqual(path.win32.normalize('/test/../?/D:/Test'), '\\?\\D:\\Test'); +assert.strictEqual(path.win32.normalize('//test/../??/D:/Test'), '\\\\test\\..\\??\\D:\\Test'); +assert.strictEqual(path.win32.normalize('//test/../?/D:/Test'), '\\\\test\\..\\?\\D:\\Test'); +assert.strictEqual(path.win32.normalize('\\\\?\\test/../?/D:/Test'), '\\\\?\\?\\D:\\Test'); +assert.strictEqual(path.win32.normalize('\\\\?\\test/../../?/D:/Test'), '\\\\?\\?\\D:\\Test'); +assert.strictEqual(path.win32.normalize('\\\\.\\test/../?/D:/Test'), '\\\\.\\?\\D:\\Test'); +assert.strictEqual(path.win32.normalize('\\\\.\\test/../../?/D:/Test'), '\\\\.\\?\\D:\\Test'); +assert.strictEqual(path.win32.normalize('//server/share/dir/../../../?/D:/file'), + '\\\\server\\share\\?\\D:\\file'); +assert.strictEqual(path.win32.normalize('//server/goodshare/../badshare/file'), + '\\\\server\\goodshare\\badshare\\file'); + assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'), 'fixtures/b/c.js'); assert.strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar'); From bf59539b980cbada964bf4c0991afe55668526e8 Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Tue, 27 Aug 2024 18:00:12 -0300 Subject: [PATCH 094/208] src,loader,permission: throw on InternalWorker use Previously this PR it was expected that InternalWorker usage doesn't require the --allow-worker when the permission model is enabled. This, however, exposes a vulnerability whenever the instance gets accessed by the user. For example through diagnostics_channel.subscribe('worker_threads') PR-URL: https://github.com/nodejs-private/node-private/pull/629 Refs: https://hackerone.com/reports/2575105 Reviewed-By: Matteo Collina Reviewed-By: Robert Nagy CVE-ID: CVE-2025-23083 --- doc/api/cli.md | 2 + src/node_worker.cc | 6 +-- test/es-module/test-esm-loader-hooks.mjs | 8 ++-- .../test-permission-dc-worker-threads.js | 19 +++++++++ test/parallel/test-runner-module-mocking.js | 41 +++++++++++++++++++ 5 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 test/parallel/test-permission-dc-worker-threads.js diff --git a/doc/api/cli.md b/doc/api/cli.md index dd058306aa26f7..e48340024e8ebf 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1061,6 +1061,8 @@ added: Enable module mocking in the test runner. +This feature requires `--allow-worker` if used with the [Permission Model][]. + ### `--experimental-transform-types` + + + Permissions can be used to control what system resources the Node.js process has access to or what actions the process can take with those resources. @@ -26,12 +30,18 @@ If you find a potential security vulnerability, please refer to our ### Permission Model - + > Stability: 2 - Stable. - - The Node.js Permission Model is a mechanism for restricting access to specific resources during execution. The API exists behind a flag [`--permission`][] which when enabled, From a4895e2f8e38f942c65be2f6b1895be8f8778eb9 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Thu, 23 Jan 2025 09:11:22 +0100 Subject: [PATCH 099/208] doc: add note for features using `InternalWorker` with permission model PR-URL: https://github.com/nodejs/node/pull/56706 Reviewed-By: James M Snell Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca --- doc/api/cli.md | 17 +++++++++++++++++ doc/api/module.md | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/doc/api/cli.md b/doc/api/cli.md index e48340024e8ebf..16ec96ec50ca35 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -943,6 +943,13 @@ Previously gated the entire `import.meta.resolve` feature. > Stability: 1.0 - Early development diff --git a/doc/api/module.md b/doc/api/module.md index 6f399fb07a44ee..7b23292a398491 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -177,6 +177,13 @@ added: - v20.6.0 - v18.19.0 changes: + - version: + - v23.6.1 + - v22.13.1 + - v20.18.2 + pr-url: https://github.com/nodejs-private/node-private/pull/629 + description: Using this feature with the permission model enabled requires + passing `--allow-worker`. - version: - v20.8.0 - v18.19.0 @@ -205,6 +212,8 @@ changes: Register a module that exports [hooks][] that customize Node.js module resolution and loading behavior. See [Customization hooks][]. +This feature requires `--allow-worker` if used with the [Permission Model][]. + ### `module.registerHooks(options)` + +* Returns: {Object} + * `enabled` {boolean} If the source maps support is enabled + * `nodeModules` {boolean} If the support is enabled for files in `node_modules`. + * `generatedCode` {boolean} If the support is enabled for generated code from `eval` or `new Function`. + +This method returns whether the [Source Map v3][Source Map] support for stack +traces is enabled. + @@ -1615,6 +1629,31 @@ added: `path` is the resolved path for the file for which a corresponding source map should be fetched. +### `module.setSourceMapsSupport(enabled[, options])` + + + +* `enabled` {boolean} Enable the source map support. +* `options` {Object} Optional + * `nodeModules` {boolean} If enabling the support for files in + `node_modules`. **Default:** `false`. + * `generatedCode` {boolean} If enabling the support for generated code from + `eval` or `new Function`. **Default:** `false`. + +This function enables or disables the [Source Map v3][Source Map] support for +stack traces. + +It provides same features as launching Node.js process with commandline options +`--enable-source-maps`, with additional options to alter the support for files +in `node_modules` or generated codes. + +Only source maps in JavaScript files that are loaded after source maps has been +enabled will be parsed and loaded. Preferably, use the commandline options +`--enable-source-maps` to avoid losing track of source maps of modules loaded +before this API call. + ### Class: `module.SourceMap` -> Stability: 1 - Experimental +> Stability: 1 - Experimental: Use [`module.setSourceMapsSupport()`][] instead. * `val` {boolean} @@ -4016,6 +4016,9 @@ It provides same features as launching Node.js process with commandline options Only source maps in JavaScript files that are loaded after source maps has been enabled will be parsed and loaded. +This implies calling `module.setSourceMapsSupport()` with an option +`{ nodeModules: true, generatedCode: true }`. + ## `process.setUncaughtExceptionCaptureCallback(fn)` -> Stability: 1 - Experimental +> Stability: 1 - Experimental: Use [`module.getSourceMapsSupport()`][] instead. * {boolean} @@ -4517,7 +4520,9 @@ cases: [`console.error()`]: console.md#consoleerrordata-args [`console.log()`]: console.md#consolelogdata-args [`domain`]: domain.md +[`module.getSourceMapsSupport()`]: module.md#modulegetsourcemapssupport [`module.isBuiltin(id)`]: module.md#moduleisbuiltinmodulename +[`module.setSourceMapsSupport()`]: module.md#modulesetsourcemapssupportenabled-options [`net.Server`]: net.md#class-netserver [`net.Socket`]: net.md#class-netsocket [`os.constants.dlopen`]: os.md#dlopen-constants diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index de2f0e00e14092..5b24a44741b0d6 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -368,8 +368,8 @@ internalBinding('process_methods').setEmitWarningSync(emitWarningSync); { const { - getSourceMapsEnabled, - setSourceMapsEnabled, + getSourceMapsSupport, + setSourceMapsSupport, maybeCacheGeneratedSourceMap, } = require('internal/source_map/source_map_cache'); const { @@ -381,10 +381,19 @@ internalBinding('process_methods').setEmitWarningSync(emitWarningSync); enumerable: true, configurable: true, get() { - return getSourceMapsEnabled(); + return getSourceMapsSupport().enabled; }, }); - process.setSourceMapsEnabled = setSourceMapsEnabled; + process.setSourceMapsEnabled = function setSourceMapsEnabled(val) { + setSourceMapsSupport(val, { + __proto__: null, + // TODO(legendecas): In order to smoothly improve the source map support, + // skip source maps in node_modules and generated code with + // `process.setSourceMapsEnabled(true)` in a semver major version. + nodeModules: val, + generatedCode: val, + }); + }; // The C++ land calls back to maybeCacheGeneratedSourceMap() // when code is generated by user with eval() or new Function() // to cache the source maps from the evaluated code, if any. diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index 8039e2f57a500f..846a336d27547e 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -30,7 +30,7 @@ const { } = internalBinding('util'); const { decorateErrorStack, kEmptyObject } = require('internal/util'); const { - getSourceMapsEnabled, + getSourceMapsSupport, } = require('internal/source_map/source_map_cache'); const assert = require('internal/assert'); const resolvedPromise = PromiseResolve(); @@ -186,7 +186,7 @@ class ModuleJob extends ModuleJobBase { // of missing named export. This is currently not possible because // stack trace originates in module_job, not the file itself. A hidden // symbol with filename could be set in node_errors.cc to facilitate this. - if (!getSourceMapsEnabled() && + if (!getSourceMapsSupport().enabled && StringPrototypeIncludes(e.message, ' does not provide an export named')) { const splitStack = StringPrototypeSplit(e.stack, '\n'); diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 3ea9a934726462..109890e5986ee4 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -618,9 +618,17 @@ function initializeESMLoader(forceDefaultLoader) { function initializeSourceMapsHandlers() { const { - setSourceMapsEnabled, + setSourceMapsSupport, } = require('internal/source_map/source_map_cache'); - setSourceMapsEnabled(getOptionValue('--enable-source-maps')); + const enabled = getOptionValue('--enable-source-maps'); + setSourceMapsSupport(enabled, { + __proto__: null, + // TODO(legendecas): In order to smoothly improve the source map support, + // skip source maps in node_modules and generated code with + // `--enable-source-maps` in a semver major version. + nodeModules: enabled, + generatedCode: enabled, + }); } function initializeFrozenIntrinsics() { diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index aaca27136e66a0..bdef338e3dd086 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -3,6 +3,7 @@ const { ArrayPrototypePush, JSONParse, + ObjectFreeze, RegExpPrototypeExec, SafeMap, StringPrototypeCodePointAt, @@ -15,7 +16,7 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => { debug = fn; }); -const { validateBoolean } = require('internal/validators'); +const { validateBoolean, validateObject } = require('internal/validators'); const { setSourceMapsEnabled: setSourceMapsNative, } = internalBinding('errors'); @@ -23,7 +24,7 @@ const { defaultPrepareStackTrace, setInternalPrepareStackTrace, } = require('internal/errors'); -const { getLazy } = require('internal/util'); +const { getLazy, isUnderNodeModules, kEmptyObject } = require('internal/util'); const getModuleSourceMapCache = getLazy(() => { const { SourceMapCacheMap } = require('internal/source_map/source_map_cache_map'); @@ -45,30 +46,48 @@ const { fileURLToPath, pathToFileURL, URL, URLParse } = require('internal/url'); let SourceMap; // This is configured with --enable-source-maps during pre-execution. -let sourceMapsEnabled = false; -function getSourceMapsEnabled() { - return sourceMapsEnabled; +let sourceMapsSupport = ObjectFreeze({ + __proto__: null, + enabled: false, + nodeModules: false, + generatedCode: false, +}); +function getSourceMapsSupport() { + // Return a read-only object. + return sourceMapsSupport; } /** * Enables or disables source maps programmatically. - * @param {boolean} val + * @param {boolean} enabled + * @param {object} options + * @param {boolean} [options.nodeModules] + * @param {boolean} [options.generatedCode] */ -function setSourceMapsEnabled(val) { - validateBoolean(val, 'val'); +function setSourceMapsSupport(enabled, options = kEmptyObject) { + validateBoolean(enabled, 'enabled'); + validateObject(options, 'options'); + + const { nodeModules = false, generatedCode = false } = options; + validateBoolean(nodeModules, 'options.nodeModules'); + validateBoolean(generatedCode, 'options.generatedCode'); - setSourceMapsNative(val); - if (val) { + setSourceMapsNative(enabled); + if (enabled) { const { prepareStackTraceWithSourceMaps, } = require('internal/source_map/prepare_stack_trace'); setInternalPrepareStackTrace(prepareStackTraceWithSourceMaps); - } else if (sourceMapsEnabled !== undefined) { - // Reset prepare stack trace callback only when disabling source maps. + } else { setInternalPrepareStackTrace(defaultPrepareStackTrace); } - sourceMapsEnabled = val; + sourceMapsSupport = ObjectFreeze({ + __proto__: null, + enabled, + nodeModules: nodeModules, + generatedCode: generatedCode, + }); } /** @@ -130,14 +149,18 @@ function extractSourceMapURLMagicComment(content) { * @param {string | undefined} sourceMapURL - the source map url */ function maybeCacheSourceMap(filename, content, moduleInstance, isGeneratedSource, sourceURL, sourceMapURL) { - const sourceMapsEnabled = getSourceMapsEnabled(); - if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return; + const support = getSourceMapsSupport(); + if (!(process.env.NODE_V8_COVERAGE || support.enabled)) return; const { normalizeReferrerURL } = require('internal/modules/helpers'); filename = normalizeReferrerURL(filename); if (filename === undefined) { // This is most likely an invalid filename in sourceURL of [eval]-wrapper. return; } + if (!support.nodeModules && isUnderNodeModules(filename)) { + // Skip file under node_modules if not enabled. + return; + } if (sourceMapURL === undefined) { sourceMapURL = extractSourceMapURLMagicComment(content); @@ -185,8 +208,8 @@ function maybeCacheSourceMap(filename, content, moduleInstance, isGeneratedSourc * @param {string} content - the eval'd source code */ function maybeCacheGeneratedSourceMap(content) { - const sourceMapsEnabled = getSourceMapsEnabled(); - if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return; + const support = getSourceMapsSupport(); + if (!(process.env.NODE_V8_COVERAGE || support.enabled || support.generated)) return; const sourceURL = extractSourceURLMagicComment(content); if (sourceURL === null) { @@ -352,6 +375,10 @@ function findSourceMap(sourceURL) { return undefined; } + if (!getSourceMapsSupport().nodeModules && isUnderNodeModules(sourceURL)) { + return undefined; + } + SourceMap ??= require('internal/source_map/source_map').SourceMap; try { if (RegExpPrototypeExec(kLeadingProtocol, sourceURL) === null) { @@ -377,8 +404,8 @@ function findSourceMap(sourceURL) { module.exports = { findSourceMap, - getSourceMapsEnabled, - setSourceMapsEnabled, + getSourceMapsSupport, + setSourceMapsSupport, maybeCacheSourceMap, maybeCacheGeneratedSourceMap, sourceMapCacheToObject, diff --git a/lib/module.js b/lib/module.js index a0317d06e0edb0..1217172afb3ccb 100644 --- a/lib/module.js +++ b/lib/module.js @@ -1,9 +1,15 @@ 'use strict'; -const { findSourceMap } = require('internal/source_map/source_map_cache'); +const { + findSourceMap, + getSourceMapsSupport, + setSourceMapsSupport, +} = require('internal/source_map/source_map_cache'); const { Module } = require('internal/modules/cjs/loader'); const { register } = require('internal/modules/esm/loader'); -const { SourceMap } = require('internal/source_map/source_map'); +const { + SourceMap, +} = require('internal/source_map/source_map'); const { constants, enableCompileCache, @@ -15,9 +21,7 @@ const { } = require('internal/modules/package_json_reader'); const { stripTypeScriptTypes } = require('internal/modules/typescript'); -Module.findSourceMap = findSourceMap; Module.register = register; -Module.SourceMap = SourceMap; Module.constants = constants; Module.enableCompileCache = enableCompileCache; Module.findPackageJSON = findPackageJSON; @@ -25,4 +29,10 @@ Module.flushCompileCache = flushCompileCache; Module.getCompileCacheDir = getCompileCacheDir; Module.stripTypeScriptTypes = stripTypeScriptTypes; +// SourceMap APIs +Module.findSourceMap = findSourceMap; +Module.SourceMap = SourceMap; +Module.getSourceMapsSupport = getSourceMapsSupport; +Module.setSourceMapsSupport = setSourceMapsSupport; + module.exports = Module; diff --git a/test/fixtures/source-map/node_modules/error-stack/enclosing-call-site-min.js b/test/fixtures/source-map/node_modules/error-stack/enclosing-call-site-min.js new file mode 100644 index 00000000000000..45b7ed2b219b86 --- /dev/null +++ b/test/fixtures/source-map/node_modules/error-stack/enclosing-call-site-min.js @@ -0,0 +1,3 @@ +var functionA=function(){functionB()};function functionB(){functionC()}var functionC=function(){functionD()},functionD=function(){if(0 { + functionB() +} + +function functionB() { + functionC() +} + +const functionC = () => { + functionD() +} + +const functionD = () => { + (function functionE () { + if (Math.random() > 0) { + throw new Error('an error!') + } + })() +} + +const thrower = functionA + +try { + thrower() +} catch (err) { + throw err +} diff --git a/test/fixtures/source-map/node_modules/error-stack/enclosing-call-site.js.map b/test/fixtures/source-map/node_modules/error-stack/enclosing-call-site.js.map new file mode 100644 index 00000000000000..d0c785f26091cc --- /dev/null +++ b/test/fixtures/source-map/node_modules/error-stack/enclosing-call-site.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"enclosing-call-site-min.js", +"lineCount":1, +"mappings":"AAAA,IAAMA,UAAYA,QAAA,EAAM,CACtBC,SAAA,EADsB,CAIxBA,SAASA,UAAS,EAAG,CACnBC,SAAA,EADmB,CAIrB,IAAMA,UAAYA,QAAA,EAAM,CACtBC,SAAA,EADsB,CAAxB,CAIMA,UAAYA,QAAA,EAAM,CAEpB,GAAoB,CAApB,CAAIC,IAAA,CAAKC,MAAL,EAAJ,CACE,KAAUC,MAAJ,CAAU,WAAV,CAAN,CAHkB,CAJxB,CAYMC,QAAUP,SAEhB,IAAI,CACFO,SAAA,EADE,CAEF,MAAOC,CAAP,CAAY,CACZ,KAAMA,EAAN,CADY;", +"sources":["enclosing-call-site.js"], +"names":["functionA","functionB","functionC","functionD","Math","random","Error","thrower","err"] +} diff --git a/test/fixtures/source-map/output/source_map_disabled_by_api.js b/test/fixtures/source-map/output/source_map_disabled_by_api.js index 8f455f26b6c9c4..1291f3583ac239 100644 --- a/test/fixtures/source-map/output/source_map_disabled_by_api.js +++ b/test/fixtures/source-map/output/source_map_disabled_by_api.js @@ -2,11 +2,23 @@ 'use strict'; require('../../../common'); -const assert = require('assert'); +const assert = require('node:assert'); +const Module = require('node:module'); Error.stackTraceLimit = 5; -assert.strictEqual(process.sourceMapsEnabled, true); -process.setSourceMapsEnabled(false); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: true, + nodeModules: true, + generatedCode: true, +}); +Module.setSourceMapsSupport(false); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: false, + nodeModules: false, + generatedCode: false, +}); assert.strictEqual(process.sourceMapsEnabled, false); try { @@ -19,7 +31,13 @@ try { // support enabled programmatically. delete require.cache[require .resolve('../enclosing-call-site-min.js')]; -process.setSourceMapsEnabled(true); +Module.setSourceMapsSupport(true); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: true, + nodeModules: false, + generatedCode: false, +}); assert.strictEqual(process.sourceMapsEnabled, true); try { diff --git a/test/fixtures/source-map/output/source_map_disabled_by_process_api.js b/test/fixtures/source-map/output/source_map_disabled_by_process_api.js new file mode 100644 index 00000000000000..f9fc5b0c966ca6 --- /dev/null +++ b/test/fixtures/source-map/output/source_map_disabled_by_process_api.js @@ -0,0 +1,42 @@ +// Flags: --enable-source-maps + +'use strict'; +require('../../../common'); +const assert = require('node:assert'); +const Module = require('node:module'); +Error.stackTraceLimit = 5; + +assert.strictEqual(process.sourceMapsEnabled, true); +process.setSourceMapsEnabled(false); +assert.strictEqual(process.sourceMapsEnabled, false); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: false, + nodeModules: false, + generatedCode: false, +}); + +try { + require('../enclosing-call-site-min.js'); +} catch (e) { + console.log(e); +} + +// Delete the CJS module cache and loading the module again with source maps +// support enabled programmatically. +delete require.cache[require + .resolve('../enclosing-call-site-min.js')]; +process.setSourceMapsEnabled(true); +assert.strictEqual(process.sourceMapsEnabled, true); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: true, + nodeModules: true, + generatedCode: true, +}); + +try { + require('../enclosing-call-site-min.js'); +} catch (e) { + console.log(e); +} diff --git a/test/fixtures/source-map/output/source_map_disabled_by_process_api.snapshot b/test/fixtures/source-map/output/source_map_disabled_by_process_api.snapshot new file mode 100644 index 00000000000000..655cd6695e1116 --- /dev/null +++ b/test/fixtures/source-map/output/source_map_disabled_by_process_api.snapshot @@ -0,0 +1,12 @@ +Error: an error! + at functionD (*enclosing-call-site-min.js:1:156) + at functionC (*enclosing-call-site-min.js:1:97) + at functionB (*enclosing-call-site-min.js:1:60) + at functionA (*enclosing-call-site-min.js:1:26) + at Object. (*enclosing-call-site-min.js:1:199) +Error: an error! + at functionD (*enclosing-call-site.js:16:17) + at functionC (*enclosing-call-site.js:10:3) + at functionB (*enclosing-call-site.js:6:3) + at functionA (*enclosing-call-site.js:2:3) + at Object. (*enclosing-call-site.js:24:3) diff --git a/test/fixtures/source-map/output/source_map_enabled_by_api.js b/test/fixtures/source-map/output/source_map_enabled_by_api.js index 1dd4f9530c68db..e09e05b59339f4 100644 --- a/test/fixtures/source-map/output/source_map_enabled_by_api.js +++ b/test/fixtures/source-map/output/source_map_enabled_by_api.js @@ -1,10 +1,22 @@ 'use strict'; require('../../../common'); -const assert = require('assert'); +const assert = require('node:assert'); +const Module = require('node:module'); Error.stackTraceLimit = 5; -assert.strictEqual(process.sourceMapsEnabled, false); -process.setSourceMapsEnabled(true); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: false, + nodeModules: false, + generatedCode: false, +}); +Module.setSourceMapsSupport(true); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: true, + nodeModules: false, + generatedCode: false, +}); assert.strictEqual(process.sourceMapsEnabled, true); try { @@ -16,7 +28,13 @@ try { delete require.cache[require .resolve('../enclosing-call-site-min.js')]; -process.setSourceMapsEnabled(false); +Module.setSourceMapsSupport(false); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: false, + nodeModules: false, + generatedCode: false, +}); assert.strictEqual(process.sourceMapsEnabled, false); try { diff --git a/test/fixtures/source-map/output/source_map_enabled_by_api_node_modules.js b/test/fixtures/source-map/output/source_map_enabled_by_api_node_modules.js new file mode 100644 index 00000000000000..5de2f3b0d7eb85 --- /dev/null +++ b/test/fixtures/source-map/output/source_map_enabled_by_api_node_modules.js @@ -0,0 +1,48 @@ +'use strict'; +require('../../../common'); +const assert = require('node:assert'); +const Module = require('node:module'); +Error.stackTraceLimit = 5; + +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: false, + nodeModules: false, + generatedCode: false, +}); +Module.setSourceMapsSupport(true, { + nodeModules: true, +}); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: true, + nodeModules: true, + generatedCode: false, +}); +assert.strictEqual(process.sourceMapsEnabled, true); + +try { + require('../node_modules/error-stack/enclosing-call-site-min.js').simpleErrorStack(); +} catch (e) { + console.log(e); +} + +delete require.cache[require + .resolve('../node_modules/error-stack/enclosing-call-site-min.js')]; + +Module.setSourceMapsSupport(true, { + nodeModules: false, +}); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: true, + nodeModules: false, + generatedCode: false, +}); +assert.strictEqual(process.sourceMapsEnabled, true); + +try { + require('../node_modules/error-stack/enclosing-call-site-min.js').simpleErrorStack(); +} catch (e) { + console.log(e); +} \ No newline at end of file diff --git a/test/fixtures/source-map/output/source_map_enabled_by_api_node_modules.snapshot b/test/fixtures/source-map/output/source_map_enabled_by_api_node_modules.snapshot new file mode 100644 index 00000000000000..f46c21dbe42057 --- /dev/null +++ b/test/fixtures/source-map/output/source_map_enabled_by_api_node_modules.snapshot @@ -0,0 +1,12 @@ +Error: an error! + at functionD (*node_modules*error-stack*enclosing-call-site.js:16:17) + at functionC (*node_modules*error-stack*enclosing-call-site.js:10:3) + at functionB (*node_modules*error-stack*enclosing-call-site.js:6:3) + at functionA (*node_modules*error-stack*enclosing-call-site.js:2:3) + at Object. (*node_modules*error-stack*enclosing-call-site.js:24:3) +Error: an error! + at functionD (*node_modules*error-stack*enclosing-call-site-min.js:1:156) + at functionC (*node_modules*error-stack*enclosing-call-site-min.js:1:97) + at functionB (*node_modules*error-stack*enclosing-call-site-min.js:1:60) + at functionA (*node_modules*error-stack*enclosing-call-site-min.js:1:26) + at Object. (*node_modules*error-stack*enclosing-call-site-min.js:1:199) diff --git a/test/fixtures/source-map/output/source_map_enabled_by_process_api.js b/test/fixtures/source-map/output/source_map_enabled_by_process_api.js new file mode 100644 index 00000000000000..867a5cc082d40b --- /dev/null +++ b/test/fixtures/source-map/output/source_map_enabled_by_process_api.js @@ -0,0 +1,39 @@ +'use strict'; +require('../../../common'); +const assert = require('node:assert'); +const Module = require('node:module'); +Error.stackTraceLimit = 5; + +assert.strictEqual(process.sourceMapsEnabled, false); +process.setSourceMapsEnabled(true); +assert.strictEqual(process.sourceMapsEnabled, true); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: true, + nodeModules: true, + generatedCode: true, +}); + +try { + require('../enclosing-call-site-min.js'); +} catch (e) { + console.log(e); +} + +delete require.cache[require + .resolve('../enclosing-call-site-min.js')]; + +process.setSourceMapsEnabled(false); +assert.strictEqual(process.sourceMapsEnabled, false); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: false, + nodeModules: false, + generatedCode: false, +}); + +try { + require('../enclosing-call-site-min.js'); +} catch (e) { + console.log(e); +} diff --git a/test/fixtures/source-map/output/source_map_enabled_by_process_api.snapshot b/test/fixtures/source-map/output/source_map_enabled_by_process_api.snapshot new file mode 100644 index 00000000000000..082b3f310ed4f9 --- /dev/null +++ b/test/fixtures/source-map/output/source_map_enabled_by_process_api.snapshot @@ -0,0 +1,12 @@ +Error: an error! + at functionD (*enclosing-call-site.js:16:17) + at functionC (*enclosing-call-site.js:10:3) + at functionB (*enclosing-call-site.js:6:3) + at functionA (*enclosing-call-site.js:2:3) + at Object. (*enclosing-call-site.js:24:3) +Error: an error! + at functionD (*enclosing-call-site-min.js:1:156) + at functionC (*enclosing-call-site-min.js:1:97) + at functionB (*enclosing-call-site-min.js:1:60) + at functionA (*enclosing-call-site-min.js:1:26) + at Object. (*enclosing-call-site-min.js:1:199) diff --git a/test/fixtures/source-map/output/source_map_prepare_stack_trace.js b/test/fixtures/source-map/output/source_map_prepare_stack_trace.js index 1b04e0a3ac221b..894aea60a96f18 100644 --- a/test/fixtures/source-map/output/source_map_prepare_stack_trace.js +++ b/test/fixtures/source-map/output/source_map_prepare_stack_trace.js @@ -2,7 +2,8 @@ 'use strict'; require('../../../common'); -const assert = require('assert'); +const assert = require('node:assert'); +const Module = require('node:module'); Error.stackTraceLimit = 5; assert.strictEqual(typeof Error.prepareStackTrace, 'function'); @@ -22,8 +23,13 @@ try { // Source maps support is disabled programmatically even without deleting the // CJS module cache. -process.setSourceMapsEnabled(false); -assert.strictEqual(process.sourceMapsEnabled, false); +Module.setSourceMapsSupport(false); +assert.deepStrictEqual(Module.getSourceMapsSupport(), { + __proto__: null, + enabled: false, + nodeModules: false, + generatedCode: false, +}); try { require('../enclosing-call-site-min.js'); diff --git a/test/parallel/test-module-setsourcemapssupport.js b/test/parallel/test-module-setsourcemapssupport.js new file mode 100644 index 00000000000000..ea3e396a5c5960 --- /dev/null +++ b/test/parallel/test-module-setsourcemapssupport.js @@ -0,0 +1,43 @@ +'use strict'; +require('../common'); +const assert = require('node:assert'); +const Module = require('node:module'); + +// This test verifies that the `Module.setSourceMapsSupport` throws on invalid +// argument inputs. + +{ + const unexpectedValues = [ + undefined, + null, + 1, + {}, + () => {}, + ]; + for (const it of unexpectedValues) { + assert.throws(() => { + Module.setSourceMapsSupport(it); + }, /ERR_INVALID_ARG_TYPE/); + } +} + +{ + const unexpectedValues = [ + null, + 1, + {}, + () => {}, + ]; + for (const it of unexpectedValues) { + assert.throws(() => { + Module.setSourceMapsSupport(true, { + nodeModules: it, + }); + }, /ERR_INVALID_ARG_TYPE/); + assert.throws(() => { + Module.setSourceMapsSupport(true, { + generatedCode: it, + }); + }, /ERR_INVALID_ARG_TYPE/); + } +} diff --git a/test/parallel/test-node-output-sourcemaps.mjs b/test/parallel/test-node-output-sourcemaps.mjs index e9104db220867f..29cc5eb711f176 100644 --- a/test/parallel/test-node-output-sourcemaps.mjs +++ b/test/parallel/test-node-output-sourcemaps.mjs @@ -27,7 +27,10 @@ describe('sourcemaps output', { concurrency: !process.env.TEST_PARALLEL }, () => const tests = [ { name: 'source-map/output/source_map_disabled_by_api.js' }, + { name: 'source-map/output/source_map_disabled_by_process_api.js' }, { name: 'source-map/output/source_map_enabled_by_api.js' }, + { name: 'source-map/output/source_map_enabled_by_api_node_modules.js' }, + { name: 'source-map/output/source_map_enabled_by_process_api.js' }, { name: 'source-map/output/source_map_enclosing_function.js' }, { name: 'source-map/output/source_map_eval.js' }, { name: 'source-map/output/source_map_no_source_file.js' }, From 869ea331f3a8215229290e2e6038956874c382a6 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 19 Jan 2025 07:41:13 -0800 Subject: [PATCH 107/208] src: replace NoArrayBufferZeroFillScope with v8 option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NoArrayBufferZeroFillScope was added before the v8 option to create uninitialized backing stores was added. We can start moving away from it. PR-URL: https://github.com/nodejs/node/pull/56658 Reviewed-By: Yagiz Nizipli Reviewed-By: Michaël Zasso Reviewed-By: Chengzhong Wu Reviewed-By: Rafael Gonzaga Reviewed-By: Matteo Collina --- src/encoding_binding.cc | 6 +++--- src/env.cc | 14 +++++++++----- src/node_buffer.cc | 20 ++++++++------------ src/node_http2.cc | 32 ++++++++++++++------------------ src/stream_base.cc | 13 +++++++------ 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/src/encoding_binding.cc b/src/encoding_binding.cc index 885a0d072312e9..0438afe6efd8b6 100644 --- a/src/encoding_binding.cc +++ b/src/encoding_binding.cc @@ -15,6 +15,7 @@ namespace encoding_binding { using v8::ArrayBuffer; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::FunctionCallbackInfo; using v8::Isolate; @@ -124,9 +125,8 @@ void BindingData::EncodeUtf8String(const FunctionCallbackInfo& args) { Local ab; { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - std::unique_ptr bs = - ArrayBuffer::NewBackingStore(isolate, length); + std::unique_ptr bs = ArrayBuffer::NewBackingStore( + isolate, length, BackingStoreInitializationMode::kUninitialized); CHECK(bs); diff --git a/src/env.cc b/src/env.cc index 0eda889802710d..cd7203ffda6e7c 100644 --- a/src/env.cc +++ b/src/env.cc @@ -39,6 +39,9 @@ namespace node { using errors::TryCatchScope; using v8::Array; +using v8::ArrayBuffer; +using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::Context; using v8::CppHeap; @@ -742,17 +745,18 @@ void Environment::add_refs(int64_t diff) { } uv_buf_t Environment::allocate_managed_buffer(const size_t suggested_size) { - NoArrayBufferZeroFillScope no_zero_fill_scope(isolate_data()); - std::unique_ptr bs = - v8::ArrayBuffer::NewBackingStore(isolate(), suggested_size); + std::unique_ptr bs = ArrayBuffer::NewBackingStore( + isolate(), + suggested_size, + BackingStoreInitializationMode::kUninitialized); uv_buf_t buf = uv_buf_init(static_cast(bs->Data()), bs->ByteLength()); released_allocated_buffers_.emplace(buf.base, std::move(bs)); return buf; } -std::unique_ptr Environment::release_managed_buffer( +std::unique_ptr Environment::release_managed_buffer( const uv_buf_t& buf) { - std::unique_ptr bs; + std::unique_ptr bs; if (buf.base != nullptr) { auto it = released_allocated_buffers_.find(buf.base); CHECK_NE(it, released_allocated_buffers_.end()); diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 2e0e8d4746fb61..e8eae4eff51144 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -58,6 +58,7 @@ namespace Buffer { using v8::ArrayBuffer; using v8::ArrayBufferView; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::EscapableHandleScope; using v8::FastApiTypedArray; @@ -372,9 +373,8 @@ MaybeLocal New(Environment* env, size_t length) { Local ab; { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - std::unique_ptr bs = - ArrayBuffer::NewBackingStore(isolate, length); + std::unique_ptr bs = ArrayBuffer::NewBackingStore( + isolate, length, BackingStoreInitializationMode::kUninitialized); CHECK(bs); @@ -413,18 +413,14 @@ MaybeLocal Copy(Environment* env, const char* data, size_t length) { return Local(); } - Local ab; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - std::unique_ptr bs = - ArrayBuffer::NewBackingStore(isolate, length); + std::unique_ptr bs = ArrayBuffer::NewBackingStore( + isolate, length, BackingStoreInitializationMode::kUninitialized); - CHECK(bs); + CHECK(bs); - memcpy(bs->Data(), data, length); + memcpy(bs->Data(), data, length); - ab = ArrayBuffer::New(isolate, std::move(bs)); - } + Local ab = ArrayBuffer::New(isolate, std::move(bs)); MaybeLocal obj = New(env, ab, 0, ab->ByteLength()) diff --git a/src/node_http2.cc b/src/node_http2.cc index b23f4080a6d4e4..38b3046861e805 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -27,6 +27,7 @@ using v8::Array; using v8::ArrayBuffer; using v8::ArrayBufferView; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::Context; using v8::EscapableHandleScope; @@ -292,11 +293,10 @@ Local Http2Settings::Pack( size_t count, const nghttp2_settings_entry* entries) { EscapableHandleScope scope(env->isolate()); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), count * 6); - } + std::unique_ptr bs = ArrayBuffer::NewBackingStore( + env->isolate(), + count * 6, + BackingStoreInitializationMode::kUninitialized); if (nghttp2_pack_settings_payload(static_cast(bs->Data()), bs->ByteLength(), entries, @@ -457,13 +457,11 @@ Origins::Origins( return; } - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs_ = ArrayBuffer::NewBackingStore(env->isolate(), - alignof(nghttp2_origin_entry) - 1 + - count_ * sizeof(nghttp2_origin_entry) + - origin_string_len); - } + bs_ = ArrayBuffer::NewBackingStore( + env->isolate(), + alignof(nghttp2_origin_entry) - 1 + + count_ * sizeof(nghttp2_origin_entry) + origin_string_len, + BackingStoreInitializationMode::kUninitialized); // Make sure the start address is aligned appropriately for an nghttp2_nv*. char* start = nbytes::AlignUp(static_cast(bs_->Data()), @@ -2090,12 +2088,10 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) { // happen, we concatenate the data we received with the already-stored // pending input data, slicing off the already processed part. size_t pending_len = stream_buf_.len - stream_buf_offset_; - std::unique_ptr new_bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - new_bs = ArrayBuffer::NewBackingStore(env()->isolate(), - pending_len + nread); - } + std::unique_ptr new_bs = ArrayBuffer::NewBackingStore( + env()->isolate(), + pending_len + nread, + BackingStoreInitializationMode::kUninitialized); memcpy(static_cast(new_bs->Data()), stream_buf_.base + stream_buf_offset_, pending_len); diff --git a/src/stream_base.cc b/src/stream_base.cc index 9d855c2992492d..518e723272dcbc 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -19,6 +19,7 @@ namespace node { using v8::Array; using v8::ArrayBuffer; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::ConstructorBehavior; using v8::Context; using v8::DontDelete; @@ -243,8 +244,8 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { std::unique_ptr bs; if (storage_size > 0) { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(isolate, storage_size); + bs = ArrayBuffer::NewBackingStore( + isolate, storage_size, BackingStoreInitializationMode::kUninitialized); } offset = 0; @@ -398,14 +399,14 @@ int StreamBase::WriteString(const FunctionCallbackInfo& args) { if (try_write) { // Copy partial data - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(isolate, buf.len); + bs = ArrayBuffer::NewBackingStore( + isolate, buf.len, BackingStoreInitializationMode::kUninitialized); memcpy(static_cast(bs->Data()), buf.base, buf.len); data_size = buf.len; } else { // Write it - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(isolate, storage_size); + bs = ArrayBuffer::NewBackingStore( + isolate, storage_size, BackingStoreInitializationMode::kUninitialized); data_size = StringBytes::Write(isolate, static_cast(bs->Data()), storage_size, From 08eeddfa8391fb8c085eb2d23ca155a9ae492d58 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 23 Jan 2025 20:38:49 -0800 Subject: [PATCH 108/208] test: enforce strict mode in test-zlib-const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of checking that assignments fail silently in sloppy mode, check that they throw in strict mode. PR-URL: https://github.com/nodejs/node/pull/56689 Reviewed-By: Michaël Zasso Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- test/parallel/test-zlib-const.js | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/test/parallel/test-zlib-const.js b/test/parallel/test-zlib-const.js index 342c8c712a475b..5b9a127f0eaa02 100644 --- a/test/parallel/test-zlib-const.js +++ b/test/parallel/test-zlib-const.js @@ -1,4 +1,4 @@ -/* eslint-disable strict */ +'use strict'; require('../common'); const assert = require('assert'); @@ -9,27 +9,17 @@ assert.strictEqual(zlib.constants.Z_OK, 0, 'Expected Z_OK to be 0;', `got ${zlib.constants.Z_OK}`, ].join(' ')); -zlib.constants.Z_OK = 1; -assert.strictEqual(zlib.constants.Z_OK, 0, - [ - 'Z_OK should be immutable.', - `Expected to get 0, got ${zlib.constants.Z_OK}`, - ].join(' ')); + +assert.throws(() => { zlib.constants.Z_OK = 1; }, + TypeError, 'zlib.constants.Z_OK should be immutable'); assert.strictEqual(zlib.codes.Z_OK, 0, `Expected Z_OK to be 0; got ${zlib.codes.Z_OK}`); -zlib.codes.Z_OK = 1; -assert.strictEqual(zlib.codes.Z_OK, 0, - [ - 'Z_OK should be immutable.', - `Expected to get 0, got ${zlib.codes.Z_OK}`, - ].join(' ')); -zlib.codes = { Z_OK: 1 }; -assert.strictEqual(zlib.codes.Z_OK, 0, - [ - 'Z_OK should be immutable.', - `Expected to get 0, got ${zlib.codes.Z_OK}`, - ].join(' ')); +assert.throws(() => { zlib.codes.Z_OK = 1; }, + TypeError, 'zlib.codes.Z_OK should be immutable'); + +assert.throws(() => { zlib.codes = { Z_OK: 1 }; }, + TypeError, 'zlib.codes should be immutable'); assert.ok(Object.isFrozen(zlib.codes), [ From e55b02b368d479bdd27eea4aa67ecdfa62ebd7bd Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Fri, 24 Jan 2025 05:44:05 -0500 Subject: [PATCH 109/208] build: drop support for python 3.8 PR-URL: https://github.com/nodejs/node/pull/55239 Reviewed-By: Christian Clauss --- android-configure | 3 +-- configure | 3 +-- configure.py | 2 +- pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/android-configure b/android-configure index fb237241d7413d..2558f52757e442 100755 --- a/android-configure +++ b/android-configure @@ -9,7 +9,6 @@ command -v python3.12 >/dev/null && exec python3.12 "$0" "$@" command -v python3.11 >/dev/null && exec python3.11 "$0" "$@" command -v python3.10 >/dev/null && exec python3.10 "$0" "$@" command -v python3.9 >/dev/null && exec python3.9 "$0" "$@" -command -v python3.8 >/dev/null && exec python3.8 "$0" "$@" command -v python3 >/dev/null && exec python3 "$0" "$@" exec python "$0" "$@" ''' "$0" "$@" @@ -23,7 +22,7 @@ except ImportError: from distutils.spawn import find_executable as which print('Node.js android configure: Found Python {}.{}.{}...'.format(*sys.version_info)) -acceptable_pythons = ((3, 13), (3, 12), (3, 11), (3, 10), (3, 9), (3, 8)) +acceptable_pythons = ((3, 13), (3, 12), (3, 11), (3, 10), (3, 9)) if sys.version_info[:2] in acceptable_pythons: import android_configure else: diff --git a/configure b/configure index 56720e8f4c42d9..412f0b416e79c3 100755 --- a/configure +++ b/configure @@ -9,7 +9,6 @@ command -v python3.12 >/dev/null && exec python3.12 "$0" "$@" command -v python3.11 >/dev/null && exec python3.11 "$0" "$@" command -v python3.10 >/dev/null && exec python3.10 "$0" "$@" command -v python3.9 >/dev/null && exec python3.9 "$0" "$@" -command -v python3.8 >/dev/null && exec python3.8 "$0" "$@" command -v python3 >/dev/null && exec python3 "$0" "$@" exec python "$0" "$@" ''' "$0" "$@" @@ -23,7 +22,7 @@ except ImportError: from distutils.spawn import find_executable as which print('Node.js configure: Found Python {}.{}.{}...'.format(*sys.version_info)) -acceptable_pythons = ((3, 13), (3, 12), (3, 11), (3, 10), (3, 9), (3, 8)) +acceptable_pythons = ((3, 13), (3, 12), (3, 11), (3, 10), (3, 9)) if sys.version_info[:2] in acceptable_pythons: import configure else: diff --git a/configure.py b/configure.py index c361676637c1cb..618117370238f7 100755 --- a/configure.py +++ b/configure.py @@ -2143,7 +2143,7 @@ def make_bin_override(): if sys.platform == 'win32': raise Exception('make_bin_override should not be called on win32.') # If the system python is not the python we are running (which should be - # python 3.8+), then create a directory with a symlink called `python` to our + # python 3.9+), then create a directory with a symlink called `python` to our # sys.executable. This directory will be prefixed to the PATH, so that # other tools that shell out to `python` will use the appropriate python diff --git a/pyproject.toml b/pyproject.toml index 45f540bd15170d..43da73beceee7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ exclude = [ "tools/eslint/node_modules" ] line-length = 172 -target-version = "py38" +target-version = "py39" [tool.ruff.lint] select = [ From 1921371349c15d31b0b6884225a2093f0925a3d7 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 24 Jan 2025 15:22:12 +0100 Subject: [PATCH 110/208] tools: do not throw on missing `create-release-proposal.sh` PR-URL: https://github.com/nodejs/node/pull/56704 Reviewed-By: James M Snell Reviewed-By: Tierney Cyren Reviewed-By: Rafael Gonzaga --- .github/workflows/create-release-proposal.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/create-release-proposal.yml b/.github/workflows/create-release-proposal.yml index 33426bdcda4a5b..0b580eab81ac76 100644 --- a/.github/workflows/create-release-proposal.yml +++ b/.github/workflows/create-release-proposal.yml @@ -71,13 +71,10 @@ jobs: git config --local user.name "Node.js GitHub Bot" - name: Start git node release prepare - # `git update-index` tells git to ignore future changes to the `.sh` file, - # `|| true` is there to ignore the error if such file doesn't exist yet. # The curl command is to make sure we run the version of the script corresponding to the current workflow. run: | - git update-index --assume-unchanged tools/actions/create-release-proposal.sh || true - curl -fsSLo tools/actions/create-release-proposal.sh https://github.com/${GITHUB_REPOSITORY}/raw/${GITHUB_SHA}/tools/actions/create-release-proposal.sh - ./tools/actions/create-release-proposal.sh "${RELEASE_DATE}" "${RELEASE_LINE}" "${GITHUB_ACTOR}" + curl -fsSL https://github.com/${GITHUB_REPOSITORY}/raw/${GITHUB_SHA}/tools/actions/create-release-proposal.sh |\ + sh -s -- "${RELEASE_DATE}" "${RELEASE_LINE}" "${GITHUB_ACTOR}" env: GH_TOKEN: ${{ github.token }} # We want the bot to push the push the release commit so CI runs on it. From 19fabc0e3175d73e5b4c8acab66dd5424372ff55 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 12 Jan 2025 13:04:40 -0800 Subject: [PATCH 111/208] util: inspect: do not crash on an Error stack that contains a Symbol See #56570 PR-URL: https://github.com/nodejs/node/pull/56573 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Jason Zhang Reviewed-By: Ruben Bridgewater --- lib/internal/util/inspect.js | 14 ++++++++++---- test/parallel/test-util-inspect.js | 20 ++++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 60bee498e5ea8b..f38eecba6ae5fb 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -1287,8 +1287,14 @@ function identicalSequenceRange(a, b) { return { len: 0, offset: 0 }; } -function getStackString(error) { - return error.stack ? String(error.stack) : ErrorPrototypeToString(error); +function getStackString(ctx, error) { + if (error.stack) { + if (typeof error.stack === 'string') { + return error.stack; + } + return formatValue(ctx, error.stack); + } + return ErrorPrototypeToString(error); } function getStackFrames(ctx, err, stack) { @@ -1303,7 +1309,7 @@ function getStackFrames(ctx, err, stack) { // Remove stack frames identical to frames in cause. if (cause != null && isError(cause)) { - const causeStack = getStackString(cause); + const causeStack = getStackString(ctx, cause); const causeStackStart = StringPrototypeIndexOf(causeStack, '\n at'); if (causeStackStart !== -1) { const causeFrames = StringPrototypeSplit(StringPrototypeSlice(causeStack, causeStackStart + 1), '\n'); @@ -1426,7 +1432,7 @@ function safeGetCWD() { function formatError(err, constructor, tag, ctx, keys) { const name = err.name != null ? err.name : 'Error'; - let stack = getStackString(err); + let stack = getStackString(ctx, err); removeDuplicateErrorKeys(ctx, keys, err, stack); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 0b04e3b8dc7179..87d92369b8deca 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -777,16 +777,18 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324'); [undefined, 'RangeError: foo', '[RangeError: foo]'], [false, 'false [RangeError]: foo', '[RangeError: foo]'], ['', 'foo', '[RangeError: foo]'], - [[1, 2, 3], '1,2,3 [RangeError]: foo', '[1,2,3]'], + [[1, 2, 3], '1,2,3 [RangeError]: foo', '[[\n 1,\n 2,\n 3\n]]'], ].forEach(([value, outputStart, stack]) => { let err = new RangeError('foo'); err.name = value; + const result = util.inspect(err); assert( - util.inspect(err).startsWith(outputStart), + result.startsWith(outputStart), util.format( - 'The name set to %o did not result in the expected output "%s"', + 'The name set to %o did not result in the expected output "%s", got "%s"', value, - outputStart + outputStart, + result.split('\n')[0] ) ); @@ -3448,3 +3450,13 @@ assert.strictEqual( ${error.stack.split('\n').slice(1).join('\n')}`, ); } + +{ + const error = new Error(); + error.stack = [Symbol('foo')]; + + assert.strictEqual( + inspect(error), + '[[\n Symbol(foo)\n]]' + ); +} From 01a5aa2ac10c051336e8ad4062b87f73a4d7824e Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 24 Jan 2025 11:22:54 -0500 Subject: [PATCH 112/208] test: add missing test for env file PR-URL: https://github.com/nodejs/node/pull/56642 Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca Reviewed-By: Jake Yuesong Li Reviewed-By: Matteo Collina Reviewed-By: James M Snell --- test/parallel/test-dotenv-edge-cases.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/parallel/test-dotenv-edge-cases.js b/test/parallel/test-dotenv-edge-cases.js index 769d33a13b8ce9..926c8d0793ac8b 100644 --- a/test/parallel/test-dotenv-edge-cases.js +++ b/test/parallel/test-dotenv-edge-cases.js @@ -170,4 +170,16 @@ describe('.env supports edge cases', () => { assert.strictEqual(SingleQuotesChild.stderr, ''); assert.strictEqual(SingleQuotesChild.code, 0); }); + + it('should reject invalid env file flag', async () => { + const child = await common.spawnPromisified( + process.execPath, + ['--env-file-ABCD', validEnvFilePath], + { cwd: __dirname }, + ); + + assert.strictEqual(child.stdout, ''); + assert.strictEqual(child.code, 9); + assert.match(child.stderr, /bad option: --env-file-ABCD/); + }); }); From c752615e2bbea73ec59de0439c997d334577fdae Mon Sep 17 00:00:00 2001 From: Pietro Marchini Date: Sun, 19 Jan 2025 23:57:49 +0100 Subject: [PATCH 113/208] test_runner: print failing assertion only once with spec reporter Co-authored-by: Llorx PR-URL: https://github.com/nodejs/node/pull/56662 Reviewed-By: Chengzhong Wu Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- lib/internal/test_runner/reporter/spec.js | 2 +- lib/internal/test_runner/reporter/utils.js | 6 +- .../output/assertion-color-tty.snapshot | 12 - .../output/default_output.snapshot | 23 -- .../test-runner/output/eval_spec.snapshot | 9 - .../output/hooks_spec_reporter.snapshot | 311 ------------------ .../test-runner/output/spec_reporter.snapshot | 212 ------------ .../output/spec_reporter_cli.snapshot | 212 ------------ 8 files changed, 5 insertions(+), 782 deletions(-) diff --git a/lib/internal/test_runner/reporter/spec.js b/lib/internal/test_runner/reporter/spec.js index 2092d22e3fe77f..caee92a7ba13e1 100644 --- a/lib/internal/test_runner/reporter/spec.js +++ b/lib/internal/test_runner/reporter/spec.js @@ -52,7 +52,7 @@ class SpecReporter extends Transform { hasChildren = true; } const indentation = indent(data.nesting); - return `${formatTestReport(type, data, prefix, indentation, hasChildren)}\n`; + return `${formatTestReport(type, data, prefix, indentation, hasChildren, false)}\n`; } #handleEvent({ type, data }) { switch (type) { diff --git a/lib/internal/test_runner/reporter/utils.js b/lib/internal/test_runner/reporter/utils.js index 9b6cc96a185a9a..256619039e8e90 100644 --- a/lib/internal/test_runner/reporter/utils.js +++ b/lib/internal/test_runner/reporter/utils.js @@ -59,7 +59,7 @@ function formatError(error, indent) { return `\n${indent} ${message}\n`; } -function formatTestReport(type, data, prefix = '', indent = '', hasChildren = false) { +function formatTestReport(type, data, prefix = '', indent = '', hasChildren = false, showErrorDetails = true) { let color = reporterColorMap[type] ?? colors.white; let symbol = reporterUnicodeSymbolMap[type] ?? ' '; const { skip, todo } = data; @@ -71,10 +71,12 @@ function formatTestReport(type, data, prefix = '', indent = '', hasChildren = fa } else if (todo !== undefined) { title += ` # ${typeof todo === 'string' && todo.length ? todo : 'TODO'}`; } - const error = formatError(data.details?.error, indent); + + const error = showErrorDetails ? formatError(data.details?.error, indent) : ''; const err = hasChildren ? (!error || data.details?.error?.failureType === 'subtestsFailed' ? '' : `\n${error}`) : error; + if (skip !== undefined) { color = colors.gray; symbol = reporterUnicodeSymbolMap['hyphen:minus']; diff --git a/test/fixtures/test-runner/output/assertion-color-tty.snapshot b/test/fixtures/test-runner/output/assertion-color-tty.snapshot index 2909d909351743..a74016febc5df4 100644 --- a/test/fixtures/test-runner/output/assertion-color-tty.snapshot +++ b/test/fixtures/test-runner/output/assertion-color-tty.snapshot @@ -1,16 +1,4 @@ [31m✖ failing assertion [90m(*ms)[39m[39m - [AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: - [32mactual[39m [31mexpected[39m - - [39m'[39m[32m![39m[39mH[39m[39me[39m[39ml[39m[39ml[39m[39mo[39m[39m [39m[39mW[39m[39mo[39m[39mr[39m[39ml[39m[39md[39m[31m![39m[39m'[39m - ] { - generatedMessage: [33mtrue[39m, - code: [32m'ERR_ASSERTION'[39m, - actual: [32m'!Hello World'[39m, - expected: [32m'Hello World!'[39m, - operator: [32m'strictEqual'[39m - } - [34mℹ tests 1[39m [34mℹ suites 0[39m [34mℹ pass 0[39m diff --git a/test/fixtures/test-runner/output/default_output.snapshot b/test/fixtures/test-runner/output/default_output.snapshot index 73bfc1da5e92e9..d0a83395733924 100644 --- a/test/fixtures/test-runner/output/default_output.snapshot +++ b/test/fixtures/test-runner/output/default_output.snapshot @@ -1,32 +1,9 @@ [32m✔ should pass [90m(*ms)[39m[39m [31m✖ should fail [90m(*ms)[39m[39m - Error: fail - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - [90m﹣ should skip [90m(*ms)[39m # SKIP[39m ▶ parent [31m✖ should fail [90m(*ms)[39m[39m - Error: fail - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - [31m✖ should pass but parent fail [90m(*ms)[39m[39m - [32m'test did not finish before its parent and was cancelled'[39m - [31m✖ parent [90m(*ms)[39m[39m [34mℹ tests 6[39m [34mℹ suites 0[39m diff --git a/test/fixtures/test-runner/output/eval_spec.snapshot b/test/fixtures/test-runner/output/eval_spec.snapshot index 5c9a53009508e1..116c23ccf97077 100644 --- a/test/fixtures/test-runner/output/eval_spec.snapshot +++ b/test/fixtures/test-runner/output/eval_spec.snapshot @@ -1,14 +1,5 @@ ✔ passes (*ms) ✖ fails (*ms) - Error: fail - * - * - * - * - * - * - * - ℹ tests 2 ℹ suites 0 ℹ pass 1 diff --git a/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot b/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot index ea916c2ee754c4..8c267672b9a951 100644 --- a/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot +++ b/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot @@ -10,161 +10,29 @@ describe hooks - no subtests (*ms) before throws 1 - 'test did not finish before its parent and was cancelled' - 2 - 'test did not finish before its parent and was cancelled' - before throws (*ms) - - Error: before - * - * - * - * - * - * - * - * - before throws - no subtests (*ms) - Error: before - * - * - * - * - * - * - * - * - after throws 1 (*ms) 2 (*ms) after throws (*ms) - - Error: after - * - * - * - * - * - * - * - * - * - * - after throws - no subtests (*ms) - Error: after - * - * - * - * - * - * - * - * - * - * - beforeEach throws 1 (*ms) - Error: beforeEach - * - * - * - * - * - * - * - * - * - at new Promise () - 2 (*ms) - Error: beforeEach - * - * - * - * - * - * - * - * - * - at async Promise.all (index 0) - beforeEach throws (*ms) afterEach throws 1 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - at async Promise.all (index 0) - * - 2 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - afterEach throws (*ms) afterEach when test fails 1 (*ms) - Error: test - * - * - * - * - * - * - at new Promise () - * - * - at Array.map () - 2 (*ms) afterEach when test fails (*ms) afterEach throws and test fails 1 (*ms) - Error: test - * - * - * - * - * - * - at new Promise () - * - * - at Array.map () - 2 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - afterEach throws and test fails (*ms) test hooks 1 (*ms) @@ -177,155 +45,24 @@ test hooks - no subtests (*ms) t.before throws 1 (*ms) - Error: before - * - * - * - * - * - * - * - * - * - * - 2 (*ms) - Error: before - * - * - * - * - * - * - * - * - * - * - t.before throws (*ms) - - Error: before - * - * - * - * - * - * - * - * - * - * - t.before throws - no subtests (*ms) - Error: before - * - * - * - * - * - * - * - * - * - * - t.after throws 1 (*ms) 2 (*ms) t.after throws (*ms) - - Error: after - * - * - * - * - * - * - * - * - * - t.after throws - no subtests (*ms) - Error: after - * - * - * - * - * - * - * - * - * - t.beforeEach throws 1 (*ms) - Error: beforeEach - * - * - * - * - * - * - * - * - * - * - 2 (*ms) - Error: beforeEach - * - * - * - * - * - * - * - * - * - * - t.beforeEach throws (*ms) t.afterEach throws 1 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - * - 2 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - * - t.afterEach throws (*ms) afterEach when test fails 1 (*ms) - Error: test - * - * - * - * - * - * - * - * - * - 2 (*ms) afterEach when test fails (*ms) afterEach context when test passes @@ -333,64 +70,16 @@ afterEach context when test passes (*ms) afterEach context when test fails 1 (*ms) - Error: test - * - * - * - * - afterEach context when test fails (*ms) afterEach throws and test fails 1 (*ms) - Error: test - * - * - * - * - * - * - * - * - * - 2 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - * - afterEach throws and test fails (*ms) t.after() is called if test body throws (*ms) - Error: bye - * - * - * - * - - after() called run after when before throws 1 - 'test did not finish before its parent and was cancelled' - run after when before throws (*ms) - - Error: before - * - * - * - * - * - * - * - * - test hooks - async 1 (*ms) 2 (*ms) diff --git a/test/fixtures/test-runner/output/spec_reporter.snapshot b/test/fixtures/test-runner/output/spec_reporter.snapshot index f3aebbd7fe5aae..1892069327f92d 100644 --- a/test/fixtures/test-runner/output/spec_reporter.snapshot +++ b/test/fixtures/test-runner/output/spec_reporter.snapshot @@ -1,91 +1,19 @@ sync pass todo (*ms) # TODO sync pass todo with message (*ms) # this is a passing todo sync fail todo (*ms) # TODO - Error: thrown from sync fail todo - * - * - * - * - * - * - * - sync fail todo with message (*ms) # this is a failing todo - Error: thrown from sync fail todo with message - * - * - * - * - * - * - * - sync skip pass (*ms) # SKIP sync skip pass with message (*ms) # this is skipped sync pass (*ms) this test should pass sync throw fail (*ms) - Error: thrown from sync throw fail - * - * - * - * - * - * - * - async skip pass (*ms) # SKIP async pass (*ms) async throw fail (*ms) - Error: thrown from async throw fail - * - * - * - * - * - * - * - async skip fail (*ms) # SKIP - Error: thrown from async throw fail - * - * - * - * - * - * - * - async assertion fail (*ms) - AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: - - true !== false - - * - * - * - * - * - * - * { - generatedMessage: true, - code: 'ERR_ASSERTION', - actual: true, - expected: false, - operator: 'strictEqual' - } - resolve pass (*ms) reject fail (*ms) - Error: rejected from reject fail - * - * - * - * - * - * - * - unhandled rejection - passes but warns (*ms) async unhandled rejection - passes but warns (*ms) immediate throw - passes but warns (*ms) @@ -93,23 +21,9 @@ immediate resolve pass (*ms) subtest sync throw fail +sync throw fail (*ms) - Error: thrown from subtest sync throw fail - * - * - * - * - * - * - * - * - * - * - this subtest should make its parent test fail subtest sync throw fail (*ms) sync throw non-error fail (*ms) - Symbol(thrown symbol from sync throw non-error fail) - level 0a level 1a (*ms) level 1b (*ms) @@ -118,8 +32,6 @@ level 0a (*ms) top level +long running (*ms) - 'test did not finish before its parent and was cancelled' - +short running ++short running (*ms) +short running (*ms) @@ -128,15 +40,6 @@ sync skip option (*ms) # SKIP sync skip option with message (*ms) # this is skipped sync skip option is false fail (*ms) - Error: this should be executed - * - * - * - * - * - * - * - (*ms) functionOnly (*ms) (*ms) @@ -147,43 +50,15 @@ functionAndOptions (*ms) # SKIP callback pass (*ms) callback fail (*ms) - Error: callback failure - * - * - sync t is this in test (*ms) async t is this in test (*ms) callback t is this in test (*ms) callback also returns a Promise (*ms) - 'passed a callback but also returned a Promise' - callback throw (*ms) - Error: thrown from callback throw - * - * - * - * - * - * - * - callback called twice (*ms) - 'callback invoked multiple times' - callback called twice in different ticks (*ms) callback called twice in future tick (*ms) - Error [ERR_TEST_FAILURE]: callback invoked multiple times - * { - code: 'ERR_TEST_FAILURE', - failureType: 'multipleCallbackInvocations', - cause: 'callback invoked multiple times' - } - callback async throw (*ms) - Error: thrown from callback async throw - * - * - callback async throw after done (*ms) only is set on subtests but not in only mode running subtest 1 (*ms) @@ -191,108 +66,21 @@ running subtest 4 (*ms) only is set on subtests but not in only mode (*ms) custom inspect symbol fail (*ms) - customized - custom inspect symbol that throws fail (*ms) - { foo: 1, Symbol(nodejs.util.inspect.custom): [Function: [nodejs.util.inspect.custom]] } - subtest sync throw fails sync throw fails at first (*ms) - Error: thrown from subtest sync throw fails at first - * - * - * - * - * - * - * - * - * - * - sync throw fails at second (*ms) - Error: thrown from subtest sync throw fails at second - * - * - * - * - * - * - * - * - subtest sync throw fails (*ms) timed out async test (*ms) - 'test timed out after *ms' - timed out callback test (*ms) - 'test timed out after *ms' - large timeout async test is ok (*ms) large timeout callback test is ok (*ms) successful thenable (*ms) rejected thenable (*ms) - 'custom error' - unfinished test with uncaughtException (*ms) - Error: foo - * - * - * - unfinished test with unhandledRejection (*ms) - Error: bar - * - * - * - assertion errors display actual and expected properly (*ms) - AssertionError [ERR_ASSERTION]: Expected values to be loosely deep-equal: - - { - bar: 1, - baz: { - date: 1970-01-01T00:00:00.000Z, - null: null, - number: 1, - string: 'Hello', - undefined: undefined - }, - boo: [ - 1 - ], - foo: 1 - } - - should loosely deep-equal - - { - baz: { - date: 1970-01-01T00:00:00.000Z, - null: null, - number: 1, - string: 'Hello', - undefined: undefined - }, - boo: [ - 1 - ], - circular: { - bar: 2, - c: [Circular *1] - } - } - * { - generatedMessage: true, - code: 'ERR_ASSERTION', - actual: [Object], - expected: [Object], - operator: 'deepEqual' - } - invalid subtest fail (*ms) - 'test could not be started because its parent finished' - Error: Test "unhandled rejection - passes but warns" at test/fixtures/test-runner/output/output.js:72:1 generated asynchronous activity after the test ended. This activity created the error "Error: rejected from unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Error: Test "async unhandled rejection - passes but warns" at test/fixtures/test-runner/output/output.js:76:1 generated asynchronous activity after the test ended. This activity created the error "Error: rejected from async unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Error: A resource generated asynchronous activity after the test ended. This activity created the error "Error: uncaught from outside of a test" which triggered an uncaughtException event, caught by the test runner. diff --git a/test/fixtures/test-runner/output/spec_reporter_cli.snapshot b/test/fixtures/test-runner/output/spec_reporter_cli.snapshot index 2e5f263e1a5e3a..52dc40bb366e2c 100644 --- a/test/fixtures/test-runner/output/spec_reporter_cli.snapshot +++ b/test/fixtures/test-runner/output/spec_reporter_cli.snapshot @@ -1,91 +1,19 @@ sync pass todo (*ms) # TODO sync pass todo with message (*ms) # this is a passing todo sync fail todo (*ms) # TODO - Error: thrown from sync fail todo - * - * - * - * - * - * - * - sync fail todo with message (*ms) # this is a failing todo - Error: thrown from sync fail todo with message - * - * - * - * - * - * - * - sync skip pass (*ms) # SKIP sync skip pass with message (*ms) # this is skipped sync pass (*ms) this test should pass sync throw fail (*ms) - Error: thrown from sync throw fail - * - * - * - * - * - * - * - async skip pass (*ms) # SKIP async pass (*ms) async throw fail (*ms) - Error: thrown from async throw fail - * - * - * - * - * - * - * - async skip fail (*ms) # SKIP - Error: thrown from async throw fail - * - * - * - * - * - * - * - async assertion fail (*ms) - AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: - - true !== false - - * - * - * - * - * - * - * { - generatedMessage: true, - code: 'ERR_ASSERTION', - actual: true, - expected: false, - operator: 'strictEqual' - } - resolve pass (*ms) reject fail (*ms) - Error: rejected from reject fail - * - * - * - * - * - * - * - unhandled rejection - passes but warns (*ms) async unhandled rejection - passes but warns (*ms) immediate throw - passes but warns (*ms) @@ -93,23 +21,9 @@ immediate resolve pass (*ms) subtest sync throw fail +sync throw fail (*ms) - Error: thrown from subtest sync throw fail - * - * - * - * - * - * - * - * - * - * - this subtest should make its parent test fail subtest sync throw fail (*ms) sync throw non-error fail (*ms) - Symbol(thrown symbol from sync throw non-error fail) - level 0a level 1a (*ms) level 1b (*ms) @@ -118,8 +32,6 @@ level 0a (*ms) top level +long running (*ms) - 'test did not finish before its parent and was cancelled' - +short running ++short running (*ms) +short running (*ms) @@ -128,15 +40,6 @@ sync skip option (*ms) # SKIP sync skip option with message (*ms) # this is skipped sync skip option is false fail (*ms) - Error: this should be executed - * - * - * - * - * - * - * - (*ms) functionOnly (*ms) (*ms) @@ -147,43 +50,15 @@ functionAndOptions (*ms) # SKIP callback pass (*ms) callback fail (*ms) - Error: callback failure - * - * - sync t is this in test (*ms) async t is this in test (*ms) callback t is this in test (*ms) callback also returns a Promise (*ms) - 'passed a callback but also returned a Promise' - callback throw (*ms) - Error: thrown from callback throw - * - * - * - * - * - * - * - callback called twice (*ms) - 'callback invoked multiple times' - callback called twice in different ticks (*ms) callback called twice in future tick (*ms) - Error [ERR_TEST_FAILURE]: callback invoked multiple times - * { - code: 'ERR_TEST_FAILURE', - failureType: 'multipleCallbackInvocations', - cause: 'callback invoked multiple times' - } - callback async throw (*ms) - Error: thrown from callback async throw - * - * - callback async throw after done (*ms) only is set on subtests but not in only mode running subtest 1 (*ms) @@ -194,108 +69,21 @@ running subtest 4 (*ms) only is set on subtests but not in only mode (*ms) custom inspect symbol fail (*ms) - customized - custom inspect symbol that throws fail (*ms) - { foo: 1 } - subtest sync throw fails sync throw fails at first (*ms) - Error: thrown from subtest sync throw fails at first - * - * - * - * - * - * - * - * - * - * - sync throw fails at second (*ms) - Error: thrown from subtest sync throw fails at second - * - * - * - * - * - * - * - * - subtest sync throw fails (*ms) timed out async test (*ms) - 'test timed out after *ms' - timed out callback test (*ms) - 'test timed out after *ms' - large timeout async test is ok (*ms) large timeout callback test is ok (*ms) successful thenable (*ms) rejected thenable (*ms) - 'custom error' - unfinished test with uncaughtException (*ms) - Error: foo - * - * - * - unfinished test with unhandledRejection (*ms) - Error: bar - * - * - * - assertion errors display actual and expected properly (*ms) - AssertionError [ERR_ASSERTION]: Expected values to be loosely deep-equal: - - { - bar: 1, - baz: { - date: 1970-01-01T00:00:00.000Z, - null: null, - number: 1, - string: 'Hello', - undefined: undefined - }, - boo: [ - 1 - ], - foo: 1 - } - - should loosely deep-equal - - { - baz: { - date: 1970-01-01T00:00:00.000Z, - null: null, - number: 1, - string: 'Hello', - undefined: undefined - }, - boo: [ - 1 - ], - circular: { - bar: 2, - c: [Circular *1] - } - } - * { - generatedMessage: true, - code: 'ERR_ASSERTION', - actual: { foo: 1, bar: 1, boo: [ 1 ], baz: { date: 1970-01-01T00:00:00.000Z, null: null, number: 1, string: 'Hello', undefined: undefined } }, - expected: { boo: [ 1 ], baz: { date: 1970-01-01T00:00:00.000Z, null: null, number: 1, string: 'Hello', undefined: undefined }, circular: { bar: 2, c: [Circular *1] } }, - operator: 'deepEqual' - } - invalid subtest fail (*ms) - 'test could not be started because its parent finished' - Error: Test "unhandled rejection - passes but warns" at test/fixtures/test-runner/output/output.js:72:1 generated asynchronous activity after the test ended. This activity created the error "Error: rejected from unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Error: Test "async unhandled rejection - passes but warns" at test/fixtures/test-runner/output/output.js:76:1 generated asynchronous activity after the test ended. This activity created the error "Error: rejected from async unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Error: A resource generated asynchronous activity after the test ended. This activity created the error "Error: uncaught from outside of a test" which triggered an uncaughtException event, caught by the test runner. From 761de815c5cce3fb67f0d14ecabc5397497285ff Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 24 Jan 2025 16:58:32 -0800 Subject: [PATCH 114/208] test: move crypto related common utilities in common/crypto Since `common/crypto` already exists, it makes sense to keep crypto-related utilities there. The only exception being common.hasCrypto which is needed up front to determine if tests should be skipped. Eliminate the redundant check in hasFipsCrypto and just use crypto.getFips() directly where needed. PR-URL: https://github.com/nodejs/node/pull/56714 Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca --- test/addons/openssl-providers/providers.cjs | 7 ++- test/benchmark/test-benchmark-crypto.js | 5 +- test/common/README.md | 17 ------ test/common/crypto.js | 52 ++++++++++++++++++- test/common/index.js | 52 ------------------- test/common/index.mjs | 2 - test/parallel/test-cli-node-options.js | 3 +- test/parallel/test-crypto-authenticated.js | 20 ++++--- .../test-crypto-cipheriv-decipheriv.js | 10 ++-- test/parallel/test-crypto-classes.js | 6 +-- test/parallel/test-crypto-dh-constructor.js | 3 +- test/parallel/test-crypto-dh-errors.js | 3 +- test/parallel/test-crypto-dh-generate-keys.js | 3 +- test/parallel/test-crypto-dh-leak.js | 3 +- test/parallel/test-crypto-dh-odd-key.js | 8 +-- test/parallel/test-crypto-dh-stateless.js | 7 +-- test/parallel/test-crypto-dh.js | 17 +++--- test/parallel/test-crypto-ecb.js | 13 +++-- test/parallel/test-crypto-fips.js | 5 +- test/parallel/test-crypto-hash.js | 7 +-- test/parallel/test-crypto-hkdf.js | 5 +- test/parallel/test-crypto-hmac.js | 5 +- test/parallel/test-crypto-key-objects.js | 10 ++-- ...test-crypto-keygen-async-dsa-key-object.js | 8 +-- test/parallel/test-crypto-keygen-async-dsa.js | 8 +-- ...-explicit-elliptic-curve-encrypted-p256.js | 4 +- ...nc-explicit-elliptic-curve-encrypted.js.js | 3 +- ...ync-named-elliptic-curve-encrypted-p256.js | 3 +- ...en-async-named-elliptic-curve-encrypted.js | 3 +- test/parallel/test-crypto-keygen-async-rsa.js | 3 +- .../parallel/test-crypto-keygen-bit-length.js | 3 +- ...rypto-keygen-empty-passphrase-no-prompt.js | 3 +- .../test-crypto-keygen-missing-oid.js | 4 +- test/parallel/test-crypto-keygen.js | 3 +- test/parallel/test-crypto-no-algorithm.js | 4 +- test/parallel/test-crypto-oneshot-hash.js | 3 +- test/parallel/test-crypto-padding.js | 5 +- test/parallel/test-crypto-pbkdf2.js | 3 +- .../test-crypto-private-decrypt-gh32240.js | 4 +- ...t-crypto-publicDecrypt-fails-first-time.js | 8 ++- test/parallel/test-crypto-rsa-dsa.js | 7 +-- test/parallel/test-crypto-secure-heap.js | 13 +++-- test/parallel/test-crypto-sign-verify.js | 13 +++-- test/parallel/test-crypto-stream.js | 8 +-- test/parallel/test-crypto-x509.js | 5 +- test/parallel/test-crypto.js | 7 +-- test/parallel/test-dsa-fips-invalid-key.js | 10 +++- .../test-https-agent-session-eviction.js | 9 ++-- .../test-https-client-renegotiation-limit.js | 8 ++- test/parallel/test-https-foafssl.js | 10 ++-- ...rocess-env-allowed-flags-are-documented.js | 7 +-- test/parallel/test-process-versions.js | 3 +- test/parallel/test-tls-alert-handling.js | 20 ++++--- test/parallel/test-tls-alert.js | 15 ++++-- test/parallel/test-tls-alpn-server-client.js | 5 +- test/parallel/test-tls-cert-ext-encoding.js | 4 +- test/parallel/test-tls-client-auth.js | 7 ++- .../test-tls-client-getephemeralkeyinfo.js | 3 +- test/parallel/test-tls-client-mindhsize.js | 5 +- .../test-tls-client-renegotiation-13.js | 8 ++- .../test-tls-client-renegotiation-limit.js | 8 ++- test/parallel/test-tls-dhe.js | 17 ++++-- test/parallel/test-tls-ecdh-auto.js | 10 ++-- test/parallel/test-tls-ecdh-multiple.js | 14 +++-- test/parallel/test-tls-ecdh.js | 10 ++-- test/parallel/test-tls-empty-sni-context.js | 4 +- test/parallel/test-tls-getprotocol.js | 6 ++- test/parallel/test-tls-junk-server.js | 7 ++- test/parallel/test-tls-key-mismatch.js | 6 ++- test/parallel/test-tls-legacy-pfx.js | 9 +++- test/parallel/test-tls-min-max-version.js | 16 ++++-- test/parallel/test-tls-no-sslv3.js | 10 ++-- test/parallel/test-tls-ocsp-callback.js | 15 ++++-- test/parallel/test-tls-psk-circuit.js | 10 ++-- test/parallel/test-tls-psk-server.js | 11 ++-- test/parallel/test-tls-securepair-server.js | 10 ++-- test/parallel/test-tls-server-verify.js | 10 ++-- test/parallel/test-tls-session-cache.js | 20 ++++--- test/parallel/test-tls-set-ciphers.js | 16 ++++-- test/parallel/test-tls-set-secure-context.js | 6 ++- test/parallel/test-tls-set-sigalgs.js | 7 ++- test/parallel/test-trace-env.js | 8 +-- test/parallel/test-x509-escaping.js | 5 +- test/pummel/test-crypto-dh-hash.js | 4 +- test/pummel/test-crypto-dh-keys.js | 3 +- test/pummel/test-dh-regr.js | 3 +- test/sequential/test-tls-psk-client.js | 11 ++-- test/sequential/test-tls-securepair-client.js | 25 +++++---- test/sequential/test-tls-session-timeout.js | 10 ++-- 89 files changed, 505 insertions(+), 288 deletions(-) diff --git a/test/addons/openssl-providers/providers.cjs b/test/addons/openssl-providers/providers.cjs index 2dabbf020e2a41..efa1019c62d99c 100644 --- a/test/addons/openssl-providers/providers.cjs +++ b/test/addons/openssl-providers/providers.cjs @@ -1,11 +1,14 @@ 'use strict'; const common = require('../../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} +const { hasOpenSSL3 } = require('../../common/crypto'); -if (!common.hasOpenSSL3) +if (!hasOpenSSL3) { common.skip('this test requires OpenSSL 3.x'); +} const assert = require('node:assert'); const { createHash, getCiphers, getHashes } = require('node:crypto'); const { debuglog } = require('node:util'); diff --git a/test/benchmark/test-benchmark-crypto.js b/test/benchmark/test-benchmark-crypto.js index 7f6988acf234d8..72d79ece13e787 100644 --- a/test/benchmark/test-benchmark-crypto.js +++ b/test/benchmark/test-benchmark-crypto.js @@ -5,8 +5,11 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (common.hasFipsCrypto) +const { getFips } = require('crypto'); + +if (getFips()) { common.skip('some benchmarks are FIPS-incompatible'); +} const runBenchmark = require('../common/benchmark'); diff --git a/test/common/README.md b/test/common/README.md index 5f5ff75fca2431..ee36503f920001 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -226,17 +226,6 @@ The TTY file descriptor is assumed to be capable of being writable. Indicates whether OpenSSL is available. -### `hasFipsCrypto` - -* [\][] - -Indicates that Node.js has been linked with a FIPS compatible OpenSSL library, -and that FIPS as been enabled using `--enable-fips`. - -To only detect if the OpenSSL library is FIPS compatible, regardless if it has -been enabled or not, then `process.config.variables.openssl_is_fips` can be -used to determine that situation. - ### `hasIntl` * [\][] @@ -417,12 +406,6 @@ Returns `true` if the exit code `exitCode` and/or signal name `signal` represent the exit code and/or signal name of a node process that aborted, `false` otherwise. -### `opensslCli` - -* [\][] - -Indicates whether 'opensslCli' is supported. - ### `platformTimeout(ms)` * `ms` [\][] | [\][] diff --git a/test/common/crypto.js b/test/common/crypto.js index 10432d7e7a7e32..f50d3895a1783b 100644 --- a/test/common/crypto.js +++ b/test/common/crypto.js @@ -1,8 +1,9 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); @@ -98,6 +99,27 @@ const pkcs8EncExp = getRegExpForPEM('ENCRYPTED PRIVATE KEY'); const sec1Exp = getRegExpForPEM('EC PRIVATE KEY'); const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); +// Synthesize OPENSSL_VERSION_NUMBER format with the layout 0xMNN00PPSL +const opensslVersionNumber = (major = 0, minor = 0, patch = 0) => { + assert(major >= 0 && major <= 0xf); + assert(minor >= 0 && minor <= 0xff); + assert(patch >= 0 && patch <= 0xff); + return (major << 28) | (minor << 20) | (patch << 4); +}; + +let OPENSSL_VERSION_NUMBER; +const hasOpenSSL = (major = 0, minor = 0, patch = 0) => { + if (!common.hasCrypto) return false; + if (OPENSSL_VERSION_NUMBER === undefined) { + const regexp = /(?\d+)\.(?\d+)\.(?

      \d+)/; + const { m, n, p } = process.versions.openssl.match(regexp).groups; + OPENSSL_VERSION_NUMBER = opensslVersionNumber(m, n, p); + } + return OPENSSL_VERSION_NUMBER >= opensslVersionNumber(major, minor, patch); +}; + +let opensslCli = null; + module.exports = { modp2buf, assertApproximateSize, @@ -111,4 +133,32 @@ module.exports = { pkcs8EncExp, // used once sec1Exp, sec1EncExp, + hasOpenSSL, + get hasOpenSSL3() { + return hasOpenSSL(3); + }, + // opensslCli defined lazily to reduce overhead of spawnSync + get opensslCli() { + if (opensslCli !== null) return opensslCli; + + if (process.config.variables.node_shared_openssl) { + // Use external command + opensslCli = 'openssl'; + } else { + const path = require('path'); + // Use command built from sources included in Node.js repository + opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli'); + } + + if (exports.isWindows) opensslCli += '.exe'; + + const { spawnSync } = require('child_process'); + + const opensslCmd = spawnSync(opensslCli, ['version']); + if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) { + // OpenSSL command cannot be executed + opensslCli = false; + } + return opensslCli; + }, }; diff --git a/test/common/index.js b/test/common/index.js index b5592a66a081c3..d2c39578324600 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -19,7 +19,6 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -/* eslint-disable node-core/crypto-check */ 'use strict'; const process = global.process; // Some tests tamper with the process global. @@ -57,25 +56,6 @@ const noop = () => {}; const hasCrypto = Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO; -// Synthesize OPENSSL_VERSION_NUMBER format with the layout 0xMNN00PPSL -const opensslVersionNumber = (major = 0, minor = 0, patch = 0) => { - assert(major >= 0 && major <= 0xf); - assert(minor >= 0 && minor <= 0xff); - assert(patch >= 0 && patch <= 0xff); - return (major << 28) | (minor << 20) | (patch << 4); -}; - -let OPENSSL_VERSION_NUMBER; -const hasOpenSSL = (major = 0, minor = 0, patch = 0) => { - if (!hasCrypto) return false; - if (OPENSSL_VERSION_NUMBER === undefined) { - const regexp = /(?\d+)\.(?\d+)\.(?

      \d+)/; - const { m, n, p } = process.versions.openssl.match(regexp).groups; - OPENSSL_VERSION_NUMBER = opensslVersionNumber(m, n, p); - } - return OPENSSL_VERSION_NUMBER >= opensslVersionNumber(major, minor, patch); -}; - const hasQuic = hasCrypto && !!process.config.variables.openssl_quic; function parseTestFlags(filename = process.argv[1]) { @@ -220,7 +200,6 @@ if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) { }).enable(); } -let opensslCli = null; let inFreeBSDJail = null; let localhostIPv4 = null; @@ -985,7 +964,6 @@ const common = { getTTYfd, hasIntl, hasCrypto, - hasOpenSSL, hasQuic, hasMultiLocalhost, invalidArgTypeHelper, @@ -1027,10 +1005,6 @@ const common = { return require('os').totalmem() > 0x70000000; /* 1.75 Gb */ }, - get hasFipsCrypto() { - return hasCrypto && require('crypto').getFips(); - }, - get hasIPv6() { const iFaces = require('os').networkInterfaces(); let re; @@ -1047,10 +1021,6 @@ const common = { }); }, - get hasOpenSSL3() { - return hasOpenSSL(3); - }, - get inFreeBSDJail() { if (inFreeBSDJail !== null) return inFreeBSDJail; @@ -1100,28 +1070,6 @@ const common = { return localhostIPv4; }, - // opensslCli defined lazily to reduce overhead of spawnSync - get opensslCli() { - if (opensslCli !== null) return opensslCli; - - if (process.config.variables.node_shared_openssl) { - // Use external command - opensslCli = 'openssl'; - } else { - // Use command built from sources included in Node.js repository - opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli'); - } - - if (exports.isWindows) opensslCli += '.exe'; - - const opensslCmd = spawnSync(opensslCli, ['version']); - if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) { - // OpenSSL command cannot be executed - opensslCli = false; - } - return opensslCli; - }, - get PORT() { if (+process.env.TEST_PARALLEL) { throw new Error('common.PORT cannot be used in a parallelized test'); diff --git a/test/common/index.mjs b/test/common/index.mjs index b252f2dc4aac5e..23328ac90ea3c9 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -41,7 +41,6 @@ const { mustNotMutateObjectDeep, mustSucceed, nodeProcessAborted, - opensslCli, parseTestFlags, PIPE, platformTimeout, @@ -97,7 +96,6 @@ export { mustNotMutateObjectDeep, mustSucceed, nodeProcessAborted, - opensslCli, parseTestFlags, PIPE, platformTimeout, diff --git a/test/parallel/test-cli-node-options.js b/test/parallel/test-cli-node-options.js index 69bf136559c1a8..9e89200e9f6dfd 100644 --- a/test/parallel/test-cli-node-options.js +++ b/test/parallel/test-cli-node-options.js @@ -12,6 +12,7 @@ const { Worker } = require('worker_threads'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); +const { hasOpenSSL3 } = require('../common/crypto'); tmpdir.refresh(); const printA = path.relative(tmpdir.path, fixtures.path('printA.js')); @@ -64,7 +65,7 @@ if (common.isLinux) { if (common.hasCrypto) { expectNoWorker('--use-openssl-ca', 'B\n'); expectNoWorker('--use-bundled-ca', 'B\n'); - if (!common.hasOpenSSL3) + if (!hasOpenSSL3) expectNoWorker('--openssl-config=_ossl_cfg', 'B\n'); } diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js index d191ab7be2de20..181ea732b91281 100644 --- a/test/parallel/test-crypto-authenticated.js +++ b/test/parallel/test-crypto-authenticated.js @@ -21,13 +21,17 @@ // Flags: --no-warnings 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); const { inspect } = require('util'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); + +const isFipsEnabled = crypto.getFips(); // // Test authenticated encryption modes. @@ -53,7 +57,7 @@ for (const test of TEST_CASES) { continue; } - if (common.hasFipsCrypto && test.iv.length < 24) { + if (isFipsEnabled && test.iv.length < 24) { common.printSkipMessage('IV len < 12 bytes unsupported in FIPS mode'); continue; } @@ -95,7 +99,7 @@ for (const test of TEST_CASES) { } { - if (isCCM && common.hasFipsCrypto) { + if (isCCM && isFipsEnabled) { assert.throws(() => { crypto.createDecipheriv(test.algo, Buffer.from(test.key, 'hex'), @@ -286,7 +290,7 @@ for (const test of TEST_CASES) { }); }, errMessages.authTagLength); - if (!common.hasFipsCrypto) { + if (!isFipsEnabled) { assert.throws(() => { crypto.createDecipheriv('aes-256-ccm', 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', @@ -312,7 +316,7 @@ for (const test of TEST_CASES) { }); // CCM decryption and create(De|C)ipher are unsupported in FIPS mode. - if (!common.hasFipsCrypto) { + if (!isFipsEnabled) { assert.throws(() => { crypto.createDecipheriv(`aes-256-${mode}`, 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', @@ -388,7 +392,7 @@ for (const test of TEST_CASES) { cipher.setAAD(Buffer.from('0123456789', 'hex')); }, /options\.plaintextLength required for CCM mode with AAD/); - if (!common.hasFipsCrypto) { + if (!isFipsEnabled) { assert.throws(() => { const cipher = crypto.createDecipheriv('aes-256-ccm', 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', @@ -403,7 +407,7 @@ for (const test of TEST_CASES) { // Test that final() throws in CCM mode when no authentication tag is provided. { - if (!common.hasFipsCrypto) { + if (!isFipsEnabled) { const key = Buffer.from('1ed2233fa2223ef5d7df08546049406c', 'hex'); const iv = Buffer.from('7305220bca40d4c90e1791e9', 'hex'); const ct = Buffer.from('8beba09d4d4d861f957d51c0794f4abf8030848e', 'hex'); @@ -562,7 +566,7 @@ for (const test of TEST_CASES) { ]) { assert.throws(() => { cipher.final(); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { code: 'ERR_OSSL_TAG_NOT_SET' } : { message: /Unsupported state/ diff --git a/test/parallel/test-crypto-cipheriv-decipheriv.js b/test/parallel/test-crypto-cipheriv-decipheriv.js index 3e3632203af72c..88d07c3b957f57 100644 --- a/test/parallel/test-crypto-cipheriv-decipheriv.js +++ b/test/parallel/test-crypto-cipheriv-decipheriv.js @@ -5,6 +5,8 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); +const isFipsEnabled = crypto.getFips(); function testCipher1(key, iv) { // Test encryption and decryption with explicit key and iv @@ -150,7 +152,7 @@ testCipher1(Buffer.from('0123456789abcd0123456789'), '12345678'); testCipher1(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678')); testCipher2(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678')); -if (!common.hasFipsCrypto) { +if (!isFipsEnabled) { testCipher3(Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'), Buffer.from('A6A6A6A6A6A6A6A6', 'hex')); } @@ -193,10 +195,10 @@ assert.throws( errMessage); // But all other IV lengths should be accepted. -const minIvLength = common.hasOpenSSL3 ? 8 : 1; -const maxIvLength = common.hasOpenSSL3 ? 64 : 256; +const minIvLength = hasOpenSSL3 ? 8 : 1; +const maxIvLength = hasOpenSSL3 ? 64 : 256; for (let n = minIvLength; n < maxIvLength; n += 1) { - if (common.hasFipsCrypto && n < 12) continue; + if (isFipsEnabled && n < 12) continue; crypto.createCipheriv('aes-128-gcm', Buffer.alloc(16), Buffer.alloc(n)); } diff --git a/test/parallel/test-crypto-classes.js b/test/parallel/test-crypto-classes.js index f736921476a1c5..429bc91d4412be 100644 --- a/test/parallel/test-crypto-classes.js +++ b/test/parallel/test-crypto-classes.js @@ -4,9 +4,9 @@ const assert = require('assert'); if (!common.hasCrypto) { common.skip('missing crypto'); - return; } const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // 'ClassName' : ['args', 'for', 'constructor'] const TEST_CASES = { @@ -21,8 +21,8 @@ const TEST_CASES = { 'ECDH': ['prime256v1'], }; -if (!common.hasFipsCrypto) { - TEST_CASES.DiffieHellman = [common.hasOpenSSL3 ? 1024 : 256]; +if (!crypto.getFips()) { + TEST_CASES.DiffieHellman = [hasOpenSSL3 ? 1024 : 256]; } for (const [clazz, args] of Object.entries(TEST_CASES)) { diff --git a/test/parallel/test-crypto-dh-constructor.js b/test/parallel/test-crypto-dh-constructor.js index c7eaca29347a2b..eb8674932484ed 100644 --- a/test/parallel/test-crypto-dh-constructor.js +++ b/test/parallel/test-crypto-dh-constructor.js @@ -5,8 +5,9 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); -const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; +const size = crypto.getFips() || hasOpenSSL3 ? 1024 : 256; const dh1 = crypto.createDiffieHellman(size); const p1 = dh1.getPrime('buffer'); diff --git a/test/parallel/test-crypto-dh-errors.js b/test/parallel/test-crypto-dh-errors.js index 476ca64b4425b5..0af4db0310750c 100644 --- a/test/parallel/test-crypto-dh-errors.js +++ b/test/parallel/test-crypto-dh-errors.js @@ -5,6 +5,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // https://github.com/nodejs/node/issues/32738 // XXX(bnoordhuis) validateInt32() throwing ERR_OUT_OF_RANGE and RangeError @@ -24,7 +25,7 @@ assert.throws(() => crypto.createDiffieHellman('abcdef', 13.37), { }); for (const bits of [-1, 0, 1]) { - if (common.hasOpenSSL3) { + if (hasOpenSSL3) { assert.throws(() => crypto.createDiffieHellman(bits), { code: 'ERR_OSSL_DH_MODULUS_TOO_SMALL', name: 'Error', diff --git a/test/parallel/test-crypto-dh-generate-keys.js b/test/parallel/test-crypto-dh-generate-keys.js index fc277bb0d9b8e4..e4598274328bd8 100644 --- a/test/parallel/test-crypto-dh-generate-keys.js +++ b/test/parallel/test-crypto-dh-generate-keys.js @@ -6,9 +6,10 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); { - const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + const size = crypto.getFips() || hasOpenSSL3 ? 1024 : 256; function unlessInvalidState(f) { try { diff --git a/test/parallel/test-crypto-dh-leak.js b/test/parallel/test-crypto-dh-leak.js index 1998d61d4affd7..3b5051feb43cd8 100644 --- a/test/parallel/test-crypto-dh-leak.js +++ b/test/parallel/test-crypto-dh-leak.js @@ -9,10 +9,11 @@ if (common.isASan) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); const before = process.memoryUsage.rss(); { - const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + const size = crypto.getFips() || hasOpenSSL3 ? 1024 : 256; const dh = crypto.createDiffieHellman(size); const publicKey = dh.generateKeys(); const privateKey = dh.getPrivateKey(); diff --git a/test/parallel/test-crypto-dh-odd-key.js b/test/parallel/test-crypto-dh-odd-key.js index 69a1eb56c866b3..fbe42be425ed1c 100644 --- a/test/parallel/test-crypto-dh-odd-key.js +++ b/test/parallel/test-crypto-dh-odd-key.js @@ -21,22 +21,24 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); function test() { const odd = Buffer.alloc(39, 'A'); - const c = crypto.createDiffieHellman(common.hasOpenSSL3 ? 1024 : 32); + const c = crypto.createDiffieHellman(hasOpenSSL3 ? 1024 : 32); c.setPrivateKey(odd); c.generateKeys(); } // FIPS requires a length of at least 1024 -if (!common.hasFipsCrypto) { +if (!crypto.getFips()) { test(); } else { assert.throws(function() { test(); }, /key size too small/); diff --git a/test/parallel/test-crypto-dh-stateless.js b/test/parallel/test-crypto-dh-stateless.js index 2ccac322e23958..f4fc1849699e31 100644 --- a/test/parallel/test-crypto-dh-stateless.js +++ b/test/parallel/test-crypto-dh-stateless.js @@ -5,6 +5,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); assert.throws(() => crypto.diffieHellman(), { name: 'TypeError', @@ -150,7 +151,7 @@ const list = [ // TODO(danbev): Take a closer look if there should be a check in OpenSSL3 // when the dh parameters differ. -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { // Same primes, but different generator. list.push([{ group: 'modp5' }, { prime: group.getPrime(), generator: 5 }]); // Same generator, but different primes. @@ -161,7 +162,7 @@ for (const [params1, params2] of list) { assert.throws(() => { test(crypto.generateKeyPairSync('dh', params1), crypto.generateKeyPairSync('dh', params2)); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { name: 'Error', code: 'ERR_OSSL_MISMATCHING_DOMAIN_PARAMETERS' } : { @@ -220,7 +221,7 @@ const not256k1 = crypto.getCurves().find((c) => /^sec.*(224|384|512)/.test(c)); assert.throws(() => { test(crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' }), crypto.generateKeyPairSync('ec', { namedCurve: not256k1 })); -}, common.hasOpenSSL3 ? { +}, hasOpenSSL3 ? { name: 'Error', code: 'ERR_OSSL_MISMATCHING_DOMAIN_PARAMETERS' } : { diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index 9ebe14011eed22..d7ffbe5eca9273 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -1,13 +1,18 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); +const { + hasOpenSSL3, + hasOpenSSL, +} = require('../common/crypto'); { - const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + const size = crypto.getFips() || hasOpenSSL3 ? 1024 : 256; const dh1 = crypto.createDiffieHellman(size); const p1 = dh1.getPrime('buffer'); const dh2 = crypto.createDiffieHellman(p1, 'buffer'); @@ -53,7 +58,7 @@ const crypto = require('crypto'); assert.strictEqual(secret1, secret4); let wrongBlockLength; - if (common.hasOpenSSL3) { + if (hasOpenSSL3) { wrongBlockLength = { message: 'error:1C80006B:Provider routines::wrong final block length', code: 'ERR_OSSL_WRONG_FINAL_BLOCK_LENGTH', @@ -87,11 +92,11 @@ const crypto = require('crypto'); { // Error message was changed in OpenSSL 3.0.x from 3.0.12, and 3.1.x from 3.1.4. - const hasOpenSSL3WithNewErrorMessage = (common.hasOpenSSL(3, 0, 12) && !common.hasOpenSSL(3, 1, 0)) || - (common.hasOpenSSL(3, 1, 4)); + const hasOpenSSL3WithNewErrorMessage = (hasOpenSSL(3, 0, 12) && !hasOpenSSL(3, 1, 0)) || + (hasOpenSSL(3, 1, 4)); assert.throws(() => { dh3.computeSecret(''); - }, { message: common.hasOpenSSL3 && !hasOpenSSL3WithNewErrorMessage ? + }, { message: hasOpenSSL3 && !hasOpenSSL3WithNewErrorMessage ? 'Unspecified validation error' : 'Supplied key is too small' }); } diff --git a/test/parallel/test-crypto-ecb.js b/test/parallel/test-crypto-ecb.js index aecd858ef3bf1e..6439c9354a059e 100644 --- a/test/parallel/test-crypto-ecb.js +++ b/test/parallel/test-crypto-ecb.js @@ -21,18 +21,23 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { hasOpenSSL3 } = require('../common/crypto'); +const crypto = require('crypto'); -if (common.hasFipsCrypto) +if (crypto.getFips()) { common.skip('BF-ECB is not FIPS 140-2 compatible'); +} -if (common.hasOpenSSL3) +if (hasOpenSSL3) { common.skip('Blowfish is only available with the legacy provider in ' + 'OpenSSl 3.x'); +} const assert = require('assert'); -const crypto = require('crypto'); // Testing whether EVP_CipherInit_ex is functioning correctly. // Reference: bug#1997 diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js index 8a8a8089a3cf3b..de004b9a9e8f23 100644 --- a/test/parallel/test-crypto-fips.js +++ b/test/parallel/test-crypto-fips.js @@ -10,6 +10,7 @@ const path = require('path'); const fixtures = require('../common/fixtures'); const { internalBinding } = require('internal/test/binding'); const { testFipsCrypto } = internalBinding('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); const FIPS_ENABLED = 1; const FIPS_DISABLED = 0; @@ -114,7 +115,7 @@ assert.ok(test_result === 1 || test_result === 0); // ("Error: Cannot set FIPS mode in a non-FIPS build."). // Due to this uncertainty the following tests are skipped when configured // with --shared-openssl. -if (!sharedOpenSSL() && !common.hasOpenSSL3) { +if (!sharedOpenSSL() && !hasOpenSSL3) { // OpenSSL config file should be able to turn on FIPS mode testHelper( 'stdout', @@ -144,7 +145,7 @@ if (!sharedOpenSSL() && !common.hasOpenSSL3) { // will not work as expected with that version. // TODO(danbev) Revisit these test once FIPS support is available in // OpenSSL 3.x. -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { testHelper( 'stdout', [`--openssl-config=${CNF_FIPS_OFF}`], diff --git a/test/parallel/test-crypto-hash.js b/test/parallel/test-crypto-hash.js index ca8f630b4bb7e7..61145aee0727fb 100644 --- a/test/parallel/test-crypto-hash.js +++ b/test/parallel/test-crypto-hash.js @@ -1,13 +1,14 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); const fs = require('fs'); -const { hasOpenSSL } = common; +const { hasOpenSSL } = require('../common/crypto'); const fixtures = require('../common/fixtures'); let cryptoType; @@ -40,7 +41,7 @@ a8.write(''); a8.end(); a8 = a8.read(); -if (!common.hasFipsCrypto) { +if (!crypto.getFips()) { cryptoType = 'md5'; digest = 'latin1'; const a0 = crypto.createHash(cryptoType).update('Test123').digest(digest); diff --git a/test/parallel/test-crypto-hkdf.js b/test/parallel/test-crypto-hkdf.js index ff3abdf291efcd..3f7e61e9b2ebc0 100644 --- a/test/parallel/test-crypto-hkdf.js +++ b/test/parallel/test-crypto-hkdf.js @@ -13,6 +13,7 @@ const { hkdfSync, getHashes } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); { assert.throws(() => hkdf(), { @@ -124,7 +125,7 @@ const algorithms = [ ['sha256', '', 'salt', '', 10], ['sha512', 'secret', 'salt', '', 15], ]; -if (!common.hasOpenSSL3) +if (!hasOpenSSL3) algorithms.push(['whirlpool', 'secret', '', 'info', 20]); algorithms.forEach(([ hash, secret, salt, info, length ]) => { @@ -215,7 +216,7 @@ algorithms.forEach(([ hash, secret, salt, info, length ]) => { }); -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { const kKnownUnsupported = ['shake128', 'shake256']; getHashes() .filter((hash) => !kKnownUnsupported.includes(hash)) diff --git a/test/parallel/test-crypto-hmac.js b/test/parallel/test-crypto-hmac.js index 62a6ac18d25265..afb5f74cbf076b 100644 --- a/test/parallel/test-crypto-hmac.js +++ b/test/parallel/test-crypto-hmac.js @@ -1,7 +1,8 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); @@ -40,7 +41,7 @@ assert.throws( function testHmac(algo, key, data, expected) { // FIPS does not support MD5. - if (common.hasFipsCrypto && algo === 'md5') + if (crypto.getFips() && algo === 'md5') return; if (!Array.isArray(data)) diff --git a/test/parallel/test-crypto-key-objects.js b/test/parallel/test-crypto-key-objects.js index f5271f16d346c0..0c516d80950694 100644 --- a/test/parallel/test-crypto-key-objects.js +++ b/test/parallel/test-crypto-key-objects.js @@ -24,6 +24,8 @@ const { generateKeyPairSync, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + const fixtures = require('../common/fixtures'); const publicPem = fixtures.readKey('rsa_public.pem', 'ascii'); @@ -297,7 +299,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', // This should not cause a crash: https://github.com/nodejs/node/issues/25247 assert.throws(() => { createPrivateKey({ key: '' }); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { message: 'error:1E08010C:DECODER routines::unsupported', } : { message: 'error:0909006C:PEM routines:get_name:no start line', @@ -323,7 +325,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', type: 'pkcs1' }); createPrivateKey({ key, format: 'der', type: 'pkcs1' }); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { message: /error:1E08010C:DECODER routines::unsupported/, library: 'DECODER routines' } : { @@ -510,7 +512,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', { // Reading an encrypted key without a passphrase should fail. - assert.throws(() => createPrivateKey(privateDsa), common.hasOpenSSL3 ? { + assert.throws(() => createPrivateKey(privateDsa), hasOpenSSL3 ? { name: 'Error', message: 'error:07880109:common libcrypto routines::interrupted or ' + 'cancelled', @@ -526,7 +528,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', key: privateDsa, format: 'pem', passphrase: Buffer.alloc(1025, 'a') - }), common.hasOpenSSL3 ? { name: 'Error' } : { + }), hasOpenSSL3 ? { name: 'Error' } : { code: 'ERR_OSSL_PEM_BAD_PASSWORD_READ', name: 'Error' }); diff --git a/test/parallel/test-crypto-keygen-async-dsa-key-object.js b/test/parallel/test-crypto-keygen-async-dsa-key-object.js index c15807295541e2..a3df136230d0f8 100644 --- a/test/parallel/test-crypto-keygen-async-dsa-key-object.js +++ b/test/parallel/test-crypto-keygen-async-dsa-key-object.js @@ -9,23 +9,25 @@ const { generateKeyPair, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + // Test async DSA key object generation. { generateKeyPair('dsa', { - modulusLength: common.hasOpenSSL3 ? 2048 : 512, + modulusLength: hasOpenSSL3 ? 2048 : 512, divisorLength: 256 }, common.mustSucceed((publicKey, privateKey) => { assert.strictEqual(publicKey.type, 'public'); assert.strictEqual(publicKey.asymmetricKeyType, 'dsa'); assert.deepStrictEqual(publicKey.asymmetricKeyDetails, { - modulusLength: common.hasOpenSSL3 ? 2048 : 512, + modulusLength: hasOpenSSL3 ? 2048 : 512, divisorLength: 256 }); assert.strictEqual(privateKey.type, 'private'); assert.strictEqual(privateKey.asymmetricKeyType, 'dsa'); assert.deepStrictEqual(privateKey.asymmetricKeyDetails, { - modulusLength: common.hasOpenSSL3 ? 2048 : 512, + modulusLength: hasOpenSSL3 ? 2048 : 512, divisorLength: 256 }); })); diff --git a/test/parallel/test-crypto-keygen-async-dsa.js b/test/parallel/test-crypto-keygen-async-dsa.js index 048c0ce6fb92ef..41968d8cc23365 100644 --- a/test/parallel/test-crypto-keygen-async-dsa.js +++ b/test/parallel/test-crypto-keygen-async-dsa.js @@ -14,6 +14,8 @@ const { spkiExp, } = require('../common/crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + // Test async DSA key generation. { const privateKeyEncoding = { @@ -22,7 +24,7 @@ const { }; generateKeyPair('dsa', { - modulusLength: common.hasOpenSSL3 ? 2048 : 512, + modulusLength: hasOpenSSL3 ? 2048 : 512, divisorLength: 256, publicKeyEncoding: { type: 'spki', @@ -39,8 +41,8 @@ const { // The private key is DER-encoded. assert(Buffer.isBuffer(privateKeyDER)); - assertApproximateSize(publicKey, common.hasOpenSSL3 ? 1194 : 440); - assertApproximateSize(privateKeyDER, common.hasOpenSSL3 ? 721 : 336); + assertApproximateSize(publicKey, hasOpenSSL3 ? 1194 : 440); + assertApproximateSize(privateKeyDER, hasOpenSSL3 ? 721 : 336); // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => { diff --git a/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted-p256.js b/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted-p256.js index 553674774571d3..55aa3831c4233b 100644 --- a/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted-p256.js +++ b/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted-p256.js @@ -14,6 +14,8 @@ const { pkcs8EncExp, } = require('../common/crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + // Test async elliptic curve key generation, e.g. for ECDSA, with an encrypted // private key with paramEncoding explicit. { @@ -38,7 +40,7 @@ const { // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => testSignVerify(publicKey, privateKey), - common.hasOpenSSL3 ? { + hasOpenSSL3 ? { message: 'error:07880109:common libcrypto ' + 'routines::interrupted or cancelled' } : { diff --git a/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted.js.js b/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted.js.js index 79a132eed0b854..8a55d4338bc72f 100644 --- a/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted.js.js +++ b/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted.js.js @@ -12,6 +12,7 @@ const { testSignVerify, spkiExp, sec1EncExp, + hasOpenSSL3, } = require('../common/crypto'); { @@ -38,7 +39,7 @@ const { // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => testSignVerify(publicKey, privateKey), - common.hasOpenSSL3 ? { + hasOpenSSL3 ? { message: 'error:07880109:common libcrypto ' + 'routines::interrupted or cancelled' } : { diff --git a/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted-p256.js b/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted-p256.js index 5e7d1a6c9b6611..4c11401d0fc516 100644 --- a/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted-p256.js +++ b/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted-p256.js @@ -12,6 +12,7 @@ const { testSignVerify, spkiExp, pkcs8EncExp, + hasOpenSSL3, } = require('../common/crypto'); // Test async elliptic curve key generation, e.g. for ECDSA, with an encrypted @@ -38,7 +39,7 @@ const { // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => testSignVerify(publicKey, privateKey), - common.hasOpenSSL3 ? { + hasOpenSSL3 ? { message: 'error:07880109:common libcrypto ' + 'routines::interrupted or cancelled' } : { diff --git a/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted.js b/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted.js index 1cc93d0a794931..0503ff74787f37 100644 --- a/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted.js +++ b/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted.js @@ -12,6 +12,7 @@ const { testSignVerify, spkiExp, sec1EncExp, + hasOpenSSL3, } = require('../common/crypto'); { @@ -38,7 +39,7 @@ const { // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => testSignVerify(publicKey, privateKey), - common.hasOpenSSL3 ? { + hasOpenSSL3 ? { message: 'error:07880109:common libcrypto ' + 'routines::interrupted or cancelled' } : { diff --git a/test/parallel/test-crypto-keygen-async-rsa.js b/test/parallel/test-crypto-keygen-async-rsa.js index f4a83809dc73c7..c80d7d33492923 100644 --- a/test/parallel/test-crypto-keygen-async-rsa.js +++ b/test/parallel/test-crypto-keygen-async-rsa.js @@ -13,6 +13,7 @@ const { testEncryptDecrypt, testSignVerify, pkcs1EncExp, + hasOpenSSL3, } = require('../common/crypto'); // Test async RSA key generation with an encrypted private key. @@ -43,7 +44,7 @@ const { type: 'pkcs1', format: 'der', }; - const expectedError = common.hasOpenSSL3 ? { + const expectedError = hasOpenSSL3 ? { name: 'Error', message: 'error:07880109:common libcrypto routines::interrupted or ' + 'cancelled' diff --git a/test/parallel/test-crypto-keygen-bit-length.js b/test/parallel/test-crypto-keygen-bit-length.js index 08772ba2e496b8..63a80659bb2f53 100644 --- a/test/parallel/test-crypto-keygen-bit-length.js +++ b/test/parallel/test-crypto-keygen-bit-length.js @@ -8,6 +8,7 @@ const assert = require('assert'); const { generateKeyPair, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // This tests check that generateKeyPair returns correct bit length in // KeyObject's asymmetricKeyDetails. @@ -27,7 +28,7 @@ const { assert.strictEqual(publicKey.asymmetricKeyDetails.modulusLength, 513); })); - if (common.hasOpenSSL3) { + if (hasOpenSSL3) { generateKeyPair('dsa', { modulusLength: 2049, divisorLength: 256, diff --git a/test/parallel/test-crypto-keygen-empty-passphrase-no-prompt.js b/test/parallel/test-crypto-keygen-empty-passphrase-no-prompt.js index 7679a492c3194c..cb873ff04748b7 100644 --- a/test/parallel/test-crypto-keygen-empty-passphrase-no-prompt.js +++ b/test/parallel/test-crypto-keygen-empty-passphrase-no-prompt.js @@ -11,6 +11,7 @@ const { } = require('crypto'); const { testSignVerify, + hasOpenSSL3, } = require('../common/crypto'); // Passing an empty passphrase string should not cause OpenSSL's default @@ -40,7 +41,7 @@ for (const type of ['pkcs1', 'pkcs8']) { // the key, and not specifying a passphrase should fail when decoding it. assert.throws(() => { return testSignVerify(publicKey, privateKey); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { name: 'Error', code: 'ERR_OSSL_CRYPTO_INTERRUPTED_OR_CANCELLED', message: 'error:07880109:common libcrypto routines::interrupted or cancelled' diff --git a/test/parallel/test-crypto-keygen-missing-oid.js b/test/parallel/test-crypto-keygen-missing-oid.js index f7fefe13848d4b..1e4f309292eb47 100644 --- a/test/parallel/test-crypto-keygen-missing-oid.js +++ b/test/parallel/test-crypto-keygen-missing-oid.js @@ -11,6 +11,8 @@ const { getCurves, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + // This test creates EC key pairs on curves without associated OIDs. // Specifying a key encoding should not crash. { @@ -20,7 +22,7 @@ const { continue; const expectedErrorCode = - common.hasOpenSSL3 ? 'ERR_OSSL_MISSING_OID' : 'ERR_OSSL_EC_MISSING_OID'; + hasOpenSSL3 ? 'ERR_OSSL_MISSING_OID' : 'ERR_OSSL_EC_MISSING_OID'; const params = { namedCurve, publicKeyEncoding: { diff --git a/test/parallel/test-crypto-keygen.js b/test/parallel/test-crypto-keygen.js index b09ca9e7c531ea..edaee845075668 100644 --- a/test/parallel/test-crypto-keygen.js +++ b/test/parallel/test-crypto-keygen.js @@ -14,6 +14,7 @@ const { } = require('crypto'); const { inspect } = require('util'); +const { hasOpenSSL3 } = require('../common/crypto'); // Test invalid parameter encoding. { @@ -351,7 +352,7 @@ const { inspect } = require('util'); publicExponent }, common.mustCall((err) => { assert.strictEqual(err.name, 'Error'); - assert.match(err.message, common.hasOpenSSL3 ? /exponent/ : /bad e value/); + assert.match(err.message, hasOpenSSL3 ? /exponent/ : /bad e value/); })); } } diff --git a/test/parallel/test-crypto-no-algorithm.js b/test/parallel/test-crypto-no-algorithm.js index 37db38cf613b65..06124e3d465e41 100644 --- a/test/parallel/test-crypto-no-algorithm.js +++ b/test/parallel/test-crypto-no-algorithm.js @@ -4,7 +4,9 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (!common.hasOpenSSL3) +const { hasOpenSSL3 } = require('../common/crypto'); + +if (!hasOpenSSL3) common.skip('this test requires OpenSSL 3.x'); const assert = require('node:assert/strict'); diff --git a/test/parallel/test-crypto-oneshot-hash.js b/test/parallel/test-crypto-oneshot-hash.js index 69051c43d9e882..861aded5dd3f60 100644 --- a/test/parallel/test-crypto-oneshot-hash.js +++ b/test/parallel/test-crypto-oneshot-hash.js @@ -8,6 +8,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL } = require('../common/crypto'); const fs = require('fs'); // Test errors for invalid arguments. @@ -32,7 +33,7 @@ const input = fs.readFileSync(fixtures.path('utf8_test_text.txt')); for (const method of methods) { // Skip failing tests on OpenSSL 3.4.0 - if (method.startsWith('shake') && common.hasOpenSSL(3, 4)) + if (method.startsWith('shake') && hasOpenSSL(3, 4)) continue; for (const outputEncoding of ['buffer', 'hex', 'base64', undefined]) { const oldDigest = crypto.createHash(method).update(input).digest(outputEncoding || 'hex'); diff --git a/test/parallel/test-crypto-padding.js b/test/parallel/test-crypto-padding.js index f1f14b472997e7..48cd1ed4df61aa 100644 --- a/test/parallel/test-crypto-padding.js +++ b/test/parallel/test-crypto-padding.js @@ -26,6 +26,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // Input data. const ODD_LENGTH_PLAIN = 'Hello node world!'; @@ -82,7 +83,7 @@ assert.strictEqual(enc(EVEN_LENGTH_PLAIN, true), EVEN_LENGTH_ENCRYPTED); assert.throws(function() { // Input must have block length %. enc(ODD_LENGTH_PLAIN, false); -}, common.hasOpenSSL3 ? { +}, hasOpenSSL3 ? { message: 'error:1C80006B:Provider routines::wrong final block length', code: 'ERR_OSSL_WRONG_FINAL_BLOCK_LENGTH', reason: 'wrong final block length', @@ -109,7 +110,7 @@ assert.strictEqual(dec(EVEN_LENGTH_ENCRYPTED, false).length, 48); assert.throws(function() { // Must have at least 1 byte of padding (PKCS): assert.strictEqual(dec(EVEN_LENGTH_ENCRYPTED_NOPAD, true), EVEN_LENGTH_PLAIN); -}, common.hasOpenSSL3 ? { +}, hasOpenSSL3 ? { message: 'error:1C800064:Provider routines::bad decrypt', reason: 'bad decrypt', code: 'ERR_OSSL_BAD_DECRYPT', diff --git a/test/parallel/test-crypto-pbkdf2.js b/test/parallel/test-crypto-pbkdf2.js index 1f8e6a81f300e7..efd8d6eaf0d640 100644 --- a/test/parallel/test-crypto-pbkdf2.js +++ b/test/parallel/test-crypto-pbkdf2.js @@ -5,6 +5,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); function runPBKDF2(password, salt, iterations, keylen, hash) { const syncResult = @@ -219,7 +220,7 @@ assert.throws( } ); -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { const kNotPBKDF2Supported = ['shake128', 'shake256']; crypto.getHashes() .filter((hash) => !kNotPBKDF2Supported.includes(hash)) diff --git a/test/parallel/test-crypto-private-decrypt-gh32240.js b/test/parallel/test-crypto-private-decrypt-gh32240.js index e88227a215ba4f..1ff5b565d6d5f4 100644 --- a/test/parallel/test-crypto-private-decrypt-gh32240.js +++ b/test/parallel/test-crypto-private-decrypt-gh32240.js @@ -14,6 +14,8 @@ const { privateDecrypt, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + const pair = generateKeyPairSync('rsa', { modulusLength: 512 }); const expected = Buffer.from('shibboleth'); @@ -34,7 +36,7 @@ function decrypt(key) { } decrypt(pkey); -assert.throws(() => decrypt(pkeyEncrypted), common.hasOpenSSL3 ? +assert.throws(() => decrypt(pkeyEncrypted), hasOpenSSL3 ? { message: 'error:07880109:common libcrypto routines::interrupted or ' + 'cancelled' } : { code: 'ERR_MISSING_PASSPHRASE' }); diff --git a/test/parallel/test-crypto-publicDecrypt-fails-first-time.js b/test/parallel/test-crypto-publicDecrypt-fails-first-time.js index a60b87dbf65229..1d64e08920c63b 100644 --- a/test/parallel/test-crypto-publicDecrypt-fails-first-time.js +++ b/test/parallel/test-crypto-publicDecrypt-fails-first-time.js @@ -3,11 +3,15 @@ const common = require('../common'); // Test for https://github.com/nodejs/node/issues/40814 -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (!common.hasOpenSSL3) +const { hasOpenSSL3 } = require('../common/crypto'); + +if (!hasOpenSSL3) { common.skip('only openssl3'); // https://github.com/nodejs/node/pull/42793#issuecomment-1107491901 +} const assert = require('assert'); const crypto = require('crypto'); diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js index 5f4fafdfffbf72..dcd5045daaf58c 100644 --- a/test/parallel/test-crypto-rsa-dsa.js +++ b/test/parallel/test-crypto-rsa-dsa.js @@ -9,6 +9,7 @@ const crypto = require('crypto'); const constants = crypto.constants; const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); // Test certificates const certPem = fixtures.readKey('rsa_cert.crt'); @@ -36,11 +37,11 @@ const openssl1DecryptError = { library: 'digital envelope routines', }; -const decryptError = common.hasOpenSSL3 ? +const decryptError = hasOpenSSL3 ? { message: 'error:1C800064:Provider routines::bad decrypt' } : openssl1DecryptError; -const decryptPrivateKeyError = common.hasOpenSSL3 ? { +const decryptPrivateKeyError = hasOpenSSL3 ? { message: 'error:1C800064:Provider routines::bad decrypt', } : openssl1DecryptError; @@ -146,7 +147,7 @@ function getBufferCopy(buf) { // Now with RSA_NO_PADDING. Plaintext needs to match key size. // OpenSSL 3.x has a rsa_check_padding that will cause an error if // RSA_NO_PADDING is used. - if (!common.hasOpenSSL3) { + if (!hasOpenSSL3) { { const plaintext = 'x'.repeat(rsaKeySize / 8); encryptedBuffer = crypto.privateEncrypt({ diff --git a/test/parallel/test-crypto-secure-heap.js b/test/parallel/test-crypto-secure-heap.js index 0e5788f00e4a44..c20b01a91a9840 100644 --- a/test/parallel/test-crypto-secure-heap.js +++ b/test/parallel/test-crypto-secure-heap.js @@ -1,21 +1,26 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (common.isWindows) +if (common.isWindows) { common.skip('Not supported on Windows'); +} -if (common.isASan) +if (common.isASan) { common.skip('ASan does not play well with secure heap allocations'); +} const assert = require('assert'); const { fork } = require('child_process'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); const { secureHeapUsed, createDiffieHellman, + getFips, } = require('crypto'); if (process.argv[2] === 'child') { @@ -29,7 +34,7 @@ if (process.argv[2] === 'child') { assert.strictEqual(a.used, 0); { - const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + const size = getFips() || hasOpenSSL3 ? 1024 : 256; const dh1 = createDiffieHellman(size); const p1 = dh1.getPrime('buffer'); const dh2 = createDiffieHellman(p1, 'buffer'); diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index 8a263ec3350f55..0589d60736e377 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -8,6 +8,10 @@ const fs = require('fs'); const exec = require('child_process').exec; const crypto = require('crypto'); const fixtures = require('../common/fixtures'); +const { + hasOpenSSL3, + opensslCli, +} = require('../common/crypto'); // Test certificates const certPem = fixtures.readKey('rsa_cert.crt'); @@ -62,7 +66,7 @@ const keySize = 2048; key: keyPem, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }); - }, { message: common.hasOpenSSL3 ? + }, { message: hasOpenSSL3 ? 'error:1C8000A5:Provider routines::illegal or unsupported padding mode' : 'bye, bye, error stack' }); @@ -340,7 +344,7 @@ assert.throws( key: keyPem, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { code: 'ERR_OSSL_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE', message: /illegal or unsupported padding mode/, } : { @@ -599,8 +603,9 @@ assert.throws( // Note: this particular test *must* be the last in this file as it will exit // early if no openssl binary is found { - if (!common.opensslCli) + if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); + } const pubfile = fixtures.path('keys', 'rsa_public_2048.pem'); const privkey = fixtures.readKey('rsa_private_2048.pem'); @@ -622,7 +627,7 @@ assert.throws( fs.writeFileSync(msgfile, msg); exec(...common.escapePOSIXShell`"${ - common.opensslCli}" dgst -sha256 -verify "${pubfile}" -signature "${ + opensslCli}" dgst -sha256 -verify "${pubfile}" -signature "${ sigfile}" -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-2 "${msgfile }"`, common.mustCall((err, stdout, stderr) => { assert(stdout.includes('Verified OK')); diff --git a/test/parallel/test-crypto-stream.js b/test/parallel/test-crypto-stream.js index 008ab129f0e019..62be4eaf6edfb0 100644 --- a/test/parallel/test-crypto-stream.js +++ b/test/parallel/test-crypto-stream.js @@ -21,14 +21,16 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const stream = require('stream'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); -if (!common.hasFipsCrypto) { +if (!crypto.getFips()) { // Small stream to buffer converter class Stream2buffer extends stream.Writable { constructor(callback) { @@ -71,7 +73,7 @@ const cipher = crypto.createCipheriv('aes-128-cbc', key, iv); const decipher = crypto.createDecipheriv('aes-128-cbc', badkey, iv); cipher.pipe(decipher) - .on('error', common.expectsError(common.hasOpenSSL3 ? { + .on('error', common.expectsError(hasOpenSSL3 ? { message: /bad decrypt/, library: 'Provider routines', reason: 'bad decrypt', diff --git a/test/parallel/test-crypto-x509.js b/test/parallel/test-crypto-x509.js index bd906c25b9ee19..f75e1d63470bfb 100644 --- a/test/parallel/test-crypto-x509.js +++ b/test/parallel/test-crypto-x509.js @@ -18,6 +18,7 @@ const { const assert = require('assert'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); const { readFileSync } = require('fs'); const cert = readFileSync(fixtures.path('keys', 'agent1-cert.pem')); @@ -50,7 +51,7 @@ emailAddress=ry@tinyclouds.org`; let infoAccessCheck = `OCSP - URI:http://ocsp.nodejs.org/ CA Issuers - URI:http://ca.nodejs.org/ca.cert`; -if (!common.hasOpenSSL3) +if (!hasOpenSSL3) infoAccessCheck += '\n'; const der = Buffer.from( @@ -357,7 +358,7 @@ UcXd/5qu2GhokrKU2cPttU+XAN2Om6a0 const cert = new X509Certificate(certPem); assert.throws(() => cert.publicKey, { - message: common.hasOpenSSL3 ? /decode error/ : /wrong tag/, + message: hasOpenSSL3 ? /decode error/ : /wrong tag/, name: 'Error' }); diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js index 4271121881379b..93644e016de447 100644 --- a/test/parallel/test-crypto.js +++ b/test/parallel/test-crypto.js @@ -29,6 +29,7 @@ const assert = require('assert'); const crypto = require('crypto'); const tls = require('tls'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); // Test Certificates const certPfx = fixtures.readKey('rsa_cert.pfx'); @@ -208,9 +209,9 @@ assert.throws(() => { ].join('\n'); crypto.createSign('SHA256').update('test').sign(priv); }, (err) => { - if (!common.hasOpenSSL3) + if (!hasOpenSSL3) assert.ok(!('opensslErrorStack' in err)); - assert.throws(() => { throw err; }, common.hasOpenSSL3 ? { + assert.throws(() => { throw err; }, hasOpenSSL3 ? { name: 'Error', message: 'error:02000070:rsa routines::digest too big for rsa key', library: 'rsa routines', @@ -225,7 +226,7 @@ assert.throws(() => { return true; }); -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { assert.throws(() => { // The correct header inside `rsa_private_pkcs8_bad.pem` should have been // -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- diff --git a/test/parallel/test-dsa-fips-invalid-key.js b/test/parallel/test-dsa-fips-invalid-key.js index 05cc1d143aca6e..3df51bfbed3517 100644 --- a/test/parallel/test-dsa-fips-invalid-key.js +++ b/test/parallel/test-dsa-fips-invalid-key.js @@ -1,12 +1,18 @@ 'use strict'; const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('no crypto'); +} + const fixtures = require('../common/fixtures'); +const crypto = require('crypto'); -if (!common.hasFipsCrypto) +if (!crypto.getFips()) { common.skip('node compiled without FIPS OpenSSL.'); +} const assert = require('assert'); -const crypto = require('crypto'); const input = 'hello'; diff --git a/test/parallel/test-https-agent-session-eviction.js b/test/parallel/test-https-agent-session-eviction.js index e0986e53c1103b..6f88e81e9ff29d 100644 --- a/test/parallel/test-https-agent-session-eviction.js +++ b/test/parallel/test-https-agent-session-eviction.js @@ -2,10 +2,13 @@ 'use strict'; const common = require('../common'); -const { readKey } = require('../common/fixtures'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { readKey } = require('../common/fixtures'); +const { hasOpenSSL } = require('../common/crypto'); const https = require('https'); const { SSL_OP_NO_TICKET } = require('crypto').constants; @@ -56,7 +59,7 @@ function faultyServer(port) { function second(server, session) { const req = https.request({ port: server.address().port, - ciphers: (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + ciphers: (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), rejectUnauthorized: false }, function(res) { res.resume(); diff --git a/test/parallel/test-https-client-renegotiation-limit.js b/test/parallel/test-https-client-renegotiation-limit.js index 35fcc6bfcc6e43..18a602d738c316 100644 --- a/test/parallel/test-https-client-renegotiation-limit.js +++ b/test/parallel/test-https-client-renegotiation-limit.js @@ -21,11 +21,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const tls = require('tls'); diff --git a/test/parallel/test-https-foafssl.js b/test/parallel/test-https-foafssl.js index d6dde97a41da9c..df375e7d22201e 100644 --- a/test/parallel/test-https-foafssl.js +++ b/test/parallel/test-https-foafssl.js @@ -21,11 +21,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (!common.opensslCli) +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const fixtures = require('../common/fixtures'); @@ -67,7 +71,7 @@ server.listen(0, function() { '-cert', fixtures.path('keys/rsa_cert_foafssl_b.crt'), '-key', fixtures.path('keys/rsa_private_b.pem')]; - const client = spawn(common.opensslCli, args); + const client = spawn(opensslCli, args); client.stdout.on('data', function(data) { console.log('response received'); diff --git a/test/parallel/test-process-env-allowed-flags-are-documented.js b/test/parallel/test-process-env-allowed-flags-are-documented.js index 2a40a821314ff3..070a88bca8c12c 100644 --- a/test/parallel/test-process-env-allowed-flags-are-documented.js +++ b/test/parallel/test-process-env-allowed-flags-are-documented.js @@ -5,6 +5,7 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); +const { hasOpenSSL3 } = require('../common/crypto'); const rootDir = path.resolve(__dirname, '..', '..'); const cliMd = path.join(rootDir, 'doc', 'api', 'cli.md'); @@ -43,7 +44,7 @@ for (const line of [...nodeOptionsLines, ...v8OptionsLines]) { } } -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { documented.delete('--openssl-legacy-provider'); documented.delete('--openssl-shared-config'); } @@ -55,8 +56,8 @@ const conditionalOpts = [ filter: (opt) => { return [ '--openssl-config', - common.hasOpenSSL3 ? '--openssl-legacy-provider' : '', - common.hasOpenSSL3 ? '--openssl-shared-config' : '', + hasOpenSSL3 ? '--openssl-legacy-provider' : '', + hasOpenSSL3 ? '--openssl-shared-config' : '', '--tls-cipher-list', '--use-bundled-ca', '--use-openssl-ca', diff --git a/test/parallel/test-process-versions.js b/test/parallel/test-process-versions.js index 3b8af4b5b52526..0a2a4014f18d6b 100644 --- a/test/parallel/test-process-versions.js +++ b/test/parallel/test-process-versions.js @@ -85,11 +85,12 @@ assert.match(process.versions.modules, /^\d+$/); assert.match(process.versions.cjs_module_lexer, commonTemplate); if (common.hasCrypto) { + const { hasOpenSSL3 } = require('../common/crypto'); assert.match(process.versions.ncrypto, commonTemplate); if (process.config.variables.node_shared_openssl) { assert.ok(process.versions.openssl); } else { - const versionRegex = common.hasOpenSSL3 ? + const versionRegex = hasOpenSSL3 ? // The following also matches a development version of OpenSSL 3.x which // can be in the format '3.0.0-alpha4-dev'. This can be handy when // building and linking against the main development branch of OpenSSL. diff --git a/test/parallel/test-tls-alert-handling.js b/test/parallel/test-tls-alert-handling.js index b14438bc92d7e6..eec072796063dc 100644 --- a/test/parallel/test-tls-alert-handling.js +++ b/test/parallel/test-tls-alert-handling.js @@ -1,11 +1,19 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { + hasOpenSSL, + hasOpenSSL3, + opensslCli, +} = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI'); +} const assert = require('assert'); const net = require('net'); @@ -33,14 +41,14 @@ let iter = 0; const errorHandler = common.mustCall((err) => { let expectedErrorCode = 'ERR_SSL_WRONG_VERSION_NUMBER'; let expectedErrorReason = 'wrong version number'; - if (common.hasOpenSSL(3, 2)) { + if (hasOpenSSL(3, 2)) { expectedErrorCode = 'ERR_SSL_PACKET_LENGTH_TOO_LONG'; expectedErrorReason = 'packet length too long'; }; assert.strictEqual(err.code, expectedErrorCode); assert.strictEqual(err.library, 'SSL routines'); - if (!common.hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_get_record'); + if (!hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_get_record'); assert.strictEqual(err.reason, expectedErrorReason); errorReceived = true; if (canCloseServer()) @@ -96,13 +104,13 @@ function sendBADTLSRecord() { client.on('error', common.mustCall((err) => { let expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION'; let expectedErrorReason = 'tlsv1 alert protocol version'; - if (common.hasOpenSSL(3, 2)) { + if (hasOpenSSL(3, 2)) { expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_RECORD_OVERFLOW'; expectedErrorReason = 'tlsv1 alert record overflow'; } assert.strictEqual(err.code, expectedErrorCode); assert.strictEqual(err.library, 'SSL routines'); - if (!common.hasOpenSSL3) + if (!hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_read_bytes'); assert.strictEqual(err.reason, expectedErrorReason); })); diff --git a/test/parallel/test-tls-alert.js b/test/parallel/test-tls-alert.js index e6aaaedfe59d72..23c92e7293458f 100644 --- a/test/parallel/test-tls-alert.js +++ b/test/parallel/test-tls-alert.js @@ -21,11 +21,18 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { + hasOpenSSL, + opensslCli, +} = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const { execFile } = require('child_process'); @@ -42,10 +49,10 @@ const server = tls.Server({ cert: loadPEM('agent2-cert') }, null).listen(0, common.mustCall(() => { const args = ['s_client', '-quiet', '-tls1_1', - '-cipher', (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + '-cipher', (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), '-connect', `127.0.0.1:${server.address().port}`]; - execFile(common.opensslCli, args, common.mustCall((err, _, stderr) => { + execFile(opensslCli, args, common.mustCall((err, _, stderr) => { assert.strictEqual(err.code, 1); assert.match(stderr, /SSL alert number 70/); server.close(); diff --git a/test/parallel/test-tls-alpn-server-client.js b/test/parallel/test-tls-alpn-server-client.js index 8f1a4b8e439aab..b7cd2806471e67 100644 --- a/test/parallel/test-tls-alpn-server-client.js +++ b/test/parallel/test-tls-alpn-server-client.js @@ -1,8 +1,9 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const { spawn } = require('child_process'); @@ -198,7 +199,7 @@ function TestFatalAlert() { // OpenSSL's s_client should output the TLS alert number, which is 120 // for the 'no_application_protocol' alert. - const { opensslCli } = common; + const { opensslCli } = require('../common/crypto'); if (opensslCli) { const addr = `${serverIP}:${port}`; let stderr = ''; diff --git a/test/parallel/test-tls-cert-ext-encoding.js b/test/parallel/test-tls-cert-ext-encoding.js index 4556b5791851c5..154e0cdcf02294 100644 --- a/test/parallel/test-tls-cert-ext-encoding.js +++ b/test/parallel/test-tls-cert-ext-encoding.js @@ -3,7 +3,9 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (common.hasOpenSSL3) +const { hasOpenSSL3 } = require('../common/crypto'); + +if (hasOpenSSL3) // TODO(danbev) This test fails with the following error: // error:0D00008F:asn1 encoding routines::no matching choice type // diff --git a/test/parallel/test-tls-client-auth.js b/test/parallel/test-tls-client-auth.js index de4c8f038ec073..b347c0a88df571 100644 --- a/test/parallel/test-tls-client-auth.js +++ b/test/parallel/test-tls-client-auth.js @@ -3,6 +3,11 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const { hasOpenSSL } = require('../common/crypto'); + const { assert, connect, keys, tls } = require(fixtures.path('tls-connect')); @@ -79,7 +84,7 @@ connect({ }, function(err, pair, cleanup) { assert.strictEqual(pair.server.err.code, 'ERR_SSL_PEER_DID_NOT_RETURN_A_CERTIFICATE'); - const expectedErr = common.hasOpenSSL(3, 2) ? + const expectedErr = hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE' : 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; assert.strictEqual(pair.client.err.code, expectedErr); diff --git a/test/parallel/test-tls-client-getephemeralkeyinfo.js b/test/parallel/test-tls-client-getephemeralkeyinfo.js index 0bacd8702fc650..0f132c565e4400 100644 --- a/test/parallel/test-tls-client-getephemeralkeyinfo.js +++ b/test/parallel/test-tls-client-getephemeralkeyinfo.js @@ -3,6 +3,7 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const { X509Certificate } = require('crypto'); @@ -69,7 +70,7 @@ function test(size, type, name, cipher) { test(undefined, undefined, undefined, 'AES256-SHA256'); test('auto', 'DH', undefined, 'DHE-RSA-AES256-GCM-SHA384'); -if (!common.hasOpenSSL(3, 2)) { +if (!hasOpenSSL(3, 2)) { test(1024, 'DH', undefined, 'DHE-RSA-AES256-GCM-SHA384'); } else { test(3072, 'DH', undefined, 'DHE-RSA-AES256-GCM-SHA384'); diff --git a/test/parallel/test-tls-client-mindhsize.js b/test/parallel/test-tls-client-mindhsize.js index 15c086842e1e4a..1ab5b5fe1bffd7 100644 --- a/test/parallel/test-tls-client-mindhsize.js +++ b/test/parallel/test-tls-client-mindhsize.js @@ -3,6 +3,7 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const tls = require('tls'); const fixtures = require('../common/fixtures'); @@ -38,7 +39,7 @@ function test(size, err, next) { // Client set minimum DH parameter size to 2048 or 3072 bits // so that it fails when it makes a connection to the tls // server where is too small - const minDHSize = common.hasOpenSSL(3, 2) ? 3072 : 2048; + const minDHSize = hasOpenSSL(3, 2) ? 3072 : 2048; const client = tls.connect({ minDHSize: minDHSize, port: this.address().port, @@ -76,7 +77,7 @@ function testDHE3072() { test(3072, false, null); } -if (common.hasOpenSSL(3, 2)) { +if (hasOpenSSL(3, 2)) { // Minimum size for OpenSSL 3.2 is 2048 by default testDHE2048(true, testDHE3072); } else { diff --git a/test/parallel/test-tls-client-renegotiation-13.js b/test/parallel/test-tls-client-renegotiation-13.js index a32baed0249a0a..38a72fb525b430 100644 --- a/test/parallel/test-tls-client-renegotiation-13.js +++ b/test/parallel/test-tls-client-renegotiation-13.js @@ -1,6 +1,12 @@ 'use strict'; const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const { hasOpenSSL3 } = require('../common/crypto'); + const fixtures = require('../common/fixtures'); // Confirm that for TLSv1.3, renegotiate() is disallowed. @@ -29,7 +35,7 @@ connect({ const ok = client.renegotiate({}, common.mustCall((err) => { assert.throws(() => { throw err; }, { - message: common.hasOpenSSL3 ? + message: hasOpenSSL3 ? 'error:0A00010A:SSL routines::wrong ssl version' : 'error:1420410A:SSL routines:SSL_renegotiate:wrong ssl version', code: 'ERR_SSL_WRONG_SSL_VERSION', diff --git a/test/parallel/test-tls-client-renegotiation-limit.js b/test/parallel/test-tls-client-renegotiation-limit.js index 71d7a85bae468b..b35140e8964ac1 100644 --- a/test/parallel/test-tls-client-renegotiation-limit.js +++ b/test/parallel/test-tls-client-renegotiation-limit.js @@ -21,11 +21,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const tls = require('tls'); diff --git a/test/parallel/test-tls-dhe.js b/test/parallel/test-tls-dhe.js index 21739ce42428eb..25b58191e1d413 100644 --- a/test/parallel/test-tls-dhe.js +++ b/test/parallel/test-tls-dhe.js @@ -22,11 +22,18 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { + hasOpenSSL, + opensslCli, +} = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const { X509Certificate } = require('crypto'); @@ -43,7 +50,7 @@ const dheCipher = 'DHE-RSA-AES128-SHA256'; const ecdheCipher = 'ECDHE-RSA-AES128-SHA256'; const ciphers = `${dheCipher}:${ecdheCipher}`; -if (!common.hasOpenSSL(3, 2)) { +if (!hasOpenSSL(3, 2)) { // Test will emit a warning because the DH parameter size is < 2048 bits // when the test is run on versions lower than OpenSSL32 common.expectWarning('SecurityWarning', @@ -70,7 +77,7 @@ function test(dhparam, keylen, expectedCipher) { const args = ['s_client', '-connect', `127.0.0.1:${server.address().port}`, '-cipher', `${ciphers}:@SECLEVEL=1`]; - execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + execFile(opensslCli, args, common.mustSucceed((stdout) => { assert(keylen === null || stdout.includes(`Server Temp Key: DH, ${keylen} bits`)); assert(stdout.includes(`Cipher : ${expectedCipher}`)); @@ -107,7 +114,7 @@ function testCustomParam(keylen, expectedCipher) { }, /DH parameter is less than 1024 bits/); // Custom DHE parameters are supported (but discouraged). - if (!common.hasOpenSSL(3, 2)) { + if (!hasOpenSSL(3, 2)) { await testCustomParam(1024, dheCipher); } else { await testCustomParam(3072, dheCipher); diff --git a/test/parallel/test-tls-ecdh-auto.js b/test/parallel/test-tls-ecdh-auto.js index 11c588d8ac8ce1..adc7817b729aa8 100644 --- a/test/parallel/test-tls-ecdh-auto.js +++ b/test/parallel/test-tls-ecdh-auto.js @@ -4,11 +4,15 @@ const common = require('../common'); // This test ensures that the value "auto" on ecdhCurve option is // supported to enable automatic curve selection in TLS server. -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -36,7 +40,7 @@ const server = tls.createServer(options, (conn) => { '-cipher', `${options.ciphers}`, '-connect', `127.0.0.1:${server.address().port}`]; - execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + execFile(opensslCli, args, common.mustSucceed((stdout) => { assert(stdout.includes(reply)); server.close(); })); diff --git a/test/parallel/test-tls-ecdh-multiple.js b/test/parallel/test-tls-ecdh-multiple.js index 5bf119f48bacad..957f8e0407a6de 100644 --- a/test/parallel/test-tls-ecdh-multiple.js +++ b/test/parallel/test-tls-ecdh-multiple.js @@ -4,11 +4,16 @@ const common = require('../common'); // This test ensures that ecdhCurve option of TLS server supports colon // separated ECDH curve names as value. -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); +const crypto = require('crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -36,7 +41,7 @@ const server = tls.createServer(options, (conn) => { '-cipher', `${options.ciphers}`, '-connect', `127.0.0.1:${server.address().port}`]; - execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + execFile(opensslCli, args, common.mustSucceed((stdout) => { assert(stdout.includes(reply)); server.close(); })); @@ -51,8 +56,9 @@ const server = tls.createServer(options, (conn) => { ]; // Brainpool is not supported in FIPS mode. - if (common.hasFipsCrypto) + if (crypto.getFips()) { unsupportedCurves.push('brainpoolP256r1'); + } unsupportedCurves.forEach((ecdhCurve) => { assert.throws(() => tls.createServer({ ecdhCurve }), diff --git a/test/parallel/test-tls-ecdh.js b/test/parallel/test-tls-ecdh.js index 276b713f5ecf70..4d45e7f024586e 100644 --- a/test/parallel/test-tls-ecdh.js +++ b/test/parallel/test-tls-ecdh.js @@ -23,11 +23,15 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (!common.opensslCli) +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -49,7 +53,7 @@ const server = tls.createServer(options, common.mustCall(function(conn) { })); server.listen(0, '127.0.0.1', common.mustCall(function() { - const cmd = common.escapePOSIXShell`"${common.opensslCli}" s_client -cipher ${ + const cmd = common.escapePOSIXShell`"${opensslCli}" s_client -cipher ${ options.ciphers} -connect 127.0.0.1:${this.address().port}`; exec(...cmd, common.mustSucceed((stdout, stderr) => { diff --git a/test/parallel/test-tls-empty-sni-context.js b/test/parallel/test-tls-empty-sni-context.js index 093e5cca712d2c..79f1ddd341d938 100644 --- a/test/parallel/test-tls-empty-sni-context.js +++ b/test/parallel/test-tls-empty-sni-context.js @@ -3,7 +3,7 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); - +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const tls = require('tls'); @@ -26,7 +26,7 @@ const server = tls.createServer(options, (c) => { }, common.mustNotCall()); c.on('error', common.mustCall((err) => { - const expectedErr = common.hasOpenSSL(3, 2) ? + const expectedErr = hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE' : 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; assert.strictEqual(err.code, expectedErr); })); diff --git a/test/parallel/test-tls-getprotocol.js b/test/parallel/test-tls-getprotocol.js index a9c8775e2f112f..b1eab88fd6517e 100644 --- a/test/parallel/test-tls-getprotocol.js +++ b/test/parallel/test-tls-getprotocol.js @@ -3,6 +3,8 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +const { hasOpenSSL } = require('../common/crypto'); + // This test ensures that `getProtocol` returns the right protocol // from a TLS connection @@ -14,11 +16,11 @@ const clientConfigs = [ { secureProtocol: 'TLSv1_method', version: 'TLSv1', - ciphers: (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT') + ciphers: (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT') }, { secureProtocol: 'TLSv1_1_method', version: 'TLSv1.1', - ciphers: (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT') + ciphers: (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT') }, { secureProtocol: 'TLSv1_2_method', version: 'TLSv1.2' diff --git a/test/parallel/test-tls-junk-server.js b/test/parallel/test-tls-junk-server.js index cc520383ede45f..0e536a66884e94 100644 --- a/test/parallel/test-tls-junk-server.js +++ b/test/parallel/test-tls-junk-server.js @@ -1,8 +1,11 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const https = require('https'); @@ -21,7 +24,7 @@ server.listen(0, function() { req.end(); let expectedErrorMessage = new RegExp('wrong version number'); - if (common.hasOpenSSL(3, 2)) { + if (hasOpenSSL(3, 2)) { expectedErrorMessage = new RegExp('packet length too long'); }; req.once('error', common.mustCall(function(err) { diff --git a/test/parallel/test-tls-key-mismatch.js b/test/parallel/test-tls-key-mismatch.js index fdbb3676267a9d..df8848a03de4a9 100644 --- a/test/parallel/test-tls-key-mismatch.js +++ b/test/parallel/test-tls-key-mismatch.js @@ -22,14 +22,16 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); const assert = require('assert'); const tls = require('tls'); -const errorMessageRegex = common.hasOpenSSL3 ? +const errorMessageRegex = hasOpenSSL3 ? /^Error: error:05800074:x509 certificate routines::key values mismatch$/ : /^Error: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch$/; diff --git a/test/parallel/test-tls-legacy-pfx.js b/test/parallel/test-tls-legacy-pfx.js index 33b4c58fc6ccc3..5106217718dbdc 100644 --- a/test/parallel/test-tls-legacy-pfx.js +++ b/test/parallel/test-tls-legacy-pfx.js @@ -1,9 +1,14 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.hasOpenSSL3) +} + +const { hasOpenSSL3 } = require('../common/crypto'); + +if (!hasOpenSSL3) { common.skip('OpenSSL legacy failures are only testable with OpenSSL 3+'); +} const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-tls-min-max-version.js b/test/parallel/test-tls-min-max-version.js index af32468eea6a68..4903d92f5c5700 100644 --- a/test/parallel/test-tls-min-max-version.js +++ b/test/parallel/test-tls-min-max-version.js @@ -1,5 +1,13 @@ 'use strict'; const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const { + hasOpenSSL, + hasOpenSSL3, +} = require('../common/crypto'); const fixtures = require('../common/fixtures'); const { inspect } = require('util'); @@ -16,13 +24,13 @@ function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) { assert(proto || cerr || serr, 'test missing any expectations'); let ciphers; - if (common.hasOpenSSL3 && (proto === 'TLSv1' || proto === 'TLSv1.1' || + if (hasOpenSSL3 && (proto === 'TLSv1' || proto === 'TLSv1.1' || proto === 'TLSv1_1_method' || proto === 'TLSv1_method' || sprot === 'TLSv1_1_method' || sprot === 'TLSv1_method')) { if (serr !== 'ERR_SSL_UNSUPPORTED_PROTOCOL') ciphers = 'ALL@SECLEVEL=0'; } - if (common.hasOpenSSL(3, 1) && cerr === 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION') { + if (hasOpenSSL(3, 1) && cerr === 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION') { ciphers = 'DEFAULT@SECLEVEL=0'; } // Report where test was called from. Strip leading garbage from @@ -125,9 +133,9 @@ test(U, U, 'TLS_method', U, U, 'TLSv1_method', 'TLSv1'); // OpenSSL 1.1.1 and 3.0 use a different error code and alert (sent to the // client) when no protocols are enabled on the server. -const NO_PROTOCOLS_AVAILABLE_SERVER = common.hasOpenSSL3 ? +const NO_PROTOCOLS_AVAILABLE_SERVER = hasOpenSSL3 ? 'ERR_SSL_NO_PROTOCOLS_AVAILABLE' : 'ERR_SSL_INTERNAL_ERROR'; -const NO_PROTOCOLS_AVAILABLE_SERVER_ALERT = common.hasOpenSSL3 ? +const NO_PROTOCOLS_AVAILABLE_SERVER_ALERT = hasOpenSSL3 ? 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION' : 'ERR_SSL_TLSV1_ALERT_INTERNAL_ERROR'; // SSLv23 also means "any supported protocol" greater than the default diff --git a/test/parallel/test-tls-no-sslv3.js b/test/parallel/test-tls-no-sslv3.js index 9282beb4bdac2c..cd5f4ad944a6c5 100644 --- a/test/parallel/test-tls-no-sslv3.js +++ b/test/parallel/test-tls-no-sslv3.js @@ -1,10 +1,14 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (common.opensslCli === false) +const { opensslCli } = require('../common/crypto'); + +if (opensslCli === false) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const tls = require('tls'); @@ -23,7 +27,7 @@ server.listen(0, '127.0.0.1', function() { '-ssl3', '-connect', address]; - const client = spawn(common.opensslCli, args, { stdio: 'pipe' }); + const client = spawn(opensslCli, args, { stdio: 'pipe' }); client.stdout.pipe(process.stdout); client.stderr.pipe(process.stderr); client.stderr.setEncoding('utf8'); diff --git a/test/parallel/test-tls-ocsp-callback.js b/test/parallel/test-tls-ocsp-callback.js index 04a60a0890c506..bdf622d4686ec1 100644 --- a/test/parallel/test-tls-ocsp-callback.js +++ b/test/parallel/test-tls-ocsp-callback.js @@ -22,12 +22,17 @@ 'use strict'; const common = require('../common'); -if (!common.opensslCli) - common.skip('node compiled without OpenSSL CLI.'); - -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { + common.skip('node compiled without OpenSSL CLI.'); +} +const crypto = require('crypto'); const tls = require('tls'); const fixtures = require('../common/fixtures'); @@ -108,6 +113,6 @@ test({ ocsp: true, response: false }); test({ ocsp: true, response: 'hello world' }); test({ ocsp: false }); -if (!common.hasFipsCrypto) { +if (!crypto.getFips()) { test({ ocsp: true, response: 'hello pfx', pfx: pfx, passphrase: 'sample' }); } diff --git a/test/parallel/test-tls-psk-circuit.js b/test/parallel/test-tls-psk-circuit.js index c06e61c321ef67..61861ecf4dafa6 100644 --- a/test/parallel/test-tls-psk-circuit.js +++ b/test/parallel/test-tls-psk-circuit.js @@ -1,9 +1,11 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const tls = require('tls'); @@ -62,12 +64,12 @@ test({ psk: USERS.UserA, identity: 'UserA' }, { minVersion: 'TLSv1.3' }); test({ psk: USERS.UserB, identity: 'UserB' }); test({ psk: USERS.UserB, identity: 'UserB' }, { minVersion: 'TLSv1.3' }); // Unrecognized user should fail handshake -const expectedHandshakeErr = common.hasOpenSSL(3, 2) ? +const expectedHandshakeErr = hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE' : 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; test({ psk: USERS.UserB, identity: 'UserC' }, {}, expectedHandshakeErr); // Recognized user but incorrect secret should fail handshake -const expectedIllegalParameterErr = common.hasOpenSSL(3, 4) ? 'ERR_SSL_TLSV1_ALERT_DECRYPT_ERROR' : - common.hasOpenSSL(3, 2) ? +const expectedIllegalParameterErr = hasOpenSSL(3, 4) ? 'ERR_SSL_TLSV1_ALERT_DECRYPT_ERROR' : + hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_ILLEGAL_PARAMETER' : 'ERR_SSL_SSLV3_ALERT_ILLEGAL_PARAMETER'; test({ psk: USERS.UserA, identity: 'UserB' }, {}, expectedIllegalParameterErr); test({ psk: USERS.UserB, identity: 'UserB' }); diff --git a/test/parallel/test-tls-psk-server.js b/test/parallel/test-tls-psk-server.js index b9260958401522..87fad86083e1ab 100644 --- a/test/parallel/test-tls-psk-server.js +++ b/test/parallel/test-tls-psk-server.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.opensslCli) +} + +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { common.skip('missing openssl cli'); +} const assert = require('assert'); @@ -41,7 +46,7 @@ let sentWorld = false; let gotWorld = false; server.listen(0, () => { - const client = spawn(common.opensslCli, [ + const client = spawn(opensslCli, [ 's_client', '-connect', `127.0.0.1:${server.address().port}`, '-cipher', CIPHERS, diff --git a/test/parallel/test-tls-securepair-server.js b/test/parallel/test-tls-securepair-server.js index 78cd9f725401ed..fb4ebe6a2511cf 100644 --- a/test/parallel/test-tls-securepair-server.js +++ b/test/parallel/test-tls-securepair-server.js @@ -21,11 +21,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -109,7 +113,7 @@ server.listen(0, common.mustCall(function() { const args = ['s_client', '-connect', `127.0.0.1:${this.address().port}`]; - const client = spawn(common.opensslCli, args); + const client = spawn(opensslCli, args); let out = ''; diff --git a/test/parallel/test-tls-server-verify.js b/test/parallel/test-tls-server-verify.js index 51ccd0d747fdf5..2517c7c8dbbb1f 100644 --- a/test/parallel/test-tls-server-verify.js +++ b/test/parallel/test-tls-server-verify.js @@ -22,11 +22,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} // This is a rather complex test which sets up various TLS servers with node // and connects to them using the 'openssl s_client' command line utility @@ -188,7 +192,7 @@ function runClient(prefix, port, options, cb) { } // To test use: openssl s_client -connect localhost:8000 - const client = spawn(common.opensslCli, args); + const client = spawn(opensslCli, args); let out = ''; diff --git a/test/parallel/test-tls-session-cache.js b/test/parallel/test-tls-session-cache.js index b55e150401d8a2..9524764aa609ee 100644 --- a/test/parallel/test-tls-session-cache.js +++ b/test/parallel/test-tls-session-cache.js @@ -21,17 +21,23 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} +const { + hasOpenSSL, + opensslCli, +} = require('../common/crypto'); + +if (!opensslCli) { + common.skip('node compiled without OpenSSL CLI.'); +} + const fixtures = require('../common/fixtures'); const assert = require('assert'); const tls = require('tls'); const { spawn } = require('child_process'); -if (!common.opensslCli) - common.skip('node compiled without OpenSSL CLI.'); - - doTest({ tickets: false }, function() { doTest({ tickets: true }, function() { doTest({ tickets: false, invalidSession: true }, function() { @@ -100,7 +106,7 @@ function doTest(testOptions, callback) { const args = [ 's_client', '-tls1', - '-cipher', (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + '-cipher', (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), '-connect', `localhost:${this.address().port}`, '-servername', 'ohgod', '-key', fixtures.path('keys/rsa_private.pem'), @@ -109,7 +115,7 @@ function doTest(testOptions, callback) { ].concat(testOptions.tickets ? [] : '-no_ticket'); function spawnClient() { - const client = spawn(common.opensslCli, args, { + const client = spawn(opensslCli, args, { stdio: [ 0, 1, 'pipe' ] }); let err = ''; diff --git a/test/parallel/test-tls-set-ciphers.js b/test/parallel/test-tls-set-ciphers.js index f7062e73c9403c..1e63e9376e134b 100644 --- a/test/parallel/test-tls-set-ciphers.js +++ b/test/parallel/test-tls-set-ciphers.js @@ -1,7 +1,17 @@ 'use strict'; const common = require('../common'); -if (!common.hasOpenSSL3) +if (!common.hasCrypto) { common.skip('missing crypto, or OpenSSL version lower than 3'); +} + +const { + hasOpenSSL, + hasOpenSSL3, +} = require('../common/crypto'); + +if (!hasOpenSSL3) { + common.skip('missing crypto, or OpenSSL version lower than 3'); +} const fixtures = require('../common/fixtures'); const { inspect } = require('util'); @@ -80,7 +90,7 @@ function test(cciphers, sciphers, cipher, cerr, serr, options) { const U = undefined; let expectedTLSAlertError = 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; -if (common.hasOpenSSL(3, 2)) { +if (hasOpenSSL(3, 2)) { expectedTLSAlertError = 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE'; } @@ -117,7 +127,7 @@ test(U, 'AES256-SHA', 'TLS_AES_256_GCM_SHA384', U, U, { maxVersion: 'TLSv1.3' }) // default, but work. // However, for OpenSSL32 AES_128 is not enabled due to the // default security level -if (!common.hasOpenSSL(3, 2)) { +if (!hasOpenSSL(3, 2)) { test('TLS_AES_128_CCM_8_SHA256', U, U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER'); diff --git a/test/parallel/test-tls-set-secure-context.js b/test/parallel/test-tls-set-secure-context.js index c056875e14ddfb..3d2de6b3321414 100644 --- a/test/parallel/test-tls-set-secure-context.js +++ b/test/parallel/test-tls-set-secure-context.js @@ -1,8 +1,9 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} // This test verifies the behavior of the tls setSecureContext() method. // It also verifies that existing connections are not disrupted when the @@ -12,6 +13,7 @@ const assert = require('assert'); const events = require('events'); const https = require('https'); const timers = require('timers/promises'); +const { hasOpenSSL3 } = require('../common/crypto'); const fixtures = require('../common/fixtures'); const credentialOptions = [ { @@ -55,7 +57,7 @@ server.listen(0, common.mustCall(() => { server.setSecureContext(credentialOptions[1]); firstResponse.write('request-'); - const errorMessageRegex = common.hasOpenSSL3 ? + const errorMessageRegex = hasOpenSSL3 ? /^Error: self-signed certificate$/ : /^Error: self signed certificate$/; await assert.rejects(makeRequest(port, 3), errorMessageRegex); diff --git a/test/parallel/test-tls-set-sigalgs.js b/test/parallel/test-tls-set-sigalgs.js index 3f3d152f4d877e..985ca13ba2ac7d 100644 --- a/test/parallel/test-tls-set-sigalgs.js +++ b/test/parallel/test-tls-set-sigalgs.js @@ -1,6 +1,9 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) common.skip('missing crypto'); +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const { hasOpenSSL } = require('../common/crypto'); const fixtures = require('../common/fixtures'); // Test sigalgs: option for TLS. @@ -63,7 +66,7 @@ test('RSA-PSS+SHA256:RSA-PSS+SHA512:ECDSA+SHA256', ['RSA-PSS+SHA256', 'ECDSA+SHA256']); // Do not have shared sigalgs. -const handshakeErr = common.hasOpenSSL(3, 2) ? +const handshakeErr = hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE' : 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; test('RSA-PSS+SHA384', 'ECDSA+SHA256', undefined, handshakeErr, diff --git a/test/parallel/test-trace-env.js b/test/parallel/test-trace-env.js index ba08e0af2aad1d..7a7b80fa4c1094 100644 --- a/test/parallel/test-trace-env.js +++ b/test/parallel/test-trace-env.js @@ -18,9 +18,11 @@ spawnSyncAndAssert(process.execPath, ['--trace-env', fixtures.path('empty.js')], } if (common.hasCrypto) { assert.match(output, /get "NODE_EXTRA_CA_CERTS"/); - } - if (common.hasOpenSSL3) { - assert.match(output, /get "OPENSSL_CONF"/); + + const { hasOpenSSL3 } = require('../common/crypto'); + if (hasOpenSSL3) { + assert.match(output, /get "OPENSSL_CONF"/); + } } assert.match(output, /get "NODE_DEBUG_NATIVE"/); assert.match(output, /get "NODE_COMPILE_CACHE"/); diff --git a/test/parallel/test-x509-escaping.js b/test/parallel/test-x509-escaping.js index e6ae4d886908cb..b507af88e1f7f3 100644 --- a/test/parallel/test-x509-escaping.js +++ b/test/parallel/test-x509-escaping.js @@ -1,15 +1,16 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const { X509Certificate } = require('crypto'); const tls = require('tls'); const fixtures = require('../common/fixtures'); -const { hasOpenSSL3 } = common; +const { hasOpenSSL3 } = require('../common/crypto'); // Test that all certificate chains provided by the reporter are rejected. { diff --git a/test/pummel/test-crypto-dh-hash.js b/test/pummel/test-crypto-dh-hash.js index ef5a640688c9bb..b59f556a2042b9 100644 --- a/test/pummel/test-crypto-dh-hash.js +++ b/test/pummel/test-crypto-dh-hash.js @@ -30,7 +30,9 @@ if (common.isPi) { common.skip('Too slow for Raspberry Pi devices'); } -if (!common.hasOpenSSL3) { +const { hasOpenSSL3 } = require('../common/crypto'); + +if (!hasOpenSSL3) { common.skip('Too slow when dynamically linked against OpenSSL 1.1.1'); } diff --git a/test/pummel/test-crypto-dh-keys.js b/test/pummel/test-crypto-dh-keys.js index 2caa4e244a9859..abce6a07acf4ac 100644 --- a/test/pummel/test-crypto-dh-keys.js +++ b/test/pummel/test-crypto-dh-keys.js @@ -36,8 +36,9 @@ const crypto = require('crypto'); [ 'modp1', 'modp2', 'modp5', 'modp14', 'modp15', 'modp16', 'modp17' ] .forEach((name) => { // modp1 is 768 bits, FIPS requires >= 1024 - if (name === 'modp1' && common.hasFipsCrypto) + if (name === 'modp1' && crypto.getFips()) { return; + } const group1 = crypto.getDiffieHellman(name); const group2 = crypto.getDiffieHellman(name); group1.generateKeys(); diff --git a/test/pummel/test-dh-regr.js b/test/pummel/test-dh-regr.js index 41d5bf872f97ec..cfae57d0728bdb 100644 --- a/test/pummel/test-dh-regr.js +++ b/test/pummel/test-dh-regr.js @@ -32,10 +32,11 @@ if (common.isPi) { const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // FIPS requires length >= 1024 but we use 512/256 in this test to keep it from // taking too long and timing out in CI. -const length = (common.hasFipsCrypto) ? 1024 : common.hasOpenSSL3 ? 512 : 256; +const length = crypto.getFips() ? 1024 : hasOpenSSL3 ? 512 : 256; const p = crypto.createDiffieHellman(length).getPrime(); diff --git a/test/sequential/test-tls-psk-client.js b/test/sequential/test-tls-psk-client.js index ddebc8f8cc9807..c07b1f92d98376 100644 --- a/test/sequential/test-tls-psk-client.js +++ b/test/sequential/test-tls-psk-client.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.opensslCli) +} + +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { common.skip('missing openssl cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -16,7 +21,7 @@ const KEY = 'd731ef57be09e5204f0b205b60627028'; const IDENTITY = 'Client_identity'; // Hardcoded by `openssl s_server` const useIPv4 = !common.hasIPv6; -const server = spawn(common.opensslCli, [ +const server = spawn(opensslCli, [ 's_server', '-accept', common.PORT, '-cipher', CIPHERS, diff --git a/test/sequential/test-tls-securepair-client.js b/test/sequential/test-tls-securepair-client.js index f3ca42ad6edfb0..262518621b5f3f 100644 --- a/test/sequential/test-tls-securepair-client.js +++ b/test/sequential/test-tls-securepair-client.js @@ -23,14 +23,19 @@ const common = require('../common'); -if (!common.opensslCli) - common.skip('node compiled without OpenSSL CLI.'); - -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (common.isWindows) +if (!opensslCli) { + common.skip('node compiled without OpenSSL CLI.'); +} + +if (common.isWindows) { common.skip('test does not work on Windows'); // ...but it should! +} const net = require('net'); const assert = require('assert'); @@ -63,11 +68,11 @@ function test(keyPath, certPath, check, next) { const key = fixtures.readSync(keyPath).toString(); const cert = fixtures.readSync(certPath).toString(); - const server = spawn(common.opensslCli, ['s_server', - '-accept', 0, - '-cert', fixtures.path(certPath), - '-key', fixtures.path(keyPath), - ...(useIPv4 ? ['-4'] : []), + const server = spawn(opensslCli, ['s_server', + '-accept', 0, + '-cert', fixtures.path(certPath), + '-key', fixtures.path(keyPath), + ...(useIPv4 ? ['-4'] : []), ]); server.stdout.pipe(process.stdout); server.stderr.pipe(process.stdout); diff --git a/test/sequential/test-tls-session-timeout.js b/test/sequential/test-tls-session-timeout.js index 09107011aeda52..a93cdc793a2337 100644 --- a/test/sequential/test-tls-session-timeout.js +++ b/test/sequential/test-tls-session-timeout.js @@ -22,8 +22,11 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); @@ -56,8 +59,9 @@ const cert = fixtures.readKey('rsa_cert.crt'); } } -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} doTest(); @@ -105,7 +109,7 @@ function doTest() { '-sess_in', sessionFileName, '-sess_out', sessionFileName, ]; - const client = spawn(common.opensslCli, flags, { + const client = spawn(opensslCli, flags, { stdio: ['ignore', 'pipe', 'ignore'] }); From f07300cfa32c197684fe44d39965770b2fcfc23d Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 24 Jan 2025 17:35:59 -0800 Subject: [PATCH 115/208] test: move hasMultiLocalhost to common/net Given that `common/net` already exists and hasMultiLocalhost is net specific, let's move it out of common/index to better encapsulate and simplify common/index more PR-URL: https://github.com/nodejs/node/pull/56716 Reviewed-By: Yagiz Nizipli Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca --- test/common/README.md | 6 ------ test/common/index.js | 10 ---------- test/common/index.mjs | 2 -- test/common/net.js | 10 ++++++++++ test/parallel/test-http-localaddress.js | 4 +++- test/parallel/test-http2-connect-options.js | 4 +++- test/parallel/test-https-localaddress.js | 4 +++- 7 files changed, 19 insertions(+), 21 deletions(-) diff --git a/test/common/README.md b/test/common/README.md index ee36503f920001..9ecee39b64a3df 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -238,12 +238,6 @@ Indicates if [internationalization][] is supported. Indicates whether `IPv6` is supported on this platform. -### `hasMultiLocalhost` - -* [\][] - -Indicates if there are multiple localhosts available. - ### `inFreeBSDJail` * [\][] diff --git a/test/common/index.js b/test/common/index.js index d2c39578324600..e8bf65d0a6edb4 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -489,15 +489,6 @@ function _mustCallInner(fn, criteria = 1, field) { return _return; } -function hasMultiLocalhost() { - const { internalBinding } = require('internal/test/binding'); - const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap'); - const t = new TCP(TCPConstants.SOCKET); - const ret = t.bind('127.0.0.2', 0); - t.close(); - return ret === 0; -} - function skipIfEslintMissing() { if (!fs.existsSync( path.join(__dirname, '..', '..', 'tools', 'eslint', 'node_modules', 'eslint'), @@ -965,7 +956,6 @@ const common = { hasIntl, hasCrypto, hasQuic, - hasMultiLocalhost, invalidArgTypeHelper, isAlive, isASan, diff --git a/test/common/index.mjs b/test/common/index.mjs index 23328ac90ea3c9..090659f93be8ef 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -20,7 +20,6 @@ const { hasCrypto, hasIntl, hasIPv6, - hasMultiLocalhost, isAIX, isAlive, isDumbTerminal, @@ -75,7 +74,6 @@ export { hasCrypto, hasIntl, hasIPv6, - hasMultiLocalhost, isAIX, isAlive, isDumbTerminal, diff --git a/test/common/net.js b/test/common/net.js index 84eddd0966ed56..3886c542421005 100644 --- a/test/common/net.js +++ b/test/common/net.js @@ -17,7 +17,17 @@ function checkSupportReusePort() { }); } +function hasMultiLocalhost() { + const { internalBinding } = require('internal/test/binding'); + const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap'); + const t = new TCP(TCPConstants.SOCKET); + const ret = t.bind('127.0.0.2', 0); + t.close(); + return ret === 0; +} + module.exports = { checkSupportReusePort, + hasMultiLocalhost, options, }; diff --git a/test/parallel/test-http-localaddress.js b/test/parallel/test-http-localaddress.js index a0e4bb80a3f8c2..da25ab3047613f 100644 --- a/test/parallel/test-http-localaddress.js +++ b/test/parallel/test-http-localaddress.js @@ -22,8 +22,10 @@ // Flags: --expose-internals 'use strict'; const common = require('../common'); -if (!common.hasMultiLocalhost()) +const { hasMultiLocalhost } = require('../common/net'); +if (!hasMultiLocalhost()) { common.skip('platform-specific test.'); +} const http = require('http'); const assert = require('assert'); diff --git a/test/parallel/test-http2-connect-options.js b/test/parallel/test-http2-connect-options.js index 233ced016974e2..1abcee99e06433 100644 --- a/test/parallel/test-http2-connect-options.js +++ b/test/parallel/test-http2-connect-options.js @@ -4,8 +4,10 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (!common.hasMultiLocalhost()) +const { hasMultiLocalhost } = require('../common/net'); +if (!hasMultiLocalhost()) { common.skip('platform-specific test.'); +} const http2 = require('http2'); const assert = require('assert'); diff --git a/test/parallel/test-https-localaddress.js b/test/parallel/test-https-localaddress.js index 0de0974dc69b04..2a4629b34e4105 100644 --- a/test/parallel/test-https-localaddress.js +++ b/test/parallel/test-https-localaddress.js @@ -25,8 +25,10 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (!common.hasMultiLocalhost()) +const { hasMultiLocalhost } = require('../common/net'); +if (!hasMultiLocalhost()) { common.skip('platform-specific test.'); +} const fixtures = require('../common/fixtures'); const assert = require('assert'); From 4a5d2c7538b412eea84a0f41544784b1b8ed7c8c Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 25 Jan 2025 03:30:27 +0100 Subject: [PATCH 116/208] module: integrate TypeScript into compile cache This integrates TypeScript into the compile cache by caching the transpilation (either type-stripping or transforming) output in addition to the V8 code cache that's generated from the transpilation output. Locally this speeds up loading with type stripping of `benchmark/fixtures/strip-types-benchmark.ts` by ~65% and loading with type transforms of `fixtures/transform-types-benchmark.ts` by ~128%. When comparing loading .ts and loading pre-transpiled .js on-disk with the compile cache enabled, previously .ts loaded 46% slower with type-stripping and 66% slower with transforms compared to loading .js files directly. After this patch, .ts loads 12% slower with type-stripping and 22% slower with transforms compared to .js. (Note that the numbers are based on microbenchmark fixtures and do not necessarily represent real-world workloads, though with bigger real-world files, the speed up should be more significant). PR-URL: https://github.com/nodejs/node/pull/56629 Fixes: https://github.com/nodejs/node/issues/54741 Reviewed-By: Geoffrey Booth Reviewed-By: Marco Ippolito Reviewed-By: James M Snell --- lib/internal/modules/typescript.js | 63 ++++++- src/compile_cache.cc | 65 ++++++- src/compile_cache.h | 15 +- src/node_modules.cc | 96 ++++++++++ .../test-compile-cache-typescript-commonjs.js | 166 +++++++++++++++++ .../test-compile-cache-typescript-esm.js | 167 ++++++++++++++++++ ...est-compile-cache-typescript-strip-miss.js | 104 +++++++++++ ...mpile-cache-typescript-strip-sourcemaps.js | 59 +++++++ ...test-compile-cache-typescript-transform.js | 127 +++++++++++++ 9 files changed, 846 insertions(+), 16 deletions(-) create mode 100644 test/parallel/test-compile-cache-typescript-commonjs.js create mode 100644 test/parallel/test-compile-cache-typescript-esm.js create mode 100644 test/parallel/test-compile-cache-typescript-strip-miss.js create mode 100644 test/parallel/test-compile-cache-typescript-strip-sourcemaps.js create mode 100644 test/parallel/test-compile-cache-typescript-transform.js diff --git a/lib/internal/modules/typescript.js b/lib/internal/modules/typescript.js index 6abfc707657b92..17bbc6ba944432 100644 --- a/lib/internal/modules/typescript.js +++ b/lib/internal/modules/typescript.js @@ -22,6 +22,11 @@ const { const { getOptionValue } = require('internal/options'); const assert = require('internal/assert'); const { Buffer } = require('buffer'); +const { + getCompileCacheEntry, + saveCompileCacheEntry, + cachedCodeTypes: { kStrippedTypeScript, kTransformedTypeScript, kTransformedTypeScriptWithSourceMaps }, +} = internalBinding('modules'); /** * The TypeScript parsing mode, either 'strip-only' or 'transform'. @@ -105,11 +110,19 @@ function stripTypeScriptTypes(code, options = kEmptyObject) { }); } +/** + * @typedef {'strip-only' | 'transform'} TypeScriptMode + * @typedef {object} TypeScriptOptions + * @property {TypeScriptMode} mode Mode. + * @property {boolean} sourceMap Whether to generate source maps. + * @property {string|undefined} filename Filename. + */ + /** * Processes TypeScript code by stripping types or transforming. * Handles source maps if needed. * @param {string} code TypeScript code to process. - * @param {object} options The configuration object. + * @param {TypeScriptOptions} options The configuration object. * @returns {string} The processed code. */ function processTypeScriptCode(code, options) { @@ -126,6 +139,20 @@ function processTypeScriptCode(code, options) { return transformedCode; } +/** + * Get the type enum used for compile cache. + * @param {TypeScriptMode} mode Mode of transpilation. + * @param {boolean} sourceMap Whether source maps are enabled. + * @returns {number} + */ +function getCachedCodeType(mode, sourceMap) { + if (mode === 'transform') { + if (sourceMap) { return kTransformedTypeScriptWithSourceMaps; } + return kTransformedTypeScript; + } + return kStrippedTypeScript; +} + /** * Performs type-stripping to TypeScript source code internally. * It is used by internal loaders. @@ -142,12 +169,40 @@ function stripTypeScriptModuleTypes(source, filename, emitWarning = true) { if (isUnderNodeModules(filename)) { throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename); } + const sourceMap = getOptionValue('--enable-source-maps'); + + const mode = getTypeScriptParsingMode(); + + // Instead of caching the compile cache status, just go into C++ to fetch it, + // as checking process.env equally involves calling into C++ anyway, and + // the compile cache can be enabled dynamically. + const type = getCachedCodeType(mode, sourceMap); + // Get a compile cache entry into the native compile cache store, + // keyed by the filename. If the cache can already be loaded on disk, + // cached.transpiled contains the cached string. Otherwise we should do + // the transpilation and save it in the native store later using + // saveCompileCacheEntry(). + const cached = (filename ? getCompileCacheEntry(source, filename, type) : undefined); + if (cached?.transpiled) { // TODO(joyeecheung): return Buffer here. + return cached.transpiled; + } + const options = { - mode: getTypeScriptParsingMode(), - sourceMap: getOptionValue('--enable-source-maps'), + mode, + sourceMap, filename, }; - return processTypeScriptCode(source, options); + + const transpiled = processTypeScriptCode(source, options); + if (cached) { + // cached.external contains a pointer to the native cache entry. + // The cached object would be unreachable once it's out of scope, + // but the pointer inside cached.external would stay around for reuse until + // environment shutdown or when the cache is manually flushed + // to disk. Unwrap it in JS before passing into C++ since it's faster. + saveCompileCacheEntry(cached.external, transpiled); + } + return transpiled; } /** diff --git a/src/compile_cache.cc b/src/compile_cache.cc index 50697bcfe1671d..f13797e5f50288 100644 --- a/src/compile_cache.cc +++ b/src/compile_cache.cc @@ -77,10 +77,27 @@ v8::ScriptCompiler::CachedData* CompileCacheEntry::CopyCache() const { // See comments in CompileCacheHandler::Persist(). constexpr uint32_t kCacheMagicNumber = 0x8adfdbb2; +const char* CompileCacheEntry::type_name() const { + switch (type) { + case CachedCodeType::kCommonJS: + return "CommonJS"; + case CachedCodeType::kESM: + return "ESM"; + case CachedCodeType::kStrippedTypeScript: + return "StrippedTypeScript"; + case CachedCodeType::kTransformedTypeScript: + return "TransformedTypeScript"; + case CachedCodeType::kTransformedTypeScriptWithSourceMaps: + return "TransformedTypeScriptWithSourceMaps"; + default: + UNREACHABLE(); + } +} + void CompileCacheHandler::ReadCacheFile(CompileCacheEntry* entry) { Debug("[compile cache] reading cache from %s for %s %s...", entry->cache_filename, - entry->type == CachedCodeType::kCommonJS ? "CommonJS" : "ESM", + entry->type_name(), entry->source_filename); uv_fs_t req; @@ -256,7 +273,8 @@ void CompileCacheHandler::MaybeSaveImpl(CompileCacheEntry* entry, v8::Local func_or_mod, bool rejected) { DCHECK_NOT_NULL(entry); - Debug("[compile cache] cache for %s was %s, ", + Debug("[compile cache] V8 code cache for %s %s was %s, ", + entry->type_name(), entry->source_filename, rejected ? "rejected" : (entry->cache == nullptr) ? "not initialized" @@ -287,6 +305,25 @@ void CompileCacheHandler::MaybeSave(CompileCacheEntry* entry, MaybeSaveImpl(entry, func, rejected); } +void CompileCacheHandler::MaybeSave(CompileCacheEntry* entry, + std::string_view transpiled) { + CHECK(entry->type == CachedCodeType::kStrippedTypeScript || + entry->type == CachedCodeType::kTransformedTypeScript || + entry->type == CachedCodeType::kTransformedTypeScriptWithSourceMaps); + Debug("[compile cache] saving transpilation cache for %s %s\n", + entry->type_name(), + entry->source_filename); + + // TODO(joyeecheung): it's weird to copy it again here. Convert the v8::String + // directly into buffer held by v8::ScriptCompiler::CachedData here. + int cache_size = static_cast(transpiled.size()); + uint8_t* data = new uint8_t[cache_size]; + memcpy(data, transpiled.data(), cache_size); + entry->cache.reset(new v8::ScriptCompiler::CachedData( + data, cache_size, v8::ScriptCompiler::CachedData::BufferOwned)); + entry->refreshed = true; +} + /** * Persist the compile cache accumulated in memory to disk. * @@ -316,18 +353,25 @@ void CompileCacheHandler::Persist() { // incur a negligible overhead from thread synchronization. for (auto& pair : compiler_cache_store_) { auto* entry = pair.second.get(); + const char* type_name = entry->type_name(); if (entry->cache == nullptr) { - Debug("[compile cache] skip %s because the cache was not initialized\n", + Debug("[compile cache] skip persisting %s %s because the cache was not " + "initialized\n", + type_name, entry->source_filename); continue; } if (entry->refreshed == false) { - Debug("[compile cache] skip %s because cache was the same\n", - entry->source_filename); + Debug( + "[compile cache] skip persisting %s %s because cache was the same\n", + type_name, + entry->source_filename); continue; } if (entry->persisted == true) { - Debug("[compile cache] skip %s because cache was already persisted\n", + Debug("[compile cache] skip persisting %s %s because cache was already " + "persisted\n", + type_name, entry->source_filename); continue; } @@ -363,8 +407,9 @@ void CompileCacheHandler::Persist() { auto cleanup_mkstemp = OnScopeLeave([&mkstemp_req]() { uv_fs_req_cleanup(&mkstemp_req); }); std::string cache_filename_tmp = entry->cache_filename + ".XXXXXX"; - Debug("[compile cache] Creating temporary file for cache of %s...", - entry->source_filename); + Debug("[compile cache] Creating temporary file for cache of %s (%s)...", + entry->source_filename, + type_name); int err = uv_fs_mkstemp( nullptr, &mkstemp_req, cache_filename_tmp.c_str(), nullptr); if (err < 0) { @@ -372,8 +417,10 @@ void CompileCacheHandler::Persist() { continue; } Debug(" -> %s\n", mkstemp_req.path); - Debug("[compile cache] writing cache for %s to temporary file %s [%d %d %d " + Debug("[compile cache] writing cache for %s %s to temporary file %s [%d " + "%d %d " "%d %d]...", + type_name, entry->source_filename, mkstemp_req.path, headers[kMagicNumberOffset], diff --git a/src/compile_cache.h b/src/compile_cache.h index a7bb58c4a0be95..72910084e18bca 100644 --- a/src/compile_cache.h +++ b/src/compile_cache.h @@ -13,10 +13,17 @@ namespace node { class Environment; -// TODO(joyeecheung): move it into a CacheHandler class. +#define CACHED_CODE_TYPES(V) \ + V(kCommonJS, 0) \ + V(kESM, 1) \ + V(kStrippedTypeScript, 2) \ + V(kTransformedTypeScript, 3) \ + V(kTransformedTypeScriptWithSourceMaps, 4) + enum class CachedCodeType : uint8_t { - kCommonJS = 0, - kESM, +#define V(type, value) type = value, + CACHED_CODE_TYPES(V) +#undef V }; struct CompileCacheEntry { @@ -34,6 +41,7 @@ struct CompileCacheEntry { // Copy the cache into a new store for V8 to consume. Caller takes // ownership. v8::ScriptCompiler::CachedData* CopyCache() const; + const char* type_name() const; }; #define COMPILE_CACHE_STATUS(V) \ @@ -70,6 +78,7 @@ class CompileCacheHandler { void MaybeSave(CompileCacheEntry* entry, v8::Local mod, bool rejected); + void MaybeSave(CompileCacheEntry* entry, std::string_view transpiled); std::string_view cache_dir() { return compile_cache_dir_; } private: diff --git a/src/node_modules.cc b/src/node_modules.cc index 4b522a91323c9f..85c8e21cf026ff 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -1,6 +1,7 @@ #include "node_modules.h" #include #include "base_object-inl.h" +#include "compile_cache.h" #include "node_errors.h" #include "node_external_reference.h" #include "node_url.h" @@ -21,12 +22,16 @@ namespace modules { using v8::Array; using v8::Context; +using v8::External; using v8::FunctionCallbackInfo; using v8::HandleScope; +using v8::Integer; using v8::Isolate; using v8::Local; using v8::LocalVector; +using v8::Name; using v8::NewStringType; +using v8::Null; using v8::Object; using v8::ObjectTemplate; using v8::Primitive; @@ -498,6 +503,74 @@ void GetCompileCacheDir(const FunctionCallbackInfo& args) { .ToLocalChecked()); } +void GetCompileCacheEntry(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + CHECK(args[0]->IsString()); // TODO(joyeecheung): accept buffer. + CHECK(args[1]->IsString()); + CHECK(args[2]->IsUint32()); + Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); + if (!env->use_compile_cache()) { + return; + } + Local source = args[0].As(); + Local filename = args[1].As(); + CachedCodeType type = + static_cast(args[2].As()->Value()); + auto* cache_entry = + env->compile_cache_handler()->GetOrInsert(source, filename, type); + if (cache_entry == nullptr) { + return; + } + + v8::LocalVector names(isolate, + {FIXED_ONE_BYTE_STRING(isolate, "external")}); + v8::LocalVector values(isolate, + {v8::External::New(isolate, cache_entry)}); + if (cache_entry->cache != nullptr) { + Debug(env, + DebugCategory::COMPILE_CACHE, + "[compile cache] retrieving transpile cache for %s %s...", + cache_entry->type_name(), + cache_entry->source_filename); + + std::string_view cache( + reinterpret_cast(cache_entry->cache->data), + cache_entry->cache->length); + Local transpiled; + // TODO(joyeecheung): convert with simdutf and into external strings + if (!ToV8Value(context, cache).ToLocal(&transpiled)) { + Debug(env, DebugCategory::COMPILE_CACHE, "failed\n"); + return; + } else { + Debug(env, DebugCategory::COMPILE_CACHE, "success\n"); + } + names.push_back(FIXED_ONE_BYTE_STRING(isolate, "transpiled")); + values.push_back(transpiled); + } else { + Debug(env, + DebugCategory::COMPILE_CACHE, + "[compile cache] no transpile cache for %s %s\n", + cache_entry->type_name(), + cache_entry->source_filename); + } + args.GetReturnValue().Set(Object::New( + isolate, v8::Null(isolate), names.data(), values.data(), names.size())); +} + +void SaveCompileCacheEntry(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); + DCHECK(env->use_compile_cache()); + CHECK(args[0]->IsExternal()); + CHECK(args[1]->IsString()); // TODO(joyeecheung): accept buffer. + auto* cache_entry = + static_cast(args[0].As()->Value()); + Utf8Value utf8(isolate, args[1].As()); + env->compile_cache_handler()->MaybeSave(cache_entry, utf8.ToStringView()); +} + void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, Local target) { Isolate* isolate = isolate_data->isolate(); @@ -514,6 +587,8 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, SetMethod(isolate, target, "enableCompileCache", EnableCompileCache); SetMethod(isolate, target, "getCompileCacheDir", GetCompileCacheDir); SetMethod(isolate, target, "flushCompileCache", FlushCompileCache); + SetMethod(isolate, target, "getCompileCacheEntry", GetCompileCacheEntry); + SetMethod(isolate, target, "saveCompileCacheEntry", SaveCompileCacheEntry); } void BindingData::CreatePerContextProperties(Local target, @@ -530,12 +605,31 @@ void BindingData::CreatePerContextProperties(Local target, compile_cache_status_values.push_back( \ FIXED_ONE_BYTE_STRING(isolate, #status)); COMPILE_CACHE_STATUS(V) +#undef V USE(target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "compileCacheStatus"), Array::New(isolate, compile_cache_status_values.data(), compile_cache_status_values.size()))); + + LocalVector cached_code_type_keys(isolate); + LocalVector cached_code_type_values(isolate); + +#define V(type, value) \ + cached_code_type_keys.push_back(FIXED_ONE_BYTE_STRING(isolate, #type)); \ + cached_code_type_values.push_back(Integer::New(isolate, value)); \ + DCHECK_EQ(value, cached_code_type_values.size() - 1); + CACHED_CODE_TYPES(V) +#undef V + + USE(target->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "cachedCodeTypes"), + Object::New(isolate, + Null(isolate), + cached_code_type_keys.data(), + cached_code_type_values.data(), + cached_code_type_keys.size()))); } void BindingData::RegisterExternalReferences( @@ -547,6 +641,8 @@ void BindingData::RegisterExternalReferences( registry->Register(EnableCompileCache); registry->Register(GetCompileCacheDir); registry->Register(FlushCompileCache); + registry->Register(GetCompileCacheEntry); + registry->Register(SaveCompileCacheEntry); } } // namespace modules diff --git a/test/parallel/test-compile-cache-typescript-commonjs.js b/test/parallel/test-compile-cache-typescript-commonjs.js new file mode 100644 index 00000000000000..b6c4581ed47be3 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-commonjs.js @@ -0,0 +1,166 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE works for CommonJS with types. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + +// Check cache for .ts files that would be run as CommonJS. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'ts', 'test-commonjs-parsing.ts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for StrippedTypeScript .*test-commonjs-parsing\.ts/); + assert.match(output, /writing cache for StrippedTypeScript .*test-commonjs-parsing\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-commonjs-parsing\.ts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-commonjs-parsing\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-commonjs-parsing\.ts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-commonjs-parsing\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-commonjs-parsing\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-commonjs-parsing\.ts because cache was the same/); + return true; + } + }); +} + +// Check cache for .cts files that require .cts files. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'cts', 'test-require-commonjs.cts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /writing cache for StrippedTypeScript .*test-require-commonjs\.cts.*success/); + assert.match(output, /writing cache for StrippedTypeScript .*test-cts-export-foo\.cts.*success/); + assert.match(output, /writing cache for CommonJS .*test-require-commonjs\.cts.*success/); + assert.match(output, /writing cache for CommonJS .*test-cts-export-foo\.cts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-require-commonjs\.cts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-require-commonjs\.cts because cache was the same/); + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-cts-export-foo\.cts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-cts-export-foo\.cts because cache was the same/); + + assert.match(output, /V8 code cache for CommonJS .*test-require-commonjs\.cts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-require-commonjs\.cts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-cts-export-foo\.cts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-cts-export-foo\.cts because cache was the same/); + return true; + } + }); +} + +// Check cache for .cts files that require .mts files. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'cts', 'test-require-mts-module.cts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /writing cache for StrippedTypeScript .*test-require-mts-module\.cts.*success/); + assert.match(output, /writing cache for StrippedTypeScript .*test-mts-export-foo\.mts.*success/); + assert.match(output, /writing cache for CommonJS .*test-require-mts-module\.cts.*success/); + assert.match(output, /writing cache for ESM .*test-mts-export-foo\.mts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-require-mts-module\.cts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-require-mts-module\.cts because cache was the same/); + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-mts-export-foo\.mts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-mts-export-foo\.mts because cache was the same/); + + assert.match(output, /V8 code cache for CommonJS .*test-require-mts-module\.cts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-require-mts-module\.cts because cache was the same/); + assert.match(output, /V8 code cache for ESM .*test-mts-export-foo\.mts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-mts-export-foo\.mts because cache was the same/); + return true; + } + }); +} diff --git a/test/parallel/test-compile-cache-typescript-esm.js b/test/parallel/test-compile-cache-typescript-esm.js new file mode 100644 index 00000000000000..cec7b814da6679 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-esm.js @@ -0,0 +1,167 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE works for ESM with types. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + +// Check cache for .ts files that would be run as ESM. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'ts', 'test-module-typescript.ts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for StrippedTypeScript .*test-module-typescript\.ts/); + assert.match(output, /writing cache for StrippedTypeScript .*test-module-typescript\.ts.*success/); + assert.match(output, /writing cache for ESM .*test-module-typescript\.ts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-module-typescript\.ts.*success/); + assert.match(output, /reading cache from .* for ESM .*test-module-typescript\.ts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-module-typescript\.ts because cache was the same/); + assert.match(output, /V8 code cache for ESM .*test-module-typescript\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-module-typescript\.ts because cache was the same/); + return true; + } + }); +} + +// Check cache for .mts files that import .mts files. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'mts', 'test-import-module.mts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /writing cache for StrippedTypeScript .*test-import-module\.mts.*success/); + assert.match(output, /writing cache for StrippedTypeScript .*test-mts-export-foo\.mts.*success/); + assert.match(output, /writing cache for ESM .*test-import-module\.mts.*success/); + assert.match(output, /writing cache for ESM .*test-mts-export-foo\.mts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-import-module\.mts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-import-module\.mts because cache was the same/); + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-mts-export-foo\.mts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-mts-export-foo\.mts because cache was the same/); + + assert.match(output, /V8 code cache for ESM .*test-import-module\.mts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-import-module\.mts because cache was the same/); + assert.match(output, /V8 code cache for ESM .*test-mts-export-foo\.mts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-mts-export-foo\.mts because cache was the same/); + return true; + } + }); +} + + +// Check cache for .mts files that import .cts files. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'mts', 'test-import-commonjs.mts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /writing cache for StrippedTypeScript .*test-import-commonjs\.mts.*success/); + assert.match(output, /writing cache for StrippedTypeScript .*test-cts-export-foo\.cts.*success/); + assert.match(output, /writing cache for ESM .*test-import-commonjs\.mts.*success/); + assert.match(output, /writing cache for CommonJS .*test-cts-export-foo\.cts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-import-commonjs\.mts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-import-commonjs\.mts because cache was the same/); + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-cts-export-foo\.cts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-cts-export-foo\.cts because cache was the same/); + + assert.match(output, /V8 code cache for ESM .*test-import-commonjs\.mts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-import-commonjs\.mts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-cts-export-foo\.cts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-cts-export-foo\.cts because cache was the same/); + return true; + } + }); +} diff --git a/test/parallel/test-compile-cache-typescript-strip-miss.js b/test/parallel/test-compile-cache-typescript-strip-miss.js new file mode 100644 index 00000000000000..5d37a377f002e4 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-strip-miss.js @@ -0,0 +1,104 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE can handle cache invalidation +// between strip-only TypeScript and transformed TypeScript. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + +tmpdir.refresh(); +const dir = tmpdir.resolve('.compile_cache_dir'); +const script = fixtures.path('typescript', 'ts', 'test-typescript.ts'); + +spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for StrippedTypeScript .*test-typescript\.ts/); + assert.match(output, /writing cache for StrippedTypeScript .*test-typescript\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-typescript\.ts.*success/); + return true; + } + }); + +// Reloading with transform should miss the cache generated without transform. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + // Both the transpile cache and the code cache should be missed. + assert.match(output, /no transpile cache for TransformedTypeScriptWithSourceMaps .*test-typescript\.ts/); + assert.match(output, /reading cache from .* for CommonJS .*test-typescript\.ts.*mismatch/); + // New cache with source map should be generated. + assert.match(output, /writing cache for TransformedTypeScriptWithSourceMaps .*test-typescript\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-typescript\.ts.*success/); + return true; + } + }); + +// Reloading with transform should hit the cache generated with transform. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for TransformedTypeScriptWithSourceMaps .*test-typescript\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-typescript\.ts.*success/); + assert.match(output, /skip persisting TransformedTypeScriptWithSourceMaps .*test-typescript\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-typescript\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-typescript\.ts because cache was the same/); + return true; + } + }); + +// Reloading without transform should hit the co-existing transpile cache generated without transform, +// but miss the code cache generated with transform. +spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-typescript\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-typescript\.ts.*mismatch/); + assert.match(output, /skip persisting StrippedTypeScript .*test-typescript\.ts because cache was the same/); + assert.match(output, /writing cache for CommonJS .*test-typescript\.ts.*success/); + return true; + } + }); diff --git a/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js new file mode 100644 index 00000000000000..da5e350496f005 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js @@ -0,0 +1,59 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE can be used for type stripping and ignores +// --enable-source-maps as there's no difference in the code generated. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + +tmpdir.refresh(); +const dir = tmpdir.resolve('.compile_cache_dir'); +const script = fixtures.path('typescript', 'ts', 'test-typescript.ts'); + +spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for StrippedTypeScript .*test-typescript\.ts/); + assert.match(output, /writing cache for StrippedTypeScript .*test-typescript\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-typescript\.ts.*success/); + return true; + } + }); + +// Reloading with source maps should hit the cache generated without source maps, because for +// type stripping, only sourceURL is added regardless of whether source map is enabled. +spawnSyncAndAssert( + process.execPath, + ['--enable-source-maps', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + // Both the transpile cache and the code cache should be missed. + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-typescript\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-typescript\.ts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-typescript\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-typescript\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-typescript\.ts because cache was the same/); + return true; + } + }); diff --git a/test/parallel/test-compile-cache-typescript-transform.js b/test/parallel/test-compile-cache-typescript-transform.js new file mode 100644 index 00000000000000..41eb67b203baa1 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-transform.js @@ -0,0 +1,127 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE works with --experimental-transform-types. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + + +tmpdir.refresh(); +const dir = tmpdir.resolve('.compile_cache_dir'); +const script = fixtures.path('typescript', 'ts', 'transformation', 'test-enum.ts'); + +// Check --experimental-transform-types which enables source maps by default. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for TransformedTypeScriptWithSourceMaps .*test-enum\.ts/); + assert.match(output, /writing cache for TransformedTypeScriptWithSourceMaps .*test-enum\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-enum\.ts.*success/); + return true; + } + }); + +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for TransformedTypeScriptWithSourceMaps .*test-enum\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-enum\.ts.*success/); + assert.match(output, /skip persisting TransformedTypeScriptWithSourceMaps .*test-enum\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-enum\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-enum\.ts because cache was the same/); + return true; + } + }); + +// Reloading without source maps should miss the cache generated with source maps. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', '--no-enable-source-maps', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + // Both the transpile cache and the code cache should be missed. + assert.match(output, /no transpile cache for TransformedTypeScript .*test-enum\.ts/); + assert.match(output, /reading cache from .* for CommonJS .*test-enum\.ts.*mismatch/); + // New cache without source map should be generated. + assert.match(output, /writing cache for TransformedTypeScript .*test-enum\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-enum\.ts.*success/); + return true; + } + }); + +// Reloading without source maps again should hit the cache generated without source maps. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', '--no-enable-source-maps', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for TransformedTypeScript .*test-enum\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-enum\.ts.*success/); + assert.match(output, /skip persisting TransformedTypeScript .*test-enum\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-enum\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-enum\.ts because cache was the same/); + return true; + } + }); + +// Reloading with source maps again should hit the co-existing transpile cache with source +// maps, but miss the code cache generated without source maps. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for TransformedTypeScriptWithSourceMaps .*test-enum\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-enum\.ts.*mismatch/); + assert.match(output, /skip persisting TransformedTypeScriptWithSourceMaps .*test-enum\.ts because cache was the same/); + assert.match(output, /writing cache for CommonJS .*test-enum\.ts.*success/); + return true; + } + }); From 0713ee3a17b06654fa80321bf53eb4f19a49413c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 13:05:54 -0800 Subject: [PATCH 117/208] test: simplify common/index.js Move single or trivial and limited use things out of common/index.js for the purpose of simplifying and reducing common/index.js PR-URL: https://github.com/nodejs/node/pull/56712 Reviewed-By: Yagiz Nizipli Reviewed-By: Matteo Collina --- test/common/README.md | 6 ------ test/common/index.js | 12 ------------ test/common/index.mjs | 4 ---- test/parallel/test-source-map-enable.js | 3 ++- test/tick-processor/util.js | 11 +++++++++-- test/wasi/test-wasi-io.js | 11 ++++++++--- 6 files changed, 19 insertions(+), 28 deletions(-) diff --git a/test/common/README.md b/test/common/README.md index 9ecee39b64a3df..c3c44e32b3788c 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -279,12 +279,6 @@ Platform check for IBMi. Platform check for Linux. -### `isLinuxPPCBE` - -* [\][] - -Platform check for Linux on PowerPC. - ### `isMacOS` * [\][] diff --git a/test/common/index.js b/test/common/index.js index e8bf65d0a6edb4..238d66e96fe257 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -1034,11 +1034,6 @@ const common = { return require('os').type() === 'OS400'; }, - get isLinuxPPCBE() { - return (process.platform === 'linux') && (process.arch === 'ppc64') && - (require('os').endianness() === 'BE'); - }, - get localhostIPv4() { if (localhostIPv4 !== null) return localhostIPv4; @@ -1067,13 +1062,6 @@ const common = { return +process.env.NODE_COMMON_PORT || 12346; }, - /** - * Returns the EOL character used by this Git checkout. - */ - get checkoutEOL() { - return fs.readFileSync(__filename).includes('\r\n') ? '\r\n' : '\n'; - }, - get isInsideDirWithUnusualChars() { return __dirname.includes('%') || (!isWindows && __dirname.includes('\\')) || diff --git a/test/common/index.mjs b/test/common/index.mjs index 090659f93be8ef..aafef1453bd78a 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -7,7 +7,6 @@ const { allowGlobals, buildType, canCreateSymLink, - checkoutEOL, childShouldThrowAndAbort, createZeroFilledFile, enoughTestMem, @@ -27,7 +26,6 @@ const { isIBMi, isInsideDirWithUnusualChars, isLinux, - isLinuxPPCBE, isMainThread, isOpenBSD, isMacOS, @@ -59,7 +57,6 @@ export { allowGlobals, buildType, canCreateSymLink, - checkoutEOL, childShouldThrowAndAbort, createRequire, createZeroFilledFile, @@ -81,7 +78,6 @@ export { isIBMi, isInsideDirWithUnusualChars, isLinux, - isLinuxPPCBE, isMainThread, isOpenBSD, isMacOS, diff --git a/test/parallel/test-source-map-enable.js b/test/parallel/test-source-map-enable.js index 46c25d26cfa8e7..64f4254fcddbc6 100644 --- a/test/parallel/test-source-map-enable.js +++ b/test/parallel/test-source-map-enable.js @@ -242,6 +242,7 @@ function nextdir() { // Persists line lengths for in-memory representation of source file. { + const checkoutEOL = fs.readFileSync(__filename).includes('\r\n') ? '\r\n' : '\n'; const coverageDirectory = nextdir(); spawnSync(process.execPath, [ require.resolve('../fixtures/source-map/istanbul-throw.js'), @@ -250,7 +251,7 @@ function nextdir() { 'istanbul-throw.js', coverageDirectory ); - if (common.checkoutEOL === '\r\n') { + if (checkoutEOL === '\r\n') { assert.deepStrictEqual(sourceMap.lineLengths, [1086, 31, 185, 649, 0]); } else { assert.deepStrictEqual(sourceMap.lineLengths, [1085, 30, 184, 648, 0]); diff --git a/test/tick-processor/util.js b/test/tick-processor/util.js index 6d118b7c38bc66..9586a81c276a65 100644 --- a/test/tick-processor/util.js +++ b/test/tick-processor/util.js @@ -5,14 +5,21 @@ const { isWindows, isSunOS, isAIX, - isLinuxPPCBE, isFreeBSD, } = require('../common'); +const { endianness } = require('os'); + +function isLinuxPPCBE() { + return (process.platform === 'linux') && + (process.arch === 'ppc64') && + (endianness() === 'BE'); +} + module.exports = { isCPPSymbolsNotMapped: isWindows || isSunOS || isAIX || - isLinuxPPCBE || + isLinuxPPCBE() || isFreeBSD, }; diff --git a/test/wasi/test-wasi-io.js b/test/wasi/test-wasi-io.js index 061ac88a73ece4..f5348644f1cfbf 100644 --- a/test/wasi/test-wasi-io.js +++ b/test/wasi/test-wasi-io.js @@ -1,14 +1,19 @@ 'use strict'; -const common = require('../common'); -const { checkoutEOL } = common; +require('../common'); +const { readFileSync } = require('fs'); const { testWasiPreview1 } = require('../common/wasi'); +const checkoutEOL = readFileSync(__filename).includes('\r\n') ? '\r\n' : '\n'; + +// TODO(@jasnell): It's not entirely clear what this test is asserting. +// More comments would be helpful. + testWasiPreview1(['freopen'], {}, { stdout: `hello from input2.txt${checkoutEOL}` }); testWasiPreview1(['read_file'], {}, { stdout: `hello from input.txt${checkoutEOL}` }); testWasiPreview1(['read_file_twice'], {}, { stdout: `hello from input.txt${checkoutEOL}hello from input.txt${checkoutEOL}`, }); // Tests that are currently unsupported on Windows. -if (!common.isWindows) { +if (process.platform !== 'win32') { testWasiPreview1(['stdin'], { input: 'hello world' }, { stdout: 'hello world' }); } From 8caa1dcee63b2c6fd7a9edf9b9a6222b38a2cf62 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 14:19:38 -0800 Subject: [PATCH 118/208] test: rely less on duplicative common test harness utilities There are several cleanups here that are not just style nits... 1. The `common.isMainThread` was just a passthrough to the `isMainThread` export on the worker_thread module. It's use was inconsistent and just obfuscated the fact that the test file depend on the `worker_threads` built-in. By eliminating it we simplify the test harness a bit and make it clearer which tests depend on the worker_threads check. 2. The `common.isDumbTerminal` is fairly unnecesary since that just wraps a public API check. 3. Several of the `common.skipIf....` checks were inconsistently used and really don't need to be separate utility functions. A key part of the motivation here is to work towards making more of the tests more self-contained and less reliant on the common test harness where possible. PR-URL: https://github.com/nodejs/node/pull/56712 Reviewed-By: Yagiz Nizipli Reviewed-By: Matteo Collina --- test/abort/test-abort-backtrace.js | 45 ++++++++++++- test/async-hooks/init-hooks.js | 5 +- test/async-hooks/test-crypto-pbkdf2.js | 7 +- test/async-hooks/test-crypto-randomBytes.js | 7 +- test/async-hooks/test-enable-disable.js | 3 +- test/async-hooks/test-fseventwrap.js | 7 +- .../test-fsreqcallback-readFile.js | 4 +- test/async-hooks/test-getaddrinforeqwrap.js | 4 +- test/async-hooks/test-getnameinforeqwrap.js | 4 +- test/async-hooks/test-graph.signal.js | 7 +- .../test-no-assert-when-disabled.js | 5 +- test/async-hooks/test-pipewrap.js | 4 +- ...promise.chain-promise-before-init-hooks.js | 4 +- test/async-hooks/test-promise.js | 4 +- test/async-hooks/test-signalwrap.js | 7 +- test/async-hooks/test-statwatcher.js | 5 +- .../test-unhandled-rejection-context.js | 4 +- test/benchmark/test-benchmark-napi.js | 4 +- test/common/README.md | 17 ----- test/common/index.js | 66 ------------------- test/common/index.mjs | 8 --- test/es-module/test-esm-resolve-type.mjs | 4 +- .../test-vm-main-context-default-loader.js | 6 +- test/fixtures/permission/fs-write.js | 8 ++- test/fixtures/permission/processbinding.js | 6 +- test/internet/test-trace-events-dns.js | 4 +- ...test-async-hooks-disable-during-promise.js | 4 +- .../test-async-hooks-promise-triggerid.js | 4 +- test/parallel/test-async-hooks-promise.js | 4 +- ...st-async-hooks-top-level-clearimmediate.js | 4 +- .../test-async-wrap-promise-after-enabled.js | 4 +- test/parallel/test-bootstrap-modules.js | 10 +-- .../test-child-process-validate-stdio.js | 4 +- .../test-cluster-net-listen-relative-path.js | 9 ++- test/parallel/test-code-cache.js | 3 +- test/parallel/test-console-clear.js | 4 +- test/parallel/test-console.js | 4 +- test/parallel/test-crypto-no-algorithm.js | 3 +- test/parallel/test-cwd-enoent-preload.js | 9 ++- test/parallel/test-cwd-enoent-repl.js | 9 ++- test/parallel/test-cwd-enoent.js | 9 ++- test/parallel/test-fs-mkdir.js | 3 +- test/parallel/test-fs-realpath.js | 4 +- test/parallel/test-fs-whatwg-url.js | 4 +- test/parallel/test-fs-write-file-sync.js | 4 +- test/parallel/test-http-chunk-problem.js | 11 +++- test/parallel/test-icu-env.js | 4 +- .../test-inspector-already-activated-cli.js | 7 +- .../test-inspector-async-hook-after-done.js | 7 +- ...st-inspector-async-hook-setup-at-signal.js | 6 +- .../test-inspector-connect-main-thread.js | 4 +- .../test-inspector-connect-to-main-thread.js | 4 +- test/parallel/test-inspector-contexts.js | 4 +- ...ctor-exit-worker-in-wait-for-connection.js | 12 +++- ...tor-exit-worker-in-wait-for-connection2.js | 6 +- test/parallel/test-inspector-open-coverage.js | 7 +- ...st-inspector-open-port-integer-overflow.js | 7 +- .../test-inspector-overwrite-config.js | 4 +- .../test-inspector-port-zero-cluster.js | 7 +- .../parallel/test-inspector-tracing-domain.js | 8 ++- .../test-inspector-workers-flat-list.js | 4 +- test/parallel/test-internal-module-require.js | 3 +- ...st-performance-nodetiming-uvmetricsinfo.js | 6 +- .../test-permission-allow-addons-cli.js | 6 +- ...test-permission-allow-child-process-cli.js | 8 ++- .../test-permission-allow-wasi-cli.js | 6 +- .../test-permission-child-process-cli.js | 7 +- .../test-permission-fs-absolute-path.js | 6 +- ...test-permission-fs-internal-module-stat.js | 6 +- test/parallel/test-permission-fs-read.js | 6 +- .../test-permission-fs-relative-path.js | 6 +- .../test-permission-fs-repeat-path.js | 6 +- test/parallel/test-permission-fs-require.js | 8 ++- .../test-permission-fs-symlink-relative.js | 7 +- ...test-permission-fs-symlink-target-write.js | 18 +++-- test/parallel/test-permission-fs-symlink.js | 12 +++- .../test-permission-fs-traversal-path.js | 13 +++- test/parallel/test-permission-fs-wildcard.js | 6 +- .../test-permission-fs-windows-path.js | 6 +- .../test-permission-fs-write-report.js | 10 ++- test/parallel/test-permission-fs-write-v8.js | 10 ++- test/parallel/test-permission-fs-write.js | 10 ++- .../parallel/test-permission-inspector-brk.js | 6 +- test/parallel/test-permission-inspector.js | 7 +- test/parallel/test-permission-no-addons.js | 6 +- .../test-permission-processbinding.js | 6 +- .../test-permission-worker-threads-cli.js | 8 ++- test/parallel/test-pipe-file-to-http.js | 7 +- .../parallel/test-preload-self-referential.js | 4 +- test/parallel/test-process-abort.js | 4 +- .../test-process-beforeexit-throw-exit.js | 6 +- .../test-process-chdir-errormessage.js | 5 +- test/parallel/test-process-chdir.js | 4 +- test/parallel/test-process-env-tz.js | 7 +- test/parallel/test-process-euid-egid.js | 5 +- test/parallel/test-process-exit-handler.js | 4 +- test/parallel/test-process-get-builtin.mjs | 3 +- test/parallel/test-process-initgroups.js | 5 +- test/parallel/test-process-load-env-file.js | 5 +- test/parallel/test-process-setgroups.js | 4 +- test/parallel/test-process-uid-gid.js | 4 +- test/parallel/test-process-umask-mask.js | 3 +- test/parallel/test-process-umask.js | 3 +- ...-readline-interface-no-trailing-newline.js | 4 +- ...est-readline-interface-recursive-writes.js | 4 +- test/parallel/test-readline-interface.js | 5 +- test/parallel/test-readline-position.js | 4 +- .../test-readline-promises-interface.js | 5 +- .../test-readline-promises-tab-complete.js | 4 +- test/parallel/test-readline-tab-complete.js | 4 +- .../test-readline-undefined-columns.js | 4 +- test/parallel/test-readline.js | 4 +- test/parallel/test-repl-autocomplete.js | 4 +- test/parallel/test-repl-editor.js | 4 +- test/parallel/test-repl-history-navigation.js | 4 +- ...repl-load-multiline-no-trailing-newline.js | 4 +- test/parallel/test-repl-load-multiline.js | 4 +- test/parallel/test-repl-mode.js | 4 +- test/parallel/test-repl-permission-model.js | 4 +- test/parallel/test-repl-persistent-history.js | 4 +- .../test-repl-programmatic-history.js | 4 +- .../test-repl-require-self-referential.js | 4 +- test/parallel/test-repl-require.js | 4 +- test/parallel/test-repl-reverse-search.js | 5 +- test/parallel/test-repl-sigint-nested-eval.js | 6 +- test/parallel/test-repl-sigint.js | 6 +- .../test-repl-strict-mode-previews.js | 5 +- .../parallel/test-repl-tab-complete-import.js | 5 +- test/parallel/test-repl-tab-complete.js | 4 +- test/parallel/test-require-symlink.js | 8 ++- test/parallel/test-runner-module-mocking.js | 3 +- test/parallel/test-set-process-debug-port.js | 6 +- test/parallel/test-setproctitle.js | 14 ++-- .../test-shadow-realm-import-value-resolve.js | 5 +- test/parallel/test-signal-args.js | 9 ++- test/parallel/test-signal-handler.js | 9 ++- test/parallel/test-stdio-pipe-access.js | 5 +- test/parallel/test-stdio-pipe-redirect.js | 5 +- .../test-timers-immediate-unref-simple.js | 3 +- test/parallel/test-trace-events-api.js | 7 +- .../test-trace-events-dynamic-enable.js | 8 ++- test/parallel/test-warn-sigprof.js | 9 ++- test/parallel/test-worker-name.js | 11 +++- test/report/test-report-signal.js | 8 ++- test/sequential/test-fs-watch.js | 4 +- test/sequential/test-heapdump.js | 4 +- test/sequential/test-init.js | 4 +- test/sequential/test-perf-hooks.js | 5 +- 148 files changed, 672 insertions(+), 290 deletions(-) diff --git a/test/abort/test-abort-backtrace.js b/test/abort/test-abort-backtrace.js index ce9ed39196eb1f..455bbf2361cf51 100644 --- a/test/abort/test-abort-backtrace.js +++ b/test/abort/test-abort-backtrace.js @@ -1,8 +1,47 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const cp = require('child_process'); +function getPrintedStackTrace(stderr) { + const lines = stderr.split('\n'); + + let state = 'initial'; + const result = { + message: [], + nativeStack: [], + jsStack: [], + }; + for (let i = 0; i < lines.length; ++i) { + const line = lines[i].trim(); + if (line.length === 0) { + continue; // Skip empty lines. + } + + switch (state) { + case 'initial': + result.message.push(line); + if (line.includes('Native stack trace')) { + state = 'native-stack'; + } else { + result.message.push(line); + } + break; + case 'native-stack': + if (line.includes('JavaScript stack trace')) { + state = 'js-stack'; + } else { + result.nativeStack.push(line); + } + break; + case 'js-stack': + result.jsStack.push(line); + break; + } + } + return result; +} + if (process.argv[2] === 'child') { process.abort(); } else { @@ -10,7 +49,7 @@ if (process.argv[2] === 'child') { const stderr = child.stderr.toString(); assert.strictEqual(child.stdout.toString(), ''); - const { nativeStack, jsStack } = common.getPrintedStackTrace(stderr); + const { nativeStack, jsStack } = getPrintedStackTrace(stderr); if (!nativeStack.every((frame, index) => frame.startsWith(`${index + 1}:`))) { assert.fail(`Each frame should start with a frame number:\n${stderr}`); @@ -18,7 +57,7 @@ if (process.argv[2] === 'child') { // For systems that don't support backtraces, the native stack is // going to be empty. - if (!common.isWindows && nativeStack.length > 0) { + if (process.platform !== 'win32' && nativeStack.length > 0) { const { getBinaryPath } = require('../common/shared-lib-util'); if (!nativeStack.some((frame) => frame.includes(`[${getBinaryPath()}]`))) { assert.fail(`Some native stack frame include the binary name:\n${stderr}`); diff --git a/test/async-hooks/init-hooks.js b/test/async-hooks/init-hooks.js index 2206ab31eba75f..8fc44994fbc497 100644 --- a/test/async-hooks/init-hooks.js +++ b/test/async-hooks/init-hooks.js @@ -1,9 +1,10 @@ 'use strict'; // Flags: --expose-gc -const common = require('../common'); +require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); const util = require('util'); const print = process._rawDebug; @@ -161,7 +162,7 @@ class ActivityCollector { const stub = { uid, type: 'Unknown', handleIsObject: true, handle: {} }; this._activities.set(uid, stub); return stub; - } else if (!common.isMainThread) { + } else if (!isMainThread) { // Worker threads start main script execution inside of an AsyncWrap // callback, so we don't yield errors for these. return null; diff --git a/test/async-hooks/test-crypto-pbkdf2.js b/test/async-hooks/test-crypto-pbkdf2.js index 4788ce4a580656..c607adf7258760 100644 --- a/test/async-hooks/test-crypto-pbkdf2.js +++ b/test/async-hooks/test-crypto-pbkdf2.js @@ -1,10 +1,13 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const assert = require('assert'); const tick = require('../common/tick'); diff --git a/test/async-hooks/test-crypto-randomBytes.js b/test/async-hooks/test-crypto-randomBytes.js index 88cd4643ab6638..8ecc1c45a9a524 100644 --- a/test/async-hooks/test-crypto-randomBytes.js +++ b/test/async-hooks/test-crypto-randomBytes.js @@ -1,10 +1,13 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const assert = require('assert'); const tick = require('../common/tick'); diff --git a/test/async-hooks/test-enable-disable.js b/test/async-hooks/test-enable-disable.js index 64139408a48209..d408338e892c32 100644 --- a/test/async-hooks/test-enable-disable.js +++ b/test/async-hooks/test-enable-disable.js @@ -87,8 +87,9 @@ const assert = require('assert'); const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) common.skip('Worker bootstrapping works differently -> different timing'); // Include "Unknown"s because hook2 will not be able to identify diff --git a/test/async-hooks/test-fseventwrap.js b/test/async-hooks/test-fseventwrap.js index 12a439f8033cbc..a5e1a3b9d2f232 100644 --- a/test/async-hooks/test-fseventwrap.js +++ b/test/async-hooks/test-fseventwrap.js @@ -6,12 +6,15 @@ const initHooks = require('./init-hooks'); const tick = require('../common/tick'); const { checkInvocations } = require('./hook-checks'); const fs = require('fs'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} -if (common.isIBMi) +if (common.isIBMi) { common.skip('IBMi does not support fs.watch()'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-fsreqcallback-readFile.js b/test/async-hooks/test-fsreqcallback-readFile.js index 01ccce9b4cc694..65f3652f12f988 100644 --- a/test/async-hooks/test-fsreqcallback-readFile.js +++ b/test/async-hooks/test-fsreqcallback-readFile.js @@ -6,9 +6,11 @@ const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const fs = require('fs'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-getaddrinforeqwrap.js b/test/async-hooks/test-getaddrinforeqwrap.js index 7291ea8a301954..a21557bcd56e7a 100644 --- a/test/async-hooks/test-getaddrinforeqwrap.js +++ b/test/async-hooks/test-getaddrinforeqwrap.js @@ -6,9 +6,11 @@ const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const dns = require('dns'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-getnameinforeqwrap.js b/test/async-hooks/test-getnameinforeqwrap.js index c7a3937ff3ceef..b00fa0d4d9dd54 100644 --- a/test/async-hooks/test-getnameinforeqwrap.js +++ b/test/async-hooks/test-getnameinforeqwrap.js @@ -6,9 +6,11 @@ const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const dns = require('dns'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-graph.signal.js b/test/async-hooks/test-graph.signal.js index f87b1215b523b9..351fb7550af431 100644 --- a/test/async-hooks/test-graph.signal.js +++ b/test/async-hooks/test-graph.signal.js @@ -1,10 +1,13 @@ 'use strict'; const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { common.skip('no signals on Windows'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} const initHooks = require('./init-hooks'); const verifyGraph = require('./verify-graph'); diff --git a/test/async-hooks/test-no-assert-when-disabled.js b/test/async-hooks/test-no-assert-when-disabled.js index 70114d1e1140f8..0e7c0568cc09fa 100644 --- a/test/async-hooks/test-no-assert-when-disabled.js +++ b/test/async-hooks/test-no-assert-when-disabled.js @@ -1,9 +1,10 @@ 'use strict'; // Flags: --no-force-async-hooks-checks --expose-internals const common = require('../common'); - -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('Workers don\'t inherit per-env state like the check flag'); +} const async_hooks = require('internal/async_hooks'); diff --git a/test/async-hooks/test-pipewrap.js b/test/async-hooks/test-pipewrap.js index 2d42e769cfd1f3..7ea5f38adc85e2 100644 --- a/test/async-hooks/test-pipewrap.js +++ b/test/async-hooks/test-pipewrap.js @@ -9,9 +9,11 @@ const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const { spawn } = require('child_process'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-promise.chain-promise-before-init-hooks.js b/test/async-hooks/test-promise.chain-promise-before-init-hooks.js index 52a312dbdfe196..c5e67b6f94ca68 100644 --- a/test/async-hooks/test-promise.chain-promise-before-init-hooks.js +++ b/test/async-hooks/test-promise.chain-promise-before-init-hooks.js @@ -4,9 +4,11 @@ const common = require('../common'); const assert = require('assert'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const p = new Promise(common.mustCall(function executor(resolve) { resolve(5); diff --git a/test/async-hooks/test-promise.js b/test/async-hooks/test-promise.js index 417cb3c80d6298..554c3ae7dd711e 100644 --- a/test/async-hooks/test-promise.js +++ b/test/async-hooks/test-promise.js @@ -5,9 +5,11 @@ const common = require('../common'); const assert = require('assert'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-signalwrap.js b/test/async-hooks/test-signalwrap.js index 4584d140ce1d0f..60adaedd476f27 100644 --- a/test/async-hooks/test-signalwrap.js +++ b/test/async-hooks/test-signalwrap.js @@ -1,10 +1,13 @@ 'use strict'; const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { common.skip('no signals in Windows'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} const assert = require('assert'); const initHooks = require('./init-hooks'); diff --git a/test/async-hooks/test-statwatcher.js b/test/async-hooks/test-statwatcher.js index f3c0e74355eeba..8f4fb2175885f3 100644 --- a/test/async-hooks/test-statwatcher.js +++ b/test/async-hooks/test-statwatcher.js @@ -7,8 +7,11 @@ const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const fs = require('fs'); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} tmpdir.refresh(); diff --git a/test/async-hooks/test-unhandled-rejection-context.js b/test/async-hooks/test-unhandled-rejection-context.js index 8404cf71f0db6f..168b51a3331f7f 100644 --- a/test/async-hooks/test-unhandled-rejection-context.js +++ b/test/async-hooks/test-unhandled-rejection-context.js @@ -5,9 +5,11 @@ const common = require('../common'); const assert = require('assert'); const initHooks = require('./init-hooks'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const promiseAsyncIds = []; const hooks = initHooks({ diff --git a/test/benchmark/test-benchmark-napi.js b/test/benchmark/test-benchmark-napi.js index 7164efe3d4e718..518e10a5111a5b 100644 --- a/test/benchmark/test-benchmark-napi.js +++ b/test/benchmark/test-benchmark-napi.js @@ -6,7 +6,9 @@ if (common.isWindows) { common.skip('vcbuild.bat doesn\'t build the n-api benchmarks yet'); } -if (!common.isMainThread) { +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('addons are not supported in workers'); } diff --git a/test/common/README.md b/test/common/README.md index c3c44e32b3788c..887dee2783ad72 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -102,10 +102,6 @@ symlinks ([SeCreateSymbolicLinkPrivilege](https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716\(v=vs.85\).aspx)). On non-Windows platforms, this always returns `true`. -### `createZeroFilledFile(filename)` - -Creates a 10 MiB file of all null characters. - ### `enoughTestMem` * [\][] @@ -257,10 +253,6 @@ Platform check for Advanced Interactive eXecutive (AIX). Attempts to 'kill' `pid` -### `isDumbTerminal` - -* [\][] - ### `isFreeBSD` * [\][] @@ -456,10 +448,6 @@ will not be run. Logs '1..0 # Skipped: ' + `msg` and exits with exit code `0`. -### `skipIfDumbTerminal()` - -Skip the rest of the tests if the current terminal is a dumb terminal - ### `skipIfEslintMissing()` Skip the rest of the tests in the current file when `ESLint` is not available @@ -475,11 +463,6 @@ was disabled at compile time. Skip the rest of the tests in the current file when the Node.js executable was compiled with a pointer size smaller than 64 bits. -### `skipIfWorker()` - -Skip the rest of the tests in the current file when not running on a main -thread. - ## ArrayStream module The `ArrayStream` module provides a simple `Stream` that pushes elements from diff --git a/test/common/index.js b/test/common/index.js index 238d66e96fe257..6086d584f0b595 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -139,8 +139,6 @@ const isPi = (() => { } })(); -const isDumbTerminal = process.env.TERM === 'dumb'; - // When using high concurrency or in the CI we need much more time for each connection attempt net.setDefaultAutoSelectFamilyAttemptTimeout(platformTimeout(net.getDefaultAutoSelectFamilyAttemptTimeout() * 10)); const defaultAutoSelectFamilyAttemptTimeout = net.getDefaultAutoSelectFamilyAttemptTimeout(); @@ -243,13 +241,6 @@ function childShouldThrowAndAbort() { }); } -function createZeroFilledFile(filename) { - const fd = fs.openSync(filename, 'w'); - fs.ftruncateSync(fd, 10 * 1024 * 1024); - fs.closeSync(fd); -} - - const pwdCommand = isWindows ? ['cmd.exe', ['/d', '/c', 'cd']] : ['pwd', []]; @@ -716,12 +707,6 @@ function skipIf32Bits() { } } -function skipIfWorker() { - if (!isMainThread) { - skip('This test only works on a main thread'); - } -} - function getArrayBufferViews(buf) { const { buffer, byteOffset, byteLength } = buf; @@ -806,12 +791,6 @@ function invalidArgTypeHelper(input) { return ` Received type ${typeof input} (${inspected})`; } -function skipIfDumbTerminal() { - if (isDumbTerminal) { - skip('skipping - dumb terminal'); - } -} - function requireNoPackageJSONAbove(dir = __dirname) { let possiblePackage = path.join(dir, '..', 'package.json'); let lastPackage = null; @@ -882,45 +861,6 @@ function escapePOSIXShell(cmdParts, ...args) { return [cmd, { env }]; }; -function getPrintedStackTrace(stderr) { - const lines = stderr.split('\n'); - - let state = 'initial'; - const result = { - message: [], - nativeStack: [], - jsStack: [], - }; - for (let i = 0; i < lines.length; ++i) { - const line = lines[i].trim(); - if (line.length === 0) { - continue; // Skip empty lines. - } - - switch (state) { - case 'initial': - result.message.push(line); - if (line.includes('Native stack trace')) { - state = 'native-stack'; - } else { - result.message.push(line); - } - break; - case 'native-stack': - if (line.includes('JavaScript stack trace')) { - state = 'js-stack'; - } else { - result.nativeStack.push(line); - } - break; - case 'js-stack': - result.jsStack.push(line); - break; - } - } - return result; -} - /** * Check the exports of require(esm). * TODO(joyeecheung): use it in all the test-require-module-* tests to minimize changes @@ -943,7 +883,6 @@ const common = { buildType, canCreateSymLink, childShouldThrowAndAbort, - createZeroFilledFile, defaultAutoSelectFamilyAttemptTimeout, escapePOSIXShell, expectsError, @@ -951,7 +890,6 @@ const common = { expectWarning, getArrayBufferViews, getBufferSources, - getPrintedStackTrace, getTTYfd, hasIntl, hasCrypto, @@ -960,10 +898,8 @@ const common = { isAlive, isASan, isDebug, - isDumbTerminal, isFreeBSD, isLinux, - isMainThread, isOpenBSD, isMacOS, isPi, @@ -985,10 +921,8 @@ const common = { runWithInvalidFD, skip, skipIf32Bits, - skipIfDumbTerminal, skipIfEslintMissing, skipIfInspectorDisabled, - skipIfWorker, spawnPromisified, get enoughTestMem() { diff --git a/test/common/index.mjs b/test/common/index.mjs index aafef1453bd78a..dd0adadcb28d38 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -8,7 +8,6 @@ const { buildType, canCreateSymLink, childShouldThrowAndAbort, - createZeroFilledFile, enoughTestMem, escapePOSIXShell, expectsError, @@ -21,12 +20,10 @@ const { hasIPv6, isAIX, isAlive, - isDumbTerminal, isFreeBSD, isIBMi, isInsideDirWithUnusualChars, isLinux, - isMainThread, isOpenBSD, isMacOS, isSunOS, @@ -45,7 +42,6 @@ const { runWithInvalidFD, skip, skipIf32Bits, - skipIfDumbTerminal, skipIfEslintMissing, skipIfInspectorDisabled, spawnPromisified, @@ -59,7 +55,6 @@ export { canCreateSymLink, childShouldThrowAndAbort, createRequire, - createZeroFilledFile, enoughTestMem, escapePOSIXShell, expectsError, @@ -73,12 +68,10 @@ export { hasIPv6, isAIX, isAlive, - isDumbTerminal, isFreeBSD, isIBMi, isInsideDirWithUnusualChars, isLinux, - isMainThread, isOpenBSD, isMacOS, isSunOS, @@ -97,7 +90,6 @@ export { runWithInvalidFD, skip, skipIf32Bits, - skipIfDumbTerminal, skipIfEslintMissing, skipIfInspectorDisabled, spawnPromisified, diff --git a/test/es-module/test-esm-resolve-type.mjs b/test/es-module/test-esm-resolve-type.mjs index 22163bbd5defb8..9d97413379ad3c 100644 --- a/test/es-module/test-esm-resolve-type.mjs +++ b/test/es-module/test-esm-resolve-type.mjs @@ -13,8 +13,10 @@ import path from 'path'; import fs from 'fs'; import url from 'url'; import process from 'process'; +import { isMainThread } from 'worker_threads'; -if (!common.isMainThread) { + +if (!isMainThread) { common.skip( 'test-esm-resolve-type.mjs: process.chdir is not available in Workers' ); diff --git a/test/es-module/test-vm-main-context-default-loader.js b/test/es-module/test-vm-main-context-default-loader.js index f9edc761465d96..bda954be6ebf97 100644 --- a/test/es-module/test-vm-main-context-default-loader.js +++ b/test/es-module/test-vm-main-context-default-loader.js @@ -3,7 +3,11 @@ const common = require('../common'); // Can't process.chdir() in worker. -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); diff --git a/test/fixtures/permission/fs-write.js b/test/fixtures/permission/fs-write.js index 0c0ec72602041a..83fe3d234db290 100644 --- a/test/fixtures/permission/fs-write.js +++ b/test/fixtures/permission/fs-write.js @@ -1,7 +1,11 @@ 'use strict'; const common = require('../../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const fs = require('fs'); @@ -553,4 +557,4 @@ const relativeProtectedFolder = process.env.RELATIVEBLOCKEDFOLDER; }, { code: 'ERR_ACCESS_DENIED', }); -} \ No newline at end of file +} diff --git a/test/fixtures/permission/processbinding.js b/test/fixtures/permission/processbinding.js index bdb958fb01b5ca..69e2fac5d7f151 100644 --- a/test/fixtures/permission/processbinding.js +++ b/test/fixtures/permission/processbinding.js @@ -1,5 +1,9 @@ const common = require('../../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); diff --git a/test/internet/test-trace-events-dns.js b/test/internet/test-trace-events-dns.js index c18a49bc9496c8..c5df4751374399 100644 --- a/test/internet/test-trace-events-dns.js +++ b/test/internet/test-trace-events-dns.js @@ -5,9 +5,11 @@ const cp = require('child_process'); const tmpdir = require('../common/tmpdir'); const fs = require('fs'); const util = require('util'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const traceFile = 'node_trace.1.log'; diff --git a/test/parallel/test-async-hooks-disable-during-promise.js b/test/parallel/test-async-hooks-disable-during-promise.js index 6b9b53bd30f0f5..a25dae51e1f82d 100644 --- a/test/parallel/test-async-hooks-disable-during-promise.js +++ b/test/parallel/test-async-hooks-disable-during-promise.js @@ -1,9 +1,11 @@ 'use strict'; const common = require('../common'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different AsyncWraps'); +} const hook = async_hooks.createHook({ init: common.mustCall(2), diff --git a/test/parallel/test-async-hooks-promise-triggerid.js b/test/parallel/test-async-hooks-promise-triggerid.js index b860d60999e1ef..89e5bc1464f8d5 100644 --- a/test/parallel/test-async-hooks-promise-triggerid.js +++ b/test/parallel/test-async-hooks-promise-triggerid.js @@ -2,9 +2,11 @@ const common = require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const promiseAsyncIds = []; diff --git a/test/parallel/test-async-hooks-promise.js b/test/parallel/test-async-hooks-promise.js index 9db510e329ffad..74f72a188240a0 100644 --- a/test/parallel/test-async-hooks-promise.js +++ b/test/parallel/test-async-hooks-promise.js @@ -2,9 +2,11 @@ const common = require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const initCalls = []; const resolveCalls = []; diff --git a/test/parallel/test-async-hooks-top-level-clearimmediate.js b/test/parallel/test-async-hooks-top-level-clearimmediate.js index cc5fcf48eb50b3..fd91fefa9c4bce 100644 --- a/test/parallel/test-async-hooks-top-level-clearimmediate.js +++ b/test/parallel/test-async-hooks-top-level-clearimmediate.js @@ -5,9 +5,11 @@ const common = require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} let seenId, seenResource; diff --git a/test/parallel/test-async-wrap-promise-after-enabled.js b/test/parallel/test-async-wrap-promise-after-enabled.js index 0d58cbd653868b..cbca873574c1f8 100644 --- a/test/parallel/test-async-wrap-promise-after-enabled.js +++ b/test/parallel/test-async-wrap-promise-after-enabled.js @@ -4,9 +4,11 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different timing'); +} const async_hooks = require('async_hooks'); diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index c75ee390dcd195..ebcd2a6d6c12b2 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -115,7 +115,9 @@ expected.atRunTime = new Set([ 'NativeModule internal/modules/esm/utils', ]); -if (common.isMainThread) { +const { isMainThread } = require('worker_threads'); + +if (isMainThread) { [ 'NativeModule url', ].forEach(expected.beforePreExec.add.bind(expected.beforePreExec)); @@ -186,7 +188,7 @@ function err(message) { } } -if (common.isMainThread) { +if (isMainThread) { const missing = expected.beforePreExec.difference(actual.beforePreExec); const extra = actual.beforePreExec.difference(expected.beforePreExec); if (missing.size !== 0) { @@ -212,10 +214,10 @@ if (common.isMainThread) { } } -if (!common.isMainThread) { +if (!isMainThread) { // For workers, just merge beforePreExec into atRunTime for now. // When we start adding modules to the worker snapshot, this branch - // can be removed and we can just remove the common.isMainThread + // can be removed and we can just remove the isMainThread // conditions. expected.beforePreExec.forEach(expected.atRunTime.add.bind(expected.atRunTime)); actual.beforePreExec.forEach(actual.atRunTime.add.bind(actual.atRunTime)); diff --git a/test/parallel/test-child-process-validate-stdio.js b/test/parallel/test-child-process-validate-stdio.js index d5958c694ff6ff..5ba6f0fd123cc1 100644 --- a/test/parallel/test-child-process-validate-stdio.js +++ b/test/parallel/test-child-process-validate-stdio.js @@ -43,7 +43,9 @@ assert.throws(() => getValidStdio(stdio2, true), assert.throws(() => getValidStdio(stdio), expectedError); } -if (common.isMainThread) { +const { isMainThread } = require('worker_threads'); + +if (isMainThread) { const stdio3 = [process.stdin, process.stdout, process.stderr]; const result = getValidStdio(stdio3, false); assert.deepStrictEqual(result, { diff --git a/test/parallel/test-cluster-net-listen-relative-path.js b/test/parallel/test-cluster-net-listen-relative-path.js index bb4d0b90f203e6..16d2bf5c836b53 100644 --- a/test/parallel/test-cluster-net-listen-relative-path.js +++ b/test/parallel/test-cluster-net-listen-relative-path.js @@ -1,11 +1,16 @@ 'use strict'; const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { common.skip('On Windows named pipes live in their own ' + 'filesystem and don\'t have a ~100 byte limit'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const cluster = require('cluster'); diff --git a/test/parallel/test-code-cache.js b/test/parallel/test-code-cache.js index 1c768d664c8a18..576f713af1b02a 100644 --- a/test/parallel/test-code-cache.js +++ b/test/parallel/test-code-cache.js @@ -5,7 +5,8 @@ // and the cache is used when built in modules are compiled. // Otherwise, verifies that no cache is used when compiling builtins. -const { isMainThread } = require('../common'); +require('../common'); +const { isMainThread } = require('worker_threads'); const assert = require('assert'); const { internalBinding diff --git a/test/parallel/test-console-clear.js b/test/parallel/test-console-clear.js index 5975602547922a..8ded51595f654e 100644 --- a/test/parallel/test-console-clear.js +++ b/test/parallel/test-console-clear.js @@ -1,6 +1,6 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const stdoutWrite = process.stdout.write; @@ -18,7 +18,7 @@ function doTest(isTTY, check) { } // Fake TTY -if (!common.isDumbTerminal) { +if (process.env.TERM !== 'dumb') { doTest(true, check); } doTest(false, ''); diff --git a/test/parallel/test-console.js b/test/parallel/test-console.js index 5dd029a6e904cc..bc08ba395e787e 100644 --- a/test/parallel/test-console.js +++ b/test/parallel/test-console.js @@ -31,10 +31,12 @@ const { restoreStderr } = require('../common/hijackstdio'); +const { isMainThread } = require('worker_threads'); + assert.ok(process.stdout.writable); assert.ok(process.stderr.writable); // Support legacy API -if (common.isMainThread) { +if (isMainThread) { assert.strictEqual(typeof process.stdout.fd, 'number'); assert.strictEqual(typeof process.stderr.fd, 'number'); } diff --git a/test/parallel/test-crypto-no-algorithm.js b/test/parallel/test-crypto-no-algorithm.js index 06124e3d465e41..bb5b81e119c87d 100644 --- a/test/parallel/test-crypto-no-algorithm.js +++ b/test/parallel/test-crypto-no-algorithm.js @@ -11,8 +11,9 @@ if (!hasOpenSSL3) const assert = require('node:assert/strict'); const crypto = require('node:crypto'); +const { isMainThread } = require('worker_threads'); -if (common.isMainThread) { +if (isMainThread) { // TODO(richardlau): Decide if `crypto.setFips` should error if the // provider named "fips" is not available. crypto.setFips(1); diff --git a/test/parallel/test-cwd-enoent-preload.js b/test/parallel/test-cwd-enoent-preload.js index 21b20d6d035672..a7841e984d0eab 100644 --- a/test/parallel/test-cwd-enoent-preload.js +++ b/test/parallel/test-cwd-enoent-preload.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); // Fails with EINVAL on SmartOS, EBUSY on Windows, EBUSY on AIX. -if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) +if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) { common.skip('cannot rmdir current working directory'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-cwd-enoent-repl.js b/test/parallel/test-cwd-enoent-repl.js index 0a61cbfbced9b4..fcb08c004f345c 100644 --- a/test/parallel/test-cwd-enoent-repl.js +++ b/test/parallel/test-cwd-enoent-repl.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); // Fails with EINVAL on SmartOS, EBUSY on Windows, EBUSY on AIX. -if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) +if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) { common.skip('cannot rmdir current working directory'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-cwd-enoent.js b/test/parallel/test-cwd-enoent.js index 876888bc2be518..ca8b460835d45a 100644 --- a/test/parallel/test-cwd-enoent.js +++ b/test/parallel/test-cwd-enoent.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); // Fails with EINVAL on SmartOS, EBUSY on Windows, EBUSY on AIX. -if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) +if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) { common.skip('cannot rmdir current working directory'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-fs-mkdir.js b/test/parallel/test-fs-mkdir.js index 89b8b436d5c9f4..f7685c7de0a962 100644 --- a/test/parallel/test-fs-mkdir.js +++ b/test/parallel/test-fs-mkdir.js @@ -24,6 +24,7 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); +const { isMainThread } = require('worker_threads'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); @@ -217,7 +218,7 @@ function nextdir() { // mkdirpSync dirname loop // XXX: windows and smartos have issues removing a directory that you're in. -if (common.isMainThread && (common.isLinux || common.isMacOS)) { +if (isMainThread && (common.isLinux || common.isMacOS)) { const pathname = tmpdir.resolve(nextdir()); fs.mkdirSync(pathname); process.chdir(pathname); diff --git a/test/parallel/test-fs-realpath.js b/test/parallel/test-fs-realpath.js index d944195de3de0c..69237e3974e5b0 100644 --- a/test/parallel/test-fs-realpath.js +++ b/test/parallel/test-fs-realpath.js @@ -23,9 +23,11 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-fs-whatwg-url.js b/test/parallel/test-fs-whatwg-url.js index 7401ed7e76ecd1..2d5664cd12015c 100644 --- a/test/parallel/test-fs-whatwg-url.js +++ b/test/parallel/test-fs-whatwg-url.js @@ -5,6 +5,8 @@ const fixtures = require('../common/fixtures'); const assert = require('assert'); const fs = require('fs'); const tmpdir = require('../common/tmpdir'); +const { isMainThread } = require('worker_threads'); + tmpdir.refresh(); const url = fixtures.fileURL('a.js'); @@ -86,7 +88,7 @@ if (common.isWindows) { // Test that strings are interpreted as paths and not as URL // Can't use process.chdir in Workers // Please avoid testing fs.rmdir('file:') or using it as cleanup -if (common.isMainThread && !common.isWindows) { +if (isMainThread && !common.isWindows) { const oldCwd = process.cwd(); process.chdir(tmpdir.path); diff --git a/test/parallel/test-fs-write-file-sync.js b/test/parallel/test-fs-write-file-sync.js index 4ead91530bb748..e5fbe32eab6d14 100644 --- a/test/parallel/test-fs-write-file-sync.js +++ b/test/parallel/test-fs-write-file-sync.js @@ -21,9 +21,11 @@ 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Setting process.umask is not supported in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-http-chunk-problem.js b/test/parallel/test-http-chunk-problem.js index 3629b7576600e8..90c54b8f5c7dcb 100644 --- a/test/parallel/test-http-chunk-problem.js +++ b/test/parallel/test-http-chunk-problem.js @@ -1,9 +1,12 @@ 'use strict'; // http://groups.google.com/group/nodejs/browse_thread/thread/f66cd3c960406919 const common = require('../common'); -if (!common.hasCrypto) + +if (!common.hasCrypto) { common.skip('missing crypto'); +} +const fs = require('fs'); const assert = require('assert'); if (process.argv[2] === 'request') { @@ -73,7 +76,11 @@ function executeRequest(cb) { tmpdir.refresh(); -common.createZeroFilledFile(filename); + +// Create a zero-filled file. +const fd = fs.openSync(filename, 'w'); +fs.ftruncateSync(fd, 10 * 1024 * 1024); +fs.closeSync(fd); server = http.createServer(function(req, res) { res.writeHead(200); diff --git a/test/parallel/test-icu-env.js b/test/parallel/test-icu-env.js index afa36132f60e8d..26075a3d0acec2 100644 --- a/test/parallel/test-icu-env.js +++ b/test/parallel/test-icu-env.js @@ -4,7 +4,7 @@ const assert = require('assert'); const { execFileSync } = require('child_process'); const { readFileSync, globSync } = require('fs'); const { path } = require('../common/fixtures'); - +const { isMainThread } = require('worker_threads'); // This test checks for regressions in environment variable handling and // caching, but the localization data originated from ICU might change @@ -169,7 +169,7 @@ if (isMockable) { // Tests with process.env mutated inside { // process.env.TZ is not intercepted in Workers - if (common.isMainThread) { + if (isMainThread) { assert.strictEqual( isSet(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toString()))), true diff --git a/test/parallel/test-inspector-already-activated-cli.js b/test/parallel/test-inspector-already-activated-cli.js index ba76d5168c14b9..9de226cedca60c 100644 --- a/test/parallel/test-inspector-already-activated-cli.js +++ b/test/parallel/test-inspector-already-activated-cli.js @@ -3,7 +3,12 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const inspector = require('inspector'); diff --git a/test/parallel/test-inspector-async-hook-after-done.js b/test/parallel/test-inspector-async-hook-after-done.js index 9f96fdb7b0da84..b49fe32982e132 100644 --- a/test/parallel/test-inspector-async-hook-after-done.js +++ b/test/parallel/test-inspector-async-hook-after-done.js @@ -3,7 +3,12 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { Worker } = require('worker_threads'); diff --git a/test/parallel/test-inspector-async-hook-setup-at-signal.js b/test/parallel/test-inspector-async-hook-setup-at-signal.js index 43f50d00615723..64a3835e415746 100644 --- a/test/parallel/test-inspector-async-hook-setup-at-signal.js +++ b/test/parallel/test-inspector-async-hook-setup-at-signal.js @@ -6,7 +6,11 @@ common.skipIf32Bits(); const { NodeInstance } = require('../common/inspector-helper.js'); const assert = require('assert'); -common.skipIfWorker(); // Signal starts a server for a main thread inspector +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const script = ` process._rawDebug('Waiting until a signal enables the inspector...'); diff --git a/test/parallel/test-inspector-connect-main-thread.js b/test/parallel/test-inspector-connect-main-thread.js index b724bf3cd9d62f..2281b5efcf3ed8 100644 --- a/test/parallel/test-inspector-connect-main-thread.js +++ b/test/parallel/test-inspector-connect-main-thread.js @@ -10,8 +10,8 @@ const { pathToFileURL } = require('url'); const { isMainThread, parentPort, Worker, workerData } = require('worker_threads'); -if (!workerData) { - common.skipIfWorker(); +if (!workerData && !isMainThread) { + common.skip('This test only works on a main thread'); } function toDebug() { diff --git a/test/parallel/test-inspector-connect-to-main-thread.js b/test/parallel/test-inspector-connect-to-main-thread.js index 7254145a2733f0..9244a85f21b15a 100644 --- a/test/parallel/test-inspector-connect-to-main-thread.js +++ b/test/parallel/test-inspector-connect-to-main-thread.js @@ -6,8 +6,8 @@ common.skipIfInspectorDisabled(); const { Session } = require('inspector'); const { Worker, isMainThread, workerData } = require('worker_threads'); -if (!workerData) { - common.skipIfWorker(); +if (!workerData && !isMainThread) { + common.skip('This test only works on a main thread'); } if (isMainThread) { diff --git a/test/parallel/test-inspector-contexts.js b/test/parallel/test-inspector-contexts.js index 3d6ee4d460e863..9ab2c515b4a9de 100644 --- a/test/parallel/test-inspector-contexts.js +++ b/test/parallel/test-inspector-contexts.js @@ -9,6 +9,8 @@ const assert = require('assert'); const vm = require('vm'); const { Session } = require('inspector'); const { gcUntil } = require('../common/gc'); +const { isMainThread } = require('worker_threads'); + const session = new Session(); session.connect(); @@ -34,7 +36,7 @@ async function testContextCreatedAndDestroyed() { assert.strictEqual(name.includes(`[${process.pid}]`), true); } else { let expects = `${process.argv0}[${process.pid}]`; - if (!common.isMainThread) { + if (!isMainThread) { expects = `Worker[${require('worker_threads').threadId}]`; } assert.strictEqual(expects, name); diff --git a/test/parallel/test-inspector-exit-worker-in-wait-for-connection.js b/test/parallel/test-inspector-exit-worker-in-wait-for-connection.js index 4fcbb092fd23cf..9215d4969fb92f 100644 --- a/test/parallel/test-inspector-exit-worker-in-wait-for-connection.js +++ b/test/parallel/test-inspector-exit-worker-in-wait-for-connection.js @@ -3,9 +3,15 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const { parentPort, workerData, Worker } = require('node:worker_threads'); -if (!workerData) { - common.skipIfWorker(); +const { + isMainThread, + parentPort, + workerData, + Worker, +} = require('node:worker_threads'); + +if (!workerData && !isMainThread) { + common.skip('This test only works on a main thread'); } const inspector = require('node:inspector'); diff --git a/test/parallel/test-inspector-exit-worker-in-wait-for-connection2.js b/test/parallel/test-inspector-exit-worker-in-wait-for-connection2.js index fb13fc3f969304..cf485ae3a4318f 100644 --- a/test/parallel/test-inspector-exit-worker-in-wait-for-connection2.js +++ b/test/parallel/test-inspector-exit-worker-in-wait-for-connection2.js @@ -3,9 +3,9 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const { workerData, Worker } = require('node:worker_threads'); -if (!workerData) { - common.skipIfWorker(); +const { isMainThread, workerData, Worker } = require('node:worker_threads'); +if (!workerData && !isMainThread) { + common.skip('This test only works on a main thread'); } const assert = require('node:assert'); diff --git a/test/parallel/test-inspector-open-coverage.js b/test/parallel/test-inspector-open-coverage.js index 259049c36822ab..33f50bfc3f53c4 100644 --- a/test/parallel/test-inspector-open-coverage.js +++ b/test/parallel/test-inspector-open-coverage.js @@ -7,7 +7,12 @@ const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} tmpdir.refresh(); diff --git a/test/parallel/test-inspector-open-port-integer-overflow.js b/test/parallel/test-inspector-open-port-integer-overflow.js index 0f9a4799d0642a..a1b5c640c4c18d 100644 --- a/test/parallel/test-inspector-open-port-integer-overflow.js +++ b/test/parallel/test-inspector-open-port-integer-overflow.js @@ -5,7 +5,12 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const inspector = require('inspector'); diff --git a/test/parallel/test-inspector-overwrite-config.js b/test/parallel/test-inspector-overwrite-config.js index c20df083256120..53599b31df8acc 100644 --- a/test/parallel/test-inspector-overwrite-config.js +++ b/test/parallel/test-inspector-overwrite-config.js @@ -13,9 +13,11 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('--require does not work with Workers'); +} const inspector = require('inspector'); const msg = 'Test inspector logging'; diff --git a/test/parallel/test-inspector-port-zero-cluster.js b/test/parallel/test-inspector-port-zero-cluster.js index 8e2db0b69d5ca0..5ee7bcf7417345 100644 --- a/test/parallel/test-inspector-port-zero-cluster.js +++ b/test/parallel/test-inspector-port-zero-cluster.js @@ -3,7 +3,12 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} // Assert that even when started with `--inspect=0` workers are assigned // consecutive (i.e. deterministically predictable) debug ports diff --git a/test/parallel/test-inspector-tracing-domain.js b/test/parallel/test-inspector-tracing-domain.js index f5ac6875a0f643..aa31d63a01577d 100644 --- a/test/parallel/test-inspector-tracing-domain.js +++ b/test/parallel/test-inspector-tracing-domain.js @@ -3,7 +3,13 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); // https://github.com/nodejs/node/issues/22767 + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + // https://github.com/nodejs/node/issues/22767 + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { Session } = require('inspector'); diff --git a/test/parallel/test-inspector-workers-flat-list.js b/test/parallel/test-inspector-workers-flat-list.js index 9f6495d10fb147..a7b57fbb0a353b 100644 --- a/test/parallel/test-inspector-workers-flat-list.js +++ b/test/parallel/test-inspector-workers-flat-list.js @@ -6,8 +6,8 @@ common.skipIfInspectorDisabled(); const { Worker, isMainThread, parentPort, workerData } = require('worker_threads'); -if (isMainThread || workerData !== 'launched by test') { - common.skipIfWorker(); +if (!isMainThread || workerData !== 'launched by test') { + common.skip('This test only works on a main thread'); } const { Session } = require('inspector'); diff --git a/test/parallel/test-internal-module-require.js b/test/parallel/test-internal-module-require.js index 058273c7ea4304..213838150b96d9 100644 --- a/test/parallel/test-internal-module-require.js +++ b/test/parallel/test-internal-module-require.js @@ -8,8 +8,9 @@ // 3. Deprecated modules are properly deprecated. const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) { +if (!isMainThread) { common.skip('Cannot test the existence of --expose-internals from worker'); } diff --git a/test/parallel/test-performance-nodetiming-uvmetricsinfo.js b/test/parallel/test-performance-nodetiming-uvmetricsinfo.js index 3d32e0deb72e94..b67682b0ff3559 100644 --- a/test/parallel/test-performance-nodetiming-uvmetricsinfo.js +++ b/test/parallel/test-performance-nodetiming-uvmetricsinfo.js @@ -1,7 +1,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const { spawnSync } = require('node:child_process'); const assert = require('node:assert'); diff --git a/test/parallel/test-permission-allow-addons-cli.js b/test/parallel/test-permission-allow-addons-cli.js index 484f16e0acb3b5..342bdb6bc01e35 100644 --- a/test/parallel/test-permission-allow-addons-cli.js +++ b/test/parallel/test-permission-allow-addons-cli.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const { createRequire } = require('node:module'); const assert = require('node:assert'); diff --git a/test/parallel/test-permission-allow-child-process-cli.js b/test/parallel/test-permission-allow-child-process-cli.js index 794f55ecf9a68c..cf7e79e208d389 100644 --- a/test/parallel/test-permission-allow-child-process-cli.js +++ b/test/parallel/test-permission-allow-child-process-cli.js @@ -2,7 +2,13 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + const assert = require('assert'); const childProcess = require('child_process'); const fs = require('fs'); diff --git a/test/parallel/test-permission-allow-wasi-cli.js b/test/parallel/test-permission-allow-wasi-cli.js index c6bea9fb39cf0a..20aca9292533d5 100644 --- a/test/parallel/test-permission-allow-wasi-cli.js +++ b/test/parallel/test-permission-allow-wasi-cli.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { WASI } = require('wasi'); diff --git a/test/parallel/test-permission-child-process-cli.js b/test/parallel/test-permission-child-process-cli.js index dfea008a60407b..7d8fbf0564d5ef 100644 --- a/test/parallel/test-permission-child-process-cli.js +++ b/test/parallel/test-permission-child-process-cli.js @@ -2,7 +2,12 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + const assert = require('assert'); const childProcess = require('child_process'); diff --git a/test/parallel/test-permission-fs-absolute-path.js b/test/parallel/test-permission-fs-absolute-path.js index 2c2257052c8b02..c3bf9ef5cfb2d1 100644 --- a/test/parallel/test-permission-fs-absolute-path.js +++ b/test/parallel/test-permission-fs-absolute-path.js @@ -3,7 +3,11 @@ const common = require('../common'); const path = require('path'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { spawnSync } = require('child_process'); diff --git a/test/parallel/test-permission-fs-internal-module-stat.js b/test/parallel/test-permission-fs-internal-module-stat.js index fd0222cc34fa2e..ef99e4cca73a4f 100644 --- a/test/parallel/test-permission-fs-internal-module-stat.js +++ b/test/parallel/test-permission-fs-internal-module-stat.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} if (!common.hasCrypto) { common.skip('no crypto'); diff --git a/test/parallel/test-permission-fs-read.js b/test/parallel/test-permission-fs-read.js index ed8e866a6a4c10..b719207bdbd820 100644 --- a/test/parallel/test-permission-fs-read.js +++ b/test/parallel/test-permission-fs-read.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} if (!common.hasCrypto) { common.skip('no crypto'); diff --git a/test/parallel/test-permission-fs-relative-path.js b/test/parallel/test-permission-fs-relative-path.js index 3b115ee35d1227..9f4ce25f0f7d37 100644 --- a/test/parallel/test-permission-fs-relative-path.js +++ b/test/parallel/test-permission-fs-relative-path.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { spawnSync } = require('child_process'); diff --git a/test/parallel/test-permission-fs-repeat-path.js b/test/parallel/test-permission-fs-repeat-path.js index 764c7d91497248..d24197e905063d 100644 --- a/test/parallel/test-permission-fs-repeat-path.js +++ b/test/parallel/test-permission-fs-repeat-path.js @@ -3,7 +3,11 @@ const common = require('../common'); const path = require('path'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { spawnSync } = require('child_process'); diff --git a/test/parallel/test-permission-fs-require.js b/test/parallel/test-permission-fs-require.js index 5d3a407708371e..8406f9ec052eae 100644 --- a/test/parallel/test-permission-fs-require.js +++ b/test/parallel/test-permission-fs-require.js @@ -2,7 +2,13 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + const fixtures = require('../common/fixtures'); const assert = require('node:assert'); diff --git a/test/parallel/test-permission-fs-symlink-relative.js b/test/parallel/test-permission-fs-symlink-relative.js index cf9b37ea79b059..e1fe5d064a8756 100644 --- a/test/parallel/test-permission-fs-symlink-relative.js +++ b/test/parallel/test-permission-fs-symlink-relative.js @@ -2,7 +2,12 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const path = require('path'); diff --git a/test/parallel/test-permission-fs-symlink-target-write.js b/test/parallel/test-permission-fs-symlink-target-write.js index f55b19fa764a89..1cffead4dd7e71 100644 --- a/test/parallel/test-permission-fs-symlink-target-write.js +++ b/test/parallel/test-permission-fs-symlink-target-write.js @@ -2,11 +2,19 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -if (!common.canCreateSymLink()) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +if (!common.canCreateSymLink()) { common.skip('insufficient privileges'); -if (!common.hasCrypto) +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const fs = require('fs'); @@ -15,9 +23,7 @@ const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); const { spawnSync } = require('child_process'); -{ - tmpdir.refresh(); -} +tmpdir.refresh(); const readOnlyFolder = tmpdir.resolve('read-only'); const readWriteFolder = tmpdir.resolve('read-write'); diff --git a/test/parallel/test-permission-fs-symlink.js b/test/parallel/test-permission-fs-symlink.js index 92965c960177d4..e5a80dba44ddf4 100644 --- a/test/parallel/test-permission-fs-symlink.js +++ b/test/parallel/test-permission-fs-symlink.js @@ -2,13 +2,19 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const fixtures = require('../common/fixtures'); -if (!common.canCreateSymLink()) +if (!common.canCreateSymLink()) { common.skip('insufficient privileges'); -if (!common.hasCrypto) +} +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-permission-fs-traversal-path.js b/test/parallel/test-permission-fs-traversal-path.js index 03571c2d01c861..ed9e434b6b862b 100644 --- a/test/parallel/test-permission-fs-traversal-path.js +++ b/test/parallel/test-permission-fs-traversal-path.js @@ -2,13 +2,20 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const fixtures = require('../common/fixtures'); -if (!common.canCreateSymLink()) +if (!common.canCreateSymLink()) { common.skip('insufficient privileges'); -if (!common.hasCrypto) +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-permission-fs-wildcard.js b/test/parallel/test-permission-fs-wildcard.js index adca56ed0dba6d..1b67f37c2dcda2 100644 --- a/test/parallel/test-permission-fs-wildcard.js +++ b/test/parallel/test-permission-fs-wildcard.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const path = require('path'); diff --git a/test/parallel/test-permission-fs-windows-path.js b/test/parallel/test-permission-fs-windows-path.js index 6869b347cf283f..c3b3683b6479f7 100644 --- a/test/parallel/test-permission-fs-windows-path.js +++ b/test/parallel/test-permission-fs-windows-path.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { spawnSync } = require('child_process'); diff --git a/test/parallel/test-permission-fs-write-report.js b/test/parallel/test-permission-fs-write-report.js index 111f73b7bcc1ed..a5f8d74904fedc 100644 --- a/test/parallel/test-permission-fs-write-report.js +++ b/test/parallel/test-permission-fs-write-report.js @@ -2,9 +2,15 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -if (!common.hasCrypto) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); diff --git a/test/parallel/test-permission-fs-write-v8.js b/test/parallel/test-permission-fs-write-v8.js index 85cb9a5519b3af..1b8691969b7afb 100644 --- a/test/parallel/test-permission-fs-write-v8.js +++ b/test/parallel/test-permission-fs-write-v8.js @@ -2,9 +2,15 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -if (!common.hasCrypto) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const v8 = require('v8'); diff --git a/test/parallel/test-permission-fs-write.js b/test/parallel/test-permission-fs-write.js index 34eab7a40005db..385a37e2a92d86 100644 --- a/test/parallel/test-permission-fs-write.js +++ b/test/parallel/test-permission-fs-write.js @@ -2,9 +2,15 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -if (!common.hasCrypto) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const path = require('path'); diff --git a/test/parallel/test-permission-inspector-brk.js b/test/parallel/test-permission-inspector-brk.js index 61c9c799ba7eb6..3cc7caabd42ba1 100644 --- a/test/parallel/test-permission-inspector-brk.js +++ b/test/parallel/test-permission-inspector-brk.js @@ -5,8 +5,12 @@ const assert = require('assert'); const { spawnSync } = require('child_process'); const fixtures = require('../common/fixtures'); const file = fixtures.path('permission', 'inspector-brk.js'); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} -common.skipIfWorker(); common.skipIfInspectorDisabled(); // See https://github.com/nodejs/node/issues/53385 diff --git a/test/parallel/test-permission-inspector.js b/test/parallel/test-permission-inspector.js index 9d3bf485fc4348..4b52e12abca090 100644 --- a/test/parallel/test-permission-inspector.js +++ b/test/parallel/test-permission-inspector.js @@ -2,7 +2,12 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + common.skipIfInspectorDisabled(); const { Session } = require('inspector'); diff --git a/test/parallel/test-permission-no-addons.js b/test/parallel/test-permission-no-addons.js index a3ae6f4be10641..df08c4aa9f9db5 100644 --- a/test/parallel/test-permission-no-addons.js +++ b/test/parallel/test-permission-no-addons.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const { createRequire } = require('node:module'); const assert = require('node:assert'); diff --git a/test/parallel/test-permission-processbinding.js b/test/parallel/test-permission-processbinding.js index 47a1364f19e303..f5e33dac4deb52 100644 --- a/test/parallel/test-permission-processbinding.js +++ b/test/parallel/test-permission-processbinding.js @@ -1,7 +1,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} if (!common.hasCrypto) { common.skip('no crypto'); diff --git a/test/parallel/test-permission-worker-threads-cli.js b/test/parallel/test-permission-worker-threads-cli.js index efd98b2a3881aa..cf397c280474c1 100644 --- a/test/parallel/test-permission-worker-threads-cli.js +++ b/test/parallel/test-permission-worker-threads-cli.js @@ -2,13 +2,17 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -const assert = require('assert'); const { Worker, isMainThread, } = require('worker_threads'); +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +const assert = require('assert'); + // Guarantee the initial state { assert.ok(!process.permission.has('worker')); diff --git a/test/parallel/test-pipe-file-to-http.js b/test/parallel/test-pipe-file-to-http.js index 82bdbe6a832a98..ffbab21f71fd9d 100644 --- a/test/parallel/test-pipe-file-to-http.js +++ b/test/parallel/test-pipe-file-to-http.js @@ -54,7 +54,12 @@ const server = http.createServer((req, res) => { server.listen(0); server.on('listening', () => { - common.createZeroFilledFile(filename); + + // Create a zero-filled file + const fd = fs.openSync(filename, 'w'); + fs.ftruncateSync(fd, 10 * 1024 * 1024); + fs.closeSync(fd); + makeRequest(); }); diff --git a/test/parallel/test-preload-self-referential.js b/test/parallel/test-preload-self-referential.js index 867e1c67983c83..68681332978ea6 100644 --- a/test/parallel/test-preload-self-referential.js +++ b/test/parallel/test-preload-self-referential.js @@ -4,11 +4,13 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const assert = require('assert'); const { exec } = require('child_process'); +const { isMainThread } = require('worker_threads'); const nodeBinary = process.argv[0]; -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const selfRefModule = fixtures.path('self_ref_module'); const fixtureA = fixtures.path('printA.js'); diff --git a/test/parallel/test-process-abort.js b/test/parallel/test-process-abort.js index 665e1399a3f362..34353befb02a44 100644 --- a/test/parallel/test-process-abort.js +++ b/test/parallel/test-process-abort.js @@ -2,9 +2,11 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.abort() is not available in Workers'); +} // Check that our built-in methods do not have a prototype/constructor behaviour // if they don't need to. This could be tested for any of our C++ methods. diff --git a/test/parallel/test-process-beforeexit-throw-exit.js b/test/parallel/test-process-beforeexit-throw-exit.js index 6e9d764be90baa..c967d3a62712a7 100644 --- a/test/parallel/test-process-beforeexit-throw-exit.js +++ b/test/parallel/test-process-beforeexit-throw-exit.js @@ -1,6 +1,10 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} // Test that 'exit' is emitted if 'beforeExit' throws. diff --git a/test/parallel/test-process-chdir-errormessage.js b/test/parallel/test-process-chdir-errormessage.js index 0ed368287b377e..727a13f6f63f16 100644 --- a/test/parallel/test-process-chdir-errormessage.js +++ b/test/parallel/test-process-chdir-errormessage.js @@ -1,8 +1,11 @@ 'use strict'; const common = require('../common'); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); assert.throws( diff --git a/test/parallel/test-process-chdir.js b/test/parallel/test-process-chdir.js index ee59df853b24ce..42d2a60c8ec63e 100644 --- a/test/parallel/test-process-chdir.js +++ b/test/parallel/test-process-chdir.js @@ -4,9 +4,11 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-process-env-tz.js b/test/parallel/test-process-env-tz.js index dcc69ed4bf1d3b..b7bf730a9afa38 100644 --- a/test/parallel/test-process-env-tz.js +++ b/test/parallel/test-process-env-tz.js @@ -1,12 +1,15 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.env.TZ is not intercepted in Workers'); +} -if (common.isWindows) // Using a different TZ format. +if (common.isWindows) { // Using a different TZ format. common.skip('todo: test on Windows'); +} const date = new Date('2018-04-14T12:34:56.789Z'); diff --git a/test/parallel/test-process-euid-egid.js b/test/parallel/test-process-euid-egid.js index 11a8cfa0ed2b3c..3f4934233a6308 100644 --- a/test/parallel/test-process-euid-egid.js +++ b/test/parallel/test-process-euid-egid.js @@ -3,6 +3,8 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); + if (common.isWindows) { assert.strictEqual(process.geteuid, undefined); assert.strictEqual(process.getegid, undefined); @@ -11,8 +13,9 @@ if (common.isWindows) { return; } -if (!common.isMainThread) +if (!isMainThread) { return; +} assert.throws(() => { process.seteuid({}); diff --git a/test/parallel/test-process-exit-handler.js b/test/parallel/test-process-exit-handler.js index d74e320fe63082..2546aa60a5cf89 100644 --- a/test/parallel/test-process-exit-handler.js +++ b/test/parallel/test-process-exit-handler.js @@ -1,8 +1,10 @@ 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('execArgv does not affect Workers'); +} // This test ensures that no asynchronous operations are performed in the 'exit' // handler. diff --git a/test/parallel/test-process-get-builtin.mjs b/test/parallel/test-process-get-builtin.mjs index b376e1b88f905a..6695828570f6c0 100644 --- a/test/parallel/test-process-get-builtin.mjs +++ b/test/parallel/test-process-get-builtin.mjs @@ -1,6 +1,7 @@ -import { isMainThread, hasCrypto, hasIntl } from '../common/index.mjs'; +import { hasCrypto, hasIntl } from '../common/index.mjs'; import assert from 'node:assert'; import { builtinModules } from 'node:module'; +import { isMainThread } from 'node:worker_threads'; for (const invalid of [1, undefined, null, false, [], {}, () => {}, Symbol('test')]) { assert.throws(() => process.getBuiltinModule(invalid), { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/test/parallel/test-process-initgroups.js b/test/parallel/test-process-initgroups.js index 6b4e3bdf1470b4..52597e096175e9 100644 --- a/test/parallel/test-process-initgroups.js +++ b/test/parallel/test-process-initgroups.js @@ -7,8 +7,11 @@ if (common.isWindows) { return; } -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { return; +} [undefined, null, true, {}, [], () => {}].forEach((val) => { assert.throws( diff --git a/test/parallel/test-process-load-env-file.js b/test/parallel/test-process-load-env-file.js index 1dada3aa9b7016..ec99c099d11b80 100644 --- a/test/parallel/test-process-load-env-file.js +++ b/test/parallel/test-process-load-env-file.js @@ -5,6 +5,7 @@ const fixtures = require('../../test/common/fixtures'); const assert = require('node:assert'); const { describe, it } = require('node:test'); const { join } = require('node:path'); +const { isMainThread } = require('worker_threads'); const basicValidEnvFilePath = fixtures.path('dotenv/basic-valid.env'); const validEnvFilePath = fixtures.path('dotenv/valid.env'); @@ -58,7 +59,7 @@ describe('process.loadEnvFile()', () => { const originalCwd = process.cwd(); try { - if (common.isMainThread) { + if (isMainThread) { process.chdir(join(originalCwd, 'lib')); } @@ -66,7 +67,7 @@ describe('process.loadEnvFile()', () => { process.loadEnvFile(); }, { code: 'ENOENT', syscall: 'open', path: '.env' }); } finally { - if (common.isMainThread) { + if (isMainThread) { process.chdir(originalCwd); } } diff --git a/test/parallel/test-process-setgroups.js b/test/parallel/test-process-setgroups.js index 9506f24a5f3447..49d147b6c2ddf5 100644 --- a/test/parallel/test-process-setgroups.js +++ b/test/parallel/test-process-setgroups.js @@ -1,14 +1,16 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); if (common.isWindows) { assert.strictEqual(process.setgroups, undefined); return; } -if (!common.isMainThread) +if (!isMainThread) { return; +} assert.throws( () => { diff --git a/test/parallel/test-process-uid-gid.js b/test/parallel/test-process-uid-gid.js index 54e87a6ff5c6e0..10eee45af1555b 100644 --- a/test/parallel/test-process-uid-gid.js +++ b/test/parallel/test-process-uid-gid.js @@ -23,6 +23,7 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); if (common.isWindows) { // uid/gid functions are POSIX only. @@ -33,8 +34,9 @@ if (common.isWindows) { return; } -if (!common.isMainThread) +if (!isMainThread) { return; +} assert.throws(() => { process.setuid({}); diff --git a/test/parallel/test-process-umask-mask.js b/test/parallel/test-process-umask-mask.js index d599379761fd40..f0a67b8f14e895 100644 --- a/test/parallel/test-process-umask-mask.js +++ b/test/parallel/test-process-umask-mask.js @@ -5,8 +5,9 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) common.skip('Setting process.umask is not supported in Workers'); let mask; diff --git a/test/parallel/test-process-umask.js b/test/parallel/test-process-umask.js index e90955f394df4e..594f75ebebed2b 100644 --- a/test/parallel/test-process-umask.js +++ b/test/parallel/test-process-umask.js @@ -22,8 +22,9 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) { +if (!isMainThread) { assert.strictEqual(typeof process.umask(), 'number'); assert.throws(() => { process.umask('0664'); diff --git a/test/parallel/test-readline-interface-no-trailing-newline.js b/test/parallel/test-readline-interface-no-trailing-newline.js index b3392db8619c95..398b85838c8b71 100644 --- a/test/parallel/test-readline-interface-no-trailing-newline.js +++ b/test/parallel/test-readline-interface-no-trailing-newline.js @@ -3,7 +3,9 @@ const common = require('../common'); const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const readline = require('readline'); const rli = new readline.Interface({ diff --git a/test/parallel/test-readline-interface-recursive-writes.js b/test/parallel/test-readline-interface-recursive-writes.js index 3a0aee5be9d619..ea3df1968d08d8 100644 --- a/test/parallel/test-readline-interface-recursive-writes.js +++ b/test/parallel/test-readline-interface-recursive-writes.js @@ -3,7 +3,9 @@ const common = require('../common'); const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const readline = require('readline'); const rli = new readline.Interface({ diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index a90e07d235030f..12ba0c709622e9 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -22,7 +22,10 @@ // Flags: --expose-internals 'use strict'; const common = require('../common'); -common.skipIfDumbTerminal(); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const assert = require('assert'); const readline = require('readline'); diff --git a/test/parallel/test-readline-position.js b/test/parallel/test-readline-position.js index 3603a42ecedc68..ac2fe43b37a097 100644 --- a/test/parallel/test-readline-position.js +++ b/test/parallel/test-readline-position.js @@ -7,7 +7,9 @@ const assert = require('assert'); const ctrlU = { ctrl: true, name: 'u' }; -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} { const input = new PassThrough(); diff --git a/test/parallel/test-readline-promises-interface.js b/test/parallel/test-readline-promises-interface.js index 8e42d977301267..97424c1372629c 100644 --- a/test/parallel/test-readline-promises-interface.js +++ b/test/parallel/test-readline-promises-interface.js @@ -1,7 +1,10 @@ // Flags: --expose-internals 'use strict'; const common = require('../common'); -common.skipIfDumbTerminal(); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const assert = require('assert'); const readline = require('readline/promises'); diff --git a/test/parallel/test-readline-promises-tab-complete.js b/test/parallel/test-readline-promises-tab-complete.js index fd32900e71d096..d8b0ac30ee779d 100644 --- a/test/parallel/test-readline-promises-tab-complete.js +++ b/test/parallel/test-readline-promises-tab-complete.js @@ -8,7 +8,9 @@ const assert = require('assert'); const { EventEmitter } = require('events'); const { getStringWidth } = require('internal/util/inspect'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // This test verifies that the tab completion supports unicode and the writes // are limited to the minimum. diff --git a/test/parallel/test-readline-tab-complete.js b/test/parallel/test-readline-tab-complete.js index 64df237d56ad44..5b7b19102f412a 100644 --- a/test/parallel/test-readline-tab-complete.js +++ b/test/parallel/test-readline-tab-complete.js @@ -8,7 +8,9 @@ const assert = require('assert'); const EventEmitter = require('events').EventEmitter; const { getStringWidth } = require('internal/util/inspect'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // This test verifies that the tab completion supports unicode and the writes // are limited to the minimum. diff --git a/test/parallel/test-readline-undefined-columns.js b/test/parallel/test-readline-undefined-columns.js index 25bafe957fa40a..d7000a16dd88a7 100644 --- a/test/parallel/test-readline-undefined-columns.js +++ b/test/parallel/test-readline-undefined-columns.js @@ -5,7 +5,9 @@ const assert = require('assert'); const PassThrough = require('stream').PassThrough; const readline = require('readline'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // Checks that tab completion still works // when output column size is undefined diff --git a/test/parallel/test-readline.js b/test/parallel/test-readline.js index 77799fc14cf75f..0cf577942915a6 100644 --- a/test/parallel/test-readline.js +++ b/test/parallel/test-readline.js @@ -4,7 +4,9 @@ const { PassThrough } = require('stream'); const readline = require('readline'); const assert = require('assert'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} { const input = new PassThrough(); diff --git a/test/parallel/test-repl-autocomplete.js b/test/parallel/test-repl-autocomplete.js index cb17523494b2ff..a68322c501e264 100644 --- a/test/parallel/test-repl-autocomplete.js +++ b/test/parallel/test-repl-autocomplete.js @@ -9,7 +9,9 @@ const assert = require('assert'); const fs = require('fs'); const { inspect } = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-repl-editor.js b/test/parallel/test-repl-editor.js index e260f5e89174a8..fee647d0478e50 100644 --- a/test/parallel/test-repl-editor.js +++ b/test/parallel/test-repl-editor.js @@ -5,7 +5,9 @@ const assert = require('assert'); const repl = require('repl'); const ArrayStream = require('../common/arraystream'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // \u001b[nG - Moves the cursor to n st column // \u001b[0J - Clear screen diff --git a/test/parallel/test-repl-history-navigation.js b/test/parallel/test-repl-history-navigation.js index 4df120d7cb9eae..64317be960e8d1 100644 --- a/test/parallel/test-repl-history-navigation.js +++ b/test/parallel/test-repl-history-navigation.js @@ -9,7 +9,9 @@ const assert = require('assert'); const fs = require('fs'); const { inspect } = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-repl-load-multiline-no-trailing-newline.js b/test/parallel/test-repl-load-multiline-no-trailing-newline.js index f57638d2521bbe..8fda91e35d1030 100644 --- a/test/parallel/test-repl-load-multiline-no-trailing-newline.js +++ b/test/parallel/test-repl-load-multiline-no-trailing-newline.js @@ -5,7 +5,9 @@ const fixtures = require('../common/fixtures'); const assert = require('assert'); const repl = require('repl'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const command = `.load ${fixtures.path('repl-load-multiline-no-trailing-newline.js')}`; const terminalCode = '\u001b[1G\u001b[0J \u001b[1G'; diff --git a/test/parallel/test-repl-load-multiline.js b/test/parallel/test-repl-load-multiline.js index 4fcf206bef1be1..920f4b1c25d144 100644 --- a/test/parallel/test-repl-load-multiline.js +++ b/test/parallel/test-repl-load-multiline.js @@ -5,7 +5,9 @@ const fixtures = require('../common/fixtures'); const assert = require('assert'); const repl = require('repl'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const command = `.load ${fixtures.path('repl-load-multiline.js')}`; const terminalCode = '\u001b[1G\u001b[0J \u001b[1G'; diff --git a/test/parallel/test-repl-mode.js b/test/parallel/test-repl-mode.js index aca8418904d082..f8a54d34089b00 100644 --- a/test/parallel/test-repl-mode.js +++ b/test/parallel/test-repl-mode.js @@ -4,7 +4,9 @@ const assert = require('assert'); const Stream = require('stream'); const repl = require('repl'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tests = [ testSloppyMode, diff --git a/test/parallel/test-repl-permission-model.js b/test/parallel/test-repl-permission-model.js index 938f5121163a23..ab5c7bff06cde8 100644 --- a/test/parallel/test-repl-permission-model.js +++ b/test/parallel/test-repl-permission-model.js @@ -8,7 +8,9 @@ const REPL = require('internal/repl'); const assert = require('assert'); const { inspect } = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // Create an input stream specialized for testing an array of actions class ActionStream extends stream.Stream { diff --git a/test/parallel/test-repl-persistent-history.js b/test/parallel/test-repl-persistent-history.js index 99ba92eda4cf3d..f5e2d48139f449 100644 --- a/test/parallel/test-repl-persistent-history.js +++ b/test/parallel/test-repl-persistent-history.js @@ -11,7 +11,9 @@ const fs = require('fs'); const os = require('os'); const util = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-repl-programmatic-history.js b/test/parallel/test-repl-programmatic-history.js index 1ae5123c6c8ea1..aae15eb752c862 100644 --- a/test/parallel/test-repl-programmatic-history.js +++ b/test/parallel/test-repl-programmatic-history.js @@ -9,7 +9,9 @@ const fs = require('fs'); const os = require('os'); const util = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-repl-require-self-referential.js b/test/parallel/test-repl-require-self-referential.js index 7ced6dbf11721e..9a4fe000bbb7e3 100644 --- a/test/parallel/test-repl-require-self-referential.js +++ b/test/parallel/test-repl-require-self-referential.js @@ -4,9 +4,11 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const assert = require('assert'); const { spawn } = require('child_process'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const selfRefModule = fixtures.path('self_ref_module'); const child = spawn(process.execPath, diff --git a/test/parallel/test-repl-require.js b/test/parallel/test-repl-require.js index fc431dea9f0f69..e740acef08b068 100644 --- a/test/parallel/test-repl-require.js +++ b/test/parallel/test-repl-require.js @@ -4,9 +4,11 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const assert = require('assert'); const net = require('net'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} process.chdir(fixtures.fixturesDir); const repl = require('repl'); diff --git a/test/parallel/test-repl-reverse-search.js b/test/parallel/test-repl-reverse-search.js index 93fb037c392c01..246488cbd0ef5f 100644 --- a/test/parallel/test-repl-reverse-search.js +++ b/test/parallel/test-repl-reverse-search.js @@ -9,7 +9,10 @@ const assert = require('assert'); const fs = require('fs'); const { inspect } = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + common.allowGlobals('aaaa'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-repl-sigint-nested-eval.js b/test/parallel/test-repl-sigint-nested-eval.js index 62eb46e0af6759..7955cf413f7c49 100644 --- a/test/parallel/test-repl-sigint-nested-eval.js +++ b/test/parallel/test-repl-sigint-nested-eval.js @@ -4,8 +4,12 @@ if (common.isWindows) { // No way to send CTRL_C_EVENT to processes from JS right now. common.skip('platform not supported'); } -if (!common.isMainThread) + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} const assert = require('assert'); const spawn = require('child_process').spawn; diff --git a/test/parallel/test-repl-sigint.js b/test/parallel/test-repl-sigint.js index 8ad0b2f5c2c853..f4087b11d488d6 100644 --- a/test/parallel/test-repl-sigint.js +++ b/test/parallel/test-repl-sigint.js @@ -4,8 +4,12 @@ if (common.isWindows) { // No way to send CTRL_C_EVENT to processes from JS right now. common.skip('platform not supported'); } -if (!common.isMainThread) + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} const assert = require('assert'); const spawn = require('child_process').spawn; diff --git a/test/parallel/test-repl-strict-mode-previews.js b/test/parallel/test-repl-strict-mode-previews.js index a05e11b39cf3ee..e7fc1ea5191ea3 100644 --- a/test/parallel/test-repl-strict-mode-previews.js +++ b/test/parallel/test-repl-strict-mode-previews.js @@ -5,7 +5,10 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfDumbTerminal(); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} if (process.argv[2] === 'child') { const stream = require('stream'); diff --git a/test/parallel/test-repl-tab-complete-import.js b/test/parallel/test-repl-tab-complete-import.js index fe9f7a3d11795b..f4ef408c89174c 100644 --- a/test/parallel/test-repl-tab-complete-import.js +++ b/test/parallel/test-repl-tab-complete-import.js @@ -7,8 +7,11 @@ const assert = require('assert'); const { builtinModules } = require('module'); const publicUnprefixedModules = builtinModules.filter((lib) => !lib.startsWith('_') && !lib.startsWith('node:')); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} // We have to change the directory to ../fixtures before requiring repl // in order to make the tests for completion of node_modules work properly diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js index ff1e927078ddf5..c79162129bd69b 100644 --- a/test/parallel/test-repl-tab-complete.js +++ b/test/parallel/test-repl-tab-complete.js @@ -34,9 +34,11 @@ const { builtinModules } = require('module'); const publicModules = builtinModules.filter((lib) => !lib.startsWith('_')); const hasInspector = process.features.inspector; +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} // We have to change the directory to ../fixtures before requiring repl // in order to make the tests for completion of node_modules work properly diff --git a/test/parallel/test-require-symlink.js b/test/parallel/test-require-symlink.js index 0c4477023bc90b..9ca543e8d64ca4 100644 --- a/test/parallel/test-require-symlink.js +++ b/test/parallel/test-require-symlink.js @@ -2,10 +2,14 @@ 'use strict'; const common = require('../common'); -if (!common.canCreateSymLink()) +if (!common.canCreateSymLink()) { common.skip('insufficient privileges'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const { spawn } = require('child_process'); diff --git a/test/parallel/test-runner-module-mocking.js b/test/parallel/test-runner-module-mocking.js index cb40df98147302..8502d4aa99a9b6 100644 --- a/test/parallel/test-runner-module-mocking.js +++ b/test/parallel/test-runner-module-mocking.js @@ -1,8 +1,9 @@ // Flags: --experimental-test-module-mocks --experimental-require-module 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) { +if (!isMainThread) { common.skip('registering customization hooks in Workers does not work'); } diff --git a/test/parallel/test-set-process-debug-port.js b/test/parallel/test-set-process-debug-port.js index d00a1ddf68ebb6..7f0cbe068549d0 100644 --- a/test/parallel/test-set-process-debug-port.js +++ b/test/parallel/test-set-process-debug-port.js @@ -2,7 +2,11 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const kMinPort = 1024; diff --git a/test/parallel/test-setproctitle.js b/test/parallel/test-setproctitle.js index 7c4287829f7c0a..b08302e0a35ac0 100644 --- a/test/parallel/test-setproctitle.js +++ b/test/parallel/test-setproctitle.js @@ -1,15 +1,16 @@ 'use strict'; // Original test written by Jakub Lekstan const common = require('../common'); +const { isMainThread } = require('worker_threads'); // FIXME add sunos support -if (common.isSunOS) +if (common.isSunOS || common.isIBMi) { common.skip(`Unsupported platform [${process.platform}]`); -// FIXME add IBMi support -if (common.isIBMi) - common.skip('Unsupported platform IBMi'); -if (!common.isMainThread) +} + +if (!isMainThread) { common.skip('Setting the process title from Workers is not supported'); +} const assert = require('assert'); const { exec, execSync } = require('child_process'); @@ -25,8 +26,9 @@ process.title = title; assert.strictEqual(process.title, title); // Test setting the title but do not try to run `ps` on Windows. -if (common.isWindows) +if (common.isWindows) { common.skip('Windows does not have "ps" utility'); +} try { execSync('command -v ps'); diff --git a/test/parallel/test-shadow-realm-import-value-resolve.js b/test/parallel/test-shadow-realm-import-value-resolve.js index ee1c17d67c12f1..eeb00509d53a6c 100644 --- a/test/parallel/test-shadow-realm-import-value-resolve.js +++ b/test/parallel/test-shadow-realm-import-value-resolve.js @@ -3,8 +3,11 @@ const common = require('../common'); const assert = require('assert'); const path = require('path'); +const { isMainThread } = require('worker_threads'); -common.skipIfWorker('process.chdir is not supported in workers.'); +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} async function main() { const realm = new ShadowRealm(); diff --git a/test/parallel/test-signal-args.js b/test/parallel/test-signal-args.js index 7b72ed6dcb92d5..28a077ecc1c7d9 100644 --- a/test/parallel/test-signal-args.js +++ b/test/parallel/test-signal-args.js @@ -3,10 +3,15 @@ const common = require('../common'); const assert = require('assert'); -if (common.isWindows) +if (common.isWindows) { common.skip('Sending signals with process.kill is not supported on Windows'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} process.once('SIGINT', common.mustCall((signal) => { assert.strictEqual(signal, 'SIGINT'); diff --git a/test/parallel/test-signal-handler.js b/test/parallel/test-signal-handler.js index 05ec4e7f73faf5..b84d2063a288db 100644 --- a/test/parallel/test-signal-handler.js +++ b/test/parallel/test-signal-handler.js @@ -23,10 +23,15 @@ const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { common.skip('SIGUSR1 and SIGHUP signals are not supported'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('Signal handling in Workers is not supported'); +} console.log(`process.pid: ${process.pid}`); diff --git a/test/parallel/test-stdio-pipe-access.js b/test/parallel/test-stdio-pipe-access.js index ac0e22c399a1b9..6bf6b107c60e92 100644 --- a/test/parallel/test-stdio-pipe-access.js +++ b/test/parallel/test-stdio-pipe-access.js @@ -1,7 +1,10 @@ 'use strict'; const common = require('../common'); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip("Workers don't have process-like stdio"); +} // Test if Node handles accessing process.stdin if it is a redirected // pipe without deadlocking diff --git a/test/parallel/test-stdio-pipe-redirect.js b/test/parallel/test-stdio-pipe-redirect.js index 8b48133c8b0317..69367119ed3402 100644 --- a/test/parallel/test-stdio-pipe-redirect.js +++ b/test/parallel/test-stdio-pipe-redirect.js @@ -1,7 +1,10 @@ 'use strict'; const common = require('../common'); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip("Workers don't have process-like stdio"); +} // Test if Node handles redirecting one child process stdout to another // process stdin without crashing. diff --git a/test/parallel/test-timers-immediate-unref-simple.js b/test/parallel/test-timers-immediate-unref-simple.js index 369894fcdebbae..fae8ad3eaea801 100644 --- a/test/parallel/test-timers-immediate-unref-simple.js +++ b/test/parallel/test-timers-immediate-unref-simple.js @@ -1,8 +1,9 @@ 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) { +if (!isMainThread) { // Note that test-timers-immediate-unref-nested-once works instead. common.skip('Worker bootstrapping works differently -> different timing'); } diff --git a/test/parallel/test-trace-events-api.js b/test/parallel/test-trace-events-api.js index 709f8de9097906..8792a40cf00c80 100644 --- a/test/parallel/test-trace-events-api.js +++ b/test/parallel/test-trace-events-api.js @@ -2,7 +2,12 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); // https://github.com/nodejs/node/issues/22767 +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + // https://github.com/nodejs/node/issues/22767 + common.skip('This test only works on a main thread'); +} try { require('trace_events'); diff --git a/test/parallel/test-trace-events-dynamic-enable.js b/test/parallel/test-trace-events-dynamic-enable.js index 69251944031e1f..5b2ce313421568 100644 --- a/test/parallel/test-trace-events-dynamic-enable.js +++ b/test/parallel/test-trace-events-dynamic-enable.js @@ -4,7 +4,13 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); // https://github.com/nodejs/node/issues/22767 + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + // https://github.com/nodejs/node/issues/22767 + common.skip('This test only works on a main thread'); +} const { internalBinding } = require('internal/test/binding'); diff --git a/test/parallel/test-warn-sigprof.js b/test/parallel/test-warn-sigprof.js index 36b0db78d82687..929deb69addb17 100644 --- a/test/parallel/test-warn-sigprof.js +++ b/test/parallel/test-warn-sigprof.js @@ -7,10 +7,15 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -if (common.isWindows) +if (common.isWindows) { common.skip('test does not apply to Windows'); +} -common.skipIfWorker(); // Worker inspector never has a server running +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} common.expectWarning('Warning', 'process.on(SIGPROF) is reserved while debugging'); diff --git a/test/parallel/test-worker-name.js b/test/parallel/test-worker-name.js index 952fcee0e05429..30f3710a826caf 100644 --- a/test/parallel/test-worker-name.js +++ b/test/parallel/test-worker-name.js @@ -4,10 +4,17 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); // This test requires both main and worker threads. + +const { + Worker, + isMainThread, +} = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); -const { Worker, isMainThread } = require('worker_threads'); if (isMainThread) { const name = 'Hello Thread'; diff --git a/test/report/test-report-signal.js b/test/report/test-report-signal.js index cb5efd9fc39fe2..03908ddcf24f16 100644 --- a/test/report/test-report-signal.js +++ b/test/report/test-report-signal.js @@ -3,11 +3,15 @@ // Test producing a report via signal. const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { return common.skip('Unsupported on Windows.'); +} -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('Signal reporting is only supported in the main thread'); +} const assert = require('assert'); const helper = require('../common/report'); diff --git a/test/sequential/test-fs-watch.js b/test/sequential/test-fs-watch.js index cb12acfc115a4b..8db27a79e33d0a 100644 --- a/test/sequential/test-fs-watch.js +++ b/test/sequential/test-fs-watch.js @@ -29,9 +29,11 @@ const fs = require('fs'); const path = require('path'); const tmpdir = require('../common/tmpdir'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const expectFilePath = common.isWindows || common.isLinux || diff --git a/test/sequential/test-heapdump.js b/test/sequential/test-heapdump.js index 1388623e61f939..f9df88375ae596 100644 --- a/test/sequential/test-heapdump.js +++ b/test/sequential/test-heapdump.js @@ -1,9 +1,11 @@ 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const { writeHeapSnapshot, getHeapSnapshot } = require('v8'); const assert = require('assert'); diff --git a/test/sequential/test-init.js b/test/sequential/test-init.js index 7195369e0e4f8e..dd5db5640d1f0c 100644 --- a/test/sequential/test-init.js +++ b/test/sequential/test-init.js @@ -24,9 +24,11 @@ const common = require('../common'); const assert = require('assert'); const child = require('child_process'); const fixtures = require('../common/fixtures'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} if (process.env.TEST_INIT) { return process.stdout.write('Loaded successfully!'); diff --git a/test/sequential/test-perf-hooks.js b/test/sequential/test-perf-hooks.js index 1e11f26571480d..847decdef18bfc 100644 --- a/test/sequential/test-perf-hooks.js +++ b/test/sequential/test-perf-hooks.js @@ -1,11 +1,12 @@ 'use strict'; -const common = require('../common'); +require('../common'); const { performance } = require('perf_hooks'); // Get the start time as soon as possible. const testStartTime = performance.now(); const assert = require('assert'); const { writeSync } = require('fs'); +const { isMainThread } = require('worker_threads'); // Use writeSync to stdout to avoid disturbing the loop. function log(str) { @@ -131,7 +132,7 @@ function checkValue(timing, name, min, max) { } let loopStart = initialTiming.loopStart; -if (common.isMainThread) { +if (isMainThread) { // In the main thread, the loop does not start until we start an operation // that requires it, e.g. setTimeout(). assert.strictEqual(initialTiming.loopStart, -1); From 552362a36607f5b7cb3ec0420b5ee8b5bcfa347e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 15:08:23 -0800 Subject: [PATCH 119/208] test: make common/index slightly less node.js specific * s/global/globalThis * clean up knownGlobals a bit, make it a Set instead of an array and condense a bit. PR-URL: https://github.com/nodejs/node/pull/56712 Reviewed-By: Yagiz Nizipli Reviewed-By: Matteo Collina --- test/common/index.js | 140 ++++++++++++++++++------------------------- 1 file changed, 57 insertions(+), 83 deletions(-) diff --git a/test/common/index.js b/test/common/index.js index 6086d584f0b595..3647f4554a4647 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -20,7 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const process = global.process; // Some tests tamper with the process global. +const process = globalThis.process; // Some tests tamper with the process globalThis. const assert = require('assert'); const { exec, execSync, spawn, spawnSync } = require('child_process'); @@ -266,7 +266,7 @@ function platformTimeout(ms) { return ms; } -let knownGlobals = [ +const knownGlobals = new Set([ AbortController, atob, btoa, @@ -278,88 +278,59 @@ let knownGlobals = [ setInterval, setTimeout, queueMicrotask, -]; - -if (global.gc) { - knownGlobals.push(global.gc); -} - -if (global.navigator) { - knownGlobals.push(global.navigator); -} - -if (global.Navigator) { - knownGlobals.push(global.Navigator); -} - -if (global.Performance) { - knownGlobals.push(global.Performance); -} -if (global.performance) { - knownGlobals.push(global.performance); -} -if (global.PerformanceMark) { - knownGlobals.push(global.PerformanceMark); -} -if (global.PerformanceMeasure) { - knownGlobals.push(global.PerformanceMeasure); -} - -// TODO(@ethan-arrowood): Similar to previous checks, this can be temporary -// until v16.x is EOL. Once all supported versions have structuredClone we -// can add this to the list above instead. -if (global.structuredClone) { - knownGlobals.push(global.structuredClone); -} - -if (global.EventSource) { - knownGlobals.push(EventSource); -} - -if (global.fetch) { - knownGlobals.push(fetch); -} -if (hasCrypto && global.crypto) { - knownGlobals.push(global.crypto); - knownGlobals.push(global.Crypto); - knownGlobals.push(global.CryptoKey); - knownGlobals.push(global.SubtleCrypto); -} -if (global.CustomEvent) { - knownGlobals.push(global.CustomEvent); -} -if (global.ReadableStream) { - knownGlobals.push( - global.ReadableStream, - global.ReadableStreamDefaultReader, - global.ReadableStreamBYOBReader, - global.ReadableStreamBYOBRequest, - global.ReadableByteStreamController, - global.ReadableStreamDefaultController, - global.TransformStream, - global.TransformStreamDefaultController, - global.WritableStream, - global.WritableStreamDefaultWriter, - global.WritableStreamDefaultController, - global.ByteLengthQueuingStrategy, - global.CountQueuingStrategy, - global.TextEncoderStream, - global.TextDecoderStream, - global.CompressionStream, - global.DecompressionStream, - ); -} + structuredClone, + fetch, +]); + +['gc', + // The following are assumed to be conditionally available in the + // global object currently. They can likely be added to the fixed + // set of known globals, however. + 'navigator', + 'Navigator', + 'performance', + 'Performance', + 'PerformanceMark', + 'PerformanceMeasure', + 'EventSource', + 'CustomEvent', + 'ReadableStream', + 'ReadableStreamDefaultReader', + 'ReadableStreamBYOBReader', + 'ReadableStreamBYOBRequest', + 'ReadableByteStreamController', + 'ReadableStreamDefaultController', + 'TransformStream', + 'TransformStreamDefaultController', + 'WritableStream', + 'WritableStreamDefaultWriter', + 'WritableStreamDefaultController', + 'ByteLengthQueuingStrategy', + 'CountQueuingStrategy', + 'TextEncoderStream', + 'TextDecoderStream', + 'CompressionStream', + 'DecompressionStream', + 'Storage', + 'localStorage', + 'sessionStorage', +].forEach((i) => { + if (globalThis[i] !== undefined) { + knownGlobals.add(globalThis[i]); + } +}); -if (global.Storage) { - knownGlobals.push( - global.localStorage, - global.sessionStorage, - global.Storage, - ); +if (hasCrypto) { + knownGlobals.add(globalThis.crypto); + knownGlobals.add(globalThis.Crypto); + knownGlobals.add(globalThis.CryptoKey); + knownGlobals.add(globalThis.SubtleCrypto); } function allowGlobals(...allowlist) { - knownGlobals = knownGlobals.concat(allowlist); + for (const val of allowlist) { + knownGlobals.add(val); + } } if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { @@ -371,10 +342,13 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { function leakedGlobals() { const leaked = []; - for (const val in global) { + for (const val in globalThis) { // globalThis.crypto is a getter that throws if Node.js was compiled - // without OpenSSL. - if (val !== 'crypto' && !knownGlobals.includes(global[val])) { + // without OpenSSL so we'll skip it if it is not available. + if (val === 'crypto' && !hasCrypto) { + continue; + } + if (!knownGlobals.has(globalThis[val])) { leaked.push(val); } } From 97a3a8204c7c0eb35fc6c11274a7aee5d2ea3ddc Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 15:30:30 -0800 Subject: [PATCH 120/208] test: replace more uses of `global` with `globalThis` PR-URL: https://github.com/nodejs/node/pull/56712 Reviewed-By: Yagiz Nizipli Reviewed-By: Matteo Collina --- test/parallel/test-aborted-util.js | 2 +- .../test-abortsignal-drop-settled-signals.mjs | 10 +-- test/parallel/test-assert-checktag.js | 8 +-- .../test-async-hooks-destroy-on-gc.js | 2 +- .../test-async-hooks-disable-gc-tracking.js | 2 +- ...test-async-hooks-prevent-double-destroy.js | 2 +- test/parallel/test-cli-eval.js | 2 +- test/parallel/test-common-gc.js | 4 +- .../parallel/test-console-assign-undefined.js | 16 ++--- test/parallel/test-console-instance.js | 4 +- test/parallel/test-console-self-assign.js | 2 +- test/parallel/test-crypto-dh-leak.js | 2 +- test/parallel/test-domain-crypto.js | 2 +- .../test-eventtarget-memoryleakwarning.js | 2 +- test/parallel/test-eventtarget.js | 2 +- .../test-finalization-registry-shutdown.js | 2 +- test/parallel/test-fs-filehandle.js | 2 +- .../test-fs-promises-file-handle-close.js | 2 +- test/parallel/test-fs-write-reuse-callback.js | 2 +- test/parallel/test-fs-write.js | 8 ++- .../test-gc-http-client-connaborted.js | 2 +- test/parallel/test-gc-net-timeout.js | 2 +- test/parallel/test-gc-tls-external-memory.js | 2 +- test/parallel/test-global-setters.js | 12 ++-- test/parallel/test-global.js | 14 ++-- ...-h2leak-destroy-session-on-socket-ended.js | 4 +- .../test-heapdump-async-hooks-init-promise.js | 2 +- .../test-http-agent-domain-reused-gc.js | 2 +- test/parallel/test-http-parser-bad-ref.js | 2 +- ...t-http-server-connections-checking-leak.js | 2 +- .../test-http-server-keepalive-req-gc.js | 4 +- test/parallel/test-http2-createwritereq.js | 2 +- ...-http2-session-gc-while-write-scheduled.js | 2 +- ...tp2-write-finishes-after-stream-destroy.js | 2 +- ...-https-server-connections-checking-leak.js | 2 +- .../test-inspector-scriptparsed-context.js | 4 +- ...r-vm-global-accessors-getter-sideeffect.js | 2 +- test/parallel/test-module-relative-lookup.js | 2 +- test/parallel/test-net-connect-memleak.js | 2 +- .../test-net-write-fully-async-buffer.js | 2 +- .../test-net-write-fully-async-hex-string.js | 2 +- test/parallel/test-performance-gc.js | 4 +- test/parallel/test-primitive-timer-leak.js | 2 +- test/parallel/test-repl-autolibs.js | 6 +- test/parallel/test-repl-underscore.js | 4 +- test/parallel/test-repl-use-global.js | 6 +- test/parallel/test-repl.js | 6 +- test/parallel/test-runner-mock-timers.js | 68 +++++++++---------- test/parallel/test-timers-api-refs.js | 12 ++-- .../parallel/test-timers-process-tampering.js | 4 +- test/parallel/test-tls-connect-memleak.js | 2 +- test/parallel/test-tls-securepair-leak.js | 2 +- ...test-tls-transport-destroy-after-own-gc.js | 6 +- test/parallel/test-trace-events-api.js | 2 +- test/parallel/test-util-format.js | 4 +- test/parallel/test-util-inspect.js | 6 +- test/parallel/test-vm-basic.js | 6 +- .../test-vm-create-and-run-in-context.js | 2 +- test/parallel/test-vm-cross-context.js | 2 +- test/parallel/test-vm-global-get-own.js | 26 +++---- test/parallel/test-vm-measure-memory-lazy.js | 10 +-- test/parallel/test-vm-module-basic.js | 12 ++-- .../test-vm-new-script-new-context.js | 34 +++++----- .../test-vm-new-script-this-context.js | 32 ++++----- test/parallel/test-vm-run-in-new-context.js | 34 +++++----- test/parallel/test-vm-static-this.js | 26 +++---- test/parallel/test-webstorage.js | 4 +- .../parallel/test-whatwg-url-custom-global.js | 4 +- test/parallel/test-worker-cli-options.js | 2 +- ...orker-message-channel-sharedarraybuffer.js | 2 +- .../parallel/test-worker-message-port-move.js | 2 +- ...est-worker-workerdata-sharedarraybuffer.js | 2 +- .../test-zlib-invalid-input-memory.js | 2 +- test/parallel/test-zlib-unused-weak.js | 4 +- 74 files changed, 246 insertions(+), 242 deletions(-) diff --git a/test/parallel/test-aborted-util.js b/test/parallel/test-aborted-util.js index 4bc45b9f5529bb..0566204ccdb074 100644 --- a/test/parallel/test-aborted-util.js +++ b/test/parallel/test-aborted-util.js @@ -32,7 +32,7 @@ test('Aborted with gc cleanup', async () => { const { promise, resolve } = Promise.withResolvers(); setImmediate(() => { - global.gc(); + globalThis.gc(); ac.abort(); strictEqual(ac.signal.aborted, true); strictEqual(getEventListeners(ac.signal, 'abort').length, 0); diff --git a/test/parallel/test-abortsignal-drop-settled-signals.mjs b/test/parallel/test-abortsignal-drop-settled-signals.mjs index 0abcaf81012716..b300b0e223fc93 100644 --- a/test/parallel/test-abortsignal-drop-settled-signals.mjs +++ b/test/parallel/test-abortsignal-drop-settled-signals.mjs @@ -16,7 +16,7 @@ function makeSubsequentCalls(limit, done, holdReferences = false) { // This setImmediate is necessary to ensure that in the last iteration the remaining signal is GCed (if not // retained) setImmediate(() => { - global.gc(); + globalThis.gc(); done(ac.signal, dependantSymbol); }); return; @@ -50,7 +50,7 @@ function runShortLivedSourceSignal(limit, done) { function run(iteration) { if (iteration > limit) { - global.gc(); + globalThis.gc(); done(signalRefs); return; } @@ -74,9 +74,9 @@ function runWithOrphanListeners(limit, done) { const ac = new AbortController(); if (iteration > limit) { setImmediate(() => { - global.gc(); + globalThis.gc(); setImmediate(() => { - global.gc(); + globalThis.gc(); done(composedSignalRefs); }); @@ -147,7 +147,7 @@ it('drops settled dependant signals when signal is composite', (t, done) => { t.assert.strictEqual(controllers[1].signal[kDependantSignals].size, 1); setImmediate(() => { - global.gc({ execution: 'async' }).then(async () => { + globalThis.gc({ execution: 'async' }).then(async () => { await gcUntil('all signals are GCed', () => { const totalDependantSignals = Math.max( controllers[0].signal[kDependantSignals].size, diff --git a/test/parallel/test-assert-checktag.js b/test/parallel/test-assert-checktag.js index 7587939436f40d..b86a1bde7f096d 100644 --- a/test/parallel/test-assert-checktag.js +++ b/test/parallel/test-assert-checktag.js @@ -49,13 +49,13 @@ test('', { skip: !hasCrypto }, () => { { // At the moment global has its own type tag const fakeGlobal = {}; - Object.setPrototypeOf(fakeGlobal, Object.getPrototypeOf(global)); - for (const prop of Object.keys(global)) { + Object.setPrototypeOf(fakeGlobal, Object.getPrototypeOf(globalThis)); + for (const prop of Object.keys(globalThis)) { fakeGlobal[prop] = global[prop]; } - assert.notDeepEqual(fakeGlobal, global); + assert.notDeepEqual(fakeGlobal, globalThis); // Message will be truncated anyway, don't validate - assert.throws(() => assert.deepStrictEqual(fakeGlobal, global), + assert.throws(() => assert.deepStrictEqual(fakeGlobal, globalThis), assert.AssertionError); } diff --git a/test/parallel/test-async-hooks-destroy-on-gc.js b/test/parallel/test-async-hooks-destroy-on-gc.js index fe6325e189734b..dd7eef8776cdf3 100644 --- a/test/parallel/test-async-hooks-destroy-on-gc.js +++ b/test/parallel/test-async-hooks-destroy-on-gc.js @@ -22,6 +22,6 @@ let asyncId = null; } setImmediate(() => { - global.gc(); + globalThis.gc(); setImmediate(() => assert.ok(destroyedIds.has(asyncId))); }); diff --git a/test/parallel/test-async-hooks-disable-gc-tracking.js b/test/parallel/test-async-hooks-disable-gc-tracking.js index 84c5043aad3335..87b096c258121c 100644 --- a/test/parallel/test-async-hooks-disable-gc-tracking.js +++ b/test/parallel/test-async-hooks-disable-gc-tracking.js @@ -14,7 +14,7 @@ const hook = async_hooks.createHook({ new async_hooks.AsyncResource('foobar', { requireManualDestroy: true }); setImmediate(() => { - global.gc(); + globalThis.gc(); setImmediate(() => { hook.disable(); }); diff --git a/test/parallel/test-async-hooks-prevent-double-destroy.js b/test/parallel/test-async-hooks-prevent-double-destroy.js index 689dc399f9d2f2..4aa55a5a6c87bf 100644 --- a/test/parallel/test-async-hooks-prevent-double-destroy.js +++ b/test/parallel/test-async-hooks-prevent-double-destroy.js @@ -17,7 +17,7 @@ const hook = async_hooks.createHook({ } setImmediate(() => { - global.gc(); + globalThis.gc(); setImmediate(() => { hook.disable(); }); diff --git a/test/parallel/test-cli-eval.js b/test/parallel/test-cli-eval.js index 24031581fd737e..9ec0fece409068 100644 --- a/test/parallel/test-cli-eval.js +++ b/test/parallel/test-cli-eval.js @@ -345,7 +345,7 @@ child.exec( // Regression test for https://github.com/nodejs/node/issues/45336 child.execFile(process.execPath, ['-p', - 'Object.defineProperty(global, "fs", { configurable: false });' + + 'Object.defineProperty(globalThis, "fs", { configurable: false });' + 'fs === require("node:fs")'], common.mustSucceed((stdout) => { assert.match(stdout, /^true/); diff --git a/test/parallel/test-common-gc.js b/test/parallel/test-common-gc.js index f7d73ccd0423e3..54abe3695cc3be 100644 --- a/test/parallel/test-common-gc.js +++ b/test/parallel/test-common-gc.js @@ -5,10 +5,10 @@ const { onGC } = require('../common/gc'); { onGC({}, { ongc: common.mustCall() }); - global.gc(); + globalThis.gc(); } { onGC(process, { ongc: common.mustNotCall() }); - global.gc(); + globalThis.gc(); } diff --git a/test/parallel/test-console-assign-undefined.js b/test/parallel/test-console-assign-undefined.js index 1021307b3c5f22..7f5b0e04727679 100644 --- a/test/parallel/test-console-assign-undefined.js +++ b/test/parallel/test-console-assign-undefined.js @@ -1,28 +1,28 @@ 'use strict'; -// Patch global.console before importing modules that may modify the console +// Patch globalThis.console before importing modules that may modify the console // object. -const tmp = global.console; -global.console = 42; +const tmp = globalThis.console; +globalThis.console = 42; require('../common'); const assert = require('assert'); // Originally the console had a getter. Test twice to verify it had no side // effect. -assert.strictEqual(global.console, 42); -assert.strictEqual(global.console, 42); +assert.strictEqual(globalThis.console, 42); +assert.strictEqual(globalThis.console, 42); assert.throws( () => console.log('foo'), { name: 'TypeError' } ); -global.console = 1; -assert.strictEqual(global.console, 1); +globalThis.console = 1; +assert.strictEqual(globalThis.console, 1); assert.strictEqual(console, 1); // Reset the console -global.console = tmp; +globalThis.console = tmp; console.log('foo'); diff --git a/test/parallel/test-console-instance.js b/test/parallel/test-console-instance.js index bf22314e22e031..0364a6213bc726 100644 --- a/test/parallel/test-console-instance.js +++ b/test/parallel/test-console-instance.js @@ -36,9 +36,9 @@ process.stdout.write = process.stderr.write = common.mustNotCall(); // Make sure that the "Console" function exists. assert.strictEqual(typeof Console, 'function'); -assert.strictEqual(requiredConsole, global.console); +assert.strictEqual(requiredConsole, globalThis.console); // Make sure the custom instanceof of Console works -assert.ok(global.console instanceof Console); +assert.ok(globalThis.console instanceof Console); assert.ok(!({} instanceof Console)); // Make sure that the Console constructor throws diff --git a/test/parallel/test-console-self-assign.js b/test/parallel/test-console-self-assign.js index 53c54ab9a327cf..46f9bc93d4f2bf 100644 --- a/test/parallel/test-console-self-assign.js +++ b/test/parallel/test-console-self-assign.js @@ -3,4 +3,4 @@ require('../common'); // Assigning to itself should not throw. -global.console = global.console; // eslint-disable-line no-self-assign +globalThis.console = globalThis.console; // eslint-disable-line no-self-assign diff --git a/test/parallel/test-crypto-dh-leak.js b/test/parallel/test-crypto-dh-leak.js index 3b5051feb43cd8..df1ba89737c619 100644 --- a/test/parallel/test-crypto-dh-leak.js +++ b/test/parallel/test-crypto-dh-leak.js @@ -22,7 +22,7 @@ const before = process.memoryUsage.rss(); dh.setPrivateKey(privateKey); } } -global.gc(); +globalThis.gc(); const after = process.memoryUsage.rss(); // RSS should stay the same, ceteris paribus, but allow for diff --git a/test/parallel/test-domain-crypto.js b/test/parallel/test-domain-crypto.js index e0a470bd9db515..47eb33f70aae45 100644 --- a/test/parallel/test-domain-crypto.js +++ b/test/parallel/test-domain-crypto.js @@ -31,7 +31,7 @@ const crypto = require('crypto'); // Pollution of global is intentional as part of test. common.allowGlobals(require('domain')); // See https://github.com/nodejs/node/commit/d1eff9ab -global.domain = require('domain'); +globalThis.domain = require('domain'); // Should not throw a 'TypeError: undefined is not a function' exception crypto.randomBytes(8); diff --git a/test/parallel/test-eventtarget-memoryleakwarning.js b/test/parallel/test-eventtarget-memoryleakwarning.js index 2c907165d865d9..2ec48720c8a8c5 100644 --- a/test/parallel/test-eventtarget-memoryleakwarning.js +++ b/test/parallel/test-eventtarget-memoryleakwarning.js @@ -103,7 +103,7 @@ common.expectWarning({ }); await setTimeout(0); - global.gc(); + globalThis.gc(); } })().then(common.mustCall(), common.mustNotCall()); } diff --git a/test/parallel/test-eventtarget.js b/test/parallel/test-eventtarget.js index cbe7eb3b0e8687..7153da172c1cf6 100644 --- a/test/parallel/test-eventtarget.js +++ b/test/parallel/test-eventtarget.js @@ -683,7 +683,7 @@ let asyncTest = Promise.resolve(); const et = new EventTarget(); et.addEventListener('foo', common.mustNotCall(), { [kWeakHandler]: {} }); setImmediate(() => { - global.gc(); + globalThis.gc(); et.dispatchEvent(new Event('foo')); }); } diff --git a/test/parallel/test-finalization-registry-shutdown.js b/test/parallel/test-finalization-registry-shutdown.js index f896aa2f285c75..e288d8fecca7e6 100644 --- a/test/parallel/test-finalization-registry-shutdown.js +++ b/test/parallel/test-finalization-registry-shutdown.js @@ -19,5 +19,5 @@ process.on('exit', () => { // This is the final chance to execute JavaScript. register(); // Queue a FinalizationRegistryCleanupTask by a testing gc request. - global.gc(); + globalThis.gc(); }); diff --git a/test/parallel/test-fs-filehandle.js b/test/parallel/test-fs-filehandle.js index 818a3824904431..bcb62da9e4c2cc 100644 --- a/test/parallel/test-fs-filehandle.js +++ b/test/parallel/test-fs-filehandle.js @@ -35,6 +35,6 @@ common.expectWarning({ 'DeprecationWarning': [[deprecationWarning, 'DEP0137']] }); -global.gc(); +globalThis.gc(); setTimeout(() => {}, 10); diff --git a/test/parallel/test-fs-promises-file-handle-close.js b/test/parallel/test-fs-promises-file-handle-close.js index d6417964746720..288bc31ea0ada5 100644 --- a/test/parallel/test-fs-promises-file-handle-close.js +++ b/test/parallel/test-fs-promises-file-handle-close.js @@ -32,7 +32,7 @@ doOpen().then(common.mustCall((fd) => { })).then(common.mustCall(() => { setImmediate(() => { // The FileHandle should be out-of-scope and no longer accessed now. - global.gc(); + globalThis.gc(); // Wait an extra event loop turn, as the warning is emitted from the // native layer in an unref()'ed setImmediate() callback. diff --git a/test/parallel/test-fs-write-reuse-callback.js b/test/parallel/test-fs-write-reuse-callback.js index 82c772ab340fed..c80902e54103fc 100644 --- a/test/parallel/test-fs-write-reuse-callback.js +++ b/test/parallel/test-fs-write-reuse-callback.js @@ -20,7 +20,7 @@ let done = 0; const ondone = common.mustSucceed(() => { if (++done < writes) { - if (done % 25 === 0) global.gc(); + if (done % 25 === 0) globalThis.gc(); setImmediate(write); } else { assert.strictEqual( diff --git a/test/parallel/test-fs-write.js b/test/parallel/test-fs-write.js index a4aeb4e16a748f..82f3425de2aa16 100644 --- a/test/parallel/test-fs-write.js +++ b/test/parallel/test-fs-write.js @@ -39,14 +39,18 @@ const { createExternalizableString, externalizeString, isOneByteString, -} = global; +} = globalThis; + +assert.notStrictEqual(createExternalizableString, undefined); +assert.notStrictEqual(externalizeString, undefined); +assert.notStrictEqual(isOneByteString, undefined); // Account for extra globals exposed by --expose_externalize_string. common.allowGlobals( createExternalizableString, externalizeString, isOneByteString, - global.x, + globalThis.x, ); { diff --git a/test/parallel/test-gc-http-client-connaborted.js b/test/parallel/test-gc-http-client-connaborted.js index 93ca8ee4de59f1..e52a555d788085 100644 --- a/test/parallel/test-gc-http-client-connaborted.js +++ b/test/parallel/test-gc-http-client-connaborted.js @@ -53,7 +53,7 @@ setImmediate(status); function status() { if (done > 0) { createClients = false; - global.gc(); + globalThis.gc(); console.log(`done/collected/total: ${done}/${countGC}/${count}`); if (countGC === count) { server.close(); diff --git a/test/parallel/test-gc-net-timeout.js b/test/parallel/test-gc-net-timeout.js index c4f74b34b79ec9..7a195c26bcdd6b 100644 --- a/test/parallel/test-gc-net-timeout.js +++ b/test/parallel/test-gc-net-timeout.js @@ -64,7 +64,7 @@ setImmediate(status); function status() { if (done > 0) { createClients = false; - global.gc(); + globalThis.gc(); console.log(`done/collected/total: ${done}/${countGC}/${count}`); if (countGC === count) { server.close(); diff --git a/test/parallel/test-gc-tls-external-memory.js b/test/parallel/test-gc-tls-external-memory.js index dcf38e11f6c6bf..480b1086b5395e 100644 --- a/test/parallel/test-gc-tls-external-memory.js +++ b/test/parallel/test-gc-tls-external-memory.js @@ -27,7 +27,7 @@ connect(); function connect() { if (runs % 64 === 0) - global.gc(); + globalThis.gc(); const externalMemoryUsage = process.memoryUsage().external; assert(externalMemoryUsage >= 0, `${externalMemoryUsage} < 0`); if (runs++ === 512) { diff --git a/test/parallel/test-global-setters.js b/test/parallel/test-global-setters.js index 7fd070ed8e1c4e..2da1097867261f 100644 --- a/test/parallel/test-global-setters.js +++ b/test/parallel/test-global-setters.js @@ -8,20 +8,20 @@ assert.strictEqual(process, _process); // eslint-disable-next-line no-global-assign process = 'asdf'; assert.strictEqual(process, 'asdf'); -assert.strictEqual(global.process, 'asdf'); -global.process = _process; +assert.strictEqual(globalThis.process, 'asdf'); +globalThis.process = _process; assert.strictEqual(process, _process); assert.strictEqual( - typeof Object.getOwnPropertyDescriptor(global, 'process').get, + typeof Object.getOwnPropertyDescriptor(globalThis, 'process').get, 'function'); assert.strictEqual(Buffer, _Buffer); // eslint-disable-next-line no-global-assign Buffer = 'asdf'; assert.strictEqual(Buffer, 'asdf'); -assert.strictEqual(global.Buffer, 'asdf'); -global.Buffer = _Buffer; +assert.strictEqual(globalThis.Buffer, 'asdf'); +globalThis.Buffer = _Buffer; assert.strictEqual(Buffer, _Buffer); assert.strictEqual( - typeof Object.getOwnPropertyDescriptor(global, 'Buffer').get, + typeof Object.getOwnPropertyDescriptor(globalThis, 'Buffer').get, 'function'); diff --git a/test/parallel/test-global.js b/test/parallel/test-global.js index 37f4db5252be5c..835bcc75a83e3b 100644 --- a/test/parallel/test-global.js +++ b/test/parallel/test-global.js @@ -60,9 +60,9 @@ for (const moduleName of builtinModules) { 'crypto', 'navigator', ]; - assert.deepStrictEqual(new Set(Object.keys(global)), new Set(expected)); + assert.deepStrictEqual(new Set(Object.keys(globalThis)), new Set(expected)); expected.forEach((value) => { - const desc = Object.getOwnPropertyDescriptor(global, value); + const desc = Object.getOwnPropertyDescriptor(globalThis, value); if (typeof desc.value === 'function') { assert.strictEqual(desc.value.name, value); } else if (typeof desc.get === 'function') { @@ -74,15 +74,15 @@ for (const moduleName of builtinModules) { common.allowGlobals('bar', 'foo'); baseFoo = 'foo'; // eslint-disable-line no-undef -global.baseBar = 'bar'; +globalThis.baseBar = 'bar'; -assert.strictEqual(global.baseFoo, 'foo', - `x -> global.x failed: global.baseFoo = ${global.baseFoo}`); +assert.strictEqual(globalThis.baseFoo, 'foo', + `x -> globalThis.x failed: globalThis.baseFoo = ${globalThis.baseFoo}`); assert.strictEqual(baseBar, // eslint-disable-line no-undef 'bar', // eslint-disable-next-line no-undef - `global.x -> x failed: baseBar = ${baseBar}`); + `globalThis.x -> x failed: baseBar = ${baseBar}`); const mod = require(fixtures.path('global', 'plain')); const fooBar = mod.fooBar; @@ -91,4 +91,4 @@ assert.strictEqual(fooBar.foo, 'foo'); assert.strictEqual(fooBar.bar, 'bar'); -assert.strictEqual(Object.prototype.toString.call(global), '[object global]'); +assert.strictEqual(Object.prototype.toString.call(globalThis), '[object global]'); diff --git a/test/parallel/test-h2leak-destroy-session-on-socket-ended.js b/test/parallel/test-h2leak-destroy-session-on-socket-ended.js index 3f0fe3e69d924d..af692b278f7d06 100644 --- a/test/parallel/test-h2leak-destroy-session-on-socket-ended.js +++ b/test/parallel/test-h2leak-destroy-session-on-socket-ended.js @@ -31,8 +31,8 @@ server.on('secureConnection', (s) => { firstServerStream = null; setImmediate(() => { - global.gc(); - global.gc(); + globalThis.gc(); + globalThis.gc(); server.close(); }); diff --git a/test/parallel/test-heapdump-async-hooks-init-promise.js b/test/parallel/test-heapdump-async-hooks-init-promise.js index c59cb89baa3d18..63b26843d1254e 100644 --- a/test/parallel/test-heapdump-async-hooks-init-promise.js +++ b/test/parallel/test-heapdump-async-hooks-init-promise.js @@ -43,4 +43,4 @@ async_hooks.createHook({ Promise.resolve().then(() => {}); -setImmediate(global.gc); +setImmediate(globalThis.gc); diff --git a/test/parallel/test-http-agent-domain-reused-gc.js b/test/parallel/test-http-agent-domain-reused-gc.js index 35146ee688eb9b..4f12c2ede839cd 100644 --- a/test/parallel/test-http-agent-domain-reused-gc.js +++ b/test/parallel/test-http-agent-domain-reused-gc.js @@ -26,7 +26,7 @@ async_hooks.createHook({ }, before(id) { if (id === reusedHandleId) { - global.gc(); + globalThis.gc(); checkBeforeCalled(); } } diff --git a/test/parallel/test-http-parser-bad-ref.js b/test/parallel/test-http-parser-bad-ref.js index 2c1bfe67485db7..e34054eca67063 100644 --- a/test/parallel/test-http-parser-bad-ref.js +++ b/test/parallel/test-http-parser-bad-ref.js @@ -18,7 +18,7 @@ let messagesComplete = 0; function flushPool() { Buffer.allocUnsafe(Buffer.poolSize - 1); - global.gc(); + globalThis.gc(); } function demoBug(part1, part2) { diff --git a/test/parallel/test-http-server-connections-checking-leak.js b/test/parallel/test-http-server-connections-checking-leak.js index 282c9a569fba7d..38dca83102cfea 100644 --- a/test/parallel/test-http-server-connections-checking-leak.js +++ b/test/parallel/test-http-server-connections-checking-leak.js @@ -20,5 +20,5 @@ for (let i = 0; i < max; i++) { } setImmediate(() => { - global.gc(); + globalThis.gc(); }); diff --git a/test/parallel/test-http-server-keepalive-req-gc.js b/test/parallel/test-http-server-keepalive-req-gc.js index 3bfb6c9600cc24..c827cd19ad7222 100644 --- a/test/parallel/test-http-server-keepalive-req-gc.js +++ b/test/parallel/test-http-server-keepalive-req-gc.js @@ -16,8 +16,8 @@ const server = createServer(common.mustCall((req, res) => { req.on('end', common.mustCall(() => { setImmediate(async () => { client.end(); - await global.gc({ type: 'major', execution: 'async' }); - await global.gc({ type: 'major', execution: 'async' }); + await globalThis.gc({ type: 'major', execution: 'async' }); + await globalThis.gc({ type: 'major', execution: 'async' }); }); })); res.end('hello world'); diff --git a/test/parallel/test-http2-createwritereq.js b/test/parallel/test-http2-createwritereq.js index 3015ad6c642801..6d2b07d5849ad0 100644 --- a/test/parallel/test-http2-createwritereq.js +++ b/test/parallel/test-http2-createwritereq.js @@ -69,7 +69,7 @@ server.listen(0, common.mustCall(function() { req.destroy = function(...args) { // Schedule a garbage collection event at the end of the current // MakeCallback() run. - process.nextTick(global.gc); + process.nextTick(globalThis.gc); return origDestroy.call(this, ...args); }; diff --git a/test/parallel/test-http2-session-gc-while-write-scheduled.js b/test/parallel/test-http2-session-gc-while-write-scheduled.js index 62379f7d7ed678..9693ded17c0a18 100644 --- a/test/parallel/test-http2-session-gc-while-write-scheduled.js +++ b/test/parallel/test-http2-session-gc-while-write-scheduled.js @@ -23,6 +23,6 @@ const tick = require('../common/tick'); // This schedules a write. client.settings(http2.getDefaultSettings()); client = null; - global.gc(); + globalThis.gc(); }); } diff --git a/test/parallel/test-http2-write-finishes-after-stream-destroy.js b/test/parallel/test-http2-write-finishes-after-stream-destroy.js index ed8833fdb926b1..bf9de8f9291917 100644 --- a/test/parallel/test-http2-write-finishes-after-stream-destroy.js +++ b/test/parallel/test-http2-write-finishes-after-stream-destroy.js @@ -9,7 +9,7 @@ const { duplexPair } = require('stream'); // Make sure the Http2Stream destructor works, since we don't clean the // stream up like we would otherwise do. -process.on('exit', global.gc); +process.on('exit', globalThis.gc); { const [ clientSide, serverSide ] = duplexPair(); diff --git a/test/parallel/test-https-server-connections-checking-leak.js b/test/parallel/test-https-server-connections-checking-leak.js index e920c8e403705f..f79149ef70a9ab 100644 --- a/test/parallel/test-https-server-connections-checking-leak.js +++ b/test/parallel/test-https-server-connections-checking-leak.js @@ -25,5 +25,5 @@ for (let i = 0; i < max; i++) { } setImmediate(() => { - global.gc(); + globalThis.gc(); }); diff --git a/test/parallel/test-inspector-scriptparsed-context.js b/test/parallel/test-inspector-scriptparsed-context.js index bd86ba53d4c986..31ae896c818b82 100644 --- a/test/parallel/test-inspector-scriptparsed-context.js +++ b/test/parallel/test-inspector-scriptparsed-context.js @@ -8,8 +8,8 @@ const script = ` 'use strict'; const assert = require('assert'); const vm = require('vm'); - global.outer = true; - global.inner = false; + globalThis.outer = true; + globalThis.inner = false; const context = vm.createContext({ outer: false, inner: true diff --git a/test/parallel/test-inspector-vm-global-accessors-getter-sideeffect.js b/test/parallel/test-inspector-vm-global-accessors-getter-sideeffect.js index 8b367e98c37f49..89414e50346871 100644 --- a/test/parallel/test-inspector-vm-global-accessors-getter-sideeffect.js +++ b/test/parallel/test-inspector-vm-global-accessors-getter-sideeffect.js @@ -14,7 +14,7 @@ session.connect(); const context = vm.createContext({ get a() { - global.foo = '1'; + globalThis.foo = '1'; return 100; } }); diff --git a/test/parallel/test-module-relative-lookup.js b/test/parallel/test-module-relative-lookup.js index 675c12c541fd4d..76af2b3b30c2e0 100644 --- a/test/parallel/test-module-relative-lookup.js +++ b/test/parallel/test-module-relative-lookup.js @@ -2,7 +2,7 @@ const common = require('../common'); const assert = require('assert'); -const _module = require('module'); // Avoid collision with global.module +const _module = require('module'); // Avoid collision with globalThis.module // Current directory gets highest priority for local modules function testFirstInPath(moduleName, isLocalModule) { diff --git a/test/parallel/test-net-connect-memleak.js b/test/parallel/test-net-connect-memleak.js index 84f643746838b6..079e45f7223a8b 100644 --- a/test/parallel/test-net-connect-memleak.js +++ b/test/parallel/test-net-connect-memleak.js @@ -49,7 +49,7 @@ const gcListener = { ongc() { collected = true; } }; } function done(sock) { - global.gc(); + globalThis.gc(); setImmediate(() => { assert.strictEqual(collected, true); sock.end(); diff --git a/test/parallel/test-net-write-fully-async-buffer.js b/test/parallel/test-net-write-fully-async-buffer.js index 0dddb51bd76ade..042dd79cb03127 100644 --- a/test/parallel/test-net-write-fully-async-buffer.js +++ b/test/parallel/test-net-write-fully-async-buffer.js @@ -23,7 +23,7 @@ const server = net.createServer(common.mustCall(function(conn) { } while (conn.write(Buffer.from(data))); - global.gc({ type: 'minor' }); + globalThis.gc({ type: 'minor' }); // The buffer allocated above should still be alive. } diff --git a/test/parallel/test-net-write-fully-async-hex-string.js b/test/parallel/test-net-write-fully-async-hex-string.js index 37b5cd75c1385c..b80b09f3244585 100644 --- a/test/parallel/test-net-write-fully-async-hex-string.js +++ b/test/parallel/test-net-write-fully-async-hex-string.js @@ -21,7 +21,7 @@ const server = net.createServer(common.mustCall(function(conn) { } while (conn.write(data, 'hex')); - global.gc({ type: 'minor' }); + globalThis.gc({ type: 'minor' }); // The buffer allocated inside the .write() call should still be alive. } diff --git a/test/parallel/test-performance-gc.js b/test/parallel/test-performance-gc.js index 9c4a3a850a22dd..9dddf7207f59d2 100644 --- a/test/parallel/test-performance-gc.js +++ b/test/parallel/test-performance-gc.js @@ -40,7 +40,7 @@ const kinds = [ obs.disconnect(); })); obs.observe({ entryTypes: ['gc'] }); - global.gc(); + globalThis.gc(); // Keep the event loop alive to witness the GC async callback happen. setImmediate(() => setImmediate(() => 0)); } @@ -51,6 +51,6 @@ const kinds = [ process.on('beforeExit', () => { assert(!didCall); didCall = true; - global.gc(); + globalThis.gc(); }); } diff --git a/test/parallel/test-primitive-timer-leak.js b/test/parallel/test-primitive-timer-leak.js index d590a0347b9cac..a0fe2765e1282d 100644 --- a/test/parallel/test-primitive-timer-leak.js +++ b/test/parallel/test-primitive-timer-leak.js @@ -5,7 +5,7 @@ const { onGC } = require('../common/gc'); // See https://github.com/nodejs/node/issues/53335 const poller = setInterval(() => { - global.gc(); + globalThis.gc(); }, 100); let count = 0; diff --git a/test/parallel/test-repl-autolibs.js b/test/parallel/test-repl-autolibs.js index 5cf3b1497221d0..a1eb476ef530b1 100644 --- a/test/parallel/test-repl-autolibs.js +++ b/test/parallel/test-repl-autolibs.js @@ -41,7 +41,7 @@ function test1() { assert.strictEqual(data, `${util.inspect(require('fs'), null, 2, false)}\n`); // Globally added lib matches required lib - assert.strictEqual(global.fs, require('fs')); + assert.strictEqual(globalThis.fs, require('fs')); test2(); } }; @@ -58,11 +58,11 @@ function test2() { // REPL response error message assert.strictEqual(data, '{}\n'); // Original value wasn't overwritten - assert.strictEqual(val, global.url); + assert.strictEqual(val, globalThis.url); } }; const val = {}; - global.url = val; + globalThis.url = val; common.allowGlobals(val); assert(!gotWrite); putIn.run(['url']); diff --git a/test/parallel/test-repl-underscore.js b/test/parallel/test-repl-underscore.js index 0f8103ce4573cd..8ce9de5563acfb 100644 --- a/test/parallel/test-repl-underscore.js +++ b/test/parallel/test-repl-underscore.js @@ -150,8 +150,8 @@ function testResetContextGlobal() { ]); // Delete globals leaked by REPL when `useGlobal` is `true` - delete global.module; - delete global.require; + delete globalThis.module; + delete globalThis.require; } function testError() { diff --git a/test/parallel/test-repl-use-global.js b/test/parallel/test-repl-use-global.js index 3457d0c5ba7210..06cda54f4d6fa2 100644 --- a/test/parallel/test-repl-use-global.js +++ b/test/parallel/test-repl-use-global.js @@ -20,10 +20,10 @@ const globalTest = (useGlobal, cb, output) => (err, repl) => { let str = ''; output.on('data', (data) => (str += data)); - global.lunch = 'tacos'; - repl.write('global.lunch;\n'); + globalThis.lunch = 'tacos'; + repl.write('globalThis.lunch;\n'); repl.close(); - delete global.lunch; + delete globalThis.lunch; cb(null, str.trim()); }; diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js index d6c1cd6a9a7d6a..6c16d2a9679b21 100644 --- a/test/parallel/test-repl.js +++ b/test/parallel/test-repl.js @@ -36,7 +36,7 @@ const prompt_tcp = 'node via TCP socket> '; const moduleFilename = fixtures.path('a'); // Function for REPL to run -global.invoke_me = function(arg) { +globalThis.invoke_me = function(arg) { return `invoked ${arg}`; }; @@ -939,8 +939,8 @@ alternatively use dynamic import: const { default: alias, namedExport } = await socket.end(); } - common.allowGlobals(global.invoke_me, global.message, global.a, global.blah, - global.I, global.f, global.path, global.x, global.name, global.foo); + common.allowGlobals(globalThis.invoke_me, globalThis.message, globalThis.a, globalThis.blah, + globalThis.I, globalThis.f, globalThis.path, globalThis.x, globalThis.name, globalThis.foo); })().then(common.mustCall()); function startTCPRepl() { diff --git a/test/parallel/test-runner-mock-timers.js b/test/parallel/test-runner-mock-timers.js index 87b8ba7e3784d2..da7458b4c46dd3 100644 --- a/test/parallel/test-runner-mock-timers.js +++ b/test/parallel/test-runner-mock-timers.js @@ -69,7 +69,7 @@ describe('Mock Timers Test Suite', () => { 'clearImmediate', ]; - const globalTimersDescriptors = timers.map((fn) => getDescriptor(global, fn)); + const globalTimersDescriptors = timers.map((fn) => getDescriptor(globalThis, fn)); const nodeTimersDescriptors = timers.map((fn) => getDescriptor(nodeTimers, fn)); const nodeTimersPromisesDescriptors = timers .filter((fn) => !fn.includes('clear')) @@ -116,7 +116,7 @@ describe('Mock Timers Test Suite', () => { it('should reset all timers when calling .reset function', (t) => { t.mock.timers.enable(); const fn = t.mock.fn(); - global.setTimeout(fn, 1000); + globalThis.setTimeout(fn, 1000); t.mock.timers.reset(); assert.deepStrictEqual(Date.now, globalThis.Date.now); assert.throws(() => { @@ -131,7 +131,7 @@ describe('Mock Timers Test Suite', () => { it('should reset all timers when calling Symbol.dispose', (t) => { t.mock.timers.enable(); const fn = t.mock.fn(); - global.setTimeout(fn, 1000); + globalThis.setTimeout(fn, 1000); // TODO(benjamingr) refactor to `using` t.mock.timers[Symbol.dispose](); assert.throws(() => { @@ -148,8 +148,8 @@ describe('Mock Timers Test Suite', () => { const order = []; const fn1 = t.mock.fn(() => order.push('f1')); const fn2 = t.mock.fn(() => order.push('f2')); - global.setTimeout(fn1, 1000); - global.setTimeout(fn2, 1000); + globalThis.setTimeout(fn1, 1000); + globalThis.setTimeout(fn2, 1000); t.mock.timers.tick(1000); assert.strictEqual(fn1.mock.callCount(), 1); assert.strictEqual(fn2.mock.callCount(), 1); @@ -170,11 +170,11 @@ describe('Mock Timers Test Suite', () => { const intervalFn = t.mock.fn(); t.mock.timers.enable(); - global.setTimeout(timeoutFn, 1111); - const id = global.setInterval(intervalFn, 9999); + globalThis.setTimeout(timeoutFn, 1111); + const id = globalThis.setInterval(intervalFn, 9999); t.mock.timers.runAll(); - global.clearInterval(id); + globalThis.clearInterval(id); assert.strictEqual(timeoutFn.mock.callCount(), 1); assert.strictEqual(intervalFn.mock.callCount(), 1); }); @@ -184,11 +184,11 @@ describe('Mock Timers Test Suite', () => { const intervalFn = t.mock.fn(); t.mock.timers.enable(); - global.setTimeout(timeoutFn, 1111); - const id = global.setInterval(intervalFn, 9999); + globalThis.setTimeout(timeoutFn, 1111); + const id = globalThis.setInterval(intervalFn, 9999); t.mock.timers.runAll(); - global.clearInterval(id); + globalThis.clearInterval(id); assert.strictEqual(timeoutFn.mock.callCount(), 1); assert.strictEqual(intervalFn.mock.callCount(), 1); assert.strictEqual(Date.now(), 9999); @@ -209,7 +209,7 @@ describe('Mock Timers Test Suite', () => { const fn = mock.fn(); - global.setTimeout(fn, 4000); + globalThis.setTimeout(fn, 4000); mock.timers.tick(4000); assert.strictEqual(fn.mock.callCount(), 1); @@ -220,7 +220,7 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setTimeout'] }); const fn = t.mock.fn(); - global.setTimeout(fn, 2000); + globalThis.setTimeout(fn, 2000); t.mock.timers.tick(1000); assert.strictEqual(fn.mock.callCount(), 0); @@ -234,7 +234,7 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setTimeout'] }); const fn = t.mock.fn(); const args = ['a', 'b', 'c']; - global.setTimeout(fn, 2000, ...args); + globalThis.setTimeout(fn, 2000, ...args); t.mock.timers.tick(1000); t.mock.timers.tick(500); @@ -248,7 +248,7 @@ describe('Mock Timers Test Suite', () => { const now = Date.now(); const timeout = 2; const expected = () => now - timeout; - global.setTimeout(common.mustCall(() => { + globalThis.setTimeout(common.mustCall(() => { assert.strictEqual(now - timeout, expected()); done(); }), timeout); @@ -257,7 +257,7 @@ describe('Mock Timers Test Suite', () => { it('should change timeout to 1ms when it is > TIMEOUT_MAX', (t) => { t.mock.timers.enable({ apis: ['setTimeout'] }); const fn = t.mock.fn(); - global.setTimeout(fn, TIMEOUT_MAX + 1); + globalThis.setTimeout(fn, TIMEOUT_MAX + 1); t.mock.timers.tick(1); assert.strictEqual(fn.mock.callCount(), 1); }); @@ -265,7 +265,7 @@ describe('Mock Timers Test Suite', () => { it('should change the delay to one if timeout < 0', (t) => { t.mock.timers.enable({ apis: ['setTimeout'] }); const fn = t.mock.fn(); - global.setTimeout(fn, -1); + globalThis.setTimeout(fn, -1); t.mock.timers.tick(1); assert.strictEqual(fn.mock.callCount(), 1); }); @@ -277,8 +277,8 @@ describe('Mock Timers Test Suite', () => { const fn = mock.fn(); - const id = global.setTimeout(fn, 4000); - global.clearTimeout(id); + const id = globalThis.setTimeout(fn, 4000); + globalThis.clearTimeout(id); t.mock.timers.tick(4000); assert.strictEqual(fn.mock.callCount(), 0); @@ -297,13 +297,13 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setInterval'] }); const fn = t.mock.fn(); - const id = global.setInterval(fn, 200); + const id = globalThis.setInterval(fn, 200); t.mock.timers.tick(200); t.mock.timers.tick(200); t.mock.timers.tick(200); - global.clearInterval(id); + globalThis.clearInterval(id); assert.strictEqual(fn.mock.callCount(), 3); }); @@ -312,13 +312,13 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setInterval'] }); const fn = t.mock.fn(); const args = ['a', 'b', 'c']; - const id = global.setInterval(fn, 200, ...args); + const id = globalThis.setInterval(fn, 200, ...args); t.mock.timers.tick(200); t.mock.timers.tick(200); t.mock.timers.tick(200); - global.clearInterval(id); + globalThis.clearInterval(id); assert.strictEqual(fn.mock.callCount(), 3); assert.deepStrictEqual(fn.mock.calls[0].arguments, args); @@ -332,8 +332,8 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setInterval'] }); const fn = mock.fn(); - const id = global.setInterval(fn, 200); - global.clearInterval(id); + const id = globalThis.setInterval(fn, 200); + globalThis.clearInterval(id); t.mock.timers.tick(200); assert.strictEqual(fn.mock.callCount(), 0); @@ -352,7 +352,7 @@ describe('Mock Timers Test Suite', () => { const now = Date.now(); const timeout = 2; const expected = () => now - timeout; - global.setImmediate(common.mustCall(() => { + globalThis.setImmediate(common.mustCall(() => { assert.strictEqual(now - timeout, expected()); done(); })); @@ -362,7 +362,7 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setImmediate'] }); const fn = t.mock.fn(); const args = ['a', 'b', 'c']; - global.setImmediate(fn, ...args); + globalThis.setImmediate(fn, ...args); t.mock.timers.tick(9999); assert.strictEqual(fn.mock.callCount(), 1); @@ -372,14 +372,14 @@ describe('Mock Timers Test Suite', () => { it('should not advance in time if clearImmediate was invoked', (t) => { t.mock.timers.enable({ apis: ['setImmediate'] }); - const id = global.setImmediate(common.mustNotCall()); - global.clearImmediate(id); + const id = globalThis.setImmediate(common.mustNotCall()); + globalThis.clearImmediate(id); t.mock.timers.tick(200); }); it('should advance in time and trigger timers when calling the .tick function', (t) => { t.mock.timers.enable({ apis: ['setImmediate'] }); - global.setImmediate(common.mustCall(1)); + globalThis.setImmediate(common.mustCall(1)); t.mock.timers.tick(0); }); @@ -389,8 +389,8 @@ describe('Mock Timers Test Suite', () => { const fn1 = t.mock.fn(common.mustCall(() => order.push('f1'), 1)); const fn2 = t.mock.fn(common.mustCall(() => order.push('f2'), 1)); - global.setImmediate(fn1); - global.setImmediate(fn2); + globalThis.setImmediate(fn1); + globalThis.setImmediate(fn2); t.mock.timers.tick(0); @@ -403,8 +403,8 @@ describe('Mock Timers Test Suite', () => { const fn1 = t.mock.fn(common.mustCall(() => order.push('f1'), 1)); const fn2 = t.mock.fn(common.mustCall(() => order.push('f2'), 1)); - global.setTimeout(fn2, 0); - global.setImmediate(fn1); + globalThis.setTimeout(fn2, 0); + globalThis.setImmediate(fn1); t.mock.timers.tick(100); diff --git a/test/parallel/test-timers-api-refs.js b/test/parallel/test-timers-api-refs.js index 3c55a05ac4c20a..a6a541963110bb 100644 --- a/test/parallel/test-timers-api-refs.js +++ b/test/parallel/test-timers-api-refs.js @@ -4,12 +4,12 @@ const timers = require('timers'); // Delete global APIs to make sure they're not relied on by the internal timers // code -delete global.setTimeout; -delete global.clearTimeout; -delete global.setInterval; -delete global.clearInterval; -delete global.setImmediate; -delete global.clearImmediate; +delete globalThis.setTimeout; +delete globalThis.clearTimeout; +delete globalThis.setInterval; +delete globalThis.clearInterval; +delete globalThis.setImmediate; +delete globalThis.clearImmediate; const timeoutCallback = () => { timers.clearTimeout(timeout); }; const timeout = timers.setTimeout(common.mustCall(timeoutCallback), 1); diff --git a/test/parallel/test-timers-process-tampering.js b/test/parallel/test-timers-process-tampering.js index 766cc9f3560c82..8632e7c96fa086 100644 --- a/test/parallel/test-timers-process-tampering.js +++ b/test/parallel/test-timers-process-tampering.js @@ -3,6 +3,6 @@ 'use strict'; const common = require('../common'); -global.process = {}; // Boom! -common.allowGlobals(global.process); +globalThis.process = {}; // Boom! +common.allowGlobals(globalThis.process); setImmediate(common.mustCall()); diff --git a/test/parallel/test-tls-connect-memleak.js b/test/parallel/test-tls-connect-memleak.js index 5bdcbe89f785f6..7b9cb71d8df0ba 100644 --- a/test/parallel/test-tls-connect-memleak.js +++ b/test/parallel/test-tls-connect-memleak.js @@ -57,7 +57,7 @@ const gcListener = { ongc() { collected = true; } }; } function done(sock) { - global.gc(); + globalThis.gc(); setImmediate(() => { assert.strictEqual(collected, true); sock.end(); diff --git a/test/parallel/test-tls-securepair-leak.js b/test/parallel/test-tls-securepair-leak.js index 98bdcde76ec034..e3d5c2cdf37b5d 100644 --- a/test/parallel/test-tls-securepair-leak.js +++ b/test/parallel/test-tls-securepair-leak.js @@ -17,7 +17,7 @@ const before = process.memoryUsage().external; createSecurePair(context, false, false, false, options).destroy(); } setImmediate(() => { - global.gc(); + globalThis.gc(); const after = process.memoryUsage().external; // It's not an exact science but a SecurePair grows .external by about 45 KiB. diff --git a/test/parallel/test-tls-transport-destroy-after-own-gc.js b/test/parallel/test-tls-transport-destroy-after-own-gc.js index 17c494ca0b79d1..bcac2c6ebde2b8 100644 --- a/test/parallel/test-tls-transport-destroy-after-own-gc.js +++ b/test/parallel/test-tls-transport-destroy-after-own-gc.js @@ -19,11 +19,11 @@ let clientTLSHandle = clientTLS._handle; // eslint-disable-line no-unused-vars setImmediate(() => { clientTLS = null; - global.gc(); + globalThis.gc(); clientTLSHandle = null; - global.gc(); + globalThis.gc(); setImmediate(() => { clientSide = null; - global.gc(); + globalThis.gc(); }); }); diff --git a/test/parallel/test-trace-events-api.js b/test/parallel/test-trace-events-api.js index 8792a40cf00c80..9bffb3b78c4ba3 100644 --- a/test/parallel/test-trace-events-api.js +++ b/test/parallel/test-trace-events-api.js @@ -109,7 +109,7 @@ if (isChild) { assert.strictEqual(getEnabledCategories(), 'abc'); tracing3 = undefined; } - global.gc(); + globalThis.gc(); assert.strictEqual(getEnabledCategories(), 'abc'); // Not able to disable the thing after this point, however. } diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index 8d2cab5a9c7a1c..6f222d0fea0fb8 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -197,9 +197,9 @@ assert.strictEqual(util.format('%s', -Infinity), '-Infinity'); util.format('%s', Object.setPrototypeOf(new Foo(), null)), '[Foo: null prototype] {}' ); - global.Foo = Foo; + globalThis.Foo = Foo; assert.strictEqual(util.format('%s', new Foo()), 'Bar'); - delete global.Foo; + delete globalThis.Foo; class Bar { abc = true; } assert.strictEqual(util.format('%s', new Bar()), 'Bar { abc: true }'); class Foobar extends Array { aaa = true; } diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 87d92369b8deca..2dc263443481a0 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -1267,9 +1267,9 @@ if (typeof Symbol !== 'undefined') { // a bonafide native Promise. { const oldPromise = Promise; - global.Promise = function() { this.bar = 42; }; + globalThis.Promise = function() { this.bar = 42; }; assert.strictEqual(util.inspect(new Promise()), '{ bar: 42 }'); - global.Promise = oldPromise; + globalThis.Promise = oldPromise; } // Test Map iterators. @@ -3183,7 +3183,7 @@ assert.strictEqual( } // Consistency check. - assert(fullObjectGraph(global).has(Function.prototype)); + assert(fullObjectGraph(globalThis).has(Function.prototype)); } { diff --git a/test/parallel/test-vm-basic.js b/test/parallel/test-vm-basic.js index 93c3fbaea631ab..5687987faeb0b9 100644 --- a/test/parallel/test-vm-basic.js +++ b/test/parallel/test-vm-basic.js @@ -59,9 +59,9 @@ const vm = require('vm'); const result = vm.runInThisContext( 'vmResult = "foo"; Object.prototype.toString.call(process);' ); - assert.strictEqual(global.vmResult, 'foo'); + assert.strictEqual(globalThis.vmResult, 'foo'); assert.strictEqual(result, '[object process]'); - delete global.vmResult; + delete globalThis.vmResult; } // vm.runInNewContext @@ -69,7 +69,7 @@ const vm = require('vm'); const result = vm.runInNewContext( 'vmResult = "foo"; typeof process;' ); - assert.strictEqual(global.vmResult, undefined); + assert.strictEqual(globalThis.vmResult, undefined); assert.strictEqual(result, 'undefined'); } diff --git a/test/parallel/test-vm-create-and-run-in-context.js b/test/parallel/test-vm-create-and-run-in-context.js index bd746cf2df7080..314ab9525743dc 100644 --- a/test/parallel/test-vm-create-and-run-in-context.js +++ b/test/parallel/test-vm-create-and-run-in-context.js @@ -45,6 +45,6 @@ assert.strictEqual(context.thing, 'lala'); // Run in contextified sandbox without referencing the context const sandbox = { x: 1 }; vm.createContext(sandbox); -global.gc(); +globalThis.gc(); vm.runInContext('x = 2', sandbox); // Should not crash. diff --git a/test/parallel/test-vm-cross-context.js b/test/parallel/test-vm-cross-context.js index b7cf1309d3689f..abdfde32a8d847 100644 --- a/test/parallel/test-vm-cross-context.js +++ b/test/parallel/test-vm-cross-context.js @@ -23,7 +23,7 @@ require('../common'); const vm = require('vm'); -const ctx = vm.createContext(global); +const ctx = vm.createContext(globalThis); // Should not throw. vm.runInContext('!function() { var x = console.log; }()', ctx); diff --git a/test/parallel/test-vm-global-get-own.js b/test/parallel/test-vm-global-get-own.js index 246fcbf866b8b6..de5e0a9619af65 100644 --- a/test/parallel/test-vm-global-get-own.js +++ b/test/parallel/test-vm-global-get-own.js @@ -9,7 +9,7 @@ const vm = require('vm'); // Related to: // - https://github.com/nodejs/node/issues/45983 -const global = vm.runInContext('this', vm.createContext()); +const contextGlobal = vm.runInContext('this', vm.createContext()); function runAssertions(data, property, viaDefine, value1, value2, value3) { // Define the property for the first time @@ -35,20 +35,20 @@ function runAssertionsOnSandbox(builder) { } // Assertions on: define property -runAssertions(global, 'toto', true, 1, 2, 3); -runAssertions(global, Symbol.for('toto'), true, 1, 2, 3); -runAssertions(global, 'tutu', true, fun1, fun2, fun3); -runAssertions(global, Symbol.for('tutu'), true, fun1, fun2, fun3); -runAssertions(global, 'tyty', true, fun1, 2, 3); -runAssertions(global, Symbol.for('tyty'), true, fun1, 2, 3); +runAssertions(contextGlobal, 'toto', true, 1, 2, 3); +runAssertions(contextGlobal, Symbol.for('toto'), true, 1, 2, 3); +runAssertions(contextGlobal, 'tutu', true, fun1, fun2, fun3); +runAssertions(contextGlobal, Symbol.for('tutu'), true, fun1, fun2, fun3); +runAssertions(contextGlobal, 'tyty', true, fun1, 2, 3); +runAssertions(contextGlobal, Symbol.for('tyty'), true, fun1, 2, 3); // Assertions on: direct assignment -runAssertions(global, 'titi', false, 1, 2, 3); -runAssertions(global, Symbol.for('titi'), false, 1, 2, 3); -runAssertions(global, 'tata', false, fun1, fun2, fun3); -runAssertions(global, Symbol.for('tata'), false, fun1, fun2, fun3); -runAssertions(global, 'tztz', false, fun1, 2, 3); -runAssertions(global, Symbol.for('tztz'), false, fun1, 2, 3); +runAssertions(contextGlobal, 'titi', false, 1, 2, 3); +runAssertions(contextGlobal, Symbol.for('titi'), false, 1, 2, 3); +runAssertions(contextGlobal, 'tata', false, fun1, fun2, fun3); +runAssertions(contextGlobal, Symbol.for('tata'), false, fun1, fun2, fun3); +runAssertions(contextGlobal, 'tztz', false, fun1, 2, 3); +runAssertions(contextGlobal, Symbol.for('tztz'), false, fun1, 2, 3); // Assertions on: define property from sandbox runAssertionsOnSandbox( diff --git a/test/parallel/test-vm-measure-memory-lazy.js b/test/parallel/test-vm-measure-memory-lazy.js index 513cfbc3672451..7f85f8d6ca9656 100644 --- a/test/parallel/test-vm-measure-memory-lazy.js +++ b/test/parallel/test-vm-measure-memory-lazy.js @@ -10,28 +10,28 @@ const vm = require('vm'); expectExperimentalWarning(); -// Test lazy memory measurement - we will need to global.gc() +// Test lazy memory measurement - we will need to globalThis.gc() // or otherwise these may not resolve. { vm.measureMemory() .then(common.mustCall(assertSummaryShape)); - global.gc(); + globalThis.gc(); } { vm.measureMemory({}) .then(common.mustCall(assertSummaryShape)); - global.gc(); + globalThis.gc(); } { vm.measureMemory({ mode: 'summary' }) .then(common.mustCall(assertSummaryShape)); - global.gc(); + globalThis.gc(); } { vm.measureMemory({ mode: 'detailed' }) .then(common.mustCall(assertSummaryShape)); - global.gc(); + globalThis.gc(); } diff --git a/test/parallel/test-vm-module-basic.js b/test/parallel/test-vm-module-basic.js index cba1e037ac455a..53fed6536079a0 100644 --- a/test/parallel/test-vm-module-basic.js +++ b/test/parallel/test-vm-module-basic.js @@ -37,15 +37,15 @@ const util = require('util'); (async () => { const m = new SourceTextModule(` - global.vmResultFoo = "foo"; - global.vmResultTypeofProcess = Object.prototype.toString.call(process); + globalThis.vmResultFoo = "foo"; + globalThis.vmResultTypeofProcess = Object.prototype.toString.call(process); `); await m.link(common.mustNotCall()); await m.evaluate(); - assert.strictEqual(global.vmResultFoo, 'foo'); - assert.strictEqual(global.vmResultTypeofProcess, '[object process]'); - delete global.vmResultFoo; - delete global.vmResultTypeofProcess; + assert.strictEqual(globalThis.vmResultFoo, 'foo'); + assert.strictEqual(globalThis.vmResultTypeofProcess, '[object process]'); + delete globalThis.vmResultFoo; + delete globalThis.vmResultTypeofProcess; })().then(common.mustCall()); (async () => { diff --git a/test/parallel/test-vm-new-script-new-context.js b/test/parallel/test-vm-new-script-new-context.js index 482b4130d615d9..b4221d81d98dcb 100644 --- a/test/parallel/test-vm-new-script-new-context.js +++ b/test/parallel/test-vm-new-script-new-context.js @@ -49,43 +49,43 @@ const Script = require('vm').Script; } { - global.hello = 5; + globalThis.hello = 5; const script = new Script('hello = 2'); script.runInNewContext(); - assert.strictEqual(global.hello, 5); + assert.strictEqual(globalThis.hello, 5); // Cleanup - delete global.hello; + delete globalThis.hello; } { - global.code = 'foo = 1;' + + globalThis.code = 'foo = 1;' + 'bar = 2;' + 'if (baz !== 3) throw new Error(\'test fail\');'; - global.foo = 2; - global.obj = { foo: 0, baz: 3 }; - const script = new Script(global.code); + globalThis.foo = 2; + globalThis.obj = { foo: 0, baz: 3 }; + const script = new Script(globalThis.code); /* eslint-disable no-unused-vars */ - const baz = script.runInNewContext(global.obj); + const baz = script.runInNewContext(globalThis.obj); /* eslint-enable no-unused-vars */ - assert.strictEqual(global.obj.foo, 1); - assert.strictEqual(global.obj.bar, 2); - assert.strictEqual(global.foo, 2); + assert.strictEqual(globalThis.obj.foo, 1); + assert.strictEqual(globalThis.obj.bar, 2); + assert.strictEqual(globalThis.foo, 2); // cleanup - delete global.code; - delete global.foo; - delete global.obj; + delete globalThis.code; + delete globalThis.foo; + delete globalThis.obj; } { const script = new Script('f()'); - function changeFoo() { global.foo = 100; } + function changeFoo() { globalThis.foo = 100; } script.runInNewContext({ f: changeFoo }); - assert.strictEqual(global.foo, 100); + assert.strictEqual(globalThis.foo, 100); // cleanup - delete global.foo; + delete globalThis.foo; } { diff --git a/test/parallel/test-vm-new-script-this-context.js b/test/parallel/test-vm-new-script-this-context.js index 18f39f9086ae2a..30b220e3d4a2c2 100644 --- a/test/parallel/test-vm-new-script-this-context.js +++ b/test/parallel/test-vm-new-script-this-context.js @@ -35,34 +35,34 @@ assert.throws(() => { script.runInThisContext(script); }, /^Error: test$/); -global.hello = 5; +globalThis.hello = 5; script = new Script('hello = 2'); script.runInThisContext(script); -assert.strictEqual(global.hello, 2); +assert.strictEqual(globalThis.hello, 2); // Pass values -global.code = 'foo = 1;' + +globalThis.code = 'foo = 1;' + 'bar = 2;' + 'if (typeof baz !== "undefined") throw new Error("test fail");'; -global.foo = 2; -global.obj = { foo: 0, baz: 3 }; -script = new Script(global.code); +globalThis.foo = 2; +globalThis.obj = { foo: 0, baz: 3 }; +script = new Script(globalThis.code); script.runInThisContext(script); -assert.strictEqual(global.obj.foo, 0); -assert.strictEqual(global.bar, 2); -assert.strictEqual(global.foo, 1); +assert.strictEqual(globalThis.obj.foo, 0); +assert.strictEqual(globalThis.bar, 2); +assert.strictEqual(globalThis.foo, 1); // Call a function -global.f = function() { global.foo = 100; }; +globalThis.f = function() { globalThis.foo = 100; }; script = new Script('f()'); script.runInThisContext(script); -assert.strictEqual(global.foo, 100); +assert.strictEqual(globalThis.foo, 100); common.allowGlobals( - global.hello, - global.code, - global.foo, - global.obj, - global.f + globalThis.hello, + globalThis.code, + globalThis.foo, + globalThis.obj, + globalThis.f ); diff --git a/test/parallel/test-vm-run-in-new-context.js b/test/parallel/test-vm-run-in-new-context.js index 6e8c42812bbc88..c6f8fbf893ca9a 100644 --- a/test/parallel/test-vm-run-in-new-context.js +++ b/test/parallel/test-vm-run-in-new-context.js @@ -26,7 +26,7 @@ const common = require('../common'); const assert = require('assert'); const vm = require('vm'); -if (typeof global.gc !== 'function') +if (typeof globalThis.gc !== 'function') assert.fail('Run this test with --expose-gc'); // Run a string @@ -38,28 +38,28 @@ assert.throws(() => { vm.runInNewContext('throw new Error(\'test\');'); }, /^Error: test$/); -global.hello = 5; +globalThis.hello = 5; vm.runInNewContext('hello = 2'); -assert.strictEqual(global.hello, 5); +assert.strictEqual(globalThis.hello, 5); // Pass values in and out -global.code = 'foo = 1;' + +globalThis.code = 'foo = 1;' + 'bar = 2;' + 'if (baz !== 3) throw new Error(\'test fail\');'; -global.foo = 2; -global.obj = { foo: 0, baz: 3 }; +globalThis.foo = 2; +globalThis.obj = { foo: 0, baz: 3 }; /* eslint-disable no-unused-vars */ -const baz = vm.runInNewContext(global.code, global.obj); +const baz = vm.runInNewContext(globalThis.code, globalThis.obj); /* eslint-enable no-unused-vars */ -assert.strictEqual(global.obj.foo, 1); -assert.strictEqual(global.obj.bar, 2); -assert.strictEqual(global.foo, 2); +assert.strictEqual(globalThis.obj.foo, 1); +assert.strictEqual(globalThis.obj.bar, 2); +assert.strictEqual(globalThis.foo, 2); // Call a function by reference -function changeFoo() { global.foo = 100; } +function changeFoo() { globalThis.foo = 100; } vm.runInNewContext('f()', { f: changeFoo }); -assert.strictEqual(global.foo, 100); +assert.strictEqual(globalThis.foo, 100); // Modify an object by reference const f = { a: 1 }; @@ -68,7 +68,7 @@ assert.strictEqual(f.a, 2); // Use function in context without referencing context const fn = vm.runInNewContext('(function() { obj.p = {}; })', { obj: {} }); -global.gc(); +globalThis.gc(); fn(); // Should not crash @@ -93,8 +93,8 @@ for (const arg of [filename, { filename }]) { } common.allowGlobals( - global.hello, - global.code, - global.foo, - global.obj + globalThis.hello, + globalThis.code, + globalThis.foo, + globalThis.obj ); diff --git a/test/parallel/test-vm-static-this.js b/test/parallel/test-vm-static-this.js index e9382d6c3b4c1a..f47c0b5d0d056a 100644 --- a/test/parallel/test-vm-static-this.js +++ b/test/parallel/test-vm-static-this.js @@ -33,9 +33,9 @@ assert.throws(function() { vm.runInThisContext('throw new Error(\'test\');'); }, /test/); -global.hello = 5; +globalThis.hello = 5; vm.runInThisContext('hello = 2'); -assert.strictEqual(global.hello, 2); +assert.strictEqual(globalThis.hello, 2); // pass values @@ -43,23 +43,23 @@ const code = 'foo = 1;' + 'bar = 2;' + 'if (typeof baz !== \'undefined\')' + 'throw new Error(\'test fail\');'; -global.foo = 2; -global.obj = { foo: 0, baz: 3 }; +globalThis.foo = 2; +globalThis.obj = { foo: 0, baz: 3 }; /* eslint-disable no-unused-vars */ const baz = vm.runInThisContext(code); /* eslint-enable no-unused-vars */ -assert.strictEqual(global.obj.foo, 0); -assert.strictEqual(global.bar, 2); -assert.strictEqual(global.foo, 1); +assert.strictEqual(globalThis.obj.foo, 0); +assert.strictEqual(globalThis.bar, 2); +assert.strictEqual(globalThis.foo, 1); // call a function -global.f = function() { global.foo = 100; }; +globalThis.f = function() { globalThis.foo = 100; }; vm.runInThisContext('f()'); -assert.strictEqual(global.foo, 100); +assert.strictEqual(globalThis.foo, 100); common.allowGlobals( - global.hello, - global.foo, - global.obj, - global.f + globalThis.hello, + globalThis.foo, + globalThis.obj, + globalThis.f ); diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index 4da6b67bd2932b..7f9fe8dfa53391 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -69,7 +69,7 @@ test('sessionStorage is not persisted', async () => { test('localStorage throws without --localstorage-file ', async () => { const cp = await spawnPromisified(process.execPath, [ '--experimental-webstorage', - '-pe', 'localStorage === global.localStorage', + '-pe', 'localStorage === globalThis.localStorage', ]); assert.strictEqual(cp.code, 1); assert.strictEqual(cp.signal, null); @@ -81,7 +81,7 @@ test('localStorage is not persisted if it is unused', async () => { const cp = await spawnPromisified(process.execPath, [ '--experimental-webstorage', '--localstorage-file', nextLocalStorage(), - '-pe', 'localStorage === global.localStorage', + '-pe', 'localStorage === globalThis.localStorage', ]); assert.strictEqual(cp.code, 0); assert.match(cp.stdout, /true/); diff --git a/test/parallel/test-whatwg-url-custom-global.js b/test/parallel/test-whatwg-url-custom-global.js index b99dfd8f3e7d94..16efdfa8df1174 100644 --- a/test/parallel/test-whatwg-url-custom-global.js +++ b/test/parallel/test-whatwg-url-custom-global.js @@ -6,7 +6,7 @@ require('../common'); const assert = require('assert'); assert.deepStrictEqual( - Object.getOwnPropertyDescriptor(global, 'URL'), + Object.getOwnPropertyDescriptor(globalThis, 'URL'), { value: URL, writable: true, @@ -16,7 +16,7 @@ assert.deepStrictEqual( ); assert.deepStrictEqual( - Object.getOwnPropertyDescriptor(global, 'URLSearchParams'), + Object.getOwnPropertyDescriptor(globalThis, 'URLSearchParams'), { value: URLSearchParams, writable: true, diff --git a/test/parallel/test-worker-cli-options.js b/test/parallel/test-worker-cli-options.js index 0c243d251e97bc..3e6ab46db6ea74 100644 --- a/test/parallel/test-worker-cli-options.js +++ b/test/parallel/test-worker-cli-options.js @@ -8,7 +8,7 @@ const CODE = ` // If the --expose-internals flag does not pass to worker // require function will throw an error require('internal/options'); -global.gc(); +globalThis.gc(); `; // Test if the flags is passed to worker threads correctly diff --git a/test/parallel/test-worker-message-channel-sharedarraybuffer.js b/test/parallel/test-worker-message-channel-sharedarraybuffer.js index 220aa978b12051..6ee577d447ec97 100644 --- a/test/parallel/test-worker-message-channel-sharedarraybuffer.js +++ b/test/parallel/test-worker-message-channel-sharedarraybuffer.js @@ -19,7 +19,7 @@ const { Worker } = require('worker_threads'); `, { eval: true }); w.on('message', common.mustCall(() => { assert.strictEqual(local.toString(), 'Hello world!'); - global.gc(); + globalThis.gc(); w.terminate(); })); w.postMessage({ sharedArrayBuffer }); diff --git a/test/parallel/test-worker-message-port-move.js b/test/parallel/test-worker-message-port-move.js index 44efd2e6a6b94f..b8db31b88c7bc4 100644 --- a/test/parallel/test-worker-message-port-move.js +++ b/test/parallel/test-worker-message-port-move.js @@ -48,7 +48,7 @@ vm.runInContext('(' + function() { { let threw = false; try { - port.postMessage(global); + port.postMessage(globalThis); } catch (e) { assert.strictEqual(e.constructor.name, 'DOMException'); assert(e instanceof Object); diff --git a/test/parallel/test-worker-workerdata-sharedarraybuffer.js b/test/parallel/test-worker-workerdata-sharedarraybuffer.js index 4e3d508ac94941..4f1b332461280f 100644 --- a/test/parallel/test-worker-workerdata-sharedarraybuffer.js +++ b/test/parallel/test-worker-workerdata-sharedarraybuffer.js @@ -23,7 +23,7 @@ const { Worker } = require('worker_threads'); }); w.on('message', common.mustCall(() => { assert.strictEqual(local.toString(), 'Hello world!'); - global.gc(); + globalThis.gc(); w.terminate(); })); w.postMessage({}); diff --git a/test/parallel/test-zlib-invalid-input-memory.js b/test/parallel/test-zlib-invalid-input-memory.js index 9761e4bbf097d8..ac718395dae184 100644 --- a/test/parallel/test-zlib-invalid-input-memory.js +++ b/test/parallel/test-zlib-invalid-input-memory.js @@ -17,7 +17,7 @@ const ongc = common.mustCall(); strm.once('error', common.mustCall((err) => { assert(err); setImmediate(() => { - global.gc(); + globalThis.gc(); // Keep the event loop alive for seeing the async_hooks destroy hook // we use for GC tracking... // TODO(addaleax): This should maybe not be necessary? diff --git a/test/parallel/test-zlib-unused-weak.js b/test/parallel/test-zlib-unused-weak.js index 2c1e2d729030dd..cd1ab91ceb5c4b 100644 --- a/test/parallel/test-zlib-unused-weak.js +++ b/test/parallel/test-zlib-unused-weak.js @@ -6,12 +6,12 @@ const zlib = require('zlib'); // Tests that native zlib handles start out their life as weak handles. -global.gc(); +globalThis.gc(); const before = process.memoryUsage().external; for (let i = 0; i < 100; ++i) zlib.createGzip(); const afterCreation = process.memoryUsage().external; -global.gc(); +globalThis.gc(); const afterGC = process.memoryUsage().external; assert((afterGC - before) / (afterCreation - before) <= 0.05, From 687be594bb45a5baed588067c3d7f1159f1ea62f Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 22 Jan 2025 20:21:46 +0900 Subject: [PATCH 121/208] test: add test that uses multibyte for path and resolves modules PR-URL: https://github.com/nodejs/node/pull/56696 Fixes: https://github.com/nodejs/node/issues/56650 Refs: https://github.com/nodejs/node/pull/56657 Reviewed-By: James M Snell Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca --- .../experimental.json" | 3 +++ .../test-module-create-require-multibyte.js | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 "test/fixtures/copy/utf/\346\226\260\345\273\272\346\226\207\344\273\266\345\244\271/experimental.json" create mode 100644 test/parallel/test-module-create-require-multibyte.js diff --git "a/test/fixtures/copy/utf/\346\226\260\345\273\272\346\226\207\344\273\266\345\244\271/experimental.json" "b/test/fixtures/copy/utf/\346\226\260\345\273\272\346\226\207\344\273\266\345\244\271/experimental.json" new file mode 100644 index 00000000000000..12611d2385a5a5 --- /dev/null +++ "b/test/fixtures/copy/utf/\346\226\260\345\273\272\346\226\207\344\273\266\345\244\271/experimental.json" @@ -0,0 +1,3 @@ +{ + "ofLife": 42 +} diff --git a/test/parallel/test-module-create-require-multibyte.js b/test/parallel/test-module-create-require-multibyte.js new file mode 100644 index 00000000000000..f9c4b6345dc59e --- /dev/null +++ b/test/parallel/test-module-create-require-multibyte.js @@ -0,0 +1,24 @@ +'use strict'; + +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); + +// This test ensures that the module can be resolved +// even if the path given to createRequire contains multibyte characters. + +const { createRequire } = require('module'); + +{ + const u = fixtures.fileURL('あ.js'); + + const reqToo = createRequire(u); + assert.deepStrictEqual(reqToo('./experimental'), { ofLife: 42 }); +} + +{ + const u = fixtures.fileURL('copy/utf/新建文件夹/index.js'); + + const reqToo = createRequire(u); + assert.deepStrictEqual(reqToo('./experimental'), { ofLife: 42 }); +} From 176002400886bc7f438236c727a61831efd5e565 Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 22 Jan 2025 20:24:38 +0900 Subject: [PATCH 122/208] src: fix to generate path from wchar_t via wstring Take a similar approach to node_file and allow the creation of paths code point must be specified to convert from wchar_t to utf8. PR-URL: https://github.com/nodejs/node/pull/56696 Fixes: https://github.com/nodejs/node/issues/56650 Refs: https://github.com/nodejs/node/pull/56657 Reviewed-By: James M Snell Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca --- src/node_file.cc | 15 +-------------- src/node_modules.cc | 24 ++++++++++++++++++++---- src/util-inl.h | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index 984bc55ee9b941..8e29bb39887625 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3146,21 +3146,8 @@ static void GetFormatOfExtensionlessFile( } #ifdef _WIN32 -std::wstring ConvertToWideString(const std::string& str) { - int size_needed = MultiByteToWideChar( - CP_UTF8, 0, &str[0], static_cast(str.size()), nullptr, 0); - std::wstring wstrTo(size_needed, 0); - MultiByteToWideChar(CP_UTF8, - 0, - &str[0], - static_cast(str.size()), - &wstrTo[0], - size_needed); - return wstrTo; -} - #define BufferValueToPath(str) \ - std::filesystem::path(ConvertToWideString(str.ToString())) + std::filesystem::path(ConvertToWideString(str.ToString(), CP_UTF8)) std::string ConvertWideToUTF8(const std::wstring& wstr) { if (wstr.empty()) return std::string(); diff --git a/src/node_modules.cc b/src/node_modules.cc index 85c8e21cf026ff..38d2c65c7f3282 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -349,8 +349,16 @@ void BindingData::GetNearestParentPackageJSON( path_value_str.push_back(kPathSeparator); } - auto package_json = - TraverseParent(realm, std::filesystem::path(path_value_str)); + std::filesystem::path path; + +#ifdef _WIN32 + std::wstring wide_path = ConvertToWideString(path_value_str, GetACP()); + path = std::filesystem::path(wide_path); +#else + path = std::filesystem::path(path_value_str); +#endif + + auto package_json = TraverseParent(realm, path); if (package_json != nullptr) { args.GetReturnValue().Set(package_json->Serialize(realm)); @@ -375,8 +383,16 @@ void BindingData::GetNearestParentPackageJSONType( path_value_str.push_back(kPathSeparator); } - auto package_json = - TraverseParent(realm, std::filesystem::path(path_value_str)); + std::filesystem::path path; + +#ifdef _WIN32 + std::wstring wide_path = ConvertToWideString(path_value_str, GetACP()); + path = std::filesystem::path(wide_path); +#else + path = std::filesystem::path(path_value_str); +#endif + + auto package_json = TraverseParent(realm, path); if (package_json == nullptr) { return; diff --git a/src/util-inl.h b/src/util-inl.h index a35e15eeed6576..b5ae5950b62767 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -562,6 +562,22 @@ bool IsWindowsBatchFile(const char* filename) { #endif // _WIN32 } +#ifdef _WIN32 +inline std::wstring ConvertToWideString(const std::string& str, + UINT code_page) { + int size_needed = MultiByteToWideChar( + code_page, 0, &str[0], static_cast(str.size()), nullptr, 0); + std::wstring wstrTo(size_needed, 0); + MultiByteToWideChar(code_page, + 0, + &str[0], + static_cast(str.size()), + &wstrTo[0], + size_needed); + return wstrTo; +} +#endif // _WIN32 + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS From bade7a1866618b9e46358b839fe5fdf16b1db2be Mon Sep 17 00:00:00 2001 From: tjuhaszrh Date: Sat, 25 Jan 2025 10:34:54 +0100 Subject: [PATCH 123/208] src: fix build with GCC 15 Added cstdint to worker_inspector as on more recent version of gcc the build was failing due to changes to libstdc++ and the removal of transitive includes. PR-URL: https://github.com/nodejs/node/pull/56740 Fixes: https://github.com/nodejs/node/issues/56731 Reviewed-By: Antoine du Hamel Reviewed-By: Chengzhong Wu Reviewed-By: Richard Lau Reviewed-By: James M Snell --- src/inspector/worker_inspector.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inspector/worker_inspector.h b/src/inspector/worker_inspector.h index d3254d5aa0ebe4..24403bb1704c40 100644 --- a/src/inspector/worker_inspector.h +++ b/src/inspector/worker_inspector.h @@ -5,6 +5,7 @@ #error("This header can only be used when inspector is enabled") #endif +#include #include #include #include From da1ca7db756c0db8a894f3b22a4d067701dc3c04 Mon Sep 17 00:00:00 2001 From: Robin Mehner Date: Sat, 25 Jan 2025 12:02:31 +0100 Subject: [PATCH 124/208] doc: fix typo in example code for util.styleText MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code shows how to style `errorMessage`, but then only logs out `successMessage` twice. This might trip people up when copying the code. PR-URL: https://github.com/nodejs/node/pull/56720 Reviewed-By: Ulises Gascón Reviewed-By: Richard Lau Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Minwoo Jung --- doc/api/util.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/util.md b/doc/api/util.md index 72a45b1cde8d11..958a49977e1a17 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -1972,7 +1972,7 @@ const errorMessage = styleText( // Validate if process.stderr has TTY { stream: stderr }, ); -console.error(successMessage); +console.error(errorMessage); ``` ```cjs From db5a2b55a59c2775f3b88f36080e51ca8069f9bd Mon Sep 17 00:00:00 2001 From: Burkov Egor Date: Wed, 22 Jan 2025 16:27:26 +0300 Subject: [PATCH 125/208] src: add default value for RSACipherConfig mode field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using default init of enum is UB Refs: https://github.com/nodejs/node/issues/56693 PR-URL: https://github.com/nodejs/node/pull/56701 Reviewed-By: Juan José Arboleda Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell --- src/crypto/crypto_rsa.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/crypto_rsa.h b/src/crypto/crypto_rsa.h index 29b259ae2f5284..6fc48da1aea2cd 100644 --- a/src/crypto/crypto_rsa.h +++ b/src/crypto/crypto_rsa.h @@ -77,7 +77,7 @@ struct RSAKeyExportTraits final { using RSAKeyExportJob = KeyExportJob; struct RSACipherConfig final : public MemoryRetainer { - CryptoJobMode mode; + CryptoJobMode mode = kCryptoJobAsync; ByteSource label; int padding = 0; const EVP_MD* digest = nullptr; From 23d0a7f80c36f238db9fd6c5575552b9a54a0f0e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 16:58:49 -0800 Subject: [PATCH 126/208] test: make some requires lazy in common/index PR-URL: https://github.com/nodejs/node/pull/56715 Reviewed-By: Yagiz Nizipli Reviewed-By: Richard Lau Reviewed-By: Matteo Collina --- test/common/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/common/index.js b/test/common/index.js index 3647f4554a4647..8113f604dfcdb6 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -23,7 +23,6 @@ const process = globalThis.process; // Some tests tamper with the process globalThis. const assert = require('assert'); -const { exec, execSync, spawn, spawnSync } = require('child_process'); const fs = require('fs'); const net = require('net'); // Do not require 'os' until needed so that test-os-checked-function can @@ -31,7 +30,6 @@ const net = require('net'); const path = require('path'); const { inspect, getCallSites } = require('util'); const { isMainThread } = require('worker_threads'); -const { isModuleNamespaceObject } = require('util/types'); const tmpdir = require('./tmpdir'); const bits = ['arm64', 'loong64', 'mips', 'mipsel', 'ppc64', 'riscv64', 's390x', 'x64'] @@ -104,6 +102,7 @@ if (process.argv.length === 2 && inspect(flags), 'Use NODE_SKIP_FLAG_CHECK to run the test with the original flags.', ); + const { spawnSync } = require('child_process'); const args = [...flags, ...process.execArgv, ...process.argv.slice(1)]; const options = { encoding: 'utf8', stdio: 'inherit' }; const result = spawnSync(process.execPath, args, options); @@ -232,6 +231,7 @@ function childShouldThrowAndAbort() { // continuous testing and developers' machines escapedArgs[0] = 'ulimit -c 0 && ' + escapedArgs[0]; } + const { exec } = require('child_process'); const child = exec(...escapedArgs); child.on('exit', function onExit(exitCode, signal) { const errMsg = 'Test should have aborted ' + @@ -474,6 +474,7 @@ function canCreateSymLink() { 'System32', 'whoami.exe'); try { + const { execSync } = require('child_process'); const output = execSync(`${whoamiPath} /priv`, { timeout: 1000 }); return output.includes('SeCreateSymbolicLinkPrivilege'); } catch { @@ -780,6 +781,7 @@ function requireNoPackageJSONAbove(dir = __dirname) { } function spawnPromisified(...args) { + const { spawn } = require('child_process'); let stderr = ''; let stdout = ''; @@ -843,6 +845,7 @@ function escapePOSIXShell(cmdParts, ...args) { * @param {object} expectation shape of expected namespace. */ function expectRequiredModule(mod, expectation, checkESModule = true) { + const { isModuleNamespaceObject } = require('util/types'); const clone = { ...mod }; if (Object.hasOwn(mod, 'default') && checkESModule) { assert.strictEqual(mod.__esModule, true); @@ -920,6 +923,7 @@ const common = { }, get inFreeBSDJail() { + const { execSync } = require('child_process'); if (inFreeBSDJail !== null) return inFreeBSDJail; if (exports.isFreeBSD && From 59b3a8b21be9f206bf319cea50a6718c2230d09b Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 25 Jan 2025 17:02:54 -0500 Subject: [PATCH 127/208] watch: reload env file for --env-file-if-exists PR-URL: https://github.com/nodejs/node/pull/56643 Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell --- lib/internal/main/watch_mode.js | 2 +- test/sequential/test-watch-mode.mjs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/internal/main/watch_mode.js b/lib/internal/main/watch_mode.js index 6e2528e64737c7..60639efb45482d 100644 --- a/lib/internal/main/watch_mode.js +++ b/lib/internal/main/watch_mode.js @@ -33,7 +33,7 @@ markBootstrapComplete(); // TODO(MoLow): Make kill signal configurable const kKillSignal = 'SIGTERM'; const kShouldFilterModules = getOptionValue('--watch-path').length === 0; -const kEnvFile = getOptionValue('--env-file'); +const kEnvFile = getOptionValue('--env-file') || getOptionValue('--env-file-if-exists'); const kWatchedPaths = ArrayPrototypeMap(getOptionValue('--watch-path'), (path) => resolve(path)); const kPreserveOutput = getOptionValue('--watch-preserve-output'); const kCommand = ArrayPrototypeSlice(process.argv, 1); diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index 39bc7223dffdfc..324cdd10b3b4ef 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -242,6 +242,32 @@ describe('watch mode', { concurrency: !process.env.TEST_PARALLEL, timeout: 60_00 } }); + it('should load new env variables when --env-file-if-exists changes', async () => { + const envKey = `TEST_ENV_${Date.now()}`; + const envKey2 = `TEST_ENV_2_${Date.now()}`; + const jsFile = createTmpFile(`console.log('ENV: ' + process.env.${envKey} + '\\n' + 'ENV2: ' + process.env.${envKey2});`); + const envFile = createTmpFile(`${envKey}=value1`, '.env'); + const { done, restart } = runInBackground({ args: ['--watch', `--env-file-if-exists=${envFile}`, jsFile] }); + + try { + await restart(); + writeFileSync(envFile, `${envKey}=value1\n${envKey2}=newValue`); + + // Second restart, after env change + const { stderr, stdout } = await restart(); + + assert.strictEqual(stderr, ''); + assert.deepStrictEqual(stdout, [ + `Restarting ${inspect(jsFile)}`, + 'ENV: value1', + 'ENV2: newValue', + `Completed running ${inspect(jsFile)}`, + ]); + } finally { + await done(); + } + }); + it('should watch changes to a failing file', async () => { const file = createTmpFile('throw new Error("fails");'); const { stderr, stdout } = await runWriteSucceed({ From 7119303a811b7f8ab5bebdaa8df4cc81f002dede Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sat, 25 Jan 2025 23:32:13 +0000 Subject: [PATCH 128/208] module: fix bad `require.resolve` with option paths for `.` and `..` this change fixes `require.resolve` used with the `paths` option not considering `.` and `..` as relative Fixes: https://github.com/nodejs/node/issues/47000 PR-URL: https://github.com/nodejs/node/pull/56735 Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell Reviewed-By: Jordan Harband Reviewed-By: Matteo Collina --- lib/internal/modules/cjs/loader.js | 34 +++++++-------- .../relative/subdir/relative-subdir.js | 1 + ...est-require-resolve-opts-paths-relative.js | 43 +++++++++++++++++++ 3 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 test/fixtures/module-require/relative/subdir/relative-subdir.js create mode 100644 test/parallel/test-require-resolve-opts-paths-relative.js diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index a558185e08ddb1..6608be9d2db029 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -722,18 +722,8 @@ Module._findPath = function(request, paths, isMain, conditions = getCjsCondition ) )); - const isRelative = StringPrototypeCharCodeAt(request, 0) === CHAR_DOT && - ( - request.length === 1 || - StringPrototypeCharCodeAt(request, 1) === CHAR_FORWARD_SLASH || - (isWindows && StringPrototypeCharCodeAt(request, 1) === CHAR_BACKWARD_SLASH) || - (StringPrototypeCharCodeAt(request, 1) === CHAR_DOT && (( - request.length === 2 || - StringPrototypeCharCodeAt(request, 2) === CHAR_FORWARD_SLASH) || - (isWindows && StringPrototypeCharCodeAt(request, 2) === CHAR_BACKWARD_SLASH))) - ); let insidePath = true; - if (isRelative) { + if (isRelative(request)) { const normalizedRequest = path.normalize(request); if (StringPrototypeStartsWith(normalizedRequest, '..')) { insidePath = false; @@ -1328,12 +1318,7 @@ Module._resolveFilename = function(request, parent, isMain, options) { if (typeof options === 'object' && options !== null) { if (ArrayIsArray(options.paths)) { - const isRelative = StringPrototypeStartsWith(request, './') || - StringPrototypeStartsWith(request, '../') || - ((isWindows && StringPrototypeStartsWith(request, '.\\')) || - StringPrototypeStartsWith(request, '..\\')); - - if (isRelative) { + if (isRelative(request)) { paths = options.paths; } else { const fakeParent = new Module('', null); @@ -1978,6 +1963,21 @@ function createRequire(filename) { return createRequireFromPath(filepath); } +/** + * Checks if a path is relative + * @param {string} path the target path + * @returns {boolean} true if the path is relative, false otherwise + */ +function isRelative(path) { + if (StringPrototypeCharCodeAt(path, 0) !== CHAR_DOT) { return false; } + + return path.length === 1 || path === '..' || + StringPrototypeStartsWith(path, './') || + StringPrototypeStartsWith(path, '../') || + ((isWindows && StringPrototypeStartsWith(path, '.\\')) || + StringPrototypeStartsWith(path, '..\\')); +} + Module.createRequire = createRequire; /** diff --git a/test/fixtures/module-require/relative/subdir/relative-subdir.js b/test/fixtures/module-require/relative/subdir/relative-subdir.js new file mode 100644 index 00000000000000..34eb71b3c6ca39 --- /dev/null +++ b/test/fixtures/module-require/relative/subdir/relative-subdir.js @@ -0,0 +1 @@ +exports.value = 'relative subdir'; diff --git a/test/parallel/test-require-resolve-opts-paths-relative.js b/test/parallel/test-require-resolve-opts-paths-relative.js new file mode 100644 index 00000000000000..522a1fdbce82a4 --- /dev/null +++ b/test/parallel/test-require-resolve-opts-paths-relative.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +if (!common.isMainThread) + common.skip('process.chdir is not available in Workers'); + +const subdir = fixtures.path('module-require', 'relative', 'subdir'); + +process.chdir(subdir); + +// Parent directory paths (`..`) work as intended +{ + assert(require.resolve('.', { paths: ['../'] }).endsWith('index.js')); + assert(require.resolve('./index.js', { paths: ['../'] }).endsWith('index.js')); + + // paths: [".."] should resolve like paths: ["../"] + assert(require.resolve('.', { paths: ['..'] }).endsWith('index.js')); + assert(require.resolve('./index.js', { paths: ['..'] }).endsWith('index.js')); +} + +process.chdir('..'); + +// Current directory paths (`.`) work as intended +{ + assert(require.resolve('.', { paths: ['.'] }).endsWith('index.js')); + assert(require.resolve('./index.js', { paths: ['./'] }).endsWith('index.js')); + + // paths: ["."] should resolve like paths: ["../"] + assert(require.resolve('.', { paths: ['.'] }).endsWith('index.js')); + assert(require.resolve('./index.js', { paths: ['.'] }).endsWith('index.js')); +} + +// Sub directory paths work as intended +{ + // assert.deepStrictEqual(fs.readdirSync('./subdir'), [5]); + assert(require.resolve('./relative-subdir.js', { paths: ['./subdir'] }).endsWith('relative-subdir.js')); + + // paths: ["subdir"] should resolve like paths: ["./subdir"] + assert(require.resolve('./relative-subdir.js', { paths: ['subdir'] }).endsWith('relative-subdir.js')); +} From ce6a62872081d720734d82bf975653a322795288 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 26 Jan 2025 01:30:11 +0000 Subject: [PATCH 129/208] doc: add note regarding commit message trailers Co-authored-by: Yagiz Nizipli Co-authored-by: Antoine du Hamel PR-URL: https://github.com/nodejs/node/pull/56736 Reviewed-By: James M Snell Reviewed-By: Antoine du Hamel Reviewed-By: Rafael Gonzaga Reviewed-By: Luigi Pinca --- doc/contributing/pull-requests.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/contributing/pull-requests.md b/doc/contributing/pull-requests.md index 2ad538b3fd8e29..8914d60c95aa2f 100644 --- a/doc/contributing/pull-requests.md +++ b/doc/contributing/pull-requests.md @@ -184,6 +184,11 @@ A good commit message should describe what changed and why. of the log. Use the `Fixes:` prefix and the full issue URL. For other references use `Refs:`. + `Fixes:` and `Refs:` trailers get automatically added to your commit message + when the Pull Request lands as long as they are included in the + Pull Request's description. If the Pull Request lands in several commits, + by default the trailers found in the description are added to each commits. + Examples: * `Fixes: https://github.com/nodejs/node/issues/1337` From e0a71517fef4ca83f2d40d2d1600022bc82a7f9f Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 15 Jan 2025 13:57:47 -0800 Subject: [PATCH 130/208] src: move more crypto to ncrypto PR-URL: https://github.com/nodejs/node/pull/56653 Reviewed-By: Yagiz Nizipli --- deps/ncrypto/ncrypto.cc | 942 ++++++++++++++++++++++++++++++++++- deps/ncrypto/ncrypto.h | 289 ++++++++++- src/crypto/crypto_cipher.cc | 126 ++--- src/crypto/crypto_cipher.h | 16 +- src/crypto/crypto_common.cc | 9 +- src/crypto/crypto_context.cc | 2 +- src/crypto/crypto_dh.cc | 24 +- src/crypto/crypto_dsa.cc | 47 +- src/crypto/crypto_ec.cc | 71 +-- src/crypto/crypto_hash.cc | 140 +++--- src/crypto/crypto_hkdf.cc | 4 +- src/crypto/crypto_hmac.cc | 75 +-- src/crypto/crypto_keygen.cc | 6 +- src/crypto/crypto_keys.cc | 15 +- src/crypto/crypto_pbkdf2.cc | 6 +- src/crypto/crypto_random.cc | 22 +- src/crypto/crypto_rsa.cc | 291 ++++------- src/crypto/crypto_sig.cc | 596 ++++++++++------------ src/crypto/crypto_sig.h | 49 +- src/crypto/crypto_tls.cc | 76 ++- src/crypto/crypto_tls.h | 22 +- src/crypto/crypto_util.cc | 13 +- src/crypto/crypto_util.h | 45 +- src/crypto/crypto_x509.cc | 227 ++++----- src/crypto/crypto_x509.h | 10 +- 25 files changed, 1989 insertions(+), 1134 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index ce2e7b384eb198..be3ef98d763366 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -15,8 +15,23 @@ #include "dh-primes.h" #endif // OPENSSL_IS_BORINGSSL +// EVP_PKEY_CTX_set_dsa_paramgen_q_bits was added in OpenSSL 1.1.1e. +#if OPENSSL_VERSION_NUMBER < 0x1010105fL +#define EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, qbits) \ + EVP_PKEY_CTX_ctrl((ctx), \ + EVP_PKEY_DSA, \ + EVP_PKEY_OP_PARAMGEN, \ + EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, \ + (qbits), \ + nullptr) +#endif + namespace ncrypto { namespace { +using BignumCtxPointer = DeleteFnPtr; +using BignumGenCallbackPointer = DeleteFnPtr; +using NetscapeSPKIPointer = DeleteFnPtr; + static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB & ~ASN1_STRFLGS_ESC_CTRL; } // namespace @@ -87,6 +102,10 @@ DataPointer DataPointer::Alloc(size_t len) { return DataPointer(OPENSSL_zalloc(len), len); } +DataPointer DataPointer::Copy(const Buffer& buffer) { + return DataPointer(OPENSSL_memdup(buffer.data, buffer.len), buffer.len); +} + DataPointer::DataPointer(void* data, size_t length) : data_(data), len_(length) {} @@ -109,6 +128,11 @@ DataPointer::~DataPointer() { reset(); } +void DataPointer::zero() { + if (!data_) return; + OPENSSL_cleanse(data_, len_); +} + void DataPointer::reset(void* data, size_t length) { if (data_ != nullptr) { OPENSSL_clear_free(data_, len_); @@ -131,6 +155,15 @@ Buffer DataPointer::release() { return buf; } +DataPointer DataPointer::resize(size_t len) { + size_t actual_len = std::min(len_, len); + auto buf = release(); + if (actual_len == len_) return DataPointer(buf); + buf.data = OPENSSL_realloc(buf.data, actual_len); + buf.len = actual_len; + return DataPointer(buf); +} + // ============================================================================ bool isFipsEnabled() { #if OPENSSL_VERSION_MAJOR >= 3 @@ -782,7 +815,7 @@ bool PrintGeneralName(const BIOPointer& out, const GENERAL_NAME* gen) { bool SafeX509SubjectAltNamePrint(const BIOPointer& out, X509_EXTENSION* ext) { auto ret = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); - NCRYPTO_ASSERT_EQUAL(ret, NID_subject_alt_name, "unexpected extension type"); + if (ret != NID_subject_alt_name) return false; GENERAL_NAMES* names = static_cast(X509V3_EXT_d2i(ext)); if (names == nullptr) return false; @@ -805,7 +838,7 @@ bool SafeX509SubjectAltNamePrint(const BIOPointer& out, X509_EXTENSION* ext) { bool SafeX509InfoAccessPrint(const BIOPointer& out, X509_EXTENSION* ext) { auto ret = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); - NCRYPTO_ASSERT_EQUAL(ret, NID_info_access, "unexpected extension type"); + if (ret != NID_info_access) return false; AUTHORITY_INFO_ACCESS* descs = static_cast(X509V3_EXT_d2i(ext)); @@ -1132,6 +1165,49 @@ Result X509Pointer::Parse( return Result(ERR_get_error()); } +bool X509View::enumUsages(UsageCallback callback) const { + if (cert_ == nullptr) return false; + StackOfASN1 eku(static_cast( + X509_get_ext_d2i(cert_, NID_ext_key_usage, nullptr, nullptr))); + if (!eku) return false; + const int count = sk_ASN1_OBJECT_num(eku.get()); + char buf[256]{}; + + for (int i = 0; i < count; i++) { + if (OBJ_obj2txt(buf, sizeof(buf), sk_ASN1_OBJECT_value(eku.get(), i), 1) >= + 0) { + callback(buf); + } + } + return true; +} + +bool X509View::ifRsa(KeyCallback callback) const { + if (cert_ == nullptr) return true; + OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); + auto id = EVP_PKEY_id(pkey); + if (id == EVP_PKEY_RSA || id == EVP_PKEY_RSA2 || id == EVP_PKEY_RSA_PSS) { + Rsa rsa(EVP_PKEY_get0_RSA(pkey)); + if (!rsa) [[unlikely]] + return true; + return callback(rsa); + } + return true; +} + +bool X509View::ifEc(KeyCallback callback) const { + if (cert_ == nullptr) return true; + OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); + auto id = EVP_PKEY_id(pkey); + if (id == EVP_PKEY_EC) { + Ec ec(EVP_PKEY_get0_EC_KEY(pkey)); + if (!ec) [[unlikely]] + return true; + return callback(ec); + } + return true; +} + X509Pointer X509Pointer::IssuerFrom(const SSLPointer& ssl, const X509View& view) { return IssuerFrom(SSL_get_SSL_CTX(ssl.get()), view); @@ -1493,7 +1569,7 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, size_t out_size; if (!ourKey || !theirKey) return {}; - EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(ourKey.get(), nullptr)); + auto ctx = EVPKeyCtxPointer::New(ourKey); if (!ctx || EVP_PKEY_derive_init(ctx.get()) <= 0 || EVP_PKEY_derive_set_peer(ctx.get(), theirKey.get()) <= 0 || EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0) { @@ -1522,9 +1598,18 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, // KDF const EVP_MD* getDigestByName(const std::string_view name) { + // Historically, "dss1" and "DSS1" were DSA aliases for SHA-1 + // exposed through the public API. + if (name == "dss1" || name == "DSS1") [[unlikely]] { + return EVP_sha1(); + } return EVP_get_digestbyname(name.data()); } +const EVP_CIPHER* getCipherByName(const std::string_view name) { + return EVP_get_cipherbyname(name.data()); +} + bool checkHkdfLength(const EVP_MD* md, size_t length) { // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as // the output of the hash function. 255 is a hard limit because HKDF appends @@ -1547,8 +1632,7 @@ DataPointer hkdf(const EVP_MD* md, return {}; } - EVPKeyCtxPointer ctx = - EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); + auto ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_HKDF); if (!ctx || !EVP_PKEY_derive_init(ctx.get()) || !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), md) || !EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), info.data, info.len)) { @@ -1704,6 +1788,26 @@ EVPKeyPointer EVPKeyPointer::NewRawPrivate( EVP_PKEY_new_raw_private_key(id, nullptr, data.data, data.len)); } +EVPKeyPointer EVPKeyPointer::NewDH(DHPointer&& dh) { + if (!dh) return {}; + auto key = New(); + if (!key) return {}; + if (EVP_PKEY_assign_DH(key.get(), dh.get())) { + dh.release(); + } + return key; +} + +EVPKeyPointer EVPKeyPointer::NewRSA(RSAPointer&& rsa) { + if (!rsa) return {}; + auto key = New(); + if (!key) return {}; + if (EVP_PKEY_assign_RSA(key.get(), rsa.get())) { + rsa.release(); + } + return key; +} + EVPKeyPointer::EVPKeyPointer(EVP_PKEY* pkey) : pkey_(pkey) {} EVPKeyPointer::EVPKeyPointer(EVPKeyPointer&& other) noexcept @@ -1757,7 +1861,7 @@ size_t EVPKeyPointer::size() const { EVPKeyCtxPointer EVPKeyPointer::newCtx() const { if (!pkey_) return {}; - return EVPKeyCtxPointer(EVP_PKEY_CTX_new(get(), nullptr)); + return EVPKeyCtxPointer::New(*this); } size_t EVPKeyPointer::rawPublicKeySize() const { @@ -2230,6 +2334,84 @@ Result EVPKeyPointer::writePublicKey( return bio; } +bool EVPKeyPointer::isRsaVariant() const { + if (!pkey_) return false; + int type = id(); + return type == EVP_PKEY_RSA || type == EVP_PKEY_RSA2 || + type == EVP_PKEY_RSA_PSS; +} + +bool EVPKeyPointer::isOneShotVariant() const { + if (!pkey_) return false; + int type = id(); + return type == EVP_PKEY_ED25519 || type == EVP_PKEY_ED448; +} + +bool EVPKeyPointer::isSigVariant() const { + if (!pkey_) return false; + int type = id(); + return type == EVP_PKEY_EC || type == EVP_PKEY_DSA; +} + +int EVPKeyPointer::getDefaultSignPadding() const { + return id() == EVP_PKEY_RSA_PSS ? RSA_PKCS1_PSS_PADDING : RSA_PKCS1_PADDING; +} + +std::optional EVPKeyPointer::getBytesOfRS() const { + if (!pkey_) return std::nullopt; + int bits, id = base_id(); + + if (id == EVP_PKEY_DSA) { + const DSA* dsa_key = EVP_PKEY_get0_DSA(get()); + // Both r and s are computed mod q, so their width is limited by that of q. + bits = BignumPointer::GetBitCount(DSA_get0_q(dsa_key)); + } else if (id == EVP_PKEY_EC) { + bits = EC_GROUP_order_bits(ECKeyPointer::GetGroup(*this)); + } else { + return std::nullopt; + } + + return (bits + 7) / 8; +} + +EVPKeyPointer::operator Rsa() const { + int type = id(); + if (type != EVP_PKEY_RSA && type != EVP_PKEY_RSA_PSS) return {}; + + // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL + // versions older than 1.1.1e via FIPS / dynamic linking. + OSSL3_CONST RSA* rsa; + if (OPENSSL_VERSION_NUMBER >= 0x1010105fL) { + rsa = EVP_PKEY_get0_RSA(get()); + } else { + rsa = static_cast(EVP_PKEY_get0(get())); + } + if (rsa == nullptr) return {}; + return Rsa(rsa); +} + +bool EVPKeyPointer::validateDsaParameters() const { + if (!pkey_) return false; + /* Validate DSA2 parameters from FIPS 186-4 */ +#if OPENSSL_VERSION_MAJOR >= 3 + if (EVP_default_properties_is_fips_enabled(nullptr) && EVP_PKEY_DSA == id()) { +#else + if (FIPS_mode() && EVP_PKEY_DSA == id()) { +#endif + const DSA* dsa = EVP_PKEY_get0_DSA(pkey_.get()); + const BIGNUM* p; + const BIGNUM* q; + DSA_get0_pqg(dsa, &p, &q, nullptr); + int L = BignumPointer::GetBitCount(p); + int N = BignumPointer::GetBitCount(q); + + return (L == 1024 && N == 160) || (L == 2048 && N == 224) || + (L == 2048 && N == 256) || (L == 3072 && N == 256); + } + + return true; +} + // ============================================================================ SSLPointer::SSLPointer(SSL* ssl) : ssl_(ssl) {} @@ -2883,4 +3065,752 @@ ECKeyPointer ECKeyPointer::New(const EC_GROUP* group) { return ptr; } +// ============================================================================ + +EVPKeyCtxPointer::EVPKeyCtxPointer() : ctx_(nullptr) {} + +EVPKeyCtxPointer::EVPKeyCtxPointer(EVP_PKEY_CTX* ctx) : ctx_(ctx) {} + +EVPKeyCtxPointer::EVPKeyCtxPointer(EVPKeyCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +EVPKeyCtxPointer& EVPKeyCtxPointer::operator=( + EVPKeyCtxPointer&& other) noexcept { + ctx_.reset(other.release()); + return *this; +} + +EVPKeyCtxPointer::~EVPKeyCtxPointer() { + reset(); +} + +void EVPKeyCtxPointer::reset(EVP_PKEY_CTX* ctx) { + ctx_.reset(ctx); +} + +EVP_PKEY_CTX* EVPKeyCtxPointer::release() { + return ctx_.release(); +} + +EVPKeyCtxPointer EVPKeyCtxPointer::New(const EVPKeyPointer& key) { + if (!key) return {}; + return EVPKeyCtxPointer(EVP_PKEY_CTX_new(key.get(), nullptr)); +} + +EVPKeyCtxPointer EVPKeyCtxPointer::NewFromID(int id) { + return EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(id, nullptr)); +} + +bool EVPKeyCtxPointer::initForDerive(const EVPKeyPointer& peer) { + if (!ctx_) return false; + if (EVP_PKEY_derive_init(ctx_.get()) != 1) return false; + return EVP_PKEY_derive_set_peer(ctx_.get(), peer.get()) == 1; +} + +bool EVPKeyCtxPointer::initForKeygen() { + if (!ctx_) return false; + return EVP_PKEY_keygen_init(ctx_.get()) == 1; +} + +bool EVPKeyCtxPointer::initForParamgen() { + if (!ctx_) return false; + return EVP_PKEY_paramgen_init(ctx_.get()) == 1; +} + +int EVPKeyCtxPointer::initForVerify() { + if (!ctx_) return 0; + return EVP_PKEY_verify_init(ctx_.get()); +} + +int EVPKeyCtxPointer::initForSign() { + if (!ctx_) return 0; + return EVP_PKEY_sign_init(ctx_.get()); +} + +bool EVPKeyCtxPointer::setDhParameters(int prime_size, uint32_t generator) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx_.get(), prime_size) == 1 && + EVP_PKEY_CTX_set_dh_paramgen_generator(ctx_.get(), generator) == 1; +} + +bool EVPKeyCtxPointer::setDsaParameters(uint32_t bits, + std::optional q_bits) { + if (!ctx_) return false; + if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx_.get(), bits) != 1) { + return false; + } + if (q_bits.has_value() && + EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx_.get(), q_bits.value()) != 1) { + return false; + } + return true; +} + +bool EVPKeyCtxPointer::setEcParameters(int curve, int encoding) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_.get(), curve) == 1 && + EVP_PKEY_CTX_set_ec_param_enc(ctx_.get(), encoding) == 1; +} + +bool EVPKeyCtxPointer::setRsaOaepMd(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_oaep_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaMgf1Md(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_mgf1_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaPadding(int padding) { + return setRsaPadding(ctx_.get(), padding, std::nullopt); +} + +bool EVPKeyCtxPointer::setRsaPadding(EVP_PKEY_CTX* ctx, + int padding, + std::optional salt_len) { + if (ctx == nullptr) return false; + if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0) { + return false; + } + if (padding == RSA_PKCS1_PSS_PADDING && salt_len.has_value()) { + return EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, salt_len.value()) > 0; + } + return true; +} + +bool EVPKeyCtxPointer::setRsaKeygenBits(int bits) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_rsa_keygen_bits(ctx_.get(), bits) == 1; +} + +bool EVPKeyCtxPointer::setRsaKeygenPubExp(BignumPointer&& e) { + if (!ctx_) return false; + if (EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx_.get(), e.get()) == 1) { + // The ctx_ takes ownership of e on success. + e.release(); + return true; + } + return false; +} + +bool EVPKeyCtxPointer::setRsaPssKeygenMd(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaPssKeygenMgf1Md(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaPssSaltlen(int salt_len) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(ctx_.get(), salt_len) > 0; +} + +bool EVPKeyCtxPointer::setRsaImplicitRejection() { + if (!ctx_) return false; + return EVP_PKEY_CTX_ctrl_str( + ctx_.get(), "rsa_pkcs1_implicit_rejection", "1") > 0; + // From the doc -2 means that the option is not supported. + // The default for the option is enabled and if it has been + // specifically disabled we want to respect that so we will + // not throw an error if the option is supported regardless + // of how it is set. The call to set the value + // will not affect what is used since a different context is + // used in the call if the option is supported +} + +bool EVPKeyCtxPointer::setRsaOaepLabel(DataPointer&& data) { + if (!ctx_) return false; + if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx_.get(), + static_cast(data.get()), + data.size()) > 0) { + // The ctx_ takes ownership of data on success. + data.release(); + return true; + } + return false; +} + +bool EVPKeyCtxPointer::setSignatureMd(const EVPMDCtxPointer& md) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_signature_md(ctx_.get(), EVP_MD_CTX_md(md.get())) == + 1; +} + +bool EVPKeyCtxPointer::initForEncrypt() { + if (!ctx_) return false; + return EVP_PKEY_encrypt_init(ctx_.get()) == 1; +} + +bool EVPKeyCtxPointer::initForDecrypt() { + if (!ctx_) return false; + return EVP_PKEY_decrypt_init(ctx_.get()) == 1; +} + +DataPointer EVPKeyCtxPointer::derive() const { + if (!ctx_) return {}; + size_t len = 0; + if (EVP_PKEY_derive(ctx_.get(), nullptr, &len) != 1) return {}; + auto data = DataPointer::Alloc(len); + if (!data) return {}; + if (EVP_PKEY_derive( + ctx_.get(), static_cast(data.get()), &len) != 1) { + return {}; + } + return data; +} + +EVPKeyPointer EVPKeyCtxPointer::paramgen() const { + if (!ctx_) return {}; + EVP_PKEY* key = nullptr; + if (EVP_PKEY_paramgen(ctx_.get(), &key) != 1) return {}; + return EVPKeyPointer(key); +} + +bool EVPKeyCtxPointer::publicCheck() const { + if (!ctx_) return false; +#if OPENSSL_VERSION_MAJOR >= 3 + return EVP_PKEY_public_check_quick(ctx_.get()) == 1; +#else + return EVP_PKEY_public_check(ctx_.get()) == 1; +#endif +} + +bool EVPKeyCtxPointer::privateCheck() const { + if (!ctx_) return false; + return EVP_PKEY_check(ctx_.get()) == 1; +} + +bool EVPKeyCtxPointer::verify(const Buffer& sig, + const Buffer& data) { + if (!ctx_) return false; + return EVP_PKEY_verify(ctx_.get(), sig.data, sig.len, data.data, data.len) == + 1; +} + +DataPointer EVPKeyCtxPointer::sign(const Buffer& data) { + if (!ctx_) return {}; + size_t len = 0; + if (EVP_PKEY_sign(ctx_.get(), nullptr, &len, data.data, data.len) != 1) { + return {}; + } + auto buf = DataPointer::Alloc(len); + if (!buf) return {}; + if (EVP_PKEY_sign(ctx_.get(), + static_cast(buf.get()), + &len, + data.data, + data.len) != 1) { + return {}; + } + return buf.resize(len); +} + +bool EVPKeyCtxPointer::signInto(const Buffer& data, + Buffer* sig) { + if (!ctx_) return false; + size_t len = sig->len; + if (EVP_PKEY_sign(ctx_.get(), sig->data, &len, data.data, data.len) != 1) { + return false; + } + sig->len = len; + return true; +} + +// ============================================================================ + +namespace { + +using EVP_PKEY_cipher_init_t = int(EVP_PKEY_CTX* ctx); +using EVP_PKEY_cipher_t = int(EVP_PKEY_CTX* ctx, + unsigned char* out, + size_t* outlen, + const unsigned char* in, + size_t inlen); + +template +DataPointer RSA_Cipher(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + EVPKeyCtxPointer ctx = key.newCtx(); + + if (!ctx || init(ctx.get()) <= 0 || !ctx.setRsaPadding(params.padding) || + (params.digest != nullptr && (!ctx.setRsaOaepMd(params.digest) || + !ctx.setRsaMgf1Md(params.digest)))) { + return {}; + } + + if (params.label.len != 0 && params.label.data != nullptr && + !ctx.setRsaOaepLabel(DataPointer::Copy(params.label))) { + return {}; + } + + size_t out_len = 0; + if (cipher(ctx.get(), + nullptr, + &out_len, + reinterpret_cast(in.data), + in.len) <= 0) { + return {}; + } + + auto buf = DataPointer::Alloc(out_len); + if (!buf) return {}; + + if (cipher(ctx.get(), + static_cast(buf.get()), + &out_len, + static_cast(in.data), + in.len) <= 0) { + return {}; + } + + return buf.resize(out_len); +} + +template +DataPointer CipherImpl(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + EVPKeyCtxPointer ctx = key.newCtx(); + if (!ctx || init(ctx.get()) <= 0 || !ctx.setRsaPadding(params.padding) || + (params.digest != nullptr && !ctx.setRsaOaepMd(params.digest))) { + return {}; + } + + if (params.label.len != 0 && params.label.data != nullptr && + !ctx.setRsaOaepLabel(DataPointer::Copy(params.label))) { + return {}; + } + + size_t out_len = 0; + if (cipher(ctx.get(), + nullptr, + &out_len, + static_cast(in.data), + in.len) <= 0) { + return {}; + } + + auto buf = DataPointer::Alloc(out_len); + if (!buf) return {}; + + if (cipher(ctx.get(), + static_cast(buf.get()), + &out_len, + static_cast(in.data), + in.len) <= 0) { + return {}; + } + + return buf.resize(out_len); +} +} // namespace + +Rsa::Rsa() : rsa_(nullptr) {} + +Rsa::Rsa(OSSL3_CONST RSA* ptr) : rsa_(ptr) {} + +const Rsa::PublicKey Rsa::getPublicKey() const { + if (rsa_ == nullptr) return {}; + PublicKey key; + RSA_get0_key(rsa_, &key.n, &key.e, &key.d); + return key; +} + +const Rsa::PrivateKey Rsa::getPrivateKey() const { + if (rsa_ == nullptr) return {}; + PrivateKey key; + RSA_get0_factors(rsa_, &key.p, &key.q); + RSA_get0_crt_params(rsa_, &key.dp, &key.dq, &key.qi); + return key; +} + +const std::optional Rsa::getPssParams() const { + if (rsa_ == nullptr) return std::nullopt; + const RSA_PSS_PARAMS* params = RSA_get0_pss_params(rsa_); + if (params == nullptr) return std::nullopt; + Rsa::PssParams ret{ + .digest = OBJ_nid2ln(NID_sha1), + .mgf1_digest = OBJ_nid2ln(NID_sha1), + .salt_length = 20, + }; + + if (params->hashAlgorithm != nullptr) { + const ASN1_OBJECT* hash_obj; + X509_ALGOR_get0(&hash_obj, nullptr, nullptr, params->hashAlgorithm); + ret.digest = OBJ_nid2ln(OBJ_obj2nid(hash_obj)); + } + + if (params->maskGenAlgorithm != nullptr) { + const ASN1_OBJECT* mgf_obj; + X509_ALGOR_get0(&mgf_obj, nullptr, nullptr, params->maskGenAlgorithm); + int mgf_nid = OBJ_obj2nid(mgf_obj); + if (mgf_nid == NID_mgf1) { + const ASN1_OBJECT* mgf1_hash_obj; + X509_ALGOR_get0(&mgf1_hash_obj, nullptr, nullptr, params->maskHash); + ret.mgf1_digest = OBJ_nid2ln(OBJ_obj2nid(mgf1_hash_obj)); + } + } + + if (params->saltLength != nullptr) { + if (ASN1_INTEGER_get_int64(&ret.salt_length, params->saltLength) != 1) { + return std::nullopt; + } + } + return ret; +} + +bool Rsa::setPublicKey(BignumPointer&& n, BignumPointer&& e) { + if (!n || !e) return false; + if (RSA_set0_key(const_cast(rsa_), n.get(), e.get(), nullptr) == 1) { + n.release(); + e.release(); + return true; + } + return false; +} + +bool Rsa::setPrivateKey(BignumPointer&& d, + BignumPointer&& q, + BignumPointer&& p, + BignumPointer&& dp, + BignumPointer&& dq, + BignumPointer&& qi) { + if (!RSA_set0_key(const_cast(rsa_), nullptr, nullptr, d.get())) { + return false; + } + d.release(); + + if (!RSA_set0_factors(const_cast(rsa_), p.get(), q.get())) { + return false; + } + p.release(); + q.release(); + + if (!RSA_set0_crt_params( + const_cast(rsa_), dp.get(), dq.get(), qi.get())) { + return false; + } + dp.release(); + dq.release(); + qi.release(); + return true; +} + +DataPointer Rsa::encrypt(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + return RSA_Cipher(key, params, in); +} + +DataPointer Rsa::decrypt(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + return RSA_Cipher(key, params, in); +} + +DataPointer Cipher::encrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // public operation + return CipherImpl(key, params, in); +} + +DataPointer Cipher::decrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // private operation + return CipherImpl(key, params, in); +} + +DataPointer Cipher::sign(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // private operation + return CipherImpl(key, params, in); +} + +DataPointer Cipher::recover(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // public operation + return CipherImpl( + key, params, in); +} + +// ============================================================================ + +Ec::Ec() : ec_(nullptr) {} + +Ec::Ec(OSSL3_CONST EC_KEY* key) : ec_(key) {} + +const EC_GROUP* Ec::getGroup() const { + return ECKeyPointer::GetGroup(ec_); +} + +int Ec::getCurve() const { + return EC_GROUP_get_curve_name(getGroup()); +} + +// ============================================================================ + +EVPMDCtxPointer::EVPMDCtxPointer() : ctx_(nullptr) {} + +EVPMDCtxPointer::EVPMDCtxPointer(EVP_MD_CTX* ctx) : ctx_(ctx) {} + +EVPMDCtxPointer::EVPMDCtxPointer(EVPMDCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +EVPMDCtxPointer& EVPMDCtxPointer::operator=(EVPMDCtxPointer&& other) noexcept { + ctx_.reset(other.release()); + return *this; +} + +EVPMDCtxPointer::~EVPMDCtxPointer() { + reset(); +} + +void EVPMDCtxPointer::reset(EVP_MD_CTX* ctx) { + ctx_.reset(ctx); +} + +EVP_MD_CTX* EVPMDCtxPointer::release() { + return ctx_.release(); +} + +bool EVPMDCtxPointer::digestInit(const EVP_MD* digest) { + if (!ctx_) return false; + return EVP_DigestInit_ex(ctx_.get(), digest, nullptr) > 0; +} + +bool EVPMDCtxPointer::digestUpdate(const Buffer& in) { + if (!ctx_) return false; + return EVP_DigestUpdate(ctx_.get(), in.data, in.len) > 0; +} + +DataPointer EVPMDCtxPointer::digestFinal(size_t length) { + if (!ctx_) return {}; + + auto buf = DataPointer::Alloc(length); + if (!buf) return {}; + + Buffer buffer = buf; + + if (!digestFinalInto(&buffer)) [[unlikely]] { + return {}; + } + + return buf; +} + +bool EVPMDCtxPointer::digestFinalInto(Buffer* buf) { + if (!ctx_) return false; + + auto ptr = static_cast(buf->data); + + int ret = (buf->len == getExpectedSize()) + ? EVP_DigestFinal_ex(ctx_.get(), ptr, nullptr) + : EVP_DigestFinalXOF(ctx_.get(), ptr, buf->len); + + if (ret != 1) [[unlikely]] + return false; + + return true; +} + +size_t EVPMDCtxPointer::getExpectedSize() { + if (!ctx_) return 0; + return EVP_MD_CTX_size(ctx_.get()); +} + +size_t EVPMDCtxPointer::getDigestSize() const { + return EVP_MD_size(getDigest()); +} + +const EVP_MD* EVPMDCtxPointer::getDigest() const { + if (!ctx_) return nullptr; + return EVP_MD_CTX_md(ctx_.get()); +} + +bool EVPMDCtxPointer::hasXofFlag() const { + if (!ctx_) return false; + return (EVP_MD_flags(getDigest()) & EVP_MD_FLAG_XOF) == EVP_MD_FLAG_XOF; +} + +bool EVPMDCtxPointer::copyTo(const EVPMDCtxPointer& other) const { + if (!ctx_ || !other) return {}; + if (EVP_MD_CTX_copy(other.get(), ctx_.get()) != 1) return false; + return true; +} + +std::optional EVPMDCtxPointer::signInit(const EVPKeyPointer& key, + const EVP_MD* digest) { + EVP_PKEY_CTX* ctx = nullptr; + if (!EVP_DigestSignInit(ctx_.get(), &ctx, digest, nullptr, key.get())) { + return std::nullopt; + } + return ctx; +} + +std::optional EVPMDCtxPointer::verifyInit( + const EVPKeyPointer& key, const EVP_MD* digest) { + EVP_PKEY_CTX* ctx = nullptr; + if (!EVP_DigestVerifyInit(ctx_.get(), &ctx, digest, nullptr, key.get())) { + return std::nullopt; + } + return ctx; +} + +DataPointer EVPMDCtxPointer::signOneShot( + const Buffer& buf) const { + if (!ctx_) return {}; + size_t len; + if (!EVP_DigestSign(ctx_.get(), nullptr, &len, buf.data, buf.len)) { + return {}; + } + auto data = DataPointer::Alloc(len); + if (!data) [[unlikely]] + return {}; + + if (!EVP_DigestSign(ctx_.get(), + static_cast(data.get()), + &len, + buf.data, + buf.len)) { + return {}; + } + return data; +} + +DataPointer EVPMDCtxPointer::sign( + const Buffer& buf) const { + if (!ctx_) [[unlikely]] + return {}; + size_t len; + if (!EVP_DigestSignUpdate(ctx_.get(), buf.data, buf.len) || + !EVP_DigestSignFinal(ctx_.get(), nullptr, &len)) { + return {}; + } + auto data = DataPointer::Alloc(len); + if (!data) [[unlikely]] + return {}; + if (!EVP_DigestSignFinal( + ctx_.get(), static_cast(data.get()), &len)) { + return {}; + } + return data.resize(len); +} + +bool EVPMDCtxPointer::verify(const Buffer& buf, + const Buffer& sig) const { + if (!ctx_) return false; + int ret = EVP_DigestVerify(ctx_.get(), sig.data, sig.len, buf.data, buf.len); + return ret == 1; +} + +EVPMDCtxPointer EVPMDCtxPointer::New() { + return EVPMDCtxPointer(EVP_MD_CTX_new()); +} + +// ============================================================================ + +bool extractP1363(const Buffer& buf, + unsigned char* dest, + size_t n) { + auto asn1_sig = ECDSASigPointer::Parse(buf); + if (!asn1_sig) return false; + + return BignumPointer::EncodePaddedInto(asn1_sig.r(), dest, n) > 0 && + BignumPointer::EncodePaddedInto(asn1_sig.s(), dest + n, n) > 0; +} + +// ============================================================================ + +HMACCtxPointer::HMACCtxPointer() : ctx_(nullptr) {} + +HMACCtxPointer::HMACCtxPointer(HMAC_CTX* ctx) : ctx_(ctx) {} + +HMACCtxPointer::HMACCtxPointer(HMACCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +HMACCtxPointer& HMACCtxPointer::operator=(HMACCtxPointer&& other) noexcept { + ctx_.reset(other.release()); + return *this; +} + +HMACCtxPointer::~HMACCtxPointer() { + reset(); +} + +void HMACCtxPointer::reset(HMAC_CTX* ctx) { + ctx_.reset(ctx); +} + +HMAC_CTX* HMACCtxPointer::release() { + return ctx_.release(); +} + +bool HMACCtxPointer::init(const Buffer& buf, const EVP_MD* md) { + if (!ctx_) return false; + return HMAC_Init_ex(ctx_.get(), buf.data, buf.len, md, nullptr) == 1; +} + +bool HMACCtxPointer::update(const Buffer& buf) { + if (!ctx_) return false; + return HMAC_Update(ctx_.get(), + static_cast(buf.data), + buf.len) == 1; +} + +DataPointer HMACCtxPointer::digest() { + auto data = DataPointer::Alloc(EVP_MAX_MD_SIZE); + if (!data) return {}; + Buffer buf = data; + if (!digestInto(&buf)) return {}; + return data.resize(buf.len); +} + +bool HMACCtxPointer::digestInto(Buffer* buf) { + if (!ctx_) return false; + + unsigned int len = buf->len; + if (!HMAC_Final(ctx_.get(), static_cast(buf->data), &len)) { + return false; + } + buf->len = len; + return true; +} + +HMACCtxPointer HMACCtxPointer::New() { + return HMACCtxPointer(HMAC_CTX_new()); +} + +DataPointer hashDigest(const Buffer& buf, + const EVP_MD* md) { + if (md == nullptr) return {}; + size_t md_len = EVP_MD_size(md); + unsigned int result_size; + auto data = DataPointer::Alloc(md_len); + if (!data) return {}; + + if (!EVP_Digest(buf.data, + buf.len, + reinterpret_cast(data.get()), + &result_size, + md, + nullptr)) { + return {}; + } + + return data.resize(result_size); +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index e5bf2b529bf239..75ac9fd8d705aa 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -201,18 +201,28 @@ struct FunctionDeleter { template using DeleteFnPtr = typename FunctionDeleter::Pointer; -using BignumCtxPointer = DeleteFnPtr; -using BignumGenCallbackPointer = DeleteFnPtr; -using EVPKeyCtxPointer = DeleteFnPtr; -using EVPMDCtxPointer = DeleteFnPtr; -using HMACCtxPointer = DeleteFnPtr; -using NetscapeSPKIPointer = DeleteFnPtr; using PKCS8Pointer = DeleteFnPtr; using RSAPointer = DeleteFnPtr; using SSLSessionPointer = DeleteFnPtr; +class BIOPointer; +class BignumPointer; class CipherCtxPointer; +class DataPointer; +class DHPointer; class ECKeyPointer; +class EVPKeyPointer; +class EVPMDCtxPointer; +class SSLCtxPointer; +class SSLPointer; +class X509View; +class X509Pointer; +class ECDSASigPointer; +class ECGroupPointer; +class ECPointPointer; +class ECKeyPointer; +class Rsa; +class Ec; struct StackOfXASN1Deleter { void operator()(STACK_OF(ASN1_OBJECT) * p) const { @@ -228,6 +238,9 @@ struct Buffer { size_t len = 0; }; +DataPointer hashDigest(const Buffer& data, + const EVP_MD* md); + class Cipher final { public: Cipher() = default; @@ -258,15 +271,108 @@ class Cipher final { static const Cipher FromNid(int nid); static const Cipher FromCtx(const CipherCtxPointer& ctx); + struct CipherParams { + int padding; + const EVP_MD* digest; + const Buffer label; + }; + + static DataPointer encrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + static DataPointer decrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + + static DataPointer sign(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + + static DataPointer recover(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + private: const EVP_CIPHER* cipher_ = nullptr; }; +// ============================================================================ +// RSA + +class Rsa final { + public: + Rsa(); + Rsa(OSSL3_CONST RSA* rsa); + NCRYPTO_DISALLOW_COPY_AND_MOVE(Rsa) + + inline operator bool() const { return rsa_ != nullptr; } + inline operator OSSL3_CONST RSA*() const { return rsa_; } + + struct PublicKey { + const BIGNUM* n; + const BIGNUM* e; + const BIGNUM* d; + }; + struct PrivateKey { + const BIGNUM* p; + const BIGNUM* q; + const BIGNUM* dp; + const BIGNUM* dq; + const BIGNUM* qi; + }; + struct PssParams { + std::string_view digest = "sha1"; + std::optional mgf1_digest = "sha1"; + int64_t salt_length = 20; + }; + + const PublicKey getPublicKey() const; + const PrivateKey getPrivateKey() const; + const std::optional getPssParams() const; + + bool setPublicKey(BignumPointer&& n, BignumPointer&& e); + bool setPrivateKey(BignumPointer&& d, + BignumPointer&& q, + BignumPointer&& p, + BignumPointer&& dp, + BignumPointer&& dq, + BignumPointer&& qi); + + using CipherParams = Cipher::CipherParams; + + static DataPointer encrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + static DataPointer decrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + + private: + OSSL3_CONST RSA* rsa_; +}; + +class Ec final { + public: + Ec(); + Ec(OSSL3_CONST EC_KEY* key); + NCRYPTO_DISALLOW_COPY_AND_MOVE(Ec) + + const EC_GROUP* getGroup() const; + int getCurve() const; + + inline operator bool() const { return ec_ != nullptr; } + inline operator OSSL3_CONST EC_KEY*() const { return ec_; } + + private: + OSSL3_CONST EC_KEY* ec_ = nullptr; +}; + // A managed pointer to a buffer of data. When destroyed the underlying // buffer will be freed. class DataPointer final { public: static DataPointer Alloc(size_t len); + static DataPointer Copy(const Buffer& buffer); DataPointer() = default; explicit DataPointer(void* data, size_t len); @@ -283,6 +389,11 @@ class DataPointer final { void reset(void* data = nullptr, size_t len = 0); void reset(const Buffer& buffer); + // Sets the underlying data buffer to all zeros. + void zero(); + + DataPointer resize(size_t len); + // Releases ownership of the underlying data buffer. It is the caller's // responsibility to ensure the buffer is appropriately freed. Buffer release(); @@ -471,6 +582,74 @@ class CipherCtxPointer final { DeleteFnPtr ctx_; }; +class EVPKeyCtxPointer final { + public: + EVPKeyCtxPointer(); + explicit EVPKeyCtxPointer(EVP_PKEY_CTX* ctx); + EVPKeyCtxPointer(EVPKeyCtxPointer&& other) noexcept; + EVPKeyCtxPointer& operator=(EVPKeyCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(EVPKeyCtxPointer) + ~EVPKeyCtxPointer(); + + inline bool operator==(std::nullptr_t) const noexcept { + return ctx_ == nullptr; + } + inline operator bool() const { return ctx_ != nullptr; } + inline EVP_PKEY_CTX* get() const { return ctx_.get(); } + void reset(EVP_PKEY_CTX* ctx = nullptr); + EVP_PKEY_CTX* release(); + + bool initForDerive(const EVPKeyPointer& peer); + DataPointer derive() const; + + bool initForParamgen(); + bool setDhParameters(int prime_size, uint32_t generator); + bool setDsaParameters(uint32_t bits, std::optional q_bits); + bool setEcParameters(int curve, int encoding); + + bool setRsaOaepMd(const EVP_MD* md); + bool setRsaMgf1Md(const EVP_MD* md); + bool setRsaPadding(int padding); + bool setRsaKeygenPubExp(BignumPointer&& e); + bool setRsaKeygenBits(int bits); + bool setRsaPssKeygenMd(const EVP_MD* md); + bool setRsaPssKeygenMgf1Md(const EVP_MD* md); + bool setRsaPssSaltlen(int salt_len); + bool setRsaImplicitRejection(); + bool setRsaOaepLabel(DataPointer&& data); + + bool setSignatureMd(const EVPMDCtxPointer& md); + + bool publicCheck() const; + bool privateCheck() const; + + bool verify(const Buffer& sig, + const Buffer& data); + DataPointer sign(const Buffer& data); + bool signInto(const Buffer& data, + Buffer* sig); + + static constexpr int kDefaultRsaExponent = 0x10001; + + static bool setRsaPadding(EVP_PKEY_CTX* ctx, + int padding, + std::optional salt_len = std::nullopt); + + EVPKeyPointer paramgen() const; + + bool initForEncrypt(); + bool initForDecrypt(); + bool initForKeygen(); + int initForVerify(); + int initForSign(); + + static EVPKeyCtxPointer New(const EVPKeyPointer& key); + static EVPKeyCtxPointer NewFromID(int id); + + private: + DeleteFnPtr ctx_; +}; + class EVPKeyPointer final { public: static EVPKeyPointer New(); @@ -478,6 +657,8 @@ class EVPKeyPointer final { const Buffer& data); static EVPKeyPointer NewRawPrivate(int id, const Buffer& data); + static EVPKeyPointer NewDH(DHPointer&& dh); + static EVPKeyPointer NewRSA(RSAPointer&& rsa); enum class PKEncodingType { // RSAPublicKey / RSAPrivateKey according to PKCS#1. @@ -578,6 +759,15 @@ class EVPKeyPointer final { static bool IsRSAPrivateKey(const Buffer& buffer); + std::optional getBytesOfRS() const; + int getDefaultSignPadding() const; + operator Rsa() const; + + bool isRsaVariant() const; + bool isOneShotVariant() const; + bool isSigVariant() const; + bool validateDsaParameters() const; + private: DeleteFnPtr pkey_; }; @@ -663,9 +853,6 @@ struct StackOfX509Deleter { }; using StackOfX509 = std::unique_ptr; -class X509Pointer; -class X509View; - class SSLCtxPointer final { public: SSLCtxPointer() = default; @@ -792,6 +979,14 @@ class X509View final { CheckMatch checkEmail(const std::string_view email, int flags) const; CheckMatch checkIp(const std::string_view ip, int flags) const; + using UsageCallback = std::function; + bool enumUsages(UsageCallback callback) const; + + template + using KeyCallback = std::function; + bool ifRsa(KeyCallback callback) const; + bool ifEc(KeyCallback callback) const; + private: const X509* cert_ = nullptr; }; @@ -948,6 +1143,77 @@ class ECKeyPointer final { DeleteFnPtr key_; }; +class EVPMDCtxPointer final { + public: + EVPMDCtxPointer(); + explicit EVPMDCtxPointer(EVP_MD_CTX* ctx); + EVPMDCtxPointer(EVPMDCtxPointer&& other) noexcept; + EVPMDCtxPointer& operator=(EVPMDCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(EVPMDCtxPointer) + ~EVPMDCtxPointer(); + + inline bool operator==(std::nullptr_t) noexcept { return ctx_ == nullptr; } + inline operator bool() const { return ctx_ != nullptr; } + inline EVP_MD_CTX* get() const { return ctx_.get(); } + inline operator EVP_MD_CTX*() const { return ctx_.get(); } + void reset(EVP_MD_CTX* ctx = nullptr); + EVP_MD_CTX* release(); + + bool digestInit(const EVP_MD* digest); + bool digestUpdate(const Buffer& in); + DataPointer digestFinal(size_t length); + bool digestFinalInto(Buffer* buf); + size_t getExpectedSize(); + + std::optional signInit(const EVPKeyPointer& key, + const EVP_MD* digest); + std::optional verifyInit(const EVPKeyPointer& key, + const EVP_MD* digest); + + DataPointer signOneShot(const Buffer& buf) const; + DataPointer sign(const Buffer& buf) const; + bool verify(const Buffer& buf, + const Buffer& sig) const; + + const EVP_MD* getDigest() const; + size_t getDigestSize() const; + bool hasXofFlag() const; + + bool copyTo(const EVPMDCtxPointer& other) const; + + static EVPMDCtxPointer New(); + + private: + DeleteFnPtr ctx_; +}; + +class HMACCtxPointer final { + public: + HMACCtxPointer(); + explicit HMACCtxPointer(HMAC_CTX* ctx); + HMACCtxPointer(HMACCtxPointer&& other) noexcept; + HMACCtxPointer& operator=(HMACCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(HMACCtxPointer) + ~HMACCtxPointer(); + + inline bool operator==(std::nullptr_t) noexcept { return ctx_ == nullptr; } + inline operator bool() const { return ctx_ != nullptr; } + inline HMAC_CTX* get() const { return ctx_.get(); } + inline operator HMAC_CTX*() const { return ctx_.get(); } + void reset(HMAC_CTX* ctx = nullptr); + HMAC_CTX* release(); + + bool init(const Buffer& buf, const EVP_MD* md); + bool update(const Buffer& buf); + DataPointer digest(); + bool digestInto(Buffer* buf); + + static HMACCtxPointer New(); + + private: + DeleteFnPtr ctx_; +}; + #ifndef OPENSSL_NO_ENGINE class EnginePointer final { public: @@ -1025,12 +1291,17 @@ Buffer ExportChallenge(const char* input, size_t length); // KDF const EVP_MD* getDigestByName(const std::string_view name); +const EVP_CIPHER* getCipherByName(const std::string_view name); // Verify that the specified HKDF output length is valid for the given digest. // The maximum length for HKDF output for a given digest is 255 times the // hash size for the given digest algorithm. bool checkHkdfLength(const EVP_MD* md, size_t length); +bool extractP1363(const Buffer& buf, + unsigned char* dest, + size_t n); + DataPointer hkdf(const EVP_MD* md, const Buffer& key, const Buffer& info, diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 61dd1e97d9672a..dca59f16723ef8 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -20,6 +20,7 @@ using ncrypto::SSLPointer; using v8::Array; using v8::ArrayBuffer; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -244,26 +245,22 @@ void CipherBase::Initialize(Environment* env, Local target) { target, "publicEncrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::encrypt>); SetMethod(context, target, "privateDecrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::decrypt>); SetMethod(context, target, "privateEncrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::sign>); SetMethod(context, target, "publicDecrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::recover>); SetMethodNoSideEffect(context, target, "getCipherInfo", GetCipherInfo); @@ -288,17 +285,13 @@ void CipherBase::RegisterExternalReferences( registry->Register(GetCiphers); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::encrypt>); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::decrypt>); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::sign>); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::recover>); registry->Register(GetCipherInfo); } @@ -773,10 +766,10 @@ CipherBase::UpdateResult CipherBase::Update( return kErrorState; } - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - *out = ArrayBuffer::NewBackingStore(env()->isolate(), buf_len); - } + *out = ArrayBuffer::NewBackingStore( + env()->isolate(), + buf_len, + BackingStoreInitializationMode::kUninitialized); buffer = { .data = reinterpret_cast(data), @@ -853,11 +846,10 @@ bool CipherBase::Final(std::unique_ptr* out) { const int mode = ctx_.getMode(); - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - *out = ArrayBuffer::NewBackingStore( - env()->isolate(), static_cast(ctx_.getBlockSize())); - } + *out = ArrayBuffer::NewBackingStore( + env()->isolate(), + static_cast(ctx_.getBlockSize()), + BackingStoreInitializationMode::kUninitialized); if (kind_ == kDecipher && Cipher::FromCtx(ctx_).isSupportedAuthenticatedMode()) { @@ -939,9 +931,7 @@ void CipherBase::Final(const FunctionCallbackInfo& args) { Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local())); } -template +template bool PublicKeyCipher::Cipher( Environment* env, const EVPKeyPointer& pkey, @@ -950,62 +940,32 @@ bool PublicKeyCipher::Cipher( const ArrayBufferOrViewContents& oaep_label, const ArrayBufferOrViewContents& data, std::unique_ptr* out) { - EVPKeyCtxPointer ctx = pkey.newCtx(); - if (!ctx) - return false; - if (EVP_PKEY_cipher_init(ctx.get()) <= 0) - return false; - if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding) <= 0) - return false; - - if (digest != nullptr) { - if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), digest) <= 0) - return false; - } - - if (!SetRsaOaepLabel(ctx, oaep_label.ToByteSource())) return false; + auto label = oaep_label.ToByteSource(); + auto in = data.ToByteSource(); - size_t out_len = 0; - if (EVP_PKEY_cipher( - ctx.get(), - nullptr, - &out_len, - data.data(), - data.size()) <= 0) { - return false; - } - - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len); - } + const ncrypto::Cipher::CipherParams params{ + .padding = padding, + .digest = digest, + .label = label, + }; - if (EVP_PKEY_cipher( - ctx.get(), - static_cast((*out)->Data()), - &out_len, - data.data(), - data.size()) <= 0) { - return false; - } + auto buf = cipher(pkey, params, in); + if (!buf) return false; - CHECK_LE(out_len, (*out)->ByteLength()); - if (out_len == 0) { + if (buf.size() == 0) { *out = ArrayBuffer::NewBackingStore(env->isolate(), 0); - } else if (out_len != (*out)->ByteLength()) { - std::unique_ptr old_out = std::move(*out); - *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len); + } else { + *out = ArrayBuffer::NewBackingStore(env->isolate(), buf.size()); memcpy(static_cast((*out)->Data()), - static_cast(old_out->Data()), - out_len); + static_cast(buf.get()), + buf.size()); } return true; } template + PublicKeyCipher::Cipher_t cipher> void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { MarkPopErrorOnReturn mark_pop_error_on_return; Environment* env = Environment::GetCurrent(args); @@ -1024,25 +984,16 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { uint32_t padding; if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return; - if (EVP_PKEY_cipher == EVP_PKEY_decrypt && + if (cipher == ncrypto::Cipher::decrypt && operation == PublicKeyCipher::kPrivate && padding == RSA_PKCS1_PADDING) { EVPKeyCtxPointer ctx = pkey.newCtx(); CHECK(ctx); - if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) { + if (!ctx.initForDecrypt()) { return ThrowCryptoError(env, ERR_get_error()); } - int rsa_pkcs1_implicit_rejection = - EVP_PKEY_CTX_ctrl_str(ctx.get(), "rsa_pkcs1_implicit_rejection", "1"); - // From the doc -2 means that the option is not supported. - // The default for the option is enabled and if it has been - // specifically disabled we want to respect that so we will - // not throw an error if the option is supported regardless - // of how it is set. The call to set the value - // will not affect what is used since a different context is - // used in the call if the option is supported - if (rsa_pkcs1_implicit_rejection <= 0) { + if (!ctx.setRsaImplicitRejection()) { return THROW_ERR_INVALID_ARG_VALUE( env, "RSA_PKCS1_PADDING is no longer supported for private decryption"); @@ -1052,7 +1003,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { const EVP_MD* digest = nullptr; if (args[offset + 2]->IsString()) { const Utf8Value oaep_str(env->isolate(), args[offset + 2]); - digest = EVP_get_digestbyname(*oaep_str); + digest = ncrypto::getDigestByName(oaep_str.ToStringView()); if (digest == nullptr) return THROW_ERR_OSSL_EVP_INVALID_DIGEST(env); } @@ -1063,8 +1014,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { return THROW_ERR_OUT_OF_RANGE(env, "oaepLabel is too big"); } std::unique_ptr out; - if (!Cipher( - env, pkey, padding, digest, oaep_label, buf, &out)) { + if (!Cipher(env, pkey, padding, digest, oaep_label, buf, &out)) { return ThrowCryptoError(env, ERR_get_error()); } diff --git a/src/crypto/crypto_cipher.h b/src/crypto/crypto_cipher.h index 57c424e7509fa2..950acfa2521ede 100644 --- a/src/crypto/crypto_cipher.h +++ b/src/crypto/crypto_cipher.h @@ -96,19 +96,17 @@ class CipherBase : public BaseObject { class PublicKeyCipher { public: - typedef int (*EVP_PKEY_cipher_init_t)(EVP_PKEY_CTX* ctx); - typedef int (*EVP_PKEY_cipher_t)(EVP_PKEY_CTX* ctx, - unsigned char* out, size_t* outlen, - const unsigned char* in, size_t inlen); + using Cipher_t = + ncrypto::DataPointer(const ncrypto::EVPKeyPointer&, + const ncrypto::Cipher::CipherParams& params, + const ncrypto::Buffer); enum Operation { kPublic, kPrivate }; - template + template static bool Cipher(Environment* env, const ncrypto::EVPKeyPointer& pkey, int padding, @@ -117,9 +115,7 @@ class PublicKeyCipher { const ArrayBufferOrViewContents& data, std::unique_ptr* out); - template + template static void Cipher(const v8::FunctionCallbackInfo& args); }; diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc index d94f6e1c82c4a6..591509e735b943 100644 --- a/src/crypto/crypto_common.cc +++ b/src/crypto/crypto_common.cc @@ -36,7 +36,7 @@ using ncrypto::StackOfX509; using ncrypto::X509Pointer; using ncrypto::X509View; using v8::ArrayBuffer; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::EscapableHandleScope; using v8::Integer; @@ -307,11 +307,8 @@ MaybeLocal ECPointToBuffer(Environment* env, return MaybeLocal(); } - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), len); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), len, BackingStoreInitializationMode::kUninitialized); len = EC_POINT_point2oct(group, point, diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index c7574e67f03f03..da5cebb87a3d51 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -1451,7 +1451,7 @@ void SecureContext::GetCertificate(const FunctionCallbackInfo& args) { } // UseExtraCaCerts is called only once at the start of the Node.js process. -void UseExtraCaCerts(const std::string& file) { +void UseExtraCaCerts(std::string_view file) { extra_root_certs_file = file; } diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc index 7041eb985d9f6d..0e25a937e175fa 100644 --- a/src/crypto/crypto_dh.cc +++ b/src/crypto/crypto_dh.cc @@ -397,31 +397,23 @@ EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) { auto dh = DHPointer::New(std::move(prime), std::move(bn_g)); if (!dh) return {}; - key_params = EVPKeyPointer::New(); - CHECK(key_params); - CHECK_EQ(EVP_PKEY_assign_DH(key_params.get(), dh.release()), 1); + key_params = EVPKeyPointer::NewDH(std::move(dh)); } else if (int* prime_size = std::get_if(¶ms->params.prime)) { - EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr)); - EVP_PKEY* raw_params = nullptr; - if (!param_ctx || - EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || - EVP_PKEY_CTX_set_dh_paramgen_prime_len( - param_ctx.get(), - *prime_size) <= 0 || - EVP_PKEY_CTX_set_dh_paramgen_generator( - param_ctx.get(), - params->params.generator) <= 0 || - EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) { + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_DH); + if (!param_ctx.initForParamgen() || + !param_ctx.setDhParameters(*prime_size, params->params.generator)) { return {}; } - key_params = EVPKeyPointer(raw_params); + key_params = param_ctx.paramgen(); } else { UNREACHABLE(); } + if (!key_params) return {}; + EVPKeyCtxPointer ctx = key_params.newCtx(); - if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) return {}; + if (!ctx.initForKeygen()) return {}; return ctx; } diff --git a/src/crypto/crypto_dsa.cc b/src/crypto/crypto_dsa.cc index 471fee77531139..cac05aa55f8dd2 100644 --- a/src/crypto/crypto_dsa.cc +++ b/src/crypto/crypto_dsa.cc @@ -12,22 +12,10 @@ #include -// EVP_PKEY_CTX_set_dsa_paramgen_q_bits was added in OpenSSL 1.1.1e. -#if OPENSSL_VERSION_NUMBER < 0x1010105fL -#define EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, qbits) \ - EVP_PKEY_CTX_ctrl((ctx), \ - EVP_PKEY_DSA, \ - EVP_PKEY_OP_PARAMGEN, \ - EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, \ - (qbits), \ - nullptr) -#endif - namespace node { using ncrypto::BignumPointer; using ncrypto::EVPKeyCtxPointer; -using ncrypto::EVPKeyPointer; using v8::FunctionCallbackInfo; using v8::Int32; using v8::JustVoid; @@ -41,33 +29,22 @@ using v8::Value; namespace crypto { EVPKeyCtxPointer DsaKeyGenTraits::Setup(DsaKeyPairGenConfig* params) { - EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, nullptr)); - EVP_PKEY* raw_params = nullptr; - - if (!param_ctx || - EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || - EVP_PKEY_CTX_set_dsa_paramgen_bits( - param_ctx.get(), - params->params.modulus_bits) <= 0) { - return EVPKeyCtxPointer(); - } - - if (params->params.divisor_bits != -1) { - if (EVP_PKEY_CTX_set_dsa_paramgen_q_bits( - param_ctx.get(), params->params.divisor_bits) <= 0) { - return EVPKeyCtxPointer(); - } + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_DSA); + + if (!param_ctx.initForParamgen() || + !param_ctx.setDsaParameters( + params->params.modulus_bits, + params->params.divisor_bits != -1 + ? std::optional(params->params.divisor_bits) + : std::nullopt)) { + return {}; } - if (EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) - return EVPKeyCtxPointer(); + auto key_params = param_ctx.paramgen(); + if (!key_params) return {}; - EVPKeyPointer key_params(raw_params); EVPKeyCtxPointer key_ctx = key_params.newCtx(); - - if (!key_ctx || EVP_PKEY_keygen_init(key_ctx.get()) <= 0) - return EVPKeyCtxPointer(); - + if (!key_ctx.initForKeygen()) return {}; return key_ctx; } diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc index 5ccda6f0768873..98f1e1312769ca 100644 --- a/src/crypto/crypto_ec.cc +++ b/src/crypto/crypto_ec.cc @@ -28,7 +28,7 @@ using ncrypto::EVPKeyPointer; using ncrypto::MarkPopErrorOnReturn; using v8::Array; using v8::ArrayBuffer; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -201,14 +201,10 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { return; } - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - // NOTE: field_size is in bits - int field_size = EC_GROUP_get_degree(ecdh->group_); - size_t out_len = (field_size + 7) / 8; - bs = ArrayBuffer::NewBackingStore(env->isolate(), out_len); - } + int field_size = EC_GROUP_get_degree(ecdh->group_); + size_t out_len = (field_size + 7) / 8; + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), out_len, BackingStoreInitializationMode::kUninitialized); if (!ECDH_compute_key( bs->Data(), bs->ByteLength(), pub, ecdh->key_.get(), nullptr)) @@ -257,12 +253,11 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get ECDH private key"); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), - BignumPointer::GetByteCount(b)); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), + BignumPointer::GetByteCount(b), + BackingStoreInitializationMode::kUninitialized); + CHECK_EQ(bs->ByteLength(), BignumPointer::EncodePaddedInto( b, static_cast(bs->Data()), bs->ByteLength())); @@ -459,24 +454,14 @@ bool ECDHBitsTraits::DeriveBits(Environment* env, case EVP_PKEY_X25519: // Fall through case EVP_PKEY_X448: { - EVPKeyCtxPointer ctx = m_privkey.newCtx(); Mutex::ScopedLock pub_lock(params.public_.mutex()); - if (EVP_PKEY_derive_init(ctx.get()) <= 0 || - EVP_PKEY_derive_set_peer( - ctx.get(), - m_pubkey.get()) <= 0 || - EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) { - return false; - } - - ByteSource::Builder buf(len); - - if (EVP_PKEY_derive(ctx.get(), buf.data(), &len) <= 0) { - return false; - } + EVPKeyCtxPointer ctx = m_privkey.newCtx(); + if (!ctx.initForDerive(m_pubkey)) return false; - *out = std::move(buf).release(len); + auto data = ctx.derive(); + if (!data) return false; + *out = ByteSource::Allocated(data.release()); break; } default: { @@ -523,28 +508,24 @@ EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) { case EVP_PKEY_X25519: // Fall through case EVP_PKEY_X448: - key_ctx.reset(EVP_PKEY_CTX_new_id(params->params.curve_nid, nullptr)); + key_ctx = EVPKeyCtxPointer::NewFromID(params->params.curve_nid); break; default: { - EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr)); - EVP_PKEY* raw_params = nullptr; - if (!param_ctx || - EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || - EVP_PKEY_CTX_set_ec_paramgen_curve_nid( - param_ctx.get(), params->params.curve_nid) <= 0 || - EVP_PKEY_CTX_set_ec_param_enc( - param_ctx.get(), params->params.param_encoding) <= 0 || - EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) { - return EVPKeyCtxPointer(); + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_EC); + if (!param_ctx.initForParamgen() || + !param_ctx.setEcParameters(params->params.curve_nid, + params->params.param_encoding)) { + return {}; } - EVPKeyPointer key_params(raw_params); + + auto key_params = param_ctx.paramgen(); + if (!key_params) return {}; + key_ctx = key_params.newCtx(); } } - if (key_ctx && EVP_PKEY_keygen_init(key_ctx.get()) <= 0) - key_ctx.reset(); - + if (!key_ctx.initForKeygen()) return {}; return key_ctx; } diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index bcd4c533b07ceb..851847483327c1 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -11,6 +11,7 @@ namespace node { +using ncrypto::DataPointer; using ncrypto::EVPMDCtxPointer; using ncrypto::MarkPopErrorOnReturn; using v8::Context; @@ -59,7 +60,7 @@ struct MaybeCachedMD { }; MaybeCachedMD FetchAndMaybeCacheMD(Environment* env, const char* search_name) { - const EVP_MD* implicit_md = EVP_get_digestbyname(search_name); + const EVP_MD* implicit_md = ncrypto::getDigestByName(search_name); if (!implicit_md) return {nullptr, nullptr, -1}; const char* real_name = EVP_MD_get0_name(implicit_md); @@ -202,7 +203,7 @@ const EVP_MD* GetDigestImplementation(Environment* env, return result.explicit_md ? result.explicit_md : result.implicit_md; #else Utf8Value utf8(env->isolate(), algorithm); - return EVP_get_digestbyname(*utf8); + return ncrypto::getDigestByName(utf8.ToStringView()); #endif } @@ -220,7 +221,7 @@ void Hash::OneShotDigest(const FunctionCallbackInfo& args) { CHECK(args[5]->IsUint32() || args[5]->IsUndefined()); // outputEncodingId const EVP_MD* md = GetDigestImplementation(env, args[0], args[1], args[2]); - if (md == nullptr) { + if (md == nullptr) [[unlikely]] { Utf8Value method(isolate, args[0]); std::string message = "Digest method " + method.ToString() + " is not supported"; @@ -229,41 +230,36 @@ void Hash::OneShotDigest(const FunctionCallbackInfo& args) { enum encoding output_enc = ParseEncoding(isolate, args[4], args[5], HEX); - int md_len = EVP_MD_size(md); - unsigned int result_size; - ByteSource::Builder output(md_len); - int success; - // On smaller inputs, EVP_Digest() can be slower than the - // deprecated helpers e.g SHA256_XXX. The speedup may not - // be worth using deprecated APIs, however, so we use - // EVP_Digest(), unless there's a better alternative - // in the future. - // https://github.com/openssl/openssl/issues/19612 - if (args[3]->IsString()) { - Utf8Value utf8(isolate, args[3]); - success = EVP_Digest(utf8.out(), - utf8.length(), - output.data(), - &result_size, - md, - nullptr); - } else { + DataPointer output = ([&] { + if (args[3]->IsString()) { + Utf8Value utf8(isolate, args[3]); + ncrypto::Buffer buf{ + .data = reinterpret_cast(utf8.out()), + .len = utf8.length(), + }; + return ncrypto::hashDigest(buf, md); + } + ArrayBufferViewContents input(args[3]); - success = EVP_Digest(input.data(), - input.length(), - output.data(), - &result_size, - md, - nullptr); - } - if (!success) { + ncrypto::Buffer buf{ + .data = reinterpret_cast(input.data()), + .len = input.length(), + }; + return ncrypto::hashDigest(buf, md); + })(); + + if (!output) [[unlikely]] { return ThrowCryptoError(env, ERR_get_error()); } Local error; - MaybeLocal rc = StringBytes::Encode( - env->isolate(), output.data(), md_len, output_enc, &error); - if (rc.IsEmpty()) { + MaybeLocal rc = + StringBytes::Encode(env->isolate(), + static_cast(output.get()), + output.size(), + output_enc, + &error); + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; @@ -314,7 +310,8 @@ void Hash::New(const FunctionCallbackInfo& args) { const EVP_MD* md = nullptr; if (args[0]->IsObject()) { ASSIGN_OR_RETURN_UNWRAP(&orig, args[0].As()); - md = EVP_MD_CTX_md(orig->mdctx_.get()); + CHECK_NOT_NULL(orig); + md = orig->mdctx_.getDigest(); } else { md = GetDigestImplementation(env, args[0], args[2], args[3]); } @@ -331,25 +328,25 @@ void Hash::New(const FunctionCallbackInfo& args) { "Digest method not supported"); } - if (orig != nullptr && - 0 >= EVP_MD_CTX_copy(hash->mdctx_.get(), orig->mdctx_.get())) { + if (orig != nullptr && !orig->mdctx_.copyTo(hash->mdctx_)) { return ThrowCryptoError(env, ERR_get_error(), "Digest copy error"); } } bool Hash::HashInit(const EVP_MD* md, Maybe xof_md_len) { - mdctx_.reset(EVP_MD_CTX_new()); - if (!mdctx_ || EVP_DigestInit_ex(mdctx_.get(), md, nullptr) <= 0) { + mdctx_ = EVPMDCtxPointer::New(); + if (!mdctx_.digestInit(md)) [[unlikely]] { mdctx_.reset(); return false; } - md_len_ = EVP_MD_size(md); + md_len_ = mdctx_.getDigestSize(); if (xof_md_len.IsJust() && xof_md_len.FromJust() != md_len_) { // This is a little hack to cause createHash to fail when an incorrect // hashSize option was passed for a non-XOF hash function. - if ((EVP_MD_flags(md) & EVP_MD_FLAG_XOF) == 0) { + if (!mdctx_.hasXofFlag()) [[unlikely]] { EVPerr(EVP_F_EVP_DIGESTFINALXOF, EVP_R_NOT_XOF_OR_INVALID_LENGTH); + mdctx_.reset(); return false; } md_len_ = xof_md_len.FromJust(); @@ -359,9 +356,11 @@ bool Hash::HashInit(const EVP_MD* md, Maybe xof_md_len) { } bool Hash::HashUpdate(const char* data, size_t len) { - if (!mdctx_) - return false; - return EVP_DigestUpdate(mdctx_.get(), data, len) == 1; + if (!mdctx_) return false; + return mdctx_.digestUpdate(ncrypto::Buffer{ + .data = data, + .len = len, + }); } void Hash::HashUpdate(const FunctionCallbackInfo& args) { @@ -402,31 +401,18 @@ void Hash::HashDigest(const FunctionCallbackInfo& args) { // and Hash.digest can both be used to retrieve the digest, // so we need to cache it. // See https://github.com/nodejs/node/issues/28245. - - ByteSource::Builder digest(len); - - size_t default_len = EVP_MD_CTX_size(hash->mdctx_.get()); - int ret; - if (len == default_len) { - ret = EVP_DigestFinal_ex( - hash->mdctx_.get(), digest.data(), &len); - // The output length should always equal hash->md_len_ - CHECK_EQ(len, hash->md_len_); - } else { - ret = EVP_DigestFinalXOF( - hash->mdctx_.get(), digest.data(), len); - } - - if (ret != 1) + auto data = hash->mdctx_.digestFinal(len); + if (!data) [[unlikely]] { return ThrowCryptoError(env, ERR_get_error()); + } - hash->digest_ = std::move(digest).release(); + hash->digest_ = ByteSource::Allocated(data.release()); } Local error; MaybeLocal rc = StringBytes::Encode( env->isolate(), hash->digest_.data(), len, encoding, &error); - if (rc.IsEmpty()) { + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; @@ -469,7 +455,7 @@ Maybe HashTraits::AdditionalConfig( CHECK(args[offset]->IsString()); // Hash algorithm Utf8Value digest(env->isolate(), args[offset]); - params->digest = EVP_get_digestbyname(*digest); + params->digest = ncrypto::getDigestByName(digest.ToStringView()); if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); @@ -492,7 +478,7 @@ Maybe HashTraits::AdditionalConfig( static_cast(args[offset + 2] .As()->Value()) / CHAR_BIT; if (params->length != expected) { - if ((EVP_MD_flags(params->digest) & EVP_MD_FLAG_XOF) == 0) { + if ((EVP_MD_flags(params->digest) & EVP_MD_FLAG_XOF) == 0) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Digest method not supported"); return Nothing(); } @@ -506,29 +492,19 @@ bool HashTraits::DeriveBits( Environment* env, const HashConfig& params, ByteSource* out) { - EVPMDCtxPointer ctx(EVP_MD_CTX_new()); + auto ctx = EVPMDCtxPointer::New(); - if (!ctx || EVP_DigestInit_ex(ctx.get(), params.digest, nullptr) <= 0 || - EVP_DigestUpdate(ctx.get(), params.in.data(), params.in.size()) <= - 0) [[unlikely]] { + if (!ctx.digestInit(params.digest) || !ctx.digestUpdate(params.in)) + [[unlikely]] { return false; } if (params.length > 0) [[likely]] { - unsigned int length = params.length; - ByteSource::Builder buf(length); - - size_t expected = EVP_MD_CTX_size(ctx.get()); - - int ret = - (length == expected) - ? EVP_DigestFinal_ex(ctx.get(), buf.data(), &length) - : EVP_DigestFinalXOF(ctx.get(), buf.data(), length); - - if (ret != 1) [[unlikely]] + auto data = ctx.digestFinal(params.length); + if (!data) [[unlikely]] return false; - *out = std::move(buf).release(); + *out = ByteSource::Allocated(data.release()); } return true; @@ -548,7 +524,7 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo& args) { CHECK(args[2]->IsArrayBufferView()); ArrayBufferOrViewContents expected(args[2]); - const EVP_MD* md_type = EVP_get_digestbyname(*algorithm); + const EVP_MD* md_type = ncrypto::getDigestByName(algorithm.ToStringView()); unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_size; if (md_type == nullptr || EVP_Digest(content.data(), @@ -556,7 +532,7 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo& args) { digest, &digest_size, md_type, - nullptr) != 1) { + nullptr) != 1) [[unlikely]] { return ThrowCryptoError( env, ERR_get_error(), "Digest method not supported"); } @@ -570,7 +546,7 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo& args) { digest_size, BASE64, &error); - if (rc.IsEmpty()) { + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; diff --git a/src/crypto/crypto_hkdf.cc b/src/crypto/crypto_hkdf.cc index 2a465c849def44..10bb8e4258bf63 100644 --- a/src/crypto/crypto_hkdf.cc +++ b/src/crypto/crypto_hkdf.cc @@ -55,7 +55,7 @@ Maybe HKDFTraits::AdditionalConfig( Utf8Value hash(env->isolate(), args[offset]); params->digest = ncrypto::getDigestByName(hash.ToStringView()); - if (params->digest == nullptr) { + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *hash); return Nothing(); } @@ -88,7 +88,7 @@ Maybe HKDFTraits::AdditionalConfig( // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as the // output of the hash function. 255 is a hard limit because HKDF appends an // 8-bit counter to each HMAC'd message, starting at 1. - if (!ncrypto::checkHkdfLength(params->digest, params->length)) { + if (!ncrypto::checkHkdfLength(params->digest, params->length)) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_KEYLEN(env); return Nothing(); } diff --git a/src/crypto/crypto_hmac.cc b/src/crypto/crypto_hmac.cc index 25ccb1b9d04e51..56a09e1a2d9b0f 100644 --- a/src/crypto/crypto_hmac.cc +++ b/src/crypto/crypto_hmac.cc @@ -70,15 +70,21 @@ void Hmac::New(const FunctionCallbackInfo& args) { void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) { HandleScope scope(env()->isolate()); - const EVP_MD* md = EVP_get_digestbyname(hash_type); - if (md == nullptr) + const EVP_MD* md = ncrypto::getDigestByName(hash_type); + if (md == nullptr) [[unlikely]] { return THROW_ERR_CRYPTO_INVALID_DIGEST( env(), "Invalid digest: %s", hash_type); + } if (key_len == 0) { key = ""; } - ctx_.reset(HMAC_CTX_new()); - if (!ctx_ || !HMAC_Init_ex(ctx_.get(), key, key_len, md, nullptr)) { + + ctx_ = HMACCtxPointer::New(); + ncrypto::Buffer key_buf{ + .data = key, + .len = static_cast(key_len), + }; + if (!ctx_.init(key_buf, md)) [[unlikely]] { ctx_.reset(); return ThrowCryptoError(env(), ERR_get_error()); } @@ -95,9 +101,11 @@ void Hmac::HmacInit(const FunctionCallbackInfo& args) { } bool Hmac::HmacUpdate(const char* data, size_t len) { - return ctx_ && HMAC_Update(ctx_.get(), - reinterpret_cast(data), - len) == 1; + ncrypto::Buffer buf{ + .data = data, + .len = len, + }; + return ctx_.update(buf); } void Hmac::HmacUpdate(const FunctionCallbackInfo& args) { @@ -123,24 +131,27 @@ void Hmac::HmacDigest(const FunctionCallbackInfo& args) { } unsigned char md_value[EVP_MAX_MD_SIZE]; - unsigned int md_len = 0; + ncrypto::Buffer buf{ + .data = md_value, + .len = sizeof(md_value), + }; if (hmac->ctx_) { - bool ok = HMAC_Final(hmac->ctx_.get(), md_value, &md_len); - hmac->ctx_.reset(); - if (!ok) { + if (!hmac->ctx_.digestInto(&buf)) [[unlikely]] { + hmac->ctx_.reset(); return ThrowCryptoError(env, ERR_get_error(), "Failed to finalize HMAC"); } + hmac->ctx_.reset(); } Local error; MaybeLocal rc = StringBytes::Encode(env->isolate(), reinterpret_cast(md_value), - md_len, + buf.len, encoding, &error); - if (rc.IsEmpty()) { + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; @@ -188,8 +199,8 @@ Maybe HmacTraits::AdditionalConfig( CHECK(args[offset + 2]->IsObject()); // Key Utf8Value digest(env->isolate(), args[offset + 1]); - params->digest = EVP_get_digestbyname(*digest); - if (params->digest == nullptr) { + params->digest = ncrypto::getDigestByName(digest.ToStringView()); + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } @@ -225,31 +236,29 @@ bool HmacTraits::DeriveBits( Environment* env, const HmacConfig& params, ByteSource* out) { - HMACCtxPointer ctx(HMAC_CTX_new()); + auto ctx = HMACCtxPointer::New(); - if (!ctx || !HMAC_Init_ex(ctx.get(), - params.key.GetSymmetricKey(), - params.key.GetSymmetricKeySize(), - params.digest, - nullptr)) { + ncrypto::Buffer key_buf{ + .data = params.key.GetSymmetricKey(), + .len = params.key.GetSymmetricKeySize(), + }; + if (!ctx.init(key_buf, params.digest)) [[unlikely]] { return false; } - if (!HMAC_Update( - ctx.get(), - params.data.data(), - params.data.size())) { + ncrypto::Buffer buffer{ + .data = params.data.data(), + .len = params.data.size(), + }; + if (!ctx.update(buffer)) [[unlikely]] { return false; } - ByteSource::Builder buf(EVP_MAX_MD_SIZE); - unsigned int len; - - if (!HMAC_Final(ctx.get(), buf.data(), &len)) { + auto buf = ctx.digest(); + if (!buf) [[unlikely]] return false; - } - *out = std::move(buf).release(len); + *out = ByteSource::Allocated(buf.release()); return true; } @@ -258,9 +267,9 @@ MaybeLocal HmacTraits::EncodeOutput(Environment* env, const HmacConfig& params, ByteSource* out) { switch (params.mode) { - case SignConfiguration::kSign: + case SignConfiguration::Mode::Sign: return out->ToArrayBuffer(env); - case SignConfiguration::kVerify: + case SignConfiguration::Mode::Verify: return Boolean::New( env->isolate(), out->size() > 0 && out->size() == params.signature.size() && diff --git a/src/crypto/crypto_keygen.cc b/src/crypto/crypto_keygen.cc index 246191f5d51796..7c3a85e9f8a24d 100644 --- a/src/crypto/crypto_keygen.cc +++ b/src/crypto/crypto_keygen.cc @@ -47,10 +47,8 @@ Maybe NidKeyPairGenTraits::AdditionalConfig( } EVPKeyCtxPointer NidKeyPairGenTraits::Setup(NidKeyPairGenConfig* params) { - EVPKeyCtxPointer ctx = - EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(params->params.id, nullptr)); - if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) return {}; - + auto ctx = EVPKeyCtxPointer::NewFromID(params->params.id); + if (!ctx || !ctx.initForKeygen()) return {}; return ctx; } diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index bedcf04d036478..2c55828facc35b 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -349,7 +349,7 @@ KeyObjectData::GetPrivateKeyEncodingFromJs( if (context != kKeyContextInput) { if (args[*offset]->IsString()) { Utf8Value cipher_name(env->isolate(), args[*offset]); - config.cipher = EVP_get_cipherbyname(*cipher_name); + config.cipher = ncrypto::getCipherByName(cipher_name.ToStringView()); if (config.cipher == nullptr) { THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env); return Nothing(); @@ -597,7 +597,7 @@ bool KeyObjectHandle::HasInstance(Environment* env, Local value) { return !t.IsEmpty() && t->HasInstance(value); } -v8::Local KeyObjectHandle::Initialize(Environment* env) { +Local KeyObjectHandle::Initialize(Environment* env) { Local templ = env->crypto_key_object_handle_constructor(); if (templ.IsEmpty()) { Isolate* isolate = env->isolate(); @@ -958,14 +958,10 @@ bool KeyObjectHandle::CheckEcKeyData() const { CHECK_EQ(key.id(), EVP_PKEY_EC); if (data_.GetKeyType() == kKeyTypePrivate) { - return EVP_PKEY_check(ctx.get()) == 1; + return ctx.privateCheck(); } -#if OPENSSL_VERSION_MAJOR >= 3 - return EVP_PKEY_public_check_quick(ctx.get()) == 1; -#else - return EVP_PKEY_public_check(ctx.get()) == 1; -#endif + return ctx.publicCheck(); } void KeyObjectHandle::CheckEcKeyData(const FunctionCallbackInfo& args) { @@ -1202,6 +1198,9 @@ void Initialize(Environment* env, Local target) { constexpr int kKeyFormatJWK = static_cast(EVPKeyPointer::PKFormatType::JWK); + constexpr auto kSigEncDER = DSASigEnc::DER; + constexpr auto kSigEncP1363 = DSASigEnc::P1363; + NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatRaw); NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatPKCS8); NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatSPKI); diff --git a/src/crypto/crypto_pbkdf2.cc b/src/crypto/crypto_pbkdf2.cc index dcaa430aacd3d7..1a0dff8238d938 100644 --- a/src/crypto/crypto_pbkdf2.cc +++ b/src/crypto/crypto_pbkdf2.cc @@ -88,20 +88,20 @@ Maybe PBKDF2Traits::AdditionalConfig( CHECK(args[offset + 4]->IsString()); // digest_name params->iterations = args[offset + 2].As()->Value(); - if (params->iterations < 0) { + if (params->iterations < 0) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "iterations must be <= %d", INT_MAX); return Nothing(); } params->length = args[offset + 3].As()->Value(); - if (params->length < 0) { + if (params->length < 0) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "length must be <= %d", INT_MAX); return Nothing(); } Utf8Value name(args.GetIsolate(), args[offset + 4]); params->digest = ncrypto::getDigestByName(name.ToStringView()); - if (params->digest == nullptr) { + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *name); return Nothing(); } diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc index cb96698aa644c3..4e9ff906c06571 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -14,7 +14,6 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::ClearErrorOnReturn; using v8::ArrayBuffer; -using v8::BackingStore; using v8::Boolean; using v8::FunctionCallbackInfo; using v8::Int32; @@ -25,6 +24,7 @@ using v8::MaybeLocal; using v8::Nothing; using v8::Object; using v8::Uint32; +using v8::Undefined; using v8::Value; namespace crypto { @@ -39,7 +39,7 @@ BignumPointer::PrimeCheckCallback getPrimeCheckCallback(Environment* env) { } // namespace MaybeLocal RandomBytesTraits::EncodeOutput( Environment* env, const RandomBytesConfig& params, ByteSource* unused) { - return v8::Undefined(env->isolate()); + return Undefined(env->isolate()); } Maybe RandomBytesTraits::AdditionalConfig( @@ -78,14 +78,13 @@ void RandomPrimeConfig::MemoryInfo(MemoryTracker* tracker) const { MaybeLocal RandomPrimeTraits::EncodeOutput( Environment* env, const RandomPrimeConfig& params, ByteSource* unused) { size_t size = params.prime.byteLength(); - std::shared_ptr store = - ArrayBuffer::NewBackingStore(env->isolate(), size); + auto store = ArrayBuffer::NewBackingStore(env->isolate(), size); CHECK_EQ(size, BignumPointer::EncodePaddedInto( params.prime.get(), reinterpret_cast(store->Data()), size)); - return ArrayBuffer::New(env->isolate(), store); + return ArrayBuffer::New(env->isolate(), std::move(store)); } Maybe RandomPrimeTraits::AdditionalConfig( @@ -104,7 +103,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( if (!args[offset + 2]->IsUndefined()) { ArrayBufferOrViewContents add(args[offset + 2]); params->add.reset(add.data(), add.size()); - if (!params->add) { + if (!params->add) [[unlikely]] { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime"); return Nothing(); } @@ -113,7 +112,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( if (!args[offset + 3]->IsUndefined()) { ArrayBufferOrViewContents rem(args[offset + 3]); params->rem.reset(rem.data(), rem.size()); - if (!params->rem) { + if (!params->rem) [[unlikely]] { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime"); return Nothing(); } @@ -124,7 +123,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( CHECK_GT(bits, 0); if (params->add) { - if (BignumPointer::GetBitCount(params->add.get()) > bits) { + if (BignumPointer::GetBitCount(params->add.get()) > bits) [[unlikely]] { // If we allowed this, the best case would be returning a static prime // that wasn't generated randomly. The worst case would be an infinite // loop within OpenSSL, blocking the main thread or one of the threads @@ -133,7 +132,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( return Nothing(); } - if (params->rem && params->add <= params->rem) { + if (params->rem && params->add <= params->rem) [[unlikely]] { // This would definitely lead to an infinite loop if allowed since // OpenSSL does not check this condition. THROW_ERR_OUT_OF_RANGE(env, "invalid options.rem"); @@ -144,7 +143,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( params->bits = bits; params->safe = safe; params->prime = BignumPointer::NewSecure(); - if (!params->prime) { + if (!params->prime) [[unlikely]] { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime"); return Nothing(); } @@ -195,7 +194,8 @@ bool CheckPrimeTraits::DeriveBits( const CheckPrimeConfig& params, ByteSource* out) { int ret = params.candidate.isPrime(params.checks, getPrimeCheckCallback(env)); - if (ret < 0) return false; + if (ret < 0) [[unlikely]] + return false; ByteSource::Builder buf(1); buf.data()[0] = ret; *out = std::move(buf).release(); diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc index 05a3882c7e17d7..f0e0f9fd5f94a1 100644 --- a/src/crypto/crypto_rsa.cc +++ b/src/crypto/crypto_rsa.cc @@ -15,13 +15,15 @@ namespace node { using ncrypto::BignumPointer; +using ncrypto::DataPointer; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::RSAPointer; using v8::ArrayBuffer; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::FunctionCallbackInfo; using v8::Int32; +using v8::Integer; using v8::JustVoid; using v8::Local; using v8::Maybe; @@ -34,37 +36,28 @@ using v8::Value; namespace crypto { EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { - EVPKeyCtxPointer ctx( - EVP_PKEY_CTX_new_id( - params->params.variant == kKeyVariantRSA_PSS - ? EVP_PKEY_RSA_PSS - : EVP_PKEY_RSA, - nullptr)); - - if (EVP_PKEY_keygen_init(ctx.get()) <= 0) - return EVPKeyCtxPointer(); - - if (EVP_PKEY_CTX_set_rsa_keygen_bits( - ctx.get(), - params->params.modulus_bits) <= 0) { - return EVPKeyCtxPointer(); + auto ctx = EVPKeyCtxPointer::NewFromID( + params->params.variant == kKeyVariantRSA_PSS ? EVP_PKEY_RSA_PSS + : EVP_PKEY_RSA); + + if (!ctx.initForKeygen() || + !ctx.setRsaKeygenBits(params->params.modulus_bits)) { + return {}; } // 0x10001 is the default RSA exponent. - if (params->params.exponent != 0x10001) { + if (params->params.exponent != EVPKeyCtxPointer::kDefaultRsaExponent) { auto bn = BignumPointer::New(); - CHECK(bn.setWord(params->params.exponent)); - // EVP_CTX accepts ownership of bn on success. - if (EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx.get(), bn.get()) <= 0) - return EVPKeyCtxPointer(); - - bn.release(); + if (!bn.setWord(params->params.exponent) || + !ctx.setRsaKeygenPubExp(std::move(bn))) { + return {}; + } } if (params->params.variant == kKeyVariantRSA_PSS) { if (params->params.md != nullptr && - EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx.get(), params->params.md) <= 0) { - return EVPKeyCtxPointer(); + !ctx.setRsaPssKeygenMd(params->params.md)) { + return {}; } // TODO(tniessen): This appears to only be necessary in OpenSSL 3, while @@ -76,11 +69,8 @@ EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { mgf1_md = params->params.md; } - if (mgf1_md != nullptr && - EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md( - ctx.get(), - mgf1_md) <= 0) { - return EVPKeyCtxPointer(); + if (mgf1_md != nullptr && !ctx.setRsaPssKeygenMgf1Md(mgf1_md)) { + return {}; } int saltlen = params->params.saltlen; @@ -88,11 +78,8 @@ EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { saltlen = EVP_MD_size(params->params.md); } - if (saltlen >= 0 && - EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen( - ctx.get(), - saltlen) <= 0) { - return EVPKeyCtxPointer(); + if (saltlen >= 0 && !ctx.setRsaPssSaltlen(saltlen)) { + return {}; } } @@ -154,7 +141,7 @@ Maybe RsaKeyGenTraits::AdditionalConfig( if (!args[*offset]->IsUndefined()) { CHECK(args[*offset]->IsString()); Utf8Value digest(env->isolate(), args[*offset]); - params->params.md = EVP_get_digestbyname(*digest); + params->params.md = ncrypto::getDigestByName(digest.ToStringView()); if (params->params.md == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); @@ -164,7 +151,7 @@ Maybe RsaKeyGenTraits::AdditionalConfig( if (!args[*offset + 1]->IsUndefined()) { CHECK(args[*offset + 1]->IsString()); Utf8Value digest(env->isolate(), args[*offset + 1]); - params->params.mgf1_md = EVP_get_digestbyname(*digest); + params->params.mgf1_md = ncrypto::getDigestByName(digest.ToStringView()); if (params->params.mgf1_md == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST( env, "Invalid MGF1 digest: %s", *digest); @@ -196,8 +183,11 @@ WebCryptoKeyExportStatus RSA_JWK_Export(const KeyObjectData& key_data, return WebCryptoKeyExportStatus::FAILED; } -template +using Cipher_t = DataPointer(const EVPKeyPointer& key, + const ncrypto::Rsa::CipherParams& params, + const ncrypto::Buffer in); + +template WebCryptoCipherStatus RSA_Cipher(Environment* env, const KeyObjectData& key_data, const RSACipherConfig& params, @@ -206,45 +196,16 @@ WebCryptoCipherStatus RSA_Cipher(Environment* env, CHECK_NE(key_data.GetKeyType(), kKeyTypeSecret); Mutex::ScopedLock lock(key_data.mutex()); const auto& m_pkey = key_data.GetAsymmetricKey(); + const ncrypto::Rsa::CipherParams nparams{ + .padding = params.padding, + .digest = params.digest, + .label = params.label, + }; - EVPKeyCtxPointer ctx = m_pkey.newCtx(); - - if (!ctx || init(ctx.get()) <= 0) - return WebCryptoCipherStatus::FAILED; - - if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), params.padding) <= 0) { - return WebCryptoCipherStatus::FAILED; - } - - if (params.digest != nullptr && - (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), params.digest) <= 0 || - EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), params.digest) <= 0)) { - return WebCryptoCipherStatus::FAILED; - } - - if (!SetRsaOaepLabel(ctx, params.label)) return WebCryptoCipherStatus::FAILED; - - size_t out_len = 0; - if (cipher( - ctx.get(), - nullptr, - &out_len, - in.data(), - in.size()) <= 0) { - return WebCryptoCipherStatus::FAILED; - } - - ByteSource::Builder buf(out_len); - - if (cipher(ctx.get(), - buf.data(), - &out_len, - in.data(), - in.size()) <= 0) { - return WebCryptoCipherStatus::FAILED; - } + auto data = cipher(m_pkey, nparams, in); + if (!data) return WebCryptoCipherStatus::FAILED; - *out = std::move(buf).release(out_len); + *out = ByteSource::Allocated(data.release()); return WebCryptoCipherStatus::OK; } } // namespace @@ -316,7 +277,7 @@ Maybe RSACipherTraits::AdditionalConfig( CHECK(args[offset + 1]->IsString()); // digest Utf8Value digest(env->isolate(), args[offset + 1]); - params->digest = EVP_get_digestbyname(*digest); + params->digest = ncrypto::getDigestByName(digest.ToStringView()); if (params->digest == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); @@ -349,12 +310,10 @@ WebCryptoCipherStatus RSACipherTraits::DoCipher(Environment* env, switch (cipher_mode) { case kWebCryptoCipherEncrypt: CHECK_EQ(key_data.GetKeyType(), kKeyTypePublic); - return RSA_Cipher( - env, key_data, params, in, out); + return RSA_Cipher(env, key_data, params, in, out); case kWebCryptoCipherDecrypt: CHECK_EQ(key_data.GetKeyType(), kKeyTypePrivate); - return RSA_Cipher( - env, key_data, params, in, out); + return RSA_Cipher(env, key_data, params, in, out); } return WebCryptoCipherStatus::FAILED; } @@ -364,50 +323,37 @@ Maybe ExportJWKRsaKey(Environment* env, Local target) { Mutex::ScopedLock lock(key.mutex()); const auto& m_pkey = key.GetAsymmetricKey(); - int type = m_pkey.id(); - CHECK(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA_PSS); - // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL - // versions older than 1.1.1e via FIPS / dynamic linking. - const RSA* rsa; - if (OpenSSL_version_num() >= 0x1010105fL) { - rsa = EVP_PKEY_get0_RSA(m_pkey.get()); - } else { - rsa = static_cast(EVP_PKEY_get0(m_pkey.get())); - } - CHECK_NOT_NULL(rsa); - - const BIGNUM* n; - const BIGNUM* e; - const BIGNUM* d; - const BIGNUM* p; - const BIGNUM* q; - const BIGNUM* dp; - const BIGNUM* dq; - const BIGNUM* qi; - RSA_get0_key(rsa, &n, &e, &d); - - if (target->Set( - env->context(), - env->jwk_kty_string(), - env->jwk_rsa_string()).IsNothing()) { + const ncrypto::Rsa rsa = m_pkey; + if (!rsa || + target->Set(env->context(), env->jwk_kty_string(), env->jwk_rsa_string()) + .IsNothing()) { return Nothing(); } - if (SetEncodedValue(env, target, env->jwk_n_string(), n).IsNothing() || - SetEncodedValue(env, target, env->jwk_e_string(), e).IsNothing()) { + auto pub_key = rsa.getPublicKey(); + + if (SetEncodedValue(env, target, env->jwk_n_string(), pub_key.n) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_e_string(), pub_key.e) + .IsNothing()) { return Nothing(); } if (key.GetKeyType() == kKeyTypePrivate) { - RSA_get0_factors(rsa, &p, &q); - RSA_get0_crt_params(rsa, &dp, &dq, &qi); - if (SetEncodedValue(env, target, env->jwk_d_string(), d).IsNothing() || - SetEncodedValue(env, target, env->jwk_p_string(), p).IsNothing() || - SetEncodedValue(env, target, env->jwk_q_string(), q).IsNothing() || - SetEncodedValue(env, target, env->jwk_dp_string(), dp).IsNothing() || - SetEncodedValue(env, target, env->jwk_dq_string(), dq).IsNothing() || - SetEncodedValue(env, target, env->jwk_qi_string(), qi).IsNothing()) { + auto pvt_key = rsa.getPrivateKey(); + if (SetEncodedValue(env, target, env->jwk_d_string(), pub_key.d) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_p_string(), pvt_key.p) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_q_string(), pvt_key.q) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_dp_string(), pvt_key.dp) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_dq_string(), pvt_key.dq) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_qi_string(), pvt_key.qi) + .IsNothing()) { return Nothing(); } } @@ -440,15 +386,12 @@ KeyObjectData ImportJWKRsaKey(Environment* env, KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic; RSAPointer rsa(RSA_new()); + ncrypto::Rsa rsa_view(rsa.get()); ByteSource n = ByteSource::FromEncodedString(env, n_value.As()); ByteSource e = ByteSource::FromEncodedString(env, e_value.As()); - if (!RSA_set0_key( - rsa.get(), - n.ToBN().release(), - e.ToBN().release(), - nullptr)) { + if (!rsa_view.setPublicKey(n.ToBN(), e.ToBN())) { THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key"); return {}; } @@ -485,20 +428,15 @@ KeyObjectData ImportJWKRsaKey(Environment* env, ByteSource dq = ByteSource::FromEncodedString(env, dq_value.As()); ByteSource qi = ByteSource::FromEncodedString(env, qi_value.As()); - if (!RSA_set0_key(rsa.get(), nullptr, nullptr, d.ToBN().release()) || - !RSA_set0_factors(rsa.get(), p.ToBN().release(), q.ToBN().release()) || - !RSA_set0_crt_params( - rsa.get(), - dp.ToBN().release(), - dq.ToBN().release(), - qi.ToBN().release())) { + if (!rsa_view.setPrivateKey( + d.ToBN(), q.ToBN(), p.ToBN(), dp.ToBN(), dq.ToBN(), qi.ToBN())) { THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key"); return {}; } } - auto pkey = EVPKeyPointer::New(); - CHECK_EQ(EVP_PKEY_set1_RSA(pkey.get(), rsa.get()), 1); + auto pkey = EVPKeyPointer::NewRSA(std::move(rsa)); + if (!pkey) return {}; return KeyObjectData::CreateAsymmetric(type, std::move(pkey)); } @@ -506,43 +444,32 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Maybe GetRsaKeyDetail(Environment* env, const KeyObjectData& key, Local target) { - const BIGNUM* e; // Public Exponent - const BIGNUM* n; // Modulus - Mutex::ScopedLock lock(key.mutex()); const auto& m_pkey = key.GetAsymmetricKey(); - int type = m_pkey.id(); - CHECK(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA_PSS); // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL // versions older than 1.1.1e via FIPS / dynamic linking. - const RSA* rsa; - if (OpenSSL_version_num() >= 0x1010105fL) { - rsa = EVP_PKEY_get0_RSA(m_pkey.get()); - } else { - rsa = static_cast(EVP_PKEY_get0(m_pkey.get())); - } - CHECK_NOT_NULL(rsa); + const ncrypto::Rsa rsa = m_pkey; + if (!rsa) return Nothing(); - RSA_get0_key(rsa, &n, &e, nullptr); + auto pub_key = rsa.getPublicKey(); if (target ->Set(env->context(), env->modulus_length_string(), - Number::New(env->isolate(), - static_cast(BignumPointer::GetBitCount(n)))) + Number::New( + env->isolate(), + static_cast(BignumPointer::GetBitCount(pub_key.n)))) .IsNothing()) { return Nothing(); } - std::unique_ptr public_exponent; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - public_exponent = ArrayBuffer::NewBackingStore( - env->isolate(), BignumPointer::GetByteCount(e)); - } + auto public_exponent = ArrayBuffer::NewBackingStore( + env->isolate(), + BignumPointer::GetByteCount(pub_key.e), + BackingStoreInitializationMode::kUninitialized); CHECK_EQ(BignumPointer::EncodePaddedInto( - e, + pub_key.e, static_cast(public_exponent->Data()), public_exponent->ByteLength()), public_exponent->ByteLength()); @@ -555,7 +482,7 @@ Maybe GetRsaKeyDetail(Environment* env, return Nothing(); } - if (type == EVP_PKEY_RSA_PSS) { + if (m_pkey.id() == EVP_PKEY_RSA_PSS) { // Due to the way ASN.1 encoding works, default values are omitted when // encoding the data structure. However, there are also RSA-PSS keys for // which no parameters are set. In that case, the ASN.1 RSASSA-PSS-params @@ -565,64 +492,34 @@ Maybe GetRsaKeyDetail(Environment* env, // In that case, RSA_get0_pss_params does not return nullptr but all fields // of the returned RSA_PSS_PARAMS will be set to nullptr. - const RSA_PSS_PARAMS* params = RSA_get0_pss_params(rsa); - if (params != nullptr) { - int hash_nid = NID_sha1; - int mgf_nid = NID_mgf1; - int mgf1_hash_nid = NID_sha1; - int64_t salt_length = 20; - - if (params->hashAlgorithm != nullptr) { - const ASN1_OBJECT* hash_obj; - X509_ALGOR_get0(&hash_obj, nullptr, nullptr, params->hashAlgorithm); - hash_nid = OBJ_obj2nid(hash_obj); - } - + auto maybe_params = rsa.getPssParams(); + if (maybe_params.has_value()) { + auto& params = maybe_params.value(); if (target - ->Set( - env->context(), - env->hash_algorithm_string(), - OneByteString(env->isolate(), OBJ_nid2ln(hash_nid))) + ->Set(env->context(), + env->hash_algorithm_string(), + OneByteString(env->isolate(), params.digest)) .IsNothing()) { return Nothing(); } - if (params->maskGenAlgorithm != nullptr) { - const ASN1_OBJECT* mgf_obj; - X509_ALGOR_get0(&mgf_obj, nullptr, nullptr, params->maskGenAlgorithm); - mgf_nid = OBJ_obj2nid(mgf_obj); - if (mgf_nid == NID_mgf1) { - const ASN1_OBJECT* mgf1_hash_obj; - X509_ALGOR_get0(&mgf1_hash_obj, nullptr, nullptr, params->maskHash); - mgf1_hash_nid = OBJ_obj2nid(mgf1_hash_obj); - } - } - // If, for some reason, the MGF is not MGF1, then the MGF1 hash function // is intentionally not added to the object. - if (mgf_nid == NID_mgf1) { + if (params.mgf1_digest.has_value()) { + auto digest = params.mgf1_digest.value(); if (target - ->Set( - env->context(), - env->mgf1_hash_algorithm_string(), - OneByteString(env->isolate(), OBJ_nid2ln(mgf1_hash_nid))) + ->Set(env->context(), + env->mgf1_hash_algorithm_string(), + OneByteString(env->isolate(), digest)) .IsNothing()) { return Nothing(); } } - if (params->saltLength != nullptr) { - if (ASN1_INTEGER_get_int64(&salt_length, params->saltLength) != 1) { - ThrowCryptoError(env, ERR_get_error(), "ASN1_INTEGER_get_in64 error"); - return Nothing(); - } - } - if (target - ->Set( - env->context(), - env->salt_length_string(), - Number::New(env->isolate(), static_cast(salt_length))) + ->Set(env->context(), + env->salt_length_string(), + Integer::New(env->isolate(), params.salt_length)) .IsNothing()) { return Nothing(); } diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc index abb8a804c1b508..175a8e92ef437f 100644 --- a/src/crypto/crypto_sig.cc +++ b/src/crypto/crypto_sig.cc @@ -14,20 +14,20 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::ClearErrorOnReturn; +using ncrypto::DataPointer; using ncrypto::ECDSASigPointer; -using ncrypto::ECKeyPointer; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::EVPMDCtxPointer; using v8::ArrayBuffer; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::Int32; using v8::Isolate; -using v8::Just; using v8::JustVoid; using v8::Local; using v8::Maybe; @@ -39,44 +39,40 @@ using v8::Value; namespace crypto { namespace { -bool ValidateDSAParameters(EVP_PKEY* key) { - /* Validate DSA2 parameters from FIPS 186-4 */ - auto id = EVPKeyPointer::base_id(key); -#if OPENSSL_VERSION_MAJOR >= 3 - if (EVP_default_properties_is_fips_enabled(nullptr) && EVP_PKEY_DSA == id) { -#else - if (FIPS_mode() && EVP_PKEY_DSA == id) { -#endif - const DSA* dsa = EVP_PKEY_get0_DSA(key); - const BIGNUM* p; - const BIGNUM* q; - DSA_get0_pqg(dsa, &p, &q, nullptr); - int L = BignumPointer::GetBitCount(p); - int N = BignumPointer::GetBitCount(q); - - return (L == 1024 && N == 160) || - (L == 2048 && N == 224) || - (L == 2048 && N == 256) || - (L == 3072 && N == 256); +int GetPaddingFromJS(const EVPKeyPointer& key, Local val) { + int padding = key.getDefaultSignPadding(); + if (!val->IsUndefined()) [[likely]] { + CHECK(val->IsInt32()); + padding = val.As()->Value(); } + return padding; +} - return true; +std::optional GetSaltLenFromJS(Local val) { + std::optional salt_len; + if (!val->IsUndefined()) [[likely]] { + CHECK(val->IsInt32()); + salt_len = val.As()->Value(); + } + return salt_len; +} + +DSASigEnc GetDSASigEncFromJS(Local val) { + CHECK(val->IsInt32()); + int i = val.As()->Value(); + if (i < 0 || i >= static_cast(DSASigEnc::Invalid)) [[unlikely]] { + return DSASigEnc::Invalid; + } + return static_cast(val.As()->Value()); } bool ApplyRSAOptions(const EVPKeyPointer& pkey, EVP_PKEY_CTX* pkctx, int padding, - const Maybe& salt_len) { - int id = pkey.id(); - if (id == EVP_PKEY_RSA || id == EVP_PKEY_RSA2 || id == EVP_PKEY_RSA_PSS) { - if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) - return false; - if (padding == RSA_PKCS1_PSS_PADDING && salt_len.IsJust()) { - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, salt_len.FromJust()) <= 0) - return false; - } + std::optional salt_len) { + if (pkey.isRsaVariant()) { + return EVPKeyCtxPointer::setRsaPadding(pkctx, padding, salt_len); } - return true; } @@ -84,38 +80,31 @@ std::unique_ptr Node_SignFinal(Environment* env, EVPMDCtxPointer&& mdctx, const EVPKeyPointer& pkey, int padding, - Maybe pss_salt_len) { - unsigned char m[EVP_MAX_MD_SIZE]; - unsigned int m_len; - - if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_len)) + std::optional pss_salt_len) { + auto data = mdctx.digestFinal(mdctx.getExpectedSize()); + if (!data) [[unlikely]] return nullptr; - size_t sig_len = pkey.size(); - std::unique_ptr sig; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_len); - } + auto sig = ArrayBuffer::NewBackingStore(env->isolate(), pkey.size()); + ncrypto::Buffer sig_buf{ + .data = static_cast(sig->Data()), + .len = pkey.size(), + }; + EVPKeyCtxPointer pkctx = pkey.newCtx(); - if (pkctx && EVP_PKEY_sign_init(pkctx.get()) > 0 && + if (pkctx.initForSign() > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, pss_salt_len) && - EVP_PKEY_CTX_set_signature_md(pkctx.get(), EVP_MD_CTX_md(mdctx.get())) > - 0 && - EVP_PKEY_sign(pkctx.get(), - static_cast(sig->Data()), - &sig_len, - m, - m_len) > 0) { - CHECK_LE(sig_len, sig->ByteLength()); - if (sig_len == 0) { - sig = ArrayBuffer::NewBackingStore(env->isolate(), 0); - } else if (sig_len != sig->ByteLength()) { - std::unique_ptr old_sig = std::move(sig); - sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_len); - memcpy(static_cast(sig->Data()), - static_cast(old_sig->Data()), - sig_len); + pkctx.setSignatureMd(mdctx) && pkctx.signInto(data, &sig_buf)) + [[likely]] { + CHECK_LE(sig_buf.len, sig->ByteLength()); + if (sig_buf.len < sig->ByteLength()) { + auto new_sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_buf.len); + if (sig_buf.len > 0) [[likely]] { + memcpy(static_cast(new_sig->Data()), + static_cast(sig->Data()), + sig_buf.len); + } + sig = std::move(new_sig); } return sig; } @@ -123,62 +112,26 @@ std::unique_ptr Node_SignFinal(Environment* env, return nullptr; } -int GetDefaultSignPadding(const EVPKeyPointer& m_pkey) { - return m_pkey.id() == EVP_PKEY_RSA_PSS ? RSA_PKCS1_PSS_PADDING - : RSA_PKCS1_PADDING; -} - -unsigned int GetBytesOfRS(const EVPKeyPointer& pkey) { - int bits, base_id = pkey.base_id(); - - if (base_id == EVP_PKEY_DSA) { - const DSA* dsa_key = EVP_PKEY_get0_DSA(pkey.get()); - // Both r and s are computed mod q, so their width is limited by that of q. - bits = BignumPointer::GetBitCount(DSA_get0_q(dsa_key)); - } else if (base_id == EVP_PKEY_EC) { - bits = EC_GROUP_order_bits(ECKeyPointer::GetGroup(pkey)); - } else { - return kNoDsaSignature; - } - - return (bits + 7) / 8; -} - -bool ExtractP1363( - const unsigned char* sig_data, - unsigned char* out, - size_t len, - size_t n) { - ncrypto::Buffer sig_buffer{ - .data = sig_data, - .len = len, - }; - auto asn1_sig = ECDSASigPointer::Parse(sig_buffer); - if (!asn1_sig) - return false; - - return BignumPointer::EncodePaddedInto(asn1_sig.r(), out, n) > 0 && - BignumPointer::EncodePaddedInto(asn1_sig.s(), out + n, n) > 0; -} - // Returns the maximum size of each of the integers (r, s) of the DSA signature. std::unique_ptr ConvertSignatureToP1363( Environment* env, const EVPKeyPointer& pkey, std::unique_ptr&& signature) { - unsigned int n = GetBytesOfRS(pkey); - if (n == kNoDsaSignature) - return std::move(signature); + uint32_t n = pkey.getBytesOfRS().value_or(kNoDsaSignature); + if (n == kNoDsaSignature) return std::move(signature); - std::unique_ptr buf; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - buf = ArrayBuffer::NewBackingStore(env->isolate(), 2 * n); - } - if (!ExtractP1363(static_cast(signature->Data()), - static_cast(buf->Data()), - signature->ByteLength(), n)) + auto buf = ArrayBuffer::NewBackingStore( + env->isolate(), 2 * n, BackingStoreInitializationMode::kUninitialized); + + ncrypto::Buffer sig_buffer{ + .data = static_cast(signature->Data()), + .len = signature->ByteLength(), + }; + + if (!ncrypto::extractP1363( + sig_buffer, static_cast(buf->Data()), n)) { return std::move(signature); + } return buf; } @@ -187,30 +140,33 @@ std::unique_ptr ConvertSignatureToP1363( ByteSource ConvertSignatureToP1363(Environment* env, const EVPKeyPointer& pkey, const ByteSource& signature) { - unsigned int n = GetBytesOfRS(pkey); - if (n == kNoDsaSignature) - return ByteSource(); + unsigned int n = pkey.getBytesOfRS().value_or(kNoDsaSignature); + if (n == kNoDsaSignature) [[unlikely]] + return {}; - const unsigned char* sig_data = signature.data(); + auto data = DataPointer::Alloc(n * 2); + if (!data) [[unlikely]] + return {}; + unsigned char* out = static_cast(data.get()); - ByteSource::Builder out(n * 2); - memset(out.data(), 0, n * 2); + // Extracting the signature may not actually use all of the allocated space. + // We need to ensure that the buffer is zeroed out before use. + data.zero(); - if (!ExtractP1363(sig_data, out.data(), signature.size(), n)) - return ByteSource(); + if (!ncrypto::extractP1363(signature, out, n)) [[unlikely]] { + return {}; + } - return std::move(out).release(); + return ByteSource::Allocated(data.release()); } ByteSource ConvertSignatureToDER(const EVPKeyPointer& pkey, ByteSource&& out) { - unsigned int n = GetBytesOfRS(pkey); - if (n == kNoDsaSignature) - return std::move(out); + unsigned int n = pkey.getBytesOfRS().value_or(kNoDsaSignature); + if (n == kNoDsaSignature) return std::move(out); const unsigned char* sig_data = out.data(); - if (out.size() != 2 * n) - return ByteSource(); + if (out.size() != 2 * n) return {}; auto asn1_sig = ECDSASigPointer::New(); CHECK(asn1_sig); @@ -221,7 +177,8 @@ ByteSource ConvertSignatureToDER(const EVPKeyPointer& pkey, ByteSource&& out) { CHECK(asn1_sig.setParams(std::move(r), std::move(s))); auto buf = asn1_sig.encode(); - if (buf.len <= 0) return ByteSource(); + if (buf.len <= 0) [[unlikely]] + return {}; CHECK_NOT_NULL(buf.data); return ByteSource::Allocated(buf); @@ -231,95 +188,87 @@ void CheckThrow(Environment* env, SignBase::Error error) { HandleScope scope(env->isolate()); switch (error) { - case SignBase::Error::kSignUnknownDigest: + case SignBase::Error::UnknownDigest: return THROW_ERR_CRYPTO_INVALID_DIGEST(env); - case SignBase::Error::kSignNotInitialised: + case SignBase::Error::NotInitialised: return THROW_ERR_CRYPTO_INVALID_STATE(env, "Not initialised"); - case SignBase::Error::kSignMalformedSignature: + case SignBase::Error::MalformedSignature: return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Malformed signature"); - case SignBase::Error::kSignInit: - case SignBase::Error::kSignUpdate: - case SignBase::Error::kSignPrivateKey: - case SignBase::Error::kSignPublicKey: - { - unsigned long err = ERR_get_error(); // NOLINT(runtime/int) - if (err) - return ThrowCryptoError(env, err); - switch (error) { - case SignBase::Error::kSignInit: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "EVP_SignInit_ex failed"); - case SignBase::Error::kSignUpdate: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "EVP_SignUpdate failed"); - case SignBase::Error::kSignPrivateKey: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "PEM_read_bio_PrivateKey failed"); - case SignBase::Error::kSignPublicKey: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "PEM_read_bio_PUBKEY failed"); - default: - ABORT(); - } + case SignBase::Error::Init: + case SignBase::Error::Update: + case SignBase::Error::PrivateKey: + case SignBase::Error::PublicKey: { + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) + if (err) return ThrowCryptoError(env, err); + switch (error) { + case SignBase::Error::Init: + return THROW_ERR_CRYPTO_OPERATION_FAILED(env, + "EVP_SignInit_ex failed"); + case SignBase::Error::Update: + return THROW_ERR_CRYPTO_OPERATION_FAILED(env, + "EVP_SignUpdate failed"); + case SignBase::Error::PrivateKey: + return THROW_ERR_CRYPTO_OPERATION_FAILED( + env, "PEM_read_bio_PrivateKey failed"); + case SignBase::Error::PublicKey: + return THROW_ERR_CRYPTO_OPERATION_FAILED( + env, "PEM_read_bio_PUBKEY failed"); + default: + ABORT(); } + } - case SignBase::Error::kSignOk: + case SignBase::Error::Ok: return; } } -bool IsOneShot(const EVPKeyPointer& key) { - return key.id() == EVP_PKEY_ED25519 || key.id() == EVP_PKEY_ED448; -} - -bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc& dsa_encoding) { - return (key.id() == EVP_PKEY_EC || key.id() == EVP_PKEY_DSA) && - dsa_encoding == kSigEncP1363; +bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc dsa_encoding) { + return key.isSigVariant() && dsa_encoding == DSASigEnc::P1363; } } // namespace -SignBase::Error SignBase::Init(const char* sign_type) { +SignBase::Error SignBase::Init(std::string_view digest) { CHECK_NULL(mdctx_); - // Historically, "dss1" and "DSS1" were DSA aliases for SHA-1 - // exposed through the public API. - if (strcmp(sign_type, "dss1") == 0 || - strcmp(sign_type, "DSS1") == 0) { - sign_type = "SHA1"; - } - const EVP_MD* md = EVP_get_digestbyname(sign_type); - if (md == nullptr) - return kSignUnknownDigest; - - mdctx_.reset(EVP_MD_CTX_new()); - if (!mdctx_ || !EVP_DigestInit_ex(mdctx_.get(), md, nullptr)) { + auto md = ncrypto::getDigestByName(digest); + if (md == nullptr) [[unlikely]] + return Error::UnknownDigest; + + mdctx_ = EVPMDCtxPointer::New(); + + if (!mdctx_.digestInit(md)) [[unlikely]] { mdctx_.reset(); - return kSignInit; + return Error::Init; } - return kSignOk; + return Error::Ok; } SignBase::Error SignBase::Update(const char* data, size_t len) { - if (mdctx_ == nullptr) - return kSignNotInitialised; - if (!EVP_DigestUpdate(mdctx_.get(), data, len)) - return kSignUpdate; - return kSignOk; + if (mdctx_ == nullptr) [[unlikely]] + return Error::NotInitialised; + + ncrypto::Buffer buf{ + .data = data, + .len = len, + }; + + return mdctx_.digestUpdate(buf) ? Error::Ok : Error::Update; } SignBase::SignBase(Environment* env, Local wrap) - : BaseObject(env, wrap) {} + : BaseObject(env, wrap) { + MakeWeak(); +} void SignBase::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackFieldWithSize("mdctx", mdctx_ ? kSizeOf_EVP_MD_CTX : 0); } -Sign::Sign(Environment* env, Local wrap) : SignBase(env, wrap) { - MakeWeak(); -} +Sign::Sign(Environment* env, Local wrap) : SignBase(env, wrap) {} void Sign::Initialize(Environment* env, Local target) { Isolate* isolate = env->isolate(); @@ -335,8 +284,13 @@ void Sign::Initialize(Environment* env, Local target) { SignJob::Initialize(env, target); - constexpr int kSignJobModeSign = SignConfiguration::kSign; - constexpr int kSignJobModeVerify = SignConfiguration::kVerify; + constexpr int kSignJobModeSign = + static_cast(SignConfiguration::Mode::Sign); + constexpr int kSignJobModeVerify = + static_cast(SignConfiguration::Mode::Verify); + + constexpr auto kSigEncDER = DSASigEnc::DER; + constexpr auto kSigEncP1363 = DSASigEnc::P1363; NODE_DEFINE_CONSTANT(target, kSignJobModeSign); NODE_DEFINE_CONSTANT(target, kSignJobModeVerify); @@ -363,8 +317,8 @@ void Sign::SignInit(const FunctionCallbackInfo& args) { Sign* sign; ASSIGN_OR_RETURN_UNWRAP(&sign, args.This()); - const node::Utf8Value sign_type(args.GetIsolate(), args[0]); - crypto::CheckThrow(env, sign->Init(*sign_type)); + const node::Utf8Value sign_type(env->isolate(), args[0]); + crypto::CheckThrow(env, sign->Init(sign_type.ToStringView())); } void Sign::SignUpdate(const FunctionCallbackInfo& args) { @@ -380,20 +334,22 @@ void Sign::SignUpdate(const FunctionCallbackInfo& args) { Sign::SignResult Sign::SignFinal(const EVPKeyPointer& pkey, int padding, - const Maybe& salt_len, + std::optional salt_len, DSASigEnc dsa_sig_enc) { - if (!mdctx_) - return SignResult(kSignNotInitialised); + if (!mdctx_) [[unlikely]] { + return SignResult(Error::NotInitialised); + } EVPMDCtxPointer mdctx = std::move(mdctx_); - if (!ValidateDSAParameters(pkey.get())) - return SignResult(kSignPrivateKey); + if (!pkey.validateDsaParameters()) { + return SignResult(Error::PrivateKey); + } - std::unique_ptr buffer = + auto buffer = Node_SignFinal(env(), std::move(mdctx), pkey, padding, salt_len); - Error error = buffer ? kSignOk : kSignPrivateKey; - if (error == kSignOk && dsa_sig_enc == kSigEncP1363) { + Error error = buffer ? Error::Ok : Error::PrivateKey; + if (error == Error::Ok && dsa_sig_enc == DSASigEnc::P1363) { buffer = ConvertSignatureToP1363(env(), pkey, std::move(buffer)); CHECK_NOT_NULL(buffer->Data()); } @@ -412,49 +368,34 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { if (!data) [[unlikely]] return; const auto& key = data.GetAsymmetricKey(); - if (!key) + if (!key) [[unlikely]] return; - if (IsOneShot(key)) { + if (key.isOneShotVariant()) [[unlikely]] { THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); return; } - int padding = GetDefaultSignPadding(key); - if (!args[offset]->IsUndefined()) { - CHECK(args[offset]->IsInt32()); - padding = args[offset].As()->Value(); - } - - Maybe salt_len = Nothing(); - if (!args[offset + 1]->IsUndefined()) { - CHECK(args[offset + 1]->IsInt32()); - salt_len = Just(args[offset + 1].As()->Value()); + int padding = GetPaddingFromJS(key, args[offset]); + std::optional salt_len = GetSaltLenFromJS(args[offset + 1]); + DSASigEnc dsa_sig_enc = GetDSASigEncFromJS(args[offset + 2]); + if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { + THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); + return; } - CHECK(args[offset + 2]->IsInt32()); - DSASigEnc dsa_sig_enc = - static_cast(args[offset + 2].As()->Value()); + SignResult ret = sign->SignFinal(key, padding, salt_len, dsa_sig_enc); - SignResult ret = sign->SignFinal( - key, - padding, - salt_len, - dsa_sig_enc); - - if (ret.error != kSignOk) + if (ret.error != Error::Ok) [[unlikely]] { return crypto::CheckThrow(env, ret.error); + } - Local ab = - ArrayBuffer::New(env->isolate(), std::move(ret.signature)); + auto ab = ArrayBuffer::New(env->isolate(), std::move(ret.signature)); args.GetReturnValue().Set( Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local())); } -Verify::Verify(Environment* env, Local wrap) - : SignBase(env, wrap) { - MakeWeak(); -} +Verify::Verify(Environment* env, Local wrap) : SignBase(env, wrap) {} void Verify::Initialize(Environment* env, Local target) { Isolate* isolate = env->isolate(); @@ -486,8 +427,8 @@ void Verify::VerifyInit(const FunctionCallbackInfo& args) { Verify* verify; ASSIGN_OR_RETURN_UNWRAP(&verify, args.This()); - const node::Utf8Value verify_type(args.GetIsolate(), args[0]); - crypto::CheckThrow(env, verify->Init(*verify_type)); + const node::Utf8Value verify_type(env->isolate(), args[0]); + crypto::CheckThrow(env, verify->Init(verify_type.ToStringView())); } void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { @@ -495,8 +436,9 @@ void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { const FunctionCallbackInfo& args, const char* data, size_t size) { Environment* env = Environment::GetCurrent(args); - if (size > INT_MAX) [[unlikely]] + if (size > INT_MAX) [[unlikely]] { return THROW_ERR_OUT_OF_RANGE(env, "data is too long"); + } Error err = verify->Update(data, size); crypto::CheckThrow(verify->env(), err); }); @@ -505,35 +447,30 @@ void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { SignBase::Error Verify::VerifyFinal(const EVPKeyPointer& pkey, const ByteSource& sig, int padding, - const Maybe& saltlen, + std::optional saltlen, bool* verify_result) { - if (!mdctx_) - return kSignNotInitialised; + if (!mdctx_) [[unlikely]] + return Error::NotInitialised; - unsigned char m[EVP_MAX_MD_SIZE]; - unsigned int m_len; *verify_result = false; EVPMDCtxPointer mdctx = std::move(mdctx_); - if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_len)) - return kSignPublicKey; + auto data = mdctx.digestFinal(mdctx.getExpectedSize()); + if (!data) [[unlikely]] + return Error::PublicKey; EVPKeyCtxPointer pkctx = pkey.newCtx(); - if (pkctx) { - const int init_ret = EVP_PKEY_verify_init(pkctx.get()); - if (init_ret == -2) { - return kSignPublicKey; - } + if (pkctx) [[likely]] { + const int init_ret = pkctx.initForVerify(); + if (init_ret == -2) [[unlikely]] + return Error::PublicKey; if (init_ret > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, saltlen) && - EVP_PKEY_CTX_set_signature_md(pkctx.get(), EVP_MD_CTX_md(mdctx.get())) > - 0) { - const unsigned char* s = sig.data(); - const int r = EVP_PKEY_verify(pkctx.get(), s, sig.size(), m, m_len); - *verify_result = r == 1; + pkctx.setSignatureMd(mdctx)) { + *verify_result = pkctx.verify(sig, data); } } - return kSignOk; + return Error::Ok; } void Verify::VerifyFinal(const FunctionCallbackInfo& args) { @@ -545,47 +482,42 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { unsigned int offset = 0; auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &offset); - if (!data) return; - const auto& pkey = data.GetAsymmetricKey(); - if (!pkey) + if (!data) [[unlikely]] + return; + const auto& key = data.GetAsymmetricKey(); + if (!key) [[unlikely]] return; - if (IsOneShot(pkey)) { + if (key.isOneShotVariant()) [[unlikely]] { THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); return; } ArrayBufferOrViewContents hbuf(args[offset]); - if (!hbuf.CheckSizeInt32()) [[unlikely]] + if (!hbuf.CheckSizeInt32()) [[unlikely]] { return THROW_ERR_OUT_OF_RANGE(env, "buffer is too big"); - - int padding = GetDefaultSignPadding(pkey); - if (!args[offset + 1]->IsUndefined()) { - CHECK(args[offset + 1]->IsInt32()); - padding = args[offset + 1].As()->Value(); } - Maybe salt_len = Nothing(); - if (!args[offset + 2]->IsUndefined()) { - CHECK(args[offset + 2]->IsInt32()); - salt_len = Just(args[offset + 2].As()->Value()); + int padding = GetPaddingFromJS(key, args[offset + 1]); + std::optional salt_len = GetSaltLenFromJS(args[offset + 2]); + DSASigEnc dsa_sig_enc = GetDSASigEncFromJS(args[offset + 3]); + if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { + THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); + return; } - CHECK(args[offset + 3]->IsInt32()); - DSASigEnc dsa_sig_enc = - static_cast(args[offset + 3].As()->Value()); - ByteSource signature = hbuf.ToByteSource(); - if (dsa_sig_enc == kSigEncP1363) { - signature = ConvertSignatureToDER(pkey, hbuf.ToByteSource()); - if (signature.data() == nullptr) - return crypto::CheckThrow(env, Error::kSignMalformedSignature); + if (dsa_sig_enc == DSASigEnc::P1363) { + signature = ConvertSignatureToDER(key, hbuf.ToByteSource()); + if (signature.data() == nullptr) [[unlikely]] { + return crypto::CheckThrow(env, Error::MalformedSignature); + } } bool verify_result; - Error err = verify->VerifyFinal(pkey, signature, padding, - salt_len, &verify_result); - if (err != kSignOk) + Error err = + verify->VerifyFinal(key, signature, padding, salt_len, &verify_result); + if (err != Error::Ok) [[unlikely]] return crypto::CheckThrow(env, err); args.GetReturnValue().Set(verify_result); } @@ -633,7 +565,7 @@ Maybe SignTraits::AdditionalConfig( static_cast(args[offset].As()->Value()); unsigned int keyParamOffset = offset + 1; - if (params->mode == SignConfiguration::kVerify) { + if (params->mode == SignConfiguration::Mode::Verify) { auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &keyParamOffset); if (!data) return Nothing(); @@ -655,8 +587,8 @@ Maybe SignTraits::AdditionalConfig( if (args[offset + 6]->IsString()) { Utf8Value digest(env->isolate(), args[offset + 6]); - params->digest = EVP_get_digestbyname(*digest); - if (params->digest == nullptr) { + params->digest = ncrypto::getDigestByName(digest.ToStringView()); + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } @@ -664,24 +596,24 @@ Maybe SignTraits::AdditionalConfig( if (args[offset + 7]->IsInt32()) { // Salt length params->flags |= SignConfiguration::kHasSaltLength; - params->salt_length = args[offset + 7].As()->Value(); + params->salt_length = + GetSaltLenFromJS(args[offset + 7]).value_or(params->salt_length); } if (args[offset + 8]->IsUint32()) { // Padding params->flags |= SignConfiguration::kHasPadding; - params->padding = args[offset + 8].As()->Value(); + params->padding = + GetPaddingFromJS(params->key.GetAsymmetricKey(), args[offset + 8]); } if (args[offset + 9]->IsUint32()) { // DSA Encoding - params->dsa_encoding = - static_cast(args[offset + 9].As()->Value()); - if (params->dsa_encoding != kSigEncDER && - params->dsa_encoding != kSigEncP1363) { + params->dsa_encoding = GetDSASigEncFromJS(args[offset + 9]); + if (params->dsa_encoding == DSASigEnc::Invalid) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); return Nothing(); } } - if (params->mode == SignConfiguration::kVerify) { + if (params->mode == SignConfiguration::Mode::Verify) { ArrayBufferOrViewContents signature(args[offset + 10]); if (!signature.CheckSizeInt32()) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "signature is too big"); @@ -708,97 +640,69 @@ bool SignTraits::DeriveBits( const SignConfiguration& params, ByteSource* out) { ClearErrorOnReturn clear_error_on_return; - EVPMDCtxPointer context(EVP_MD_CTX_new()); - EVP_PKEY_CTX* ctx = nullptr; - + auto context = EVPMDCtxPointer::New(); + if (!context) [[unlikely]] + return false; const auto& key = params.key.GetAsymmetricKey(); - switch (params.mode) { - case SignConfiguration::kSign: - if (!EVP_DigestSignInit( - context.get(), &ctx, params.digest, nullptr, key.get())) { - crypto::CheckThrow(env, SignBase::Error::kSignInit); - return false; - } - break; - case SignConfiguration::kVerify: - if (!EVP_DigestVerifyInit( - context.get(), &ctx, params.digest, nullptr, key.get())) { - crypto::CheckThrow(env, SignBase::Error::kSignInit); - return false; - } - break; + auto ctx = ([&] { + switch (params.mode) { + case SignConfiguration::Mode::Sign: + return context.signInit(key, params.digest); + case SignConfiguration::Mode::Verify: + return context.verifyInit(key, params.digest); + } + UNREACHABLE(); + })(); + + if (!ctx.has_value()) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::Init); + return false; } int padding = params.flags & SignConfiguration::kHasPadding ? params.padding - : GetDefaultSignPadding(key); + : key.getDefaultSignPadding(); - Maybe salt_length = params.flags & SignConfiguration::kHasSaltLength - ? Just(params.salt_length) : Nothing(); + std::optional salt_length = + params.flags & SignConfiguration::kHasSaltLength + ? std::optional(params.salt_length) + : std::nullopt; - if (!ApplyRSAOptions(key, ctx, padding, salt_length)) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); + if (!ApplyRSAOptions(key, *ctx, padding, salt_length)) { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } switch (params.mode) { - case SignConfiguration::kSign: { - if (IsOneShot(key)) { - size_t len; - if (!EVP_DigestSign( - context.get(), - nullptr, - &len, - params.data.data(), - params.data.size())) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); - return false; - } - ByteSource::Builder buf(len); - if (!EVP_DigestSign(context.get(), - buf.data(), - &len, - params.data.data(), - params.data.size())) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); + case SignConfiguration::Mode::Sign: { + if (key.isOneShotVariant()) { + auto data = context.signOneShot(params.data); + if (!data) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } - *out = std::move(buf).release(len); + *out = ByteSource::Allocated(data.release()); } else { - size_t len; - if (!EVP_DigestSignUpdate( - context.get(), - params.data.data(), - params.data.size()) || - !EVP_DigestSignFinal(context.get(), nullptr, &len)) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); - return false; - } - ByteSource::Builder buf(len); - if (!EVP_DigestSignFinal( - context.get(), buf.data(), &len)) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); + auto data = context.sign(params.data); + if (!data) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } + auto bs = ByteSource::Allocated(data.release()); if (UseP1363Encoding(key, params.dsa_encoding)) { - *out = ConvertSignatureToP1363(env, key, std::move(buf).release()); + *out = ConvertSignatureToP1363(env, key, std::move(bs)); } else { - *out = std::move(buf).release(len); + *out = std::move(bs); } } break; } - case SignConfiguration::kVerify: { + case SignConfiguration::Mode::Verify: { ByteSource::Builder buf(1); buf.data()[0] = 0; - if (EVP_DigestVerify( - context.get(), - params.signature.data(), - params.signature.size(), - params.data.data(), - params.data.size()) == 1) { + if (context.verify(params.data, params.signature)) { buf.data()[0] = 1; } *out = std::move(buf).release(); @@ -812,9 +716,9 @@ MaybeLocal SignTraits::EncodeOutput(Environment* env, const SignConfiguration& params, ByteSource* out) { switch (params.mode) { - case SignConfiguration::kSign: + case SignConfiguration::Mode::Sign: return out->ToArrayBuffer(env); - case SignConfiguration::kVerify: + case SignConfiguration::Mode::Verify: return Boolean::New(env->isolate(), out->data()[0] == 1); } UNREACHABLE(); diff --git a/src/crypto/crypto_sig.h b/src/crypto/crypto_sig.h index 3a3c27b4e8e748..36c51b07bb5692 100644 --- a/src/crypto/crypto_sig.h +++ b/src/crypto/crypto_sig.h @@ -13,27 +13,24 @@ namespace node { namespace crypto { static const unsigned int kNoDsaSignature = static_cast(-1); -enum DSASigEnc { - kSigEncDER, - kSigEncP1363 -}; +enum class DSASigEnc { DER, P1363, Invalid }; class SignBase : public BaseObject { public: - enum Error { - kSignOk, - kSignUnknownDigest, - kSignInit, - kSignNotInitialised, - kSignUpdate, - kSignPrivateKey, - kSignPublicKey, - kSignMalformedSignature + enum class Error { + Ok, + UnknownDigest, + Init, + NotInitialised, + Update, + PrivateKey, + PublicKey, + MalformedSignature }; SignBase(Environment* env, v8::Local wrap); - Error Init(const char* sign_type); + Error Init(std::string_view digest); Error Update(const char* data, size_t len); // TODO(joyeecheung): track the memory used by OpenSSL types @@ -45,7 +42,7 @@ class SignBase : public BaseObject { ncrypto::EVPMDCtxPointer mdctx_; }; -class Sign : public SignBase { +class Sign final : public SignBase { public: static void Initialize(Environment* env, v8::Local target); static void RegisterExternalReferences(ExternalReferenceRegistry* registry); @@ -54,15 +51,14 @@ class Sign : public SignBase { Error error; std::unique_ptr signature; - explicit SignResult( - Error err, - std::unique_ptr&& sig = nullptr) - : error(err), signature(std::move(sig)) {} + inline explicit SignResult( + Error err, std::unique_ptr&& sig = nullptr) + : error(err), signature(std::move(sig)) {} }; SignResult SignFinal(const ncrypto::EVPKeyPointer& pkey, int padding, - const v8::Maybe& saltlen, + std::optional saltlen, DSASigEnc dsa_sig_enc); static void SignSync(const v8::FunctionCallbackInfo& args); @@ -76,7 +72,7 @@ class Sign : public SignBase { Sign(Environment* env, v8::Local wrap); }; -class Verify : public SignBase { +class Verify final : public SignBase { public: static void Initialize(Environment* env, v8::Local target); static void RegisterExternalReferences(ExternalReferenceRegistry* registry); @@ -84,7 +80,7 @@ class Verify : public SignBase { Error VerifyFinal(const ncrypto::EVPKeyPointer& key, const ByteSource& sig, int padding, - const v8::Maybe& saltlen, + std::optional saltlen, bool* verify_result); static void VerifySync(const v8::FunctionCallbackInfo& args); @@ -99,10 +95,7 @@ class Verify : public SignBase { }; struct SignConfiguration final : public MemoryRetainer { - enum Mode { - kSign, - kVerify - }; + enum class Mode { Sign, Verify }; enum Flags { kHasNone = 0, kHasSaltLength = 1, @@ -118,7 +111,7 @@ struct SignConfiguration final : public MemoryRetainer { int flags = SignConfiguration::kHasNone; int padding = 0; int salt_length = 0; - DSASigEnc dsa_encoding = kSigEncDER; + DSASigEnc dsa_encoding = DSASigEnc::DER; SignConfiguration() = default; @@ -135,8 +128,6 @@ struct SignTraits final { using AdditionalParameters = SignConfiguration; static constexpr const char* JobName = "SignJob"; -// TODO(@jasnell): Sign request vs. Verify request - static constexpr AsyncWrap::ProviderType Provider = AsyncWrap::PROVIDER_SIGNREQUEST; diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc index 0f1defef16661a..7104ee19a6dd79 100644 --- a/src/crypto/crypto_tls.cc +++ b/src/crypto/crypto_tls.cc @@ -46,6 +46,7 @@ using v8::Array; using v8::ArrayBuffer; using v8::ArrayBufferView; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::Context; using v8::DontDelete; @@ -60,6 +61,7 @@ using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::Null; +using v8::Number; using v8::Object; using v8::PropertyAttribute; using v8::ReadOnly; @@ -161,7 +163,7 @@ int NewSessionCallback(SSL* s, SSL_SESSION* sess) { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); - if (!w->has_session_callbacks()) + if (!w->has_session_callbacks()) [[unlikely]] return 0; // Check if session is small enough to be stored @@ -225,10 +227,9 @@ int SSLCertCallback(SSL* s, void* arg) { auto servername = SSLPointer::GetServerName(s); Local servername_str = - !servername.has_value() ? String::Empty(env->isolate()) - : OneByteString(env->isolate(), - servername.value().data(), - servername.value().length()); + !servername.has_value() + ? String::Empty(env->isolate()) + : OneByteString(env->isolate(), servername.value()); Local ocsp = Boolean::New( env->isolate(), SSL_get_tlsext_status_type(s) == TLSEXT_STATUSTYPE_ocsp); @@ -257,30 +258,27 @@ int SelectALPNCallback( Environment* env = w->env(); HandleScope handle_scope(env->isolate()); - Local callback_arg = - Buffer::Copy(env, reinterpret_cast(in), inlen) - .ToLocalChecked(); + Local callback_arg; + Local callback_result; - MaybeLocal maybe_callback_result = - w->MakeCallback(env->alpn_callback_string(), 1, &callback_arg); - - if (maybe_callback_result.IsEmpty()) [[unlikely]] { - // Implies the callback didn't return, because some exception was thrown - // during processing, e.g. if callback returned an invalid ALPN value. + if (!Buffer::Copy(env, reinterpret_cast(in), inlen) + .ToLocal(&callback_arg)) { return SSL_TLSEXT_ERR_ALERT_FATAL; } - Local callback_result = maybe_callback_result.ToLocalChecked(); + if (!w->MakeCallback(env->alpn_callback_string(), 1, &callback_arg) + .ToLocal(&callback_result)) { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } - if (callback_result->IsUndefined()) { + if (callback_result->IsUndefined() && !callback_result->IsNumber()) { // If you set an ALPN callback, but you return undefined for an ALPN // request, you're rejecting all proposed ALPN protocols, and so we send // a fatal alert: return SSL_TLSEXT_ERR_ALERT_FATAL; } - CHECK(callback_result->IsNumber()); - unsigned int result_int = callback_result.As()->Value(); + unsigned int result_int = callback_result.As()->Value(); // The callback returns an offset into the given buffer, for the selected // protocol that should be returned. We then set outlen & out to point @@ -1087,10 +1085,10 @@ int TLSWrap::DoWrite(WriteWrap* w, // and copying it when it could just be used. if (nonempty_count != 1) { - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env()->isolate(), length); - } + bs = ArrayBuffer::NewBackingStore( + env()->isolate(), + length, + BackingStoreInitializationMode::kUninitialized); size_t offset = 0; for (i = 0; i < count; i++) { memcpy(static_cast(bs->Data()) + offset, @@ -1107,8 +1105,10 @@ int TLSWrap::DoWrite(WriteWrap* w, written = SSL_write(ssl_.get(), buf->base, buf->len); if (written == -1) { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env()->isolate(), length); + bs = ArrayBuffer::NewBackingStore( + env()->isolate(), + length, + BackingStoreInitializationMode::kUninitialized); memcpy(bs->Data(), buf->base, buf->len); } } @@ -1750,11 +1750,8 @@ void TLSWrap::GetFinished(const FunctionCallbackInfo& args) { if (len == 0) return; - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), len); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), len, BackingStoreInitializationMode::kUninitialized); CHECK_EQ(bs->ByteLength(), SSL_get_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); @@ -1781,11 +1778,8 @@ void TLSWrap::GetPeerFinished(const FunctionCallbackInfo& args) { if (len == 0) return; - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), len); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), len, BackingStoreInitializationMode::kUninitialized); CHECK_EQ(bs->ByteLength(), SSL_get_peer_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); @@ -1810,11 +1804,8 @@ void TLSWrap::GetSession(const FunctionCallbackInfo& args) { if (slen <= 0) return; // Invalid or malformed session. - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), slen); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), slen, BackingStoreInitializationMode::kUninitialized); unsigned char* p = static_cast(bs->Data()); CHECK_LT(0, i2d_SSL_SESSION(sess, &p)); @@ -1997,11 +1988,8 @@ void TLSWrap::ExportKeyingMaterial(const FunctionCallbackInfo& args) { uint32_t olen = args[0].As()->Value(); Utf8Value label(env->isolate(), args[1]); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), olen); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), olen, BackingStoreInitializationMode::kUninitialized); ByteSource context; bool use_context = !args[2]->IsUndefined(); diff --git a/src/crypto/crypto_tls.h b/src/crypto/crypto_tls.h index ed1ee337b42ec1..f02c37182ff189 100644 --- a/src/crypto/crypto_tls.h +++ b/src/crypto/crypto_tls.h @@ -58,15 +58,17 @@ class TLSWrap : public AsyncWrap, ~TLSWrap() override; - bool is_cert_cb_running() const { return cert_cb_running_; } - bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } - bool has_session_callbacks() const { return session_callbacks_; } - void set_cert_cb_running(bool on = true) { cert_cb_running_ = on; } - void set_awaiting_new_session(bool on = true) { awaiting_new_session_ = on; } - void enable_session_callbacks() { session_callbacks_ = true; } - bool is_server() const { return kind_ == Kind::kServer; } - bool is_client() const { return kind_ == Kind::kClient; } - bool is_awaiting_new_session() const { return awaiting_new_session_; } + inline bool is_cert_cb_running() const { return cert_cb_running_; } + inline bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } + inline bool has_session_callbacks() const { return session_callbacks_; } + inline void set_cert_cb_running(bool on = true) { cert_cb_running_ = on; } + inline void set_awaiting_new_session(bool on = true) { + awaiting_new_session_ = on; + } + inline void enable_session_callbacks() { session_callbacks_ = true; } + inline bool is_server() const { return kind_ == Kind::kServer; } + inline bool is_client() const { return kind_ == Kind::kClient; } + inline bool is_awaiting_new_session() const { return awaiting_new_session_; } // Implement StreamBase: bool IsAlive() override; @@ -128,7 +130,7 @@ class TLSWrap : public AsyncWrap, // Alternative to StreamListener::stream(), that returns a StreamBase instead // of a StreamResource. - StreamBase* underlying_stream() const { + inline StreamBase* underlying_stream() const { return static_cast(stream()); } diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 60610b1b795c9b..61f8cbf6703b50 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -630,17 +630,12 @@ Maybe SetEncodedValue(Environment* env, : Nothing(); } -bool SetRsaOaepLabel(const EVPKeyCtxPointer& ctx, const ByteSource& label) { +bool SetRsaOaepLabel(EVPKeyCtxPointer* ctx, const ByteSource& label) { if (label.size() != 0) { // OpenSSL takes ownership of the label, so we need to create a copy. - void* label_copy = OPENSSL_memdup(label.data(), label.size()); - CHECK_NOT_NULL(label_copy); - int ret = EVP_PKEY_CTX_set0_rsa_oaep_label( - ctx.get(), static_cast(label_copy), label.size()); - if (ret <= 0) { - OPENSSL_free(label_copy); - return false; - } + auto dup = ncrypto::DataPointer::Copy(label); + if (!dup) return false; + return ctx->setRsaOaepLabel(std::move(dup)); } return true; } diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index a5967c7d24b836..e3c9a82c17b382 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -61,10 +61,9 @@ void InitCryptoOnce(); void InitCrypto(v8::Local target); -extern void UseExtraCaCerts(const std::string& file); +extern void UseExtraCaCerts(std::string_view file); int PasswordCallback(char* buf, int size, int rwflag, void* u); - int NoPasswordCallback(char* buf, int size, int rwflag, void* u); // Decode is used by the various stream-based crypto utilities to decode @@ -165,7 +164,7 @@ T* MallocOpenSSL(size_t count) { // A helper class representing a read-only byte array. When deallocated, its // contents are zeroed. -class ByteSource { +class ByteSource final { public: class Builder { public: @@ -224,17 +223,25 @@ class ByteSource { ByteSource& operator=(const ByteSource&) = delete; template - const T* data() const { + inline const T* data() const { return reinterpret_cast(data_); } - size_t size() const { return size_; } + template + operator ncrypto::Buffer() const { + return ncrypto::Buffer{ + .data = data(), + .len = size(), + }; + } + + inline size_t size() const { return size_; } - bool empty() const { return size_ == 0; } + inline bool empty() const { return size_ == 0; } - operator bool() const { return data_ != nullptr; } + inline operator bool() const { return data_ != nullptr; } - ncrypto::BignumPointer ToBN() const { + inline ncrypto::BignumPointer ToBN() const { return ncrypto::BignumPointer(data(), size()); } @@ -518,7 +525,7 @@ void ThrowCryptoError(Environment* env, unsigned long err, // NOLINT(runtime/int) const char* message = nullptr); -class CipherPushContext { +class CipherPushContext final { public: inline explicit CipherPushContext(Environment* env) : list_(env->isolate()), env_(env) {} @@ -546,16 +553,13 @@ void array_push_back(const TypeName* evp_ref, const char* from, const char* to, void* arg) { - if (!from) - return; + if (!from) return; const TypeName* real_instance = getbyname(from); - if (!real_instance) - return; + if (!real_instance) return; const char* real_name = getname(real_instance); - if (!real_name) - return; + if (!real_name) return; // EVP_*_fetch() does not support alias names, so we need to pass it the // real/original algorithm name. @@ -564,8 +568,7 @@ void array_push_back(const TypeName* evp_ref, // algorithms are used internally by OpenSSL and are also passed to this // callback). TypeName* fetched = fetch_type(nullptr, real_name, nullptr); - if (!fetched) - return; + if (!fetched) return; free_type(fetched); static_cast(arg)->push_back(from); @@ -576,8 +579,7 @@ void array_push_back(const TypeName* evp_ref, const char* from, const char* to, void* arg) { - if (!from) - return; + if (!from) return; static_cast(arg)->push_back(from); } #endif @@ -590,7 +592,7 @@ inline bool IsAnyBufferSource(v8::Local arg) { } template -class ArrayBufferOrViewContents { +class ArrayBufferOrViewContents final { public: ArrayBufferOrViewContents() = default; ArrayBufferOrViewContents(const ArrayBufferOrViewContents&) = delete; @@ -707,8 +709,7 @@ v8::Maybe SetEncodedValue(Environment* env, const BIGNUM* bn, int size = 0); -bool SetRsaOaepLabel(const ncrypto::EVPKeyCtxPointer& rsa, - const ByteSource& label); +bool SetRsaOaepLabel(ncrypto::EVPKeyCtxPointer* rsa, const ByteSource& label); namespace Util { void Initialize(Environment* env, v8::Local target); diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index 3465454e4de4a7..782eced277518d 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -21,13 +21,12 @@ using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; using ncrypto::ECKeyPointer; using ncrypto::SSLPointer; -using ncrypto::StackOfASN1; using ncrypto::X509Pointer; using ncrypto::X509View; using v8::Array; using v8::ArrayBuffer; using v8::ArrayBufferView; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::Context; using v8::Date; @@ -38,6 +37,7 @@ using v8::FunctionTemplate; using v8::Integer; using v8::Isolate; using v8::Local; +using v8::LocalVector; using v8::MaybeLocal; using v8::NewStringType; using v8::Object; @@ -55,14 +55,13 @@ ManagedX509::ManagedX509(const ManagedX509& that) { ManagedX509& ManagedX509::operator=(const ManagedX509& that) { cert_.reset(that.get()); - - if (cert_) + if (cert_) [[likely]] X509_up_ref(cert_.get()); - return *this; } void ManagedX509::MemoryInfo(MemoryTracker* tracker) const { + if (!cert_) return; // This is an approximation based on the der encoding size. int size = i2d_X509(cert_.get(), nullptr); tracker->TrackFieldWithSize("cert", size); @@ -94,7 +93,8 @@ void Fingerprint(const FunctionCallbackInfo& args) { } MaybeLocal ToV8Value(Local context, BIOPointer&& bio) { - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), @@ -135,7 +135,7 @@ MaybeLocal ToV8Value(Local context, const ASN1_STRING* str) { // not escape anything. unsigned char* value_str; int value_str_size = ASN1_STRING_to_UTF8(&value_str, str); - if (value_str_size < 0) { + if (value_str_size < 0) [[unlikely]] { return Undefined(context->GetIsolate()); } DataPointer free_value_str(value_str, value_str_size); @@ -152,7 +152,8 @@ MaybeLocal ToV8Value(Local context, const ASN1_STRING* str) { } MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), @@ -165,7 +166,8 @@ MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { } MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { - if (bio == nullptr || !*bio) return {}; + if (bio == nullptr || !*bio) [[unlikely]] + return {}; BUF_MEM* mem = *bio; auto backing = ArrayBuffer::NewBackingStore( mem->data, @@ -183,7 +185,8 @@ MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { MaybeLocal GetDer(Environment* env, const X509View& view) { Local ret; auto bio = view.toDER(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToBuffer(env, &bio).ToLocal(&ret)) { return {}; } @@ -194,7 +197,8 @@ MaybeLocal GetSubjectAltNameString(Environment* env, const X509View& view) { Local ret; auto bio = view.getSubjectAltName(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) return {}; return ret; } @@ -202,7 +206,8 @@ MaybeLocal GetSubjectAltNameString(Environment* env, MaybeLocal GetInfoAccessString(Environment* env, const X509View& view) { Local ret; auto bio = view.getInfoAccess(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } @@ -212,7 +217,8 @@ MaybeLocal GetInfoAccessString(Environment* env, const X509View& view) { MaybeLocal GetValidFrom(Environment* env, const X509View& view) { Local ret; auto bio = view.getValidFrom(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } @@ -222,7 +228,8 @@ MaybeLocal GetValidFrom(Environment* env, const X509View& view) { MaybeLocal GetValidTo(Environment* env, const X509View& view) { Local ret; auto bio = view.getValidTo(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } @@ -248,25 +255,12 @@ MaybeLocal GetSerialNumber(Environment* env, const X509View& view) { } MaybeLocal GetKeyUsage(Environment* env, const X509View& cert) { - StackOfASN1 eku(static_cast( - X509_get_ext_d2i(cert.get(), NID_ext_key_usage, nullptr, nullptr))); - if (eku) { - const int count = sk_ASN1_OBJECT_num(eku.get()); - MaybeStackBuffer, 16> ext_key_usage(count); - char buf[256]; - - int j = 0; - for (int i = 0; i < count; i++) { - if (OBJ_obj2txt( - buf, sizeof(buf), sk_ASN1_OBJECT_value(eku.get(), i), 1) >= 0) { - ext_key_usage[j++] = OneByteString(env->isolate(), buf); - } - } - - return Array::New(env->isolate(), ext_key_usage.out(), count); - } - - return Undefined(env->isolate()); + LocalVector vec(env->isolate()); + bool res = cert.enumUsages([&](std::string_view view) { + vec.push_back(OneByteString(env->isolate(), view)); + }); + if (!res) return Undefined(env->isolate()); + return Array::New(env->isolate(), vec.data(), vec.size()); } void Pem(const FunctionCallbackInfo& args) { @@ -387,7 +381,7 @@ void PublicKey(const FunctionCallbackInfo& args) { // TODO(tniessen): consider checking X509_get_pubkey() when the // X509Certificate object is being created. auto result = cert->view().getPublicKey(); - if (!result.value) { + if (!result.value) [[unlikely]] { ThrowCryptoError(env, result.error.value_or(0)); return; } @@ -547,7 +541,9 @@ void Parse(const FunctionCallbackInfo& args) { .len = buf.length(), }); - if (!result.value) return ThrowCryptoError(env, result.error.value_or(0)); + if (!result.value) [[unlikely]] { + return ThrowCryptoError(env, result.error.value_or(0)); + } if (X509Certificate::New(env, std::move(result.value)).ToLocal(&cert)) { args.GetReturnValue().Set(cert); @@ -571,7 +567,8 @@ bool Set(Environment* env, Local name, MaybeLocal maybe_value) { Local value; - if (!maybe_value.ToLocal(&value)) return false; + if (!maybe_value.ToLocal(&value)) [[unlikely]] + return false; // Undefined is ignored, but still considered successful if (value->IsUndefined()) return true; @@ -585,7 +582,8 @@ bool Set(Environment* env, uint32_t index, MaybeLocal maybe_value) { Local value; - if (!maybe_value.ToLocal(&value)) return false; + if (!maybe_value.ToLocal(&value)) [[unlikely]] + return false; // Undefined is ignored, but still considered successful if (value->IsUndefined()) return true; @@ -664,33 +662,32 @@ static MaybeLocal GetX509NameObject(Environment* env, return result; } -MaybeLocal GetPubKey(Environment* env, OSSL3_CONST RSA* rsa) { +MaybeLocal GetPubKey(Environment* env, const ncrypto::Rsa& rsa) { int size = i2d_RSA_PUBKEY(rsa, nullptr); CHECK_GE(size, 0); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), size); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), size, BackingStoreInitializationMode::kUninitialized); - unsigned char* serialized = reinterpret_cast(bs->Data()); + auto serialized = reinterpret_cast(bs->Data()); CHECK_GE(i2d_RSA_PUBKEY(rsa, &serialized), 0); - Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + auto ab = ArrayBuffer::New(env->isolate(), std::move(bs)); return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local()); } MaybeLocal GetModulusString(Environment* env, const BIGNUM* n) { auto bio = BIOPointer::New(n); - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; return ToV8Value(env->context(), bio); } MaybeLocal GetExponentString(Environment* env, const BIGNUM* e) { uint64_t exponent_word = static_cast(BignumPointer::GetWord(e)); auto bio = BIOPointer::NewMem(); - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; BIO_printf(bio.get(), "0x%" PRIx64, exponent_word); return ToV8Value(env->context(), bio); } @@ -699,14 +696,16 @@ MaybeLocal GetECPubKey(Environment* env, const EC_GROUP* group, OSSL3_CONST EC_KEY* ec) { const auto pubkey = ECKeyPointer::GetPublicKey(ec); - if (pubkey == nullptr) return Undefined(env->isolate()); + if (pubkey == nullptr) [[unlikely]] + return Undefined(env->isolate()); return ECPointToBuffer(env, group, pubkey, EC_KEY_get_conv_form(ec), nullptr) .FromMaybe(Local()); } MaybeLocal GetECGroupBits(Environment* env, const EC_GROUP* group) { - if (group == nullptr) return Undefined(env->isolate()); + if (group == nullptr) [[unlikely]] + return Undefined(env->isolate()); int bits = EC_GROUP_order_bits(group); if (bits <= 0) return Undefined(env->isolate()); @@ -716,10 +715,9 @@ MaybeLocal GetECGroupBits(Environment* env, const EC_GROUP* group) { template MaybeLocal GetCurveName(Environment* env, const int nid) { - const char* name = nid2string(nid); - return name != nullptr - ? MaybeLocal(OneByteString(env->isolate(), name)) - : MaybeLocal(Undefined(env->isolate())); + std::string_view name = nid2string(nid); + return name.size() ? MaybeLocal(OneByteString(env->isolate(), name)) + : MaybeLocal(Undefined(env->isolate())); } MaybeLocal X509ToObject(Environment* env, const X509View& cert) { @@ -745,68 +743,68 @@ MaybeLocal X509ToObject(Environment* env, const X509View& cert) { !Set(env, info, env->ca_string(), - Boolean::New(env->isolate(), cert.isCA()))) { + Boolean::New(env->isolate(), cert.isCA()))) [[unlikely]] { return {}; } - OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert.get()); - OSSL3_CONST RSA* rsa = nullptr; - OSSL3_CONST EC_KEY* ec = nullptr; - if (pkey != nullptr) { - switch (EVP_PKEY_id(pkey)) { - case EVP_PKEY_RSA: - rsa = EVP_PKEY_get0_RSA(pkey); - break; - case EVP_PKEY_EC: - ec = EVP_PKEY_get0_EC_KEY(pkey); - break; - } + if (!cert.ifRsa([&](const ncrypto::Rsa& rsa) { + auto pub_key = rsa.getPublicKey(); + if (!Set(env, + info, + env->modulus_string(), + GetModulusString(env, pub_key.n)) || + !Set(env, + info, + env->bits_string(), + Integer::New(env->isolate(), + BignumPointer::GetBitCount(pub_key.n))) || + !Set(env, + info, + env->exponent_string(), + GetExponentString(env, pub_key.e)) || + !Set(env, info, env->pubkey_string(), GetPubKey(env, rsa))) + [[unlikely]] { + return false; + } + return true; + })) [[unlikely]] { + return {}; } - if (rsa) { - const BIGNUM* n; - const BIGNUM* e; - RSA_get0_key(rsa, &n, &e, nullptr); - if (!Set( - env, info, env->modulus_string(), GetModulusString(env, n)) || - !Set( - env, - info, - env->bits_string(), - Integer::New(env->isolate(), BignumPointer::GetBitCount(n))) || - !Set( - env, info, env->exponent_string(), GetExponentString(env, e)) || - !Set(env, info, env->pubkey_string(), GetPubKey(env, rsa))) { - return {}; - } - } else if (ec) { - const auto group = ECKeyPointer::GetGroup(ec); + if (!cert.ifEc([&](const ncrypto::Ec& ec) { + const auto group = ec.getGroup(); - if (!Set( - env, info, env->bits_string(), GetECGroupBits(env, group)) || - !Set( - env, info, env->pubkey_string(), GetECPubKey(env, group, ec))) { - return {}; - } + if (!Set( + env, info, env->bits_string(), GetECGroupBits(env, group)) || + !Set( + env, info, env->pubkey_string(), GetECPubKey(env, group, ec))) + [[unlikely]] { + return false; + } - const int nid = EC_GROUP_get_curve_name(group); - if (nid != 0) { - // Curve is well-known, get its OID and NIST nick-name (if it has one). - - if (!Set(env, - info, - env->asn1curve_string(), - GetCurveName(env, nid)) || - !Set(env, - info, - env->nistcurve_string(), - GetCurveName(env, nid))) { - return {}; - } - } else { - // Unnamed curves can be described by their mathematical properties, - // but aren't used much (at all?) with X.509/TLS. Support later if needed. - } + const int nid = ec.getCurve(); + if (nid != 0) [[likely]] { + // Curve is well-known, get its OID and NIST nick-name (if it has + // one). + + if (!Set(env, + info, + env->asn1curve_string(), + GetCurveName(env, nid)) || + !Set(env, + info, + env->nistcurve_string(), + GetCurveName(env, nid))) + [[unlikely]] { + return false; + } + } + // Unnamed curves can be described by their mathematical properties, + // but aren't used much (at all?) with X.509/TLS. Support later if + // needed. + return true; + })) [[unlikely]] { + return {}; } if (!Set( @@ -828,7 +826,8 @@ MaybeLocal X509ToObject(Environment* env, const X509View& cert) { env, info, env->ext_key_usage_string(), GetKeyUsage(env, cert)) || !Set( env, info, env->serial_number_string(), GetSerialNumber(env, cert)) || - !Set(env, info, env->raw_string(), GetDer(env, cert))) { + !Set(env, info, env->raw_string(), GetDer(env, cert))) + [[unlikely]] { return {}; } @@ -910,7 +909,8 @@ MaybeLocal X509Certificate::New(Environment* env, MaybeLocal X509Certificate::GetCert(Environment* env, const SSLPointer& ssl) { auto cert = X509View::From(ssl); - if (!cert) return {}; + if (!cert) [[unlikely]] + return {}; return New(env, cert.clone()); } @@ -930,7 +930,7 @@ MaybeLocal X509Certificate::GetPeerCert(Environment* env, if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) return MaybeLocal(); - if (!cert) { + if (!cert) [[unlikely]] { cert.reset(sk_X509_value(ssl_certs, 0)); sk_X509_delete(ssl_certs, 0); } @@ -945,7 +945,8 @@ v8::MaybeLocal X509Certificate::toObject(Environment* env) { v8::MaybeLocal X509Certificate::toObject(Environment* env, const X509View& cert) { - if (!cert) return {}; + if (!cert) [[unlikely]] + return {}; return X509ToObject(env, cert).FromMaybe(Local()); } @@ -979,7 +980,7 @@ X509Certificate::X509CertificateTransferData::Deserialize( Environment* env, Local context, std::unique_ptr self) { - if (context != env->context()) { + if (context != env->context()) [[unlikely]] { THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env); return {}; } diff --git a/src/crypto/crypto_x509.h b/src/crypto/crypto_x509.h index 54f4b2a40732d2..39f1878be1925f 100644 --- a/src/crypto/crypto_x509.h +++ b/src/crypto/crypto_x509.h @@ -25,10 +25,10 @@ class ManagedX509 final : public MemoryRetainer { ManagedX509(const ManagedX509& that); ManagedX509& operator=(const ManagedX509& that); - operator bool() const { return !!cert_; } - X509* get() const { return cert_.get(); } - ncrypto::X509View view() const { return cert_; } - operator ncrypto::X509View() const { return cert_; } + inline operator bool() const { return !!cert_; } + inline X509* get() const { return cert_.get(); } + inline ncrypto::X509View view() const { return cert_; } + inline operator ncrypto::X509View() const { return cert_; } void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(ManagedX509) @@ -77,7 +77,7 @@ class X509Certificate final : public BaseObject { } inline ncrypto::X509View view() const { return *cert_; } - X509* get() { return cert_->get(); } + inline X509* get() { return cert_->get(); } v8::MaybeLocal toObject(Environment* env); static v8::MaybeLocal toObject(Environment* env, From 1c7c32f96128ac2ae5472dd2da425d9cf638b453 Mon Sep 17 00:00:00 2001 From: Burkov Egor Date: Wed, 22 Jan 2025 16:16:31 +0300 Subject: [PATCH 131/208] src: add nullptr handling from X509_STORE_new() In openssl we should check result of X509_STORE_new() for nullptr Refs: https://github.com/nodejs/node/issues/56694 PR-URL: https://github.com/nodejs/node/pull/56700 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- src/crypto/crypto_context.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index da5cebb87a3d51..af7ac2e5e8eba9 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -272,6 +272,7 @@ X509_STORE* NewRootCertStore() { } X509_STORE* store = X509_STORE_new(); + CHECK_NOT_NULL(store); if (*system_cert_path != '\0') { ERR_set_mark(); X509_STORE_load_locations(store, system_cert_path, nullptr); From 80c70ee84520b6f489e63c1e4298c3a296f00aab Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Sun, 26 Jan 2025 17:41:21 +0100 Subject: [PATCH 132/208] test: do not use common.isMainThread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `common.isMainThread` was removed in https://github.com/nodejs/node/commit/8caa1dcee63b2c6fd7a9, use the `isMainThread` export of the `worker_threads` module instead. PR-URL: https://github.com/nodejs/node/pull/56768 Reviewed-By: Michaël Zasso Reviewed-By: James M Snell Reviewed-By: Chengzhong Wu Reviewed-By: Matteo Collina --- test/parallel/test-require-resolve-opts-paths-relative.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/parallel/test-require-resolve-opts-paths-relative.js b/test/parallel/test-require-resolve-opts-paths-relative.js index 522a1fdbce82a4..13d17d478b753d 100644 --- a/test/parallel/test-require-resolve-opts-paths-relative.js +++ b/test/parallel/test-require-resolve-opts-paths-relative.js @@ -3,8 +3,9 @@ const common = require('../common'); const assert = require('assert'); const fixtures = require('../common/fixtures'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) common.skip('process.chdir is not available in Workers'); const subdir = fixtures.path('module-require', 'relative', 'subdir'); From ea7ab162d67b4122510d23e197a80ec8c5a2ecde Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Jan 2025 09:19:39 -0800 Subject: [PATCH 133/208] test: cleanup and simplify test-crypto-aes-wrap * Add comment explaining purpose of the test * Eliminate duplicative/extraneous buffer allocations PR-URL: https://github.com/nodejs/node/pull/56748 Reviewed-By: Yagiz Nizipli Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Matteo Collina --- test/parallel/test-crypto-aes-wrap.js | 58 +++++++++++++-------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/test/parallel/test-crypto-aes-wrap.js b/test/parallel/test-crypto-aes-wrap.js index 6fe35258f7d6b2..21d48d8a3fbae7 100644 --- a/test/parallel/test-crypto-aes-wrap.js +++ b/test/parallel/test-crypto-aes-wrap.js @@ -1,62 +1,60 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +// Tests that the AES wrap and unwrap functions are working correctly. const assert = require('assert'); const crypto = require('crypto'); -const test = [ +const ivShort = Buffer.from('3fd838af', 'hex'); +const ivLong = Buffer.from('3fd838af4093d749', 'hex'); +const key1 = Buffer.from('b26f309fbe57e9b3bb6ae5ef31d54450', 'hex'); +const key2 = Buffer.from('40978085d68091f7dfca0d7dfc7a5ee76d2cc7f2f345a304', 'hex'); +const key3 = Buffer.from('29c9eab5ed5ad44134a1437fe2e673b4d88a5b7c72e68454fea08721392b7323', 'hex'); + +[ { algorithm: 'aes128-wrap', - key: 'b26f309fbe57e9b3bb6ae5ef31d54450', - iv: '3fd838af4093d749', + key: key1, + iv: ivLong, text: '12345678123456781234567812345678' }, { algorithm: 'id-aes128-wrap-pad', - key: 'b26f309fbe57e9b3bb6ae5ef31d54450', - iv: '3fd838af', + key: key1, + iv: ivShort, text: '12345678123456781234567812345678123' }, { algorithm: 'aes192-wrap', - key: '40978085d68091f7dfca0d7dfc7a5ee76d2cc7f2f345a304', - iv: '3fd838af4093d749', + key: key2, + iv: ivLong, text: '12345678123456781234567812345678' }, { algorithm: 'id-aes192-wrap-pad', - key: '40978085d68091f7dfca0d7dfc7a5ee76d2cc7f2f345a304', - iv: '3fd838af', + key: key2, + iv: ivShort, text: '12345678123456781234567812345678123' }, { algorithm: 'aes256-wrap', - key: '29c9eab5ed5ad44134a1437fe2e673b4d88a5b7c72e68454fea08721392b7323', - iv: '3fd838af4093d749', + key: key3, + iv: ivLong, text: '12345678123456781234567812345678' }, { algorithm: 'id-aes256-wrap-pad', - key: '29c9eab5ed5ad44134a1437fe2e673b4d88a5b7c72e68454fea08721392b7323', - iv: '3fd838af', + key: key3, + iv: ivShort, text: '12345678123456781234567812345678123' }, -]; - -test.forEach((data) => { - const cipher = crypto.createCipheriv( - data.algorithm, - Buffer.from(data.key, 'hex'), - Buffer.from(data.iv, 'hex')); - const ciphertext = cipher.update(data.text, 'utf8'); - - const decipher = crypto.createDecipheriv( - data.algorithm, - Buffer.from(data.key, 'hex'), - Buffer.from(data.iv, 'hex')); - const msg = decipher.update(ciphertext, 'buffer', 'utf8'); - - assert.strictEqual(msg, data.text, `${data.algorithm} test case failed`); +].forEach(({ algorithm, key, iv, text }) => { + const cipher = crypto.createCipheriv(algorithm, key, iv); + const decipher = crypto.createDecipheriv(algorithm, key, iv); + const msg = decipher.update(cipher.update(text, 'utf8'), 'buffer', 'utf8'); + assert.strictEqual(msg, text, `${algorithm} test case failed`); }); From a6c5ce27d39d52e8bfe04ed8d747b19eb8970960 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 26 Jan 2025 19:31:35 +0100 Subject: [PATCH 134/208] doc: improve accessibility of expandable lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56749 Reviewed-By: James M Snell Reviewed-By: Ulises Gascón Reviewed-By: Claudio Wunder --- doc/api_assets/api.js | 6 +++++- doc/api_assets/style.css | 17 ++++++----------- doc/template.html | 6 +++--- tools/doc/html.mjs | 12 ++++++------ 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/doc/api_assets/api.js b/doc/api_assets/api.js index 394b5ba990946c..e86f110e0346bf 100644 --- a/doc/api_assets/api.js +++ b/doc/api_assets/api.js @@ -41,6 +41,7 @@ function closeAllPickers() { for (const picker of pickers) { picker.parentNode.classList.remove('expanded'); + picker.ariaExpanded = false; } window.removeEventListener('click', closeAllPickers); @@ -58,6 +59,7 @@ for (const picker of pickers) { const parentNode = picker.parentNode; + picker.ariaExpanded = parentNode.classList.contains('expanded'); picker.addEventListener('click', function(e) { e.preventDefault(); @@ -65,7 +67,7 @@ closeAllPickers as window event trigger already closed all the pickers, if it already closed there is nothing else to do here */ - if (parentNode.classList.contains('expanded')) { + if (picker.ariaExpanded === 'true') { return; } @@ -75,9 +77,11 @@ */ requestAnimationFrame(function() { + picker.ariaExpanded = true; parentNode.classList.add('expanded'); window.addEventListener('click', closeAllPickers); window.addEventListener('keydown', onKeyDown); + parentNode.querySelector('.picker a').focus(); }); }); } diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index 28a284e3b975b8..a40990a39252a4 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -182,22 +182,15 @@ li.picker-header .picker-arrow { height: .6rem; border-top: .3rem solid transparent; border-bottom: .3rem solid transparent; - border-left: .6rem solid var(--color-links); + border-left: .6rem solid currentColor; border-right: none; margin: 0 .2rem .05rem 0; } -li.picker-header a:focus .picker-arrow, -li.picker-header a:active .picker-arrow, -li.picker-header a:hover .picker-arrow { - border-left: .6rem solid var(--white); -} - -li.picker-header.expanded a:focus .picker-arrow, -li.picker-header.expanded a:active .picker-arrow, -li.picker-header.expanded a:hover .picker-arrow, +li.picker-header.expanded .picker-arrow, +:root:not(.has-js) li.picker-header:focus-within .picker-arrow, :root:not(.has-js) li.picker-header:hover .picker-arrow { - border-top: .6rem solid var(--white); + border-top: .6rem solid currentColor; border-bottom: none; border-left: .35rem solid transparent; border-right: .35rem solid transparent; @@ -205,11 +198,13 @@ li.picker-header.expanded a:hover .picker-arrow, } li.picker-header.expanded > a, +:root:not(.has-js) li.picker-header:focus-within > a, :root:not(.has-js) li.picker-header:hover > a { border-radius: 2px 2px 0 0; } li.picker-header.expanded > .picker, +:root:not(.has-js) li.picker-header:focus-within > .picker, :root:not(.has-js) li.picker-header:hover > .picker { display: block; z-index: 1; diff --git a/doc/template.html b/doc/template.html index ab8be0e747f492..51e789b7e6168c 100644 --- a/doc/template.html +++ b/doc/template.html @@ -59,13 +59,13 @@

      Node.js __VERSION__ documentation

      __GTOC_PICKER__ __ALTDOCS__
    • - + Options -
      -
        +
        +
        • View on single page
        • diff --git a/tools/doc/html.mjs b/tools/doc/html.mjs index 68762d89e048ce..d61d335c7b8957 100644 --- a/tools/doc/html.mjs +++ b/tools/doc/html.mjs @@ -527,11 +527,11 @@ function altDocs(filename, docCreated, versions) { return list ? `
        • - + Other versions -
            ${list}
          +
            ${list}
        • ` : ''; } @@ -557,12 +557,12 @@ function gtocPicker(id) { return `
        • - + Index -
          ${gtoc}
          +
          ${gtoc}
        • `; } @@ -574,12 +574,12 @@ function tocPicker(id, content) { return `
        • - + Table of contents -
          ${content.tocPicker}
          +
          ${content.tocPicker.replace('
        • `; } From f1196ee3bbd115dbf0937c3a48c7b47da0274e34 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 26 Jan 2025 19:41:45 +0100 Subject: [PATCH 135/208] doc: add "Skip to content" button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56750 Reviewed-By: James M Snell Reviewed-By: Ulises Gascón Reviewed-By: Claudio Wunder --- doc/api_assets/style.css | 13 +++++++++++++ doc/template.html | 1 + 2 files changed, 14 insertions(+) diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index a40990a39252a4..35c216bb0523fc 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -122,6 +122,19 @@ a.type { font-size: .9em; } +.skip-to-content { + position: fixed; + top: -300%; +} +.skip-to-content:focus { + display: block; + top: 0; + left: 0; + background-color: var(--green1); + padding: 1rem; + z-index: 999999; +} + #content { position: relative; } diff --git a/doc/template.html b/doc/template.html index 51e789b7e6168c..34edf068df5c8d 100644 --- a/doc/template.html +++ b/doc/template.html @@ -26,6 +26,7 @@ __JS_FLAVORED_DYNAMIC_CSS__ + Skip to content