@socketsecurity/lib 5.5.3 → 5.7.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.
@@ -5,6 +5,14 @@ export interface ExecutableDetectionResult {
5
5
  packageJsonPath?: string;
6
6
  inDlxCache?: boolean;
7
7
  }
8
+ /**
9
+ * Detect executable type for paths in DLX cache.
10
+ * Uses filesystem structure (node_modules/ presence).
11
+ *
12
+ * @param filePath - Path within DLX cache (~/.socket/_dlx/)
13
+ * @returns Detection result
14
+ */
15
+ export declare function detectDlxExecutableType(filePath: string): ExecutableDetectionResult;
8
16
  /**
9
17
  * Detect if a path is a Node.js package or native binary executable.
10
18
  * Works for both DLX cache paths and local filesystem paths.
@@ -27,14 +35,6 @@ export interface ExecutableDetectionResult {
27
35
  * ```
28
36
  */
29
37
  export declare function detectExecutableType(filePath: string): ExecutableDetectionResult;
30
- /**
31
- * Detect executable type for paths in DLX cache.
32
- * Uses filesystem structure (node_modules/ presence).
33
- *
34
- * @param filePath - Path within DLX cache (~/.socket/_dlx/)
35
- * @returns Detection result
36
- */
37
- export declare function detectDlxExecutableType(filePath: string): ExecutableDetectionResult;
38
38
  /**
39
39
  * Detect executable type for local filesystem paths.
40
40
  * Uses package.json and file extension checks.
@@ -46,11 +46,19 @@ function getPath() {
46
46
  return _path;
47
47
  }
48
48
  const NODE_JS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs"]);
49
- function detectExecutableType(filePath) {
50
- if ((0, import_paths.isInSocketDlx)(filePath)) {
51
- return detectDlxExecutableType(filePath);
49
+ function findPackageJson(filePath) {
50
+ const fs = /* @__PURE__ */ getFs();
51
+ const path = /* @__PURE__ */ getPath();
52
+ let currentDir = path.dirname(path.resolve(filePath));
53
+ const root = path.parse(currentDir).root;
54
+ while (currentDir !== root) {
55
+ const packageJsonPath = path.join(currentDir, "package.json");
56
+ if (fs.existsSync(packageJsonPath)) {
57
+ return packageJsonPath;
58
+ }
59
+ currentDir = path.dirname(currentDir);
52
60
  }
53
- return detectLocalExecutableType(filePath);
61
+ return void 0;
54
62
  }
55
63
  function detectDlxExecutableType(filePath) {
56
64
  const fs = /* @__PURE__ */ getFs();
@@ -73,6 +81,12 @@ function detectDlxExecutableType(filePath) {
73
81
  inDlxCache: true
74
82
  };
75
83
  }
84
+ function detectExecutableType(filePath) {
85
+ if ((0, import_paths.isInSocketDlx)(filePath)) {
86
+ return detectDlxExecutableType(filePath);
87
+ }
88
+ return detectLocalExecutableType(filePath);
89
+ }
76
90
  function detectLocalExecutableType(filePath) {
77
91
  const fs = /* @__PURE__ */ getFs();
78
92
  const packageJsonPath = findPackageJson(filePath);
@@ -103,20 +117,6 @@ function detectLocalExecutableType(filePath) {
103
117
  inDlxCache: false
104
118
  };
105
119
  }
