@preship/core 2.0.1 → 2.1.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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- type ProjectType = 'npm' | 'yarn' | 'pnpm';
1
+ type ProjectType = 'npm' | 'yarn' | 'pnpm' | 'flutter' | 'maven' | 'gradle' | 'cocoapods' | 'spm';
2
2
  interface DetectedProject {
3
3
  type: ProjectType;
4
4
  path: string;
@@ -14,7 +14,7 @@ interface Dependency {
14
14
  isDirect: boolean;
15
15
  isDevDependency: boolean;
16
16
  }
17
- type LicenseSource = 'package-json-license-field' | 'license-file' | 'package-json-licenses-field' | 'npm-registry' | 'github-api' | 'cache' | 'unknown';
17
+ type LicenseSource = 'package-json-license-field' | 'license-file' | 'package-json-licenses-field' | 'npm-registry' | 'pub-registry' | 'maven-central' | 'cocoapods-cdn' | 'github-api' | 'cache' | 'unknown';
18
18
  interface ParsedDependency {
19
19
  name: string;
20
20
  version: string;
@@ -205,6 +205,130 @@ declare function parseYarnLockfile(lockfilePath: string, packageJsonPath: string
205
205
 
206
206
  declare function parsePnpmLockfile(lockfilePath: string, packageJsonPath: string): ParsedDependency[];
207
207
 
208
+ /**
209
+ * Parse a Flutter/Dart pubspec.lock file and return all dependencies.
210
+ *
211
+ * pubspec.lock uses a well-defined YAML structure:
212
+ * packages:
213
+ * <name>:
214
+ * dependency: "direct main"
215
+ * description:
216
+ * name: <name>
217
+ * ...
218
+ * source: hosted
219
+ * version: "1.2.3"
220
+ *
221
+ * We use a simple line-based parser (no js-yaml dependency needed)
222
+ * because pubspec.lock has a consistent, predictable structure.
223
+ *
224
+ * SDK packages (source: sdk) are skipped — these are Flutter/Dart
225
+ * built-ins like `flutter`, `sky_engine`, `flutter_test`.
226
+ *
227
+ * @param lockfilePath - Absolute path to pubspec.lock
228
+ * @param pubspecYamlPath - Absolute path to pubspec.yaml
229
+ * @returns Array of ParsedDependency
230
+ */
231
+ declare function parsePubspecLockfile(lockfilePath: string, pubspecYamlPath: string): ParsedDependency[];
232
+
233
+ /**
234
+ * Parse a Maven pom.xml file and return all dependencies.
235
+ *
236
+ * Maven doesn't have a lockfile, so we parse pom.xml directly.
237
+ * Extracts <dependency> blocks from the <dependencies> section,
238
+ * resolving ${property} version references from <properties>.
239
+ *
240
+ * Skips:
241
+ * - <scope>provided</scope> and <scope>system</scope> (always)
242
+ * - <scope>test</scope> (marked as dev dependency)
243
+ *
244
+ * Package name format: groupId:artifactId
245
+ * (e.g., "org.springframework.boot:spring-boot-starter-web")
246
+ *
247
+ * @param pomPath - Absolute path to pom.xml
248
+ * @param _manifestPath - Same as pomPath (Maven uses pom.xml as both)
249
+ * @returns Array of ParsedDependency
250
+ */
251
+ declare function parseMavenPom(pomPath: string, _manifestPath: string): ParsedDependency[];
252
+
253
+ /**
254
+ * Parse a Gradle project's dependencies.
255
+ *
256
+ * Tries sources in priority order:
257
+ * 1. gradle.lockfile (opt-in, most reliable)
258
+ * 2. gradle/libs.versions.toml (version catalog)
259
+ * 3. build.gradle or build.gradle.kts (regex fallback)
260
+ *
261
+ * Package name format: groupId:artifactId
262
+ * (e.g., "org.springframework.boot:spring-boot-starter-web")
263
+ *
264
+ * @param lockfilePath - Path to gradle.lockfile or build.gradle(.kts)
265
+ * @param manifestPath - Path to build.gradle or build.gradle.kts
266
+ * @returns Array of ParsedDependency
267
+ */
268
+ declare function parseGradleLockfile(lockfilePath: string, manifestPath: string): ParsedDependency[];
269
+
270
+ /**
271
+ * Parse a CocoaPods Podfile.lock and return all dependencies.
272
+ *
273
+ * Podfile.lock is a YAML-like format with consistent structure:
274
+ *
275
+ * PODS:
276
+ * - AFNetworking (4.0.1):
277
+ * - AFNetworking/NSURLSession (= 4.0.1)
278
+ * - AFNetworking/Reachability (= 4.0.1)
279
+ * - Alamofire (5.9.1)
280
+ *
281
+ * DEPENDENCIES:
282
+ * - AFNetworking (~> 4.0)
283
+ * - Alamofire
284
+ *
285
+ * SPEC REPOS:
286
+ * ...
287
+ *
288
+ * We use a line-based parser (no YAML library needed) since the
289
+ * format is predictable.
290
+ *
291
+ * Subspecs (e.g., AFNetworking/NSURLSession) are skipped — only
292
+ * top-level pods are returned.
293
+ *
294
+ * @param lockfilePath - Absolute path to Podfile.lock
295
+ * @param _manifestPath - Absolute path to Podfile (unused, for API consistency)
296
+ * @returns Array of ParsedDependency
297
+ */
298
+ declare function parsePodfileLock(lockfilePath: string, _manifestPath: string): ParsedDependency[];
299
+
300
+ /**
301
+ * Parse a Swift Package Manager Package.resolved file.
302
+ *
303
+ * Supports:
304
+ * - v2 format (Swift 5.6+): { "pins": [...] }
305
+ * - v3 format: same structure with "originHash"
306
+ * - v1 format (legacy): { "object": { "pins": [...] } }
307
+ *
308
+ * Skips:
309
+ * - Branch-based pins (no version)
310
+ * - Local packages (kind: "localSourceControl")
311
+ *
312
+ * All SPM deps are treated as direct (Package.resolved doesn't
313
+ * distinguish direct vs transitive).
314
+ *
315
+ * The `location` field contains the Git URL, useful for
316
+ * GitHub-based license resolution.
317
+ *
318
+ * @param resolvedPath - Absolute path to Package.resolved
319
+ * @param _manifestPath - Absolute path to Package.swift (unused, for API consistency)
320
+ * @returns Array of ParsedDependency
321
+ */
322
+ declare function parsePackageResolved(resolvedPath: string, _manifestPath: string): ParsedDependency[];
323
+ /**
324
+ * Extract the Git location URL for a pin.
325
+ * Used by the license resolver to find GitHub repos.
326
+ *
327
+ * @param resolvedPath - Absolute path to Package.resolved
328
+ * @returns Map of package identity → location URL
329
+ */
330
+ declare function extractSpmLocations(resolvedPath: string): Map<string, string>;
331
+
208
332
  /**
209
333
  * Encrypted Project-Level License Cache
210
334
  *
@@ -360,4 +484,4 @@ interface ScanTimeoutController {
360
484
  */
361
485
  declare function createScanTimeoutController(timeout?: number): ScanTimeoutController;
362
486
 
363
- export { AdaptiveRateLimiter, type CacheEntry, type CacheFile, type Dependency, type DetectedProject, type ExceptionEntry, type LicenseSource, type ModuleConfig, type PackageHealth, type ParsedDependency, type PolicyResult, type PolicyTemplate, type PolicyVerdict, type PreshipConfig, type ProjectType, type ResolverMode, type ScanResult, type ScanTimeoutController, type SecretFinding, type SecretRule, type SecretSeverity, type SecretsConfig, type SecretsResult, type SecurityConfig, type SecurityFinding, type SecurityPolicyLevel, type SecurityResult, type SecuritySeverity, type SecurityVulnerability, type UnifiedScanResult, cacheKey, createEmptyCache, createScanTimeoutController, detectProjects, getCacheEntry, getDefaultConfig, getOrCreateKey, loadCache, loadConfig, parseNpmLockfile, parsePnpmLockfile, parseYarnLockfile, saveCache, setCacheEntry };
487
+ export { AdaptiveRateLimiter, type CacheEntry, type CacheFile, type Dependency, type DetectedProject, type ExceptionEntry, type LicenseSource, type ModuleConfig, type PackageHealth, type ParsedDependency, type PolicyResult, type PolicyTemplate, type PolicyVerdict, type PreshipConfig, type ProjectType, type ResolverMode, type ScanResult, type ScanTimeoutController, type SecretFinding, type SecretRule, type SecretSeverity, type SecretsConfig, type SecretsResult, type SecurityConfig, type SecurityFinding, type SecurityPolicyLevel, type SecurityResult, type SecuritySeverity, type SecurityVulnerability, type UnifiedScanResult, cacheKey, createEmptyCache, createScanTimeoutController, detectProjects, extractSpmLocations, getCacheEntry, getDefaultConfig, getOrCreateKey, loadCache, loadConfig, parseGradleLockfile, parseMavenPom, parseNpmLockfile, parsePackageResolved, parsePnpmLockfile, parsePodfileLock, parsePubspecLockfile, parseYarnLockfile, saveCache, setCacheEntry };
package/dist/index.js CHANGED
@@ -35,13 +35,19 @@ __export(index_exports, {
35
35
  createEmptyCache: () => createEmptyCache,
36
36
  createScanTimeoutController: () => createScanTimeoutController,
37
37
  detectProjects: () => detectProjects,
38
+ extractSpmLocations: () => extractSpmLocations,
38
39
  getCacheEntry: () => getCacheEntry,
39
40
  getDefaultConfig: () => getDefaultConfig,
40
41
  getOrCreateKey: () => getOrCreateKey,
41
42
  loadCache: () => loadCache,
42
43
  loadConfig: () => loadConfig,
44
+ parseGradleLockfile: () => parseGradleLockfile,
45
+ parseMavenPom: () => parseMavenPom,
43
46
  parseNpmLockfile: () => parseNpmLockfile,
47
+ parsePackageResolved: () => parsePackageResolved,
44
48
  parsePnpmLockfile: () => parsePnpmLockfile,
49
+ parsePodfileLock: () => parsePodfileLock,
50
+ parsePubspecLockfile: () => parsePubspecLockfile,
45
51
  parseYarnLockfile: () => parseYarnLockfile,
46
52
  saveCache: () => saveCache,
47
53
  setCacheEntry: () => setCacheEntry
@@ -378,10 +384,30 @@ function loadConfig(projectPath) {
378
384
  // src/detector.ts
379
385
  var fs2 = __toESM(require("fs"));
380
386
  var path2 = __toESM(require("path"));
387
+ var NODE_LOCK_FILES = [
388
+ { filename: "package-lock.json", type: "npm", manifest: "package.json" },
389
+ { filename: "yarn.lock", type: "yarn", manifest: "package.json" },
390
+ { filename: "pnpm-lock.yaml", type: "pnpm", manifest: "package.json" }
391
+ ];
392
+ var FLUTTER_LOCK_FILES = [
393
+ { filename: "pubspec.lock", type: "flutter", manifest: "pubspec.yaml" }
394
+ ];
395
+ var JAVA_LOCK_FILES = [
396
+ { filename: "gradle.lockfile", type: "gradle", manifest: "build.gradle" },
397
+ { filename: "gradle.lockfile", type: "gradle", manifest: "build.gradle.kts" },
398
+ { filename: "build.gradle", type: "gradle", manifest: "build.gradle" },
399
+ { filename: "build.gradle.kts", type: "gradle", manifest: "build.gradle.kts" },
400
+ { filename: "pom.xml", type: "maven", manifest: "pom.xml" }
401
+ ];
402
+ var IOS_LOCK_FILES = [
403
+ { filename: "Podfile.lock", type: "cocoapods", manifest: "Podfile" },
404
+ { filename: "Package.resolved", type: "spm", manifest: "Package.swift" }
405
+ ];
381
406
  var LOCK_FILES = [
382
- { filename: "package-lock.json", type: "npm" },
383
- { filename: "yarn.lock", type: "yarn" },
384
- { filename: "pnpm-lock.yaml", type: "pnpm" }
407
+ ...NODE_LOCK_FILES,
408
+ ...FLUTTER_LOCK_FILES,
409
+ ...JAVA_LOCK_FILES,
410
+ ...IOS_LOCK_FILES
385
411
  ];
386
412
  function detectFramework(packageJsonPath) {
387
413
  try {
@@ -391,30 +417,129 @@ function detectFramework(packageJsonPath) {
391
417
  ...pkg.dependencies || {},
392
418
  ...pkg.devDependencies || {}
393
419
  };
420
+ if (allDeps["expo"]) return "expo";
421
+ if (allDeps["react-native"]) return "react-native";
422
+ if (allDeps["electron"]) return "electron";
423
+ if (allDeps["@tauri-apps/api"] || allDeps["tauri"]) return "tauri";
394
424
  if (allDeps["next"]) return "nextjs";
425
+ if (allDeps["nuxt"]) return "nuxtjs";
426
+ if (allDeps["@remix-run/react"] || allDeps["remix"]) return "remix";
427
+ if (allDeps["astro"]) return "astro";
428
+ if (allDeps["gatsby"]) return "gatsby";
395
429
  if (allDeps["react"]) return "react";
430
+ if (allDeps["vue"]) return "vue";
431
+ if (allDeps["@angular/core"]) return "angular";
432
+ if (allDeps["svelte"] || allDeps["@sveltejs/kit"]) return "svelte";
396
433
  if (allDeps["express"]) return "express";
397
434
  if (allDeps["fastify"]) return "fastify";
398
435
  if (allDeps["@nestjs/core"] || allDeps["nestjs"]) return "nestjs";
436
+ if (allDeps["@hapi/hapi"]) return "hapi";
437
+ if (allDeps["koa"]) return "koa";
399
438
  return void 0;
400
439
  } catch {
401
440
  return void 0;
402
441
  }
403
442
  }
443
+ function detectFlutterFramework(pubspecYamlPath, projectRoot) {
444
+ try {
445
+ const content = fs2.readFileSync(pubspecYamlPath, "utf-8");
446
+ const hasFlutterSdk = /^\s{2}flutter:\s*$/m.test(content) && /sdk:\s*flutter/m.test(content);
447
+ if (hasFlutterSdk) {
448
+ const hasWebDir = fs2.existsSync(path2.join(projectRoot, "web"));
449
+ if (hasWebDir) return "flutter-web";
450
+ const hasDesktopDir = fs2.existsSync(path2.join(projectRoot, "linux")) || fs2.existsSync(path2.join(projectRoot, "windows")) || fs2.existsSync(path2.join(projectRoot, "macos"));
451
+ if (hasDesktopDir) return "flutter-desktop";
452
+ return "flutter";
453
+ }
454
+ const hasShelf = /shelf/m.test(content);
455
+ const hasDartFrog = /dart_frog/m.test(content);
456
+ if (hasShelf || hasDartFrog) return "dart-server";
457
+ return "dart-cli";
458
+ } catch {
459
+ return void 0;
460
+ }
461
+ }
462
+ function detectJavaFramework(manifestPath, _projectRoot) {
463
+ try {
464
+ const content = fs2.readFileSync(manifestPath, "utf-8");
465
+ if (/com\.android\.(application|library)/m.test(content)) return "android-java";
466
+ if (/spring-boot-starter/m.test(content)) return "spring-boot";
467
+ if (/org\.springframework/m.test(content)) return "spring";
468
+ if (/io\.quarkus/m.test(content)) return "quarkus";
469
+ if (/io\.micronaut/m.test(content)) return "micronaut";
470
+ return "java";
471
+ } catch {
472
+ return "java";
473
+ }
474
+ }
475
+ function detectIosFramework(manifestPath, projectType) {
476
+ if (projectType === "spm") return "ios-spm";
477
+ try {
478
+ const content = fs2.readFileSync(manifestPath, "utf-8");
479
+ if (/platform\s*:tvos/m.test(content)) return "tvos-native";
480
+ if (/platform\s*:osx/m.test(content) || /platform\s*:macos/m.test(content)) return "macos-native";
481
+ return "ios-native";
482
+ } catch {
483
+ return "ios-native";
484
+ }
485
+ }
404
486
  function detectProjects(rootPath) {
405
487
  const absRoot = path2.resolve(rootPath);
406
488
  const packageJsonPath = path2.join(absRoot, "package.json");
407
- if (!fs2.existsSync(packageJsonPath)) {
489
+ const pubspecYamlPath = path2.join(absRoot, "pubspec.yaml");
490
+ const pomXmlPath = path2.join(absRoot, "pom.xml");
491
+ const buildGradlePath = path2.join(absRoot, "build.gradle");
492
+ const buildGradleKtsPath = path2.join(absRoot, "build.gradle.kts");
493
+ const podfilePath = path2.join(absRoot, "Podfile");
494
+ const packageSwiftPath = path2.join(absRoot, "Package.swift");
495
+ const hasPackageJson = fs2.existsSync(packageJsonPath);
496
+ const hasPubspecYaml = fs2.existsSync(pubspecYamlPath);
497
+ const hasPomXml = fs2.existsSync(pomXmlPath);
498
+ const hasBuildGradle = fs2.existsSync(buildGradlePath) || fs2.existsSync(buildGradleKtsPath);
499
+ const hasPodfile = fs2.existsSync(podfilePath);
500
+ const hasPackageSwift = fs2.existsSync(packageSwiftPath);
501
+ if (!hasPackageJson && !hasPubspecYaml && !hasPomXml && !hasBuildGradle && !hasPodfile && !hasPackageSwift) {
408
502
  throw new Error(
409
- `No package.json found in ${absRoot}.
410
- PreShip needs a Node.js project to scan.
503
+ `No supported project manifest found in ${absRoot}.
504
+ PreShip supports:
505
+ - Node.js (package.json)
506
+ - Flutter/Dart (pubspec.yaml)
507
+ - Maven (pom.xml)
508
+ - Gradle (build.gradle / build.gradle.kts)
509
+ - CocoaPods (Podfile)
510
+ - Swift Package Manager (Package.swift)
411
511
  Make sure you're running from the project root.`
412
512
  );
413
513
  }
414
514
  const detected = [];
415
515
  for (const entry of LOCK_FILES) {
416
516
  const lockFilePath = path2.join(absRoot, entry.filename);
417
- if (fs2.existsSync(lockFilePath)) {
517
+ const manifestPath = path2.join(absRoot, entry.manifest);
518
+ if (!fs2.existsSync(lockFilePath) || !fs2.existsSync(manifestPath)) {
519
+ continue;
520
+ }
521
+ if (entry.type === "flutter") {
522
+ detected.push({
523
+ type: entry.type,
524
+ path: absRoot,
525
+ lockFile: lockFilePath,
526
+ framework: detectFlutterFramework(pubspecYamlPath, absRoot)
527
+ });
528
+ } else if (entry.type === "maven" || entry.type === "gradle") {
529
+ detected.push({
530
+ type: entry.type,
531
+ path: absRoot,
532
+ lockFile: lockFilePath,
533
+ framework: detectJavaFramework(manifestPath, absRoot)
534
+ });
535
+ } else if (entry.type === "cocoapods" || entry.type === "spm") {
536
+ detected.push({
537
+ type: entry.type,
538
+ path: absRoot,
539
+ lockFile: lockFilePath,
540
+ framework: detectIosFramework(manifestPath, entry.type)
541
+ });
542
+ } else {
418
543
  detected.push({
419
544
  type: entry.type,
420
545
  path: absRoot,
@@ -424,11 +549,42 @@ function detectProjects(rootPath) {
424
549
  }
425
550
  }
426
551
  if (detected.length === 0) {
427
- throw new Error(
428
- `No supported lock file found in ${absRoot}.
552
+ if (hasPackageJson) {
553
+ throw new Error(
554
+ `No supported lock file found in ${absRoot}.
429
555
  PreShip needs a lock file to scan dependencies.
430
556
  Run 'npm install', 'yarn install', or 'pnpm install' first.`
431
- );
557
+ );
558
+ } else if (hasPubspecYaml) {
559
+ throw new Error(
560
+ `No pubspec.lock found in ${absRoot}.
561
+ PreShip needs a lock file to scan dependencies.
562
+ Run 'flutter pub get' or 'dart pub get' first.`
563
+ );
564
+ } else if (hasPomXml) {
565
+ throw new Error(
566
+ `Maven project detected in ${absRoot} but scanning is ready.
567
+ PreShip will parse pom.xml directly (no lockfile needed).`
568
+ );
569
+ } else if (hasBuildGradle) {
570
+ throw new Error(
571
+ `No gradle.lockfile found in ${absRoot}.
572
+ PreShip needs a lockfile or build file to scan dependencies.
573
+ For best results, enable lockfile: add 'dependencyLocking { lockAllConfigurations() }' to build.gradle.`
574
+ );
575
+ } else if (hasPodfile) {
576
+ throw new Error(
577
+ `No Podfile.lock found in ${absRoot}.
578
+ PreShip needs a lock file to scan dependencies.
579
+ Run 'pod install' first.`
580
+ );
581
+ } else {
582
+ throw new Error(
583
+ `No Package.resolved found in ${absRoot}.
584
+ PreShip needs a resolved file to scan dependencies.
585
+ Run 'swift package resolve' first.`
586
+ );
587
+ }
432
588
  }
433
589
  return [detected[0]];
434
590
  }
@@ -481,7 +637,13 @@ function parseV2Packages(packages, directDeps, directDevDeps) {
481
637
  if (key === "") continue;
482
638
  if (!key.startsWith("node_modules/")) continue;
483
639
  const parts = key.split("node_modules/");
484
- const name = parts[parts.length - 1];
640
+ let name = parts[parts.length - 1];
641
+ if (entry.resolved && typeof entry.resolved === "string") {
642
+ const realName = extractPackageNameFromResolved(entry.resolved);
643
+ if (realName && realName !== name) {
644
+ name = realName;
645
+ }
646
+ }
485
647
  if (!name || !entry.version) continue;
486
648
  const dedupKey = `${name}@${entry.version}`;
487
649
  if (seen.has(dedupKey)) continue;
@@ -497,6 +659,21 @@ function parseV2Packages(packages, directDeps, directDevDeps) {
497
659
  }
498
660
  return results;
499
661
  }
662
+ function extractPackageNameFromResolved(resolved) {
663
+ const scopedMatch = resolved.match(
664
+ /registry\.npmjs\.org\/(@[^/]+\/[^/]+)\/-\//
665
+ );
666
+ if (scopedMatch) {
667
+ return scopedMatch[1];
668
+ }
669
+ const regularMatch = resolved.match(
670
+ /registry\.npmjs\.org\/([^@/][^/]*)\/-\//
671
+ );
672
+ if (regularMatch) {
673
+ return regularMatch[1];
674
+ }
675
+ return null;
676
+ }
500
677
  function parseV1Dependencies(dependencies, directDeps, directDevDeps) {
501
678
  const results = [];
502
679
  function walk(deps) {
@@ -757,18 +934,595 @@ function parseV5Key(key) {
757
934
  return { name, pkgVersion };
758
935
  }
759
936
 
760
- // src/cache.ts
761
- var crypto = __toESM(require("crypto"));
937
+ // src/parsers/flutter.ts
762
938
  var fs6 = __toESM(require("fs"));
939
+ function parsePubspecLockfile(lockfilePath, pubspecYamlPath) {
940
+ let lockContent;
941
+ try {
942
+ lockContent = fs6.readFileSync(lockfilePath, "utf-8");
943
+ } catch {
944
+ throw new Error(
945
+ `Failed to read ${lockfilePath}.
946
+ The lock file may be missing or corrupted. Try running 'flutter pub get'.`
947
+ );
948
+ }
949
+ let pubspecContent;
950
+ try {
951
+ pubspecContent = fs6.readFileSync(pubspecYamlPath, "utf-8");
952
+ } catch {
953
+ throw new Error(
954
+ `Failed to read ${pubspecYamlPath}.
955
+ Make sure pubspec.yaml exists and is readable.`
956
+ );
957
+ }
958
+ const { directDeps, devDeps } = parsePubspecYamlDeps(pubspecContent);
959
+ const packages = parsePubspecLockPackages(lockContent);
960
+ const results = [];
961
+ const seen = /* @__PURE__ */ new Set();
962
+ for (const [name, entry] of packages) {
963
+ if (entry.source === "sdk") {
964
+ continue;
965
+ }
966
+ if (!entry.version) {
967
+ continue;
968
+ }
969
+ const dedupKey = `${name}@${entry.version}`;
970
+ if (seen.has(dedupKey)) {
971
+ continue;
972
+ }
973
+ seen.add(dedupKey);
974
+ const isDirect = entry.dependency === "direct main" || entry.dependency === "direct dev" || entry.dependency === "direct overridden";
975
+ const isDevDependency = entry.dependency === "direct dev" || !isDirect && devDeps.has(name) && !directDeps.has(name);
976
+ results.push({
977
+ name,
978
+ version: entry.version,
979
+ isDirect,
980
+ isDevDependency
981
+ });
982
+ }
983
+ return results;
984
+ }
985
+ function parsePubspecYamlDeps(content) {
986
+ const directDeps = /* @__PURE__ */ new Set();
987
+ const devDeps = /* @__PURE__ */ new Set();
988
+ const lines = content.split("\n");
989
+ let currentSection = null;
990
+ for (const line of lines) {
991
+ const trimmed = line.trimEnd();
992
+ if (trimmed === "" || trimmed.startsWith("#")) {
993
+ continue;
994
+ }
995
+ if (!trimmed.startsWith(" ") && !trimmed.startsWith(" ")) {
996
+ if (trimmed === "dependencies:" || trimmed.startsWith("dependencies:")) {
997
+ currentSection = "dependencies";
998
+ continue;
999
+ }
1000
+ if (trimmed === "dev_dependencies:" || trimmed.startsWith("dev_dependencies:")) {
1001
+ currentSection = "dev_dependencies";
1002
+ continue;
1003
+ }
1004
+ currentSection = null;
1005
+ continue;
1006
+ }
1007
+ if (currentSection) {
1008
+ const depMatch = trimmed.match(/^ ([a-zA-Z_][a-zA-Z0-9_]*):/);
1009
+ if (depMatch) {
1010
+ const depName = depMatch[1];
1011
+ if (currentSection === "dependencies") {
1012
+ directDeps.add(depName);
1013
+ } else {
1014
+ devDeps.add(depName);
1015
+ }
1016
+ }
1017
+ }
1018
+ }
1019
+ return { directDeps, devDeps };
1020
+ }
1021
+ function parsePubspecLockPackages(content) {
1022
+ const packages = /* @__PURE__ */ new Map();
1023
+ const lines = content.split("\n");
1024
+ let inPackages = false;
1025
+ let currentPackage = null;
1026
+ let currentEntry = {};
1027
+ for (let i = 0; i < lines.length; i++) {
1028
+ const line = lines[i];
1029
+ const trimmedEnd = line.trimEnd();
1030
+ if (trimmedEnd === "packages:") {
1031
+ inPackages = true;
1032
+ continue;
1033
+ }
1034
+ if (!inPackages) {
1035
+ continue;
1036
+ }
1037
+ if (trimmedEnd !== "" && !trimmedEnd.startsWith(" ") && !trimmedEnd.startsWith(" ")) {
1038
+ if (currentPackage && currentEntry.dependency && currentEntry.source && currentEntry.version) {
1039
+ packages.set(currentPackage, currentEntry);
1040
+ }
1041
+ break;
1042
+ }
1043
+ if (trimmedEnd === "") {
1044
+ continue;
1045
+ }
1046
+ const packageNameMatch = line.match(/^ ([a-zA-Z_][a-zA-Z0-9_-]*):\s*$/);
1047
+ if (packageNameMatch) {
1048
+ if (currentPackage && currentEntry.dependency && currentEntry.source && currentEntry.version) {
1049
+ packages.set(currentPackage, currentEntry);
1050
+ }
1051
+ currentPackage = packageNameMatch[1];
1052
+ currentEntry = {};
1053
+ continue;
1054
+ }
1055
+ if (currentPackage && line.startsWith(" ") && !line.startsWith(" ")) {
1056
+ const propMatch = line.match(/^ (\w+):\s*"?([^"]*)"?\s*$/);
1057
+ if (propMatch) {
1058
+ const [, key, value] = propMatch;
1059
+ if (key === "dependency") {
1060
+ currentEntry.dependency = value;
1061
+ } else if (key === "source") {
1062
+ currentEntry.source = value;
1063
+ } else if (key === "version") {
1064
+ currentEntry.version = value;
1065
+ }
1066
+ }
1067
+ }
1068
+ }
1069
+ if (currentPackage && currentEntry.dependency && currentEntry.source && currentEntry.version) {
1070
+ packages.set(currentPackage, currentEntry);
1071
+ }
1072
+ return packages;
1073
+ }
1074
+
1075
+ // src/parsers/maven.ts
1076
+ var fs7 = __toESM(require("fs"));
1077
+ function parseMavenPom(pomPath, _manifestPath) {
1078
+ let content;
1079
+ try {
1080
+ content = fs7.readFileSync(pomPath, "utf-8");
1081
+ } catch {
1082
+ throw new Error(
1083
+ `Failed to read ${pomPath}.
1084
+ The pom.xml file may be missing or corrupted.`
1085
+ );
1086
+ }
1087
+ const properties = parseProperties(content);
1088
+ const dependencies = parseDependencies(content, properties);
1089
+ return dependencies;
1090
+ }
1091
+ function parseProperties(content) {
1092
+ const props = /* @__PURE__ */ new Map();
1093
+ const propsMatch = content.match(/<properties>([\s\S]*?)<\/properties>/);
1094
+ if (!propsMatch) return props;
1095
+ const propsBlock = propsMatch[1];
1096
+ const propRegex = /<([a-zA-Z0-9._-]+)>(.*?)<\/\1>/g;
1097
+ let match;
1098
+ while ((match = propRegex.exec(propsBlock)) !== null) {
1099
+ props.set(match[1], match[2].trim());
1100
+ }
1101
+ return props;
1102
+ }
1103
+ function resolveVersion(version, properties) {
1104
+ if (!version) return void 0;
1105
+ return version.replace(/\$\{([^}]+)\}/g, (_, propName) => {
1106
+ return properties.get(propName) ?? `\${${propName}}`;
1107
+ });
1108
+ }
1109
+ function parseDependencies(content, properties) {
1110
+ const results = [];
1111
+ const seen = /* @__PURE__ */ new Set();
1112
+ const withoutMgmt = content.replace(
1113
+ /<dependencyManagement>[\s\S]*?<\/dependencyManagement>/g,
1114
+ ""
1115
+ );
1116
+ const depsBlockRegex = /<dependencies>([\s\S]*?)<\/dependencies>/g;
1117
+ let depsMatch;
1118
+ while ((depsMatch = depsBlockRegex.exec(withoutMgmt)) !== null) {
1119
+ const depsBlock = depsMatch[1];
1120
+ const depRegex = /<dependency>([\s\S]*?)<\/dependency>/g;
1121
+ let depMatch;
1122
+ while ((depMatch = depRegex.exec(depsBlock)) !== null) {
1123
+ const depBlock = depMatch[1];
1124
+ const groupId = extractXmlValue(depBlock, "groupId");
1125
+ const artifactId = extractXmlValue(depBlock, "artifactId");
1126
+ const rawVersion = extractXmlValue(depBlock, "version");
1127
+ const scope = extractXmlValue(depBlock, "scope");
1128
+ if (!groupId || !artifactId) continue;
1129
+ if (scope === "provided" || scope === "system") continue;
1130
+ const version = resolveVersion(rawVersion, properties) ?? "UNKNOWN";
1131
+ const name = `${groupId}:${artifactId}`;
1132
+ const isDevDependency = scope === "test";
1133
+ const dedupKey = `${name}@${version}`;
1134
+ if (seen.has(dedupKey)) continue;
1135
+ seen.add(dedupKey);
1136
+ results.push({
1137
+ name,
1138
+ version,
1139
+ isDirect: true,
1140
+ // pom.xml only lists direct dependencies
1141
+ isDevDependency
1142
+ });
1143
+ }
1144
+ }
1145
+ return results;
1146
+ }
1147
+ function extractXmlValue(block, tag) {
1148
+ const match = block.match(new RegExp(`<${tag}>\\s*(.*?)\\s*</${tag}>`));
1149
+ return match ? match[1].trim() : void 0;
1150
+ }
1151
+
1152
+ // src/parsers/gradle.ts
1153
+ var fs8 = __toESM(require("fs"));
763
1154
  var path3 = __toESM(require("path"));
1155
+ function parseGradleLockfile(lockfilePath, manifestPath) {
1156
+ const lockFileName = path3.basename(lockfilePath);
1157
+ if (lockFileName === "gradle.lockfile") {
1158
+ return parseGradleLockfileFormat(lockfilePath);
1159
+ }
1160
+ const projectDir = path3.dirname(manifestPath);
1161
+ const versionCatalogPath = path3.join(projectDir, "gradle", "libs.versions.toml");
1162
+ if (fs8.existsSync(versionCatalogPath)) {
1163
+ const catalogDeps = parseVersionCatalog(versionCatalogPath);
1164
+ if (catalogDeps.length > 0) return catalogDeps;
1165
+ }
1166
+ return parseBuildGradleFile(manifestPath);
1167
+ }
1168
+ function parseGradleLockfileFormat(lockfilePath) {
1169
+ let content;
1170
+ try {
1171
+ content = fs8.readFileSync(lockfilePath, "utf-8");
1172
+ } catch {
1173
+ throw new Error(
1174
+ `Failed to read ${lockfilePath}.
1175
+ The gradle.lockfile may be missing or corrupted.`
1176
+ );
1177
+ }
1178
+ const results = [];
1179
+ const seen = /* @__PURE__ */ new Set();
1180
+ for (const line of content.split("\n")) {
1181
+ const trimmed = line.trim();
1182
+ if (!trimmed || trimmed.startsWith("#") || trimmed === "empty=") continue;
1183
+ const eqIdx = trimmed.indexOf("=");
1184
+ if (eqIdx === -1) continue;
1185
+ const coordPart = trimmed.slice(0, eqIdx);
1186
+ const configPart = trimmed.slice(eqIdx + 1);
1187
+ const parts = coordPart.split(":");
1188
+ if (parts.length < 3) continue;
1189
+ const groupId = parts[0];
1190
+ const artifactId = parts[1];
1191
+ const version = parts[2];
1192
+ const name = `${groupId}:${artifactId}`;
1193
+ const dedupKey = `${name}@${version}`;
1194
+ if (seen.has(dedupKey)) continue;
1195
+ seen.add(dedupKey);
1196
+ const configs = configPart.split(",").map((c) => c.trim().toLowerCase());
1197
+ const isTestOnly = configs.length > 0 && configs.every(
1198
+ (c) => c.includes("test") || c.includes("androidtest") || c.includes("unittest")
1199
+ );
1200
+ results.push({
1201
+ name,
1202
+ version,
1203
+ isDirect: true,
1204
+ // Lockfile doesn't distinguish, assume direct
1205
+ isDevDependency: isTestOnly
1206
+ });
1207
+ }
1208
+ return results;
1209
+ }
1210
+ function parseVersionCatalog(catalogPath) {
1211
+ let content;
1212
+ try {
1213
+ content = fs8.readFileSync(catalogPath, "utf-8");
1214
+ } catch {
1215
+ return [];
1216
+ }
1217
+ const versions = /* @__PURE__ */ new Map();
1218
+ const versionsSection = extractTomlSection(content, "versions");
1219
+ if (versionsSection) {
1220
+ for (const line of versionsSection.split("\n")) {
1221
+ const match = line.match(/^\s*([a-zA-Z0-9_-]+)\s*=\s*"([^"]+)"/);
1222
+ if (match) {
1223
+ versions.set(match[1], match[2]);
1224
+ }
1225
+ }
1226
+ }
1227
+ const results = [];
1228
+ const seen = /* @__PURE__ */ new Set();
1229
+ const librariesSection = extractTomlSection(content, "libraries");
1230
+ if (!librariesSection) return results;
1231
+ for (const line of librariesSection.split("\n")) {
1232
+ const trimmed = line.trim();
1233
+ if (!trimmed || trimmed.startsWith("#")) continue;
1234
+ const moduleMatch = trimmed.match(/module\s*=\s*"([^"]+)"/);
1235
+ if (!moduleMatch) {
1236
+ const groupMatch = trimmed.match(/group\s*=\s*"([^"]+)"/);
1237
+ const nameMatch = trimmed.match(/name\s*=\s*"([^"]+)"/);
1238
+ if (!groupMatch || !nameMatch) continue;
1239
+ const name2 = `${groupMatch[1]}:${nameMatch[1]}`;
1240
+ const version2 = resolveTomlVersion(trimmed, versions);
1241
+ if (!version2) continue;
1242
+ const dedupKey2 = `${name2}@${version2}`;
1243
+ if (seen.has(dedupKey2)) continue;
1244
+ seen.add(dedupKey2);
1245
+ results.push({ name: name2, version: version2, isDirect: true, isDevDependency: false });
1246
+ continue;
1247
+ }
1248
+ const name = moduleMatch[1];
1249
+ const version = resolveTomlVersion(trimmed, versions);
1250
+ if (!version) continue;
1251
+ const dedupKey = `${name}@${version}`;
1252
+ if (seen.has(dedupKey)) continue;
1253
+ seen.add(dedupKey);
1254
+ results.push({ name, version, isDirect: true, isDevDependency: false });
1255
+ }
1256
+ return results;
1257
+ }
1258
+ function extractTomlSection(content, sectionName) {
1259
+ const sectionRegex = new RegExp(`^\\[${sectionName}\\]\\s*$`, "m");
1260
+ const match = content.match(sectionRegex);
1261
+ if (!match || match.index === void 0) return null;
1262
+ const start = match.index + match[0].length;
1263
+ const nextSection = content.slice(start).match(/^\[/m);
1264
+ const end = nextSection?.index !== void 0 ? start + nextSection.index : content.length;
1265
+ return content.slice(start, end);
1266
+ }
1267
+ function resolveTomlVersion(line, versions) {
1268
+ const refMatch = line.match(/version\.ref\s*=\s*"([^"]+)"/);
1269
+ if (refMatch) {
1270
+ return versions.get(refMatch[1]);
1271
+ }
1272
+ const versionMatch = line.match(/version\s*=\s*"([^"]+)"/);
1273
+ if (versionMatch) {
1274
+ return versionMatch[1];
1275
+ }
1276
+ return void 0;
1277
+ }
1278
+ function parseBuildGradleFile(buildFilePath) {
1279
+ let content;
1280
+ try {
1281
+ content = fs8.readFileSync(buildFilePath, "utf-8");
1282
+ } catch {
1283
+ throw new Error(
1284
+ `Failed to read ${buildFilePath}.
1285
+ The build file may be missing or corrupted.`
1286
+ );
1287
+ }
1288
+ const results = [];
1289
+ const seen = /* @__PURE__ */ new Set();
1290
+ const depConfigs = [
1291
+ "implementation",
1292
+ "api",
1293
+ "compileOnly",
1294
+ "runtimeOnly",
1295
+ "testImplementation",
1296
+ "testCompileOnly",
1297
+ "testRuntimeOnly",
1298
+ "androidTestImplementation",
1299
+ "debugImplementation",
1300
+ "releaseImplementation"
1301
+ ];
1302
+ for (const config of depConfigs) {
1303
+ const patterns = [
1304
+ new RegExp(`${config}\\s*\\(\\s*["']([^"']+)["']\\s*\\)`, "g"),
1305
+ new RegExp(`${config}\\s+["']([^"']+)["']`, "g")
1306
+ ];
1307
+ for (const pattern of patterns) {
1308
+ let match;
1309
+ while ((match = pattern.exec(content)) !== null) {
1310
+ const dep = match[1];
1311
+ const parts = dep.split(":");
1312
+ if (parts.length < 3) continue;
1313
+ const name = `${parts[0]}:${parts[1]}`;
1314
+ const version = parts[2];
1315
+ const dedupKey = `${name}@${version}`;
1316
+ if (seen.has(dedupKey)) continue;
1317
+ seen.add(dedupKey);
1318
+ const isTest = config.toLowerCase().includes("test");
1319
+ results.push({
1320
+ name,
1321
+ version,
1322
+ isDirect: true,
1323
+ isDevDependency: isTest
1324
+ });
1325
+ }
1326
+ }
1327
+ const mapPattern = new RegExp(
1328
+ `${config}\\s+group:\\s*["']([^"']+)["']\\s*,\\s*name:\\s*["']([^"']+)["']\\s*,\\s*version:\\s*["']([^"']+)["']`,
1329
+ "g"
1330
+ );
1331
+ let mapMatch;
1332
+ while ((mapMatch = mapPattern.exec(content)) !== null) {
1333
+ const name = `${mapMatch[1]}:${mapMatch[2]}`;
1334
+ const version = mapMatch[3];
1335
+ const dedupKey = `${name}@${version}`;
1336
+ if (seen.has(dedupKey)) continue;
1337
+ seen.add(dedupKey);
1338
+ const isTest = config.toLowerCase().includes("test");
1339
+ results.push({
1340
+ name,
1341
+ version,
1342
+ isDirect: true,
1343
+ isDevDependency: isTest
1344
+ });
1345
+ }
1346
+ }
1347
+ return results;
1348
+ }
1349
+
1350
+ // src/parsers/cocoapods.ts
1351
+ var fs9 = __toESM(require("fs"));
1352
+ function parsePodfileLock(lockfilePath, _manifestPath) {
1353
+ let content;
1354
+ try {
1355
+ content = fs9.readFileSync(lockfilePath, "utf-8");
1356
+ } catch {
1357
+ throw new Error(
1358
+ `Failed to read ${lockfilePath}.
1359
+ The Podfile.lock may be missing or corrupted. Try running 'pod install'.`
1360
+ );
1361
+ }
1362
+ const pods = parsePodsSection(content);
1363
+ const directDeps = parseDependenciesSection(content);
1364
+ const results = [];
1365
+ const seen = /* @__PURE__ */ new Set();
1366
+ for (const [name, version] of pods) {
1367
+ if (name.includes("/")) continue;
1368
+ const dedupKey = `${name}@${version}`;
1369
+ if (seen.has(dedupKey)) continue;
1370
+ seen.add(dedupKey);
1371
+ const isDirect = directDeps.has(name);
1372
+ results.push({
1373
+ name,
1374
+ version,
1375
+ isDirect,
1376
+ isDevDependency: false
1377
+ // CocoaPods doesn't have dev dependency concept
1378
+ });
1379
+ }
1380
+ return results;
1381
+ }
1382
+ function parsePodsSection(content) {
1383
+ const pods = /* @__PURE__ */ new Map();
1384
+ const lines = content.split("\n");
1385
+ let inPodsSection = false;
1386
+ for (const line of lines) {
1387
+ const trimmed = line.trimEnd();
1388
+ if (trimmed === "PODS:") {
1389
+ inPodsSection = true;
1390
+ continue;
1391
+ }
1392
+ if (inPodsSection && trimmed !== "" && !trimmed.startsWith(" ") && !trimmed.startsWith(" ")) {
1393
+ break;
1394
+ }
1395
+ if (!inPodsSection) continue;
1396
+ if (trimmed === "") continue;
1397
+ const podMatch = trimmed.match(/^ - ([^\s(/]+(?:\/[^\s(]+)?)\s+\(([^)]+)\)/);
1398
+ if (podMatch) {
1399
+ pods.set(podMatch[1], podMatch[2]);
1400
+ }
1401
+ }
1402
+ return pods;
1403
+ }
1404
+ function parseDependenciesSection(content) {
1405
+ const directDeps = /* @__PURE__ */ new Set();
1406
+ const lines = content.split("\n");
1407
+ let inDepsSection = false;
1408
+ for (const line of lines) {
1409
+ const trimmed = line.trimEnd();
1410
+ if (trimmed === "DEPENDENCIES:") {
1411
+ inDepsSection = true;
1412
+ continue;
1413
+ }
1414
+ if (inDepsSection && trimmed !== "" && !trimmed.startsWith(" ") && !trimmed.startsWith(" ")) {
1415
+ break;
1416
+ }
1417
+ if (!inDepsSection) continue;
1418
+ if (trimmed === "") continue;
1419
+ const depMatch = trimmed.match(/^ - ([^\s(/]+)/);
1420
+ if (depMatch) {
1421
+ directDeps.add(depMatch[1]);
1422
+ }
1423
+ }
1424
+ return directDeps;
1425
+ }
1426
+
1427
+ // src/parsers/spm.ts
1428
+ var fs10 = __toESM(require("fs"));
1429
+ function parsePackageResolved(resolvedPath, _manifestPath) {
1430
+ let content;
1431
+ try {
1432
+ content = fs10.readFileSync(resolvedPath, "utf-8");
1433
+ } catch {
1434
+ throw new Error(
1435
+ `Failed to read ${resolvedPath}.
1436
+ The Package.resolved file may be missing or corrupted.
1437
+ Run 'swift package resolve' to regenerate it.`
1438
+ );
1439
+ }
1440
+ let parsed;
1441
+ try {
1442
+ parsed = JSON.parse(content);
1443
+ } catch {
1444
+ throw new Error(
1445
+ `Failed to parse ${resolvedPath}: Invalid JSON.
1446
+ The Package.resolved file may be corrupted.`
1447
+ );
1448
+ }
1449
+ const pins = extractPins(parsed);
1450
+ if (!pins) return [];
1451
+ const results = [];
1452
+ const seen = /* @__PURE__ */ new Set();
1453
+ for (const pin of pins) {
1454
+ if (pin.kind === "localSourceControl") continue;
1455
+ const version = pin.state?.version;
1456
+ if (!version) continue;
1457
+ const name = pin.identity;
1458
+ if (!name) continue;
1459
+ const dedupKey = `${name}@${version}`;
1460
+ if (seen.has(dedupKey)) continue;
1461
+ seen.add(dedupKey);
1462
+ results.push({
1463
+ name,
1464
+ version,
1465
+ isDirect: true,
1466
+ // Package.resolved doesn't distinguish
1467
+ isDevDependency: false
1468
+ });
1469
+ }
1470
+ return results;
1471
+ }
1472
+ function extractPins(parsed) {
1473
+ if (Array.isArray(parsed.pins)) {
1474
+ return parsed.pins;
1475
+ }
1476
+ if (parsed.object && Array.isArray(parsed.object.pins)) {
1477
+ return parsed.object.pins.map((pin) => ({
1478
+ identity: pin.package,
1479
+ kind: "remoteSourceControl",
1480
+ location: pin.repositoryURL || "",
1481
+ state: {
1482
+ revision: pin.state?.revision,
1483
+ version: pin.state?.version,
1484
+ branch: pin.state?.branch
1485
+ }
1486
+ }));
1487
+ }
1488
+ return null;
1489
+ }
1490
+ function extractSpmLocations(resolvedPath) {
1491
+ const locations = /* @__PURE__ */ new Map();
1492
+ let content;
1493
+ try {
1494
+ content = fs10.readFileSync(resolvedPath, "utf-8");
1495
+ } catch {
1496
+ return locations;
1497
+ }
1498
+ let parsed;
1499
+ try {
1500
+ parsed = JSON.parse(content);
1501
+ } catch {
1502
+ return locations;
1503
+ }
1504
+ const pins = extractPins(parsed);
1505
+ if (!pins) return locations;
1506
+ for (const pin of pins) {
1507
+ if (pin.identity && pin.location) {
1508
+ locations.set(pin.identity, pin.location);
1509
+ }
1510
+ }
1511
+ return locations;
1512
+ }
1513
+
1514
+ // src/cache.ts
1515
+ var crypto = __toESM(require("crypto"));
1516
+ var fs11 = __toESM(require("fs"));
1517
+ var path4 = __toESM(require("path"));
764
1518
  var KEY_FILENAME = ".preship-key";
765
1519
  var ALGORITHM = "aes-256-cbc";
766
1520
  var IV_LENGTH = 16;
767
1521
  function getOrCreateKey(projectPath) {
768
- const keyPath = path3.join(projectPath, KEY_FILENAME);
1522
+ const keyPath = path4.join(projectPath, KEY_FILENAME);
769
1523
  try {
770
- if (fs6.existsSync(keyPath)) {
771
- const hex = fs6.readFileSync(keyPath, "utf-8").trim();
1524
+ if (fs11.existsSync(keyPath)) {
1525
+ const hex = fs11.readFileSync(keyPath, "utf-8").trim();
772
1526
  if (hex.length === 64) {
773
1527
  return Buffer.from(hex, "hex");
774
1528
  }
@@ -777,7 +1531,7 @@ function getOrCreateKey(projectPath) {
777
1531
  }
778
1532
  const key = crypto.randomBytes(32);
779
1533
  try {
780
- fs6.writeFileSync(keyPath, key.toString("hex"), { mode: 384 });
1534
+ fs11.writeFileSync(keyPath, key.toString("hex"), { mode: 384 });
781
1535
  } catch {
782
1536
  }
783
1537
  return key;
@@ -813,10 +1567,10 @@ function createEmptyCache() {
813
1567
  }
814
1568
  function loadCache(cacheFilePath, key) {
815
1569
  try {
816
- if (!fs6.existsSync(cacheFilePath)) {
1570
+ if (!fs11.existsSync(cacheFilePath)) {
817
1571
  return createEmptyCache();
818
1572
  }
819
- const encryptedContent = fs6.readFileSync(cacheFilePath, "utf-8").trim();
1573
+ const encryptedContent = fs11.readFileSync(cacheFilePath, "utf-8").trim();
820
1574
  if (!encryptedContent) {
821
1575
  return createEmptyCache();
822
1576
  }
@@ -835,8 +1589,8 @@ function saveCache(cacheFilePath, cache, key) {
835
1589
  const jsonStr = JSON.stringify(cache);
836
1590
  const encrypted = encrypt(jsonStr, key);
837
1591
  const tmpPath = cacheFilePath + ".tmp";
838
- fs6.writeFileSync(tmpPath, encrypted, "utf-8");
839
- fs6.renameSync(tmpPath, cacheFilePath);
1592
+ fs11.writeFileSync(tmpPath, encrypted, "utf-8");
1593
+ fs11.renameSync(tmpPath, cacheFilePath);
840
1594
  } catch {
841
1595
  }
842
1596
  }
@@ -1025,13 +1779,19 @@ function createScanTimeoutController(timeout) {
1025
1779
  createEmptyCache,
1026
1780
  createScanTimeoutController,
1027
1781
  detectProjects,
1782
+ extractSpmLocations,
1028
1783
  getCacheEntry,
1029
1784
  getDefaultConfig,
1030
1785
  getOrCreateKey,
1031
1786
  loadCache,
1032
1787
  loadConfig,
1788
+ parseGradleLockfile,
1789
+ parseMavenPom,
1033
1790
  parseNpmLockfile,
1791
+ parsePackageResolved,
1034
1792
  parsePnpmLockfile,
1793
+ parsePodfileLock,
1794
+ parsePubspecLockfile,
1035
1795
  parseYarnLockfile,
1036
1796
  saveCache,
1037
1797
  setCacheEntry
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preship/core",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Core infrastructure for PreShip — types, config, detection, parsing, caching, and rate limiting",
5
5
  "author": "Cyfox Inc.",
6
6
  "license": "Apache-2.0",