@socketsecurity/sdk 1.10.1 → 1.11.1
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 +19 -0
- package/README.md +153 -311
- package/dist/constants.js +23 -117
- package/dist/file-upload.js +118 -127
- package/dist/http-client.d.ts +11 -8
- package/dist/http-client.js +281 -223
- package/dist/index.js +43 -48
- package/dist/package.json.js +212 -0
- package/dist/quota-utils.d.ts +13 -6
- package/dist/quota-utils.js +128 -105
- package/dist/socket-sdk-class.d.ts +1 -1
- package/dist/socket-sdk-class.js +1488 -1407
- package/dist/testing.d.ts +453 -0
- package/dist/testing.js +387 -0
- package/dist/types.d.ts +26 -0
- package/dist/user-agent.js +11 -7
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +66 -60
- package/package.json +27 -6
- package/dist/promise-queue.js +0 -91
- package/dist/types.js +0 -3
package/dist/testing.js
ADDED
|
@@ -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;
|
package/dist/types.d.ts
CHANGED
|
@@ -129,12 +129,38 @@ export type SocketSdkGenericResult<T> = {
|
|
|
129
129
|
status: number;
|
|
130
130
|
success: false;
|
|
131
131
|
};
|
|
132
|
+
/**
|
|
133
|
+
* Configuration options for SocketSdk.
|
|
134
|
+
*/
|
|
132
135
|
export interface SocketSdkOptions {
|
|
136
|
+
/** HTTP agent for connection pooling and proxy support */
|
|
133
137
|
agent?: Agent | GotOptions | undefined;
|
|
138
|
+
/** Base URL for Socket API (default: 'https://api.socket.dev/v0/') */
|
|
134
139
|
baseUrl?: string | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* Enable TTL caching for API responses (default: false).
|
|
142
|
+
* When enabled, GET requests are cached with a 5-minute TTL.
|
|
143
|
+
*/
|
|
144
|
+
cache?: boolean | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* Cache TTL in milliseconds (default: 300000 = 5 minutes).
|
|
147
|
+
* Only used when cache is enabled.
|
|
148
|
+
*/
|
|
149
|
+
cacheTtl?: number | undefined;
|
|
150
|
+
/**
|
|
151
|
+
* Number of retry attempts on failure (default: 0, retries disabled).
|
|
152
|
+
* Retries are opt-in following Node.js fs.rm() pattern.
|
|
153
|
+
* Recommended: 3 for production, 0 for testing.
|
|
154
|
+
*/
|
|
135
155
|
retries?: number | undefined;
|
|
156
|
+
/**
|
|
157
|
+
* Initial delay in milliseconds between retries (default: 100).
|
|
158
|
+
* Uses exponential backoff: 100ms, 200ms, 400ms, etc.
|
|
159
|
+
*/
|
|
136
160
|
retryDelay?: number | undefined;
|
|
161
|
+
/** Request timeout in milliseconds */
|
|
137
162
|
timeout?: number | undefined;
|
|
163
|
+
/** Custom User-Agent header */
|
|
138
164
|
userAgent?: string | undefined;
|
|
139
165
|
}
|
|
140
166
|
export type UploadManifestFilesResponse = {
|
package/dist/user-agent.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
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,99 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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 = Object.create(null);
|
|
50
|
+
const entries = params.entries();
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
let key = entry[0];
|
|
53
|
+
const value = entry[1];
|
|
54
|
+
if (key === 'defaultBranch') {
|
|
55
|
+
/* c8 ignore next - query parameter normalization for API compatibility */
|
|
56
|
+
key = 'default_branch';
|
|
57
|
+
} else if (key === 'perPage') {
|
|
58
|
+
/* c8 ignore next 2 - query parameter normalization for API compatibility */
|
|
59
|
+
key = 'per_page';
|
|
68
60
|
}
|
|
69
|
-
|
|
61
|
+
/* c8 ignore next - skip empty string values in params */
|
|
62
|
+
if (value) {
|
|
63
|
+
normalized[key] = value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return new URLSearchParams(normalized);
|
|
70
67
|
}
|
|
68
|
+
|
|
71
69
|
/**
|
|
72
70
|
* Convert relative file paths to absolute paths.
|
|
73
71
|
* Resolves paths relative to specified base directory or current working directory.
|
|
74
72
|
*/
|
|
75
73
|
function resolveAbsPaths(filepaths, pathsRelativeTo) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
const basePath = resolveBasePath(pathsRelativeTo);
|
|
75
|
+
// Node's path.resolve will process path segments from right to left until
|
|
76
|
+
// it creates a valid absolute path. So if `pathsRelativeTo` is an absolute
|
|
77
|
+
// path, process.cwd() is not used, which is the common expectation. If none
|
|
78
|
+
// of the paths resolve then it defaults to process.cwd().
|
|
79
|
+
return filepaths.map(p => path.normalizePath(path$1.resolve(basePath, p)));
|
|
82
80
|
}
|
|
81
|
+
|
|
83
82
|
/**
|
|
84
83
|
* Resolve base path to an absolute directory path.
|
|
85
84
|
* Converts relative paths to absolute using current working directory as reference.
|
|
86
85
|
*/
|
|
87
86
|
function resolveBasePath(pathsRelativeTo = '.') {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
// Node's path.resolve will process path segments from right to left until
|
|
88
|
+
// it creates a valid absolute path. So if `pathsRelativeTo` is an absolute
|
|
89
|
+
// path, process.cwd() is not used, which is the common expectation. If none
|
|
90
|
+
// of the paths resolve then it defaults to process.cwd().
|
|
91
|
+
return path.normalizePath(path$1.resolve(process.cwd(), pathsRelativeTo));
|
|
93
92
|
}
|
|
93
|
+
|
|
94
|
+
exports.createUserAgentFromPkgJson = userAgent.createUserAgentFromPkgJson;
|
|
95
|
+
exports.normalizeBaseUrl = normalizeBaseUrl;
|
|
96
|
+
exports.promiseWithResolvers = promiseWithResolvers;
|
|
97
|
+
exports.queryToSearchParams = queryToSearchParams;
|
|
98
|
+
exports.resolveAbsPaths = resolveAbsPaths;
|
|
99
|
+
exports.resolveBasePath = resolveBasePath;
|