From 783b1a961b65eb7061f7ad1138a144289adf06bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 29 Jun 2026 12:14:08 +0200 Subject: [PATCH 1/2] feat(no-undef, no-global-assign): support host-supplied globals `no-undef` and `no-global-assign` previously consulted a single hand-maintained `GLOBALS` static, so DOM globals such as `document` were reported as "not defined" even when a project's `compilerOptions.lib` includes `"dom"` (denoland/deno#27379, #622, #590), and there was no way to reflect the configured environment. Add an optional `globals` field to `LintConfig`: a name -> writable map (`ConfiguredGlobals`) that, when present, fully replaces the built-in `GLOBALS` for both rules. The host (deno) derives it from the resolved TypeScript `lib`, keeping deno_lint free of any hardcoded per-lib tables. When absent, the built-in list is used, so default behavior is unchanged. Both rules now go through `Context::global_with_writable` / `Context::is_global` rather than reading `GLOBALS` directly, so the writable flag (e.g. `onmessage` may be reassigned, `Object` may not) keeps driving `no-global-assign`. --- examples/dlint/main.rs | 1 + src/context.rs | 28 ++++++++++++++++++ src/lib.rs | 2 ++ src/linter.rs | 24 +++++++++++++++ src/rules/no_global_assign.rs | 39 ++++++++++++++++++++++--- src/rules/no_undef.rs | 27 +++++++++++++++-- src/test_util.rs | 55 +++++++++++++++++++++++++++++++++-- 7 files changed, 167 insertions(+), 9 deletions(-) diff --git a/examples/dlint/main.rs b/examples/dlint/main.rs index 86c7184f..23a3c7fe 100644 --- a/examples/dlint/main.rs +++ b/examples/dlint/main.rs @@ -134,6 +134,7 @@ fn run_linter( config: LintConfig { default_jsx_factory: Some("React.createElement".to_string()), default_jsx_fragment_factory: Some("React.Fragment".to_string()), + globals: None, }, external_linter: None, })?; diff --git a/src/context.rs b/src/context.rs index 195c74b8..dd6ef3de 100644 --- a/src/context.rs +++ b/src/context.rs @@ -5,10 +5,12 @@ use crate::diagnostic::{ LintDiagnostic, LintDiagnosticDetails, LintDiagnosticRange, LintDiagnosticSeverity, LintDocsUrl, LintFix, }; +use crate::globals::GLOBALS; use crate::ignore_directives::{ parse_line_ignore_directives, CodeStatus, FileIgnoreDirective, LineIgnoreDirective, }; +use crate::linter::ConfiguredGlobals; use crate::linter::LinterContext; use crate::rules; use deno_ast::swc::ast::Expr; @@ -40,6 +42,7 @@ pub struct Context<'a> { jsx_factory: Option>>, #[allow(clippy::redundant_allocation)] // This type comes from SWC. jsx_fragment_factory: Option>>, + globals: Option, } impl<'a> Context<'a> { @@ -50,6 +53,7 @@ impl<'a> Context<'a> { file_ignore_directive: Option, default_jsx_factory: Option, default_jsx_fragment_factory: Option, + globals: Option, ) -> Self { let line_ignore_directives = parse_line_ignore_directives( linter_ctx.ignore_diagnostic_directive, @@ -117,6 +121,7 @@ impl<'a> Context<'a> { check_unknown_rules: linter_ctx.check_unknown_rules, jsx_factory, jsx_fragment_factory, + globals, } } @@ -194,6 +199,29 @@ impl<'a> Context<'a> { self.jsx_fragment_factory.clone() } + /// Looks up a global variable by name, returning its writability when the + /// name is a recognized global: `Some(true)` if the global may be reassigned, + /// `Some(false)` if it is read-only, and `None` if `name` is not a global. + /// + /// When the host has supplied a configured set of globals (derived from the + /// TypeScript `lib`), that set is consulted; otherwise the built-in `GLOBALS` + /// list is used. + pub fn global_with_writable(&self, name: &str) -> Option { + match &self.globals { + Some(globals) => globals.get(name).copied(), + None => GLOBALS + .iter() + .find(|(global, _)| *global == name) + .map(|(_, writable)| *writable), + } + } + + /// Returns `true` if `name` is a recognized global variable. See + /// [`Context::global_with_writable`] for how the global set is resolved. + pub fn is_global(&self, name: &str) -> bool { + self.global_with_writable(name).is_some() + } + /// The `SyntaxContext` of any unresolved identifiers pub(crate) fn unresolved_ctxt(&self) -> SyntaxContext { self.parsed_source.unresolved_context() diff --git a/src/lib.rs b/src/lib.rs index 6c6ff18d..5f2570f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,7 @@ mod lint_tests { config: LintConfig { default_jsx_factory: None, default_jsx_fragment_factory: None, + globals: None, }, external_linter: None, }) @@ -82,6 +83,7 @@ mod lint_tests { LintConfig { default_jsx_factory: None, default_jsx_fragment_factory: None, + globals: None, }, None, ) diff --git a/src/linter.rs b/src/linter.rs index 54fb3179..0963e58c 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -11,9 +11,20 @@ use deno_ast::MediaType; use deno_ast::ParsedSource; use deno_ast::{ModuleSpecifier, ParseDiagnostic}; use std::borrow::Cow; +use std::collections::HashMap; use std::collections::HashSet; use std::sync::Arc; +/// Set of global variables recognized by `no-undef` and `no-global-assign`, +/// keyed by name. The boolean value indicates whether the global may be +/// reassigned (`true` = writable, e.g. `onmessage`; `false` = read-only, e.g. +/// `Object`). +/// +/// The host derives this from the project's TypeScript `lib` configuration and +/// passes it via [`LintConfig::globals`]. It is wrapped in an [`Arc`] because +/// the same set is shared, unchanged, across every file in a lint run. +pub type ConfiguredGlobals = Arc>; + pub struct LinterOptions { /// Rules to lint with. pub rules: Vec>, @@ -94,6 +105,14 @@ pub struct LintFileOptions { pub struct LintConfig { pub default_jsx_factory: Option, pub default_jsx_fragment_factory: Option, + /// Optional override for the set of recognized global variables. + /// + /// When `Some`, it fully replaces the built-in `GLOBALS` list for the + /// `no-undef` and `no-global-assign` rules, letting the host derive the + /// available globals from the configured TypeScript `lib` (e.g. adding the + /// DOM globals when `"dom"` is in `lib`). When `None`, the built-in list is + /// used, preserving the default behavior. + pub globals: Option, } impl Linter { @@ -125,6 +144,7 @@ impl Linter { &parsed_source, options.config.default_jsx_factory, options.config.default_jsx_fragment_factory, + options.config.globals, options.external_linter, ); @@ -146,6 +166,7 @@ impl Linter { parsed_source, config.default_jsx_factory, config.default_jsx_fragment_factory, + config.globals, maybe_external_linter, ) } @@ -192,6 +213,7 @@ impl Linter { parsed_source: &ParsedSource, default_jsx_factory: Option, default_jsx_fragment_factory: Option, + globals: Option, maybe_external_linter: Option, ) -> Vec { let _mark = PerformanceMark::new("Linter::lint_inner"); @@ -227,6 +249,7 @@ impl Linter { file_ignore_directive, default_jsx_factory, default_jsx_fragment_factory, + globals.clone(), ); // Run configured lint rules. @@ -274,6 +297,7 @@ mod tests { config: LintConfig { default_jsx_factory: None, default_jsx_fragment_factory: None, + globals: None, }, external_linter: None, }) diff --git a/src/rules/no_global_assign.rs b/src/rules/no_global_assign.rs index 2b6783a2..c2e129d7 100644 --- a/src/rules/no_global_assign.rs +++ b/src/rules/no_global_assign.rs @@ -2,9 +2,9 @@ use super::{Context, LintRule}; use crate::handler::{Handler, Traverse}; +use crate::swc_util::find_lhs_ids; use crate::tags::{self, Tags}; use crate::Program; -use crate::{globals::GLOBALS, swc_util::find_lhs_ids}; use deno_ast::swc::ast::Id; use deno_ast::SourceRange; use deno_ast::SourceRangedForSpanned; @@ -59,11 +59,11 @@ impl NoGlobalAssignVisitor { } // We only care about globals. - let maybe_global = GLOBALS.iter().find(|(name, _)| name == &&*id.0); + let maybe_writable = ctx.global_with_writable(&id.0); - if let Some(global) = maybe_global { + if let Some(writable) = maybe_writable { // If global can be overwritten then don't need to report anything - if !global.1 { + if !writable { ctx.add_diagnostic_with_hint( range, CODE, @@ -167,4 +167,35 @@ Boolean = true; ], }; } + + // When the host supplies a set of globals, their writability flag drives the + // rule: read-only globals can't be reassigned, writable ones can. + #[test] + fn no_global_assign_configured_globals() { + use crate::test_util::{ + assert_lint_ok_with_globals, assert_lint_some_with_globals, globals, + }; + + // A read-only DOM global may not be reassigned. + assert_lint_some_with_globals( + Box::new(NoGlobalAssign), + "document = 1;", + globals(&[("document", false)]), + ); + + // A writable global may be reassigned without complaint. + assert_lint_ok_with_globals( + Box::new(NoGlobalAssign), + "onmessage = function () {};", + globals(&[("onmessage", true)]), + ); + + // Built-in globals are no longer consulted once the host supplies a set: + // `Object` isn't in the supplied list, so assigning to it is allowed. + assert_lint_ok_with_globals( + Box::new(NoGlobalAssign), + "Object = 1;", + globals(&[("document", false)]), + ); + } } diff --git a/src/rules/no_undef.rs b/src/rules/no_undef.rs index ee0f4d57..a5b6bd09 100644 --- a/src/rules/no_undef.rs +++ b/src/rules/no_undef.rs @@ -2,7 +2,6 @@ use super::program_ref; use super::{Context, LintRule}; -use crate::globals::GLOBALS; use crate::Program; use crate::ProgramRef; use deno_ast::swc::{ @@ -62,7 +61,7 @@ impl<'c, 'view> NoUndefVisitor<'c, 'view> { } // Globals - if GLOBALS.iter().any(|(name, _)| name == &&*ident.sym) { + if self.context.is_global(&ident.sym) { return; } @@ -390,4 +389,28 @@ mod tests { ], }; } + + // When the host supplies a set of globals (derived from the configured + // TypeScript `lib`), those names are recognized as defined and replace the + // built-in list. See https://github.com/denoland/deno_lint/issues/622 and + // denoland/deno#27379. + #[test] + fn no_undef_configured_globals() { + use crate::test_util::{ + assert_lint_ok_with_globals, assert_lint_some_with_globals, globals, + }; + + let dom = || globals(&[("document", false), ("HTMLElement", false)]); + + // DOM globals are recognized when supplied. + assert_lint_ok_with_globals(Box::new(NoUndef), "document.body;", dom()); + assert_lint_ok_with_globals(Box::new(NoUndef), "new HTMLElement();", dom()); + + // Typos are still reported. + assert_lint_some_with_globals(Box::new(NoUndef), "documentt;", dom()); + + // The supplied set fully replaces the built-in list: a Deno runtime global + // is no longer recognized when the host doesn't include it. + assert_lint_some_with_globals(Box::new(NoUndef), "Deno;", dom()); + } } diff --git a/src/test_util.rs b/src/test_util.rs index 3746354b..df5aa1dd 100644 --- a/src/test_util.rs +++ b/src/test_util.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use crate::ast_parser; use crate::diagnostic::LintDiagnostic; +use crate::linter::ConfiguredGlobals; use crate::linter::LintConfig; use crate::linter::LintFileOptions; use crate::linter::Linter; @@ -205,7 +206,8 @@ impl LintErrTester { #[track_caller] pub fn run(self) { let rule_code = self.rule.code(); - let (parsed_source, diagnostics) = lint(self.rule, self.src, self.filename); + let (parsed_source, diagnostics) = + lint(self.rule, self.src, self.filename, None); if self.errors.len() != diagnostics.len() { eprintln!( "Actual diagnostics:\n{:#?}", @@ -322,6 +324,7 @@ fn lint( rule: Box, source: &str, specifier: &str, + globals: Option, ) -> (ParsedSource, Vec) { let linter = Linter::new(LinterOptions { rules: vec![rule], @@ -343,6 +346,7 @@ fn lint( config: LintConfig { default_jsx_factory: Some("React.createElement".to_owned()), default_jsx_fragment_factory: Some("React.Fragment".to_owned()), + globals, }, external_linter: None, }); @@ -461,7 +465,7 @@ pub fn assert_lint_ok( source: &str, specifier: &'static str, ) { - let (_parsed_source, diagnostics) = lint(rule, source, specifier); + let (_parsed_source, diagnostics) = lint(rule, source, specifier, None); if !diagnostics.is_empty() { eprintln!("filename {:?}", specifier); panic!( @@ -474,7 +478,52 @@ pub fn assert_lint_ok( /// Just run the specified lint on the source code to make sure it doesn't panic. pub fn assert_lint_not_panic(rule: Box, source: &str) { - let _result = lint(rule, source, TEST_FILE_NAME); + let _result = lint(rule, source, TEST_FILE_NAME, None); +} + +/// Builds a [`ConfiguredGlobals`] set from `(name, writable)` pairs, mirroring +/// what the host derives from the TypeScript `lib` configuration. +pub fn globals(entries: &[(&str, bool)]) -> ConfiguredGlobals { + std::sync::Arc::new( + entries + .iter() + .map(|(name, writable)| (name.to_string(), *writable)) + .collect(), + ) +} + +/// Like [`assert_lint_ok`] but lints with a host-supplied set of globals. +pub fn assert_lint_ok_with_globals( + rule: Box, + source: &str, + globals: ConfiguredGlobals, +) { + let (_parsed_source, diagnostics) = + lint(rule, source, TEST_FILE_NAME, Some(globals)); + if !diagnostics.is_empty() { + panic!( + "Unexpected diagnostics found:\n{:#?}\n\nsource:\n{}\n", + diagnostics.iter().map(|d| d.message()).collect::>(), + source + ); + } +} + +/// Like [`assert_lint_ok`] but asserts that linting with a host-supplied set of +/// globals produces at least one diagnostic. +pub fn assert_lint_some_with_globals( + rule: Box, + source: &str, + globals: ConfiguredGlobals, +) { + let (_parsed_source, diagnostics) = + lint(rule, source, TEST_FILE_NAME, Some(globals)); + if diagnostics.is_empty() { + panic!( + "Expected diagnostics but found none.\n\nsource:\n{}\n", + source + ); + } } const TEST_FILE_NAME: &str = "file:///lint_test.ts"; From 8bedeb86a02587f47738fbc18a9e35f5b21435c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 29 Jun 2026 13:08:07 +0200 Subject: [PATCH 2/2] perf(globals): back built-in GLOBALS with a phf::Map The None branch of Context::global_with_writable did an O(n) linear scan over the ~210-entry GLOBALS slice for every identifier. Convert GLOBALS to a compile-time phf::Map so both the built-in and host-supplied lookups are O(1). GLOBALS is private (mod globals is not pub), so the type change is not a public API break. Also removes a duplicate TransformStream entry that was harmless for the linear scan but rejected by phf::phf_map! as a duplicate key. --- src/context.rs | 5 +- src/globals.rs | 425 ++++++++++++++++++++++++------------------------- 2 files changed, 213 insertions(+), 217 deletions(-) diff --git a/src/context.rs b/src/context.rs index dd6ef3de..1d56fcf7 100644 --- a/src/context.rs +++ b/src/context.rs @@ -209,10 +209,7 @@ impl<'a> Context<'a> { pub fn global_with_writable(&self, name: &str) -> Option { match &self.globals { Some(globals) => globals.get(name).copied(), - None => GLOBALS - .iter() - .find(|(global, _)| *global == name) - .map(|(_, writable)| *writable), + None => GLOBALS.get(name).copied(), } } diff --git a/src/globals.rs b/src/globals.rs index 0ed5f3c2..523c8c3c 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -5,216 +5,215 @@ /// Boolean tells if global can be overwritten /// /// Adapted from https://www.npmjs.com/package/globals -pub static GLOBALS: &[(&str, bool)] = &[ - ("AbortController", false), - ("AbortSignal", false), - ("addEventListener", false), - ("AggregateError", false), - ("alert", false), - ("Array", false), - ("ArrayBuffer", false), - ("atob", false), - ("Atomics", false), - ("BigInt", false), - ("BigInt64Array", false), - ("BigUint64Array", false), - ("Blob", false), - ("Boolean", false), - ("BroadcastChannel", false), - ("btoa", false), - ("ByteLengthQueuingStrategy", false), - ("Cache", false), - ("caches", false), - ("CacheStorage", false), - ("clearInterval", false), - ("clearTimeout", false), - ("close", false), - ("closed", false), - ("CloseEvent", false), - ("CompressionStream", false), - ("confirm", false), - ("console", false), - ("constructor", false), - ("CountQueuingStrategy", false), - ("createImageBitmap", false), - ("crypto", false), - ("Crypto", false), - ("CryptoKey", false), - ("CustomEvent", false), - ("DataView", false), - ("Date", false), - ("decodeURI", false), - ("decodeURIComponent", false), - ("DecompressionStream", false), - ("DedicatedWorkerGlobalScope", false), - ("Deno", false), - ("dispatchEvent", false), - ("DOMException", false), - ("encodeURI", false), - ("encodeURIComponent", false), - ("Error", false), - ("ErrorEvent", false), - ("escape", false), - ("eval", false), - ("EvalError", false), - ("Event", false), - ("EventSource", false), - ("EventTarget", false), - ("fetch", false), - ("File", false), - ("FileReader", false), - ("FinalizationRegistry", false), - ("Float16Array", false), - ("Float32Array", false), - ("Float64Array", false), - ("FormData", false), - ("Function", false), - ("globalThis", false), - ("GPU", false), - ("GPUAdapter", false), - ("GPUAdapterInfo", false), - ("GPUBindGroup", false), - ("GPUBindGroupLayout", false), - ("GPUBuffer", false), - ("GPUBufferUsage", false), - ("GPUCanvasContext", false), - ("GPUColorWrite", false), - ("GPUCommandBuffer", false), - ("GPUCommandEncoder", false), - ("GPUComputePassEncoder", false), - ("GPUComputePipeline", false), - ("GPUDevice", false), - ("GPUDeviceLostInfo", false), - ("GPUError", false), - ("GPUMapMode", false), - ("GPUOutOfMemoryError", false), - ("GPUPipelineLayout", false), - ("GPUQuerySet", false), - ("GPUQueue", false), - ("GPURenderBundle", false), - ("GPURenderBundleEncoder", false), - ("GPURenderPassEncoder", false), - ("GPURenderPipeline", false), - ("GPUSampler", false), - ("GPUShaderModule", false), - ("GPUShaderStage", false), - ("GPUSupportedFeatures", false), - ("GPUSupportedLimits", false), - ("GPUTexture", false), - ("GPUTextureUsage", false), - ("GPUTextureView", false), - ("GPUValidationError", false), - ("hasOwnProperty", false), - ("Headers", false), - ("ImageBitmap", false), - ("ImageData", false), - ("Infinity", false), - ("Intl", false), - ("Int16Array", false), - ("Int32Array", false), - ("Int8Array", false), - ("isFinite", false), - ("isNaN", false), - ("isPrototypeOf", false), - ("JSON", false), - ("localStorage", false), - ("location", false), - ("Location", false), - ("Map", false), - ("Math", false), - ("MessageChannel", false), - ("MessageEvent", false), - ("MessagePort", false), - ("NaN", false), - ("navigator", false), - ("Navigator", false), - ("Number", false), - ("Object", false), - ("onbeforeunload", true), - ("onerror", true), - ("onload", true), - ("onmessage", true), - ("onmessageerror", true), - ("onunhandledrejection", true), - ("onunload", true), - ("parseFloat", false), - ("parseInt", false), - ("performance", false), - ("Performance", false), - ("PerformanceEntry", false), - ("PerformanceMark", false), - ("PerformanceMeasure", false), - ("Permissions", false), - ("PermissionStatus", false), - ("postMessage", true), - ("ProgressEvent", false), - ("Promise", false), - ("PromiseRejectionEvent", false), - ("prompt", false), - ("propertyIsEnumerable", false), - ("Proxy", false), - ("queueMicrotask", false), - ("RangeError", false), - ("ReadableStream", false), - ("ReadableByteStreamController", false), - ("ReadableStreamBYOBReader", false), - ("ReadableStreamBYOBRequest", false), - ("ReadableStreamDefaultController", false), - ("ReadableStreamDefaultReader", false), - ("ReferenceError", false), - ("Reflect", false), - ("RegExp", false), - ("removeEventListener", false), - ("reportError", false), - ("Request", false), - ("Response", false), - ("self", false), - ("sessionStorage", false), - ("Set", false), - ("setInterval", false), - ("setTimeout", false), - ("SharedArrayBuffer", false), - ("Storage", false), - ("String", false), - ("structuredClone", false), - ("SubtleCrypto", false), - ("Symbol", false), - ("SyntaxError", false), - ("TextDecoder", false), - ("TextDecoderStream", false), - ("TextEncoder", false), - ("TextEncoderStream", false), - ("TransformStream", false), - ("TransformStreamDefaultController", false), - ("toLocaleString", false), - ("toString", false), - ("TransformStream", false), - ("TypeError", false), - ("Uint16Array", false), - ("Uint32Array", false), - ("Uint8Array", false), - ("Uint8ClampedArray", false), - ("undefined", false), - ("unescape", false), - ("URIError", false), - ("URL", false), - ("URLPattern", false), - ("URLSearchParams", false), - ("valueOf", false), - ("WeakMap", false), - ("WeakRef", false), - ("WeakSet", false), - ("WebAssembly", false), - ("WebSocket", false), - ("WebSocketError", false), - ("WebSocketStream", false), - ("window", false), - ("Window", false), - ("Worker", false), - ("WorkerGlobalScope", false), - ("WorkerLocation", false), - ("WorkerNavigator", false), - ("WritableStream", false), - ("WritableStreamDefaultController", false), - ("WritableStreamDefaultWriter", false), -]; +pub static GLOBALS: phf::Map<&'static str, bool> = phf::phf_map! { + "AbortController" => false, + "AbortSignal" => false, + "addEventListener" => false, + "AggregateError" => false, + "alert" => false, + "Array" => false, + "ArrayBuffer" => false, + "atob" => false, + "Atomics" => false, + "BigInt" => false, + "BigInt64Array" => false, + "BigUint64Array" => false, + "Blob" => false, + "Boolean" => false, + "BroadcastChannel" => false, + "btoa" => false, + "ByteLengthQueuingStrategy" => false, + "Cache" => false, + "caches" => false, + "CacheStorage" => false, + "clearInterval" => false, + "clearTimeout" => false, + "close" => false, + "closed" => false, + "CloseEvent" => false, + "CompressionStream" => false, + "confirm" => false, + "console" => false, + "constructor" => false, + "CountQueuingStrategy" => false, + "createImageBitmap" => false, + "crypto" => false, + "Crypto" => false, + "CryptoKey" => false, + "CustomEvent" => false, + "DataView" => false, + "Date" => false, + "decodeURI" => false, + "decodeURIComponent" => false, + "DecompressionStream" => false, + "DedicatedWorkerGlobalScope" => false, + "Deno" => false, + "dispatchEvent" => false, + "DOMException" => false, + "encodeURI" => false, + "encodeURIComponent" => false, + "Error" => false, + "ErrorEvent" => false, + "escape" => false, + "eval" => false, + "EvalError" => false, + "Event" => false, + "EventSource" => false, + "EventTarget" => false, + "fetch" => false, + "File" => false, + "FileReader" => false, + "FinalizationRegistry" => false, + "Float16Array" => false, + "Float32Array" => false, + "Float64Array" => false, + "FormData" => false, + "Function" => false, + "globalThis" => false, + "GPU" => false, + "GPUAdapter" => false, + "GPUAdapterInfo" => false, + "GPUBindGroup" => false, + "GPUBindGroupLayout" => false, + "GPUBuffer" => false, + "GPUBufferUsage" => false, + "GPUCanvasContext" => false, + "GPUColorWrite" => false, + "GPUCommandBuffer" => false, + "GPUCommandEncoder" => false, + "GPUComputePassEncoder" => false, + "GPUComputePipeline" => false, + "GPUDevice" => false, + "GPUDeviceLostInfo" => false, + "GPUError" => false, + "GPUMapMode" => false, + "GPUOutOfMemoryError" => false, + "GPUPipelineLayout" => false, + "GPUQuerySet" => false, + "GPUQueue" => false, + "GPURenderBundle" => false, + "GPURenderBundleEncoder" => false, + "GPURenderPassEncoder" => false, + "GPURenderPipeline" => false, + "GPUSampler" => false, + "GPUShaderModule" => false, + "GPUShaderStage" => false, + "GPUSupportedFeatures" => false, + "GPUSupportedLimits" => false, + "GPUTexture" => false, + "GPUTextureUsage" => false, + "GPUTextureView" => false, + "GPUValidationError" => false, + "hasOwnProperty" => false, + "Headers" => false, + "ImageBitmap" => false, + "ImageData" => false, + "Infinity" => false, + "Intl" => false, + "Int16Array" => false, + "Int32Array" => false, + "Int8Array" => false, + "isFinite" => false, + "isNaN" => false, + "isPrototypeOf" => false, + "JSON" => false, + "localStorage" => false, + "location" => false, + "Location" => false, + "Map" => false, + "Math" => false, + "MessageChannel" => false, + "MessageEvent" => false, + "MessagePort" => false, + "NaN" => false, + "navigator" => false, + "Navigator" => false, + "Number" => false, + "Object" => false, + "onbeforeunload" => true, + "onerror" => true, + "onload" => true, + "onmessage" => true, + "onmessageerror" => true, + "onunhandledrejection" => true, + "onunload" => true, + "parseFloat" => false, + "parseInt" => false, + "performance" => false, + "Performance" => false, + "PerformanceEntry" => false, + "PerformanceMark" => false, + "PerformanceMeasure" => false, + "Permissions" => false, + "PermissionStatus" => false, + "postMessage" => true, + "ProgressEvent" => false, + "Promise" => false, + "PromiseRejectionEvent" => false, + "prompt" => false, + "propertyIsEnumerable" => false, + "Proxy" => false, + "queueMicrotask" => false, + "RangeError" => false, + "ReadableStream" => false, + "ReadableByteStreamController" => false, + "ReadableStreamBYOBReader" => false, + "ReadableStreamBYOBRequest" => false, + "ReadableStreamDefaultController" => false, + "ReadableStreamDefaultReader" => false, + "ReferenceError" => false, + "Reflect" => false, + "RegExp" => false, + "removeEventListener" => false, + "reportError" => false, + "Request" => false, + "Response" => false, + "self" => false, + "sessionStorage" => false, + "Set" => false, + "setInterval" => false, + "setTimeout" => false, + "SharedArrayBuffer" => false, + "Storage" => false, + "String" => false, + "structuredClone" => false, + "SubtleCrypto" => false, + "Symbol" => false, + "SyntaxError" => false, + "TextDecoder" => false, + "TextDecoderStream" => false, + "TextEncoder" => false, + "TextEncoderStream" => false, + "TransformStream" => false, + "TransformStreamDefaultController" => false, + "toLocaleString" => false, + "toString" => false, + "TypeError" => false, + "Uint16Array" => false, + "Uint32Array" => false, + "Uint8Array" => false, + "Uint8ClampedArray" => false, + "undefined" => false, + "unescape" => false, + "URIError" => false, + "URL" => false, + "URLPattern" => false, + "URLSearchParams" => false, + "valueOf" => false, + "WeakMap" => false, + "WeakRef" => false, + "WeakSet" => false, + "WebAssembly" => false, + "WebSocket" => false, + "WebSocketError" => false, + "WebSocketStream" => false, + "window" => false, + "Window" => false, + "Worker" => false, + "WorkerGlobalScope" => false, + "WorkerLocation" => false, + "WorkerNavigator" => false, + "WritableStream" => false, + "WritableStreamDefaultController" => false, + "WritableStreamDefaultWriter" => false, +};