-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathapi-errors.mts
More file actions
125 lines (120 loc) · 3.94 KB
/
Copy pathapi-errors.mts
File metadata and controls
125 lines (120 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
* @file Error result conversion for Socket API clients.
*/
import {
SOCKET_API_TOKENS_URL,
SOCKET_CONTACT_URL,
SOCKET_DASHBOARD_URL,
} from './constants.mts'
import { ResponseError } from './http-client.mts'
import { filterRedundantCause } from './utils.mts'
import type { SocketSdkErrorResult } from './types/core.mts'
export const SDK_ERROR_GUIDANCE: Readonly<Record<number, readonly string[]>> = {
400: [
'→ Bad request. Invalid parameters or request body.',
'→ Check: All required parameters are provided and correctly formatted.',
'→ Verify: Package URLs (PURLs) follow correct format.',
],
401: [
'→ Authentication failed. API token is invalid or expired.',
'→ Check: Your API token is correct and active.',
`→ Generate a new token at: ${SOCKET_API_TOKENS_URL}`,
],
403: [
'→ Authorization failed. Insufficient permissions.',
'→ Check: Your API token has required permissions for this operation.',
'→ Check: You have access to the specified organization/repository.',
`→ Verify: Organization settings at ${SOCKET_DASHBOARD_URL}`,
],
404: [
'→ Resource not found.',
'→ Verify: Package name, version, or resource ID is correct.',
'→ Check: Organization or repository exists and is accessible.',
],
413: [
'→ Payload too large. Request exceeds size limits.',
'→ Try: Reduce the number of files or packages in a single request.',
'→ Try: Use batch operations with smaller chunks.',
],
}
export function getSdkErrorBody(text: string): string | undefined {
let parsed: unknown
try {
parsed = JSON.parse(text)
} catch {
return text
}
if (!parsed || typeof parsed !== 'object') {
return undefined
}
const payload = parsed as Record<string, unknown>
if (typeof payload['message'] === 'string') {
return payload['message']
}
const nested = payload['error']
if (typeof nested === 'string') {
return nested
}
if (!nested || typeof nested !== 'object') {
return undefined
}
const details = nested as Record<string, unknown>
if (typeof details['message'] !== 'string') {
return undefined
}
if (!details['details']) {
return details['message']
}
const extra =
typeof details['details'] === 'string'
? details['details']
: JSON.stringify(details['details'])
return `${details['message']} - Details: ${extra}`
}
export function getSdkErrorGuidance(error: ResponseError): string | undefined {
if (error.response.status !== 429) {
return SDK_ERROR_GUIDANCE[error.response.status]?.join('\n')
}
const retryAfter = error.response.headers['retry-after']
return [
'→ Rate limit exceeded. Too many requests.',
retryAfter
? `→ Retry after ${retryAfter} seconds.`
: '→ Wait before retrying.',
'→ Try: Implement exponential backoff or enable SDK retry option.',
`→ Contact support to increase rate limits: ${SOCKET_CONTACT_URL}`,
].join('\n')
}
export async function handleSdkApiError(
error: unknown,
): Promise<SocketSdkErrorResult<never>> {
if (error instanceof SyntaxError) {
return { success: false, error: error.message, status: 200 }
}
if (!(error instanceof ResponseError)) {
throw new Error('Unexpected Socket API error', { cause: error })
}
const { status, statusText } = error.response
if (status >= 500) {
throw new Error(`Socket API server error (${status})`, { cause: error })
}
const body = getSdkErrorBody(error.response.text())?.trim()
let message = error.message
if (body && !message.includes(body)) {
message =
statusText && message.includes(statusText)
? message.replace(statusText, () => body)
: `${message}: ${body}`
}
const guidance = getSdkErrorGuidance(error)
const cause = guidance
? [body, '', guidance].filter(Boolean).join('\n')
: body
return {
cause: filterRedundantCause(message, cause),
error: message,
status,
success: false,
url: error.url,
}
}