@zudojs/errors 1.0.1 → 1.2.0
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 +9 -2
- package/dist/base/core/baseError.core.d.ts +18 -4
- package/dist/base/core/baseError.core.js +31 -46
- package/dist/base/core/baseError.serialize.d.ts +53 -0
- package/dist/base/core/baseError.serialize.js +109 -0
- package/dist/base/core/errorCause.redact.d.ts +25 -0
- package/dist/base/core/errorCause.redact.js +75 -0
- package/dist/base/types/errorCode.type.d.ts +11 -1
- package/dist/base/types/errorCode.type.js +12 -0
- package/dist/domain/access/authError.base.d.ts +25 -0
- package/dist/domain/access/authError.base.js +26 -0
- package/dist/domain/access/index.d.ts +2 -0
- package/dist/domain/access/index.js +2 -0
- package/dist/domain/access/oauthError.base.d.ts +24 -0
- package/dist/domain/access/oauthError.base.js +30 -0
- package/dist/domain/cli/cliError.base.d.ts +24 -0
- package/dist/domain/cli/cliError.base.js +34 -0
- package/dist/domain/cli/index.d.ts +7 -0
- package/dist/domain/cli/index.js +7 -0
- package/dist/domain/constant/constantError.base.d.ts +21 -0
- package/dist/domain/constant/constantError.base.js +31 -0
- package/dist/domain/constant/index.d.ts +7 -0
- package/dist/domain/constant/index.js +7 -0
- package/dist/domain/cqrs/cqrsError.base.d.ts +14 -0
- package/dist/domain/cqrs/cqrsError.base.js +26 -0
- package/dist/domain/cqrs/index.d.ts +7 -0
- package/dist/domain/cqrs/index.js +7 -0
- package/dist/domain/event/event.error.d.ts +1 -1
- package/dist/domain/event/event.error.js +1 -1
- package/dist/domain/event/eventError.lifecycle.d.ts +16 -0
- package/dist/domain/event/eventError.lifecycle.js +31 -0
- package/dist/domain/index.d.ts +6 -0
- package/dist/domain/index.js +6 -0
- package/dist/domain/openapi/index.d.ts +7 -0
- package/dist/domain/openapi/index.js +7 -0
- package/dist/domain/openapi/openApiError.base.d.ts +20 -0
- package/dist/domain/openapi/openApiError.base.js +24 -0
- package/dist/domain/schema/schemaError.base.js +1 -1
- package/dist/domain/shared/domainError.helpers.d.ts +3 -2
- package/dist/domain/shared/domainError.helpers.js +32 -8
- package/dist/domain/state/validationError.base.d.ts +4 -3
- package/dist/domain/state/validationError.base.js +5 -4
- package/dist/domain/transaction/index.d.ts +10 -0
- package/dist/domain/transaction/index.js +10 -0
- package/dist/domain/transaction/transactionError.base.d.ts +18 -0
- package/dist/domain/transaction/transactionError.base.js +21 -0
- package/dist/domain/transaction/transactionError.misc.d.ts +26 -0
- package/dist/domain/transaction/transactionError.misc.js +47 -0
- package/dist/domain/transaction/transactionError.types.d.ts +33 -0
- package/dist/domain/transaction/transactionError.types.js +62 -0
- package/dist/domain/traversal/index.d.ts +7 -0
- package/dist/domain/traversal/index.js +7 -0
- package/dist/domain/traversal/traversalLimit.error.d.ts +37 -0
- package/dist/domain/traversal/traversalLimit.error.js +39 -0
- package/dist/infrastructure/httpPipeline/httpMiddleware.error.d.ts +26 -0
- package/dist/infrastructure/httpPipeline/httpMiddleware.error.js +43 -0
- package/dist/infrastructure/httpPipeline/httpRequestGuard.error.d.ts +21 -0
- package/dist/infrastructure/httpPipeline/httpRequestGuard.error.js +28 -0
- package/dist/infrastructure/httpPipeline/index.d.ts +8 -0
- package/dist/infrastructure/httpPipeline/index.js +8 -0
- package/dist/infrastructure/index.d.ts +1 -0
- package/dist/infrastructure/index.js +1 -0
- package/dist/infrastructure/middleware/index.d.ts +1 -0
- package/dist/infrastructure/middleware/index.js +1 -0
- package/dist/infrastructure/middleware/middlewareError.types.d.ts +28 -0
- package/dist/infrastructure/middleware/middlewareError.types.js +44 -0
- package/dist/system/index.d.ts +1 -0
- package/dist/system/index.js +1 -0
- package/dist/system/observability/index.d.ts +7 -0
- package/dist/system/observability/index.js +7 -0
- package/dist/system/observability/observabilityError.base.d.ts +16 -0
- package/dist/system/observability/observabilityError.base.js +23 -0
- package/dist/utils/errorMapper.mappers.d.ts +1 -1
- package/dist/utils/errorSerializer.core.d.ts +10 -2
- package/dist/utils/errorSerializer.core.js +30 -76
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Shared error base class, error codes, and error handling utilities for the Zudojs framework.
|
|
4
4
|
|
|
5
|
+
<!-- zudo-docs:start -->
|
|
6
|
+
|
|
7
|
+
**Documentation:** [zudojs.oyinlola.site/docs/packages-errors](https://zudojs.oyinlola.site/docs/packages-errors) · **For AI agents:** [Markdown version](https://zudojs.oyinlola.site/docs/packages-errors.md), [llms.txt](https://zudojs.oyinlola.site/llms.txt)
|
|
8
|
+
|
|
9
|
+
<!-- zudo-docs:end -->
|
|
10
|
+
|
|
5
11
|
## Installation
|
|
6
12
|
|
|
7
13
|
```bash
|
|
@@ -29,7 +35,7 @@ try {
|
|
|
29
35
|
await work();
|
|
30
36
|
} catch (thrown) {
|
|
31
37
|
const error = toBaseError(thrown).withMetadata({ requestId });
|
|
32
|
-
logger.error(error.toLogObject()); // stack + cause chain,
|
|
38
|
+
logger.error(error.toLogObject()); // stack + cause chain, sensitive metadata redacted
|
|
33
39
|
res.status(error.statusCode).json(serializePublicError(error));
|
|
34
40
|
// -> { code, message, category, statusCode } — no stack, no cause,
|
|
35
41
|
// and no metadata unless the error is exposable (or keys are allow-listed)
|
|
@@ -41,8 +47,9 @@ try {
|
|
|
41
47
|
- `BaseError` with stable `code`, `category`, `severity`, `statusCode`, `expose`, `isOperational`, deep-frozen `metadata` and `cause`
|
|
42
48
|
- `ErrorCode`, `ErrorCategory` and `ErrorSeverity` enums with guards (`isErrorCode`, ...)
|
|
43
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`
|
|
44
51
|
- `withMetadata()` copies any error (including subclasses with custom constructors) with extra metadata
|
|
45
|
-
- Serialization: `toJSON()`/`toLogObject()` for trusted logs (cycle-safe cause chains), `serializePublicError` / `ErrorSerializer` / `ErrorHandler.toPublicResult` for untrusted clients (recursive redaction, metadata allow-list)
|
|
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)
|
|
46
53
|
- Metadata utilities: `createErrorMetadata`, `mergeErrorMetadata`, `sanitizeErrorMetadata` (drops unsupported values) and `redactErrorMetadata` (removes secrets)
|
|
47
54
|
- Normalization (`toBaseError`, `normalizeUnknownError`, `ErrorHandler.normalize`) that keeps the thrown value as `cause` and classifies unknown failures as internal, non-operational 500s
|
|
48
55
|
- Error mapping registry (`ErrorMapperRegistry`, `mapErrorType`, declarative `ErrorMapping`)
|
|
@@ -45,11 +45,23 @@ export declare class BaseError extends Error {
|
|
|
45
45
|
* Converts the error into a serializable representation.
|
|
46
46
|
*
|
|
47
47
|
* Intended for trusted internal logging: includes the stack trace and the
|
|
48
|
-
* cause chain (
|
|
49
|
-
*
|
|
48
|
+
* cause chain (cycle-safe, and truncated with `"[MaxDepth]"` after 8
|
|
49
|
+
* levels counted across the whole chain, BaseError causes included).
|
|
50
|
+
*
|
|
51
|
+
* Metadata values under sensitive keys (`password`, `authorization`,
|
|
52
|
+
* `token`, ...) and sensitive keys inside plain-object causes are replaced
|
|
53
|
+
* with `"[REDACTED]"`, because `JSON.stringify(error)` and `res.json(error)`
|
|
54
|
+
* call this implicitly. The raw values stay on `error.metadata` and
|
|
55
|
+
* `error.cause`. Use `ErrorSerializer` (`serializePublicError`) for anything
|
|
56
|
+
* sent to an untrusted client.
|
|
50
57
|
*/
|
|
51
58
|
toJSON(): SerializedBaseError;
|
|
52
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Returns the error as a plain object for internal logging.
|
|
61
|
+
*
|
|
62
|
+
* Same output as {@link BaseError.toJSON}, so sensitive metadata is
|
|
63
|
+
* redacted.
|
|
64
|
+
*/
|
|
53
65
|
toLogObject(): SerializedBaseError;
|
|
54
66
|
/** Returns a concise error description. */
|
|
55
67
|
toString(): string;
|
|
@@ -57,7 +69,9 @@ export declare class BaseError extends Error {
|
|
|
57
69
|
/**
|
|
58
70
|
* Serializes nested Error causes while avoiding recursive failures.
|
|
59
71
|
*
|
|
60
|
-
* Cause chains are cycle-safe and truncated after
|
|
72
|
+
* Cause chains are cycle-safe and truncated after 8 levels counted from
|
|
73
|
+
* `depth`; BaseError causes continue the count rather than restarting it.
|
|
74
|
+
* Plain-object causes are redacted.
|
|
61
75
|
*/
|
|
62
76
|
export declare function serializeErrorCause(cause: unknown, depth?: number): SerializedBaseError | unknown;
|
|
63
77
|
//# sourceMappingURL=baseError.core.d.ts.map
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { ErrorCategory } from "../types/errorCategory.type.js";
|
|
2
2
|
import { ErrorCode } from "../types/errorCode.type.js";
|
|
3
3
|
import { ErrorSeverity } from "../types/errorSeverity.type.js";
|
|
4
|
-
import { createErrorMetadata, serializeErrorMetadata, } from "./errorMetadata.core.js";
|
|
4
|
+
import { createErrorMetadata, redactErrorMetadata, serializeErrorMetadata, } from "./errorMetadata.core.js";
|
|
5
|
+
import { beginSerializing, serializeCauseAt, takeSerializationFrame, } from "./baseError.serialize.js";
|
|
5
6
|
/**
|
|
6
7
|
* Brand used to recognise BaseError instances across duplicated copies of
|
|
7
8
|
* this package (for example when two versions of `@zudojs/errors` are
|
|
8
9
|
* installed side by side and `instanceof` fails across the boundary).
|
|
9
10
|
*/
|
|
10
11
|
export const BASE_ERROR_BRAND = Symbol.for("@zudojs/errors.BaseError");
|
|
11
|
-
/** Maximum depth of cause chains included in serialized output. */
|
|
12
|
-
const MAX_CAUSE_DEPTH = 8;
|
|
13
|
-
/** Errors currently being serialized (guards against cyclic cause chains). */
|
|
14
|
-
const serializing = new WeakSet();
|
|
15
12
|
/**
|
|
16
13
|
* Base error class shared by all Zudojs application errors.
|
|
17
14
|
*
|
|
@@ -97,10 +94,19 @@ export class BaseError extends Error {
|
|
|
97
94
|
* Converts the error into a serializable representation.
|
|
98
95
|
*
|
|
99
96
|
* Intended for trusted internal logging: includes the stack trace and the
|
|
100
|
-
* cause chain (
|
|
101
|
-
*
|
|
97
|
+
* cause chain (cycle-safe, and truncated with `"[MaxDepth]"` after 8
|
|
98
|
+
* levels counted across the whole chain, BaseError causes included).
|
|
99
|
+
*
|
|
100
|
+
* Metadata values under sensitive keys (`password`, `authorization`,
|
|
101
|
+
* `token`, ...) and sensitive keys inside plain-object causes are replaced
|
|
102
|
+
* with `"[REDACTED]"`, because `JSON.stringify(error)` and `res.json(error)`
|
|
103
|
+
* call this implicitly. The raw values stay on `error.metadata` and
|
|
104
|
+
* `error.cause`. Use `ErrorSerializer` (`serializePublicError`) for anything
|
|
105
|
+
* sent to an untrusted client.
|
|
102
106
|
*/
|
|
103
107
|
toJSON() {
|
|
108
|
+
const frame = takeSerializationFrame();
|
|
109
|
+
const metadata = serializeErrorMetadata(this.metadata);
|
|
104
110
|
const base = {
|
|
105
111
|
name: this.name,
|
|
106
112
|
message: this.message,
|
|
@@ -110,23 +116,30 @@ export class BaseError extends Error {
|
|
|
110
116
|
statusCode: this.statusCode,
|
|
111
117
|
expose: this.expose,
|
|
112
118
|
isOperational: this.isOperational,
|
|
113
|
-
metadata:
|
|
119
|
+
metadata: frame.redact ? { ...redactErrorMetadata(metadata) } : metadata,
|
|
114
120
|
...(this.stack ? { stack: this.stack } : {}),
|
|
115
121
|
};
|
|
116
122
|
if (this.cause === undefined)
|
|
117
123
|
return base;
|
|
118
|
-
|
|
124
|
+
const release = beginSerializing(this);
|
|
125
|
+
if (release === undefined)
|
|
119
126
|
return { ...base, cause: "[Circular]" };
|
|
120
|
-
}
|
|
121
|
-
serializing.add(this);
|
|
122
127
|
try {
|
|
123
|
-
return {
|
|
128
|
+
return {
|
|
129
|
+
...base,
|
|
130
|
+
cause: serializeCauseAt(this.cause, frame.depth + 1, frame.redact, BASE_ERROR_BRAND),
|
|
131
|
+
};
|
|
124
132
|
}
|
|
125
133
|
finally {
|
|
126
|
-
|
|
134
|
+
release();
|
|
127
135
|
}
|
|
128
136
|
}
|
|
129
|
-
/**
|
|
137
|
+
/**
|
|
138
|
+
* Returns the error as a plain object for internal logging.
|
|
139
|
+
*
|
|
140
|
+
* Same output as {@link BaseError.toJSON}, so sensitive metadata is
|
|
141
|
+
* redacted.
|
|
142
|
+
*/
|
|
130
143
|
toLogObject() {
|
|
131
144
|
return this.toJSON();
|
|
132
145
|
}
|
|
@@ -149,39 +162,11 @@ function normalizeStatusCode(statusCode) {
|
|
|
149
162
|
/**
|
|
150
163
|
* Serializes nested Error causes while avoiding recursive failures.
|
|
151
164
|
*
|
|
152
|
-
* Cause chains are cycle-safe and truncated after
|
|
165
|
+
* Cause chains are cycle-safe and truncated after 8 levels counted from
|
|
166
|
+
* `depth`; BaseError causes continue the count rather than restarting it.
|
|
167
|
+
* Plain-object causes are redacted.
|
|
153
168
|
*/
|
|
154
169
|
export function serializeErrorCause(cause, depth = 1) {
|
|
155
|
-
|
|
156
|
-
return "[MaxDepth]";
|
|
157
|
-
if (cause instanceof BaseError) {
|
|
158
|
-
if (serializing.has(cause))
|
|
159
|
-
return "[Circular]";
|
|
160
|
-
return cause.toJSON();
|
|
161
|
-
}
|
|
162
|
-
if (cause instanceof Error) {
|
|
163
|
-
if (serializing.has(cause))
|
|
164
|
-
return "[Circular]";
|
|
165
|
-
serializing.add(cause);
|
|
166
|
-
try {
|
|
167
|
-
return {
|
|
168
|
-
name: cause.name,
|
|
169
|
-
message: cause.message,
|
|
170
|
-
...(cause.stack ? { stack: cause.stack } : {}),
|
|
171
|
-
...(cause.cause !== undefined
|
|
172
|
-
? { cause: serializeErrorCause(cause.cause, depth + 1) }
|
|
173
|
-
: {}),
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
finally {
|
|
177
|
-
serializing.delete(cause);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (cause !== null && typeof cause === "object") {
|
|
181
|
-
if (serializing.has(cause))
|
|
182
|
-
return "[Circular]";
|
|
183
|
-
return cause;
|
|
184
|
-
}
|
|
185
|
-
return cause;
|
|
170
|
+
return serializeCauseAt(cause, depth, true, BASE_ERROR_BRAND);
|
|
186
171
|
}
|
|
187
172
|
//# sourceMappingURL=baseError.core.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Depth- and redaction-aware serialization of BaseError cause chains.
|
|
3
|
+
*
|
|
4
|
+
* `BaseError.toJSON()` takes no arguments (it is called implicitly by
|
|
5
|
+
* `JSON.stringify`), and subclasses override it as
|
|
6
|
+
* `{ ...super.toJSON(), extra }`. To carry the chain depth and the redaction
|
|
7
|
+
* mode through those overrides, the caller parks a frame here immediately
|
|
8
|
+
* before invoking `toJSON()`, and `BaseError.toJSON` consumes it on entry.
|
|
9
|
+
* Serialization is synchronous, so the frame cannot leak across calls.
|
|
10
|
+
*
|
|
11
|
+
* Internal to `@zudojs/errors`: not re-exported from the package barrel.
|
|
12
|
+
*
|
|
13
|
+
* @module base/core/baseError.serialize
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Maximum depth of the cause chain included in serialized output, counted
|
|
17
|
+
* across the WHOLE chain (BaseError and native causes alike).
|
|
18
|
+
*/
|
|
19
|
+
export declare const MAX_CAUSE_DEPTH = 8;
|
|
20
|
+
/** How a `toJSON()` call should serialize. */
|
|
21
|
+
export interface SerializationFrame {
|
|
22
|
+
/** Depth of the error being serialized; the outermost error is 0. */
|
|
23
|
+
readonly depth: number;
|
|
24
|
+
/** Whether metadata, issues and object causes are redacted. */
|
|
25
|
+
readonly redact: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Structural shape of a BaseError, recognised by its `toJSON`. */
|
|
28
|
+
interface JsonableError extends Error {
|
|
29
|
+
toJSON(): object;
|
|
30
|
+
}
|
|
31
|
+
/** Consumes the frame parked for the current `toJSON()` call. */
|
|
32
|
+
export declare function takeSerializationFrame(): SerializationFrame;
|
|
33
|
+
/** Marks an error as in-progress for cycle detection; returns a release. */
|
|
34
|
+
export declare function beginSerializing(error: object): (() => void) | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Calls `error.toJSON()` with an explicit frame and records the result as a
|
|
37
|
+
* genuine serialized BaseError.
|
|
38
|
+
*/
|
|
39
|
+
export declare function toJSONWithFrame(error: JsonableError, frame: SerializationFrame): Record<string, unknown>;
|
|
40
|
+
/**
|
|
41
|
+
* Whether `value` came from a BaseError's `toJSON` via this module, as
|
|
42
|
+
* opposed to an arbitrary object that merely has the same field names.
|
|
43
|
+
*/
|
|
44
|
+
export declare function isGenuineSerializedBaseError(value: object): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Serializes one cause at `depth`, carrying the depth into BaseError causes
|
|
47
|
+
* so the limit applies across the whole chain. Recursion is bounded by
|
|
48
|
+
* {@link MAX_CAUSE_DEPTH}, so even a 20 000-deep chain cannot overflow the
|
|
49
|
+
* stack.
|
|
50
|
+
*/
|
|
51
|
+
export declare function serializeCauseAt(cause: unknown, depth: number, redact: boolean, brand: symbol): unknown;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=baseError.serialize.d.ts.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Depth- and redaction-aware serialization of BaseError cause chains.
|
|
3
|
+
*
|
|
4
|
+
* `BaseError.toJSON()` takes no arguments (it is called implicitly by
|
|
5
|
+
* `JSON.stringify`), and subclasses override it as
|
|
6
|
+
* `{ ...super.toJSON(), extra }`. To carry the chain depth and the redaction
|
|
7
|
+
* mode through those overrides, the caller parks a frame here immediately
|
|
8
|
+
* before invoking `toJSON()`, and `BaseError.toJSON` consumes it on entry.
|
|
9
|
+
* Serialization is synchronous, so the frame cannot leak across calls.
|
|
10
|
+
*
|
|
11
|
+
* Internal to `@zudojs/errors`: not re-exported from the package barrel.
|
|
12
|
+
*
|
|
13
|
+
* @module base/core/baseError.serialize
|
|
14
|
+
*/
|
|
15
|
+
import { redactCauseValue } from "./errorCause.redact.js";
|
|
16
|
+
/**
|
|
17
|
+
* Maximum depth of the cause chain included in serialized output, counted
|
|
18
|
+
* across the WHOLE chain (BaseError and native causes alike).
|
|
19
|
+
*/
|
|
20
|
+
export const MAX_CAUSE_DEPTH = 8;
|
|
21
|
+
const DEFAULT_FRAME = Object.freeze({
|
|
22
|
+
depth: 0,
|
|
23
|
+
redact: true,
|
|
24
|
+
});
|
|
25
|
+
let pendingFrame;
|
|
26
|
+
/** Errors currently being serialized (guards against cyclic cause chains). */
|
|
27
|
+
const serializing = new WeakSet();
|
|
28
|
+
/** Serialized objects genuinely produced by a BaseError's `toJSON`. */
|
|
29
|
+
const producedByBaseError = new WeakSet();
|
|
30
|
+
/** Consumes the frame parked for the current `toJSON()` call. */
|
|
31
|
+
export function takeSerializationFrame() {
|
|
32
|
+
const frame = pendingFrame ?? DEFAULT_FRAME;
|
|
33
|
+
pendingFrame = undefined;
|
|
34
|
+
return frame;
|
|
35
|
+
}
|
|
36
|
+
/** Marks an error as in-progress for cycle detection; returns a release. */
|
|
37
|
+
export function beginSerializing(error) {
|
|
38
|
+
if (serializing.has(error))
|
|
39
|
+
return undefined;
|
|
40
|
+
serializing.add(error);
|
|
41
|
+
return () => serializing.delete(error);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Calls `error.toJSON()` with an explicit frame and records the result as a
|
|
45
|
+
* genuine serialized BaseError.
|
|
46
|
+
*/
|
|
47
|
+
export function toJSONWithFrame(error, frame) {
|
|
48
|
+
pendingFrame = frame;
|
|
49
|
+
try {
|
|
50
|
+
const result = error.toJSON();
|
|
51
|
+
producedByBaseError.add(result);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
pendingFrame = undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Whether `value` came from a BaseError's `toJSON` via this module, as
|
|
60
|
+
* opposed to an arbitrary object that merely has the same field names.
|
|
61
|
+
*/
|
|
62
|
+
export function isGenuineSerializedBaseError(value) {
|
|
63
|
+
return producedByBaseError.has(value);
|
|
64
|
+
}
|
|
65
|
+
function isBaseErrorLike(value, brand) {
|
|
66
|
+
return (value instanceof Error &&
|
|
67
|
+
value[brand] === true &&
|
|
68
|
+
typeof value.toJSON === "function");
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Serializes one cause at `depth`, carrying the depth into BaseError causes
|
|
72
|
+
* so the limit applies across the whole chain. Recursion is bounded by
|
|
73
|
+
* {@link MAX_CAUSE_DEPTH}, so even a 20 000-deep chain cannot overflow the
|
|
74
|
+
* stack.
|
|
75
|
+
*/
|
|
76
|
+
export function serializeCauseAt(cause, depth, redact, brand) {
|
|
77
|
+
if (depth > MAX_CAUSE_DEPTH)
|
|
78
|
+
return "[MaxDepth]";
|
|
79
|
+
if (isBaseErrorLike(cause, brand)) {
|
|
80
|
+
if (serializing.has(cause))
|
|
81
|
+
return "[Circular]";
|
|
82
|
+
return toJSONWithFrame(cause, { depth, redact });
|
|
83
|
+
}
|
|
84
|
+
if (cause instanceof Error) {
|
|
85
|
+
const release = beginSerializing(cause);
|
|
86
|
+
if (release === undefined)
|
|
87
|
+
return "[Circular]";
|
|
88
|
+
try {
|
|
89
|
+
return {
|
|
90
|
+
name: cause.name,
|
|
91
|
+
message: cause.message,
|
|
92
|
+
...(cause.stack ? { stack: cause.stack } : {}),
|
|
93
|
+
...(cause.cause !== undefined
|
|
94
|
+
? { cause: serializeCauseAt(cause.cause, depth + 1, redact, brand) }
|
|
95
|
+
: {}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
release();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (cause !== null && typeof cause === "object") {
|
|
103
|
+
if (serializing.has(cause))
|
|
104
|
+
return "[Circular]";
|
|
105
|
+
return redact ? redactCauseValue(cause, undefined) : cause;
|
|
106
|
+
}
|
|
107
|
+
return cause;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=baseError.serialize.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redaction of non-BaseError cause values (plain objects, arrays).
|
|
3
|
+
*
|
|
4
|
+
* Internal to `@zudojs/errors`: shared by `BaseError.toJSON` and
|
|
5
|
+
* `ErrorSerializer`, not re-exported from the package barrel.
|
|
6
|
+
*
|
|
7
|
+
* @module base/core/errorCause.redact
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Redacts sensitive keys inside a record without changing its shape:
|
|
11
|
+
* primitives, dates and class instances are kept, plain objects and arrays
|
|
12
|
+
* are walked, cycles stop at `"[Circular]"`, prototype keys are dropped.
|
|
13
|
+
*
|
|
14
|
+
* The walk is depth-bounded exactly as metadata cloning is: a subtree past
|
|
15
|
+
* {@link MAX_METADATA_DEPTH} becomes `"[MaxDepth]"`. Without that bound a
|
|
16
|
+
* parsed request body attached as a cause could overflow the stack *inside*
|
|
17
|
+
* the serializer, turning a logged error into an uncaught `RangeError`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function redactCauseFields(fields: Record<string, unknown>, pattern: RegExp | undefined, seen?: WeakSet<object>, depth?: number): Record<string, unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* Redacts one cause value: arrays and plain objects are walked, every other
|
|
22
|
+
* value is returned unchanged. Walking stops at {@link MAX_METADATA_DEPTH}.
|
|
23
|
+
*/
|
|
24
|
+
export declare function redactCauseValue(value: unknown, pattern: RegExp | undefined, seen?: WeakSet<object>, depth?: number): unknown;
|
|
25
|
+
//# sourceMappingURL=errorCause.redact.d.ts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redaction of non-BaseError cause values (plain objects, arrays).
|
|
3
|
+
*
|
|
4
|
+
* Internal to `@zudojs/errors`: shared by `BaseError.toJSON` and
|
|
5
|
+
* `ErrorSerializer`, not re-exported from the package barrel.
|
|
6
|
+
*
|
|
7
|
+
* @module base/core/errorCause.redact
|
|
8
|
+
*/
|
|
9
|
+
import { isForbiddenMetadataKey, isSensitiveMetadataKey, MAX_METADATA_DEPTH, REDACTED_METADATA_VALUE, } from "./errorMetadata.core.js";
|
|
10
|
+
/** Marker substituted for a cause subtree deeper than {@link MAX_METADATA_DEPTH}. */
|
|
11
|
+
const MAX_DEPTH_VALUE = "[MaxDepth]";
|
|
12
|
+
/** Plain objects (Object.prototype or null prototype) are walked; anything else is kept as-is. */
|
|
13
|
+
function isPlainRecord(value) {
|
|
14
|
+
if (value === null || typeof value !== "object")
|
|
15
|
+
return false;
|
|
16
|
+
const proto = Object.getPrototypeOf(value);
|
|
17
|
+
return proto === Object.prototype || proto === null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Redacts sensitive keys inside a record without changing its shape:
|
|
21
|
+
* primitives, dates and class instances are kept, plain objects and arrays
|
|
22
|
+
* are walked, cycles stop at `"[Circular]"`, prototype keys are dropped.
|
|
23
|
+
*
|
|
24
|
+
* The walk is depth-bounded exactly as metadata cloning is: a subtree past
|
|
25
|
+
* {@link MAX_METADATA_DEPTH} becomes `"[MaxDepth]"`. Without that bound a
|
|
26
|
+
* parsed request body attached as a cause could overflow the stack *inside*
|
|
27
|
+
* the serializer, turning a logged error into an uncaught `RangeError`.
|
|
28
|
+
*/
|
|
29
|
+
export function redactCauseFields(fields, pattern, seen = new WeakSet(), depth = 0) {
|
|
30
|
+
const result = {};
|
|
31
|
+
for (const key of Object.keys(fields)) {
|
|
32
|
+
if (isForbiddenMetadataKey(key))
|
|
33
|
+
continue;
|
|
34
|
+
const sensitive = pattern === undefined
|
|
35
|
+
? isSensitiveMetadataKey(key)
|
|
36
|
+
: isSensitiveMetadataKey(key, pattern);
|
|
37
|
+
result[key] = sensitive
|
|
38
|
+
? REDACTED_METADATA_VALUE
|
|
39
|
+
: redactCauseValue(fields[key], pattern, seen, depth + 1);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Redacts one cause value: arrays and plain objects are walked, every other
|
|
45
|
+
* value is returned unchanged. Walking stops at {@link MAX_METADATA_DEPTH}.
|
|
46
|
+
*/
|
|
47
|
+
export function redactCauseValue(value, pattern, seen = new WeakSet(), depth = 0) {
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
if (depth > MAX_METADATA_DEPTH)
|
|
50
|
+
return MAX_DEPTH_VALUE;
|
|
51
|
+
if (seen.has(value))
|
|
52
|
+
return "[Circular]";
|
|
53
|
+
seen.add(value);
|
|
54
|
+
try {
|
|
55
|
+
return value.map((entry) => redactCauseValue(entry, pattern, seen, depth + 1));
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
seen.delete(value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!isPlainRecord(value))
|
|
62
|
+
return value;
|
|
63
|
+
if (depth > MAX_METADATA_DEPTH)
|
|
64
|
+
return MAX_DEPTH_VALUE;
|
|
65
|
+
if (seen.has(value))
|
|
66
|
+
return "[Circular]";
|
|
67
|
+
seen.add(value);
|
|
68
|
+
try {
|
|
69
|
+
return redactCauseFields(value, pattern, seen, depth);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
seen.delete(value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=errorCause.redact.js.map
|
|
@@ -114,6 +114,7 @@ export declare enum ErrorCode {
|
|
|
114
114
|
EVENT_REGISTRY_DISPOSED = "ERR_EVENT_REGISTRY_DISPOSED",
|
|
115
115
|
EVENT_BUS_DISPOSED = "ERR_EVENT_BUS_DISPOSED",
|
|
116
116
|
EVENT_SUBSCRIPTION_CLOSED = "ERR_EVENT_SUBSCRIPTION_CLOSED",
|
|
117
|
+
EVENT_LISTENER_LIMIT_EXCEEDED = "ERR_EVENT_LISTENER_LIMIT_EXCEEDED",
|
|
117
118
|
EVENT_TIMEOUT = "ERR_EVENT_TIMEOUT",
|
|
118
119
|
EVENT_SERIALIZATION_FAILED = "ERR_EVENT_SERIALIZATION_FAILED",
|
|
119
120
|
EVENT_DESERIALIZATION_FAILED = "ERR_EVENT_DESERIALIZATION_FAILED",
|
|
@@ -370,7 +371,16 @@ export declare enum ErrorCode {
|
|
|
370
371
|
HTTP_CLIENT_REQUEST_BODY_TIMEOUT = "ERR_HTTP_CLIENT_REQUEST_BODY_TIMEOUT",
|
|
371
372
|
HTTP_CLIENT_UNSUPPORTED_PROTOCOL = "ERR_HTTP_CLIENT_UNSUPPORTED_PROTOCOL",
|
|
372
373
|
HTTP_CLIENT_INVALID_HEADER = "ERR_HTTP_CLIENT_INVALID_HEADER",
|
|
373
|
-
HTTP_CLIENT_INVALID_CONTENT_TYPE = "ERR_HTTP_CLIENT_INVALID_CONTENT_TYPE"
|
|
374
|
+
HTTP_CLIENT_INVALID_CONTENT_TYPE = "ERR_HTTP_CLIENT_INVALID_CONTENT_TYPE",
|
|
375
|
+
OAUTH_CONFIGURATION_INVALID = "OAUTH_CONFIGURATION_INVALID",
|
|
376
|
+
OAUTH_ENDPOINT_NOT_ALLOWED = "OAUTH_ENDPOINT_NOT_ALLOWED",
|
|
377
|
+
OAUTH_REDIRECT_URI_NOT_ALLOWED = "OAUTH_REDIRECT_URI_NOT_ALLOWED",
|
|
378
|
+
OAUTH_STATE_MISMATCH = "OAUTH_STATE_MISMATCH",
|
|
379
|
+
OAUTH_PKCE_INVALID = "OAUTH_PKCE_INVALID",
|
|
380
|
+
OAUTH_PROVIDER_REJECTED = "OAUTH_PROVIDER_REJECTED",
|
|
381
|
+
OAUTH_PROVIDER_RESPONSE_INVALID = "OAUTH_PROVIDER_RESPONSE_INVALID",
|
|
382
|
+
OAUTH_RESPONSE_TOO_LARGE = "OAUTH_RESPONSE_TOO_LARGE",
|
|
383
|
+
OAUTH_NETWORK = "OAUTH_NETWORK"
|
|
374
384
|
}
|
|
375
385
|
/**
|
|
376
386
|
* Determines whether a value is a valid Zudojs error code.
|
|
@@ -120,6 +120,7 @@ export var ErrorCode;
|
|
|
120
120
|
ErrorCode["EVENT_REGISTRY_DISPOSED"] = "ERR_EVENT_REGISTRY_DISPOSED";
|
|
121
121
|
ErrorCode["EVENT_BUS_DISPOSED"] = "ERR_EVENT_BUS_DISPOSED";
|
|
122
122
|
ErrorCode["EVENT_SUBSCRIPTION_CLOSED"] = "ERR_EVENT_SUBSCRIPTION_CLOSED";
|
|
123
|
+
ErrorCode["EVENT_LISTENER_LIMIT_EXCEEDED"] = "ERR_EVENT_LISTENER_LIMIT_EXCEEDED";
|
|
123
124
|
ErrorCode["EVENT_TIMEOUT"] = "ERR_EVENT_TIMEOUT";
|
|
124
125
|
ErrorCode["EVENT_SERIALIZATION_FAILED"] = "ERR_EVENT_SERIALIZATION_FAILED";
|
|
125
126
|
ErrorCode["EVENT_DESERIALIZATION_FAILED"] = "ERR_EVENT_DESERIALIZATION_FAILED";
|
|
@@ -410,6 +411,17 @@ export var ErrorCode;
|
|
|
410
411
|
ErrorCode["HTTP_CLIENT_UNSUPPORTED_PROTOCOL"] = "ERR_HTTP_CLIENT_UNSUPPORTED_PROTOCOL";
|
|
411
412
|
ErrorCode["HTTP_CLIENT_INVALID_HEADER"] = "ERR_HTTP_CLIENT_INVALID_HEADER";
|
|
412
413
|
ErrorCode["HTTP_CLIENT_INVALID_CONTENT_TYPE"] = "ERR_HTTP_CLIENT_INVALID_CONTENT_TYPE";
|
|
414
|
+
// OAuth2 client. Values match `@zudojs/auth-oauth`'s `OAuthErrorCode`
|
|
415
|
+
// strings exactly (no `ERR_` prefix) so that package can adopt them.
|
|
416
|
+
ErrorCode["OAUTH_CONFIGURATION_INVALID"] = "OAUTH_CONFIGURATION_INVALID";
|
|
417
|
+
ErrorCode["OAUTH_ENDPOINT_NOT_ALLOWED"] = "OAUTH_ENDPOINT_NOT_ALLOWED";
|
|
418
|
+
ErrorCode["OAUTH_REDIRECT_URI_NOT_ALLOWED"] = "OAUTH_REDIRECT_URI_NOT_ALLOWED";
|
|
419
|
+
ErrorCode["OAUTH_STATE_MISMATCH"] = "OAUTH_STATE_MISMATCH";
|
|
420
|
+
ErrorCode["OAUTH_PKCE_INVALID"] = "OAUTH_PKCE_INVALID";
|
|
421
|
+
ErrorCode["OAUTH_PROVIDER_REJECTED"] = "OAUTH_PROVIDER_REJECTED";
|
|
422
|
+
ErrorCode["OAUTH_PROVIDER_RESPONSE_INVALID"] = "OAUTH_PROVIDER_RESPONSE_INVALID";
|
|
423
|
+
ErrorCode["OAUTH_RESPONSE_TOO_LARGE"] = "OAUTH_RESPONSE_TOO_LARGE";
|
|
424
|
+
ErrorCode["OAUTH_NETWORK"] = "OAUTH_NETWORK";
|
|
413
425
|
})(ErrorCode || (ErrorCode = {}));
|
|
414
426
|
/** Precomputed set of every error code value. */
|
|
415
427
|
const ERROR_CODE_VALUES = new Set(Object.values(ErrorCode));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base authentication-package error. Defaults to 401, category
|
|
3
|
+
* `authentication`, exposed; every default can be overridden.
|
|
4
|
+
*/
|
|
5
|
+
import { BaseError } from "../../base/core/baseError.core.js";
|
|
6
|
+
import { ErrorCategory } from "../../base/types/errorCategory.type.js";
|
|
7
|
+
import { ErrorCode } from "../../base/types/errorCode.type.js";
|
|
8
|
+
import { ErrorSeverity } from "../../base/types/errorSeverity.type.js";
|
|
9
|
+
import type { ErrorMetadata } from "../../base/core/errorMetadata.type.js";
|
|
10
|
+
/** Options accepted by {@link AuthError} and its subclasses. */
|
|
11
|
+
export interface AuthErrorOptions {
|
|
12
|
+
readonly code?: ErrorCode;
|
|
13
|
+
readonly category?: ErrorCategory;
|
|
14
|
+
readonly severity?: ErrorSeverity;
|
|
15
|
+
readonly statusCode?: number;
|
|
16
|
+
readonly expose?: boolean;
|
|
17
|
+
readonly isOperational?: boolean;
|
|
18
|
+
readonly metadata?: ErrorMetadata;
|
|
19
|
+
readonly cause?: unknown;
|
|
20
|
+
}
|
|
21
|
+
/** Base error for all auth-related failures. */
|
|
22
|
+
export declare class AuthError extends BaseError {
|
|
23
|
+
constructor(message: string, options?: AuthErrorOptions);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=authError.base.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base authentication-package error. Defaults to 401, category
|
|
3
|
+
* `authentication`, exposed; every default can be overridden.
|
|
4
|
+
*/
|
|
5
|
+
import { BaseError } from "../../base/core/baseError.core.js";
|
|
6
|
+
import { ErrorCategory } from "../../base/types/errorCategory.type.js";
|
|
7
|
+
import { ErrorCode } from "../../base/types/errorCode.type.js";
|
|
8
|
+
import { ErrorSeverity } from "../../base/types/errorSeverity.type.js";
|
|
9
|
+
/** Base error for all auth-related failures. */
|
|
10
|
+
export class AuthError extends BaseError {
|
|
11
|
+
constructor(message, options) {
|
|
12
|
+
super(message, {
|
|
13
|
+
code: options?.code ?? ErrorCode.AUTHENTICATION,
|
|
14
|
+
category: options?.category ?? ErrorCategory.AUTHENTICATION,
|
|
15
|
+
severity: options?.severity ?? ErrorSeverity.ERROR,
|
|
16
|
+
statusCode: options?.statusCode ?? 401,
|
|
17
|
+
expose: options?.expose ?? true,
|
|
18
|
+
...(options?.isOperational !== undefined
|
|
19
|
+
? { isOperational: options.isOperational }
|
|
20
|
+
: {}),
|
|
21
|
+
metadata: options?.metadata,
|
|
22
|
+
cause: options?.cause,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=authError.base.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base OAuth2 client error, on the shared hierarchy.
|
|
3
|
+
*
|
|
4
|
+
* Matches `@zudojs/auth-oauth`'s local `OAuthError` (name, `code`,
|
|
5
|
+
* `statusCode`, `expose` defaults), so that package can extend this class
|
|
6
|
+
* instead of `Error`. Codes are the `ErrorCode.OAUTH_*` members, whose values
|
|
7
|
+
* equal that package's `OAuthErrorCode` strings.
|
|
8
|
+
*/
|
|
9
|
+
import { BaseError } from "../../base/core/baseError.core.js";
|
|
10
|
+
/** Options accepted by {@link OAuthError}. */
|
|
11
|
+
export interface OAuthErrorOptions {
|
|
12
|
+
readonly code?: string;
|
|
13
|
+
readonly statusCode?: number;
|
|
14
|
+
readonly expose?: boolean;
|
|
15
|
+
readonly cause?: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Base error for every OAuth2 failure. Defaults: 400, exposed, code
|
|
19
|
+
* `OAUTH_PROVIDER_REJECTED`.
|
|
20
|
+
*/
|
|
21
|
+
export declare class OAuthError extends BaseError {
|
|
22
|
+
constructor(message: string, options?: OAuthErrorOptions);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=oauthError.base.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base OAuth2 client error, on the shared hierarchy.
|
|
3
|
+
*
|
|
4
|
+
* Matches `@zudojs/auth-oauth`'s local `OAuthError` (name, `code`,
|
|
5
|
+
* `statusCode`, `expose` defaults), so that package can extend this class
|
|
6
|
+
* instead of `Error`. Codes are the `ErrorCode.OAUTH_*` members, whose values
|
|
7
|
+
* equal that package's `OAuthErrorCode` strings.
|
|
8
|
+
*/
|
|
9
|
+
import { BaseError } from "../../base/core/baseError.core.js";
|
|
10
|
+
import { ErrorCategory } from "../../base/types/errorCategory.type.js";
|
|
11
|
+
import { ErrorCode } from "../../base/types/errorCode.type.js";
|
|
12
|
+
import { ErrorSeverity } from "../../base/types/errorSeverity.type.js";
|
|
13
|
+
/**
|
|
14
|
+
* Base error for every OAuth2 failure. Defaults: 400, exposed, code
|
|
15
|
+
* `OAUTH_PROVIDER_REJECTED`.
|
|
16
|
+
*/
|
|
17
|
+
export class OAuthError extends BaseError {
|
|
18
|
+
constructor(message, options) {
|
|
19
|
+
super(message, {
|
|
20
|
+
code: options?.code ?? ErrorCode.OAUTH_PROVIDER_REJECTED,
|
|
21
|
+
category: ErrorCategory.AUTHENTICATION,
|
|
22
|
+
severity: ErrorSeverity.ERROR,
|
|
23
|
+
statusCode: options?.statusCode ?? 400,
|
|
24
|
+
expose: options?.expose ?? true,
|
|
25
|
+
cause: options?.cause,
|
|
26
|
+
});
|
|
27
|
+
this.name = "OAuthError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=oauthError.base.js.map
|