code-gauge 10.3.0 → 10.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,29 +1,29 @@
1
- use rustc_hash::FxHashSet;
2
1
  use tree_sitter::Node;
3
2
 
4
- use crate::tree_index::NodeExt;
3
+ use crate::tree_index::{KindSet, NodeExt};
5
4
  use crate::util::find_children_by_field_name;
6
5
 
7
6
  pub const COMMENT_NODE_TYPES: &[&str] = &["comment", "line_comment", "block_comment"];
8
7
 
9
8
  /// Nodes never counted positionally inside NCSS containers: metadata, empty statements, Ruby
10
9
  /// heredoc bodies (tree-sitter emits them as siblings of the statement that opened the heredoc),
11
- /// Ruby statement parentheses (transparent wrappers whose children count instead), Kotlin enum
12
- /// entries (Java enum constants are not counted either), and Kotlin lambda parameters (siblings of
13
- /// the lambda's statements). Kotlin's `try_expression` is excluded contextually below because Rust
10
+ /// TRANSPARENT_WRAPPER_TYPES (their children count instead), Kotlin enum entries (Java enum
11
+ /// constants are not counted either), and Kotlin lambda parameters (siblings of the lambda's
12
+ /// statements). Kotlin's `try_expression` is excluded contextually below because Rust
14
13
  /// shares its name: Rust's `?` operator must keep counting as a tail expression.
15
- const POSITIONAL_EXCLUSION_TYPES: &[&str] = &[
14
+ static POSITIONAL_EXCLUSION_KINDS: KindSet = KindSet::new(&[
16
15
  "attribute_item",
17
16
  "inner_attribute_item",
18
17
  "empty_statement",
19
18
  "heredoc_body",
20
19
  "parenthesized_statements",
20
+ "attributed_declaration",
21
21
  "annotation",
22
22
  "file_annotation",
23
23
  "shebang",
24
24
  "enum_entry",
25
25
  "lambda_parameters",
26
- ];
26
+ ]);
27
27
 
28
28
  /// TypeScript interface members count like Java interface members, but the same node types appear
29
29
  /// inside object-type annotations (`let x: { a: number }`), which are part of one declaration, so
