DC-140: network provider abstraction (src/network/) + tailscale provider + registry + tests

This commit is contained in:
Hermes committed 2026-10-03 21:16:20 -07:00
1 parent 420fa2ba2a
commit 7ef3914730
7 files changed
+1308

No files matched your search

+59
View File
@@ -0,0 +1,59 @@
/**
* Provider error types for the DashCaddy Private Network abstraction
* (DC-140, specs/PRIVATE-NETWORK-SPEC.md §3).
*
* Every provider-facing failure surfaces as a ProviderError carrying a
* machine-readable `code` and a `retryable` hint, so feature modules can
* degrade cleanly without knowing which provider is active.
*
* Canonical codes (providers may add, but these are the shared vocabulary):
* - not_configured provider has no credentials yet
* - unauthorized token rejected (401/403)
* - not_found entity does not exist upstream
* - rate_limited upstream 429 (retryable, honor retryAfter)
* - server_error upstream 5xx (retryable)
* - network_error transport failure (retryable)
* - bad_input caller passed invalid arguments
* - not_implemented_in_provider capability exists but lands in a later DC
* - provider_not_active registry has no active provider
*/
'use strict';
/** Codes for which a retry (possibly after a backoff) is reasonable. */
const RETRYABLE_CODES = new Set(['rate_limited', 'server_error', 'network_error']);
class ProviderError extends Error {
/**
* @param {string} message - human-readable description
* @param {object} [opts]
* @param {string} [opts.code='unknown'] - machine-readable error code
* @param {boolean} [opts.retryable] - defaults to derived-from-code
* @param {*} [opts.cause] - original error (e.g. TailscaleCoordError)
* @param {*} [opts.retryAfter] - upstream retry-after hint, seconds
*/
constructor(message, { code, retryable, cause, retryAfter } = {}) {
super(message);
this.name = 'ProviderError';
this.code = code || 'unknown';
this.retryable = retryable !== undefined ? !!retryable : RETRYABLE_CODES.has(this.code);
if (retryAfter !== undefined) this.retryAfter = retryAfter;
if (cause !== undefined) this.cause = cause;
}
}
/**
* Thrown by the registry when no provider has been activated yet
* (default state until the setup wizard connects one — DC-152).
*/
class ProviderNotActiveError extends ProviderError {
constructor(message) {
super(message || 'No active network provider configured', {
code: 'provider_not_active',
retryable: false,
});
this.name = 'ProviderNotActiveError';
}
}
module.exports = { ProviderError, ProviderNotActiveError };