@zhin.js/plugin-nettools 0.1.2 → 0.1.3

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/commands/dns/[name]/[[type]]/index.js +5 -5
  3. package/commands/dns/[name]/[[type]]/index.ts +5 -5
  4. package/commands/domain-price/[suffix]/[[order]]/index.js +5 -5
  5. package/commands/domain-price/[suffix]/[[order]]/index.ts +5 -5
  6. package/commands/ping/[host]/index.js +4 -4
  7. package/commands/ping/[host]/index.ts +4 -4
  8. package/commands/whois/[domain]/index.js +4 -4
  9. package/commands/whois/[domain]/index.ts +4 -4
  10. package/lib/address-policy.d.ts +2 -0
  11. package/lib/address-policy.js +55 -54
  12. package/lib/address-policy.js.map +1 -1
  13. package/lib/cooldown.js +2 -1
  14. package/lib/cooldown.js.map +1 -1
  15. package/lib/dns.d.ts +2 -1
  16. package/lib/dns.js +14 -12
  17. package/lib/dns.js.map +1 -1
  18. package/lib/domain-price.d.ts +2 -1
  19. package/lib/domain-price.js +16 -19
  20. package/lib/domain-price.js.map +1 -1
  21. package/lib/errors.d.ts +5 -2
  22. package/lib/errors.js +8 -4
  23. package/lib/errors.js.map +1 -1
  24. package/lib/http.js +12 -11
  25. package/lib/http.js.map +1 -1
  26. package/lib/messages.d.ts +329 -0
  27. package/lib/messages.js +335 -0
  28. package/lib/messages.js.map +1 -0
  29. package/lib/ping.d.ts +2 -1
  30. package/lib/ping.js +13 -11
  31. package/lib/ping.js.map +1 -1
  32. package/lib/presentation.d.ts +1 -0
  33. package/lib/presentation.js +36 -0
  34. package/lib/presentation.js.map +1 -0
  35. package/lib/service.d.ts +6 -5
  36. package/lib/service.js +16 -15
  37. package/lib/service.js.map +1 -1
  38. package/lib/target-guard.js +6 -4
  39. package/lib/target-guard.js.map +1 -1
  40. package/lib/whois.d.ts +2 -1
  41. package/lib/whois.js +18 -16
  42. package/lib/whois.js.map +1 -1
  43. package/package.json +7 -7
  44. package/skills/nettools/tools/dns_lookup/index.js +1 -1
  45. package/skills/nettools/tools/domain_price/index.js +1 -1
  46. package/skills/nettools/tools/net_ping/index.js +1 -1
  47. package/skills/nettools/tools/whois_lookup/index.js +1 -1
