From c69277788d6ef1e88406fb005cdd0746d19fda81 Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Mon, 11 May 2026 15:15:25 +0530 Subject: [PATCH 01/12] feat(snapshot): add nested CORS iframe capture with depth, cycle, ignore, and recovery controls Replace the single-level processFrame helper with a recursive ProcessFrameTree pipeline that mirrors the canonical Percy CORS iframe spec from percy/percy-nightwatch#869. Adds: - DEFAULT_MAX_FRAME_DEPTH/MAX_ALLOWED_FRAME_DEPTH, ClampFrameDepth, NormalizeIgnoreSelectors, ResolveMaxFrameDepth, ResolveIgnoreSelectors inlined since .NET has no @percy/sdk-utils equivalent. - ENUMERATE_IFRAMES_SCRIPT + IframeInfo to collect iframe metadata in one round-trip per frame context. - ShouldSkipIframe centralizes filtering: data-percy-ignore attribute, ignoreIframeSelectors matches, unsupported / srcdoc / same-origin frames, and frames without data-percy-element-id are dropped early. - ProcessFrameTree recurses cross-origin descendants up to MaxFrameDepth, with a HashSet ancestor-URL chain to break cyclic A->B->A graphs. - Post-switch URL re-check: after SwitchTo().Frame, the loaded document.URL is rechecked against IsUnsupportedIframeSrc to handle late navigations. - PercyContextLostException carries the partial capture so a failed SwitchTo().ParentFrame() unwind still surfaces every frame serialized up to the failure. CaptureCorsIframes wires the helpers into getSerializedDom, replacing the prior ad-hoc top-level iframe loop. Co-Authored-By: Claude Opus 4.7 (1M context) --- Percy/Percy.cs | 537 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 452 insertions(+), 85 deletions(-) diff --git a/Percy/Percy.cs b/Percy/Percy.cs index 98f1731..2e016ff 100644 --- a/Percy/Percy.cs +++ b/Percy/Percy.cs @@ -312,7 +312,37 @@ public static Region CreateRegion( return region; } - private static bool IsUnsupportedIframeSrc(string? src) + // Default cap on nested CORS iframe recursion. Inlined from @percy/sdk-utils + // (DEFAULT_MAX_FRAME_DEPTH) because .NET has no equivalent package. + internal const int DEFAULT_MAX_FRAME_DEPTH = 5; + internal const int MAX_ALLOWED_FRAME_DEPTH = 10; + + // In-browser script that enumerates iframes and reports each frame's src, + // srcdoc, percyElementId, data-percy-ignore presence, and whether it matches + // any ignore selector. Returned as a JSON array (List of Dictionaries + // when read back through Selenium's executor). + internal const string ENUMERATE_IFRAMES_SCRIPT = + "var selectors = arguments[0] || [];" + + "var iframes = document.querySelectorAll('iframe');" + + "var result = [];" + + "for (var i = 0; i < iframes.length; i++) {" + + " var frame = iframes[i];" + + " var matchesIgnore = false;" + + " for (var j = 0; j < selectors.length; j++) {" + + " try { if (frame.matches(selectors[j])) { matchesIgnore = true; break; } } catch (e) {}" + + " }" + + " result.push({" + + " src: frame.src || ''," + + " srcdoc: frame.getAttribute('srcdoc')," + + " percyElementId: frame.getAttribute('data-percy-element-id')," + + " dataPercyIgnore: frame.hasAttribute('data-percy-ignore')," + + " matchesIgnoreSelector: matchesIgnore," + + " index: i" + + " });" + + "}" + + "return result;"; + + internal static bool IsUnsupportedIframeSrc(string? src) { return string.IsNullOrEmpty(src) || src.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase) || @@ -320,8 +350,9 @@ private static bool IsUnsupportedIframeSrc(string? src) src.StartsWith("vbscript:", StringComparison.OrdinalIgnoreCase); } - private static string GetOrigin(string url) + internal static string GetOrigin(string? url) { + if (string.IsNullOrEmpty(url)) return ""; try { Uri uri = new Uri(url); @@ -333,63 +364,428 @@ private static string GetOrigin(string url) } } - private static Dictionary? ProcessFrame( - WebDriver driver, - IWebElement frameElement, - Dictionary? options, - string domJs) + // Clamp user-supplied iframe nesting depth to a safe range. Mirrors + // @percy/sdk-utils clampFrameDepth — anything non-positive falls back to + // the default, anything above MAX_ALLOWED_FRAME_DEPTH is capped. + internal static int ClampFrameDepth(int depth, int defaultMax = DEFAULT_MAX_FRAME_DEPTH) { - // Read attributes while still in parent context — these calls will - // fail if made after switchTo().frame(). - string? frameUrl = frameElement.GetAttribute("src") ?? "unknown-src"; - Log($"processFrame: checking iframe src=\"{frameUrl}\"", "debug"); + if (depth <= 0) return defaultMax; + if (depth > MAX_ALLOWED_FRAME_DEPTH) return MAX_ALLOWED_FRAME_DEPTH; + return depth; + } - string? percyElementId = frameElement.GetAttribute("data-percy-element-id"); - Log($"processFrame: data-percy-element-id=\"{percyElementId}\" for src=\"{frameUrl}\"", "debug"); - if (string.IsNullOrEmpty(percyElementId)) + // Accept either a single string or a list of strings as the + // ignoreIframeSelectors input, normalize to a flat List, and + // strip any non-string / empty entries. + internal static List NormalizeIgnoreSelectors(object? input) + { + var result = new List(); + if (input == null) return result; + if (input is string s) { - Log($"Skipping frame {frameUrl}: no matching percyElementId found", "debug"); - return null; + if (!string.IsNullOrWhiteSpace(s)) result.Add(s); + return result; + } + if (input is System.Collections.IEnumerable enumerable) + { + foreach (var item in enumerable) + { + if (item is string str && !string.IsNullOrWhiteSpace(str)) + result.Add(str); + } + } + return result; + } + + private static int ResolveMaxFrameDepth(Dictionary? options) + { + int? candidate = null; + if (options != null && options.TryGetValue("maxIframeDepth", out object? v) && v != null) + { + if (v is int iv) candidate = iv; + else if (v is long lv) candidate = (int)lv; + else if (int.TryParse(v.ToString(), out int parsed)) candidate = parsed; + } + if (candidate == null && cliConfig != null) + { + try + { + JsonElement config = (JsonElement)cliConfig; + if (config.TryGetProperty("snapshot", out JsonElement snap) && + snap.TryGetProperty("maxIframeDepth", out JsonElement md) && + md.ValueKind == JsonValueKind.Number) + { + candidate = md.GetInt32(); + } + } + catch { /* fall through */ } } + return ClampFrameDepth(candidate ?? DEFAULT_MAX_FRAME_DEPTH); + } + + private static List ResolveIgnoreSelectors(Dictionary? options) + { + object? raw = null; + if (options != null && options.TryGetValue("ignoreIframeSelectors", out object? optVal)) + { + raw = optVal; + } + else if (cliConfig != null) + { + try + { + JsonElement config = (JsonElement)cliConfig; + if (config.TryGetProperty("snapshot", out JsonElement snap) && + snap.TryGetProperty("ignoreIframeSelectors", out JsonElement sel)) + { + if (sel.ValueKind == JsonValueKind.String) + { + raw = sel.GetString(); + } + else if (sel.ValueKind == JsonValueKind.Array) + { + var list = new List(); + foreach (var item in sel.EnumerateArray()) + if (item.ValueKind == JsonValueKind.String) list.Add(item.GetString() ?? ""); + raw = list; + } + } + } + catch { /* fall through */ } + } + return NormalizeIgnoreSelectors(raw); + } + + // Raised inside ProcessFrameTree when SwitchTo().ParentFrame() (or its + // fallback) cannot reliably return us to the parent context. Carries + // whatever frames the recursion managed to capture before the failure + // so callers can still ship a partial result. + public class PercyContextLostException : Exception + { + public List> PartialCapture { get; set; } + public PercyContextLostException(string message, Exception? inner = null) + : base(message, inner) + { + PartialCapture = new List>(); + } + } + + // Metadata for an iframe discovered via ENUMERATE_IFRAMES_SCRIPT. Built from + // the raw Dictionary returned by the in-browser script so the rest of the + // C# code can deal with typed fields instead of dynamic property access. + internal class IframeInfo + { + public string Src = ""; + public string? Srcdoc; + public string? PercyElementId; + public bool DataPercyIgnore; + public bool MatchesIgnoreSelector; + public int Index; + + public static IframeInfo FromDictionary(IDictionary d) + { + var info = new IframeInfo(); + if (d.TryGetValue("src", out object? src) && src != null) info.Src = src.ToString() ?? ""; + if (d.TryGetValue("srcdoc", out object? sd)) info.Srcdoc = sd?.ToString(); + if (d.TryGetValue("percyElementId", out object? pid)) info.PercyElementId = pid?.ToString(); + if (d.TryGetValue("dataPercyIgnore", out object? dpi)) info.DataPercyIgnore = Convert.ToBoolean(dpi); + if (d.TryGetValue("matchesIgnoreSelector", out object? mis)) info.MatchesIgnoreSelector = Convert.ToBoolean(mis); + if (d.TryGetValue("index", out object? idx) && idx != null && int.TryParse(idx.ToString(), out int parsed)) + info.Index = parsed; + return info; + } + } + + // Enumerate iframes in the current frame context. Returns a list of + // IframeInfo. Each entry carries enough metadata for ShouldSkipIframe to + // decide whether to recurse without re-querying the DOM. + private static List EnumerateIframes(WebDriver driver, List ignoreSelectors) + { + var raw = driver.ExecuteScript(ENUMERATE_IFRAMES_SCRIPT, ignoreSelectors); + var result = new List(); + var items = raw as System.Collections.IEnumerable; + if (items == null) return result; + foreach (var item in items) + { + if (item is IDictionary dict) + { + result.Add(IframeInfo.FromDictionary(dict)); + } + else if (item is Dictionary d2) + { + var coerced = new Dictionary(); + foreach (var kv in d2) coerced[kv.Key] = kv.Value; + result.Add(IframeInfo.FromDictionary(coerced)); + } + } + return result; + } + + // Decide whether to skip a discovered iframe, comparing its origin to + // the IMMEDIATE parent's origin (passed in by the caller) rather than + // the top-level page origin — this is how nested CORS iframes get + // detected when the parent itself is cross-origin. + internal static bool ShouldSkipIframe(IframeInfo iframe, string parentOrigin) + { + if (iframe.DataPercyIgnore) + { + Log($"Skipping iframe marked with data-percy-ignore: {iframe.Src}", "debug"); + return true; + } + if (iframe.MatchesIgnoreSelector) + { + Log($"Skipping iframe matching ignoreIframeSelectors: {iframe.Src}", "debug"); + return true; + } + if (string.IsNullOrEmpty(iframe.Src) || IsUnsupportedIframeSrc(iframe.Src)) + { + if (!string.IsNullOrEmpty(iframe.Src)) + Log($"Skipping unsupported iframe src: {iframe.Src}", "debug"); + return true; + } + if (!string.IsNullOrEmpty(iframe.Srcdoc)) + { + Log($"Skipping srcdoc iframe at index {iframe.Index}", "debug"); + return true; + } + string frameOrigin = GetOrigin(iframe.Src); + if (string.IsNullOrEmpty(frameOrigin)) + { + Log($"Skipping iframe with invalid URL: {iframe.Src}", "debug"); + return true; + } + if (frameOrigin == parentOrigin) + { + Log($"Skipping same-origin iframe: {iframe.Src}", "debug"); + return true; + } + if (string.IsNullOrEmpty(iframe.PercyElementId)) + { + Log($"Skipping cross-origin iframe without data-percy-element-id: {iframe.Src}", "debug"); + return true; + } + return false; + } - Dictionary? iframeSnapshot = null; + // Context threaded through recursive ProcessFrameTree calls so we don't + // re-resolve options/depth on every level. + internal class FrameTreeContext + { + public int MaxFrameDepth; + public List IgnoreSelectors = new List(); + public Dictionary SerializeOptions = new Dictionary(); + public string DomJs = ""; + } + + // Switch into the iframe described by `info`, serialize its DOM, then + // recurse into any nested cross-origin iframes. Restores the parent + // context on exit via SwitchTo().ParentFrame(). Bounded by + // ctx.MaxFrameDepth. `ancestorUrls` carries the chain of frame URLs + // above us so cyclic A->B->A graphs terminate after one capture per + // unique URL instead of running to MAX depth. + private static List> ProcessFrameTree( + WebDriver driver, + IframeInfo info, + int depth, + HashSet ancestorUrls, + FrameTreeContext ctx) + { + var collected = new List>(); + if (depth > ctx.MaxFrameDepth) + { + Log($"Reached max iframe nesting depth ({ctx.MaxFrameDepth}); stopping at {info.Src}", "debug"); + return collected; + } + if (ancestorUrls.Contains(info.Src)) + { + Log($"Skipping cyclic iframe ({info.Src} appears in ancestor chain)", "debug"); + return collected; + } + + bool switchedIn = false; + Exception? capturedError = null; try { - driver.SwitchTo().Frame(frameElement); - // Inject Percy DOM into the cross-origin frame context - driver.ExecuteScript(domJs); - // Serialize inside the frame; enableJavaScript=true is required for CORS iframes - var iframeOptions = options != null - ? new Dictionary(options) - : new Dictionary(); - iframeOptions["enableJavaScript"] = true; - var iframeOpts = JsonSerializer.Serialize(iframeOptions); - iframeSnapshot = (Dictionary)driver.ExecuteScript( - $"return PercyDOM.serialize({iframeOpts})"); + Log($"Processing cross-origin iframe (depth {depth}): {info.Src}", "debug"); + + // Look up the iframe element by data-percy-element-id rather than + // index so we tolerate DOM reorders between enumeration and switch. + IWebElement? element = null; + try + { + element = (IWebElement)driver.ExecuteScript( + "return document.querySelector('iframe[data-percy-element-id=\"' + arguments[0] + '\"]');", + info.PercyElementId); + } + catch (Exception e) + { + Log($"Could not resolve iframe element for percyElementId {info.PercyElementId}: {e.Message}", "warn"); + } + if (element == null) + { + Log($"Could not find iframe element with data-percy-element-id: {info.PercyElementId}", "warn"); + return collected; + } + + driver.SwitchTo().Frame(element); + switchedIn = true; + + // Post-switch URL re-check: the frame may have navigated to an + // unsupported URL (about:blank, javascript:, data:, ...) after the + // src attribute was first read. Skip those before serializing. + string? frameUrl = null; + try + { + frameUrl = driver.ExecuteScript("return document.URL")?.ToString(); + } + catch (Exception e) + { + Log($"Could not read document.URL inside frame {info.Src}: {e.Message}", "debug"); + } + if (!string.IsNullOrEmpty(frameUrl) && IsUnsupportedIframeSrc(frameUrl)) + { + Log($"Skipping iframe whose document loaded an unsupported URL: {frameUrl}", "debug"); + return collected; + } + if (string.IsNullOrEmpty(frameUrl)) frameUrl = info.Src; + + // Inject PercyDOM into the cross-origin frame and serialize. + // enableJavaScript=true is mandatory: it disables the in-DOM + // iframe serializer (we recurse manually instead). + driver.ExecuteScript(ctx.DomJs); + var serializeOptions = new Dictionary(ctx.SerializeOptions) + { + ["enableJavaScript"] = true + }; + string optsJson = JsonSerializer.Serialize(serializeOptions); + Dictionary? snapshot = null; + try + { + snapshot = (Dictionary)driver.ExecuteScript( + $"return PercyDOM.serialize({optsJson})"); + } + catch (Exception e) + { + Log($"Serialization failed for frame {info.Src}: {e.Message}", "warn"); + return collected; + } + + if (snapshot == null) + { + Log($"Serialization returned empty result for frame: {info.Src}", "warn"); + return collected; + } + + Log($"Captured cross-origin iframe (depth {depth}): {frameUrl}", "debug"); + + collected.Add(new Dictionary + { + ["frameUrl"] = frameUrl, + ["iframeData"] = new Dictionary { ["percyElementId"] = info.PercyElementId! }, + ["iframeSnapshot"] = snapshot + }); + + // Recurse into nested cross-origin iframes if we haven't hit the cap. + if (depth < ctx.MaxFrameDepth) + { + string currentOrigin = GetOrigin(frameUrl); + var children = EnumerateIframes(driver, ctx.IgnoreSelectors); + var nextAncestors = new HashSet(ancestorUrls); + nextAncestors.Add(frameUrl); + nextAncestors.Add(info.Src); + foreach (var child in children) + { + if (ShouldSkipIframe(child, currentOrigin)) continue; + var nested = ProcessFrameTree(driver, child, depth + 1, nextAncestors, ctx); + if (nested.Count > 0) collected.AddRange(nested); + } + } + + return collected; + } + catch (PercyContextLostException ctxLost) + { + // Merge any partial capture into the inner exception's payload + // before propagating, so the top-level caller gets every frame + // serialized before the context was lost. + if (ctxLost.PartialCapture != null && ctxLost.PartialCapture.Count > 0) + { + collected.AddRange(ctxLost.PartialCapture); + } + ctxLost.PartialCapture = collected; + throw; } catch (Exception e) { - Log($"Failed to process cross-origin frame {frameUrl}: {e.Message}", "error"); - throw new Exception($"Failed to process cross-origin frame {frameUrl}", e); + Log($"Failed to process cross-origin iframe {info.Src}: {e.Message}", "warn"); + capturedError = e; + return collected; } finally { - try + if (switchedIn) { - driver.SwitchTo().DefaultContent(); + try + { + driver.SwitchTo().ParentFrame(); + } + catch (Exception e) + { + Log($"Failed to switch back to parent frame: {e.Message}", "warn"); + try { driver.SwitchTo().DefaultContent(); } catch { /* ignore */ } + if (depth > 1) + { + var err = new PercyContextLostException( + $"Lost parent frame context: {e.Message}", capturedError); + err.PartialCapture = collected; + throw err; + } + } } - catch (Exception err) + } + } + + // Walk top-level iframes, recurse via ProcessFrameTree, and roll up the + // results. Mirrors @percy/sdk-utils captureCorsIframes. + private static List> CaptureCorsIframes( + WebDriver driver, + string pageUrl, + FrameTreeContext ctx) + { + var corsIframes = new List>(); + try + { + var topLevel = EnumerateIframes(driver, ctx.IgnoreSelectors); + if (topLevel.Count == 0) return corsIframes; + + Log($"Found {topLevel.Count} top-level iframe(s)", "debug"); + string pageOrigin = GetOrigin(pageUrl); + + foreach (var iframe in topLevel) { - Log($"Fatal: could not exit iframe context after processing \"{frameUrl}\". Driver may be unstable. {err.Message}", "error"); + if (ShouldSkipIframe(iframe, pageOrigin)) continue; + try + { + var entries = ProcessFrameTree(driver, iframe, 1, + new HashSet { pageUrl }, ctx); + if (entries.Count > 0) corsIframes.AddRange(entries); + } + catch (PercyContextLostException ctxLost) + { + Log("Aborting further nested CORS capture due to lost frame context", "warn"); + if (ctxLost.PartialCapture != null && ctxLost.PartialCapture.Count > 0) + corsIframes.AddRange(ctxLost.PartialCapture); + break; + } } - } - return new Dictionary + Log($"Captured {corsIframes.Count} cross-origin iframe(s)", "debug"); + } + catch (Exception e) { - { "iframeData", new Dictionary { { "percyElementId", percyElementId } } }, - { "iframeSnapshot", iframeSnapshot }, - { "frameUrl", frameUrl } - }; + Log($"Error capturing CORS iframes: {e.Message}", "warn"); + } + return corsIframes; } private static dynamic getSerializedDom( @@ -403,60 +799,31 @@ private static dynamic getSerializedDom( var domSnapshot = (Dictionary)driver.ExecuteScript(script); domSnapshot["cookies"] = cookies; - // Process CORS iframes when DOM script is available + // Process CORS iframes when DOM script is available. Uses the + // nested ProcessFrameTree pipeline so we capture multi-level + // cross-origin nesting, with cycle and depth guards, plus + // data-percy-ignore / ignoreIframeSelectors filtering. if (!string.IsNullOrEmpty(domJs)) { try { - string pageOrigin = GetOrigin(driver.Url); - var iframes = driver.FindElements(By.TagName("iframe")); - if (iframes.Count > 0) + var ctx = new FrameTreeContext { - var processedFrames = new List>(); - foreach (IWebElement frame in iframes) - { - string? frameSrc = frame.GetAttribute("src"); - if (IsUnsupportedIframeSrc(frameSrc)) - continue; - - string frameOrigin; - try - { - Uri baseUri = new Uri(driver.Url); - Uri resolvedUri = new Uri(baseUri, frameSrc); - frameOrigin = GetOrigin(resolvedUri.ToString()); - } - catch (Exception e) - { - Log($"Skipping iframe \"{frameSrc}\": {e.Message}", "debug"); - continue; - } - - if (frameOrigin == pageOrigin) - continue; - - try - { - var result = ProcessFrame(driver, frame, options, domJs); - if (result != null) - processedFrames.Add(result); - } - catch (Exception e) - { - Log($"Skipping frame \"{frameSrc}\" due to error: {e.Message}", "debug"); - if (e.Message.Contains("Fatal")) - throw; - } - } - if (processedFrames.Count > 0) - domSnapshot["corsIframes"] = processedFrames; - } + MaxFrameDepth = ResolveMaxFrameDepth(options), + IgnoreSelectors = ResolveIgnoreSelectors(options), + SerializeOptions = options != null + ? new Dictionary(options) + : new Dictionary(), + DomJs = domJs! + }; + string pageUrl = driver.Url ?? ""; + var corsIframes = CaptureCorsIframes(driver, pageUrl, ctx); + if (corsIframes.Count > 0) + domSnapshot["corsIframes"] = corsIframes; } catch (Exception e) { Log($"Failed to process cross-origin iframes: {e.Message}", "debug"); - if (e.Message.Contains("Fatal")) - throw; } } From e94c4b5d6819c9f094fd60cd78d9e0e82beff09c Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Mon, 11 May 2026 15:16:23 +0530 Subject: [PATCH 02/12] feat(snapshot): expose closed shadow DOM via CDP for Chrome drivers Mirrors percy/percy-playwright#609. Walks the CDP DOM tree (DOM.getDocument with depth=-1 and pierce=true), collects backendNodeId pairs for every closed shadow root, resolves both sides via DOM.resolveNode, then uses Runtime.callFunctionOn to register each shadow root in a window-bound WeakMap (window.__percyClosedShadowRoots) that PercyDOM.serialize() reads during cloning. Nodes inside child frame documents are skipped because their execution contexts don't share the WeakMap. Wired into Snapshot() before serialization and into the responsive capture reload path so the WeakMap survives page.reload(). No-op on non-Chrome drivers and when ExecuteCdpCommand isn't available, so existing Firefox / non-Chrome test paths are unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) --- Percy/Percy.cs | 144 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/Percy/Percy.cs b/Percy/Percy.cs index 2e016ff..32fc29c 100644 --- a/Percy/Percy.cs +++ b/Percy/Percy.cs @@ -788,6 +788,142 @@ private static List> CaptureCorsIframes( return corsIframes; } + // Use Chrome DevTools Protocol to discover closed shadow roots (which are + // invisible to JS — `el.shadowRoot` is null) and expose each one to the + // page via a window-bound WeakMap. PercyDOM.serialize() looks up the map + // when cloning shadow hosts so the closed content is included in the + // snapshot. No-op on non-Chrome drivers / when CDP isn't reachable. + // Re-prime after page reloads — the WeakMap lives on `window` and is + // wiped along with the rest of the document. + internal static void ExposeClosedShadowRoots(WebDriver driver) + { + if (!IsChromeBrowser(driver)) return; + MethodInfo? executeCdp = driver.GetType().GetMethod( + "ExecuteCdpCommand", new[] { typeof(string), typeof(Dictionary) }); + if (executeCdp == null) + { + Log("ExecuteCdpCommand unavailable on driver; skipping closed shadow root exposure", "debug"); + return; + } + + try + { + executeCdp.Invoke(driver, new object[] { "DOM.enable", new Dictionary() }); + + var getDocResult = executeCdp.Invoke(driver, new object[] { + "DOM.getDocument", + new Dictionary { ["depth"] = -1, ["pierce"] = true } + }); + if (getDocResult == null) return; + + // The .NET ExecuteCdpCommand returns a Dictionary + // whose values are JSON-decoded. Pull out the root node and walk it. + var docDict = getDocResult as IDictionary; + if (docDict == null || !docDict.TryGetValue("root", out object? rootObj) || rootObj == null) + return; + + var closedPairs = new List<(long host, long shadow)>(); + CollectClosedShadowRoots(rootObj, closedPairs); + + if (closedPairs.Count == 0) return; + Log($"Found {closedPairs.Count} closed shadow root(s), exposing via CDP", "debug"); + + // Prime the WeakMap on the page first (same key as PercyDOM uses). + driver.ExecuteScript( + "window.__percyClosedShadowRoots = window.__percyClosedShadowRoots || new WeakMap();"); + + foreach (var pair in closedPairs) + { + try + { + var hostResolve = executeCdp.Invoke(driver, new object[] { + "DOM.resolveNode", + new Dictionary { ["backendNodeId"] = pair.host } + }) as IDictionary; + var shadowResolve = executeCdp.Invoke(driver, new object[] { + "DOM.resolveNode", + new Dictionary { ["backendNodeId"] = pair.shadow } + }) as IDictionary; + string? hostObjectId = ExtractObjectId(hostResolve); + string? shadowObjectId = ExtractObjectId(shadowResolve); + if (hostObjectId == null || shadowObjectId == null) continue; + + var args = new List { + new Dictionary { ["objectId"] = shadowObjectId } + }; + executeCdp.Invoke(driver, new object[] { + "Runtime.callFunctionOn", + new Dictionary { + ["functionDeclaration"] = "function(shadowRoot) { window.__percyClosedShadowRoots.set(this, shadowRoot); }", + ["objectId"] = hostObjectId, + ["arguments"] = args + } + }); + } + catch (Exception e) + { + Log($"Failed to expose one closed shadow root: {e.Message}", "debug"); + } + } + } + catch (Exception e) + { + Log($"Could not expose closed shadow roots via CDP: {e.Message}", "debug"); + } + } + + // Walk a CDP DOM node tree collecting (hostBackendNodeId, + // shadowBackendNodeId) pairs for every closed shadow root. Skips nodes + // inside child frame documents — closed shadow DOM inside cross-frame + // documents lives in a different execution context whose window has no + // WeakMap to write into. + private static void CollectClosedShadowRoots(object nodeObj, List<(long, long)> pairs) + { + var node = nodeObj as IDictionary; + if (node == null) return; + if (node.ContainsKey("contentDocument")) return; // child frame document + + if (node.TryGetValue("shadowRoots", out object? srRaw) && srRaw is System.Collections.IEnumerable shadowRoots) + { + long? hostId = TryGetLong(node, "backendNodeId"); + foreach (var srItem in shadowRoots) + { + var sr = srItem as IDictionary; + if (sr == null) continue; + string? type = sr.TryGetValue("shadowRootType", out object? t) ? t?.ToString() : null; + long? shadowId = TryGetLong(sr, "backendNodeId"); + if (type == "closed" && hostId.HasValue && shadowId.HasValue) + { + pairs.Add((hostId.Value, shadowId.Value)); + } + CollectClosedShadowRoots(sr, pairs); + } + } + + if (node.TryGetValue("children", out object? childrenRaw) && childrenRaw is System.Collections.IEnumerable children) + { + foreach (var child in children) CollectClosedShadowRoots(child, pairs); + } + } + + private static long? TryGetLong(IDictionary dict, string key) + { + if (!dict.TryGetValue(key, out object? raw) || raw == null) return null; + if (raw is long l) return l; + if (raw is int i) return i; + if (long.TryParse(raw.ToString(), out long parsed)) return parsed; + return null; + } + + private static string? ExtractObjectId(IDictionary? resolveResult) + { + if (resolveResult == null) return null; + if (!resolveResult.TryGetValue("object", out object? obj)) return null; + var objDict = obj as IDictionary; + if (objDict == null) return null; + return objDict.TryGetValue("objectId", out object? id) ? id?.ToString() : null; + } + private static dynamic getSerializedDom( WebDriver driver, object cookies, @@ -1059,6 +1195,9 @@ public static List> CaptureResponsiveDom(WebDriver dr { driver.ExecuteScript(GetPercyDOM()); } + // Re-prime the closed shadow root WeakMap — page reload + // creates a fresh document and the previous map is gone. + ExposeClosedShadowRoots(driver); driver.ExecuteScript("PercyDOM.waitForResize()"); resizeCount = 0; } @@ -1130,6 +1269,11 @@ public class Options : Dictionary {} if (_dom == null) _dom = GetPercyDOM(); + // Expose closed shadow roots via CDP before serializing so + // PercyDOM.serialize() can pick them up through the WeakMap. + // Non-Chrome browsers and missing ExecuteCdpCommand are no-ops. + ExposeClosedShadowRoots(driver); + var cookies = driver.Manage().Cookies.AllCookies; string opts = JsonSerializer.Serialize(options); dynamic domSnapshot = null; From eb87a77a6bce6778f5f04e323f51a1a2bdb1ab11 Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Mon, 11 May 2026 15:16:31 +0530 Subject: [PATCH 03/12] chore(test): add unit tests for CORS iframe helpers Covers the inlined helpers (GetOrigin, IsUnsupportedIframeSrc, ClampFrameDepth, NormalizeIgnoreSelectors), the ShouldSkipIframe skip matrix (data-percy-ignore, ignoreIframeSelectors, unsupported src, srcdoc, same-origin, missing percyElementId, and immediate-parent origin comparison), and the PercyContextLostException carrier. Tests rely on the existing InternalsVisibleTo("Percy.Test") in AssemblyInfo.cs and use reflection for the private IframeInfo / ShouldSkipIframe symbols so the production API stays sealed. Co-Authored-By: Claude Opus 4.7 (1M context) --- Percy.Test/CorsIframesTest.cs | 178 ++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 Percy.Test/CorsIframesTest.cs diff --git a/Percy.Test/CorsIframesTest.cs b/Percy.Test/CorsIframesTest.cs new file mode 100644 index 0000000..bcbae8b --- /dev/null +++ b/Percy.Test/CorsIframesTest.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Xunit; + +namespace PercyIO.Selenium.Tests +{ + // Unit tests for the CORS iframe + closed shadow DOM helpers added to + // Percy.cs. These don't require the Percy CLI or a real browser; they + // exercise the pure-C# helpers via reflection where they are internal. + public class CorsIframesTest + { + // -- GetOrigin ----------------------------------------------------------- + + [Fact] + public void GetOrigin_ExtractsSchemeAndAuthority() + { + Assert.Equal("https://example.com", Percy.GetOrigin("https://example.com/some/path?q=1")); + Assert.Equal("http://localhost:8000", Percy.GetOrigin("http://localhost:8000/page")); + } + + [Fact] + public void GetOrigin_ReturnsEmptyForInvalidOrEmptyUrl() + { + Assert.Equal("", Percy.GetOrigin("")); + Assert.Equal("", Percy.GetOrigin(null)); + Assert.Equal("", Percy.GetOrigin("not a url")); + } + + // -- IsUnsupportedIframeSrc --------------------------------------------- + + [Theory] + [InlineData("javascript:void(0)", true)] + [InlineData("JAVASCRIPT:alert(1)", true)] + [InlineData("data:text/html,