@@ -58,9 +58,9 @@ const BODYLESS_NCSS_SPECIFIER_TYPES: &[&str] = &[
58
58
  pub fn ncss_contribution(
59
59
  node: Node<'_>,
60
60
  parent: Option<Node<'_>>,
61
- countable: &FxHashSet<&'static str>,
62
- containers: &FxHashSet<&'static str>,
63
- bare_body_parents: &FxHashSet<&'static str>,
61
+ countable: &KindSet,
62
+ containers: &KindSet,
63
+ bare_body_parents: &KindSet,
64
64
  ) -> u64 {
65
65
  if !node.is_named()
66
66
  || COMMENT_NODE_TYPES.contains(&node.kind_name())
@@ -81,8 +81,8 @@ pub fn ncss_contribution(
81
81
  // it stands like any other expression.
82
82
  let positional = (is_in_container_position(parent, containers)
83
83
  || is_bare_body(node, parent, bare_body_parents))
84
- && (!containers.contains(node.kind_name()) || node.kind_name() == "lambda_literal")
85
- && !POSITIONAL_EXCLUSION_TYPES.contains(&node.kind_name())
84
+ && (!containers.contains(node) || node.kind_name() == "lambda_literal")
85
+ && !POSITIONAL_EXCLUSION_KINDS.contains(node)
86
86
  && !crate::util::is_kotlin_try_expression(node);
87
87
  if (counts_through_node_type(node, countable)
88
88
  || positional
@@ -92,6 +92,12 @@ pub fn ncss_contribution(
92
92
  contribution += 1;
93
93
  }
94
94
 
95
+ // A Rust `else if` clause also stands for the `if` that other grammars nest in their else
96
+ // clause and charge separately.
97
+ if crate::util::is_else_if_clause(node) {
98
+ contribution += 1;
99
+ }
100
+
95
101
  // A bare else branch (Java/Go/Kotlin `alternative:` without an else-clause wrapper) counts 1
96
102
  // like the `else` keyword does in PMD; an `else if` chain charges the nested if separately on
97
103
  // top.
@@ -102,8 +108,8 @@ pub fn ncss_contribution(
102
108
  contribution
103
109
  }
104
110
 
105
- fn counts_through_node_type(node: Node<'_>, countable: &FxHashSet<&'static str>) -> bool {
106
- if !countable.contains(node.kind_name()) {
111
+ fn counts_through_node_type(node: Node<'_>, countable: &KindSet) -> bool {
112
+ if !countable.contains(node) {
107
113
  return false;
108
114
  }
109
115
  if BODYLESS_NCSS_SPECIFIER_TYPES.contains(&node.kind_name()) {
@@ -117,16 +123,17 @@ fn counts_through_node_type(node: Node<'_>, countable: &FxHashSet<&'static str>)
117
123
  true
118
124
  }
119
125
 
120
- /// Direct container children count positionally; Ruby's `(foo; bar)` statement parentheses are
121
- /// transparent, so their children count when the parentheses themselves sit in a container.
122
- fn is_in_container_position(
123
- parent: Option<Node<'_>>,
124
- containers: &FxHashSet<&'static str>,
125
- ) -> bool {
126
+ /// Ruby's `(foo; bar)` statement parentheses and Rust's attributes-plus-declaration pairs: their
127
+ /// children count when the wrapper itself sits in a container.
128
+ const TRANSPARENT_WRAPPER_TYPES: &[&str] = &["parenthesized_statements", "attributed_declaration"];
129
+
130
+ /// Direct container children count positionally, as do the children of TRANSPARENT_WRAPPER_TYPES
131
+ /// in that position.
132
+ fn is_in_container_position(parent: Option<Node<'_>>, containers: &KindSet) -> bool {
126
133
  let mut ancestor = parent;
127
134
  while let Some(current) = ancestor {
128
- if current.kind_name() != "parenthesized_statements" {
129
- return containers.contains(current.kind_name());
135
+ if !TRANSPARENT_WRAPPER_TYPES.contains(&current.kind_name()) {
136
+ return containers.contains(current);
130
137
  }
131
138
  ancestor = current.parent_node();
132
139
  }
@@ -135,12 +142,8 @@ fn is_in_container_position(
135
142
 
136
143
  /// A braceless branch or loop body (`if (x) foo()`) stands for the single statement a braced body
137
144
  /// would hold.
138
- fn is_bare_body(
139
- node: Node<'_>,
140
- parent: Option<Node<'_>>,
141
- bare_body_parents: &FxHashSet<&'static str>,
142
- ) -> bool {
143
- parent.is_some_and(|parent| bare_body_parents.contains(parent.kind_name()))
145
+ fn is_bare_body(node: Node<'_>, parent: Option<Node<'_>>, bare_body_parents: &KindSet) -> bool {
146
+ parent.is_some_and(|parent| bare_body_parents.contains(parent))
144
147
  && ["consequence", "alternative", "body"]
145
148
  .iter()
146
149
  .any(|field_name| is_field_of_parent(node, parent, field_name))
@@ -148,13 +151,13 @@ fn is_bare_body(
148
151
 
149
152
  /// `export const x = 1` nests a countable declaration inside `export_statement`; only the inner
150
153
  /// declaration counts, mirroring how PMD counts one statement per declared entity.
151
- fn is_declaration_wrapper(node: Node<'_>, countable: &FxHashSet<&'static str>) -> bool {
154
+ fn is_declaration_wrapper(node: Node<'_>, countable: &KindSet) -> bool {
152
155
  if node.kind_name() != "export_statement" {
153
156
  return false;
154
157
  }
155
158
  node.child_by_field_name("declaration")
156
159
  .is_some_and(|declaration| {
157
- countable.contains(declaration.kind_name())
160
+ countable.contains(declaration)
158
161
  || declaration.kind_name() == "internal_module"
159
162
  || declaration.kind_name() == "ambient_declaration"
160
163
  })
@@ -238,12 +241,18 @@ fn counts_contextually(node: Node<'_>, parent: Option<Node<'_>>) -> bool {
238
241
  child.kind_name() == "declaration" || child.kind_name() == "function_definition"
239
242
  });
240
243
  }
241
- // A Rust item-position macro invocation (`foo! {}` at module level) has no expression_statement
242
- // wrapper; the semicolon form does and already counts through it.
243
- if node.kind_name() == "macro_invocation"
244
- && (parent_kind == Some("source_file") || parent_kind == Some("declaration_list"))
245
- {
246
- return true;
244
+ // A Rust item-position macro invocation (`foo! {}` at module level, possibly attributed) has no
245
+ // expression_statement wrapper; the semicolon form does and already counts through it.
246
+ if node.kind_name() == "macro_invocation" {
247
+ let item_parent = match parent {
248
+ Some(wrapper) if wrapper.kind_name() == "attributed_declaration" => {
249
+ wrapper.parent_node()
250
+ }
251
+ _ => parent,
252
+ };
253
+ return item_parent.is_some_and(|item_parent| {
254
+ matches!(item_parent.kind_name(), "source_file" | "declaration_list")
255
+ });
247
256
  }
248
257
  // Go struct fields and interface members count like other languages' member declarations, but
249
258
  // only inside a named type declaration; inline anonymous types (`var x struct{ ... }`,
@@ -1,19 +1,32 @@
1
1
  use std::cell::{Cell, RefCell};
2
2
  use std::marker::PhantomData;
3
+ use std::sync::OnceLock;
3
4
 
4
5
  use rustc_hash::FxHashMap;
5
6
  use tree_sitter::{Node, Tree};
6
7
 
7
- use crate::languages::LanguageDefinition;
8
+ use crate::languages::{LanguageDefinition, GRAMMAR_COUNT};
9
+
10
+ /// A node of the indexed tree: its parent and where its children lie in CHILDREN.
11
+ struct IndexedNode {
12
+ parent: Option<Node<'static>>,
13
+ first_child: u32,
14
+ child_count: u32,
15
+ }
8
16
 
9
17
  thread_local! {
10
- /// Node id -> parent for the tree indexed on this thread. The lifetime is erased to store the
11
- /// nodes; TreeIndex borrows the tree and clears the map on drop, so no entry outlives it.
12
- static PARENTS: RefCell<FxHashMap<usize, Node<'static>>> = RefCell::new(FxHashMap::default());
18
+ /// Node id -> parent and children for the tree indexed on this thread. The lifetime is erased
19
+ /// to store the nodes; TreeIndex borrows the tree and clears the tables on drop, so no entry
20
+ /// outlives it.
21
+ static NODES: RefCell<FxHashMap<usize, IndexedNode>> = RefCell::new(FxHashMap::default());
22
+ /// The children of every indexed node with their field names, each node's children contiguous.
23
+ static CHILDREN: RefCell<Vec<(Node<'static>, Option<&'static str>)>> = const { RefCell::new(Vec::new()) };
13
24
  /// Node kind names of the indexed tree's language, indexed by kind id.
14
25
  static KIND_NAMES: Cell<&'static [String]> = const { Cell::new(&[]) };
15
26
  /// Name of the indexed tree's language.
16
27
  static LANGUAGE_NAME: Cell<&'static str> = const { Cell::new("") };
28
+ /// LanguageDefinition::grammar_index of the indexed tree's language.
29
+ static GRAMMAR_INDEX: Cell<Option<usize>> = const { Cell::new(None) };
17
30
  }
18
31
 
19
32
  /// The language of the tree indexed on this thread, or "" when none is.
@@ -22,8 +35,9 @@ pub fn language_name() -> &'static str {
22
35
  }
23
36
 
24
37
  /// Makes NodeExt lookups O(1) while the tree is being measured: tree-sitter's `Node::parent`
25
- /// rescans from the root on every call (linear in the sibling counts along the path), and
26
- /// `Node::kind` measures and UTF-8-validates the C string on every call.
38
+ /// rescans from the root on every call (linear in the sibling counts along the path), each
39
+ /// `Node::children` walk runs a fresh cursor, and `Node::kind` measures and UTF-8-validates the C
40
+ /// string on every call.
27
41
  pub struct TreeIndex<'t> {
28
42
  tree: PhantomData<&'t Tree>,
29
43
  }
@@ -38,51 +52,104 @@ impl<'t> TreeIndex<'t> {
38
52
  let index = TreeIndex { tree: PhantomData };
39
53
  KIND_NAMES.set(language.kind_names());
40
54
  LANGUAGE_NAME.set(language.name);
41
- PARENTS.with_borrow_mut(|parents| {
42
- assert!(
43
- parents.is_empty(),
44
- "one tree is indexed per thread at a time"
45
- );
46
- let mut cursor = tree.walk();
47
- let mut stack: Vec<Node<'static>> = Vec::new();
48
- loop {
49
- let node = erase_lifetime(cursor.node());
50
- if let Some(&parent) = stack.last() {
51
- parents.insert(node.id(), parent);
52
- }
53
- if cursor.goto_first_child() {
54
- stack.push(node);
55
- if stack.len() > MAX_TREE_DEPTH {
56
- return Err(format!("tree depth exceeds {MAX_TREE_DEPTH}"));
57
- }
58
- continue;
59
- }
60
- while !cursor.goto_next_sibling() {
61
- if !cursor.goto_parent() {
62
- return Ok(());
63
- }
64
- stack.pop();
65
- }
66
- }
55
+ GRAMMAR_INDEX.set(Some(language.grammar_index()));
56
+ NODES.with_borrow_mut(|nodes| {
57
+ CHILDREN.with_borrow_mut(|children| {
58
+ assert!(nodes.is_empty(), "one tree is indexed per thread at a time");
59
+ index_tree(tree, nodes, children)
60
+ })
67
61
  })?;
68
62
  Ok(index)
69
63
  }
70
64
  }
71
65
 
66
+ /// One cursor pass: each node's children are gathered on `pending` while its subtree is walked
67
+ /// and moved to `children` in one piece when the walk leaves it.
68
+ fn index_tree(
69
+ tree: &Tree,
70
+ nodes: &mut FxHashMap<usize, IndexedNode>,
71
+ children: &mut Vec<(Node<'static>, Option<&'static str>)>,
72
+ ) -> Result<(), String> {
73
+ let mut cursor = tree.walk();
74
+ // The entered ancestors, each with the length `pending` had when its first child was reached.
75
+ let mut stack: Vec<(Node<'static>, usize)> = Vec::new();
76
+ let mut pending: Vec<(Node<'static>, Option<&'static str>)> = Vec::new();
77
+ loop {
78
+ let node = erase_lifetime(cursor.node());
79
+ let parent = stack.last().map(|&(parent, _)| parent);
80
+ nodes.insert(
81
+ node.id(),
82
+ IndexedNode {
83
+ parent,
84
+ first_child: 0,
85
+ child_count: 0,
86
+ },
87
+ );
88
+ if parent.is_some() {
89
+ pending.push((node, erase_field_lifetime(cursor.field_name())));
90
+ }
91
+ if cursor.goto_first_child() {
92
+ stack.push((node, pending.len()));
93
+ if stack.len() > MAX_TREE_DEPTH {
94
+ return Err(format!("tree depth exceeds {MAX_TREE_DEPTH}"));
95
+ }
96
+ continue;
97
+ }
98
+ while !cursor.goto_next_sibling() {
99
+ if !cursor.goto_parent() {
100
+ return Ok(());
101
+ }
102
+ let (closed, first_pending) = stack.pop().expect("a child's parent was entered");
103
+ let entry = nodes
104
+ .get_mut(&closed.id())
105
+ .expect("every entered node is indexed");
106
+ entry.first_child = children.len() as u32;
107
+ entry.child_count = (pending.len() - first_pending) as u32;
108
+ children.extend(pending.drain(first_pending..));
109
+ }
110
+ }
111
+ }
112
+
72
113
  impl Drop for TreeIndex<'_> {
73
114
  fn drop(&mut self) {
74
- PARENTS.with_borrow_mut(|parents| parents.clear());
115
+ NODES.with_borrow_mut(|nodes| nodes.clear());
116
+ CHILDREN.with_borrow_mut(|children| children.clear());
75
117
  KIND_NAMES.set(&[]);
76
118
  LANGUAGE_NAME.set("");
119
+ GRAMMAR_INDEX.set(None);
77
120
  }
78
121
  }
79
122
 
80
123
  fn erase_lifetime(node: Node<'_>) -> Node<'static> {
81
- // SAFETY: Node only borrows its tree; the erased node is stored in PARENTS, which the
82
- // TreeIndex borrowing that tree empties before the tree can be dropped.
124
+ // SAFETY: Node only borrows its tree; the erased node is stored in NODES and CHILDREN, which
125
+ // the TreeIndex borrowing that tree empties before the tree can be dropped. Lookups are keyed
126
+ // by node id, an address inside the indexed tree, so a node handed back for a node of the
127
+ // indexed tree belongs to the tree that node borrows.
83
128
  unsafe { std::mem::transmute::<Node<'_>, Node<'static>>(node) }
84
129
  }
85
130
 
131
+ fn erase_field_lifetime(field_name: Option<&str>) -> Option<&'static str> {
132
+ // SAFETY: as in erase_lifetime; field names borrow the tree's language as nodes borrow the tree.
133
+ unsafe { std::mem::transmute::<Option<&str>, Option<&'static str>>(field_name) }
134
+ }
135
+
136
+ /// Calls `visit` with the node's children and their field names (TreeCursor::field_name), or
137
+ /// returns None when no installed index holds the node.
138
+ pub fn with_indexed_children<'t, R>(
139
+ node: Node<'t>,
140
+ visit: impl FnOnce(&[(Node<'t>, Option<&'t str>)]) -> R,
141
+ ) -> Option<R> {
142
+ let (first_child, child_count) = NODES.with_borrow(|nodes| {
143
+ nodes
144
+ .get(&node.id())
145
+ .map(|entry| (entry.first_child as usize, entry.child_count as usize))
146
+ })?;
147
+ CHILDREN.with_borrow(|children| {
148
+ let children = &children[first_child..first_child + child_count];
149
+ Some(visit(children))
150
+ })
151
+ }
152
+
86
153
  pub trait NodeExt<'t> {
87
154
  /// `Node::parent`, answered from the TreeIndex when one is installed.
88
155
  fn parent_node(self) -> Option<Node<'t>>;
@@ -98,15 +165,9 @@ impl<'t> NodeExt<'t> for Node<'t> {
98
165
  if self.start_byte() == self.end_byte() {
99
166
  return self.parent();
100
167
  }
101
- PARENTS
102
- .with_borrow(|parents| {
103
- parents.get(&self.id()).map(|parent| {
104
- // SAFETY: ids are addresses inside the indexed tree, which outlives the
105
- // index, so a hit means `parent` belongs to the tree `self` borrows.
106
- unsafe { std::mem::transmute::<Node<'static>, Node<'t>>(*parent) }
107
- })
108
- })
109
- .or_else(|| self.parent())
168
+ NODES
169
+ .with_borrow(|nodes| nodes.get(&self.id()).map(|entry| entry.parent))
170
+ .unwrap_or_else(|| self.parent())
110
171
  }
111
172
 
112
173
  fn kind_name(self) -> &'t str {
@@ -117,3 +178,43 @@ impl<'t> NodeExt<'t> for Node<'t> {
117
178
  .unwrap_or_else(|| self.kind())
118
179
  }
119
180
  }
181
+
182
+ /// A set of node kind names that answers for a node by its kind id while a TreeIndex is
183
+ /// installed: per grammar, a flag per kind id, built on the set's first use with that grammar.
184
+ pub struct KindSet {
185
+ names: &'static [&'static str],
186
+ flags_by_grammar: [OnceLock<Vec<bool>>; GRAMMAR_COUNT],
187
+ }
188
+
189
+ impl KindSet {
190
+ pub const fn new(names: &'static [&'static str]) -> KindSet {
191
+ KindSet {
192
+ names,
193
+ flags_by_grammar: [const { OnceLock::new() }; GRAMMAR_COUNT],
194
+ }
195
+ }
196
+
197
+ /// Whether the set holds `node.kind_name()`.
198
+ pub fn contains(&self, node: Node<'_>) -> bool {
199
+ GRAMMAR_INDEX
200
+ .get()
201
+ .and_then(|grammar_index| {
202
+ self.flags_by_grammar[grammar_index]
203
+ .get_or_init(|| {
204
+ KIND_NAMES
205
+ .get()
206
+ .iter()
207
+ .map(|name| self.contains_name(name))
208
+ .collect()
209
+ })
210
+ // An id past the grammar's kinds (ERROR's) is looked up by name, as kind_name does.
211
+ .get(usize::from(node.kind_id()))
212
+ .copied()
213
+ })
214
+ .unwrap_or_else(|| self.contains_name(node.kind_name()))
215
+ }
216
+
217
+ pub fn contains_name(&self, name: &str) -> bool {
218
+ self.names.contains(&name)
219
+ }
220
+ }
@@ -1,6 +1,6 @@
1
1
  use tree_sitter::Node;
2
2
 
3
- use crate::tree_index::NodeExt;
3
+ use crate::tree_index::{with_indexed_children, NodeExt};
4
4
 
5
5
  /// The measured source. Trees are parsed from UTF-16 (matching node-tree-sitter, which parses
6
6
  /// JavaScript strings as UTF-16 — tree-sitter's error recovery can differ between encodings), so
@@ -69,13 +69,44 @@ pub fn is_token(node: Node<'_>) -> bool {
69
69
  }
70
70
 
71
71
  pub fn named_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
72
- let mut cursor = node.walk();
73
- node.named_children(&mut cursor).collect()
72
+ with_indexed_children(node, |children| {
73
+ children
74
+ .iter()
75
+ .map(|&(child, _)| child)
76
+ .filter(Node::is_named)
77
+ .collect()
78
+ })
79
+ .unwrap_or_else(|| {
80
+ let mut cursor = node.walk();
81
+ node.named_children(&mut cursor).collect()
82
+ })
74
83
  }
75
84
 
76
85
  pub fn all_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
77
- let mut cursor = node.walk();
78
- node.children(&mut cursor).collect()
86
+ with_indexed_children(node, |children| {
87
+ children.iter().map(|&(child, _)| child).collect()
88
+ })
89
+ .unwrap_or_else(|| {
90
+ let mut cursor = node.walk();
91
+ node.children(&mut cursor).collect()
92
+ })
93
+ }
94
+
95
+ /// The children with their field names as a TreeCursor reports them.
96
+ pub fn children_with_field_names<'t>(node: Node<'t>) -> Vec<(Node<'t>, Option<&'t str>)> {
97
+ with_indexed_children(node, <[_]>::to_vec).unwrap_or_else(|| {
98
+ let mut children = Vec::new();
99
+ let mut cursor = node.walk();
100
+ if cursor.goto_first_child() {
101
+ loop {
102
+ children.push((cursor.node(), cursor.field_name()));
103
+ if !cursor.goto_next_sibling() {
104
+ break;
105
+ }
106
+ }
107
+ }
108
+ children
109
+ })
79
110
  }
80
111
 
81
112
  /// Children carrying the field, with node-tree-sitter's vendored-core semantics: extra children
@@ -155,6 +186,12 @@ pub fn to_int32(value: i64) -> i32 {
155
186
  value as i32
156
187
  }
157
188
 
189
+ /// A Rust `else if`: tree-sitter-rust flattens the chain into `else_clause`s that hold the
190
+ /// condition of the `if` they continue, while a final `else` holds only its block.
191
+ pub fn is_else_if_clause(node: Node<'_>) -> bool {
192
+ node.kind_name() == "else_clause" && node.child_by_field_name("condition").is_some()
193
+ }
194
+
158
195
  /// Kotlin's `try { } catch { }` shares its node kind with Rust's `?` operator; only the Kotlin form
159
196
  /// starts with the `try` keyword.
160
197
  pub fn is_kotlin_try_expression(node: Node<'_>) -> bool {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-gauge",
3
- "version": "10.3.0",
3
+ "version": "10.4.1",
4
4
  "description": "Measure code metrics with tree-sitter.",
5
5
  "keywords": [
6
6
  "cli",
@@ -72,15 +72,15 @@
72
72
  "@tsconfig/bun": "1.0.10",
73
73
  "@tsconfig/node-lts": "24.0.0",
74
74
  "@tsconfig/node-ts": "23.6.4",
75
- "@types/bun": "1.4.3",
75
+ "@types/bun": "1.4.4",
76
76
  "@types/node": "25.9.4",
77
77
  "@willbooster/oxlint-config": "1.4.9",
78
- "@willbooster/wb": "22.29.3",
79
- "build-ts": "21.0.29",
78
+ "@willbooster/wb": "23.0.2",
79
+ "build-ts": "21.0.31",
80
80
  "conventional-changelog-conventionalcommits": "9.3.1",
81
81
  "lefthook": "2.2.2",
82
- "oxlint": "1.88.0",
83
- "oxlint-tsgolint": "7.0.2004",
82
+ "oxlint": "1.89.0",
83
+ "oxlint-tsgolint": "7.0.2005",
84
84
  "semantic-release": "25.0.5",
85
85
  "vitest": "4.1.9",
86
86
  "wrangler": "4.135.0"
@@ -89,12 +89,12 @@
89
89
  "node": ">=24"
90
90
  },
91
91
  "optionalDependencies": {
92
- "code-gauge-linux-x64-gnu": "10.3.0",
93
- "code-gauge-linux-arm64-gnu": "10.3.0",
94
- "code-gauge-linux-x64-musl": "10.3.0",
95
- "code-gauge-linux-arm64-musl": "10.3.0",
96
- "code-gauge-darwin-x64": "10.3.0",
97
- "code-gauge-darwin-arm64": "10.3.0",
98
- "code-gauge-win32-x64-msvc": "10.3.0"
92
+ "code-gauge-linux-x64-gnu": "10.4.1",
93
+ "code-gauge-linux-arm64-gnu": "10.4.1",
94
+ "code-gauge-linux-x64-musl": "10.4.1",
95
+ "code-gauge-linux-arm64-musl": "10.4.1",
96
+ "code-gauge-darwin-x64": "10.4.1",
97
+ "code-gauge-darwin-arm64": "10.4.1",
98
+ "code-gauge-win32-x64-msvc": "10.4.1"
99
99
  }
100
100
  }