Self-hosted multi-platform broadcast newsroom for activist collectives: publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style column wall with hostile-content-safe rendering, live control room with Auto Director, bridged chat governance with polls and quorum, distributed sandboxed transcode workers, encrypted content-addressed archive, monthly analytics. MIT licensed. See README.md and docs/.
115 lines
3.4 KiB
TypeScript
115 lines
3.4 KiB
TypeScript
/**
|
|
* What a failed provider call proves about the side effect:
|
|
* - "rejected": the provider answered and definitively did not perform it.
|
|
* - "unknown": the request may have been applied (5xx/transport loss on a
|
|
* non-idempotent request) — never resend without a provider dedupe key.
|
|
*/
|
|
export type DeliveryCertainty = "rejected" | "unknown";
|
|
|
|
export class ConnectorError extends Error {
|
|
constructor(
|
|
message: string,
|
|
readonly provider: string,
|
|
override readonly cause?: unknown,
|
|
) {
|
|
super(message);
|
|
this.name = "ConnectorError";
|
|
}
|
|
}
|
|
|
|
export class UnknownProviderError extends ConnectorError {
|
|
constructor(providerId: string) {
|
|
super(`no connector registered for provider "${providerId}"`, providerId);
|
|
this.name = "UnknownProviderError";
|
|
}
|
|
}
|
|
|
|
export class AuthError extends ConnectorError {
|
|
constructor(message: string, provider: string, readonly retryable: boolean) {
|
|
super(message, provider);
|
|
this.name = "AuthError";
|
|
}
|
|
}
|
|
|
|
export class RateLimitError extends ConnectorError {
|
|
/** A 429 is a refusal before processing, so nothing was delivered. */
|
|
readonly delivery: DeliveryCertainty = "rejected";
|
|
|
|
constructor(
|
|
provider: string,
|
|
readonly retryAfterMs?: number,
|
|
readonly status = 429,
|
|
) {
|
|
super(`rate limited by ${provider}`, provider);
|
|
this.name = "RateLimitError";
|
|
}
|
|
}
|
|
|
|
/** The provider answered with a non-2xx status that is a definite refusal (4xx). */
|
|
export class ProviderRejectedError extends ConnectorError {
|
|
readonly delivery: DeliveryCertainty = "rejected";
|
|
|
|
constructor(
|
|
message: string,
|
|
provider: string,
|
|
readonly status: number,
|
|
readonly retryAfterMs?: number,
|
|
) {
|
|
// cause keeps the `{ status }` shape connectors already inspect.
|
|
super(message, provider, { status });
|
|
this.name = "ProviderRejectedError";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 5xx answer the SDK client did not retry (or stopped retrying). For
|
|
* non-idempotent requests delivery is "unknown": the server may have applied
|
|
* the write before failing.
|
|
*/
|
|
export class ProviderServerError extends ConnectorError {
|
|
constructor(
|
|
provider: string,
|
|
readonly status: number,
|
|
readonly method: string,
|
|
readonly delivery: DeliveryCertainty,
|
|
readonly retryAfterMs?: number,
|
|
) {
|
|
super(`${provider}: HTTP ${status} on ${method}`, provider, { status });
|
|
this.name = "ProviderServerError";
|
|
}
|
|
}
|
|
|
|
/** fetch itself failed (socket loss, DNS, abort) on a non-idempotent request. */
|
|
export class ProviderTransportError extends ConnectorError {
|
|
readonly delivery: DeliveryCertainty = "unknown";
|
|
|
|
constructor(
|
|
provider: string,
|
|
readonly method: string,
|
|
cause: unknown,
|
|
) {
|
|
super(`${provider}: transport failure on ${method}`, provider, cause);
|
|
this.name = "ProviderTransportError";
|
|
}
|
|
}
|
|
|
|
/** Delivery certainty carried by an SDK error, if the error type records one. */
|
|
export function deliveryCertaintyOf(error: unknown): DeliveryCertainty | undefined {
|
|
if (error instanceof AuthError) return "rejected";
|
|
if (
|
|
error instanceof RateLimitError ||
|
|
error instanceof ProviderRejectedError ||
|
|
error instanceof ProviderServerError ||
|
|
error instanceof ProviderTransportError
|
|
) {
|
|
return error.delivery;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export class CapabilityNotSupportedError extends ConnectorError {
|
|
constructor(provider: string, capability: string) {
|
|
super(`${provider} does not support "${capability}"`, provider);
|
|
this.name = "CapabilityNotSupportedError";
|
|
}
|
|
}
|