@socketsecurity/lib 5.1.4 → 5.2.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/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.2.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.2.0) - 2026-01-06
9
+
10
+ ### Added
11
+
12
+ - **releases**: Added GitHub release download utilities for cross-project use
13
+ - Added `downloadGitHubRelease()` for downloading releases from any GitHub repository
14
+ - Added `downloadSocketBtmRelease()` specialized wrapper for socket-btm releases
15
+ - Features version caching with `.version` files to avoid redundant downloads
16
+ - Supports cross-platform binary downloads (darwin, linux, win32) with automatic platform/arch detection
17
+ - Includes Linux musl/glibc support with musl as default for broader compatibility
18
+ - Automatically removes macOS quarantine attributes from downloaded binaries
19
+ - Supports generic asset downloads (WASM files, models, etc.)
20
+ - API inspired by industry tools: `brew`, `cargo`, `gh` for intuitive usage
21
+ - Package exports: `@socketsecurity/lib/releases/github` and `@socketsecurity/lib/releases/socket-btm`
22
+
8
23
  ## [5.1.4](https://github.com/SocketDev/socket-lib/releases/tag/v5.1.4) - 2025-12-30
9
24
 
10
25
  ### Fixed
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Socket-btm GitHub repository configuration.
3
+ */
4
+ export declare const SOCKET_BTM_REPO: {
5
+ readonly owner: "SocketDev";
6
+ readonly repo: "socket-btm";
7
+ };
8
+ /**
9
+ * Configuration for repository access.
10
+ */
11
+ export interface RepoConfig {
12
+ /**
13
+ * GitHub repository owner/organization.
14
+ */
15
+ owner: string;
16
+ /**
17
+ * GitHub repository name.
18
+ */
19
+ repo: string;
20
+ }
21
+ /**
22
+ * Get latest release tag matching a tool prefix.
23
+ *
24
+ * Searches recent releases for the first tag matching the given prefix.
25
+ * Useful for finding latest versions of tools with date-based tags like
26
+ * `node-smol-20260105-c47753c` or `binject-20260106-1df5745`.
27
+ *
28
+ * @param toolPrefix - Tool name prefix to search for (e.g., 'node-smol-', 'binject-')
29
+ * @param repoConfig - Repository configuration (owner/repo)
30
+ * @param options - Additional options
31
+ * @returns Latest release tag or null if not found
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const tag = await getLatestRelease('node-smol-', {
36
+ * owner: 'SocketDev',
37
+ * repo: 'socket-btm'
38
+ * })
39
+ * // Returns: 'node-smol-20260105-c47753c'
40
+ * ```
41
+ */
42
+ export declare function getLatestRelease(toolPrefix: string, repoConfig: RepoConfig, options?: {
43
+ quiet?: boolean;
44
+ }): Promise<string | null>;
45
+ /**
46
+ * Get download URL for a specific release asset.
47
+ *
48
+ * @param tag - Release tag name (e.g., 'node-smol-20260105-c47753c')
49
+ * @param assetName - Asset name to download (e.g., 'node-linux-x64-musl')
50
+ * @param repoConfig - Repository configuration (owner/repo)
51
+ * @param options - Additional options
52
+ * @returns Browser download URL for the asset
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const url = await getReleaseAssetUrl(
57
+ * 'node-smol-20260105-c47753c',
58
+ * 'node-linux-x64-musl',
59
+ * { owner: 'SocketDev', repo: 'socket-btm' }
60
+ * )
61
+ * ```
62
+ */
63
+ export declare function getReleaseAssetUrl(tag: string, assetName: string, repoConfig: RepoConfig, options?: {
64
+ quiet?: boolean;
65
+ }): Promise<string | null>;
66
+ /**
67
+ * Download a specific release asset.
68
+ *
69
+ * Uses browser_download_url to avoid consuming GitHub API quota.
70
+ * Automatically follows redirects and retries on failure.
71
+ *
72
+ * @param tag - Release tag name
73
+ * @param assetName - Asset name to download
74
+ * @param outputPath - Path to write the downloaded file
75
+ * @param repoConfig - Repository configuration (owner/repo)
76
+ * @param options - Additional options
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * await downloadReleaseAsset(
81
+ * 'node-smol-20260105-c47753c',
82
+ * 'node-linux-x64-musl',
83
+ * '/path/to/output/node',
84
+ * { owner: 'SocketDev', repo: 'socket-btm' }
85
+ * )
86
+ * ```
87
+ */
88
+ export declare function downloadReleaseAsset(tag: string, assetName: string, outputPath: string, repoConfig: RepoConfig, options?: {
89
+ quiet?: boolean;
90
+ }): Promise<void>;
91
+ /**
92
+ * Configuration for downloading a GitHub release.
93
+ */
94
+ export interface DownloadGitHubReleaseConfig {
95
+ /**
96
+ * GitHub repository owner/organization.
97
+ */
98
+ owner: string;
99
+ /**
100
+ * GitHub repository name.
101
+ */
102
+ repo: string;
103
+ /**
104
+ * Working directory (defaults to process.cwd()).
105
+ * Used to resolve relative paths in downloadDir.
106
+ */
107
+ cwd?: string;
108
+ /**
109
+ * Download destination directory.
110
+ * Can be absolute or relative to cwd.
111
+ * @default 'build/downloaded' (relative to cwd)
112
+ */
113
+ downloadDir?: string;
114
+ /**
115
+ * Tool name for directory structure (e.g., 'node-smol', 'binject', 'lief').
116
+ * Creates subdirectory: {downloadDir}/{toolName}/{platformArch}/
117
+ */
118
+ toolName: string;
119
+ /**
120
+ * Platform-arch identifier (e.g., 'linux-x64-musl', 'darwin-arm64').
121
+ * Used for the download directory path.
122
+ */
123
+ platformArch: string;
124
+ /**
125
+ * Binary filename (e.g., 'node', 'binject', 'lief', 'node.exe').
126
+ */
127
+ binaryName: string;
128
+ /**
129
+ * Asset name on GitHub (e.g., 'node-linux-x64-musl', 'binject-darwin-arm64').
130
+ */
131
+ assetName: string;
132
+ /**
133
+ * Tool prefix for finding latest release (e.g., 'node-smol-', 'binject-').
134
+ * Either this or `tag` must be provided.
135
+ */
136
+ toolPrefix?: string;
137
+ /**
138
+ * Specific release tag to download (e.g., 'node-smol-20260105-c47753c').
139
+ * If not provided, uses `toolPrefix` to find the latest release.
140
+ */
141
+ tag?: string;
142
+ /**
143
+ * Suppress log messages.
144
+ * @default false
145
+ */
146
+ quiet?: boolean;
147
+ /**
148
+ * Remove macOS quarantine attribute after download.
149
+ * Only applies when downloading on macOS for macOS binaries.
150
+ * @default true
151
+ */
152
+ removeMacOSQuarantine?: boolean;
153
+ }
154
+ /**
155
+ * Download a binary from any GitHub repository with version caching.
156
+ *
157
+ * Downloads to: `{downloadDir}/{toolName}/{platformArch}/{binaryName}`
158
+ * Caches version in: `{downloadDir}/{toolName}/{platformArch}/.version`
159
+ *
160
+ * @param config - Download configuration
161
+ * @returns Path to the downloaded binary
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * // Download from any GitHub repo
166
+ * const nodePath = await downloadGitHubRelease({
167
+ * owner: 'nodejs',
168
+ * repo: 'node',
169
+ * cwd: process.cwd(),
170
+ * downloadDir: 'build/downloaded', // relative to cwd
171
+ * toolName: 'node',
172
+ * platformArch: 'linux-x64',
173
+ * binaryName: 'node',
174
+ * assetName: 'node-v20.10.0-linux-x64.tar.gz',
175
+ * tag: 'v20.10.0'
176
+ * })
177
+ * ```
178
+ */
179
+ export declare function downloadGitHubRelease(config: DownloadGitHubReleaseConfig): Promise<string>;
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ /* Socket Lib - Built with esbuild */
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var github_exports = {};
31
+ __export(github_exports, {
32
+ SOCKET_BTM_REPO: () => SOCKET_BTM_REPO,
33
+ downloadGitHubRelease: () => downloadGitHubRelease,
34
+ downloadReleaseAsset: () => downloadReleaseAsset,
35
+ getLatestRelease: () => getLatestRelease,
36
+ getReleaseAssetUrl: () => getReleaseAssetUrl
37
+ });
38
+ module.exports = __toCommonJS(github_exports);
39
+ var import_node_fs = require("node:fs");
40
+ var import_promises = require("node:fs/promises");
41
+ var import_node_path = __toESM(require("node:path"));
42
+ var import_fs = require("../fs.js");
43
+ var import_http_request = require("../http-request.js");
44
+ var import_logger = require("../logger.js");
45
+ var import_promises2 = require("../promises.js");
46
+ var import_spawn = require("../spawn.js");
47
+ const logger = (0, import_logger.getDefaultLogger)();
48
+ const SOCKET_BTM_REPO = {
49
+ owner: "SocketDev",
50
+ repo: "socket-btm"
51
+ };
52
+ const RETRY_CONFIG = Object.freeze({
53
+ __proto__: null,
54
+ // Exponential backoff: delay doubles with each retry (5s, 10s, 20s).
55
+ backoffFactor: 2,
56
+ // Initial delay before first retry.
57
+ baseDelayMs: 5e3,
58
+ // Maximum number of retry attempts (excluding initial request).
59
+ retries: 2
60
+ });
61
+ function getAuthHeaders() {
62
+ const token = process.env["GH_TOKEN"] || process.env["GITHUB_TOKEN"];
63
+ const headers = {
64
+ Accept: "application/vnd.github+json",
65
+ "X-GitHub-Api-Version": "2022-11-28"
66
+ };
67
+ if (token) {
68
+ headers["Authorization"] = `Bearer ${token}`;
69
+ }
70
+ return headers;
71
+ }
72
+ async function getLatestRelease(toolPrefix, repoConfig, options = {}) {
73
+ const { owner, repo } = repoConfig;
74
+ const { quiet = false } = options;
75
+ return await (0, import_promises2.pRetry)(
76
+ async () => {
77
+ const response = await (0, import_http_request.httpRequest)(
78
+ `https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`,
79
+ {
80
+ headers: getAuthHeaders()
81
+ }
82
+ );
83
+ if (!response.ok) {
84
+ throw new Error(`Failed to fetch releases: ${response.status}`);
85
+ }
86
+ const releases = JSON.parse(response.body.toString("utf8"));
87
+ for (const release of releases) {
88
+ const { tag_name: tag } = release;
89
+ if (tag.startsWith(toolPrefix)) {
90
+ if (!quiet) {
91
+ logger.info(`Found release: ${tag}`);
92
+ }
93
+ return tag;
94
+ }
95
+ }
96
+ if (!quiet) {
97
+ logger.info(`No ${toolPrefix} release found in latest 100 releases`);
98
+ }
99
+ return null;
100
+ },
101
+ {
102
+ ...RETRY_CONFIG,
103
+ onRetry: (attempt, error) => {
104
+ if (!quiet) {
105
+ logger.info(
106
+ `Retry attempt ${attempt + 1}/${RETRY_CONFIG.retries + 1} for ${toolPrefix} release...`
107
+ );
108
+ logger.warn(
109
+ `Attempt ${attempt + 1}/${RETRY_CONFIG.retries + 1} failed: ${error instanceof Error ? error.message : String(error)}`
110
+ );
111
+ }
112
+ return void 0;
113
+ }
114
+ }
115
+ );
116
+ }
117
+ async function getReleaseAssetUrl(tag, assetName, repoConfig, options = {}) {
118
+ const { owner, repo } = repoConfig;
119
+ const { quiet = false } = options;
120
+ return await (0, import_promises2.pRetry)(
121
+ async () => {
122
+ const response = await (0, import_http_request.httpRequest)(
123
+ `https://api.github.com/repos/${owner}/${repo}/releases/tags/${tag}`,
124
+ {
125
+ headers: getAuthHeaders()
126
+ }
127
+ );
128
+ if (!response.ok) {
129
+ throw new Error(`Failed to fetch release ${tag}: ${response.status}`);
130
+ }
131
+ const release = JSON.parse(response.body.toString("utf8"));
132
+ const asset = release.assets.find(
133
+ (a) => a.name === assetName
134
+ );
135
+ if (!asset) {
136
+ throw new Error(`Asset ${assetName} not found in release ${tag}`);
137
+ }
138
+ if (!quiet) {
139
+ logger.info(`Found asset: ${assetName}`);
140
+ }
141
+ return asset.browser_download_url;
142
+ },
143
+ {
144
+ ...RETRY_CONFIG,
145
+ onRetry: (attempt, error) => {
146
+ if (!quiet) {
147
+ logger.info(
148
+ `Retry attempt ${attempt + 1}/${RETRY_CONFIG.retries + 1} for asset URL...`
149
+ );
150
+ logger.warn(
151
+ `Attempt ${attempt + 1}/${RETRY_CONFIG.retries + 1} failed: ${error instanceof Error ? error.message : String(error)}`
152
+ );
153
+ }
154
+ return void 0;
155
+ }
156
+ }
157
+ );
158
+ }
159
+ async function downloadReleaseAsset(tag, assetName, outputPath, repoConfig, options = {}) {
160
+ const { owner, repo } = repoConfig;
161
+ const { quiet = false } = options;
162
+ const downloadUrl = await getReleaseAssetUrl(
163
+ tag,
164
+ assetName,
165
+ { owner, repo },
166
+ { quiet }
167
+ );
168
+ if (!downloadUrl) {
169
+ throw new Error(`Asset ${assetName} not found in release ${tag}`);
170
+ }
171
+ await (0, import_fs.safeMkdir)(import_node_path.default.dirname(outputPath));
172
+ await (0, import_http_request.httpDownload)(downloadUrl, outputPath, {
173
+ logger: quiet ? void 0 : logger,
174
+ progressInterval: 10,
175
+ retries: 2,
176
+ retryDelay: 5e3
177
+ });
178
+ }
179
+ async function downloadGitHubRelease(config) {
180
+ const {
181
+ assetName,
182
+ binaryName,
183
+ cwd = process.cwd(),
184
+ downloadDir = "build/downloaded",
185
+ owner,
186
+ platformArch,
187
+ quiet = false,
188
+ removeMacOSQuarantine = true,
189
+ repo,
190
+ tag: explicitTag,
191
+ toolName,
192
+ toolPrefix
193
+ } = config;
194
+ let tag;
195
+ if (explicitTag) {
196
+ tag = explicitTag;
197
+ } else if (toolPrefix) {
198
+ const latestTag = await getLatestRelease(
199
+ toolPrefix,
200
+ { owner, repo },
201
+ { quiet }
202
+ );
203
+ if (!latestTag) {
204
+ throw new Error(`No ${toolPrefix} release found in ${owner}/${repo}`);
205
+ }
206
+ tag = latestTag;
207
+ } else {
208
+ throw new Error("Either toolPrefix or tag must be provided");
209
+ }
210
+ const resolvedDownloadDir = import_node_path.default.isAbsolute(downloadDir) ? downloadDir : import_node_path.default.join(cwd, downloadDir);
211
+ const binaryDir = import_node_path.default.join(resolvedDownloadDir, toolName, platformArch);
212
+ const binaryPath = import_node_path.default.join(binaryDir, binaryName);
213
+ const versionPath = import_node_path.default.join(binaryDir, ".version");
214
+ if ((0, import_node_fs.existsSync)(versionPath) && (0, import_node_fs.existsSync)(binaryPath)) {
215
+ const cachedVersion = (await (0, import_promises.readFile)(versionPath, "utf8")).trim();
216
+ if (cachedVersion === tag) {
217
+ if (!quiet) {
218
+ logger.info(`Using cached ${toolName} (${platformArch}): ${binaryPath}`);
219
+ }
220
+ return binaryPath;
221
+ }
222
+ }
223
+ if (!quiet) {
224
+ logger.info(`Downloading ${toolName} for ${platformArch}...`);
225
+ }
226
+ await downloadReleaseAsset(
227
+ tag,
228
+ assetName,
229
+ binaryPath,
230
+ { owner, repo },
231
+ { quiet }
232
+ );
233
+ const isWindows = binaryName.endsWith(".exe");
234
+ if (!isWindows) {
235
+ await (0, import_promises.chmod)(binaryPath, 493);
236
+ if (removeMacOSQuarantine && process.platform === "darwin" && platformArch.startsWith("darwin")) {
237
+ try {
238
+ await (0, import_spawn.spawn)("xattr", ["-d", "com.apple.quarantine", binaryPath], {
239
+ stdio: "ignore"
240
+ });
241
+ } catch {
242
+ }
243
+ }
244
+ }
245
+ await (0, import_promises.writeFile)(versionPath, tag, "utf8");
246
+ if (!quiet) {
247
+ logger.info(`Downloaded ${toolName} to ${binaryPath}`);
248
+ }
249
+ return binaryPath;
250
+ }
251
+ // Annotate the CommonJS export names for ESM import in node:
252
+ 0 && (module.exports = {
253
+ SOCKET_BTM_REPO,
254
+ downloadGitHubRelease,
255
+ downloadReleaseAsset,
256
+ getLatestRelease,
257
+ getReleaseAssetUrl
258
+ });
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Platform type for socket-btm binaries.
3
+ */
4
+ export type Platform = 'darwin' | 'linux' | 'win32';
5
+ /**
6
+ * Architecture type for socket-btm binaries.
7
+ */
8
+ export type Arch = 'arm64' | 'x64';
9
+ /**
10
+ * Linux libc variant.
11
+ */
12
+ export type Libc = 'musl' | 'glibc';
13
+ /**
14
+ * Configuration for downloading socket-btm binary releases.
15
+ */
16
+ export interface SocketBtmBinaryConfig {
17
+ /**
18
+ * Working directory (defaults to process.cwd()).
19
+ */
20
+ cwd?: string;
21
+ /**
22
+ * Download destination directory.
23
+ * Can be absolute or relative to cwd.
24
+ * @default 'build/downloaded' (relative to cwd)
25
+ *
26
+ * Inspired by: gh release download --dir
27
+ */
28
+ downloadDir?: string;
29
+ /**
30
+ * Tool/package name for directory structure and release matching.
31
+ * Similar to: brew install <formula>, cargo install <crate>
32
+ *
33
+ * Examples: 'node-smol', 'binject', 'binflate'
34
+ *
35
+ * Used for:
36
+ * - Directory path: {downloadDir}/{tool}/{platformArch}/
37
+ * - Finding release: Searches for tags starting with '{tool}-'
38
+ */
39
+ tool: string;
40
+ /**
41
+ * Binary/executable name (without extension).
42
+ * Similar to: brew formula→binary mapping (postgresql→psql, imagemagick→magick)
43
+ *
44
+ * Examples: 'node', 'binject', 'psql', 'magick'
45
+ *
46
+ * Used to construct:
47
+ * - Asset pattern: {bin}-{platform}-{arch}[-musl][.exe]
48
+ * - Output filename: {bin} or {bin}.exe
49
+ *
50
+ * Presence of this field indicates binary download (vs asset download).
51
+ *
52
+ * @default tool (e.g., 'binject'→'binject', but 'node-smol'→'node-smol')
53
+ */
54
+ bin?: string;
55
+ /**
56
+ * Target platform (defaults to current platform).
57
+ */
58
+ targetPlatform?: Platform;
59
+ /**
60
+ * Target architecture (defaults to current arch).
61
+ */
62
+ targetArch?: Arch;
63
+ /**
64
+ * Linux libc variant (musl or glibc).
65
+ * Defaults to musl for Linux for broader compatibility.
66
+ * Ignored for non-Linux platforms.
67
+ */
68
+ libc?: Libc;
69
+ /**
70
+ * Specific release tag to download.
71
+ * Inspired by: gh release download <tag>
72
+ *
73
+ * If not provided, downloads the latest release matching '{tool}-*' pattern.
74
+ *
75
+ * Examples: 'node-smol-20260105-c47753c', 'binject-20260106-1df5745'
76
+ */
77
+ tag?: string;
78
+ /**
79
+ * Suppress log messages.
80
+ * @default false
81
+ */
82
+ quiet?: boolean;
83
+ /**
84
+ * Remove macOS quarantine attribute after download.
85
+ * Only applies when downloading on macOS for macOS binaries.
86
+ * @default true
87
+ */
88
+ removeMacOSQuarantine?: boolean;
89
+ // Discriminator: presence of 'asset' means this is NOT a binary config
90
+ asset?: never;
91
+ }
92
+ /**
93
+ * Configuration for downloading socket-btm generic assets.
94
+ */
95
+ export interface SocketBtmAssetConfig {
96
+ /**
97
+ * Working directory (defaults to process.cwd()).
98
+ */
99
+ cwd?: string;
100
+ /**
101
+ * Download destination directory.
102
+ * Can be absolute or relative to cwd.
103
+ * @default 'build/downloaded' (relative to cwd)
104
+ *
105
+ * Inspired by: gh release download --dir
106
+ */
107
+ downloadDir?: string;
108
+ /**
109
+ * Tool/package name for directory structure and release matching.
110
+ *
111
+ * Examples: 'yoga-layout', 'onnxruntime', 'models'
112
+ *
113
+ * Used for:
114
+ * - Directory path: {downloadDir}/{tool}/assets/
115
+ * - Finding release: Searches for tags starting with '{tool}-'
116
+ */
117
+ tool: string;
118
+ /**
119
+ * Asset name pattern on GitHub.
120
+ * Inspired by: gh release download --pattern
121
+ *
122
+ * Examples: 'yoga-sync.mjs', 'ort-wasm-simd.wasm', '*.onnx'
123
+ *
124
+ * Presence of this field indicates asset download (vs binary download).
125
+ */
126
+ asset: string;
127
+ /**
128
+ * Output filename (e.g., 'yoga-sync.mjs').
129
+ * Inspired by: gh release download --output
130
+ *
131
+ * @default asset (uses the asset name as-is)
132
+ */
133
+ output?: string;
134
+ /**
135
+ * Specific release tag to download.
136
+ * Inspired by: gh release download <tag>
137
+ *
138
+ * If not provided, downloads the latest release matching '{tool}-*' pattern.
139
+ *
140
+ * Examples: 'yoga-layout-v20260106-a39285c', 'onnxruntime-v20260106-a39285c'
141
+ */
142
+ tag?: string;
143
+ /**
144
+ * Suppress log messages.
145
+ * @default false
146
+ */
147
+ quiet?: boolean;
148
+ /**
149
+ * Remove macOS quarantine attribute after download.
150
+ * @default false (not needed for non-executable assets)
151
+ */
152
+ removeMacOSQuarantine?: boolean;
153
+ // Discriminators: mutually exclusive with binary-specific fields
154
+ bin?: never;
155
+ targetPlatform?: never;
156
+ targetArch?: never;
157
+ libc?: never;
158
+ }
159
+ /**
160
+ * Configuration for downloading socket-btm releases (binary or asset).
161
+ */
162
+ export type SocketBtmReleaseConfig = SocketBtmBinaryConfig | SocketBtmAssetConfig;
163
+ /**
164
+ * Download a release from socket-btm.
165
+ *
166
+ * Generic function for downloading any socket-btm binary or asset.
167
+ * Handles both platform-specific binaries and generic assets.
168
+ *
169
+ * @param config - Download configuration
170
+ * @returns Path to the downloaded file
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * // Binary: node-smol (like: brew install nodejs → node)
175
+ * const nodePath = await downloadSocketBtmRelease({
176
+ * tool: 'node-smol',
177
+ * bin: 'node'
178
+ * })
179
+ *
180
+ * // Binary: binject (like: cargo install binject → binject)
181
+ * const binjectPath = await downloadSocketBtmRelease({
182
+ * tool: 'binject'
183
+ * })
184
+ *
185
+ * // Binary: cross-platform
186
+ * const binflatePath = await downloadSocketBtmRelease({
187
+ * tool: 'binflate',
188
+ * targetPlatform: 'linux',
189
+ * targetArch: 'x64',
190
+ * libc: 'musl'
191
+ * })
192
+ *
193
+ * // Asset: WASM file
194
+ * const yogaPath = await downloadSocketBtmRelease({
195
+ * tool: 'yoga-layout',
196
+ * asset: 'yoga-sync.mjs'
197
+ * })
198
+ *
199
+ * // Asset: with custom output name
200
+ * const ortPath = await downloadSocketBtmRelease({
201
+ * tool: 'onnxruntime',
202
+ * asset: 'ort-wasm-simd.wasm',
203
+ * output: 'ort.wasm'
204
+ * })
205
+ *
206
+ * // Custom paths (like: gh release download --dir)
207
+ * await downloadSocketBtmRelease({
208
+ * tool: 'node-smol',
209
+ * bin: 'node',
210
+ * cwd: '/path/to/project',
211
+ * downloadDir: 'build/cache'
212
+ * })
213
+ *
214
+ * // Specific version (like: gh release download <tag>)
215
+ * await downloadSocketBtmRelease({
216
+ * tool: 'binject',
217
+ * tag: 'binject-20260106-1df5745'
218
+ * })
219
+ * ```
220
+ */
221
+ export declare function downloadSocketBtmRelease(config: SocketBtmReleaseConfig): Promise<string>;
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ /* Socket Lib - Built with esbuild */
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var socket_btm_exports = {};
31
+ __export(socket_btm_exports, {
32
+ downloadSocketBtmRelease: () => downloadSocketBtmRelease
33
+ });
34
+ module.exports = __toCommonJS(socket_btm_exports);
35
+ var import_node_os = __toESM(require("node:os"));
36
+ var import_github = require("./github.js");
37
+ const ARCH_MAP = {
38
+ arm64: "arm64",
39
+ x64: "x64"
40
+ };
41
+ function getBinaryAssetName(binaryBaseName, platform, arch, libc) {
42
+ const mappedArch = ARCH_MAP[arch];
43
+ if (!mappedArch) {
44
+ throw new Error(`Unsupported architecture: ${arch}`);
45
+ }
46
+ const muslSuffix = platform === "linux" && libc === "musl" ? "-musl" : "";
47
+ const ext = platform === "win32" ? ".exe" : "";
48
+ if (platform === "darwin") {
49
+ return `${binaryBaseName}-darwin-${mappedArch}${ext}`;
50
+ }
51
+ if (platform === "linux") {
52
+ return `${binaryBaseName}-linux-${mappedArch}${muslSuffix}${ext}`;
53
+ }
54
+ if (platform === "win32") {
55
+ return `${binaryBaseName}-win-${mappedArch}${ext}`;
56
+ }
57
+ throw new Error(`Unsupported platform: ${platform}`);
58
+ }
59
+ function getPlatformArch(platform, arch, libc) {
60
+ const mappedArch = ARCH_MAP[arch];
61
+ if (!mappedArch) {
62
+ throw new Error(`Unsupported architecture: ${arch}`);
63
+ }
64
+ const muslSuffix = platform === "linux" && libc === "musl" ? "-musl" : "";
65
+ return `${platform}-${mappedArch}${muslSuffix}`;
66
+ }
67
+ function getBinaryName(binaryBaseName, platform) {
68
+ return platform === "win32" ? `${binaryBaseName}.exe` : binaryBaseName;
69
+ }
70
+ async function downloadSocketBtmRelease(config) {
71
+ const { cwd, downloadDir, quiet = false, tag, tool } = config;
72
+ const toolPrefix = `${tool}-`;
73
+ let downloadConfig;
74
+ if ("asset" in config) {
75
+ const {
76
+ asset,
77
+ output,
78
+ removeMacOSQuarantine = false
79
+ } = config;
80
+ const outputName = output || asset;
81
+ const platformArch = "assets";
82
+ downloadConfig = {
83
+ owner: import_github.SOCKET_BTM_REPO.owner,
84
+ repo: import_github.SOCKET_BTM_REPO.repo,
85
+ cwd,
86
+ downloadDir,
87
+ toolName: tool,
88
+ platformArch,
89
+ binaryName: outputName,
90
+ assetName: asset,
91
+ toolPrefix,
92
+ tag,
93
+ quiet,
94
+ removeMacOSQuarantine
95
+ };
96
+ } else {
97
+ const {
98
+ bin,
99
+ libc,
100
+ removeMacOSQuarantine = true,
101
+ targetArch,
102
+ targetPlatform
103
+ } = config;
104
+ const baseName = bin || tool;
105
+ const platform = targetPlatform || import_node_os.default.platform();
106
+ const arch = targetArch || import_node_os.default.arch();
107
+ const libcType = libc || (platform === "linux" ? "musl" : void 0);
108
+ const assetName = getBinaryAssetName(baseName, platform, arch, libcType);
109
+ const platformArch = getPlatformArch(platform, arch, libcType);
110
+ const binaryName = getBinaryName(baseName, platform);
111
+ downloadConfig = {
112
+ owner: import_github.SOCKET_BTM_REPO.owner,
113
+ repo: import_github.SOCKET_BTM_REPO.repo,
114
+ cwd,
115
+ downloadDir,
116
+ toolName: tool,
117
+ platformArch,
118
+ binaryName,
119
+ assetName,
120
+ toolPrefix,
121
+ tag,
122
+ quiet,
123
+ removeMacOSQuarantine
124
+ };
125
+ }
126
+ return await (0, import_github.downloadGitHubRelease)(downloadConfig);
127
+ }
128
+ // Annotate the CommonJS export names for ESM import in node:
129
+ 0 && (module.exports = {
130
+ downloadSocketBtmRelease
131
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socketsecurity/lib",
3
- "version": "5.1.4",
3
+ "version": "5.2.0",
4
4
  "packageManager": "pnpm@10.26.2",
5
5
  "license": "MIT",
6
6
  "description": "Core utilities and infrastructure for Socket.dev security tools",
@@ -523,6 +523,14 @@
523
523
  "types": "./dist/regexps.d.ts",
524
524
  "default": "./dist/regexps.js"
525
525
  },
526
+ "./releases/github": {
527
+ "types": "./dist/releases/github.d.ts",
528
+ "default": "./dist/releases/github.js"
529
+ },
530
+ "./releases/socket-btm": {
531
+ "types": "./dist/releases/socket-btm.d.ts",
532
+ "default": "./dist/releases/socket-btm.js"
533
+ },
526
534
  "./sea": {
527
535
  "types": "./dist/sea.d.ts",
528
536
  "default": "./dist/sea.js"