@zudojs/errors 1.2.0 → 1.3.1

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.
package/README.md CHANGED
@@ -47,7 +47,7 @@ try {
47
47
  - `BaseError` with stable `code`, `category`, `severity`, `statusCode`, `expose`, `isOperational`, deep-frozen `metadata` and `cause`
48
48
  - `ErrorCode`, `ErrorCategory` and `ErrorSeverity` enums with guards (`isErrorCode`, ...)
49
49
  - Domain, infrastructure and system error families (access, state, HTTP, database, network, container, adapter, crypto, ...) with factories and type guards
50
- - Base classes for other packages' error families, so their `instanceof` checks match: `TransactionError` (+ 10 subclasses), `MiddlewareLimitExceededError` / `MiddlewareDepthExceededError` / `MiddlewareRateLimitError` / `MiddlewareAbortedError`, `TraversalLimitError`, `HttpMiddlewareError` / `HttpMiddlewarePipelineError` / `HttpRequestGuardError`, `OpenAPIError`, `AuthError`, `OAuthError` (with `ErrorCode.OAUTH_*`), `CqrsError`, `ObservabilityError`, `InvalidConstantError` / `ConstantContextError`, and the `zudojs-cli` errors `CLIValidationError` / `CLIGenerationError` / `CLINotInProjectError` / `CLITemplateError`
50
+ - Base classes for other packages' error families, so their `instanceof` checks match: `TransactionError` (+ 10 subclasses), `MiddlewareLimitExceededError` / `MiddlewareDepthExceededError` / `MiddlewareRateLimitError` / `MiddlewareAbortedError`, `TraversalLimitError`, `HttpMiddlewareError` / `HttpMiddlewarePipelineError` / `HttpRequestGuardError`, `OpenAPIError`, `AuthError`, `OAuthError` (with `ErrorCode.OAUTH_*`), `CqrsError` (+ `CommandFailedError` / `QueryFailedError`, thrown when a failed command or query result is unwrapped), `EventBusStoppedError` / `EventBusDisposedError` (re-exported by `@zudojs/events`), `ObservabilityError`, `InvalidConstantError` / `ConstantContextError`, and the `zudojs-cli` errors `CLIValidationError` / `CLIGenerationError` / `CLINotInProjectError` / `CLITemplateError`
51
51
  - `withMetadata()` copies any error (including subclasses with custom constructors) with extra metadata
52
52
  - Serialization: `toJSON()`/`toLogObject()` for trusted logs (cycle-safe cause chains truncated with `"[MaxDepth]"` after 8 levels across the whole chain; metadata under sensitive keys, sensitive keys in object causes and submitted issue values are redacted, so `JSON.stringify(error)` is safe to log), `serializePublicError` / `ErrorSerializer` / `ErrorHandler.toPublicResult` for untrusted clients (recursive redaction, metadata allow-list)
53
53
  - Metadata utilities: `createErrorMetadata`, `mergeErrorMetadata`, `sanitizeErrorMetadata` (drops unsupported values) and `redactErrorMetadata` (removes secrets)
@@ -43,6 +43,12 @@ export declare enum ErrorCode {
43
43
  SESSION_EXPIRED = "ERR_SESSION_EXPIRED",
44
44
  TOKEN_INVALID = "ERR_TOKEN_INVALID",
45
45
  TOKEN_EXPIRED = "ERR_TOKEN_EXPIRED",
46
+ /** A token that verified but has been revoked (logout, rotation replay). */
47
+ TOKEN_REVOKED = "ERR_TOKEN_REVOKED",
48
+ /** The account is temporarily locked after repeated failed sign-ins. */
49
+ ACCOUNT_LOCKED = "ERR_ACCOUNT_LOCKED",
50
+ /** The account exists but has been deactivated. */
51
+ ACCOUNT_DEACTIVATED = "ERR_ACCOUNT_DEACTIVATED",
46
52
  CRYPTO = "ERR_CRYPTO",
47
53
  CRYPTO_KEY = "ERR_CRYPTO_KEY",
48
54
  CRYPTO_HASH = "ERR_CRYPTO_HASH",
@@ -84,6 +90,8 @@ export declare enum ErrorCode {
84
90
  QUERY_HANDLER_NOT_FOUND = "ERR_QUERY_HANDLER_NOT_FOUND",
85
91
  INVALID_COMMAND = "ERR_INVALID_COMMAND",
86
92
  INVALID_QUERY = "ERR_INVALID_QUERY",
93
+ COMMAND_FAILED = "ERR_COMMAND_FAILED",
94
+ QUERY_FAILED = "ERR_QUERY_FAILED",
87
95
  EVENT_HANDLER_FAILED = "ERR_EVENT_HANDLER_FAILED",
88
96
  CONFIGURATION_ERROR = "ERR_CONFIGURATION_ERROR",
89
97
  CONTAINER_DUPLICATE_REGISTRATION = "ERR_CONTAINER_DUPLICATE_REGISTRATION",
@@ -44,6 +44,12 @@ export var ErrorCode;
44
44
  ErrorCode["SESSION_EXPIRED"] = "ERR_SESSION_EXPIRED";
45
45
  ErrorCode["TOKEN_INVALID"] = "ERR_TOKEN_INVALID";
46
46
  ErrorCode["TOKEN_EXPIRED"] = "ERR_TOKEN_EXPIRED";
47
+ /** A token that verified but has been revoked (logout, rotation replay). */
48
+ ErrorCode["TOKEN_REVOKED"] = "ERR_TOKEN_REVOKED";
49
+ /** The account is temporarily locked after repeated failed sign-ins. */
50
+ ErrorCode["ACCOUNT_LOCKED"] = "ERR_ACCOUNT_LOCKED";
51
+ /** The account exists but has been deactivated. */
52
+ ErrorCode["ACCOUNT_DEACTIVATED"] = "ERR_ACCOUNT_DEACTIVATED";
47
53
  ErrorCode["CRYPTO"] = "ERR_CRYPTO";
48
54
  ErrorCode["CRYPTO_KEY"] = "ERR_CRYPTO_KEY";
49
55
  ErrorCode["CRYPTO_HASH"] = "ERR_CRYPTO_HASH";
@@ -86,6 +92,8 @@ export var ErrorCode;
86
92
  ErrorCode["QUERY_HANDLER_NOT_FOUND"] = "ERR_QUERY_HANDLER_NOT_FOUND";
87
93
  ErrorCode["INVALID_COMMAND"] = "ERR_INVALID_COMMAND";
88
94
  ErrorCode["INVALID_QUERY"] = "ERR_INVALID_QUERY";
95
+ ErrorCode["COMMAND_FAILED"] = "ERR_COMMAND_FAILED";
96
+ ErrorCode["QUERY_FAILED"] = "ERR_QUERY_FAILED";
89
97
  ErrorCode["EVENT_HANDLER_FAILED"] = "ERR_EVENT_HANDLER_FAILED";
90
98
  ErrorCode["CONFIGURATION_ERROR"] = "ERR_CONFIGURATION_ERROR";
91
99
  // Container
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Errors raised when a failed command or query result is unwrapped.
3
+ */
4
+ import { CqrsError } from "./cqrsError.base.js";
5
+ /**
6
+ * Thrown by `unwrapCommandResult` for a result whose status is `"failure"`.
7
+ *
8
+ * `failure` holds the failure payload the result carried, which is also the
9
+ * error's `cause`. Not exposed to clients: the payload is an internal value.
10
+ */
11
+ export declare class CommandFailedError extends CqrsError {
12
+ readonly commandType: string;
13
+ readonly failure: unknown;
14
+ constructor(commandType: string, failure: unknown);
15
+ }
16
+ /**
17
+ * Thrown by `unwrapQueryResult` for a result whose status is `"failure"`.
18
+ *
19
+ * `failure` holds the failure payload the result carried, which is also the
20
+ * error's `cause`. Not exposed to clients: the payload is an internal value.
21
+ */
22
+ export declare class QueryFailedError extends CqrsError {
23
+ readonly queryType: string;
24
+ readonly failure: unknown;
25
+ constructor(queryType: string, failure: unknown);
26
+ }
27
+ //# sourceMappingURL=cqrsError.result.d.ts.map
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Errors raised when a failed command or query result is unwrapped.
3
+ */
4
+ import { ErrorCode } from "../../base/types/errorCode.type.js";
5
+ import { CqrsError } from "./cqrsError.base.js";
6
+ /**
7
+ * Thrown by `unwrapCommandResult` for a result whose status is `"failure"`.
8
+ *
9
+ * `failure` holds the failure payload the result carried, which is also the
10
+ * error's `cause`. Not exposed to clients: the payload is an internal value.
11
+ */
12
+ export class CommandFailedError extends CqrsError {
13
+ commandType;
14
+ failure;
15
+ constructor(commandType, failure) {
16
+ super(`Command "${commandType}" failed.`, {
17
+ code: ErrorCode.COMMAND_FAILED,
18
+ statusCode: 500,
19
+ expose: false,
20
+ isOperational: true,
21
+ cause: failure,
22
+ metadata: { commandType },
23
+ });
24
+ this.commandType = commandType;
25
+ this.failure = failure;
26
+ }
27
+ }
28
+ /**
29
+ * Thrown by `unwrapQueryResult` for a result whose status is `"failure"`.
30
+ *
31
+ * `failure` holds the failure payload the result carried, which is also the
32
+ * error's `cause`. Not exposed to clients: the payload is an internal value.
33
+ */
34
+ export class QueryFailedError extends CqrsError {
35
+ queryType;
36
+ failure;
37
+ constructor(queryType, failure) {
38
+ super(`Query "${queryType}" failed.`, {
39
+ code: ErrorCode.QUERY_FAILED,
40
+ statusCode: 500,
41
+ expose: false,
42
+ isOperational: true,
43
+ cause: failure,
44
+ metadata: { queryType },
45
+ });
46
+ this.queryType = queryType;
47
+ this.failure = failure;
48
+ }
49
+ }
50
+ //# sourceMappingURL=cqrsError.result.js.map
@@ -4,4 +4,5 @@
4
4
  * Command/query bus errors.
5
5
  */
6
6
  export * from "./cqrsError.base.js";
7
+ export * from "./cqrsError.result.js";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,5 @@
4
4
  * Command/query bus errors.
5
5
  */
6
6
  export * from "./cqrsError.base.js";
7
+ export * from "./cqrsError.result.js";
7
8
  //# sourceMappingURL=index.js.map
@@ -4,6 +4,6 @@
4
4
  export { EventError, createEventError, isEventError, toEventError, } from "./eventError.base.js";
5
5
  export type { EventErrorOptions } from "./eventError.base.js";
6
6
  export { EventHandlerError, createEventHandlerError, EventHandlerNotFoundError, DuplicateEventHandlerError, EventMiddlewareError, } from "./eventError.handler.js";
7
- export { EventPublishError, InvalidEventError, EventTypeNotFoundError, DuplicateEventDefinitionError, EventDefinitionNotFoundError, EventDispatchAbortedError, EventEmitterDisposedError, EventRegistryDisposedError, EventSubscriptionClosedError, EventBusDisposedError, EventListenerLimitExceededError, EventTimeoutError, } from "./eventError.lifecycle.js";
7
+ export { EventPublishError, InvalidEventError, EventTypeNotFoundError, DuplicateEventDefinitionError, EventDefinitionNotFoundError, EventDispatchAbortedError, EventEmitterDisposedError, EventRegistryDisposedError, EventSubscriptionClosedError, EventBusDisposedError, EventBusStoppedError, EventListenerLimitExceededError, EventTimeoutError, } from "./eventError.lifecycle.js";
8
8
  export { EventSerializationError, EventDeserializationError, } from "./eventError.serialization.js";
9
9
  //# sourceMappingURL=event.error.d.ts.map
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export { EventError, createEventError, isEventError, toEventError, } from "./eventError.base.js";
5
5
  export { EventHandlerError, createEventHandlerError, EventHandlerNotFoundError, DuplicateEventHandlerError, EventMiddlewareError, } from "./eventError.handler.js";
6
- export { EventPublishError, InvalidEventError, EventTypeNotFoundError, DuplicateEventDefinitionError, EventDefinitionNotFoundError, EventDispatchAbortedError, EventEmitterDisposedError, EventRegistryDisposedError, EventSubscriptionClosedError, EventBusDisposedError, EventListenerLimitExceededError, EventTimeoutError, } from "./eventError.lifecycle.js";
6
+ export { EventPublishError, InvalidEventError, EventTypeNotFoundError, DuplicateEventDefinitionError, EventDefinitionNotFoundError, EventDispatchAbortedError, EventEmitterDisposedError, EventRegistryDisposedError, EventSubscriptionClosedError, EventBusDisposedError, EventBusStoppedError, EventListenerLimitExceededError, EventTimeoutError, } from "./eventError.lifecycle.js";
7
7
  export { EventSerializationError, EventDeserializationError, } from "./eventError.serialization.js";
8
8
  //# sourceMappingURL=event.error.js.map
@@ -62,6 +62,14 @@ export declare class EventSubscriptionClosedError extends EventError {
62
62
  export declare class EventBusDisposedError extends EventError {
63
63
  constructor();
64
64
  }
65
+ /**
66
+ * Error thrown when publishing or subscribing on a stopped event bus.
67
+ * Call `start()` to resume.
68
+ */
69
+ export declare class EventBusStoppedError extends EventError {
70
+ readonly operation: string;
71
+ constructor(operation: string);
72
+ }
65
73
  /**
66
74
  * Error thrown when a pattern exceeds its configured handler limit.
67
75
  *
@@ -130,6 +130,23 @@ export class EventBusDisposedError extends EventError {
130
130
  });
131
131
  }
132
132
  }
133
+ /**
134
+ * Error thrown when publishing or subscribing on a stopped event bus.
135
+ * Call `start()` to resume.
136
+ */
137
+ export class EventBusStoppedError extends EventError {
138
+ operation;
139
+ constructor(operation) {
140
+ super(`Cannot ${operation} on a stopped event bus. Call start() first.`, {
141
+ code: ErrorCode.LIFECYCLE_STATE,
142
+ statusCode: 500,
143
+ expose: false,
144
+ isOperational: true,
145
+ metadata: { operation },
146
+ });
147
+ this.operation = operation;
148
+ }
149
+ }
133
150
  /**
134
151
  * Error thrown when a pattern exceeds its configured handler limit.
135
152
  *
@@ -22,11 +22,17 @@ export declare class InvalidMessageError extends MessageError {
22
22
  export declare class MessageTypeNotFoundError extends MessageError {
23
23
  constructor(messageType: string);
24
24
  }
25
- /** Error thrown when message dispatch is aborted. */
25
+ /**
26
+ * Error thrown when message dispatch is aborted.
27
+ *
28
+ * `options.cause` is the abort signal's `reason`, so the caller's reason
29
+ * for cancelling survives as `error.cause`.
30
+ */
26
31
  export declare class MessageDispatchAbortedError extends MessageError {
27
32
  constructor(message?: string, options?: {
28
33
  messageType?: string;
29
34
  messageId?: string;
35
+ cause?: unknown;
30
36
  });
31
37
  }
32
38
  /** Error thrown when the message bus has been disposed. */
@@ -43,13 +43,19 @@ export class MessageTypeNotFoundError extends MessageError {
43
43
  });
44
44
  }
45
45
  }
46
- /** Error thrown when message dispatch is aborted. */
46
+ /**
47
+ * Error thrown when message dispatch is aborted.
48
+ *
49
+ * `options.cause` is the abort signal's `reason`, so the caller's reason
50
+ * for cancelling survives as `error.cause`.
51
+ */
47
52
  export class MessageDispatchAbortedError extends MessageError {
48
53
  constructor(message = "Message dispatch was aborted.", options = {}) {
49
54
  super(message, {
50
55
  code: ErrorCode.MESSAGE_ABORTED,
51
56
  messageType: options.messageType,
52
57
  messageId: options.messageId,
58
+ cause: options.cause,
53
59
  statusCode: 499,
54
60
  expose: false,
55
61
  });
@@ -9,10 +9,19 @@ import { ErrorSeverity } from "../../base/types/errorSeverity.type.js";
9
9
  export interface RPCErrorOptions extends Omit<BaseErrorOptions, "category"> {
10
10
  readonly category?: ErrorCategory;
11
11
  readonly procedureName?: string;
12
+ /** Structured, caller-safe detail sent with the error's wire payload. */
13
+ readonly details?: unknown;
12
14
  }
13
15
  /** Base error for all RPC failures. */
14
16
  export declare class RPCError extends BaseError {
15
17
  readonly procedureName?: string;
18
+ /**
19
+ * Structured, caller-safe detail carried by the wire payload — validation
20
+ * issues, `{ retryAfter }` for a rate limit, or whatever a custom error
21
+ * sent. Set on errors an RPC client rebuilds from a response; `undefined`
22
+ * when the payload had none.
23
+ */
24
+ readonly details?: unknown;
16
25
  constructor(message: string, options?: RPCErrorOptions);
17
26
  toJSON(): {
18
27
  name: string;
@@ -27,6 +36,7 @@ export declare class RPCError extends BaseError {
27
36
  stack?: string;
28
37
  cause?: import("../../base/types/baseError.type.js").SerializedBaseError | unknown;
29
38
  procedureName?: string | undefined;
39
+ details?: {} | null | undefined;
30
40
  };
31
41
  }
32
42
  /** Creates an RPC error. */
@@ -19,6 +19,13 @@ export class RPCError extends BaseError {
19
19
  isOperational: options.isOperational ?? true,
20
20
  });
21
21
  this.procedureName = options.procedureName;
22
+ if (options.details !== undefined) {
23
+ Object.defineProperty(this, "details", {
24
+ value: options.details,
25
+ enumerable: true,
26
+ configurable: true,
27
+ });
28
+ }
22
29
  }
23
30
  toJSON() {
24
31
  return {
@@ -26,6 +33,7 @@ export class RPCError extends BaseError {
26
33
  ...(this.procedureName !== undefined
27
34
  ? { procedureName: this.procedureName }
28
35
  : {}),
36
+ ...(this.details !== undefined ? { details: this.details } : {}),
29
37
  };
30
38
  }
31
39
  }
@@ -5,10 +5,16 @@ import { BaseError } from "../../base/core/baseError.core.js";
5
5
  import type { BaseErrorOptions } from "../../base/types/baseError.type.js";
6
6
  import { ErrorCategory } from "../../base/types/errorCategory.type.js";
7
7
  import { ErrorSeverity } from "../../base/types/errorSeverity.type.js";
8
- /** Options for constructing a SchemaError. */
9
- export interface SchemaErrorOptions extends Omit<BaseErrorOptions, "category"> {
8
+ /**
9
+ * Options for constructing a SchemaError.
10
+ *
11
+ * @typeParam TIssue - Shape of one issue. `@zudojs/errors` cannot know it
12
+ * (the schema package sits above it), so it defaults to `unknown`;
13
+ * `@zudojs/schema` throws `SchemaError<SchemaIssue>`.
14
+ */
15
+ export interface SchemaErrorOptions<TIssue = unknown> extends Omit<BaseErrorOptions, "category"> {
10
16
  readonly category?: ErrorCategory;
11
- readonly issues?: readonly unknown[];
17
+ readonly issues?: readonly TIssue[];
12
18
  }
13
19
  /**
14
20
  * Base error for all schema validation failures.
@@ -19,9 +25,9 @@ export interface SchemaErrorOptions extends Omit<BaseErrorOptions, "category"> {
19
25
  * (`value`, `received`, `input`, `actual`) with a type/size description so
20
26
  * that secrets submitted by a client are never echoed back or logged.
21
27
  */
22
- export declare class SchemaError extends BaseError {
23
- readonly issues: readonly unknown[];
24
- constructor(message: string, options?: SchemaErrorOptions);
28
+ export declare class SchemaError<TIssue = unknown> extends BaseError {
29
+ readonly issues: readonly TIssue[];
30
+ constructor(message: string, options?: SchemaErrorOptions<TIssue>);
25
31
  /** Returns whether any issues were recorded. */
26
32
  hasIssues(): boolean;
27
33
  /** Returns a serialized representation including (redacted) issues. */
@@ -37,11 +43,11 @@ export declare class SchemaError extends BaseError {
37
43
  metadata: Readonly<import("../../index.js").ErrorMetadata>;
38
44
  stack?: string;
39
45
  cause?: import("../../base/types/baseError.type.js").SerializedBaseError | unknown;
40
- issues: readonly unknown[];
46
+ issues: readonly TIssue[];
41
47
  };
42
48
  }
43
49
  /** Creates a schema error. */
44
- export declare function createSchemaError(message: string, options?: SchemaErrorOptions): SchemaError;
50
+ export declare function createSchemaError<TIssue = unknown>(message: string, options?: SchemaErrorOptions<TIssue>): SchemaError<TIssue>;
45
51
  /** Determines whether an unknown value is a SchemaError. */
46
52
  export declare function isSchemaError(value: unknown): value is SchemaError;
47
53
  //# sourceMappingURL=schemaError.base.d.ts.map
@@ -42,14 +42,21 @@ export declare class CircularReferenceError extends SerializationError {
42
42
  /**
43
43
  * Error thrown when maximum serialization depth is exceeded.
44
44
  *
45
- * Over-deep data is a server-side data bug, so this is an internal (500) error.
45
+ * By default over-deep data is a server-side data bug, so this is an
46
+ * internal (500) error. Code that checks UNTRUSTED input (for example
47
+ * `assertDepthWithinLimit` in `@zudojs/validation`) passes
48
+ * `{ statusCode: 400, expose: true }`: too-deep client input is a client
49
+ * error. The message holds only the two numbers, so it is safe to expose.
46
50
  */
47
51
  export declare class SerializationDepthError extends SerializationError {
48
52
  readonly depth: number;
49
53
  readonly maxDepth: number;
50
54
  /** @deprecated Use `maxDepth`. */
51
55
  readonly maxDepthValue: number;
52
- constructor(depth: number, maxDepth: number);
56
+ constructor(depth: number, maxDepth: number, options?: {
57
+ readonly statusCode?: number;
58
+ readonly expose?: boolean;
59
+ });
53
60
  }
54
61
  /** Error thrown when a serialized payload exceeds the size limit. */
55
62
  export declare class SerializationPayloadTooLargeError extends SerializationError {
@@ -74,20 +74,24 @@ export class CircularReferenceError extends SerializationError {
74
74
  /**
75
75
  * Error thrown when maximum serialization depth is exceeded.
76
76
  *
77
- * Over-deep data is a server-side data bug, so this is an internal (500) error.
77
+ * By default over-deep data is a server-side data bug, so this is an
78
+ * internal (500) error. Code that checks UNTRUSTED input (for example
79
+ * `assertDepthWithinLimit` in `@zudojs/validation`) passes
80
+ * `{ statusCode: 400, expose: true }`: too-deep client input is a client
81
+ * error. The message holds only the two numbers, so it is safe to expose.
78
82
  */
79
83
  export class SerializationDepthError extends SerializationError {
80
84
  depth;
81
85
  maxDepth;
82
86
  /** @deprecated Use `maxDepth`. */
83
87
  maxDepthValue;
84
- constructor(depth, maxDepth) {
88
+ constructor(depth, maxDepth, options = {}) {
85
89
  super(`Maximum serialization depth exceeded: ${depth} > ${maxDepth}`, {
86
90
  code: ErrorCode.MAX_DEPTH_EXCEEDED,
87
91
  depth,
88
92
  maxDepth,
89
- statusCode: 500,
90
- expose: false,
93
+ statusCode: options.statusCode ?? 500,
94
+ expose: options.expose ?? false,
91
95
  });
92
96
  this.depth = depth;
93
97
  this.maxDepth = maxDepth;
@@ -20,8 +20,23 @@ export declare class TransactionRollbackError extends TransactionError {
20
20
  constructor(transactionId: string, options?: {
21
21
  readonly cause?: unknown;
22
22
  readonly originalError?: unknown;
23
+ /** Overrides the default "rollback failed" message (for subclasses). */
24
+ readonly message?: string;
23
25
  });
24
26
  }
27
+ /**
28
+ * A commit was refused because the transaction was marked rollback-only;
29
+ * the transaction was rolled back instead.
30
+ *
31
+ * Distinct from a rollback that *failed*. It extends
32
+ * `TransactionRollbackError`, so existing `instanceof` checks and the
33
+ * `ERR_DATABASE_TRANSACTION` code still match, while the message and
34
+ * class say what actually happened. `metadata.originalError` carries the
35
+ * reason passed to `markRollbackOnly`.
36
+ */
37
+ export declare class TransactionRollbackOnlyError extends TransactionRollbackError {
38
+ constructor(transactionId: string, reason?: unknown);
39
+ }
25
40
  /** The underlying adapter threw an error. */
26
41
  export declare class TransactionAdapterError extends TransactionError {
27
42
  constructor(message: string, cause?: unknown);
@@ -35,7 +35,7 @@ export class TransactionCommitError extends TransactionError {
35
35
  /** Transaction rollback failed. */
36
36
  export class TransactionRollbackError extends TransactionError {
37
37
  constructor(transactionId, options) {
38
- super(`Transaction "${transactionId}" rollback failed`, {
38
+ super(options?.message ?? `Transaction "${transactionId}" rollback failed`, {
39
39
  code: ErrorCode.DATABASE_TRANSACTION,
40
40
  cause: options?.cause,
41
41
  metadata: {
@@ -47,6 +47,24 @@ export class TransactionRollbackError extends TransactionError {
47
47
  });
48
48
  }
49
49
  }
50
+ /**
51
+ * A commit was refused because the transaction was marked rollback-only;
52
+ * the transaction was rolled back instead.
53
+ *
54
+ * Distinct from a rollback that *failed*. It extends
55
+ * `TransactionRollbackError`, so existing `instanceof` checks and the
56
+ * `ERR_DATABASE_TRANSACTION` code still match, while the message and
57
+ * class say what actually happened. `metadata.originalError` carries the
58
+ * reason passed to `markRollbackOnly`.
59
+ */
60
+ export class TransactionRollbackOnlyError extends TransactionRollbackError {
61
+ constructor(transactionId, reason) {
62
+ super(transactionId, {
63
+ originalError: reason ?? "marked rollback-only",
64
+ message: `Transaction "${transactionId}" commit refused: transaction marked rollback-only`,
65
+ });
66
+ }
67
+ }
50
68
  /** The underlying adapter threw an error. */
51
69
  export class TransactionAdapterError extends TransactionError {
52
70
  constructor(message, cause) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zudojs/errors",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Shared error base class, error codes, and error handling utilities for the Zudojs framework.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "dependencies": {},
23
23
  "devDependencies": {
24
24
  "typescript": "7.0.2",
25
- "vitest": "^4.1.11"
25
+ "vitest": "^5.0.1"
26
26
  },
27
27
  "engines": {
28
28
  "node": ">=24.0.0"
@@ -40,7 +40,7 @@
40
40
  "errors",
41
41
  "error-handling"
42
42
  ],
43
- "homepage": "https://github.com/oyinlola-tech/zudo#readme",
43
+ "homepage": "https://zudojs.oyinlola.site/docs/packages-errors",
44
44
  "bugs": {
45
45
  "url": "https://github.com/oyinlola-tech/zudo/issues"
46
46
  },