", true)] + [InlineData("vbscript:foo()", true)] + [InlineData("", true)] + [InlineData(null, true)] + [InlineData("https://example.com/x", false)] + [InlineData("http://localhost/page", false)] + public void IsUnsupportedIframeSrc_RecognizesUnsupportedSchemes(string? src, bool expected) + { + Assert.Equal(expected, Percy.IsUnsupportedIframeSrc(src)); + } + + // -- ClampFrameDepth ----------------------------------------------------- + + [Theory] + [InlineData(0, Percy.DEFAULT_MAX_FRAME_DEPTH)] + [InlineData(-5, Percy.DEFAULT_MAX_FRAME_DEPTH)] + [InlineData(3, 3)] + [InlineData(100, Percy.MAX_ALLOWED_FRAME_DEPTH)] + public void ClampFrameDepth_AppliesBounds(int input, int expected) + { + Assert.Equal(expected, Percy.ClampFrameDepth(input)); + } + + // -- NormalizeIgnoreSelectors ------------------------------------------- + + [Fact] + public void NormalizeIgnoreSelectors_AcceptsSingleString() + { + var result = Percy.NormalizeIgnoreSelectors(".ad"); + Assert.Equal(new List { ".ad" }, result); + } + + [Fact] + public void NormalizeIgnoreSelectors_AcceptsArrayAndDropsEmpties() + { + var result = Percy.NormalizeIgnoreSelectors(new List { ".ad", "", null!, "iframe[data-ad]" }); + Assert.Equal(new List { ".ad", "iframe[data-ad]" }, result); + } + + [Fact] + public void NormalizeIgnoreSelectors_ReturnsEmptyOnNull() + { + Assert.Empty(Percy.NormalizeIgnoreSelectors(null)); + } + + // -- ShouldSkipIframe ---------------------------------------------------- + // + // The skip helper is internal — call via reflection so tests live in the + // same project without changing visibility on production code. + private static bool InvokeShouldSkipIframe(object iframeInfo, string parentOrigin) + { + MethodInfo method = typeof(Percy).GetMethod( + "ShouldSkipIframe", BindingFlags.Static | BindingFlags.NonPublic)!; + return (bool)method.Invoke(null, new[] { iframeInfo, parentOrigin })!; + } + + private static object MakeIframeInfo(string src, string? percyElementId, + bool dataPercyIgnore = false, bool matchesIgnoreSelector = false, string? srcdoc = null) + { + Type t = typeof(Percy).GetNestedType("IframeInfo", BindingFlags.NonPublic)!; + object info = Activator.CreateInstance(t)!; + t.GetField("Src")!.SetValue(info, src); + t.GetField("PercyElementId")!.SetValue(info, percyElementId); + t.GetField("DataPercyIgnore")!.SetValue(info, dataPercyIgnore); + t.GetField("MatchesIgnoreSelector")!.SetValue(info, matchesIgnoreSelector); + t.GetField("Srcdoc")!.SetValue(info, srcdoc); + return info; + } + + [Fact] + public void ShouldSkipIframe_SkipsDataPercyIgnore() + { + var info = MakeIframeInfo("https://cross.example.com", "p-1", dataPercyIgnore: true); + Assert.True(InvokeShouldSkipIframe(info, "https://parent.example.com")); + } + + [Fact] + public void ShouldSkipIframe_SkipsMatchesIgnoreSelector() + { + var info = MakeIframeInfo("https://ads.example.com", "p-2", matchesIgnoreSelector: true); + Assert.True(InvokeShouldSkipIframe(info, "https://parent.example.com")); + } + + [Fact] + public void ShouldSkipIframe_SkipsUnsupportedSrc() + { + var info = MakeIframeInfo("javascript:void(0)", "p-3"); + Assert.True(InvokeShouldSkipIframe(info, "https://parent.example.com")); + } + + [Fact] + public void ShouldSkipIframe_SkipsSrcdoc() + { + var info = MakeIframeInfo("https://cross.example.com", "p-4", srcdoc: "

