dependabot-common 0.330.0 → 0.332.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e75354fcd97069450a7897bc3f0c4788fc502b6b7541c7bb1ef01f14779aaec3
4
- data.tar.gz: c71e3cf39d50d4510f12d137e17ba63204aacedce829eb66f48c7da672cece39
3
+ metadata.gz: 96e4e83c7cdbf12714aa4542b6dae8a9c71b4c5f89f696f0da5a9b5edb3ef9d4
4
+ data.tar.gz: 9ed849c328be46ed3372c220b4d40d34ac3f66ed1d458ab6e30dd9df870d3fff
5
5
  SHA512:
6
- metadata.gz: 844f8f4ad3fb9b201791b0499fa8c733667ed792701d19fe0791aaad172170b1dfa399244b32477e440ba97d8f2f2862a882c41eb9ab40e029b96affc8c75a9a
7
- data.tar.gz: dd31e1be979b0a984c896be9ba4bc5060175deb1feea9ac0278e03666034dbdc5b711314603c8b56089bd7e2aa93e2e8cbbaa9e552deb75e565d26a3e49129ae
6
+ metadata.gz: 2130d48ea935228392ce811fb9e91a4d6322dfee13296df1b66247169fccc7279f4213f01b8a716b91317cf31574647719b28ec4bda35ab86ff26f8a112c930f
7
+ data.tar.gz: 301db4be228ea9ddb8a40b55a1f1649bf1c9af444f3bf7f0d107c005ca01a76640016d61144a389baf1b2404648c0393f1da2efd1f023153bce3a715d5291a23
@@ -27,8 +27,6 @@ module Dependabot
27
27
  @config_file ||= T.let(files.first, T.nilable(Dependabot::DependencyFile))
28
28
  end
29
29
 
30
- private
31
-
32
30
  sig { override.returns(T::Array[Dependabot::DependencyFile]) }
33
31
  def fetch_files
34
32
  fetched_files = T.let([], T::Array[Dependabot::DependencyFile])
@@ -101,6 +101,9 @@ module Dependabot
101
101
  sig { returns(T.nilable(Time)) }
102
102
  attr_accessor :attribution_timestamp
103
103
 
104
+ sig { returns(T::Array[String]) }
105
+ attr_reader :origin_files
106
+
104
107
  # rubocop:disable Metrics/AbcSize
105
108
  # rubocop:disable Metrics/PerceivedComplexity
106
109
  sig do
@@ -116,12 +119,12 @@ module Dependabot
116
119
  subdependency_metadata: T.nilable(T::Array[T::Hash[T.any(Symbol, String), String]]),
117
120
  removed: T::Boolean,
118
121
  metadata: T.nilable(T::Hash[T.any(Symbol, String), String]),
119
- direct_relationship: T::Boolean
122
+ origin_files: T::Array[String]
120
123
  ).void
121
124
  end
122
125
  def initialize(name:, requirements:, package_manager:, version: nil,
123
126
  previous_version: nil, previous_requirements: nil, directory: nil,
124
- subdependency_metadata: [], removed: false, metadata: {}, direct_relationship: false)
127
+ subdependency_metadata: [], removed: false, metadata: {}, origin_files: [])
125
128
  @name = name
