Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
import type { SuaveplanErrorOptions } from "@suaveplan/error";
import { defineCodes, SuaveplanError } from "@suaveplan/error";
/**
* Stable error-code registry for `@suaveplan/imessage-adapter`. Every error
* thrown by the package surfaces one of these ten 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 IMESSAGE_CODES = defineCodes("IMESSAGE", [
"DB_OPEN_FAILED",
"DB_QUERY_FAILED",
"WATCHER_NOT_RUNNING",
"WATCHER_ALREADY_RUNNING",
"ADAPTER_NOT_INITIALIZED",
"INVALID_CONFIG",
"POLL_ABORTED",
"POLL_FAILED",
"ATTACHMENT_PARSE_FAILED",
"APPLE_DATE_PARSE_FAILED",
] as const);
/** Union of every fully-qualified code emitted by this package. */
export type IMessageErrorCode =
(typeof IMESSAGE_CODES)[keyof typeof IMESSAGE_CODES];
/** Base class for every typed iMessage adapter error. */
export abstract class IMessageError extends SuaveplanError<IMessageErrorCode> {}
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;
}
/** Failed to open the chat.db SQLite database. */
export class IMessageDbOpenFailedError extends IMessageError {
public constructor(opts: BaseOpts = {}) {
super(
IMESSAGE_CODES.DB_OPEN_FAILED,
opts.message ?? "imessage.errors.dbOpenFailed",
pickOpts(opts)
);
}
}
/** SQLite query against chat.db failed. */
export class IMessageDbQueryFailedError extends IMessageError {
public constructor(opts: BaseOpts = {}) {
super(
IMESSAGE_CODES.DB_QUERY_FAILED,
opts.message ?? "imessage.errors.dbQueryFailed",
pickOpts(opts)
);
}
}
/** stop() called while watcher is not running. */
export class IMessageWatcherNotRunningError extends IMessageError {
public constructor(opts: BaseOpts = {}) {
super(
IMESSAGE_CODES.WATCHER_NOT_RUNNING,
opts.message ?? "imessage.errors.watcherNotRunning",
pickOpts(opts)
);
}
}
/** start() called twice. */
export class IMessageWatcherAlreadyRunningError extends IMessageError {
public constructor(opts: BaseOpts = {}) {
super(
IMESSAGE_CODES.WATCHER_ALREADY_RUNNING,
opts.message ?? "imessage.errors.watcherAlreadyRunning",
pickOpts(opts)
);
}
}
/** poll() called before initialize(). */
export class IMessageAdapterNotInitializedError extends IMessageError {
public constructor(opts: BaseOpts = {}) {
super(
IMESSAGE_CODES.ADAPTER_NOT_INITIALIZED,
opts.message ?? "imessage.errors.adapterNotInitialized",
pickOpts(opts)
);
}
}
/** Zod schema rejected the config. */
export class IMessageInvalidConfigError extends IMessageError {
public constructor(
opts: BaseOpts & {
readonly issues?: ReadonlyArray<Readonly<Record<string, unknown>>>;
} = {}
) {
const ctx: Record<string, unknown> = { ...(opts.context ?? {}) };
if (opts.issues !== undefined) ctx["issues"] = opts.issues;
super(
IMESSAGE_CODES.INVALID_CONFIG,
opts.message ?? "imessage.errors.invalidConfig",
{ ...pickOpts(opts), context: ctx }
);
}
}
/** poll() / start() aborted by AbortSignal. */
export class IMessagePollAbortedError extends IMessageError {
public constructor(opts: BaseOpts = {}) {
super(
IMESSAGE_CODES.POLL_ABORTED,
opts.message ?? "imessage.errors.pollAborted",
pickOpts(opts)
);
}
}
/** poll() failed for a reason other than abort. */
export class IMessagePollFailedError extends IMessageError {
public constructor(opts: BaseOpts = {}) {
super(
IMESSAGE_CODES.POLL_FAILED,
opts.message ?? "imessage.errors.pollFailed",
pickOpts(opts)
);
}
}
/** Attachment row could not be parsed. */
export class IMessageAttachmentParseError extends IMessageError {
public constructor(opts: BaseOpts = {}) {
super(
IMESSAGE_CODES.ATTACHMENT_PARSE_FAILED,
opts.message ?? "imessage.errors.attachmentParseFailed",
pickOpts(opts)
);
}
}
/** Apple-epoch timestamp could not be decoded. */
export class IMessageAppleDateParseError extends IMessageError {
public constructor(opts: BaseOpts = {}) {
super(
IMESSAGE_CODES.APPLE_DATE_PARSE_FAILED,
opts.message ?? "imessage.errors.appleDateParseFailed",
pickOpts(opts)
);
}
}