@socketsecurity/sdk 1.11.0 → 1.11.2

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.
@@ -0,0 +1,387 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Testing utilities for Socket SDK.
5
+ * Provides mock factories, response builders, and test helpers for easier SDK testing.
6
+ */
7
+
8
+ /**
9
+ * Create a successful SDK response.
10
+ *
11
+ * @template T - The data type
12
+ * @param data - The response data
13
+ * @param status - HTTP status code (default: 200)
14
+ * @returns A successful SDK result
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const response = mockSuccessResponse({ id: '123', name: 'test' })
19
+ * expect(response.success).toBe(true)
20
+ * ```
21
+ */
22
+ function mockSuccessResponse(data, status = 200) {
23
+ return {
24
+ cause: undefined,
25
+ data,
26
+ error: undefined,
27
+ status,
28
+ success: true
29
+ };
30
+ }
31
+
32
+ /**
33
+ * Create an error SDK response.
34
+ *
35
+ * @template T - The data type (unused in error responses)
36
+ * @param error - The error message
37
+ * @param status - HTTP status code (default: 500)
38
+ * @param cause - Optional error cause
39
+ * @returns An error SDK result
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * const response = mockErrorResponse('Not found', 404)
44
+ * expect(response.success).toBe(false)
45
+ * ```
46
+ */
47
+ function mockErrorResponse(error, status = 500, cause) {
48
+ return {
49
+ cause,
50
+ data: undefined,
51
+ error,
52
+ status,
53
+ success: false
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Create a mock Socket API error response body.
59
+ *
60
+ * @param message - Error message
61
+ * @param details - Optional error details
62
+ * @returns Socket API error response structure
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * nock('https://api.socket.dev')
67
+ * .get('/v0/repo/org/repo')
68
+ * .reply(404, mockApiErrorBody('Repository not found'))
69
+ * ```
70
+ */
71
+ function mockApiErrorBody(message, details) {
72
+ return {
73
+ error: {
74
+ message,
75
+ ...(details ? {
76
+ details
77
+ } : {})
78
+ }
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Common fixture data for organization responses.
84
+ */
85
+ const organizationFixtures = {
86
+ /**
87
+ * Basic organization with minimal data.
88
+ */
89
+ basic: {
90
+ id: 'org_123',
91
+ name: 'test-org',
92
+ plan: 'free'
93
+ },
94
+ /**
95
+ * Organization with full details.
96
+ */
97
+ full: {
98
+ id: 'org_123',
99
+ name: 'test-org',
100
+ plan: 'enterprise',
101
+ created_at: '2024-01-01T00:00:00Z',
102
+ updated_at: '2024-01-02T00:00:00Z'
103
+ }
104
+ };
105
+
106
+ /**
107
+ * Common fixture data for repository responses.
108
+ */
109
+ const repositoryFixtures = {
110
+ /**
111
+ * Basic repository with minimal data.
112
+ */
113
+ basic: {
114
+ id: 'repo_123',
115
+ name: 'test-repo',
116
+ archived: false,
117
+ default_branch: 'main'
118
+ },
119
+ /**
120
+ * Archived repository.
121
+ */
122
+ archived: {
123
+ id: 'repo_456',
124
+ name: 'old-repo',
125
+ archived: true,
126
+ default_branch: 'master'
127
+ },
128
+ /**
129
+ * Repository with full details.
130
+ */
131
+ full: {
132
+ id: 'repo_123',
133
+ name: 'test-repo',
134
+ archived: false,
135
+ default_branch: 'main',
136
+ homepage: 'https://example.com',
137
+ visibility: 'public',
138
+ created_at: '2024-01-01T00:00:00Z',
139
+ updated_at: '2024-01-02T00:00:00Z'
140
+ }
141
+ };
142
+
143
+ /**
144
+ * Common fixture data for scan responses.
145
+ */
146
+ const scanFixtures = {
147
+ /**
148
+ * Pending scan.
149
+ */
150
+ pending: {
151
+ id: 'scan_pending',
152
+ status: 'pending',
153
+ created_at: '2024-01-01T00:00:00Z'
154
+ },
155
+ /**
156
+ * Completed scan with no issues.
157
+ */
158
+ completed: {
159
+ id: 'scan_completed',
160
+ status: 'completed',
161
+ created_at: '2024-01-01T00:00:00Z',
162
+ completed_at: '2024-01-01T00:01:00Z',
163
+ issues_found: 0
164
+ },
165
+ /**
166
+ * Completed scan with issues.
167
+ */
168
+ withIssues: {
169
+ id: 'scan_with_issues',
170
+ status: 'completed',
171
+ created_at: '2024-01-01T00:00:00Z',
172
+ completed_at: '2024-01-01T00:01:00Z',
173
+ issues_found: 3
174
+ },
175
+ /**
176
+ * Failed scan.
177
+ */
178
+ failed: {
179
+ id: 'scan_failed',
180
+ status: 'failed',
181
+ created_at: '2024-01-01T00:00:00Z',
182
+ error: 'Scan timeout'
183
+ }
184
+ };
185
+
186
+ /**
187
+ * Common fixture data for package/artifact responses.
188
+ */
189
+ const packageFixtures = {
190
+ /**
191
+ * Safe package with high score.
192
+ */
193
+ safe: {
194
+ id: 'pkg_safe',
195
+ name: 'safe-package',
196
+ version: '1.0.0',
197
+ score: 95
198
+ },
199
+ /**
200
+ * Package with vulnerabilities.
201
+ */
202
+ vulnerable: {
203
+ id: 'pkg_vuln',
204
+ name: 'vulnerable-package',
205
+ version: '2.0.0',
206
+ score: 45,
207
+ issues: ['vulnerability']
208
+ },
209
+ /**
210
+ * Package with malware alert.
211
+ */
212
+ malware: {
213
+ id: 'pkg_malware',
214
+ name: 'malware-package',
215
+ version: '3.0.0',
216
+ score: 0,
217
+ issues: ['malware']
218
+ }
219
+ };
220
+
221
+ /**
222
+ * Common fixture data for issue/alert responses.
223
+ */
224
+ const issueFixtures = {
225
+ /**
226
+ * Vulnerability issue.
227
+ */
228
+ vulnerability: {
229
+ type: 'vulnerability',
230
+ severity: 'high',
231
+ key: 'CVE-2024-1234',
232
+ description: 'SQL Injection vulnerability'
233
+ },
234
+ /**
235
+ * Malware issue.
236
+ */
237
+ malware: {
238
+ type: 'malware',
239
+ severity: 'critical',
240
+ key: 'malware-detected',
241
+ description: 'Malicious code detected'
242
+ },
243
+ /**
244
+ * License issue.
245
+ */
246
+ license: {
247
+ type: 'license',
248
+ severity: 'medium',
249
+ key: 'license-incompatible',
250
+ description: 'License incompatible with project'
251
+ }
252
+ };
253
+
254
+ /**
255
+ * All fixture categories in one object.
256
+ */
257
+ const fixtures = {
258
+ issues: issueFixtures,
259
+ organizations: organizationFixtures,
260
+ packages: packageFixtures,
261
+ repositories: repositoryFixtures,
262
+ scans: scanFixtures
263
+ };
264
+
265
+ /**
266
+ * Mock SDK method result with proper typing.
267
+ *
268
+ * @template T - The operation type
269
+ * @param success - Whether the operation succeeded
270
+ * @param data - Success data or error details
271
+ * @returns Properly typed SDK result
272
+ *
273
+ * @example
274
+ * ```ts
275
+ * const mockGet = vi.fn().mockResolvedValue(
276
+ * mockSdkResult<'getRepo'>(true, { id: '123', name: 'repo' })
277
+ * )
278
+ * ```
279
+ */
280
+
281
+ function mockSdkResult(success, dataOrError, status = success ? 200 : 500, cause) {
282
+ if (success) {
283
+ return {
284
+ cause: undefined,
285
+ data: dataOrError,
286
+ error: undefined,
287
+ status,
288
+ success: true
289
+ };
290
+ }
291
+ return {
292
+ cause,
293
+ data: undefined,
294
+ error: dataOrError,
295
+ status,
296
+ success: false
297
+ };
298
+ }
299
+
300
+ /**
301
+ * Create a mock SDK error with proper structure.
302
+ *
303
+ * @param type - Error type ('NOT_FOUND', 'UNAUTHORIZED', etc.)
304
+ * @param options - Error options
305
+ * @returns Error response matching SDK structure
306
+ *
307
+ * @example
308
+ * ```ts
309
+ * const mockMethod = vi.fn().mockRejectedValue(
310
+ * mockSdkError('NOT_FOUND', { status: 404, message: 'Repository not found' })
311
+ * )
312
+ * ```
313
+ */
314
+ function mockSdkError(type, options = {}) {
315
+ const statusMap = {
316
+ FORBIDDEN: 403,
317
+ NOT_FOUND: 404,
318
+ SERVER_ERROR: 500,
319
+ TIMEOUT: 408,
320
+ UNAUTHORIZED: 401
321
+ };
322
+ const messageMap = {
323
+ FORBIDDEN: 'Access forbidden',
324
+ NOT_FOUND: 'Resource not found',
325
+ SERVER_ERROR: 'Internal server error',
326
+ TIMEOUT: 'Request timeout',
327
+ UNAUTHORIZED: 'Unauthorized'
328
+ };
329
+ const status = options.status ?? statusMap[type];
330
+ const message = options.message ?? messageMap[type];
331
+ const error = new Error(message);
332
+ error.status = status;
333
+ if (options.cause) {
334
+ error.cause = options.cause;
335
+ }
336
+ return error;
337
+ }
338
+
339
+ /**
340
+ * Type guard to check if SDK result is successful.
341
+ *
342
+ * @param result - SDK result to check
343
+ * @returns True if result is successful
344
+ *
345
+ * @example
346
+ * ```ts
347
+ * const result = await sdk.getRepo('org', 'repo')
348
+ * if (isSuccessResult(result)) {
349
+ * console.log(result.data.name) // Type-safe access
350
+ * }
351
+ * ```
352
+ */
353
+ function isSuccessResult(result) {
354
+ return result.success === true;
355
+ }
356
+
357
+ /**
358
+ * Type guard to check if SDK result is an error.
359
+ *
360
+ * @param result - SDK result to check
361
+ * @returns True if result is an error
362
+ *
363
+ * @example
364
+ * ```ts
365
+ * const result = await sdk.getRepo('org', 'repo')
366
+ * if (isErrorResult(result)) {
367
+ * console.error(result.error) // Type-safe access
368
+ * }
369
+ * ```
370
+ */
371
+ function isErrorResult(result) {
372
+ return result.success === false;
373
+ }
374
+
375
+ exports.fixtures = fixtures;
376
+ exports.isErrorResult = isErrorResult;
377
+ exports.isSuccessResult = isSuccessResult;
378
+ exports.issueFixtures = issueFixtures;
379
+ exports.mockApiErrorBody = mockApiErrorBody;
380
+ exports.mockErrorResponse = mockErrorResponse;
381
+ exports.mockSdkError = mockSdkError;
382
+ exports.mockSdkResult = mockSdkResult;
383
+ exports.mockSuccessResponse = mockSuccessResponse;
384
+ exports.organizationFixtures = organizationFixtures;
385
+ exports.packageFixtures = packageFixtures;
386
+ exports.repositoryFixtures = repositoryFixtures;
387
+ exports.scanFixtures = scanFixtures;
@@ -1,17 +1,21 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createUserAgentFromPkgJson = createUserAgentFromPkgJson;
1
+ 'use strict';
2
+
4
3
  /**
5
4
  * @fileoverview User-Agent string generation utilities.
6
5
  * Creates standardized User-Agent headers from package.json data for API requests.
7
6
  */
7
+
8
8
  /**
9
9
  * Generate a User-Agent string from package.json data.
10
10
  * Creates standardized User-Agent format with optional homepage URL.
11
11
  */
12
12
  function createUserAgentFromPkgJson(pkgData) {
13
- const { homepage } = pkgData;
14
- const name = pkgData.name.replace('@', '').replace('/', '-');
15
- /* c8 ignore next - homepage URL is optional in package.json */
16
- return `${name}/${pkgData.version}${homepage ? ` (${homepage})` : ''}`;
13
+ const {
14
+ homepage
15
+ } = pkgData;
16
+ const name = pkgData.name.replace('@', '').replace('/', '-');
17
+ /* c8 ignore next - homepage URL is optional in package.json */
18
+ return `${name}/${pkgData.version}${homepage ? ` (${homepage})` : ''}`;
17
19
  }
20
+
21
+ exports.createUserAgentFromPkgJson = createUserAgentFromPkgJson;
package/dist/utils.d.ts CHANGED
@@ -3,8 +3,9 @@ export { createUserAgentFromPkgJson } from './user-agent';
3
3
  /**
4
4
  * Normalize base URL by ensuring it ends with a trailing slash.
5
5
  * Required for proper URL joining with relative paths.
6
+ * Memoized for performance since base URLs are typically reused.
6
7
  */
7
- export declare function normalizeBaseUrl(baseUrl: string): string;
8
+ export declare const normalizeBaseUrl: (baseUrl: string) => string;
8
9
  /**
9
10
  * Create a promise with externally accessible resolve/reject functions.
10
11
  * Polyfill for Promise.withResolvers() on older Node.js versions.
package/dist/utils.js CHANGED
@@ -1,93 +1,101 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createUserAgentFromPkgJson = void 0;
7
- exports.normalizeBaseUrl = normalizeBaseUrl;
8
- exports.promiseWithResolvers = promiseWithResolvers;
9
- exports.queryToSearchParams = queryToSearchParams;
10
- exports.resolveAbsPaths = resolveAbsPaths;
11
- exports.resolveBasePath = resolveBasePath;
1
+ 'use strict';
2
+
3
+ var path$1 = require('node:path');
4
+ var memoization = require('@socketsecurity/registry/lib/memoization');
5
+ var path = require('@socketsecurity/registry/lib/path');
6
+ var userAgent = require('./user-agent.js');
7
+
12
8
  /**
13
9
  * @fileoverview Utility functions for Socket SDK operations.
14
10
  * Provides URL normalization, query parameter handling, and path resolution utilities.
15
11
  */
16
- const node_path_1 = __importDefault(require("node:path"));
17
- const path_1 = require("@socketsecurity/registry/lib/path");
18
- // Re-export user agent function for convenience
19
- const user_agent_1 = require("./user-agent");
20
- Object.defineProperty(exports, "createUserAgentFromPkgJson", { enumerable: true, get: function () { return user_agent_1.createUserAgentFromPkgJson; } });
12
+
21
13
  /**
22
14
  * Normalize base URL by ensuring it ends with a trailing slash.
23
15
  * Required for proper URL joining with relative paths.
16
+ * Memoized for performance since base URLs are typically reused.
24
17
  */
25
- function normalizeBaseUrl(baseUrl) {
26
- return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
27
- }
18
+ const normalizeBaseUrl = memoization.memoize(baseUrl => {
19
+ return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
20
+ }, {
21
+ name: 'normalizeBaseUrl'
22
+ });
23
+
28
24
  /**
29
25
  * Create a promise with externally accessible resolve/reject functions.
30
26
  * Polyfill for Promise.withResolvers() on older Node.js versions.
31
27
  */
32
28
  function promiseWithResolvers() {
33
- /* c8 ignore next 3 - polyfill for older Node versions without Promise.withResolvers */
34
- if (Promise.withResolvers) {
35
- return Promise.withResolvers();
36
- }
37
- /* c8 ignore next 7 - polyfill implementation for older Node versions */
38
- const obj = {};
39
- obj.promise = new Promise((resolver, reject) => {
40
- obj.resolve = resolver;
41
- obj.reject = reject;
42
- });
43
- return obj;
29
+ /* c8 ignore next 3 - polyfill for older Node versions without Promise.withResolvers */
30
+ if (Promise.withResolvers) {
31
+ return Promise.withResolvers();
32
+ }
33
+
34
+ /* c8 ignore next 7 - polyfill implementation for older Node versions */
35
+ const obj = {};
36
+ obj.promise = new Promise((resolver, reject) => {
37
+ obj.resolve = resolver;
38
+ obj.reject = reject;
39
+ });
40
+ return obj;
44
41
  }
42
+
45
43
  /**
46
44
  * Convert query parameters to URLSearchParams with API-compatible key normalization.
47
45
  * Transforms camelCase keys to snake_case and filters out empty values.
48
46
  */
49
47
  function queryToSearchParams(init) {
50
- const params = new URLSearchParams(init);
51
- const normalized = Object.create(null);
52
- const entries = params.entries();
53
- for (const entry of entries) {
54
- let key = entry[0];
55
- const value = entry[1];
56
- if (key === 'defaultBranch') {
57
- /* c8 ignore next - query parameter normalization for API compatibility */
58
- key = 'default_branch';
59
- }
60
- else if (key === 'perPage') {
61
- /* c8 ignore next 2 - query parameter normalization for API compatibility */
62
- key = 'per_page';
63
- }
64
- /* c8 ignore next - skip empty string values in params */
65
- if (value) {
66
- normalized[key] = value;
67
- }
48
+ const params = new URLSearchParams(init);
49
+ const normalized = {
50
+ __proto__: null
51
+ };
52
+ const entries = params.entries();
53
+ for (const entry of entries) {
54
+ let key = entry[0];
55
+ const value = entry[1];
56
+ if (key === 'defaultBranch') {
57
+ /* c8 ignore next - query parameter normalization for API compatibility */
58
+ key = 'default_branch';
59
+ } else if (key === 'perPage') {
60
+ /* c8 ignore next 2 - query parameter normalization for API compatibility */
61
+ key = 'per_page';
68
62
  }
69
- return new URLSearchParams(normalized);
63
+ /* c8 ignore next - skip empty string values in params */
64
+ if (value) {
65
+ normalized[key] = value;
66
+ }
67
+ }
68
+ return new URLSearchParams(normalized);
70
69
  }
70
+
71
71
  /**
72
72
  * Convert relative file paths to absolute paths.
73
73
  * Resolves paths relative to specified base directory or current working directory.
74
74
  */
75
75
  function resolveAbsPaths(filepaths, pathsRelativeTo) {
76
- const basePath = resolveBasePath(pathsRelativeTo);
77
- // Node's path.resolve will process path segments from right to left until
78
- // it creates a valid absolute path. So if `pathsRelativeTo` is an absolute
79
- // path, process.cwd() is not used, which is the common expectation. If none
80
- // of the paths resolve then it defaults to process.cwd().
81
- return filepaths.map(p => (0, path_1.normalizePath)(node_path_1.default.resolve(basePath, p)));
76
+ const basePath = resolveBasePath(pathsRelativeTo);
77
+ // Node's path.resolve will process path segments from right to left until
78
+ // it creates a valid absolute path. So if `pathsRelativeTo` is an absolute
79
+ // path, process.cwd() is not used, which is the common expectation. If none
80
+ // of the paths resolve then it defaults to process.cwd().
81
+ return filepaths.map(p => path.normalizePath(path$1.resolve(basePath, p)));
82
82
  }
83
+
83
84
  /**
84
85
  * Resolve base path to an absolute directory path.
85
86
  * Converts relative paths to absolute using current working directory as reference.
86
87
  */
87
88
  function resolveBasePath(pathsRelativeTo = '.') {
88
- // Node's path.resolve will process path segments from right to left until
89
- // it creates a valid absolute path. So if `pathsRelativeTo` is an absolute
90
- // path, process.cwd() is not used, which is the common expectation. If none
91
- // of the paths resolve then it defaults to process.cwd().
92
- return (0, path_1.normalizePath)(node_path_1.default.resolve(process.cwd(), pathsRelativeTo));
89
+ // Node's path.resolve will process path segments from right to left until
90
+ // it creates a valid absolute path. So if `pathsRelativeTo` is an absolute
91
+ // path, process.cwd() is not used, which is the common expectation. If none
92
+ // of the paths resolve then it defaults to process.cwd().
93
+ return path.normalizePath(path$1.resolve(process.cwd(), pathsRelativeTo));
93
94
  }
95
+
96
+ exports.createUserAgentFromPkgJson = userAgent.createUserAgentFromPkgJson;
97
+ exports.normalizeBaseUrl = normalizeBaseUrl;
98
+ exports.promiseWithResolvers = promiseWithResolvers;
99
+ exports.queryToSearchParams = queryToSearchParams;
100
+ exports.resolveAbsPaths = resolveAbsPaths;
101
+ exports.resolveBasePath = resolveBasePath;