Initial Commit
This commit is contained in:
130
packages/networking/events/broadcast-channel/src/errors/codes.ts
Normal file
130
packages/networking/events/broadcast-channel/src/errors/codes.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import type { SuaveplanErrorOptions } from "@suaveplan/error";
|
||||
import { defineCodes, SuaveplanError } from "@suaveplan/error";
|
||||
|
||||
/**
|
||||
* Stable error-code registry for `@suaveplan/broadcast-channel`. Every error
|
||||
* thrown by the package surfaces one of these codes so downstream consumers
|
||||
* (loggers, problem+json responders, retry wrappers) can map to retry policy,
|
||||
* HTTP status, and i18n keys without re-implementing the classifier.
|
||||
*/
|
||||
export const BROADCAST_CHANNEL_CODES = defineCodes("BROADCAST_CHANNEL", [
|
||||
"CHANNEL_CLOSED",
|
||||
"MESSAGE_MALFORMED",
|
||||
"PAYLOAD_TOO_LARGE",
|
||||
"STORAGE_FAILED",
|
||||
"ASK_TIMEOUT",
|
||||
"ASK_NO_RESPONDER",
|
||||
"ASK_HANDLER_DUPLICATE",
|
||||
] as const);
|
||||
|
||||
/** Union of every fully-qualified code emitted by this package. */
|
||||
export type BroadcastChannelErrorCode =
|
||||
(typeof BROADCAST_CHANNEL_CODES)[keyof typeof BROADCAST_CHANNEL_CODES];
|
||||
|
||||
/** Base class for every typed broadcast-channel error. */
|
||||
export abstract class BroadcastChannelError extends SuaveplanError<BroadcastChannelErrorCode> {}
|
||||
|
||||
interface BaseOpts extends SuaveplanErrorOptions {
|
||||
readonly message?: string;
|
||||
}
|
||||
|
||||
function pickOpts(opts: BaseOpts): SuaveplanErrorOptions {
|
||||
const out: {
|
||||
cause?: unknown;
|
||||
context?: Readonly<Record<string, unknown>>;
|
||||
traceId?: string;
|
||||
} = {};
|
||||
if (opts.cause !== undefined) out.cause = opts.cause;
|
||||
if (opts.context !== undefined) out.context = opts.context;
|
||||
if (opts.traceId !== undefined) out.traceId = opts.traceId;
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Send attempted on a channel that has not been opened or has been closed. */
|
||||
export class BroadcastChannelClosedError extends BroadcastChannelError {
|
||||
public constructor(opts: BaseOpts = {}) {
|
||||
super(
|
||||
BROADCAST_CHANNEL_CODES.CHANNEL_CLOSED,
|
||||
opts.message ?? "broadcast-channel.errors.channelClosed",
|
||||
pickOpts(opts)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** An inbound frame failed `ChannelMessageSchema` validation. */
|
||||
export class BroadcastChannelMessageMalformedError extends BroadcastChannelError {
|
||||
public constructor(opts: BaseOpts = {}) {
|
||||
super(
|
||||
BROADCAST_CHANNEL_CODES.MESSAGE_MALFORMED,
|
||||
opts.message ?? "broadcast-channel.errors.messageMalformed",
|
||||
pickOpts(opts)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Outbound payload exceeded the configured `maxMessageBytes` budget. */
|
||||
export class BroadcastChannelPayloadTooLargeError extends BroadcastChannelError {
|
||||
public constructor(
|
||||
opts: BaseOpts & {
|
||||
readonly sizeBytes?: number;
|
||||
readonly maxBytes?: number;
|
||||
} = {}
|
||||
) {
|
||||
const ctx: Record<string, unknown> = { ...(opts.context ?? {}) };
|
||||
if (opts.sizeBytes !== undefined) ctx["sizeBytes"] = opts.sizeBytes;
|
||||
if (opts.maxBytes !== undefined) ctx["maxBytes"] = opts.maxBytes;
|
||||
super(
|
||||
BROADCAST_CHANNEL_CODES.PAYLOAD_TOO_LARGE,
|
||||
opts.message ?? "broadcast-channel.errors.payloadTooLarge",
|
||||
pickOpts({ ...opts, context: ctx })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Injected `StorageLike` rejected a get/set/list call in the fallback transport. */
|
||||
export class BroadcastChannelStorageFailedError extends BroadcastChannelError {
|
||||
public constructor(opts: BaseOpts = {}) {
|
||||
super(
|
||||
BROADCAST_CHANNEL_CODES.STORAGE_FAILED,
|
||||
opts.message ?? "broadcast-channel.errors.storageFailed",
|
||||
pickOpts(opts)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Pending `ask()` timed out before any peer responded. */
|
||||
export class BroadcastChannelAskTimeoutError extends BroadcastChannelError {
|
||||
public constructor(opts: BaseOpts = {}) {
|
||||
super(
|
||||
BROADCAST_CHANNEL_CODES.ASK_TIMEOUT,
|
||||
opts.message ?? "broadcast-channel.errors.askTimeout",
|
||||
pickOpts(opts)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `ask()` was called with `requireResponder: true` and timed out without any
|
||||
* peer claiming the handler key. Distinct from `ASK_TIMEOUT` so callers can
|
||||
* drive different retry policies for "no listener" vs. "slow listener".
|
||||
*/
|
||||
export class BroadcastChannelAskNoResponderError extends BroadcastChannelError {
|
||||
public constructor(opts: BaseOpts = {}) {
|
||||
super(
|
||||
BROADCAST_CHANNEL_CODES.ASK_NO_RESPONDER,
|
||||
opts.message ?? "broadcast-channel.errors.askNoResponder",
|
||||
pickOpts(opts)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** `onAsk()` called twice for the same `handlerKey` without disposal in between. */
|
||||
export class BroadcastChannelAskHandlerDuplicateError extends BroadcastChannelError {
|
||||
public constructor(opts: BaseOpts = {}) {
|
||||
super(
|
||||
BROADCAST_CHANNEL_CODES.ASK_HANDLER_DUPLICATE,
|
||||
opts.message ?? "broadcast-channel.errors.askHandlerDuplicate",
|
||||
pickOpts(opts)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user