x

"); + Assert.True(InvokeShouldSkipIframe(info, "https://parent.example.com")); + } + + [Fact] + public void ShouldSkipIframe_SkipsSameOrigin() + { + var info = MakeIframeInfo("https://parent.example.com/iframe", "p-5"); + Assert.True(InvokeShouldSkipIframe(info, "https://parent.example.com")); + } + + [Fact] + public void ShouldSkipIframe_SkipsMissingPercyElementId() + { + var info = MakeIframeInfo("https://cross.example.com", percyElementId: null); + Assert.True(InvokeShouldSkipIframe(info, "https://parent.example.com")); + } + + [Fact] + public void ShouldSkipIframe_AllowsCrossOriginWithPercyElementId() + { + var info = MakeIframeInfo("https://cross.example.com/x", "p-6"); + Assert.False(InvokeShouldSkipIframe(info, "https://parent.example.com")); + } + + // Origin is compared to the IMMEDIATE parent, not the top-level page — + // a frame whose origin matches an ancestor higher up the chain should + // still be considered cross-origin from its parent. + [Fact] + public void ShouldSkipIframe_ComparesAgainstImmediateParentOrigin() + { + // Parent = http://b, child src points back to http://a (the top page). + // From the parent's perspective the child is cross-origin and should + // be captured. + var info = MakeIframeInfo("http://a.example.com/page", "p-7"); + Assert.False(InvokeShouldSkipIframe(info, "http://b.example.com")); + } + + // -- PercyContextLostException ------------------------------------------- + + [Fact] + public void PercyContextLostException_CarriesPartialCapture() + { + var ex = new Percy.PercyContextLostException("ctx lost"); + ex.PartialCapture.Add(new Dictionary { ["frameUrl"] = "http://a/" }); + Assert.Single(ex.PartialCapture); + Assert.Equal("http://a/", ex.PartialCapture[0]["frameUrl"]); + } + } +} From 202a9eeef09f8a5467ef2fcfb184b210cd0373c9 Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Fri, 22 May 2026 17:47:37 +0530 Subject: [PATCH 04/12] Refresh package-lock.json Co-Authored-By: Claude Opus 4.7 (1M context) --- package-lock.json | 467 +++++++++++++++++----------------------------- 1 file changed, 175 insertions(+), 292 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08c8f1b..b67e7e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "devDependencies": { - "@percy/cli": "^1.31.10-beta.2" + "@percy/cli": "^1.31.10" } }, "node_modules/@babel/code-frame": { @@ -13,7 +13,6 @@ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", @@ -28,7 +27,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -38,7 +36,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -52,7 +49,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -62,7 +58,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -72,22 +67,21 @@ } }, "node_modules/@percy/cli": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/cli/-/cli-1.31.10-beta.2.tgz", - "integrity": "sha512-l1oZ1D+NZiyxJXQXbNhFTY7yhzzcKwtHh7yzr1C5ll7JoAQF2U50HonwqRlxrlHLFIZumvWxGNmBlHCAwPQcqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@percy/cli-app": "1.31.10-beta.2", - "@percy/cli-build": "1.31.10-beta.2", - "@percy/cli-command": "1.31.10-beta.2", - "@percy/cli-config": "1.31.10-beta.2", - "@percy/cli-doctor": "1.31.10-beta.2", - "@percy/cli-exec": "1.31.10-beta.2", - "@percy/cli-snapshot": "1.31.10-beta.2", - "@percy/cli-upload": "1.31.10-beta.2", - "@percy/client": "1.31.10-beta.2", - "@percy/logger": "1.31.10-beta.2" + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/cli/-/cli-1.31.13.tgz", + "integrity": "sha512-QpNpqt4rpmQMOixvWzds2bMKaNT64cUNsELokM0jAP8wSbKZLAsvWddM24fZTqsKXpm4peBpzO73S3bwV0Ep5w==", + "dev": true, + "dependencies": { + "@percy/cli-app": "1.31.13", + "@percy/cli-build": "1.31.13", + "@percy/cli-command": "1.31.13", + "@percy/cli-config": "1.31.13", + "@percy/cli-doctor": "1.31.13", + "@percy/cli-exec": "1.31.13", + "@percy/cli-snapshot": "1.31.13", + "@percy/cli-upload": "1.31.13", + "@percy/client": "1.31.13", + "@percy/logger": "1.31.13" }, "bin": { "percy": "bin/run.cjs" @@ -97,42 +91,39 @@ } }, "node_modules/@percy/cli-app": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/cli-app/-/cli-app-1.31.10-beta.2.tgz", - "integrity": "sha512-3Nd/SJH7/7pM9vXO7W57xiq4X6O856AJOUjDU4/7loSULCNh2X8DWSJFpTDZc3FzyiUYZTUf+VlKuH5WvTb5bw==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/cli-app/-/cli-app-1.31.13.tgz", + "integrity": "sha512-onVbiQkG2FaAnP9rw1OtCR/8OkD0lo/S8Cz56Qe3sln/kJtfEC1VAsTq9x/e3caeNFPh7aObF78o8BVvIexcNQ==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.10-beta.2", - "@percy/cli-exec": "1.31.10-beta.2" + "@percy/cli-command": "1.31.13", + "@percy/cli-exec": "1.31.13" }, "engines": { "node": ">=14" } }, "node_modules/@percy/cli-build": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/cli-build/-/cli-build-1.31.10-beta.2.tgz", - "integrity": "sha512-88pWeQ8TMVp6KuiA9oom8YuFbqu55C4V4tZJAIpILpOVUP4FSaWlzXoh18kvi+s935AU4lJIfN+HgeF90FA/3Q==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/cli-build/-/cli-build-1.31.13.tgz", + "integrity": "sha512-tsAr+pzGe5LqRNJiB4CBo7YDBXqH/uXm/89iovwor8wkqDgHR3DfC8u5+GgNNbfazEG2nDx4l3QmmOvN+APARw==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.10-beta.2" + "@percy/cli-command": "1.31.13" }, "engines": { "node": ">=14" } }, "node_modules/@percy/cli-command": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/cli-command/-/cli-command-1.31.10-beta.2.tgz", - "integrity": "sha512-ezMsAYiJzg4nQ28x1nbIWxLe39giiGBcUMAzggdZa7VwI1JYd/huv3+bXPdIM0LDe569wBa5Vnxq0kydxod/Aw==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/cli-command/-/cli-command-1.31.13.tgz", + "integrity": "sha512-pGcs0yPocP3bXqpkzS1UThoQ+8ayVDXLBtpSU+/7N0Rnj6R0eUD1xWggobPT7pY1EOrWqKMEzb67scEvrSIq7Q==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/config": "1.31.10-beta.2", - "@percy/core": "1.31.10-beta.2", - "@percy/logger": "1.31.10-beta.2" + "@percy/config": "1.31.13", + "@percy/core": "1.31.13", + "@percy/logger": "1.31.13" }, "bin": { "percy-cli-readme": "bin/readme.js" @@ -142,28 +133,30 @@ } }, "node_modules/@percy/cli-config": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/cli-config/-/cli-config-1.31.10-beta.2.tgz", - "integrity": "sha512-T1tSc7t97doA+6rUIR8PEHA+NDfiDKiwGofJhQWiyvqu2Zvuthh3zj/wewVkpKzfDz94TRQE1ONe0ErihaXBgw==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/cli-config/-/cli-config-1.31.13.tgz", + "integrity": "sha512-t0K7SYCeWp+UrRMqrTmzZiidBTxWkAvNqCyrExa5yQy3GJXyYqhygqymfP8Y5+8e2SbIGBoXZQW0fIzoSc83ww==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.10-beta.2" + "@percy/cli-command": "1.31.13" }, "engines": { "node": ">=14" } }, "node_modules/@percy/cli-doctor": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/cli-doctor/-/cli-doctor-1.31.10-beta.2.tgz", - "integrity": "sha512-CIi0WBJlNaf3AYgBU9rg9OPTULKrDgqgps0ZrqG6EnfyBIDuBeQvYzqhVdX9fGbUqTwYWnX3Esgx5buSX56JQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@percy/cli-command": "1.31.10-beta.2", - "@percy/core": "1.31.10-beta.2", - "@percy/logger": "1.31.10-beta.2", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/cli-doctor/-/cli-doctor-1.31.13.tgz", + "integrity": "sha512-sX+VNyyTZ3viFW75NPFYMTjD+CHraD43VJ7K5Mpt4hq2IR8LS1J0C37kAiMJnTwFHeeXpaaIsCxVrKiS+jHvOQ==", + "dev": true, + "dependencies": { + "@percy/cli-command": "1.31.13", + "@percy/client": "1.31.13", + "@percy/config": "1.31.13", + "@percy/core": "1.31.13", + "@percy/env": "1.31.13", + "@percy/logger": "1.31.13", + "@percy/monitoring": "1.31.13", "minimatch": "^9.0.0", "ws": "^8.17.1" }, @@ -172,14 +165,13 @@ } }, "node_modules/@percy/cli-exec": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/cli-exec/-/cli-exec-1.31.10-beta.2.tgz", - "integrity": "sha512-3Mhlo9n+KetbDkQd5ADKkmt+xkuahb2cGi/G2vWG82l/0/Myj6dNJ5sa5Y6HsANg85Tynfh1bUxHK27/io0RfA==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/cli-exec/-/cli-exec-1.31.13.tgz", + "integrity": "sha512-93w98Mejp2V7vuXgvEdwbJ4odHa3KHHJ6yrWkvydhKe0FmjoEnH20fYpZ1661Rh6YPTm61a/VvspoWEMD7E+5g==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.10-beta.2", - "@percy/logger": "1.31.10-beta.2", + "@percy/cli-command": "1.31.13", + "@percy/logger": "1.31.13", "cross-spawn": "^7.0.3", "which": "^2.0.2" }, @@ -188,13 +180,12 @@ } }, "node_modules/@percy/cli-snapshot": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/cli-snapshot/-/cli-snapshot-1.31.10-beta.2.tgz", - "integrity": "sha512-H9eOiQqEc4y+TEj2wr1NXiPz4o/MI6SfODLIXIC1dgzkyceSO9c+3z+Au74938VbeHHpVkGxAjdPFSjmaiUI1w==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/cli-snapshot/-/cli-snapshot-1.31.13.tgz", + "integrity": "sha512-k9gG8ImwbcLZPeLtanBETdT8qWvAIKPcZXJ0P8OnoL+onyqEbT/BuYNTY7tlezWDg5utJFwT4LczOqAnHdS0AA==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.10-beta.2", + "@percy/cli-command": "1.31.13", "yaml": "^2.0.0" }, "engines": { @@ -202,13 +193,12 @@ } }, "node_modules/@percy/cli-upload": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/cli-upload/-/cli-upload-1.31.10-beta.2.tgz", - "integrity": "sha512-TTiA0F2TwxbtdMPIxgsxSThRPHAz+iN7PmvlWRp6NJ7LzDbAKswyzfev+7mBosU5R2nY7K2WwMHsADf4/M8OKg==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/cli-upload/-/cli-upload-1.31.13.tgz", + "integrity": "sha512-+/HkIGWqHH/EJmbxATXO0N8XnwtXrhnePDKFZtWx7JrpaZXxRXI96NGL7/mjKQ2sAQKPSGvbbUAB++UoFZSnYg==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.10-beta.2", + "@percy/cli-command": "1.31.13", "fast-glob": "^3.2.11", "image-size": "^1.0.0" }, @@ -217,15 +207,14 @@ } }, "node_modules/@percy/client": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/client/-/client-1.31.10-beta.2.tgz", - "integrity": "sha512-bY47kAHWaxf3htp4HS87ySj3WIJ6c0uBXKVqa34yPnnWvVKgH8hOmSrQZq2/03wf1i/ZUyw4uiC/Fj9/EpXYkA==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/client/-/client-1.31.13.tgz", + "integrity": "sha512-eFoUacW2GolCm3rL8oS1QyWD6b4UWds9HrMbtp6OJWUr6sQ8bcOemjr3M1YpkDsV2wGC0ikLISQxU8XrTxKDhA==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/config": "1.31.10-beta.2", - "@percy/env": "1.31.10-beta.2", - "@percy/logger": "1.31.10-beta.2", + "@percy/config": "1.31.13", + "@percy/env": "1.31.13", + "@percy/logger": "1.31.13", "pac-proxy-agent": "^7.0.2", "pako": "^2.1.0" }, @@ -234,13 +223,12 @@ } }, "node_modules/@percy/config": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/config/-/config-1.31.10-beta.2.tgz", - "integrity": "sha512-X0Gzu0l6uwZlyKDBDPlIZBnn8QbTc5XPM05ST4gCO0stMAun+N8zbxxegh23ZTR/jtYKz22GRnWojA1wCe5xkg==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/config/-/config-1.31.13.tgz", + "integrity": "sha512-gxd292C8+v/psxYJNfId2gwhlIGjvRGGOTCw9gBTb/eBT8cZirVLHq+d/j2nqPxCDC7JzZqChWH4TkVkTr/Xwg==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/logger": "1.31.10-beta.2", + "@percy/logger": "1.31.13", "ajv": "^8.6.2", "cosmiconfig": "^8.0.0", "yaml": "^2.0.0" @@ -250,19 +238,18 @@ } }, "node_modules/@percy/core": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/core/-/core-1.31.10-beta.2.tgz", - "integrity": "sha512-nNIpejojfsB45AWpvoz3uuD9EXNGM6g/B8TU/Xr4G7R8OCjUsfaMKCKKcOu5Ivdj7930Om1Yjx6fNMEQ67txPg==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/core/-/core-1.31.13.tgz", + "integrity": "sha512-SFVw/skIpcFecn8V3emAhmqqOgsX3K9toamMhhVay9FtAoy/NmsV+WuvP3IsAvujepY8ApZe2Zi5CuzZbmDy0w==", "dev": true, "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@percy/client": "1.31.10-beta.2", - "@percy/config": "1.31.10-beta.2", - "@percy/dom": "1.31.10-beta.2", - "@percy/logger": "1.31.10-beta.2", - "@percy/monitoring": "1.31.10-beta.2", - "@percy/webdriver-utils": "1.31.10-beta.2", + "dependencies": { + "@percy/client": "1.31.13", + "@percy/config": "1.31.13", + "@percy/dom": "1.31.13", + "@percy/logger": "1.31.13", + "@percy/monitoring": "1.31.13", + "@percy/webdriver-utils": "1.31.13", "content-disposition": "^0.5.4", "cross-spawn": "^7.0.3", "extract-zip": "^2.0.1", @@ -277,48 +264,47 @@ }, "engines": { "node": ">=14" + }, + "optionalDependencies": { + "@percy/cli-doctor": "1.31.13" } }, "node_modules/@percy/dom": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/dom/-/dom-1.31.10-beta.2.tgz", - "integrity": "sha512-n/tOlyqdNnhtW9TEtCGzSdnR0ibfOdAFVNqEjk0HtIeMTL3NWGP15ollK78CMno2SWg41rgg8/fM2K3eT5l0Tg==", - "dev": true, - "license": "MIT" + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/dom/-/dom-1.31.13.tgz", + "integrity": "sha512-5v/ybb2f4aQLUdsbQzTuHzQNAP5zcqn/aO18k2AjvnNdxNmtLC3XwMaSMznImQAXCGsSqsmrJTGtrYP3pbTPYw==", + "dev": true }, "node_modules/@percy/env": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/env/-/env-1.31.10-beta.2.tgz", - "integrity": "sha512-ZrD1BxJd87osIU5AszK5sKFQ/BYrz7mok993+gWMQTq3KuEu57pjzOTp3zbVDivkiALHopMan9KIEpofHXeZ2Q==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/env/-/env-1.31.13.tgz", + "integrity": "sha512-oYEPbZwEtRFfuso+V1lnAZkZZC1gSk+H+//AdLU5De1+h2jxYW14qc1bpW707ZSina5DY5C2tcbsHNV4lcYI/A==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/logger": "1.31.10-beta.2" + "@percy/logger": "1.31.13" }, "engines": { "node": ">=14" } }, "node_modules/@percy/logger": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/logger/-/logger-1.31.10-beta.2.tgz", - "integrity": "sha512-xUVTzmdoYSBmz2s1Pihi/lxYDz7azP9SFnA1datmKrKgj2ZpjUS+NNFXL6yneKJdW4zvuQu+L85uAkAnYrKGcQ==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/logger/-/logger-1.31.13.tgz", + "integrity": "sha512-ym8Viw3mAGv00afk4NwaODBXLcS7/pPtpa/RSPS0knToPbCvN4/aE9PoZ99M8UbKY02ZFqGAbBNL+uPgS1nKbA==", "dev": true, - "license": "MIT", "engines": { "node": ">=14" } }, "node_modules/@percy/monitoring": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/monitoring/-/monitoring-1.31.10-beta.2.tgz", - "integrity": "sha512-KmQMFQ0u+FY0uBDgQiFBGSHAFS0VJvl8TLLwkpvYGnVS6BZ4FrXv0xBpIWhztXMsrCQetPb/1YKoumnKj0x+Tw==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/monitoring/-/monitoring-1.31.13.tgz", + "integrity": "sha512-TNHrEA8juyvSM2Egnub9gEDi3p1yYXwGfa4F+H3Ub+zNWFZ/11vxFRQuryzN9aQha/hvcBnXFQJk8JR73I+T4g==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/config": "1.31.10-beta.2", - "@percy/logger": "1.31.10-beta.2", - "@percy/sdk-utils": "1.31.10-beta.2", + "@percy/config": "1.31.13", + "@percy/logger": "1.31.13", + "@percy/sdk-utils": "1.31.13", "systeminformation": "^5.25.11" }, "engines": { @@ -326,11 +312,10 @@ } }, "node_modules/@percy/sdk-utils": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.31.10-beta.2.tgz", - "integrity": "sha512-Ya8d2J6Sb502+mlE2zJJLTPJBNesW8CLpb1jbyRPpOg2/CoivPuBLtFB519/IAA7D7gRkOy+0viBSIqCP7Rt2g==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.31.13.tgz", + "integrity": "sha512-0JW+ngBKLjkhbsI6ZD8wnWDV1U/S66X4vBrJqHLSi8t8BygQrulAwKLrtSV27DHCxz3a+zptTMBQufwFbal5gg==", "dev": true, - "license": "MIT", "dependencies": { "pac-proxy-agent": "^7.0.2" }, @@ -339,14 +324,13 @@ } }, "node_modules/@percy/webdriver-utils": { - "version": "1.31.10-beta.2", - "resolved": "https://registry.npmjs.org/@percy/webdriver-utils/-/webdriver-utils-1.31.10-beta.2.tgz", - "integrity": "sha512-D4t6uuC6NHJtXfVoJzxhRktzM106nPcDkTTneCMPApa8NENy8jN/q7+0kOaGE/iTmXmLThUiYVG4pDcCgCMdhA==", + "version": "1.31.13", + "resolved": "https://registry.npmjs.org/@percy/webdriver-utils/-/webdriver-utils-1.31.13.tgz", + "integrity": "sha512-wiCPRI42Vg6+qL/wiDMPkpauBHbpPJoiaMZB0gDyzrjHJx8cPwG85+oUOgvtMDNdvy55jAtcSdqzA5kXcvMsOw==", "dev": true, - "license": "MIT", "dependencies": { - "@percy/config": "1.31.10-beta.2", - "@percy/sdk-utils": "1.31.10-beta.2" + "@percy/config": "1.31.13", + "@percy/sdk-utils": "1.31.13" }, "engines": { "node": ">=14" @@ -356,18 +340,16 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz", + "integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "undici-types": "~7.18.0" + "undici-types": "~7.21.0" } }, "node_modules/@types/yauzl": { @@ -375,7 +357,6 @@ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "@types/node": "*" @@ -386,17 +367,15 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 14" } }, "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -412,15 +391,13 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "dev": true }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -432,25 +409,22 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/basic-ftp": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", - "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -460,7 +434,6 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -473,7 +446,6 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, - "license": "MIT", "engines": { "node": "*" } @@ -483,7 +455,6 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -492,15 +463,13 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" }, @@ -513,7 +482,6 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, - "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -540,7 +508,6 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -555,7 +522,6 @@ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 14" } @@ -565,7 +531,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -583,7 +548,6 @@ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, - "license": "MIT", "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -598,7 +562,6 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "dev": true, - "license": "MIT", "dependencies": { "once": "^1.4.0" } @@ -608,7 +571,6 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, - "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -618,7 +580,6 @@ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -640,7 +601,6 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -654,7 +614,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -664,7 +623,6 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -674,7 +632,6 @@ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -694,15 +651,13 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-glob": { "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": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -715,9 +670,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -728,15 +683,13 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -746,7 +699,6 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, - "license": "MIT", "dependencies": { "pend": "~1.2.0" } @@ -756,7 +708,6 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -768,15 +719,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, - "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -792,7 +741,6 @@ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", "dev": true, - "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", @@ -808,7 +756,6 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -829,7 +776,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -838,11 +784,10 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -853,7 +798,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -866,7 +810,6 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -880,7 +823,6 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -894,7 +836,6 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", "dev": true, - "license": "MIT", "dependencies": { "queue": "6.0.2" }, @@ -910,7 +851,6 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -928,7 +868,6 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -938,15 +877,13 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 12" } @@ -955,15 +892,13 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -973,7 +908,6 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -986,7 +920,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -995,22 +928,19 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1022,29 +952,25 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -1054,7 +980,6 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -1068,7 +993,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1078,7 +1002,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -1091,7 +1014,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.2" }, @@ -1106,15 +1028,13 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -1124,7 +1044,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "license": "ISC", "dependencies": { "wrappy": "1" } @@ -1134,7 +1053,6 @@ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "dev": true, - "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", @@ -1154,7 +1072,6 @@ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, - "license": "MIT", "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -1167,15 +1084,13 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "dev": true, - "license": "(MIT AND Zlib)" + "dev": true }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -1188,7 +1103,6 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -1207,7 +1121,6 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1217,7 +1130,6 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -1226,15 +1138,13 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -1243,22 +1153,19 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -1271,7 +1178,6 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", "dev": true, - "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -1282,7 +1188,6 @@ "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", "dev": true, - "license": "MIT", "dependencies": { "inherits": "~2.0.3" } @@ -1305,15 +1210,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1323,7 +1226,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -1333,7 +1235,6 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -1345,7 +1246,6 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -1375,7 +1275,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -1398,15 +1297,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1419,7 +1316,6 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -1429,20 +1325,18 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", "dev": true, - "license": "MIT", "dependencies": { - "ip-address": "^10.0.1", + "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -1455,7 +1349,6 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -1470,18 +1363,16 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/systeminformation": { - "version": "5.31.4", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.4.tgz", - "integrity": "sha512-lZppDyQx91VdS5zJvAyGkmwe+Mq6xY978BDUG2wRkWE+jkmUF5ti8cvOovFQoN5bvSFKCXVkyKEaU5ec3SJiRg==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.6.tgz", + "integrity": "sha512-Uv2b2uGGM6ns+26czgW2cYRabYdnswM0ddSOOlryHOaelzsmDSet1iM/NT7VOYxW8x/BW+HkY+b1Ve2pLTSGSA==", "dev": true, - "license": "MIT", "os": [ "darwin", "linux", @@ -1508,7 +1399,6 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -1520,15 +1410,13 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" + "dev": true }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz", + "integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/which": { @@ -1536,7 +1424,6 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -1551,15 +1438,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -1577,11 +1462,10 @@ } }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "dev": true, - "license": "ISC", "bin": { "yaml": "bin.mjs" }, @@ -1597,7 +1481,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, - "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" From 699d05cc325e19ac3a68c9558947c72559bf69f9 Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Sun, 24 May 2026 12:24:43 +0530 Subject: [PATCH 05/12] Mark _http volatile; release DOM domain after closed shadow exposure - Add volatile to the _http field so the unlocked outer read in getHttpClient sees a fully-published HttpClient (Timeout set) on weak memory models. Without it the DCL pattern provides no happens-before guarantee on ARM/Apple Silicon. - Pair DOM.enable with DOM.disable in a finally block so the CDP session doesn't keep emitting DOM events after every snapshot + resize/reload. Co-Authored-By: Claude Opus 4.7 (1M context) --- Percy/Percy.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Percy/Percy.cs b/Percy/Percy.cs index 32fc29c..dcf676e 100644 --- a/Percy/Percy.cs +++ b/Percy/Percy.cs @@ -76,7 +76,10 @@ private static void Log(T message, string lvl = "info") } private static readonly object _httpLock = new object(); - private static HttpClient? _http; + // volatile so the unlocked read in getHttpClient sees a fully-published + // HttpClient (Timeout set) before the field reference becomes visible + // to other threads on weak memory models (e.g. ARM). + private static volatile HttpClient? _http; private static string? sessionType = null; private static object? eligibleWidths; @@ -806,9 +809,11 @@ internal static void ExposeClosedShadowRoots(WebDriver driver) return; } + bool domEnabled = false; try { executeCdp.Invoke(driver, new object[] { "DOM.enable", new Dictionary() }); + domEnabled = true; var getDocResult = executeCdp.Invoke(driver, new object[] { "DOM.getDocument", @@ -870,6 +875,17 @@ internal static void ExposeClosedShadowRoots(WebDriver driver) { Log($"Could not expose closed shadow roots via CDP: {e.Message}", "debug"); } + finally + { + // Release the DOM domain so subsequent CDP commands don't keep + // emitting DOM events for this session. Best-effort — we don't + // care if disable fails (session already closed, etc.). + if (domEnabled) + { + try { executeCdp.Invoke(driver, new object[] { "DOM.disable", new Dictionary() }); } + catch (Exception) { /* defensive */ } + } + } } // Walk a CDP DOM node tree collecting (hostBackendNodeId, From 20068952cb210035384023015d2626f6214aef65 Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Sun, 24 May 2026 12:31:51 +0530 Subject: [PATCH 06/12] Pin HttpClient init invariant under DCL + volatile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two reflection-based unit tests confirm the invariant the newly-volatile _http field exists to protect: - First getHttpClient() call returns a client whose Timeout is already TimeSpan.FromMinutes(10) (the bug we fixed: setting Timeout AFTER assigning the field could let concurrent callers observe a default 100s timeout). - Subsequent calls return the same instance — DCL must not rebuild. Co-Authored-By: Claude Opus 4.7 (1M context) --- Percy.Test/CorsIframesTest.cs | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Percy.Test/CorsIframesTest.cs b/Percy.Test/CorsIframesTest.cs index bcbae8b..f9600ef 100644 --- a/Percy.Test/CorsIframesTest.cs +++ b/Percy.Test/CorsIframesTest.cs @@ -174,5 +174,44 @@ public void PercyContextLostException_CarriesPartialCapture() Assert.Single(ex.PartialCapture); Assert.Equal("http://a/", ex.PartialCapture[0]["frameUrl"]); } + + // -- HttpClient init invariant ------------------------------------------- + + [Fact] + public void GetHttpClient_AlwaysReturnsClientWithTenMinuteTimeout() + { + // The newly-volatile _http field exists so the unlocked outer + // read in getHttpClient is guaranteed to see a fully-published + // HttpClient with Timeout already set. Force the first-caller + // path and confirm the invariant. + var field = typeof(Percy).GetField("_http", + BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(field); + field!.SetValue(null, null); + + var method = typeof(Percy).GetMethod("getHttpClient", + BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(method); + var client = (System.Net.Http.HttpClient)method!.Invoke(null, null)!; + + Assert.NotNull(client); + Assert.Equal(TimeSpan.FromMinutes(10), client.Timeout); + } + + [Fact] + public void GetHttpClient_IsIdempotentAcrossCalls() + { + var field = typeof(Percy).GetField("_http", + BindingFlags.NonPublic | BindingFlags.Static); + field!.SetValue(null, null); + + var method = typeof(Percy).GetMethod("getHttpClient", + BindingFlags.NonPublic | BindingFlags.Static); + var first = method!.Invoke(null, null); + var second = method.Invoke(null, null); + + Assert.Same(first, second); + } + } } From daca3d294aab78524853d8917db8ffe21ef9a18d Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Sun, 24 May 2026 13:26:36 +0530 Subject: [PATCH 07/12] docs(snapshot): document ClampFrameDepth(0) -> default semantic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CE re-review flagged ClampFrameDepth silently mapping user-supplied 0 to DEFAULT_MAX_FRAME_DEPTH (5) as a hidden behavior. The intent matches @percy/sdk-utils clampFrameDepth and the JS SDKs: 0 means "use default" (unset / config placeholder), not "disable iframe capture". To disable nested capture, users should omit the option or use data-percy-ignore / ignoreIframeSelectors. No behavior change — pure documentation so future readers don't re-flag. Co-Authored-By: Claude Opus 4.7 (1M context) --- Percy/Percy.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Percy/Percy.cs b/Percy/Percy.cs index dcf676e..2fff5bb 100644 --- a/Percy/Percy.cs +++ b/Percy/Percy.cs @@ -368,10 +368,24 @@ internal static string GetOrigin(string? url) } // Clamp user-supplied iframe nesting depth to a safe range. Mirrors - // @percy/sdk-utils clampFrameDepth — anything non-positive falls back to - // the default, anything above MAX_ALLOWED_FRAME_DEPTH is capped. + // @percy/sdk-utils clampFrameDepth and the JS SDKs: + // * depth <= 0 -> "use default" (DEFAULT_MAX_FRAME_DEPTH = 5). + // `0` is deliberately treated as "unset / use default" + // rather than "disable iframe capture" — matching + // sdk-utils so users who explicitly pass `0` (e.g. + // from a config file with an unset value) get the + // standard depth instead of silently disabling + // capture. To disable nested capture, omit the + // option or use `data-percy-ignore` / + // `ignoreIframeSelectors`. + // * depth > MAX_ALLOWED_FRAME_DEPTH -> capped at the hard ceiling. + // * otherwise -> returned as-is. + // If this semantic ever needs to change to "0 disables", do it across + // all Percy SDKs at once via sdk-utils first. internal static int ClampFrameDepth(int depth, int defaultMax = DEFAULT_MAX_FRAME_DEPTH) { + // Intentionally maps 0 -> default (see comment above). Negative + // values also collapse here as a defensive fallback. if (depth <= 0) return defaultMax; if (depth > MAX_ALLOWED_FRAME_DEPTH) return MAX_ALLOWED_FRAME_DEPTH; return depth; From 6ac2e7b8bbd1111929698f8114ca0b49a733c96d Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Sun, 24 May 2026 13:27:02 +0530 Subject: [PATCH 08/12] fix(snapshot): recurse closed shadow walk into same-origin iframes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CE re-review flagged that CollectClosedShadowRoots early-returned on any node carrying contentDocument, so closed shadow roots living inside same-origin iframes were silently skipped — even though those frames share the parent's JS realm and therefore the same window.__percyClosedShadowRoots WeakMap that PercyDOM.serialize reads. Walker now decides per-frame: * Same origin (compared via GetOrigin against the page URL) -> recurse INTO contentDocument; found closed roots use the parent realm's map. * Cross origin -> still skipped (different JS realm, the resolveNode objectIds don't belong to our execution context anyway). * Missing documentURL / unknown origin -> defensive skip (pre-fix behavior for the unknown case). Also factored the CDP flow inside ExposeClosedShadowRoots into a pure internal RunClosedShadowRootExposure that takes a CDP invoker delegate, a script-runner delegate, and a page-URL getter. The WebDriver-bound entrypoint now adapts the reflected ExecuteCdpCommand to those delegates, which makes the DOM.enable / DOM.disable lifecycle directly unit-testable without a real browser. Co-Authored-By: Claude Opus 4.7 (1M context) --- Percy/Percy.cs | 130 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 31 deletions(-) diff --git a/Percy/Percy.cs b/Percy/Percy.cs index 2fff5bb..40f0577 100644 --- a/Percy/Percy.cs +++ b/Percy/Percy.cs @@ -823,16 +823,40 @@ internal static void ExposeClosedShadowRoots(WebDriver driver) return; } + // Adapt the reflected MethodInfo to a Func so the core flow can be + // unit-tested with a fake invoker. ScriptRunner is a small adapter + // around driver.ExecuteScript for the same reason. + Func, object?> cdpInvoker = (cmd, args) => + executeCdp.Invoke(driver, new object[] { cmd, args ?? new Dictionary() }); + Action scriptRunner = (script) => driver.ExecuteScript(script); + Func pageUrlGetter = () => { + try { return driver.Url ?? ""; } + catch (Exception e) { + Log($"Could not read driver.Url for shadow walk origin: {e.Message}", "debug"); + return ""; + } + }; + + RunClosedShadowRootExposure(cdpInvoker, scriptRunner, pageUrlGetter); + } + + // Pure-logic core of ExposeClosedShadowRoots, factored out so unit + // tests can drive it with a fake CDP invoker and assert that + // DOM.disable always runs in the finally branch when DOM.enable + // succeeded — and stays unsent when DOM.enable itself threw. + internal static void RunClosedShadowRootExposure( + Func, object?> cdpInvoker, + Action scriptRunner, + Func pageUrlGetter) + { bool domEnabled = false; try { - executeCdp.Invoke(driver, new object[] { "DOM.enable", new Dictionary() }); + cdpInvoker("DOM.enable", new Dictionary()); domEnabled = true; - var getDocResult = executeCdp.Invoke(driver, new object[] { - "DOM.getDocument", - new Dictionary { ["depth"] = -1, ["pierce"] = true } - }); + var getDocResult = cdpInvoker("DOM.getDocument", + new Dictionary { ["depth"] = -1, ["pierce"] = true }); if (getDocResult == null) return; // The .NET ExecuteCdpCommand returns a Dictionary @@ -841,28 +865,32 @@ internal static void ExposeClosedShadowRoots(WebDriver driver) if (docDict == null || !docDict.TryGetValue("root", out object? rootObj) || rootObj == null) return; + // Compute the top-level page origin once so the walker can + // recurse INTO same-origin child frame documents (those share + // the parent's JS realm, so writing into window.__percyClosedShadowRoots + // works) but still skip cross-origin frames (different realm). + string pageOrigin = GetOrigin(pageUrlGetter() ?? ""); + var closedPairs = new List<(long host, long shadow)>(); - CollectClosedShadowRoots(rootObj, closedPairs); + CollectClosedShadowRoots(rootObj, closedPairs, pageOrigin); if (closedPairs.Count == 0) return; Log($"Found {closedPairs.Count} closed shadow root(s), exposing via CDP", "debug"); // Prime the WeakMap on the page first (same key as PercyDOM uses). - driver.ExecuteScript( + scriptRunner( "window.__percyClosedShadowRoots = window.__percyClosedShadowRoots || new WeakMap();"); foreach (var pair in closedPairs) { try { - var hostResolve = executeCdp.Invoke(driver, new object[] { - "DOM.resolveNode", - new Dictionary { ["backendNodeId"] = pair.host } - }) as IDictionary; - var shadowResolve = executeCdp.Invoke(driver, new object[] { - "DOM.resolveNode", - new Dictionary { ["backendNodeId"] = pair.shadow } - }) as IDictionary; + var hostResolve = cdpInvoker("DOM.resolveNode", + new Dictionary { ["backendNodeId"] = pair.host }) + as IDictionary; + var shadowResolve = cdpInvoker("DOM.resolveNode", + new Dictionary { ["backendNodeId"] = pair.shadow }) + as IDictionary; string? hostObjectId = ExtractObjectId(hostResolve); string? shadowObjectId = ExtractObjectId(shadowResolve); if (hostObjectId == null || shadowObjectId == null) continue; @@ -870,13 +898,10 @@ internal static void ExposeClosedShadowRoots(WebDriver driver) var args = new List { new Dictionary { ["objectId"] = shadowObjectId } }; - executeCdp.Invoke(driver, new object[] { - "Runtime.callFunctionOn", - new Dictionary { - ["functionDeclaration"] = "function(shadowRoot) { window.__percyClosedShadowRoots.set(this, shadowRoot); }", - ["objectId"] = hostObjectId, - ["arguments"] = args - } + cdpInvoker("Runtime.callFunctionOn", new Dictionary { + ["functionDeclaration"] = "function(shadowRoot) { window.__percyClosedShadowRoots.set(this, shadowRoot); }", + ["objectId"] = hostObjectId, + ["arguments"] = args }); } catch (Exception e) @@ -894,24 +919,67 @@ internal static void ExposeClosedShadowRoots(WebDriver driver) // Release the DOM domain so subsequent CDP commands don't keep // emitting DOM events for this session. Best-effort — we don't // care if disable fails (session already closed, etc.). + // Only sent when DOM.enable previously succeeded; if DOM.enable + // itself threw, domEnabled stays false and we don't send a + // spurious DOM.disable. if (domEnabled) { - try { executeCdp.Invoke(driver, new object[] { "DOM.disable", new Dictionary() }); } + try { cdpInvoker("DOM.disable", new Dictionary()); } catch (Exception) { /* defensive */ } } } } // Walk a CDP DOM node tree collecting (hostBackendNodeId, - // shadowBackendNodeId) pairs for every closed shadow root. Skips nodes - // inside child frame documents — closed shadow DOM inside cross-frame - // documents lives in a different execution context whose window has no - // WeakMap to write into. - private static void CollectClosedShadowRoots(object nodeObj, List<(long, long)> pairs) + // shadowBackendNodeId) pairs for every closed shadow root. + // + // When the walker encounters an iframe node (CDP exposes the nested + // document as `contentDocument`), it decides whether to recurse based + // on the document's `documentURL`: + // * Same origin as the top-level page -> recurse INTO contentDocument. + // The frame shares the parent's JS realm, so closed shadow roots + // discovered inside it can be written into the same + // window.__percyClosedShadowRoots WeakMap that PercyDOM.serialize + // reads. Without this, same-origin iframes silently skipped the + // entire closed-shadow walk. + // * Cross origin -> skip. Different JS realm; the host objectIds + // resolved by DOM.resolveNode wouldn't belong to our page's + // execution context anyway. + // * pageOrigin empty / contentDocument missing documentURL -> skip, + // defensive default (matches prior behavior). + internal static void CollectClosedShadowRoots( + object nodeObj, List<(long, long)> pairs, string pageOrigin) { var node = nodeObj as IDictionary; if (node == null) return; - if (node.ContainsKey("contentDocument")) return; // child frame document + + // Child frame document handling. CDP attaches `contentDocument` to + // iframe / frame nodes. Decide whether to recurse into the nested + // document based on origin compared to the top-level page. + if (node.TryGetValue("contentDocument", out object? contentDocRaw) && contentDocRaw != null) + { + var contentDoc = contentDocRaw as IDictionary; + if (contentDoc == null) return; + string? docUrl = contentDoc.TryGetValue("documentURL", out object? du) ? du?.ToString() : null; + if (string.IsNullOrEmpty(pageOrigin) || string.IsNullOrEmpty(docUrl)) + { + // Defensive: we can't prove same-origin, so skip — matches + // pre-fix behavior for the unknown case. + return; + } + string frameOrigin = GetOrigin(docUrl); + if (frameOrigin != pageOrigin) + { + Log($"Skipping cross-origin frame document during closed-shadow walk: {docUrl}", "debug"); + return; + } + // Same-origin frame: walk into the contentDocument as if it + // were any other subtree. Closed shadow roots found inside + // share the parent window's __percyClosedShadowRoots map. + CollectClosedShadowRoots(contentDoc, pairs, pageOrigin); + // Continue with this node's own children/shadowRoots below + // (e.g. the iframe element itself could host a shadow root). + } if (node.TryGetValue("shadowRoots", out object? srRaw) && srRaw is System.Collections.IEnumerable shadowRoots) { @@ -926,13 +994,13 @@ private static void CollectClosedShadowRoots(object nodeObj, List<(long, long)> { pairs.Add((hostId.Value, shadowId.Value)); } - CollectClosedShadowRoots(sr, pairs); + CollectClosedShadowRoots(sr, pairs, pageOrigin); } } if (node.TryGetValue("children", out object? childrenRaw) && childrenRaw is System.Collections.IEnumerable children) { - foreach (var child in children) CollectClosedShadowRoots(child, pairs); + foreach (var child in children) CollectClosedShadowRoots(child, pairs, pageOrigin); } } From 21f457d03beeedfc739f77f2eabd89f92d51e9cb Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Sun, 24 May 2026 13:27:16 +0530 Subject: [PATCH 09/12] test(snapshot): cover DOM.disable lifecycle and same-origin shadow walk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses CE re-review MINORs and MAJOR coverage gaps: * Same-origin closed-shadow-in-iframe recursion: three new tests drive CollectClosedShadowRoots with synthesized CDP DOM payloads — one same-origin iframe (closed root inside contentDocument is captured), one cross-origin iframe (skipped), one with missing documentURL (defensive skip). Plus a sanity test that a top-level closed root still works. * DOM.disable invocation regression coverage: a FakeCdp recorder exercises RunClosedShadowRootExposure end-to-end and asserts: - DOM.disable is sent after a successful run, as the final command. - DOM.disable is still sent if DOM.getDocument throws. - DOM.disable is NOT sent when DOM.enable itself throws (domEnabled stays false in the finally block — avoids spurious disable on a session that never enabled the domain). * HttpClient state serialization: the GetHttpClient_* tests mutate the static volatile _http field. Tag the suite with [Collection("HttpClientStateSerial")] (DisableParallelization = true) so a future flip of parallelizeTestCollections in xunit.runner.json can't race them against Percy.Test.cs / PercyDriver.Test.cs, which also touch setHttpClient. Co-Authored-By: Claude Opus 4.7 (1M context) --- Percy.Test/CorsIframesTest.cs | 216 ++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/Percy.Test/CorsIframesTest.cs b/Percy.Test/CorsIframesTest.cs index f9600ef..e0c6d1a 100644 --- a/Percy.Test/CorsIframesTest.cs +++ b/Percy.Test/CorsIframesTest.cs @@ -6,9 +6,19 @@ namespace PercyIO.Selenium.Tests { + // Collection definition used to serialize tests that mutate Percy's + // static _http field. xunit.runner.json already disables parallelization + // across collections, but the explicit Collection attribute pins the + // invariant so a future flip back to parallel test collections doesn't + // silently race GetHttpClient_* against any other suite that touches + // setHttpClient / _http (Percy.Test.cs and PercyDriver.Test.cs both do). + [CollectionDefinition("HttpClientStateSerial", DisableParallelization = true)] + public class HttpClientStateSerialCollection { } + // Unit tests for the CORS iframe + closed shadow DOM helpers added to // Percy.cs. These don't require the Percy CLI or a real browser; they // exercise the pure-C# helpers via reflection where they are internal. + [Collection("HttpClientStateSerial")] public class CorsIframesTest { // -- GetOrigin ----------------------------------------------------------- @@ -213,5 +223,211 @@ public void GetHttpClient_IsIdempotentAcrossCalls() Assert.Same(first, second); } + // -- CollectClosedShadowRoots same-origin iframe recursion -------------- + // + // The CDP DOM walker used to early-return on any node with a + // contentDocument, silently skipping closed shadow DOM inside + // same-origin iframes. The new behavior recurses into same-origin + // contentDocument trees (same JS realm, same WeakMap) while still + // skipping cross-origin frames. + private static Dictionary Node(long backendNodeId, + List>? children = null, + List>? shadowRoots = null, + Dictionary? contentDocument = null) + { + var n = new Dictionary + { + ["backendNodeId"] = backendNodeId + }; + if (children != null) n["children"] = children; + if (shadowRoots != null) n["shadowRoots"] = shadowRoots; + if (contentDocument != null) n["contentDocument"] = contentDocument; + return n; + } + + private static Dictionary ClosedShadowRoot(long backendNodeId) => + new Dictionary + { + ["backendNodeId"] = backendNodeId, + ["shadowRootType"] = "closed" + }; + + [Fact] + public void CollectClosedShadowRoots_RecursesIntoSameOriginContentDocument() + { + // host (id=10) hosts a closed shadow root (id=11), nested inside an + // iframe whose contentDocument is same-origin with the page. + var sameOriginIframe = Node( + backendNodeId: 5, + contentDocument: new Dictionary + { + ["backendNodeId"] = 6, + ["documentURL"] = "https://example.com/iframe-page", + ["children"] = new List> { + Node(10, shadowRoots: new List> { ClosedShadowRoot(11) }) + } + }); + var root = Node(1, children: new List> { sameOriginIframe }); + + var pairs = new List<(long, long)>(); + Percy.CollectClosedShadowRoots(root, pairs, "https://example.com"); + + Assert.Single(pairs); + Assert.Equal((10L, 11L), pairs[0]); + } + + [Fact] + public void CollectClosedShadowRoots_SkipsCrossOriginContentDocument() + { + var crossOriginIframe = Node( + backendNodeId: 5, + contentDocument: new Dictionary + { + ["backendNodeId"] = 6, + ["documentURL"] = "https://other.example.com/iframe-page", + ["children"] = new List> { + Node(10, shadowRoots: new List> { ClosedShadowRoot(11) }) + } + }); + var root = Node(1, children: new List> { crossOriginIframe }); + + var pairs = new List<(long, long)>(); + Percy.CollectClosedShadowRoots(root, pairs, "https://example.com"); + + Assert.Empty(pairs); + } + + [Fact] + public void CollectClosedShadowRoots_SkipsContentDocumentWithMissingDocumentUrl() + { + // Defensive fallback: if documentURL is absent we can't prove + // same-origin, so skip — matches pre-fix behavior. + var iframe = Node( + backendNodeId: 5, + contentDocument: new Dictionary + { + ["backendNodeId"] = 6, + ["children"] = new List> { + Node(10, shadowRoots: new List> { ClosedShadowRoot(11) }) + } + }); + var root = Node(1, children: new List> { iframe }); + + var pairs = new List<(long, long)>(); + Percy.CollectClosedShadowRoots(root, pairs, "https://example.com"); + + Assert.Empty(pairs); + } + + [Fact] + public void CollectClosedShadowRoots_TopLevelClosedRootStillCaptured() + { + // Sanity check: the same-origin-recursion fix mustn't regress the + // baseline top-level closed shadow root capture. + var host = Node(10, shadowRoots: new List> { ClosedShadowRoot(11) }); + var root = Node(1, children: new List> { host }); + + var pairs = new List<(long, long)>(); + Percy.CollectClosedShadowRoots(root, pairs, "https://example.com"); + + Assert.Single(pairs); + Assert.Equal((10L, 11L), pairs[0]); + } + + // -- RunClosedShadowRootExposure: DOM.enable / DOM.disable lifecycle --- + // + // Regression coverage for the new domEnabled finally path. The fake + // CDP invoker records every command issued so the test can assert the + // expected lifecycle, including the negative case where DOM.disable + // must NOT be sent if DOM.enable itself failed. + private sealed class FakeCdp + { + public readonly List Commands = new List(); + public Func, object?> Handler { get; set; } + = (_, __) => null; + + public object? Invoke(string command, Dictionary args) + { + Commands.Add(command); + return Handler(command, args); + } + } + + private static Dictionary MinimalGetDocumentResponse() => + new Dictionary + { + ["root"] = new Dictionary + { + ["backendNodeId"] = 1L, + // No shadowRoots and no children -> walker finds no pairs, + // so the resolveNode / Runtime.callFunctionOn branch is + // skipped and we get a clean DOM.enable / DOM.disable pair. + } + }; + + [Fact] + public void RunClosedShadowRootExposure_CallsDomDisableAfterSuccess() + { + var fake = new FakeCdp(); + fake.Handler = (cmd, args) => cmd == "DOM.getDocument" + ? MinimalGetDocumentResponse() + : (object?)null; + + Percy.RunClosedShadowRootExposure( + fake.Invoke, + _ => { /* scriptRunner no-op */ }, + () => "https://example.com/"); + + Assert.Contains("DOM.enable", fake.Commands); + Assert.Contains("DOM.disable", fake.Commands); + // DOM.disable must be the last CDP command sent. + Assert.Equal("DOM.disable", fake.Commands[fake.Commands.Count - 1]); + } + + [Fact] + public void RunClosedShadowRootExposure_CallsDomDisableAfterGetDocumentThrows() + { + var fake = new FakeCdp(); + fake.Handler = (cmd, args) => + { + if (cmd == "DOM.getDocument") throw new InvalidOperationException("boom"); + return null; + }; + + // Should swallow the exception and still issue DOM.disable. + Percy.RunClosedShadowRootExposure( + fake.Invoke, + _ => { }, + () => "https://example.com/"); + + Assert.Contains("DOM.enable", fake.Commands); + Assert.Contains("DOM.getDocument", fake.Commands); + Assert.Contains("DOM.disable", fake.Commands); + } + + [Fact] + public void RunClosedShadowRootExposure_DoesNotCallDomDisableWhenDomEnableFails() + { + // Critical invariant: if DOM.enable itself threw, domEnabled stays + // false and the finally block must NOT issue a spurious DOM.disable + // (which would be sent on a session that never enabled the DOM + // domain in the first place). + var fake = new FakeCdp(); + fake.Handler = (cmd, args) => + { + if (cmd == "DOM.enable") throw new InvalidOperationException("session closed"); + return null; + }; + + Percy.RunClosedShadowRootExposure( + fake.Invoke, + _ => { }, + () => "https://example.com/"); + + Assert.Contains("DOM.enable", fake.Commands); + Assert.DoesNotContain("DOM.disable", fake.Commands); + Assert.DoesNotContain("DOM.getDocument", fake.Commands); + } + } } From 1ca579983e9a03a4ca9d2de7343f87d024d33d0b Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Sun, 24 May 2026 13:27:24 +0530 Subject: [PATCH 10/12] chore(deps): bump @percy/cli to 1.31.14 Co-Authored-By: Claude Opus 4.7 (1M context) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5a0f1a..76a292c 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,6 @@ "test": "percy exec --testing -- dotnet test" }, "devDependencies": { - "@percy/cli": "^1.31.10" + "@percy/cli": "^1.31.14" } } From 67bbb5e28f16f2fbdbcb208ff3553c53b93a890e Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Thu, 28 May 2026 16:59:55 +0530 Subject: [PATCH 11/12] Address review: full iframe prefix list and serial test collection - Percy.cs: expand IsUnsupportedIframeSrc to the canonical list (about:, chrome:, chrome-extension:, devtools:, edge:, opera:, view-source:, data:, javascript:, blob:, vbscript:, file:), matching the nightwatch/protractor ports. Blocks about:blank, blob:, and file:// iframes that previously slipped through. - Percy.Test.cs / PercyDriver.Test.cs: opt UnitTests and PercyDriverTest into the HttpClientStateSerial collection so GetHttpClient_* tests no longer race them on Percy._http. - CorsIframesTest.cs: cover the new prefixes and case-insensitivity. Co-Authored-By: Claude Opus 4.7 (1M context) --- Percy.Test/CorsIframesTest.cs | 7 +++++++ Percy.Test/Percy.Test.cs | 1 + Percy.Test/PercyDriver.Test.cs | 1 + Percy/Percy.cs | 18 ++++++++++++++---- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Percy.Test/CorsIframesTest.cs b/Percy.Test/CorsIframesTest.cs index e0c6d1a..6484201 100644 --- a/Percy.Test/CorsIframesTest.cs +++ b/Percy.Test/CorsIframesTest.cs @@ -45,6 +45,13 @@ public void GetOrigin_ReturnsEmptyForInvalidOrEmptyUrl() [InlineData("JAVASCRIPT:alert(1)", true)] [InlineData("data:text/html,