106
- function findPackageJson(filePath) {
107
- const fs = /* @__PURE__ */ getFs();
108
- const path = /* @__PURE__ */ getPath();
109
- let currentDir = path.dirname(path.resolve(filePath));
110
- const root = path.parse(currentDir).root;
111
- while (currentDir !== root) {
112
- const packageJsonPath = path.join(currentDir, "package.json");
113
- if (fs.existsSync(packageJsonPath)) {
114
- return packageJsonPath;
115
- }
116
- currentDir = path.dirname(currentDir);
117
- }
118
- return void 0;
119
- }
120
120
  function isJsFilePath(filePath) {
121
121
  const path = /* @__PURE__ */ getPath();
122
122
  const ext = path.extname(filePath).toLowerCase();
@@ -14,17 +14,23 @@ export interface PackageDetails {
14
14
  * Details for binary download entries.
15
15
  */
16
16
  export interface BinaryDetails {
17
- checksum: string;
18
- checksum_algorithm: ChecksumAlgorithm;
17
+ /** SRI integrity hash (sha512-<base64>, aligned with npm). */
18
+ integrity: string;
19
19
  platform: string;
20
20
  arch: string;
21
21
  size: number;
22
22
  source: {
23
- type: 'download';
24
- url: string;
23
+ type: 'download' | 'extract';
24
+ url?: string;
25
+ path?: string;
26
+ };
27
+ /** Update check metadata (same structure as packages). */
28
+ update_check?: {
29
+ last_check: number;
30
+ last_notification: number;
31
+ latest_known: string;
25
32
  };
26
33
  }
27
- export type ChecksumAlgorithm = 'sha256' | 'sha512';
28
34
  /**
29
35
  * Unified manifest entry for all cached items (packages and binaries).
30
36
  * Shared fields at root, type-specific fields in details.
@@ -35,18 +41,6 @@ export interface ManifestEntry {
35
41
  timestamp: number;
36
42
  details: PackageDetails | BinaryDetails;
37
43
  }
38
- /**
39
- * Type guard for package entries.
40
- */
41
- export declare function isPackageEntry(entry: ManifestEntry): entry is ManifestEntry & {
42
- details: PackageDetails;
43
- };
44
- /**
45
- * Type guard for binary entries.
46
- */
47
- export declare function isBinaryEntry(entry: ManifestEntry): entry is ManifestEntry & {
48
- details: BinaryDetails;
49
- };
50
44
  /**
51
45
  * Legacy store record format (deprecated, for migration).
52
46
  */
@@ -61,6 +55,18 @@ export interface DlxManifestOptions {
61
55
  */
62
56
  manifestPath?: string;
63
57
  }
58
+ /**
59
+ * Type guard for binary entries.
60
+ */
61
+ export declare function isBinaryEntry(entry: ManifestEntry): entry is ManifestEntry & {
62
+ details: BinaryDetails;
63
+ };
64
+ /**
65
+ * Type guard for package entries.
66
+ */
67
+ export declare function isPackageEntry(entry: ManifestEntry): entry is ManifestEntry & {
68
+ details: PackageDetails;
69
+ };
64
70
  /**
65
71
  * DLX manifest storage manager with atomic operations.
66
72
  * Supports both legacy format (package name keys) and new unified manifest format (spec keys).
@@ -71,47 +77,48 @@ export declare class DlxManifest {
71
77
  constructor(options?: DlxManifestOptions);
72
78
  /**
73
79
  * Read the entire manifest file.
80
+ * @private
74
81
  */
75
82
  private readManifest;
83
+ private writeManifest;
76
84
  /**
77
- * Get a manifest entry by spec (e.g., "@socketsecurity/cli@^2.0.11").
85
+ * Clear cached data for a specific entry.
78
86
  */
79
- getManifestEntry(spec: string): ManifestEntry | undefined;
87
+ clear(name: string): Promise<void>;
88
+ /**
89
+ * Clear all cached data.
90
+ */
91
+ clearAll(): Promise<void>;
80
92
  /**
81
93
  * Get cached update information for a package (legacy format).
82
94
  * @deprecated Use getManifestEntry() for new code.
83
95
  */
84
96
  get(name: string): StoreRecord | undefined;
85
97
  /**
86
- * Set a package manifest entry.
98
+ * Get all cached package names.
87
99
  */
88
- setPackageEntry(spec: string, cacheKey: string, details: PackageDetails): Promise<void>;
100
+ getAllPackages(): string[];
89
101
  /**
90
- * Set a binary manifest entry.
102
+ * Get a manifest entry by spec (e.g., "@socketsecurity/cli@^2.0.11").
91
103
  */
92
- setBinaryEntry(spec: string, cacheKey: string, details: BinaryDetails): Promise<void>;
93
- private writeManifest;
104
+ getManifestEntry(spec: string): ManifestEntry | undefined;
105
+ /**
106
+ * Check if cached data is fresh based on TTL.
107
+ */
108
+ isFresh(record: StoreRecord | undefined, ttlMs: number): boolean;
94
109
  /**
95
110
  * Store update information for a package (legacy format).
96
111
  * @deprecated Use setPackageEntry() for new code.
97
112
  */
98
113
  set(name: string, record: StoreRecord): Promise<void>;
99
114
  /**
100
- * Clear cached data for a specific entry.
101
- */
102
- clear(name: string): Promise<void>;
103
- /**
104
- * Clear all cached data.
105
- */
106
- clearAll(): Promise<void>;
107
- /**
108
- * Check if cached data is fresh based on TTL.
115
+ * Set a binary manifest entry.
109
116
  */
110
- isFresh(record: StoreRecord | undefined, ttlMs: number): boolean;
117
+ setBinaryEntry(spec: string, cacheKey: string, details: BinaryDetails): Promise<void>;
111
118
  /**
112
- * Get all cached package names.
119
+ * Set a package manifest entry.
113
120
  */
114
- getAllPackages(): string[];
121
+ setPackageEntry(spec: string, cacheKey: string, details: PackageDetails): Promise<void>;
115
122
  }
116
123
  // Export singleton instance using default manifest location.
117
124
  export declare const dlxManifest: DlxManifest;
@@ -47,12 +47,12 @@ function getPath() {
47
47
  }
48
48
  const logger = (0, import_logger.getDefaultLogger)();
49
49
  const MANIFEST_FILE_NAME = ".dlx-manifest.json";
50
- function isPackageEntry(entry) {
51
- return entry.type === "package";
52
- }
53
50
  function isBinaryEntry(entry) {
54
51
  return entry.type === "binary";
55
52
  }
53
+ function isPackageEntry(entry) {
54
+ return entry.type === "package";
55
+ }
56
56
  class DlxManifest {
57
57
  manifestPath;
58
58
  lockPath;
@@ -62,6 +62,7 @@ class DlxManifest {
62
62
  }
63
63
  /**
64
64
  * Read the entire manifest file.
65
+ * @private
65
66
  */
66
67
  readManifest() {
67
68
  try {
@@ -81,61 +82,9 @@ class DlxManifest {
81
82
  return /* @__PURE__ */ Object.create(null);
82
83
  }
83
84
  }
84
- /**
85
- * Get a manifest entry by spec (e.g., "@socketsecurity/cli@^2.0.11").
86
- */
87
- getManifestEntry(spec) {
88
- const data = this.readManifest();
89
- const entry = data[spec];
90
- if (entry && "type" in entry) {
91
- return entry;
92
- }
93
- return void 0;
94
- }
95
- /**
96
- * Get cached update information for a package (legacy format).
97
- * @deprecated Use getManifestEntry() for new code.
98
- */
99
- get(name) {
100
- const data = this.readManifest();
101
- const entry = data[name];
102
- if (entry && !("type" in entry)) {
103
- return entry;
104
- }
105
- return void 0;
106
- }
107
- /**
108
- * Set a package manifest entry.
109
- */
110
- async setPackageEntry(spec, cacheKey, details) {
111
- await import_process_lock.processLock.withLock(this.lockPath, async () => {
112
- const data = this.readManifest();
113
- data[spec] = {
114
- type: "package",
115
- cache_key: cacheKey,
116
- timestamp: Date.now(),
117
- details
118
- };
119
- await this.writeManifest(data);
120
- });
121
- }
122
- /**
123
- * Set a binary manifest entry.
124
- */
125
- async setBinaryEntry(spec, cacheKey, details) {
126
- await import_process_lock.processLock.withLock(this.lockPath, async () => {
127
- const data = this.readManifest();
128
- data[spec] = {
129
- type: "binary",
130
- cache_key: cacheKey,
131
- timestamp: Date.now(),
132
- details
133
- };
134
- await this.writeManifest(data);
135
- });
136
- }
137
85
  /**
138
86
  * Write the manifest file atomically.
87
+ * @private
139
88
  */
140
89
  async writeManifest(data) {
141
90
  const manifestDir = (/* @__PURE__ */ getPath()).dirname(this.manifestPath);
@@ -167,56 +116,6 @@ class DlxManifest {
167
116
  throw error;
168
117
  }
169
118
  }
170
- /**
171
- * Store update information for a package (legacy format).
172
- * @deprecated Use setPackageEntry() for new code.
173
- */
174
- async set(name, record) {
175
- await import_process_lock.processLock.withLock(this.lockPath, async () => {
176
- let data = /* @__PURE__ */ Object.create(null);
177
- try {
178
- if ((/* @__PURE__ */ getFs()).existsSync(this.manifestPath)) {
179
- const content2 = (/* @__PURE__ */ getFs()).readFileSync(this.manifestPath, "utf8");
180
- if (content2.trim()) {
181
- data = JSON.parse(content2);
182
- }
183
- }
184
- } catch (error) {
185
- logger.warn(
186
- `Failed to read existing manifest: ${error instanceof Error ? error.message : String(error)}`
187
- );
188
- }
189
- data[name] = record;
190
- const manifestDir = (/* @__PURE__ */ getPath()).dirname(this.manifestPath);
191
- try {
192
- (0, import_fs.safeMkdirSync)(manifestDir, { recursive: true });
193
- } catch (error) {
194
- logger.warn(
195
- `Failed to create manifest directory: ${error instanceof Error ? error.message : String(error)}`
196
- );
197
- }
198
- const content = JSON.stringify(data, null, 2);
199
- const tempPath = `${this.manifestPath}.tmp`;
200
- try {
201
- (/* @__PURE__ */ getFs()).writeFileSync(tempPath, content, "utf8");
202
- (/* @__PURE__ */ getFs()).writeFileSync(this.manifestPath, content, "utf8");
203
- try {
204
- if ((/* @__PURE__ */ getFs()).existsSync(tempPath)) {
205
- (/* @__PURE__ */ getFs()).unlinkSync(tempPath);
206
- }
207
- } catch {
208
- }
209
- } catch (error) {
210
- try {
211
- if ((/* @__PURE__ */ getFs()).existsSync(tempPath)) {
212
- (/* @__PURE__ */ getFs()).unlinkSync(tempPath);
213
- }
214
- } catch {
215
- }
216
- throw error;
217
- }
218
- });
219
- }
220
119
  /**
221
120
  * Clear cached data for a specific entry.
222
121
  */
@@ -258,14 +157,16 @@ class DlxManifest {
258
157
  });
259
158
  }
260
159
  /**
261
- * Check if cached data is fresh based on TTL.
160
+ * Get cached update information for a package (legacy format).
161
+ * @deprecated Use getManifestEntry() for new code.
262
162
  */
263
- isFresh(record, ttlMs) {
264
- if (!record) {
265
- return false;
163
+ get(name) {
164
+ const data = this.readManifest();
165
+ const entry = data[name];
166
+ if (entry && !("type" in entry)) {
167
+ return entry;
266
168
  }
267
- const age = Date.now() - record.timestampFetch;
268
- return age < ttlMs;
169
+ return void 0;
269
170
  }
270
171
  /**
271
172
  * Get all cached package names.
@@ -289,6 +190,107 @@ class DlxManifest {
289
190
  return [];
290
191
  }
291
192
  }
193
+ /**
194
+ * Get a manifest entry by spec (e.g., "@socketsecurity/cli@^2.0.11").
195
+ */
196
+ getManifestEntry(spec) {
197
+ const data = this.readManifest();
198
+ const entry = data[spec];
199
+ if (entry && "type" in entry) {
200
+ return entry;
201
+ }
202
+ return void 0;
203
+ }
204
+ /**
205
+ * Check if cached data is fresh based on TTL.
206
+ */
207
+ isFresh(record, ttlMs) {
208
+ if (!record) {
209
+ return false;
210
+ }
211
+ const age = Date.now() - record.timestampFetch;
212
+ return age < ttlMs;
213
+ }
214
+ /**
215
+ * Store update information for a package (legacy format).
216
+ * @deprecated Use setPackageEntry() for new code.
217
+ */
218
+ async set(name, record) {
219
+ await import_process_lock.processLock.withLock(this.lockPath, async () => {
220
+ let data = /* @__PURE__ */ Object.create(null);
221
+ try {
222
+ if ((/* @__PURE__ */ getFs()).existsSync(this.manifestPath)) {
223
+ const content2 = (/* @__PURE__ */ getFs()).readFileSync(this.manifestPath, "utf8");
224
+ if (content2.trim()) {
225
+ data = JSON.parse(content2);
226
+ }
227
+ }
228
+ } catch (error) {
229
+ logger.warn(
230
+ `Failed to read existing manifest: ${error instanceof Error ? error.message : String(error)}`
231
+ );
232
+ }
233
+ data[name] = record;
234
+ const manifestDir = (/* @__PURE__ */ getPath()).dirname(this.manifestPath);
235
+ try {
236
+ (0, import_fs.safeMkdirSync)(manifestDir, { recursive: true });
237
+ } catch (error) {
238
+ logger.warn(
239
+ `Failed to create manifest directory: ${error instanceof Error ? error.message : String(error)}`
240
+ );
241
+ }
242
+ const content = JSON.stringify(data, null, 2);
243
+ const tempPath = `${this.manifestPath}.tmp`;
244
+ try {
245
+ (/* @__PURE__ */ getFs()).writeFileSync(tempPath, content, "utf8");
246
+ (/* @__PURE__ */ getFs()).writeFileSync(this.manifestPath, content, "utf8");
247
+ try {
248
+ if ((/* @__PURE__ */ getFs()).existsSync(tempPath)) {
249
+ (/* @__PURE__ */ getFs()).unlinkSync(tempPath);
250
+ }
251
+ } catch {
252
+ }
253
+ } catch (error) {
254
+ try {
255
+ if ((/* @__PURE__ */ getFs()).existsSync(tempPath)) {
256
+ (/* @__PURE__ */ getFs()).unlinkSync(tempPath);
257
+ }
258
+ } catch {
259
+ }
260
+ throw error;
261
+ }
262
+ });
263
+ }
264
+ /**
265
+ * Set a binary manifest entry.
266
+ */
267
+ async setBinaryEntry(spec, cacheKey, details) {
268
+ await import_process_lock.processLock.withLock(this.lockPath, async () => {
269
+ const data = this.readManifest();
270
+ data[spec] = {
271
+ type: "binary",
272
+ cache_key: cacheKey,
273
+ timestamp: Date.now(),
274
+ details
275
+ };
276
+ await this.writeManifest(data);
277
+ });
278
+ }
279
+ /**
280
+ * Set a package manifest entry.
281
+ */
282
+ async setPackageEntry(spec, cacheKey, details) {
283
+ await import_process_lock.processLock.withLock(this.lockPath, async () => {
284
+ const data = this.readManifest();
285
+ data[spec] = {
286
+ type: "package",
287
+ cache_key: cacheKey,
288
+ timestamp: Date.now(),
289
+ details
290
+ };
291
+ await this.writeManifest(data);
292
+ });
293
+ }
292
294
  }
293
295
  const dlxManifest = new DlxManifest();
294
296
  // Annotate the CommonJS export names for ESM import in node:
@@ -99,6 +99,15 @@ export declare function dlxPackage(args: readonly string[] | string[], options?:
99
99
  * ```
100
100
  */
101
101
  export declare function downloadPackage(options: DlxPackageOptions): Promise<DownloadPackageResult>;
102
+ /**
103
+ * Install package to ~/.socket/_dlx/<hash>/ if not already installed.
104
+ * Uses pacote for installation (no npm CLI required).
105
+ * Protected by process lock to prevent concurrent installation corruption.
106
+ */
107
+ export declare function ensurePackageInstalled(packageName: string, packageSpec: string, force: boolean): Promise<{
108
+ installed: boolean;
109
+ packageDir: string;
110
+ }>;
102
111
  /**
103
112
  * Execute a package's binary with cross-platform shell handling.
104
113
  * The package must already be installed (use downloadPackage first).
@@ -118,3 +127,49 @@ export declare function downloadPackage(options: DlxPackageOptions): Promise<Dow
118
127
  * ```
119
128
  */
120
129
  export declare function executePackage(binaryPath: string, args: readonly string[] | string[], spawnOptions?: SpawnOptions | undefined, spawnExtra?: SpawnExtra | undefined): ReturnType<typeof spawn>;
130
+ /**
131
+ * Find the binary path for an installed package.
132
+ * Uses npm's bin resolution strategy with user-friendly fallbacks.
133
+ * Resolves platform-specific wrappers (.cmd, .ps1, etc.) on Windows.
134
+ *
135
+ * Resolution strategy (cherry-picked from libnpmexec):
136
+ * 1. Use npm's getBinFromManifest (handles aliases and standard cases)
137
+ * 2. Fall back to user-provided binaryName if npm's strategy fails
138
+ * 3. Try last segment of package name as final fallback
139
+ * 4. Use first binary as last resort
140
+ */
141
+ export declare function findBinaryPath(packageDir: string, packageName: string, binaryName?: string): string;
142
+ /**
143
+ * Make all binaries in an installed package executable.
144
+ * Reads the package.json bin field and makes all binaries executable (chmod 0o755).
145
+ * Handles both single binary (string) and multiple binaries (object) formats.
146
+ *
147
+ * Aligns with npm's approach:
148
+ * - Uses 0o755 permission (matches npm's cmd-shim)
149
+ * - Reads bin field from package.json (matches npm's bin-links and libnpmexec)
150
+ * - Handles both string and object bin formats
151
+ *
152
+ * References:
153
+ * - npm cmd-shim: https://github.com/npm/cmd-shim/blob/main/lib/index.js
154
+ * - npm getBinFromManifest: https://github.com/npm/libnpmexec/blob/main/lib/get-bin-from-manifest.js
155
+ */
156
+ export declare function makePackageBinsExecutable(packageDir: string, packageName: string): void;
157
+ /**
158
+ * Parse package spec into name and version using npm-package-arg.
159
+ * Examples:
160
+ * - 'lodash@4.17.21' → { name: 'lodash', version: '4.17.21' }
161
+ * - '@scope/pkg@1.0.0' → { name: '@scope/pkg', version: '1.0.0' }
162
+ * - 'lodash' → { name: 'lodash', version: undefined }
163
+ */
164
+ export declare function parsePackageSpec(spec: string): {
165
+ name: string;
166
+ version: string | undefined;
167
+ };
168
+ /**
169
+ * Resolve binary path with cross-platform wrapper support.
170
+ * On Windows, checks for .cmd, .bat, .ps1, .exe wrappers in order.
171
+ * On Unix, uses path directly.
172
+ *
173
+ * Aligns with npm/npx binary resolution strategy.
174
+ */
175
+ export declare function resolveBinaryPath(basePath: string): string;