126
129
  @version = T.let(
127
130
  case version
@@ -148,8 +151,7 @@ module Dependabot
148
151
  end
149
152
  @removed = removed
150
153
  @metadata = T.let(symbolize_keys(metadata || {}), T::Hash[Symbol, T.untyped])
151
- @direct_relationship = direct_relationship
152
-
154
+ @origin_files = origin_files
153
155
  check_values
154
156
  end
155
157
  # rubocop:enable Metrics/AbcSize
@@ -160,12 +162,6 @@ module Dependabot
160
162
  requirements.any?
161
163
  end
162
164
 
163
- # used to support lockfile parsing/DependencySubmission
164
- sig { returns(T::Boolean) }
165
- def direct?
166
- top_level? || @direct_relationship
167
- end
168
-
169
165
  sig { returns(T::Boolean) }
170
166
  def removed?
171
167
  @removed
@@ -28,14 +28,6 @@ module Dependabot
28
28
  sig { returns(T::Boolean) }
29
29
  attr_accessor :vendored_file
30
30
 
31
- # Dependency file priority is used to determine which files are relevant when generating a dependency graph for the
32
- # project - only the highest priority files will be graphed for each directory.
33
- #
34
- # This allows us to default to treating all dependency files as relevant unless the ecosystem's file parser tells
35
- # us otherwise, for example indicating that a Gemfile.lock fully supersedes its peered Gemfile.
36
- sig { returns(Integer) }
37
- attr_accessor :priority
38
-
39
31
  sig { returns(T.nilable(String)) }
40
32
  attr_accessor :symlink_target
41
33
 
@@ -48,9 +40,6 @@ module Dependabot
48
40
  sig { returns(T.nilable(String)) }
49
41
  attr_accessor :mode
50
42
 
51
- sig { returns(T::Set[T.untyped]) }
52
- attr_accessor :dependencies
53
-
54
43
  class ContentEncoding
55
44
  UTF_8 = "utf-8"
56
45
  BASE64 = "base64"
@@ -86,15 +75,14 @@ module Dependabot
86
75
  content_encoding: String,
87
76
  deleted: T::Boolean,
88
77
  operation: String,
89
- mode: T.nilable(String),
90
- priority: Integer
78
+ mode: T.nilable(String)
91
79
  )
92
80
  .void
93
81
  end
94
82
  def initialize(name:, content:, directory: "/", type: "file",
95
83
  support_file: false, vendored_file: false, symlink_target: nil,
96
84
  content_encoding: ContentEncoding::UTF_8, deleted: false,
97
- operation: Operation::UPDATE, mode: nil, priority: 0)
85
+ operation: Operation::UPDATE, mode: nil)
98
86
  @name = name
99
87
  @content = content
100
88
  @directory = T.let(clean_directory(directory), String)
@@ -104,8 +92,6 @@ module Dependabot
104
92
  @content_encoding = content_encoding
105
93
  @operation = operation
106
94
  @mode = mode
107
- @dependencies = T.let(Set.new, T::Set[T.untyped])
108
- @priority = priority
109
95
  raise ArgumentError, "Invalid Git mode: #{mode}" if mode && !VALID_MODES.include?(mode)
110
96
 
111
97
  # Make deleted override the operation. Deleted is kept when operation
@@ -158,9 +158,6 @@ module Dependabot
158
158
  @files = files
159
159
  end
160
160
 
161
- sig { abstract.returns(T::Array[DependencyFile]) }
162
- def fetch_files; end
163
-
164
161
  sig { returns(T.nilable(String)) }
165
162
  def commit
166
163
  return T.must(cloned_commit) if cloned_commit
@@ -195,6 +192,9 @@ module Dependabot
195
192
  sig { overridable.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
196
193
  def ecosystem_versions; end
197
194
 
195
+ sig { abstract.returns(T::Array[DependencyFile]) }
196
+ def fetch_files; end
197
+
198
198
  private
199
199
 
200
200
  sig { params(name: String).returns(T.nilable(Dependabot::DependencyFile)) }
@@ -462,18 +462,14 @@ module Dependabot
462
462
  params(path: String, fetch_submodules: T::Boolean, raise_errors: T::Boolean)
463
463
  .returns(T::Array[OpenStruct])
464
464
  end
465
- def _fetch_repo_contents(path, fetch_submodules: false, raise_errors: true) # rubocop:disable Metrics/PerceivedComplexity
465
+ def _fetch_repo_contents(path, fetch_submodules: false, raise_errors: true)
466
466
  path = path.gsub(" ", "%20")
467
467
  provider, repo, tmp_path, commit =
468
468
  _full_specification_for(path, fetch_submodules: fetch_submodules)
469
469
  .values_at(:provider, :repo, :path, :commit)
470
470
 
471
471
  entries = _fetch_repo_contents_fully_specified(provider, repo, tmp_path, commit)
472
- if Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files)
473
- filter_excluded(entries)
474
- else
475
- entries
476
- end
472
+ entries
477
473
  rescue *CLIENT_NOT_FOUND_ERRORS