", true)] [InlineData("vbscript:foo()", true)] + [InlineData("about:blank", true)] + [InlineData("About:Blank", true)] + [InlineData("blob:https://example.com/abc", true)] + [InlineData("file:///etc/passwd", true)] + [InlineData("FILE:///C:/Users", true)] + [InlineData("chrome://settings", true)] + [InlineData("view-source:https://example.com", true)] [InlineData("", true)] [InlineData(null, true)] [InlineData("https://example.com/x", false)] diff --git a/Percy.Test/Percy.Test.cs b/Percy.Test/Percy.Test.cs index 211e6b2..3c0d360 100644 --- a/Percy.Test/Percy.Test.cs +++ b/Percy.Test/Percy.Test.cs @@ -35,6 +35,7 @@ public void Dispose() } } + [Collection("HttpClientStateSerial")] public class UnitTests : IClassFixture { public readonly FirefoxDriver driver; diff --git a/Percy.Test/PercyDriver.Test.cs b/Percy.Test/PercyDriver.Test.cs index 4028964..3381337 100644 --- a/Percy.Test/PercyDriver.Test.cs +++ b/Percy.Test/PercyDriver.Test.cs @@ -10,6 +10,7 @@ namespace PercyIO.Selenium.Tests { + [Collection("HttpClientStateSerial")] public class PercyDriverTest { private readonly Mock remoteDriver; diff --git a/Percy/Percy.cs b/Percy/Percy.cs index 3bd6b19..2d68c88 100644 --- a/Percy/Percy.cs +++ b/Percy/Percy.cs @@ -347,10 +347,20 @@ public static Region CreateRegion( internal static bool IsUnsupportedIframeSrc(string? src) { - return string.IsNullOrEmpty(src) || - src.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase) || - src.StartsWith("data:", StringComparison.OrdinalIgnoreCase) || - src.StartsWith("vbscript:", StringComparison.OrdinalIgnoreCase); + if (string.IsNullOrEmpty(src)) return true; + // Canonical list mirrors @percy/sdk-utils BROWSER_INTERNAL_PREFIXES and + // the protractor port (#708). `file:` is included to block local-FS + // iframes that could leak filesystem content into snapshots. + string[] prefixes = { + "about:", "chrome:", "chrome-extension:", "devtools:", + "edge:", "opera:", "view-source:", "data:", "javascript:", + "blob:", "vbscript:", "file:" + }; + foreach (var p in prefixes) + { + if (src.StartsWith(p, StringComparison.OrdinalIgnoreCase)) return true; + } + return false; } internal static string GetOrigin(string? url) From 051e350b6ee29cea9e2ce984e60fe87f43474d3e Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Mon, 1 Jun 2026 11:16:07 +0530 Subject: [PATCH 12/12] chore: regenerate package-lock.json to match package.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Master's lockfile was stale (pinned @percy/cli 1.31.10-beta.2 while package.json declared ^1.31.15-beta.0), and the previous regeneration on this branch was done with an npm version that stripped the "license" fields — producing ~124 lines of phantom diff. Regenerated with npm 10.8.2 so license fields are preserved and the lockfile resolves the version package.json actually asks for. Co-Authored-By: Claude Opus 4.7 (1M context) --- package-lock.json | 428 ++++++++++++++++++++++++++++++---------------- 1 file changed, 276 insertions(+), 152 deletions(-) diff --git a/package-lock.json b/package-lock.json index b67e7e6..298b0eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,16 +5,17 @@ "packages": { "": { "devDependencies": { - "@percy/cli": "^1.31.10" + "@percy/cli": "^1.31.15-beta.0" } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -23,10 +24,11 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -36,6 +38,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -49,6 +52,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -58,6 +62,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -67,21 +72,22 @@ } }, "node_modules/@percy/cli": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/cli/-/cli-1.31.13.tgz", - "integrity": "sha512-QpNpqt4rpmQMOixvWzds2bMKaNT64cUNsELokM0jAP8wSbKZLAsvWddM24fZTqsKXpm4peBpzO73S3bwV0Ep5w==", - "dev": true, - "dependencies": { - "@percy/cli-app": "1.31.13", - "@percy/cli-build": "1.31.13", - "@percy/cli-command": "1.31.13", - "@percy/cli-config": "1.31.13", - "@percy/cli-doctor": "1.31.13", - "@percy/cli-exec": "1.31.13", - "@percy/cli-snapshot": "1.31.13", - "@percy/cli-upload": "1.31.13", - "@percy/client": "1.31.13", - "@percy/logger": "1.31.13" + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/cli/-/cli-1.31.15-beta.0.tgz", + "integrity": "sha512-cCz71pqDN7xcagUrfFwLuZkQNW3SXZWDuQplRr7aivuNV07vdhMAN4PLnJL6GmBmi8IjWlGdDin0cCXA2//9Ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@percy/cli-app": "1.31.15-beta.0", + "@percy/cli-build": "1.31.15-beta.0", + "@percy/cli-command": "1.31.15-beta.0", + "@percy/cli-config": "1.31.15-beta.0", + "@percy/cli-doctor": "1.31.15-beta.0", + "@percy/cli-exec": "1.31.15-beta.0", + "@percy/cli-snapshot": "1.31.15-beta.0", + "@percy/cli-upload": "1.31.15-beta.0", + "@percy/client": "1.31.15-beta.0", + "@percy/logger": "1.31.15-beta.0" }, "bin": { "percy": "bin/run.cjs" @@ -91,39 +97,42 @@ } }, "node_modules/@percy/cli-app": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/cli-app/-/cli-app-1.31.13.tgz", - "integrity": "sha512-onVbiQkG2FaAnP9rw1OtCR/8OkD0lo/S8Cz56Qe3sln/kJtfEC1VAsTq9x/e3caeNFPh7aObF78o8BVvIexcNQ==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/cli-app/-/cli-app-1.31.15-beta.0.tgz", + "integrity": "sha512-AYazqcaV+U69a0Mo8r/wrsU3cNRNKhxpznlQUphaQ0rFdD86UhgYPe+9Rt7D+WyXqvf0zCWdoCyFyVqnH/6eTg==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.13", - "@percy/cli-exec": "1.31.13" + "@percy/cli-command": "1.31.15-beta.0", + "@percy/cli-exec": "1.31.15-beta.0" }, "engines": { "node": ">=14" } }, "node_modules/@percy/cli-build": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/cli-build/-/cli-build-1.31.13.tgz", - "integrity": "sha512-tsAr+pzGe5LqRNJiB4CBo7YDBXqH/uXm/89iovwor8wkqDgHR3DfC8u5+GgNNbfazEG2nDx4l3QmmOvN+APARw==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/cli-build/-/cli-build-1.31.15-beta.0.tgz", + "integrity": "sha512-qb8GwF41toscSm6vYOu0jng6gXjbEDcoTMzafgWF62loP1JZT20SjewbaRW1tG51Wgg2aWuGuaIqN6dOyKFZig==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.13" + "@percy/cli-command": "1.31.15-beta.0" }, "engines": { "node": ">=14" } }, "node_modules/@percy/cli-command": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/cli-command/-/cli-command-1.31.13.tgz", - "integrity": "sha512-pGcs0yPocP3bXqpkzS1UThoQ+8ayVDXLBtpSU+/7N0Rnj6R0eUD1xWggobPT7pY1EOrWqKMEzb67scEvrSIq7Q==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/cli-command/-/cli-command-1.31.15-beta.0.tgz", + "integrity": "sha512-9AOe2qIqbsN3LAhuOVrM2H7LrXyU88dN5WDnO9usIUVIVT60B0PBlLgdbPzMsYbPLrlq2CWFCShbB/zq6+CgVw==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/config": "1.31.13", - "@percy/core": "1.31.13", - "@percy/logger": "1.31.13" + "@percy/config": "1.31.15-beta.0", + "@percy/core": "1.31.15-beta.0", + "@percy/logger": "1.31.15-beta.0" }, "bin": { "percy-cli-readme": "bin/readme.js" @@ -133,30 +142,32 @@ } }, "node_modules/@percy/cli-config": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/cli-config/-/cli-config-1.31.13.tgz", - "integrity": "sha512-t0K7SYCeWp+UrRMqrTmzZiidBTxWkAvNqCyrExa5yQy3GJXyYqhygqymfP8Y5+8e2SbIGBoXZQW0fIzoSc83ww==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/cli-config/-/cli-config-1.31.15-beta.0.tgz", + "integrity": "sha512-9Qhl0G4eS77J0bHnEY0wZmgeHY3E5IERzWv9ie8oTAcaVaHGv+QlJSwsn9ZPspR6KomF47nSN0FjmSn+cNHPBQ==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.13" + "@percy/cli-command": "1.31.15-beta.0" }, "engines": { "node": ">=14" } }, "node_modules/@percy/cli-doctor": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/cli-doctor/-/cli-doctor-1.31.13.tgz", - "integrity": "sha512-sX+VNyyTZ3viFW75NPFYMTjD+CHraD43VJ7K5Mpt4hq2IR8LS1J0C37kAiMJnTwFHeeXpaaIsCxVrKiS+jHvOQ==", - "dev": true, - "dependencies": { - "@percy/cli-command": "1.31.13", - "@percy/client": "1.31.13", - "@percy/config": "1.31.13", - "@percy/core": "1.31.13", - "@percy/env": "1.31.13", - "@percy/logger": "1.31.13", - "@percy/monitoring": "1.31.13", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/cli-doctor/-/cli-doctor-1.31.15-beta.0.tgz", + "integrity": "sha512-nqzUr9shAtJwuH0KkF2mqM1m6b6USSn+FAKqE4MWiAAKeskC0W7YB/4wSmzSJ2mtQCTmXj6pj0VWKNsxS+cSWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@percy/cli-command": "1.31.15-beta.0", + "@percy/client": "1.31.15-beta.0", + "@percy/config": "1.31.15-beta.0", + "@percy/core": "1.31.15-beta.0", + "@percy/env": "1.31.15-beta.0", + "@percy/logger": "1.31.15-beta.0", + "@percy/monitoring": "1.31.15-beta.0", "minimatch": "^9.0.0", "ws": "^8.17.1" }, @@ -165,13 +176,14 @@ } }, "node_modules/@percy/cli-exec": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/cli-exec/-/cli-exec-1.31.13.tgz", - "integrity": "sha512-93w98Mejp2V7vuXgvEdwbJ4odHa3KHHJ6yrWkvydhKe0FmjoEnH20fYpZ1661Rh6YPTm61a/VvspoWEMD7E+5g==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/cli-exec/-/cli-exec-1.31.15-beta.0.tgz", + "integrity": "sha512-9lmkwFAWlgIlJqYLeRefIr+cGtf7KRSmrM7/BI7uUmB9vfYcPdX2CROhgVHoeOe6Kp05c2jZRk70iYMJcM3DOw==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.13", - "@percy/logger": "1.31.13", + "@percy/cli-command": "1.31.15-beta.0", + "@percy/logger": "1.31.15-beta.0", "cross-spawn": "^7.0.3", "which": "^2.0.2" }, @@ -180,12 +192,13 @@ } }, "node_modules/@percy/cli-snapshot": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/cli-snapshot/-/cli-snapshot-1.31.13.tgz", - "integrity": "sha512-k9gG8ImwbcLZPeLtanBETdT8qWvAIKPcZXJ0P8OnoL+onyqEbT/BuYNTY7tlezWDg5utJFwT4LczOqAnHdS0AA==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/cli-snapshot/-/cli-snapshot-1.31.15-beta.0.tgz", + "integrity": "sha512-qOPoRlfeVVrAzWOfDzAcUcdwrbxe1tZMd12vTRu7KmglbcywssxNRWNcsXr0e9QFRWhR1/bqnd9nOhKEDEZxnQ==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.13", + "@percy/cli-command": "1.31.15-beta.0", "yaml": "^2.0.0" }, "engines": { @@ -193,12 +206,13 @@ } }, "node_modules/@percy/cli-upload": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/cli-upload/-/cli-upload-1.31.13.tgz", - "integrity": "sha512-+/HkIGWqHH/EJmbxATXO0N8XnwtXrhnePDKFZtWx7JrpaZXxRXI96NGL7/mjKQ2sAQKPSGvbbUAB++UoFZSnYg==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/cli-upload/-/cli-upload-1.31.15-beta.0.tgz", + "integrity": "sha512-kYEZxUpvMaDYDhJ1revqyHESFN8tLhTyYEqv2mwW6NEUW6shGaEaQURPglWxlVYexGwJQqYp0VIrNx6y94pzfQ==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.31.13", + "@percy/cli-command": "1.31.15-beta.0", "fast-glob": "^3.2.11", "image-size": "^1.0.0" }, @@ -207,14 +221,15 @@ } }, "node_modules/@percy/client": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/client/-/client-1.31.13.tgz", - "integrity": "sha512-eFoUacW2GolCm3rL8oS1QyWD6b4UWds9HrMbtp6OJWUr6sQ8bcOemjr3M1YpkDsV2wGC0ikLISQxU8XrTxKDhA==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/client/-/client-1.31.15-beta.0.tgz", + "integrity": "sha512-oR5YqLulSlu8ROEA9mcMffQWXNdPXKPZ0qWHHjpvlfp1ydVYhAwXutCW6HC85ThqXL/C6XbiZN9e74ThvsHxpQ==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/config": "1.31.13", - "@percy/env": "1.31.13", - "@percy/logger": "1.31.13", + "@percy/config": "1.31.15-beta.0", + "@percy/env": "1.31.15-beta.0", + "@percy/logger": "1.31.15-beta.0", "pac-proxy-agent": "^7.0.2", "pako": "^2.1.0" }, @@ -223,12 +238,13 @@ } }, "node_modules/@percy/config": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/config/-/config-1.31.13.tgz", - "integrity": "sha512-gxd292C8+v/psxYJNfId2gwhlIGjvRGGOTCw9gBTb/eBT8cZirVLHq+d/j2nqPxCDC7JzZqChWH4TkVkTr/Xwg==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/config/-/config-1.31.15-beta.0.tgz", + "integrity": "sha512-ZmZip2svLFKoay0FYCpgRc7GEVljA+Od4LJhYMaEwcBIvv3PP04JNOHcFqa6Gp1hs8CpPAMoYylLZSg0Ux2vVA==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/logger": "1.31.13", + "@percy/logger": "1.31.15-beta.0", "ajv": "^8.6.2", "cosmiconfig": "^8.0.0", "yaml": "^2.0.0" @@ -238,18 +254,19 @@ } }, "node_modules/@percy/core": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/core/-/core-1.31.13.tgz", - "integrity": "sha512-SFVw/skIpcFecn8V3emAhmqqOgsX3K9toamMhhVay9FtAoy/NmsV+WuvP3IsAvujepY8ApZe2Zi5CuzZbmDy0w==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/core/-/core-1.31.15-beta.0.tgz", + "integrity": "sha512-c9lzKuEBrMlfyRGXm6Bvg8CcO/9qwYaizJOH2RBR14B+ta0DfC/bc8xY2Knc9L6Q3kdtFcuGXMzDCh8XBvvgdg==", "dev": true, "hasInstallScript": true, - "dependencies": { - "@percy/client": "1.31.13", - "@percy/config": "1.31.13", - "@percy/dom": "1.31.13", - "@percy/logger": "1.31.13", - "@percy/monitoring": "1.31.13", - "@percy/webdriver-utils": "1.31.13", + "license": "MIT", + "dependencies": { + "@percy/client": "1.31.15-beta.0", + "@percy/config": "1.31.15-beta.0", + "@percy/dom": "1.31.15-beta.0", + "@percy/logger": "1.31.15-beta.0", + "@percy/monitoring": "1.31.15-beta.0", + "@percy/webdriver-utils": "1.31.15-beta.0", "content-disposition": "^0.5.4", "cross-spawn": "^7.0.3", "extract-zip": "^2.0.1", @@ -266,45 +283,49 @@ "node": ">=14" }, "optionalDependencies": { - "@percy/cli-doctor": "1.31.13" + "@percy/cli-doctor": "1.31.15-beta.0" } }, "node_modules/@percy/dom": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/dom/-/dom-1.31.13.tgz", - "integrity": "sha512-5v/ybb2f4aQLUdsbQzTuHzQNAP5zcqn/aO18k2AjvnNdxNmtLC3XwMaSMznImQAXCGsSqsmrJTGtrYP3pbTPYw==", - "dev": true + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/dom/-/dom-1.31.15-beta.0.tgz", + "integrity": "sha512-ViZpaiRrY0oTxmolH0NkQXJAota7u1qablpi+xZnzaZFZdCyRZ6iAVlsbyJp2s59lgHqmj2ACQh22o3g1iAK2A==", + "dev": true, + "license": "MIT" }, "node_modules/@percy/env": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/env/-/env-1.31.13.tgz", - "integrity": "sha512-oYEPbZwEtRFfuso+V1lnAZkZZC1gSk+H+//AdLU5De1+h2jxYW14qc1bpW707ZSina5DY5C2tcbsHNV4lcYI/A==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/env/-/env-1.31.15-beta.0.tgz", + "integrity": "sha512-HhQTKp8SGJqDL7i9PeMjRMDJXgsgyG/wQO5U1u1f+fMD5Zeuqvc9jYYouFBtpCmR66ZMJzYnRZflcf8DBEcQJw==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/logger": "1.31.13" + "@percy/logger": "1.31.15-beta.0" }, "engines": { "node": ">=14" } }, "node_modules/@percy/logger": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/logger/-/logger-1.31.13.tgz", - "integrity": "sha512-ym8Viw3mAGv00afk4NwaODBXLcS7/pPtpa/RSPS0knToPbCvN4/aE9PoZ99M8UbKY02ZFqGAbBNL+uPgS1nKbA==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/logger/-/logger-1.31.15-beta.0.tgz", + "integrity": "sha512-SGjiHeiqcBHxGjyxsRxjzo2gnVV7mw6SnW36J0ao+lMPcTQ4Tyy8JhV1+An5RLmGxqRnwD5oX7i3NisZgj3kWg==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" } }, "node_modules/@percy/monitoring": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/monitoring/-/monitoring-1.31.13.tgz", - "integrity": "sha512-TNHrEA8juyvSM2Egnub9gEDi3p1yYXwGfa4F+H3Ub+zNWFZ/11vxFRQuryzN9aQha/hvcBnXFQJk8JR73I+T4g==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/monitoring/-/monitoring-1.31.15-beta.0.tgz", + "integrity": "sha512-R3OILiCiaEmT0fqf7SqdTHXn4r71ywMHTt9sWorbhAtB1yLQusIYBuFXndKR6gSeRZfxNDa9zGkJVs9U99qiVQ==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/config": "1.31.13", - "@percy/logger": "1.31.13", - "@percy/sdk-utils": "1.31.13", + "@percy/config": "1.31.15-beta.0", + "@percy/logger": "1.31.15-beta.0", + "@percy/sdk-utils": "1.31.15-beta.0", "systeminformation": "^5.25.11" }, "engines": { @@ -312,10 +333,11 @@ } }, "node_modules/@percy/sdk-utils": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.31.13.tgz", - "integrity": "sha512-0JW+ngBKLjkhbsI6ZD8wnWDV1U/S66X4vBrJqHLSi8t8BygQrulAwKLrtSV27DHCxz3a+zptTMBQufwFbal5gg==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.31.15-beta.0.tgz", + "integrity": "sha512-ab15RQ6/o458nPJK4bNJdv1rt3QV3PWT0vxrAOhiM2AAkgrJmRWhEmNqKcFrsKtmM2RXvW6rQPAI/ZUFRk/t1Q==", "dev": true, + "license": "MIT", "dependencies": { "pac-proxy-agent": "^7.0.2" }, @@ -324,13 +346,14 @@ } }, "node_modules/@percy/webdriver-utils": { - "version": "1.31.13", - "resolved": "https://registry.npmjs.org/@percy/webdriver-utils/-/webdriver-utils-1.31.13.tgz", - "integrity": "sha512-wiCPRI42Vg6+qL/wiDMPkpauBHbpPJoiaMZB0gDyzrjHJx8cPwG85+oUOgvtMDNdvy55jAtcSdqzA5kXcvMsOw==", + "version": "1.31.15-beta.0", + "resolved": "https://registry.npmjs.org/@percy/webdriver-utils/-/webdriver-utils-1.31.15-beta.0.tgz", + "integrity": "sha512-yjz2fQjzBk5mN28LjCLCKCOj0jJjCdak6+6/ndrtKqai9Ri2GJ43gUEUWBm+QfEbIOUcGKLiP1LWYCBZMxKKEg==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/config": "1.31.13", - "@percy/sdk-utils": "1.31.13" + "@percy/config": "1.31.15-beta.0", + "@percy/sdk-utils": "1.31.15-beta.0" }, "engines": { "node": ">=14" @@ -340,16 +363,18 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz", - "integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==", + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { - "undici-types": "~7.21.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/yauzl": { @@ -357,6 +382,7 @@ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "@types/node": "*" @@ -367,6 +393,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 14" } @@ -376,6 +403,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -391,13 +419,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -409,22 +439,25 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/basic-ftp": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -434,6 +467,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -446,6 +480,7 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -455,6 +490,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -463,13 +499,15 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" }, @@ -482,6 +520,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, + "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -508,6 +547,7 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -522,6 +562,7 @@ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 14" } @@ -531,6 +572,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -548,6 +590,7 @@ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, + "license": "MIT", "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -562,6 +605,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "dev": true, + "license": "MIT", "dependencies": { "once": "^1.4.0" } @@ -571,6 +615,7 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -580,6 +625,7 @@ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -601,6 +647,7 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -614,6 +661,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -623,6 +671,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -632,6 +681,7 @@ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -651,13 +701,15 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "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": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -683,13 +735,15 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -699,6 +753,7 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, + "license": "MIT", "dependencies": { "pend": "~1.2.0" } @@ -708,6 +763,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -719,13 +775,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -741,6 +799,7 @@ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", "dev": true, + "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", @@ -756,6 +815,7 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -776,6 +836,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -784,10 +845,11 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -798,6 +860,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -810,6 +873,7 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -823,6 +887,7 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -836,6 +901,7 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", "dev": true, + "license": "MIT", "dependencies": { "queue": "6.0.2" }, @@ -851,6 +917,7 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -868,6 +935,7 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -877,13 +945,15 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ip-address": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12" } @@ -892,13 +962,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -908,6 +980,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -920,6 +993,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -928,19 +1002,22 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -952,25 +1029,29 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -980,6 +1061,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -993,6 +1075,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1002,6 +1085,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -1014,6 +1098,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.2" }, @@ -1028,13 +1113,15 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/netmask": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -1044,6 +1131,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -1053,6 +1141,7 @@ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "dev": true, + "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", @@ -1072,6 +1161,7 @@ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, + "license": "MIT", "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -1084,13 +1174,15 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "dev": true + "dev": true, + "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -1103,6 +1195,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -1121,6 +1214,7 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1130,6 +1224,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1138,13 +1233,15 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1153,19 +1250,22 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -1178,6 +1278,7 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", "dev": true, + "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -1188,6 +1289,7 @@ "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "~2.0.3" } @@ -1210,13 +1312,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1226,6 +1330,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -1235,6 +1340,7 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -1246,6 +1352,7 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -1275,6 +1382,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -1297,13 +1405,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1316,6 +1426,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1325,6 +1436,7 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -1335,6 +1447,7 @@ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", "dev": true, + "license": "MIT", "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" @@ -1349,6 +1462,7 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -1363,6 +1477,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "optional": true, "engines": { "node": ">=0.10.0" @@ -1373,6 +1488,7 @@ "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.6.tgz", "integrity": "sha512-Uv2b2uGGM6ns+26czgW2cYRabYdnswM0ddSOOlryHOaelzsmDSet1iM/NT7VOYxW8x/BW+HkY+b1Ve2pLTSGSA==", "dev": true, + "license": "MIT", "os": [ "darwin", "linux", @@ -1399,6 +1515,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -1410,13 +1527,15 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true + "dev": true, + "license": "0BSD" }, "node_modules/undici-types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz", - "integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/which": { @@ -1424,6 +1543,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -1438,13 +1558,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -1466,6 +1588,7 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "dev": true, + "license": "ISC", "bin": { "yaml": "bin.mjs" }, @@ -1481,6 +1604,7 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, + "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0"