package/lib/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Resilient JSON-over-HTTP for third-party, keyless public APIs.\n *\n * Every external call in this plugin goes through {@link fetchJson}:\n * - per-attempt timeout (AbortSignal), combined with the caller's signal;\n * - retry of transient failures only: network errors, timeouts, 408, 425, 429\n * and 5xx gateway-ish statuses (500, 502, 503, 504);\n * - exponential backoff with \"equal jitter\" (half fixed, half random) so many\n * bots do not retry in lock-step;\n * - `Retry-After` is honoured (seconds or HTTP-date) up to a cap; a longer\n * server-requested wait fails fast with a clear rate-limit message instead of\n * blocking a chat reply for minutes;\n * - response bodies are size-capped and parsed defensively; malformed JSON is\n * reported as `malformed`, never thrown as a raw SyntaxError.\n *\n * All clocks, sleeps, randomness and fetch are injectable for tests.\n */\nimport { NettoolsError } from './errors.js';\n\nexport type FetchLike = (input: URL, init: RequestInit) => Promise<Response>;\n\nexport interface HttpDependencies {\n readonly fetch: FetchLike;\n readonly sleep: (ms: number, signal?: AbortSignal) => Promise<void>;\n readonly random: () => number;\n readonly now: () => number;\n}\n\nexport interface RetryPolicy {\n /** Per-attempt timeout in ms. */\n readonly timeoutMs: number;\n /** Extra attempts after the first one. */\n readonly maxRetries: number;\n /** First backoff step in ms; doubles on every retry. */\n readonly baseDelayMs: number;\n /** Upper bound for one computed backoff step. */\n readonly maxDelayMs: number;\n /** Longest `Retry-After` we are willing to wait inside one chat request. */\n readonly maxRetryAfterMs: number;\n}\n\nexport interface FetchJsonOptions {\n /** Human name of the provider, used in error messages. */\n readonly provider: string;\n readonly accept?: string;\n readonly signal?: AbortSignal;\n /** Statuses that are a valid answer rather than an error (e.g. RDAP 404). */\n readonly passStatuses?: readonly number[];\n readonly maxBodyBytes?: number;\n /** Override {@link RetryPolicy.maxRetries} for this call (e.g. when a fallback provider exists). */\n readonly maxRetries?: number;\n}\n\nexport interface JsonResponse {\n readonly status: number;\n readonly body: unknown;\n readonly url: string;\n}\n\nexport const DEFAULT_RETRY_POLICY: RetryPolicy = Object.freeze({\n timeoutMs: 8_000,\n maxRetries: 2,\n baseDelayMs: 500,\n maxDelayMs: 4_000,\n maxRetryAfterMs: 10_000,\n});\n\n/** 1 MiB is far above any legitimate answer from the APIs we call. */\nexport const DEFAULT_MAX_BODY_BYTES = 1024 * 1024;\n\nconst RETRYABLE_STATUSES = new Set([408, 425, 429, 500, 502, 503, 504]);\n\nexport function defaultHttpDependencies(): HttpDependencies {\n return {\n fetch: (input, init) => fetch(input, init),\n sleep: abortableSleep,\n random: Math.random,\n now: Date.now,\n };\n}\n\nexport class HttpClient {\n readonly #deps: HttpDependencies;\n readonly #policy: RetryPolicy;\n\n constructor(policy: RetryPolicy = DEFAULT_RETRY_POLICY, deps: HttpDependencies = defaultHttpDependencies()) {\n this.#policy = policy;\n this.#deps = deps;\n }\n\n get policy(): RetryPolicy {\n return this.#policy;\n }\n\n get dependencies(): HttpDependencies {\n return this.#deps;\n }\n\n /** GET a JSON document with timeout, retry, backoff and Retry-After handling. */\n async fetchJson(url: URL, options: FetchJsonOptions): Promise<JsonResponse> {\n const maxRetries = options.maxRetries ?? this.#policy.maxRetries;\n const attempts = Math.max(0, maxRetries) + 1;\n let lastError: NettoolsError | undefined;\n for (let attempt = 0; attempt < attempts; attempt += 1) {\n throwIfAborted(options.signal, options.provider);\n const outcome = await this.#attempt(url, options);\n if (outcome.kind === 'ok') {\n return outcome.response;\n }\n lastError = outcome.error;\n const isLastAttempt = attempt === attempts - 1;\n if (!outcome.retryable || isLastAttempt) {\n break;\n }\n const delay = this.#computeDelay(attempt, outcome.retryAfterMs);\n if (delay === undefined) {\n break;\n }\n await this.#deps.sleep(delay, options.signal);\n }\n throw lastError ?? new NettoolsError('network', `${options.provider} 请求失败`);\n }\n\n async #attempt(url: URL, options: FetchJsonOptions): Promise<AttemptOutcome> {\n const timeoutSignal = AbortSignal.timeout(this.#policy.timeoutMs);\n const signal = options.signal ? AbortSignal.any([options.signal, timeoutSignal]) : timeoutSignal;\n let response: Response;\n try {\n response = await this.#deps.fetch(url, {\n method: 'GET',\n headers: { accept: options.accept ?? 'application/json' },\n redirect: 'follow',\n signal,\n });\n } catch (error) {\n return this.#classifyTransportError(error, options, timeoutSignal);\n }\n\n const passStatuses = options.passStatuses ?? [];\n const statusIsAnswer = response.ok || passStatuses.includes(response.status);\n if (!statusIsAnswer) {\n await discardBody(response);\n return this.#classifyStatus(response, options.provider);\n }\n\n let text: string;\n try {\n text = await readLimitedText(response, options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES);\n } catch (error) {\n if (error instanceof NettoolsError) {\n return { kind: 'error', retryable: false, error };\n }\n return this.#classifyTransportError(error, options, timeoutSignal);\n }\n\n const body = parseJsonSafely(text);\n if (body === undefined) {\n const error = new NettoolsError('malformed', `${options.provider} 返回了无法解析的数据`);\n return { kind: 'error', retryable: false, error };\n }\n const finalUrl = response.url || url.toString();\n return { kind: 'ok', response: { status: response.status, body, url: finalUrl } };\n }\n\n #classifyTransportError(error: unknown, options: FetchJsonOptions, timeoutSignal: AbortSignal): AttemptOutcome {\n if (options.signal?.aborted) {\n return { kind: 'error', retryable: false, error: abortedError(options.provider) };\n }\n if (timeoutSignal.aborted) {\n const seconds = Math.round(this.#policy.timeoutMs / 1000);\n const timeout = new NettoolsError('timeout', `${options.provider} 响应超时(${seconds}s)`, { cause: error });\n return { kind: 'error', retryable: true, error: timeout };\n }\n const detail = error instanceof Error ? error.message : String(error);\n const network = new NettoolsError('network', `无法连接 ${options.provider}:${detail}`, { cause: error });\n return { kind: 'error', retryable: true, error: network };\n }\n\n #classifyStatus(response: Response, provider: string): AttemptOutcome {\n const status = response.status;\n const retryAfterMs = parseRetryAfter(response.headers.get('retry-after'), this.#deps.now());\n if (status === 429) {\n const error = new NettoolsError('rate_limited', `${provider} 请求过于频繁,请稍后再试`, {\n status,\n ...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n });\n return { kind: 'error', retryable: true, error, retryAfterMs };\n }\n const error = new NettoolsError('http', `${provider} 返回错误 HTTP ${status}`, { status });\n const retryable = RETRYABLE_STATUSES.has(status);\n return { kind: 'error', retryable, error, retryAfterMs };\n }\n\n /**\n * Backoff for retry number `attempt + 1`. Returns `undefined` when the\n * server asked us to wait longer than {@link RetryPolicy.maxRetryAfterMs}.\n */\n #computeDelay(attempt: number, retryAfterMs: number | undefined): number | undefined {\n if (retryAfterMs !== undefined) {\n if (retryAfterMs > this.#policy.maxRetryAfterMs) {\n return undefined;\n }\n return retryAfterMs;\n }\n const exponential = this.#policy.baseDelayMs * 2 ** attempt;\n const capped = Math.min(this.#policy.maxDelayMs, exponential);\n const half = capped / 2;\n return Math.round(half + this.#deps.random() * half);\n }\n}\n\ntype AttemptOutcome =\n | { readonly kind: 'ok'; readonly response: JsonResponse }\n | {\n readonly kind: 'error';\n readonly retryable: boolean;\n readonly error: NettoolsError;\n readonly retryAfterMs?: number | undefined;\n };\n\n/** Parse `Retry-After` as delta-seconds or an HTTP-date. Invalid values are ignored. */\nexport function parseRetryAfter(value: string | null, nowMs: number): number | undefined {\n if (value === null) {\n return undefined;\n }\n const trimmed = value.trim();\n if (/^\\d+$/u.test(trimmed)) {\n return Number(trimmed) * 1000;\n }\n const dateMs = Date.parse(trimmed);\n if (Number.isNaN(dateMs)) {\n return undefined;\n }\n return Math.max(0, dateMs - nowMs);\n}\n\nfunction parseJsonSafely(text: string): unknown {\n try {\n const parsed: unknown = JSON.parse(text);\n return parsed === undefined ? undefined : parsed;\n } catch {\n return undefined;\n }\n}\n\nasync function readLimitedText(response: Response, maxBytes: number): Promise<string> {\n const declared = Number(response.headers.get('content-length') ?? '');\n if (Number.isFinite(declared) && declared > maxBytes) {\n await discardBody(response);\n throw new NettoolsError('malformed', '响应体过大,已拒绝读取');\n }\n if (!response.body) {\n return await response.text();\n }\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let received = 0;\n let text = '';\n for (;;) {\n const chunk = await reader.read();\n if (chunk.done) {\n break;\n }\n received += chunk.value.byteLength;\n if (received > maxBytes) {\n await reader.cancel();\n throw new NettoolsError('malformed', '响应体过大,已拒绝读取');\n }\n text += decoder.decode(chunk.value, { stream: true });\n }\n text += decoder.decode();\n return text;\n}\n\nasync function discardBody(response: Response): Promise<void> {\n try {\n await response.body?.cancel();\n } catch {\n // Ignore: the body is irrelevant once we decided the status is an error.\n }\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined, provider: string): void {\n if (signal?.aborted) {\n throw abortedError(provider);\n }\n}\n\nfunction abortedError(provider: string): NettoolsError {\n return new NettoolsError('aborted', `${provider} 请求已取消`);\n}\n\nexport function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new NettoolsError('aborted', '请求已取消'));\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(new NettoolsError('aborted', '请求已取消'));\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n"]}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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","sourcesContent":["import { message } from './messages.js';\n/**\n * Resilient JSON-over-HTTP for third-party, keyless public APIs.\n *\n * Every external call in this plugin goes through {@link fetchJson}:\n * - per-attempt timeout (AbortSignal), combined with the caller's signal;\n * - retry of transient failures only: network errors, timeouts, 408, 425, 429\n * and 5xx gateway-ish statuses (500, 502, 503, 504);\n * - exponential backoff with \"equal jitter\" (half fixed, half random) so many\n * bots do not retry in lock-step;\n * - `Retry-After` is honoured (seconds or HTTP-date) up to a cap; a longer\n * server-requested wait fails fast with a clear rate-limit message instead of\n * blocking a chat reply for minutes;\n * - response bodies are size-capped and parsed defensively; malformed JSON is\n * reported as `malformed`, never thrown as a raw SyntaxError.\n *\n * All clocks, sleeps, randomness and fetch are injectable for tests.\n */\nimport { NettoolsError } from './errors.js';\n\nexport type FetchLike = (input: URL, init: RequestInit) => Promise<Response>;\n\nexport interface HttpDependencies {\n readonly fetch: FetchLike;\n readonly sleep: (ms: number, signal?: AbortSignal) => Promise<void>;\n readonly random: () => number;\n readonly now: () => number;\n}\n\nexport interface RetryPolicy {\n /** Per-attempt timeout in ms. */\n readonly timeoutMs: number;\n /** Extra attempts after the first one. */\n readonly maxRetries: number;\n /** First backoff step in ms; doubles on every retry. */\n readonly baseDelayMs: number;\n /** Upper bound for one computed backoff step. */\n readonly maxDelayMs: number;\n /** Longest `Retry-After` we are willing to wait inside one chat request. */\n readonly maxRetryAfterMs: number;\n}\n\nexport interface FetchJsonOptions {\n /** Human name of the provider, used in error messages. */\n readonly provider: string;\n readonly accept?: string;\n readonly signal?: AbortSignal;\n /** Statuses that are a valid answer rather than an error (e.g. RDAP 404). */\n readonly passStatuses?: readonly number[];\n readonly maxBodyBytes?: number;\n /** Override {@link RetryPolicy.maxRetries} for this call (e.g. when a fallback provider exists). */\n readonly maxRetries?: number;\n}\n\nexport interface JsonResponse {\n readonly status: number;\n readonly body: unknown;\n readonly url: string;\n}\n\nexport const DEFAULT_RETRY_POLICY: RetryPolicy = Object.freeze({\n timeoutMs: 8_000,\n maxRetries: 2,\n baseDelayMs: 500,\n maxDelayMs: 4_000,\n maxRetryAfterMs: 10_000,\n});\n\n/** 1 MiB is far above any legitimate answer from the APIs we call. */\nexport const DEFAULT_MAX_BODY_BYTES = 1024 * 1024;\n\nconst RETRYABLE_STATUSES = new Set([408, 425, 429, 500, 502, 503, 504]);\n\nexport function defaultHttpDependencies(): HttpDependencies {\n return {\n fetch: (input, init) => fetch(input, init),\n sleep: abortableSleep,\n random: Math.random,\n now: Date.now,\n };\n}\n\nexport class HttpClient {\n readonly #deps: HttpDependencies;\n readonly #policy: RetryPolicy;\n\n constructor(policy: RetryPolicy = DEFAULT_RETRY_POLICY, deps: HttpDependencies = defaultHttpDependencies()) {\n this.#policy = policy;\n this.#deps = deps;\n }\n\n get policy(): RetryPolicy {\n return this.#policy;\n }\n\n get dependencies(): HttpDependencies {\n return this.#deps;\n }\n\n /** GET a JSON document with timeout, retry, backoff and Retry-After handling. */\n async fetchJson(url: URL, options: FetchJsonOptions): Promise<JsonResponse> {\n const maxRetries = options.maxRetries ?? this.#policy.maxRetries;\n const attempts = Math.max(0, maxRetries) + 1;\n let lastError: NettoolsError | undefined;\n for (let attempt = 0; attempt < attempts; attempt += 1) {\n throwIfAborted(options.signal, options.provider);\n const outcome = await this.#attempt(url, options);\n if (outcome.kind === 'ok') {\n return outcome.response;\n }\n lastError = outcome.error;\n const isLastAttempt = attempt === attempts - 1;\n if (!outcome.retryable || isLastAttempt) {\n break;\n }\n const delay = this.#computeDelay(attempt, outcome.retryAfterMs);\n if (delay === undefined) {\n break;\n }\n await this.#deps.sleep(delay, options.signal);\n }\n throw lastError ?? new NettoolsError('network', message('requestFailed', { provider: options.provider }));\n }\n\n async #attempt(url: URL, options: FetchJsonOptions): Promise<AttemptOutcome> {\n const timeoutSignal = AbortSignal.timeout(this.#policy.timeoutMs);\n const signal = options.signal ? AbortSignal.any([options.signal, timeoutSignal]) : timeoutSignal;\n let response: Response;\n try {\n response = await this.#deps.fetch(url, {\n method: 'GET',\n headers: { accept: options.accept ?? 'application/json' },\n redirect: 'follow',\n signal,\n });\n } catch (error) {\n return this.#classifyTransportError(error, options, timeoutSignal);\n }\n\n const passStatuses = options.passStatuses ?? [];\n const statusIsAnswer = response.ok || passStatuses.includes(response.status);\n if (!statusIsAnswer) {\n await discardBody(response);\n return this.#classifyStatus(response, options.provider);\n }\n\n let text: string;\n try {\n text = await readLimitedText(response, options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES);\n } catch (error) {\n if (error instanceof NettoolsError) {\n return { kind: 'error', retryable: false, error };\n }\n return this.#classifyTransportError(error, options, timeoutSignal);\n }\n\n const body = parseJsonSafely(text);\n if (body === undefined) {\n const error = new NettoolsError('malformed', message('responseMalformed', { provider: options.provider }));\n return { kind: 'error', retryable: false, error };\n }\n const finalUrl = response.url || url.toString();\n return { kind: 'ok', response: { status: response.status, body, url: finalUrl } };\n }\n\n #classifyTransportError(error: unknown, options: FetchJsonOptions, timeoutSignal: AbortSignal): AttemptOutcome {\n if (options.signal?.aborted) {\n return { kind: 'error', retryable: false, error: abortedError(options.provider) };\n }\n if (timeoutSignal.aborted) {\n const seconds = Math.round(this.#policy.timeoutMs / 1000);\n const timeout = new NettoolsError('timeout', message('timeout', { provider: options.provider, seconds }), { cause: error });\n return { kind: 'error', retryable: true, error: timeout };\n }\n const detail = error instanceof Error ? error.message : String(error);\n const network = new NettoolsError('network', message('network', { provider: options.provider, detail }), { cause: error });\n return { kind: 'error', retryable: true, error: network };\n }\n\n #classifyStatus(response: Response, provider: string): AttemptOutcome {\n const status = response.status;\n const retryAfterMs = parseRetryAfter(response.headers.get('retry-after'), this.#deps.now());\n if (status === 429) {\n const error = new NettoolsError('rate_limited', message('rateLimited', { provider }), {\n status,\n ...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n });\n return { kind: 'error', retryable: true, error, retryAfterMs };\n }\n const error = new NettoolsError('http', message('http', { provider, status }), { status });\n const retryable = RETRYABLE_STATUSES.has(status);\n return { kind: 'error', retryable, error, retryAfterMs };\n }\n\n /**\n * Backoff for retry number `attempt + 1`. Returns `undefined` when the\n * server asked us to wait longer than {@link RetryPolicy.maxRetryAfterMs}.\n */\n #computeDelay(attempt: number, retryAfterMs: number | undefined): number | undefined {\n if (retryAfterMs !== undefined) {\n if (retryAfterMs > this.#policy.maxRetryAfterMs) {\n return undefined;\n }\n return retryAfterMs;\n }\n const exponential = this.#policy.baseDelayMs * 2 ** attempt;\n const capped = Math.min(this.#policy.maxDelayMs, exponential);\n const half = capped / 2;\n return Math.round(half + this.#deps.random() * half);\n }\n}\n\ntype AttemptOutcome =\n | { readonly kind: 'ok'; readonly response: JsonResponse }\n | {\n readonly kind: 'error';\n readonly retryable: boolean;\n readonly error: NettoolsError;\n readonly retryAfterMs?: number | undefined;\n };\n\n/** Parse `Retry-After` as delta-seconds or an HTTP-date. Invalid values are ignored. */\nexport function parseRetryAfter(value: string | null, nowMs: number): number | undefined {\n if (value === null) {\n return undefined;\n }\n const trimmed = value.trim();\n if (/^\\d+$/u.test(trimmed)) {\n return Number(trimmed) * 1000;\n }\n const dateMs = Date.parse(trimmed);\n if (Number.isNaN(dateMs)) {\n return undefined;\n }\n return Math.max(0, dateMs - nowMs);\n}\n\nfunction parseJsonSafely(text: string): unknown {\n try {\n const parsed: unknown = JSON.parse(text);\n return parsed === undefined ? undefined : parsed;\n } catch {\n return undefined;\n }\n}\n\nasync function readLimitedText(response: Response, maxBytes: number): Promise<string> {\n const declared = Number(response.headers.get('content-length') ?? '');\n if (Number.isFinite(declared) && declared > maxBytes) {\n await discardBody(response);\n throw new NettoolsError('malformed', message('tooLarge'));\n }\n if (!response.body) {\n return await response.text();\n }\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let received = 0;\n let text = '';\n for (;;) {\n const chunk = await reader.read();\n if (chunk.done) {\n break;\n }\n received += chunk.value.byteLength;\n if (received > maxBytes) {\n await reader.cancel();\n throw new NettoolsError('malformed', message('tooLarge'));\n }\n text += decoder.decode(chunk.value, { stream: true });\n }\n text += decoder.decode();\n return text;\n}\n\nasync function discardBody(response: Response): Promise<void> {\n try {\n await response.body?.cancel();\n } catch {\n // Ignore: the body is irrelevant once we decided the status is an error.\n }\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined, provider: string): void {\n if (signal?.aborted) {\n throw abortedError(provider);\n }\n}\n\nfunction abortedError(provider: string): NettoolsError {\n return new NettoolsError('aborted', message('providerAborted', { provider }));\n}\n\nexport function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new NettoolsError('aborted', message('aborted')));\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(new NettoolsError('aborted', message('aborted')));\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n"]}
@@ -0,0 +1,329 @@
1
+ import { type Locale } from 'zhin.js/i18n';
2
+ declare const catalog: {
3
+ readonly cloudMetadata: {
4
+ readonly "zh-CN": "云服务元数据地址";
5
+ readonly en: "Cloud metadata address";
6
+ };
7
+ readonly unspecifiedV4: {
8
+ readonly "zh-CN": "未指定 / “本网络”地址";
9
+ readonly en: "Unspecified / current-network address";
10
+ };
11
+ readonly privateV4: {
12
+ readonly "zh-CN": "私有地址 (RFC 1918)";
13
+ readonly en: "Private address (RFC 1918)";
14
+ };
15
+ readonly cgnat: {
16
+ readonly "zh-CN": "运营商级 NAT 地址 (CGNAT, RFC 6598)";
17
+ readonly en: "Carrier-grade NAT address (CGNAT, RFC 6598)";
18
+ };
19
+ readonly loopback: {
20
+ readonly "zh-CN": "回环地址";
21
+ readonly en: "Loopback address";
22
+ };
23
+ readonly linkLocal: {
24
+ readonly "zh-CN": "链路本地地址";
25
+ readonly en: "Link-local address";
26
+ };
27
+ readonly ietfReserved: {
28
+ readonly "zh-CN": "IETF 协议保留地址";
29
+ readonly en: "IETF protocol reserved address";
30
+ };
31
+ readonly documentation: {
32
+ readonly "zh-CN": "文档示例地址";
33
+ readonly en: "Documentation address";
34
+ };
35
+ readonly relay6to4: {
36
+ readonly "zh-CN": "已废弃的 6to4 中继地址";
37
+ readonly en: "Deprecated 6to4 relay address";
38
+ };
39
+ readonly benchmark: {
40
+ readonly "zh-CN": "基准测试保留地址";
41
+ readonly en: "Benchmarking address";
42
+ };
43
+ readonly multicast: {
44
+ readonly "zh-CN": "组播地址";
45
+ readonly en: "Multicast address";
46
+ };
47
+ readonly reservedBroadcast: {
48
+ readonly "zh-CN": "保留地址(含广播地址)";
49
+ readonly en: "Reserved address (including broadcast)";
50
+ };
51
+ readonly teredo: {
52
+ readonly "zh-CN": "Teredo 隧道地址";
53
+ readonly en: "Teredo tunnel address";
54
+ };
55
+ readonly orchid: {
56
+ readonly "zh-CN": "ORCHID 保留地址";
57
+ readonly en: "ORCHID reserved address";
58
+ };
59
+ readonly hostRequired: {
60
+ readonly "zh-CN": "请提供主机名或 IP 地址";
61
+ readonly en: "Provide a hostname or IP address";
62
+ };
63
+ readonly hostTooLong: {
64
+ readonly "zh-CN": "主机名过长";
65
+ readonly en: "Hostname is too long";
66
+ };
67
+ readonly bareHost: {
68
+ readonly "zh-CN": "只接受裸主机名或 IP,不要带协议、端口、路径或特殊字符";
69
+ readonly en: "Use a bare hostname or IP, without a scheme, port, path or special characters";
70
+ };
71
+ readonly ipv6NoPort: {
72
+ readonly "zh-CN": "不是有效的 IPv6 地址(如需端口请去掉端口)";
73
+ readonly en: "Invalid IPv6 address; omit any port";
74
+ };
75
+ readonly invalidHost: {
76
+ readonly "zh-CN": "不是有效的主机名或 IP 地址";
77
+ readonly en: "Invalid hostname or IP address";
78
+ };
79
+ readonly hostLength: {
80
+ readonly "zh-CN": "主机名长度不合法";
81
+ readonly en: "Invalid hostname length";
82
+ };
83
+ readonly fullDomain: {
84
+ readonly "zh-CN": "请提供完整域名(例如 example.com)";
85
+ readonly en: "Provide a full domain name, such as example.com";
86
+ };
87
+ readonly invalidLabel: {
88
+ readonly "zh-CN": "域名标签不合法:{label}";
89
+ readonly en: "Invalid domain label: {label}";
90
+ };
91
+ readonly empty: {
92
+ readonly "zh-CN": "(空)";
93
+ readonly en: "(empty)";
94
+ };
95
+ readonly numericTld: {
96
+ readonly "zh-CN": "顶级域不能是纯数字";
97
+ readonly en: "The top-level domain cannot be entirely numeric";
98
+ };
99
+ readonly notIp: {
100
+ readonly "zh-CN": "不是 IP 地址:{address}";
101
+ readonly en: "Not an IP address: {address}";
102
+ };
103
+ readonly localhost: {
104
+ readonly "zh-CN": "本机主机名";
105
+ readonly en: "Local hostname";
106
+ };
107
+ readonly reservedSuffix: {
108
+ readonly "zh-CN": "内网或保留域名后缀 .{suffix}";
109
+ readonly en: "Private or reserved domain suffix .{suffix}";
110
+ };
111
+ readonly unspecifiedV6: {
112
+ readonly "zh-CN": "未指定地址 (::)";
113
+ readonly en: "Unspecified address (::)";
114
+ };
115
+ readonly loopbackV6: {
116
+ readonly "zh-CN": "回环地址 (::1)";
117
+ readonly en: "Loopback address (::1)";
118
+ };
119
+ readonly mappedV4: {
120
+ readonly "zh-CN": "IPv4 映射地址";
121
+ readonly en: "IPv4-mapped address";
122
+ };
123
+ readonly nat64: {
124
+ readonly "zh-CN": "NAT64 地址";
125
+ readonly en: "NAT64 address";
126
+ };
127
+ readonly localNat64: {
128
+ readonly "zh-CN": "本地 NAT64 地址";
129
+ readonly en: "Local NAT64 address";
130
+ };
131
+ readonly sixToFour: {
132
+ readonly "zh-CN": "6to4 地址";
133
+ readonly en: "6to4 address";
134
+ };
135
+ readonly ula: {
136
+ readonly "zh-CN": "唯一本地地址 (ULA, fc00::/7)";
137
+ readonly en: "Unique-local address (ULA, fc00::/7)";
138
+ };
139
+ readonly linkLocalV6: {
140
+ readonly "zh-CN": "链路本地地址 (fe80::/10)";
141
+ readonly en: "Link-local address (fe80::/10)";
142
+ };
143
+ readonly siteLocalV6: {
144
+ readonly "zh-CN": "已废弃的站点本地地址 (fec0::/10)";
145
+ readonly en: "Deprecated site-local address (fec0::/10)";
146
+ };
147
+ readonly multicastV6: {
148
+ readonly "zh-CN": "组播地址 (ff00::/8)";
149
+ readonly en: "Multicast address (ff00::/8)";
150
+ };
151
+ readonly notGlobalV6: {
152
+ readonly "zh-CN": "非全球单播 IPv6 地址";
153
+ readonly en: "Non-global-unicast IPv6 address";
154
+ };
155
+ readonly embeddedV4: {
156
+ readonly "zh-CN": "{wrapper}指向 {address}({reason})";
157
+ readonly en: "{wrapper} points to {address} ({reason})";
158
+ };
159
+ readonly invalidV4: {
160
+ readonly "zh-CN": "不是有效的 IPv4 地址:{address}";
161
+ readonly en: "Invalid IPv4 address: {address}";
162
+ };
163
+ readonly invalidV6: {
164
+ readonly "zh-CN": "不是有效的 IPv6 地址:{address}";
165
+ readonly en: "Invalid IPv6 address: {address}";
166
+ };
167
+ readonly invalidV6Group: {
168
+ readonly "zh-CN": "IPv6 分组不合法:{group}";
169
+ readonly en: "Invalid IPv6 group: {group}";
170
+ };
171
+ readonly reverseNeedsIp: {
172
+ readonly "zh-CN": "反向解析需要 IP 地址";
173
+ readonly en: "Reverse lookup requires an IP address";
174
+ };
175
+ readonly probeBlocked: {
176
+ readonly "zh-CN": "拒绝探测 {host}:{reason}";
177
+ readonly en: "Refused to probe {host}: {reason}";
178
+ };
179
+ readonly queryBlocked: {
180
+ readonly "zh-CN": "拒绝查询 {host}:{reason}";
181
+ readonly en: "Refused to query {host}: {reason}";
182
+ };
183
+ readonly whoisDomainOnly: {
184
+ readonly "zh-CN": "whois 目前只支持域名查询";
185
+ readonly en: "WHOIS supports domain names only";
186
+ };
187
+ readonly reverseOnly: {
188
+ readonly "zh-CN": "IP 地址只能做反向解析,请使用 PTR 记录类型";
189
+ readonly en: "IP addresses only support reverse lookup; select the PTR record type";
190
+ };
191
+ readonly noAddresses: {
192
+ readonly "zh-CN": "无法解析 {host}(没有 A/AAAA 记录)";
193
+ readonly en: "Cannot resolve {host} (no A/AAAA records)";
194
+ };
195
+ readonly resolvedBlocked: {
196
+ readonly "zh-CN": "拒绝探测 {host}:解析到 {address}({reason})";
197
+ readonly en: "Refused to probe {host}: resolves to {address} ({reason})";
198
+ };
199
+ readonly unknownAddress: {
200
+ readonly "zh-CN": "无法识别的地址记录";
201
+ readonly en: "Unrecognized address record";
202
+ };
203
+ readonly cooldown: {
204
+ readonly "zh-CN": "操作太频繁,请 {seconds} 秒后再试";
205
+ readonly en: "Too many requests. Try again in {seconds} seconds";
206
+ };
207
+ readonly dnsType: {
208
+ readonly "zh-CN": "不支持的记录类型 {type},可选:{types}";
209
+ readonly en: "Unsupported record type {type}; choose {types}";
210
+ };
211
+ readonly queryFailed: {
212
+ readonly "zh-CN": "{provider} 查询失败";
213
+ readonly en: "{provider} lookup failed";
214
+ };
215
+ readonly dnsUnavailable: {
216
+ readonly "zh-CN": "所有 DNS 服务均不可用:{failures}";
217
+ readonly en: "All DNS services are unavailable: {failures}";
218
+ };
219
+ readonly dnsMalformed: {
220
+ readonly "zh-CN": "{provider} 返回了无法识别的 DNS 响应";
221
+ readonly en: "{provider} returned an unrecognized DNS response";
222
+ };
223
+ readonly dnsFailed: {
224
+ readonly "zh-CN": "{provider} 解析失败({rcode})";
225
+ readonly en: "{provider} lookup failed ({rcode})";
226
+ };
227
+ readonly requestFailed: {
228
+ readonly "zh-CN": "{provider} 请求失败";
229
+ readonly en: "{provider} request failed";
230
+ };
231
+ readonly responseMalformed: {
232
+ readonly "zh-CN": "{provider} 返回了无法解析的数据";
233
+ readonly en: "{provider} returned data that could not be parsed";
234
+ };
235
+ readonly timeout: {
236
+ readonly "zh-CN": "{provider} 响应超时({seconds}s)";
237
+ readonly en: "{provider} timed out ({seconds}s)";
238
+ };
239
+ readonly network: {
240
+ readonly "zh-CN": "无法连接 {provider}:{detail}";
241
+ readonly en: "Cannot connect to {provider}: {detail}";
242
+ };
243
+ readonly rateLimited: {
244
+ readonly "zh-CN": "{provider} 请求过于频繁,请稍后再试";
245
+ readonly en: "{provider} rate limit reached; try again later";
246
+ };
247
+ readonly http: {
248
+ readonly "zh-CN": "{provider} 返回错误 HTTP {status}";
249
+ readonly en: "{provider} returned HTTP {status}";
250
+ };
251
+ readonly tooLarge: {
252
+ readonly "zh-CN": "响应体过大,已拒绝读取";
253
+ readonly en: "Response body is too large; reading was refused";
254
+ };
255
+ readonly providerAborted: {
256
+ readonly "zh-CN": "{provider} 请求已取消";
257
+ readonly en: "{provider} request was cancelled";
258
+ };
259
+ readonly aborted: {
260
+ readonly "zh-CN": "请求已取消";
261
+ readonly en: "Request was cancelled";
262
+ };
263
+ readonly pingRefused: {
264
+ readonly "zh-CN": "check-host.net 拒绝了请求:{detail}";
265
+ readonly en: "check-host.net refused the request: {detail}";
266
+ };
267
+ readonly pingMalformed: {
268
+ readonly "zh-CN": "check-host.net 返回了无法识别的数据";
269
+ readonly en: "check-host.net returned unrecognized data";
270
+ };
271
+ readonly priceKind: {
272
+ readonly "zh-CN": "价格类型可选:注册(register) / 续费(renew) / 转入(transfer)";
273
+ readonly en: "Price type must be register, renew, or transfer";
274
+ };
275
+ readonly suffixRequired: {
276
+ readonly "zh-CN": "请提供域名后缀,例如 com 或 .cn";
277
+ readonly en: "Provide a domain suffix, such as com or .cn";
278
+ };
279
+ readonly suffixNotIp: {
280
+ readonly "zh-CN": "请提供域名或后缀,而不是 IP 地址";
281
+ readonly en: "Provide a domain name or suffix, rather than an IP address";
282
+ };
283
+ readonly priceMalformed: {
284
+ readonly "zh-CN": "nazhumi.com 返回了无法识别的数据";
285
+ readonly en: "nazhumi.com returned unrecognized data";
286
+ };
287
+ readonly priceError: {
288
+ readonly "zh-CN": "nazhumi.com 返回错误 {code}{detail}";
289
+ readonly en: "nazhumi.com returned error {code}{detail}";
290
+ };
291
+ readonly rdapFailed: {
292
+ readonly "zh-CN": "RDAP / WHOIS 查询失败";
293
+ readonly en: "RDAP / WHOIS lookup failed";
294
+ };
295
+ readonly ianaMalformed: {
296
+ readonly "zh-CN": "IANA RDAP 引导数据格式异常";
297
+ readonly en: "Invalid IANA RDAP bootstrap data";
298
+ };
299
+ readonly rdapMalformed: {
300
+ readonly "zh-CN": "RDAP 返回了无法识别的数据";
301
+ readonly en: "RDAP returned unrecognized data";
302
+ };
303
+ readonly rdapNotDomain: {
304
+ readonly "zh-CN": "RDAP 返回的不是域名对象";
305
+ readonly en: "RDAP did not return a domain object";
306
+ };
307
+ readonly whoisMalformed: {
308
+ readonly "zh-CN": "WHOIS 服务返回了无法识别的数据";
309
+ readonly en: "WHOIS service returned unrecognized data";
310
+ };
311
+ readonly whoisError: {
312
+ readonly "zh-CN": "WHOIS 服务返回错误 {code}";
313
+ readonly en: "WHOIS service returned error {code}";
314
+ };
315
+ readonly unexpected: {
316
+ readonly "zh-CN": "出错了:{detail}";
317
+ readonly en: "An error occurred: {detail}";
318
+ };
319
+ };
320
+ export type NettoolsMessageKey = keyof typeof catalog;
321
+ export type NettoolsMessageValue = string | number | NettoolsMessage | readonly (string | NettoolsMessage)[];
322
+ export interface NettoolsMessage {
323
+ readonly key: NettoolsMessageKey;
324
+ readonly values: Readonly<Record<string, NettoolsMessageValue>>;
325
+ }
326
+ /** Stable source keys preserve nested provider/target reasons until the caller chooses a language. */
327
+ export declare function message(key: NettoolsMessageKey, values?: Readonly<Record<string, NettoolsMessageValue>>): NettoolsMessage;
328
+ export declare function formatMessage(detail: NettoolsMessage, locale?: Locale): string;
329
+ export {};