@ban-sky/dsh-github 0.0.0-stage → 0.4.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.
package/lib/errors.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Shared error and redaction helpers for the GitHub plugin.
3
+ *
4
+ * Every failure that can reach the Client, a log line, or a Tool result flows
5
+ * through {@link GithubError} and {@link redact}. The token is never part of a
6
+ * message: git stderr and HTTP failures are scrubbed before they leave.
7
+ *
8
+ * @module dsh-github/lib/errors
9
+ */
10
+
11
+ /** Maximum length of one failure message handed to the Client. */
12
+ const MAX_MESSAGE_LENGTH = 4000
13
+
14
+ /**
15
+ * A failure with a machine-readable code.
16
+ */
17
+ export class GithubError extends Error {
18
+ /**
19
+ * @param {string} code - Stable machine-readable code (`invalid-repo-name`, ...).
20
+ * @param {string} message - Human-readable message; must not contain secrets.
21
+ * @param {Record<string, unknown>} [details] - Extra structured data.
22
+ * @param {{cause?: unknown}} [options] - Error options.
23
+ */
24
+ constructor(code, message, details = {}, options) {
25
+ super(message, options)
26
+ this.name = 'GithubError'
27
+ this.code = code
28
+ this.details = details
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Normalize any thrown value into a GithubError.
34
+ *
35
+ * @param {unknown} error - Thrown value.
36
+ * @param {string} [fallbackCode] - Code used when the value carries none.
37
+ * @returns {GithubError}
38
+ */
39
+ export function asGithubError(error, fallbackCode = 'internal') {
40
+ if (error instanceof GithubError) return error
41
+ const message = error instanceof Error ? error.message : String(error)
42
+ const code = typeof /** @type {{code?: unknown}} */ (error)?.code === 'string'
43
+ ? /** @type {{code: string}} */ (error).code
44
+ : fallbackCode
45
+ return new GithubError(code, message || 'unknown error')
46
+ }
47
+
48
+ /**
49
+ * Scrub secrets from text before it is returned or logged.
50
+ *
51
+ * Replaces: every known token value, any GitHub token literal, and any
52
+ * `scheme://userinfo@host` fragment.
53
+ *
54
+ * @param {string} value - Candidate text.
55
+ * @param {Iterable<string>} [secrets] - Secret values to remove verbatim.
56
+ * @returns {string}
57
+ */
58
+ export function redact(value, secrets = []) {
59
+ let text = typeof value === 'string' ? value : String(value)
60
+ for (const secret of secrets) {
61
+ if (typeof secret === 'string' && secret.length >= 8) text = text.split(secret).join('***')
62
+ }
63
+ text = text.replace(/\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g, '***')
64
+ text = text.replace(/(https?:\/\/)[^\s/@]+:[^\s/@]+@/g, '$1***@')
65
+ text = text.replace(/(https?:\/\/)[^\s/@]+@/g, '$1***@')
66
+ return text.length > MAX_MESSAGE_LENGTH ? `${text.slice(0, MAX_MESSAGE_LENGTH)}…` : text
67
+ }