478
474
  raise Dependabot::DirectoryNotFound, directory if path == directory.gsub(%r{^/*}, "")
479
475
 
@@ -554,11 +550,7 @@ module Dependabot
554
550
  size: 0 # NOTE: added for parity with github contents API
555
551
  )
556
552
  end
557
- if Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files)
558
- filter_excluded(entries)
559
- else
560
- entries
561
- end
553
+ entries
562
554
  end
563
555
 
564
556
  # Filters out any entries whose paths match one of the exclude_paths globs.
@@ -0,0 +1,70 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module FileFiltering
6
+ extend T::Sig
7
+
8
+ # Returns true if the given path matches any of the exclude patterns
9
+ sig { params(path: String, exclude_patterns: T.nilable(T::Array[String])).returns(T::Boolean) }
10
+ def self.exclude_path?(path, exclude_patterns) # rubocop:disable Metrics/PerceivedComplexity
11
+ return false if exclude_patterns.nil? || exclude_patterns.empty?
12
+
13
+ # Normalize the path by removing leading slashes and resolving relative paths
14
+ normalized_path = normalize_path(path)
15
+
16
+ exclude_patterns.any? do |pattern|
17
+ normalized_pattern = normalize_path(pattern.chomp("/"))
18
+
19
+ # case 1: exact match
20
+ exclude_exact = normalized_path == pattern || normalized_path == normalized_pattern
21
+
22
+ # case 2: Directory prefix matching: check if path is inside an excluded directory
23
+ exclude_deeper = normalized_path.start_with?("#{pattern}#{File::SEPARATOR}",
24
+ "#{normalized_pattern}#{File::SEPARATOR}")
25
+
26
+ # case 3: Explicit recursive (patterns that end with /**)
27
+ exclude_recursive = false
28
+ if pattern.end_with?("/**")
29
+ base_pattern_str = pattern[0...-3]
30
+ base_pattern = normalize_path(base_pattern_str) if base_pattern_str
31
+ exclude_recursive = base_pattern && (
32
+ normalized_path == base_pattern ||
33
+ normalized_path.start_with?("#{base_pattern}/") ||
34
+ normalized_path.start_with?("#{base_pattern}#{File::SEPARATOR}")
35
+ )
36
+ end
37
+
38
+ # case 4: Glob pattern matching with enhanced flags
39
+ # Use multiple fnmatch attempts with different flag combinations
40
+ fnmatch_flags = [
41
+ File::FNM_EXTGLOB,
42
+ File::FNM_EXTGLOB | File::FNM_PATHNAME,
43
+ File::FNM_EXTGLOB | File::FNM_PATHNAME | File::FNM_DOTMATCH,
44
+ File::FNM_PATHNAME
45
+ ]
46
+ exclude_fnmatch_paths = fnmatch_flags.any? do |flag|
47
+ File.fnmatch?(pattern, normalized_path, flag) || File.fnmatch?(normalized_pattern, normalized_path, flag)
48
+ end
49
+
50
+ result = exclude_exact || exclude_deeper || exclude_recursive || exclude_fnmatch_paths
51
+ result
52
+ end
53
+ end
54
+
55
+ # Normalize a file path for consistent comparison
56
+ # - Removes leading slashes
57
+ # - Resolves relative path components (., ..)
58
+ sig { params(path: String).returns(String) }
59
+ def self.normalize_path(path)
60
+ return path if path.empty?
61
+
62
+ pathname = Pathname.new(path)
63
+ normalized = pathname.cleanpath.to_s
64
+
65
+ # Remove leading slash for relative comparison
66
+ normalized = normalized.sub(%r{^/+}, "")
67
+ normalized
68
+ end
69
+ end
70
+ end
@@ -150,6 +150,10 @@ module Dependabot
150
150
  (old_dep.subdependency_metadata || []) +
151
151
  (new_dep.subdependency_metadata || [])
152
152
  ).uniq
153
+ origin_files = (
154
+ old_dep.origin_files +
155
+ new_dep.origin_files
156
+ ).uniq
153
157
 
154
158
  Dependency.new(
155
159
  name: old_dep.name,
@@ -157,7 +161,8 @@ module Dependabot
157
161
  requirements: requirements,
158
162
  package_manager: old_dep.package_manager,
159
163
  metadata: old_dep.metadata,
160
- subdependency_metadata: subdependency_metadata
164
+ subdependency_metadata: subdependency_metadata,
165
+ origin_files: origin_files
161
166
  )
162
167
  end
163
168
 
@@ -106,9 +106,6 @@ module Dependabot
106
106
  @lowest_security_fix_version ||= fetch_lowest_security_fix_version(language_version: language_version)
107
107
  end
108
108
 
109
- sig { abstract.returns(T.nilable(Dependabot::Package::PackageDetails)) }
110
- def package_details; end
111
-
112
109
  sig do
113
110
  returns(T.nilable(T::Array[Dependabot::Package::PackageRelease]))
114
111
  end
@@ -118,23 +115,6 @@ module Dependabot
118
115
 
119
116
  protected
120
117
 
121
- sig do
122
- params(language_version: T.nilable(T.any(String, Dependabot::Version)))
123
- .returns(T.nilable(Dependabot::Version))
124
- end
125
- def fetch_latest_version(language_version: nil)
126
- releases = available_versions
127
- return unless releases
128
-
129
- releases = filter_yanked_versions(releases)
130
- releases = filter_by_cooldown(releases)
131
- releases = filter_unsupported_versions(releases, language_version)
132
- releases = filter_prerelease_versions(releases)
133
- releases = filter_ignored_versions(releases)
134
- releases = apply_post_fetch_latest_versions_filter(releases)
135
- releases.max_by(&:version)&.version
136
- end
137
-
138
118
  sig do
139
119
  params(language_version: T.nilable(T.any(String, Dependabot::Version)))
140
120
  .returns(T.nilable(Dependabot::Version))
@@ -153,45 +133,6 @@ module Dependabot
153
133
  releases.max_by(&:version)&.version
154
134
  end
155
135
 
156
- sig do
157
- params(language_version: T.nilable(T.any(String, Dependabot::Version)))
158
- .returns(T.nilable(Dependabot::Version))
159
- end
160
- def fetch_lowest_security_fix_version(language_version: nil)
161
- releases = available_versions
162
- return unless releases
163
-
164
- releases = filter_yanked_versions(releases)
165
- releases = filter_unsupported_versions(releases, language_version)
166
- # versions = filter_prerelease_versions(versions)
167
- releases = Dependabot::UpdateCheckers::VersionFilters
168
- .filter_vulnerable_versions(
169
- releases,
170
- security_advisories
171
- )
172
- releases = filter_ignored_versions(releases)
173
- releases = filter_lower_versions(releases)
174
- releases = apply_post_fetch_lowest_security_fix_versions_filter(releases)
175
-
176
- releases.min_by(&:version)&.version
177
- end
178
-
179
- sig do
180
- params(releases: T::Array[Dependabot::Package::PackageRelease])
181
- .returns(T::Array[Dependabot::Package::PackageRelease])
182
- end
183
- def apply_post_fetch_latest_versions_filter(releases)
184
- releases
185
- end
186
-
187
- sig do
188
- params(releases: T::Array[Dependabot::Package::PackageRelease])
189
- .returns(T::Array[Dependabot::Package::PackageRelease])
190
- end
191
- def apply_post_fetch_lowest_security_fix_versions_filter(releases)
192
- releases
193
- end
194
-
195
136
  sig do
196
137
  params(releases: T::Array[Dependabot::Package::PackageRelease])
197
138
  .returns(T::Array[Dependabot::Package::PackageRelease])
@@ -322,11 +263,6 @@ module Dependabot
322
263
  end
323
264
  end
324
265
 
325
- sig { returns(T::Boolean) }
326
- def cooldown_enabled?
327
- false
328
- end
329
-
330
266
  sig do
331
267
  params(
332
268
  current_version: T.nilable(Dependabot::Version),
@@ -382,6 +318,72 @@ module Dependabot
382
318
  def requirement_class
383
319
  dependency.requirement_class
384
320
  end
321
+
322
+ private
323
+
324
+ sig { abstract.returns(T.nilable(Dependabot::Package::PackageDetails)) }
325
+ def package_details; end
326
+
327
+ sig { returns(T::Boolean) }
328
+ def cooldown_enabled?
329
+ false
330
+ end
331
+
332
+ sig do
333
+ params(language_version: T.nilable(T.any(String, Dependabot::Version)))
334
+ .returns(T.nilable(Dependabot::Version))
335
+ end
336
+ def fetch_latest_version(language_version: nil)
337
+ releases = available_versions
338
+ return unless releases
339
+
340
+ releases = filter_yanked_versions(releases)
341
+ releases = filter_by_cooldown(releases)
342
+ releases = filter_unsupported_versions(releases, language_version)
343
+ releases = filter_prerelease_versions(releases)
344
+ releases = filter_ignored_versions(releases)
345
+ releases = apply_post_fetch_latest_versions_filter(releases)
346
+ releases.max_by(&:version)&.version
347
+ end
348
+
349
+ sig do
350
+ params(language_version: T.nilable(T.any(String, Dependabot::Version)))
351
+ .returns(T.nilable(Dependabot::Version))
352
+ end
353
+ def fetch_lowest_security_fix_version(language_version: nil)
354
+ releases = available_versions
355
+ return unless releases
356
+
357
+ releases = filter_yanked_versions(releases)
358
+ releases = filter_unsupported_versions(releases, language_version)
359
+ # versions = filter_prerelease_versions(versions)
360
+ releases = Dependabot::UpdateCheckers::VersionFilters
361
+ .filter_vulnerable_versions(
362
+ releases,
363
+ security_advisories
364
+ )
365
+ releases = filter_ignored_versions(releases)
366
+ releases = filter_lower_versions(releases)
367
+ releases = apply_post_fetch_lowest_security_fix_versions_filter(releases)
368
+
369
+ releases.min_by(&:version)&.version
370
+ end
371
+
372
+ sig do
373
+ params(releases: T::Array[Dependabot::Package::PackageRelease])
374
+ .returns(T::Array[Dependabot::Package::PackageRelease])
375
+ end
376
+ def apply_post_fetch_latest_versions_filter(releases)
377
+ releases
378
+ end
379
+
380
+ sig do
381
+ params(releases: T::Array[Dependabot::Package::PackageRelease])
382
+ .returns(T::Array[Dependabot::Package::PackageRelease])
383
+ end
384
+ def apply_post_fetch_lowest_security_fix_versions_filter(releases)
385
+ releases
386
+ end
385
387
  end
386
388
  end
387
389
  end
@@ -18,6 +18,8 @@ module Dependabot
18
18
  sig { abstract.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
19
19
  def updated_requirements; end
20
20
 
21
+ private
22
+
21
23
  sig { abstract.returns(T::Class[Version]) }
22
24
  def version_class; end
23
25
 
@@ -8,6 +8,7 @@ require "dependabot/requirements_update_strategy"
8
8
  require "dependabot/security_advisory"
9
9
  require "dependabot/utils"
10
10
  require "dependabot/package/release_cooldown_options"
11
+ require "dependabot/file_filtering"
11
12
 
12
13
  module Dependabot
13
14
  module UpdateCheckers
@@ -45,6 +46,9 @@ module Dependabot
45
46
  sig { returns(T.nilable(Dependabot::Package::ReleaseCooldownOptions)) }
46
47
  attr_reader :update_cooldown
47
48
 
49
+ sig { returns(T.nilable(T::Array[String])) }
50
+ attr_reader :exclude_paths
51
+
48
52
  sig { returns(T::Hash[Symbol, T.untyped]) }
49
53
  attr_reader :options
50
54
 
@@ -60,6 +64,7 @@ module Dependabot
60
64
  requirements_update_strategy: T.nilable(Dependabot::RequirementsUpdateStrategy),
61
65
  dependency_group: T.nilable(Dependabot::DependencyGroup),
62
66
  update_cooldown: T.nilable(Dependabot::Package::ReleaseCooldownOptions),
67
+ exclude_paths: T.nilable(T::Array[String]),
63
68
  options: T::Hash[Symbol, T.untyped]
64
69
  )
65
70
  .void
@@ -68,7 +73,7 @@ module Dependabot
68
73
  repo_contents_path: nil, ignored_versions: [],
69
74
  raise_on_ignored: false, security_advisories: [],
70
75
  requirements_update_strategy: nil, dependency_group: nil,
71
- update_cooldown: nil, options: {})
76
+ update_cooldown: nil, exclude_paths: [], options: {})
72
77
  @dependency = dependency
73
78
  @dependency_files = dependency_files
74
79
  @repo_contents_path = repo_contents_path
@@ -79,6 +84,7 @@ module Dependabot
79
84
  @security_advisories = security_advisories
80
85
  @dependency_group = dependency_group
81
86
  @update_cooldown = update_cooldown
87
+ @exclude_paths = exclude_paths
82
88
  @options = options
83
89
  end
84
90
 
@@ -106,6 +112,37 @@ module Dependabot
106
112
  end
107
113
  end
108
114
 
115
+ sig { returns(T::Boolean) }
116
+ def excluded? # rubocop:disable Metrics/PerceivedComplexity
117
+ return false unless Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files)
118
+
119
+ return false if exclude_paths.nil? || exclude_paths&.empty?
120
+
121
+ origin_files = @dependency.origin_files
122
+ if origin_files.length.positive?
123
+ excluded_files = []
124
+ non_excluded_files = []
125
+
126
+ origin_files.each do |origin_file|
127
+ if Dependabot::FileFiltering.exclude_path?(origin_file, exclude_paths)
128
+ excluded_files << origin_file
129
+ else
130
+ non_excluded_files << origin_file
131
+ end
132
+ end
133
+
134
+ # Only exclude if the dependency appears ONLY in excluded paths
135
+ # If it appears in any non-excluded path, we should process it
136
+ if non_excluded_files.empty? && excluded_files.any?
137
+ Dependabot.logger.info("Excluding dependency #{dependency.name} - only found in excluded paths " \
138
+ "#{excluded_files.join(', ')}")
139
+ return true
140
+ end
141
+ end
142
+
143
+ false
144
+ end
145
+
109
146
  sig { params(requirements_to_unlock: T.nilable(Symbol)).returns(T::Array[Dependabot::Dependency]) }
110
147
  def updated_dependencies(requirements_to_unlock:)
111
148
  return [] unless can_update?(requirements_to_unlock: requirements_to_unlock)
@@ -75,6 +75,8 @@ module Dependabot
75
75
  end
76
76
  def capture_failed_change_attempt(memo = nil, error = nil); end
77
77
 
78
+ private
79
+
78
80
  sig { abstract.returns(String) }
79
81
  def clean; end
80
82
  end
data/lib/dependabot.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Dependabot
5
- VERSION = "0.330.0"
5
+ VERSION = "0.332.0"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.330.0
4
+ version: 0.332.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -550,6 +550,7 @@ files:
550
550
  - lib/dependabot/file_fetchers.rb
551
551
  - lib/dependabot/file_fetchers/README.md
552
552
  - lib/dependabot/file_fetchers/base.rb
553
+ - lib/dependabot/file_filtering.rb
553
554
  - lib/dependabot/file_parsers.rb
554
555
  - lib/dependabot/file_parsers/README.md
555
556
  - lib/dependabot/file_parsers/base.rb
@@ -625,7 +626,7 @@ licenses:
625
626
  - MIT
626
627
  metadata:
627
628
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
628
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.330.0
629
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.332.0
629
630
  rdoc_options: []
630
631
  require_paths:
631
632
  - lib