@zudojs/errors 1.0.0 → 1.1.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.
Files changed (73) hide show
  1. package/README.md +9 -2
  2. package/dist/base/core/baseError.core.d.ts +18 -4
  3. package/dist/base/core/baseError.core.js +31 -46
  4. package/dist/base/core/baseError.serialize.d.ts +53 -0
  5. package/dist/base/core/baseError.serialize.js +109 -0
  6. package/dist/base/core/errorCause.redact.d.ts +20 -0
  7. package/dist/base/core/errorCause.redact.js +64 -0
  8. package/dist/base/core/errorMetadata.core.js +14 -2
  9. package/dist/base/types/errorCode.type.d.ts +10 -1
  10. package/dist/base/types/errorCode.type.js +11 -0
  11. package/dist/domain/access/authError.base.d.ts +25 -0
  12. package/dist/domain/access/authError.base.js +26 -0
  13. package/dist/domain/access/index.d.ts +2 -0
  14. package/dist/domain/access/index.js +2 -0
  15. package/dist/domain/access/oauthError.base.d.ts +24 -0
  16. package/dist/domain/access/oauthError.base.js +30 -0
  17. package/dist/domain/cli/cliError.base.d.ts +24 -0
  18. package/dist/domain/cli/cliError.base.js +34 -0
  19. package/dist/domain/cli/index.d.ts +7 -0
  20. package/dist/domain/cli/index.js +7 -0
  21. package/dist/domain/constant/constantError.base.d.ts +21 -0
  22. package/dist/domain/constant/constantError.base.js +31 -0
  23. package/dist/domain/constant/index.d.ts +7 -0
  24. package/dist/domain/constant/index.js +7 -0
  25. package/dist/domain/cqrs/cqrsError.base.d.ts +14 -0
  26. package/dist/domain/cqrs/cqrsError.base.js +26 -0
  27. package/dist/domain/cqrs/index.d.ts +7 -0
  28. package/dist/domain/cqrs/index.js +7 -0
  29. package/dist/domain/index.d.ts +6 -0
  30. package/dist/domain/index.js +6 -0
  31. package/dist/domain/openapi/index.d.ts +7 -0
  32. package/dist/domain/openapi/index.js +7 -0
  33. package/dist/domain/openapi/openApiError.base.d.ts +20 -0
  34. package/dist/domain/openapi/openApiError.base.js +24 -0
  35. package/dist/domain/schema/schemaError.base.js +1 -1
  36. package/dist/domain/shared/domainError.helpers.d.ts +3 -2
  37. package/dist/domain/shared/domainError.helpers.js +32 -8
  38. package/dist/domain/state/validationError.base.d.ts +4 -3
  39. package/dist/domain/state/validationError.base.js +5 -4
  40. package/dist/domain/transaction/index.d.ts +10 -0
  41. package/dist/domain/transaction/index.js +10 -0
  42. package/dist/domain/transaction/transactionError.base.d.ts +18 -0
  43. package/dist/domain/transaction/transactionError.base.js +21 -0
  44. package/dist/domain/transaction/transactionError.misc.d.ts +26 -0
  45. package/dist/domain/transaction/transactionError.misc.js +47 -0
  46. package/dist/domain/transaction/transactionError.types.d.ts +33 -0
  47. package/dist/domain/transaction/transactionError.types.js +62 -0
  48. package/dist/domain/traversal/index.d.ts +7 -0
  49. package/dist/domain/traversal/index.js +7 -0
  50. package/dist/domain/traversal/traversalLimit.error.d.ts +37 -0
  51. package/dist/domain/traversal/traversalLimit.error.js +39 -0
  52. package/dist/infrastructure/httpPipeline/httpMiddleware.error.d.ts +26 -0
  53. package/dist/infrastructure/httpPipeline/httpMiddleware.error.js +43 -0
  54. package/dist/infrastructure/httpPipeline/httpRequestGuard.error.d.ts +21 -0
  55. package/dist/infrastructure/httpPipeline/httpRequestGuard.error.js +28 -0
  56. package/dist/infrastructure/httpPipeline/index.d.ts +8 -0
  57. package/dist/infrastructure/httpPipeline/index.js +8 -0
  58. package/dist/infrastructure/index.d.ts +1 -0
  59. package/dist/infrastructure/index.js +1 -0
  60. package/dist/infrastructure/middleware/index.d.ts +1 -0
  61. package/dist/infrastructure/middleware/index.js +1 -0
  62. package/dist/infrastructure/middleware/middlewareError.types.d.ts +28 -0
  63. package/dist/infrastructure/middleware/middlewareError.types.js +44 -0
  64. package/dist/system/index.d.ts +1 -0
  65. package/dist/system/index.js +1 -0
  66. package/dist/system/observability/index.d.ts +7 -0
  67. package/dist/system/observability/index.js +7 -0
  68. package/dist/system/observability/observabilityError.base.d.ts +16 -0
  69. package/dist/system/observability/observabilityError.base.js +23 -0
  70. package/dist/utils/errorMapper.mappers.d.ts +1 -1
  71. package/dist/utils/errorSerializer.core.d.ts +10 -2
  72. package/dist/utils/errorSerializer.core.js +33 -22
  73. package/package.json +5 -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, for trusted logs
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 (depth-limited and cycle-safe). Use `ErrorSerializer`
49
- * (`serializePublicError`) for anything sent to an untrusted client.
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
- /** Returns the error as a plain object for internal logging. */
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 `MAX_CAUSE_DEPTH` levels.
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 (depth-limited and cycle-safe). Use `ErrorSerializer`
101
- * (`serializePublicError`) for anything sent to an untrusted client.
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: serializeErrorMetadata(this.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
- if (serializing.has(this)) {
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 { ...base, cause: serializeErrorCause(this.cause, 1) };
128
+ return {
129
+ ...base,
130
+ cause: serializeCauseAt(this.cause, frame.depth + 1, frame.redact, BASE_ERROR_BRAND),
131
+ };
124
132
  }
125
133
  finally {
126
- serializing.delete(this);
134
+ release();
127
135
  }
128
136
  }
129
- /** Returns the error as a plain object for internal logging. */
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 `MAX_CAUSE_DEPTH` levels.
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
- if (depth > MAX_CAUSE_DEPTH)
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,20 @@
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
+ export declare function redactCauseFields(fields: Record<string, unknown>, pattern: RegExp | undefined, seen?: WeakSet<object>): Record<string, unknown>;
15
+ /**
16
+ * Redacts one cause value: arrays and plain objects are walked, every other
17
+ * value is returned unchanged.
18
+ */
19
+ export declare function redactCauseValue(value: unknown, pattern: RegExp | undefined, seen?: WeakSet<object>): unknown;
20
+ //# sourceMappingURL=errorCause.redact.d.ts.map
@@ -0,0 +1,64 @@
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, REDACTED_METADATA_VALUE, } from "./errorMetadata.core.js";
10
+ /** Plain objects (Object.prototype or null prototype) are walked; anything else is kept as-is. */
11
+ function isPlainRecord(value) {
12
+ if (value === null || typeof value !== "object")
13
+ return false;
14
+ const proto = Object.getPrototypeOf(value);
15
+ return proto === Object.prototype || proto === null;
16
+ }
17
+ /**
18
+ * Redacts sensitive keys inside a record without changing its shape:
19
+ * primitives, dates and class instances are kept, plain objects and arrays
20
+ * are walked, cycles stop at `"[Circular]"`, prototype keys are dropped.
21
+ */
22
+ export function redactCauseFields(fields, pattern, seen = new WeakSet()) {
23
+ const result = {};
24
+ for (const key of Object.keys(fields)) {
25
+ if (isForbiddenMetadataKey(key))
26
+ continue;
27
+ const sensitive = pattern === undefined
28
+ ? isSensitiveMetadataKey(key)
29
+ : isSensitiveMetadataKey(key, pattern);
30
+ result[key] = sensitive
31
+ ? REDACTED_METADATA_VALUE
32
+ : redactCauseValue(fields[key], pattern, seen);
33
+ }
34
+ return result;
35
+ }
36
+ /**
37
+ * Redacts one cause value: arrays and plain objects are walked, every other
38
+ * value is returned unchanged.
39
+ */
40
+ export function redactCauseValue(value, pattern, seen = new WeakSet()) {
41
+ if (Array.isArray(value)) {
42
+ if (seen.has(value))
43
+ return "[Circular]";
44
+ seen.add(value);
45
+ try {
46
+ return value.map((entry) => redactCauseValue(entry, pattern, seen));
47
+ }
48
+ finally {
49
+ seen.delete(value);
50
+ }
51
+ }
52
+ if (!isPlainRecord(value))
53
+ return value;
54
+ if (seen.has(value))
55
+ return "[Circular]";
56
+ seen.add(value);
57
+ try {
58
+ return redactCauseFields(value, pattern, seen);
59
+ }
60
+ finally {
61
+ seen.delete(value);
62
+ }
63
+ }
64
+ //# sourceMappingURL=errorCause.redact.js.map
@@ -19,9 +19,21 @@ export const REDACTED_METADATA_VALUE = "[REDACTED]";
19
19
  export function isForbiddenMetadataKey(key) {
20
20
  return FORBIDDEN_METADATA_KEYS.has(key);
21
21
  }
22
+ /**
23
+ * Returns a copy of `pattern` without the `g` and `y` flags.
24
+ *
25
+ * Those flags make `RegExp.prototype.test` advance `lastIndex`, so a shared
26
+ * sensitive-key pattern would redact a key on one call and let the same key
27
+ * through on the next. Every key check in this module goes through here.
28
+ */
29
+ function statelessPattern(pattern) {
30
+ if (!pattern.global && !pattern.sticky)
31
+ return pattern;
32
+ return new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, ""));
33
+ }
22
34
  /** Returns whether a metadata key looks like it carries a secret. */
23
35
  export function isSensitiveMetadataKey(key, pattern = SENSITIVE_METADATA_KEY_PATTERN) {
24
- return pattern.test(key);
36
+ return statelessPattern(pattern).test(key);
25
37
  }
26
38
  /** Returns whether a value is a plain object (Object.prototype or null prototype). */
27
39
  function isPlainObject(value) {
@@ -286,7 +298,7 @@ function sanitizeValue(value, seen, depth) {
286
298
  export function redactErrorMetadata(metadata, options = {}) {
287
299
  if (metadata === undefined || metadata === null)
288
300
  return Object.freeze({});
289
- const pattern = options.sensitiveKeyPattern ?? SENSITIVE_METADATA_KEY_PATTERN;
301
+ const pattern = statelessPattern(options.sensitiveKeyPattern ?? SENSITIVE_METADATA_KEY_PATTERN);
290
302
  const extraKeys = new Set((options.keys ?? []).map((key) => key.toLowerCase()));
291
303
  const replacement = options.replacement ?? REDACTED_METADATA_VALUE;
292
304
  const isSensitive = (key) => pattern.test(key) || extraKeys.has(key.toLowerCase());
@@ -370,7 +370,16 @@ export declare enum ErrorCode {
370
370
  HTTP_CLIENT_REQUEST_BODY_TIMEOUT = "ERR_HTTP_CLIENT_REQUEST_BODY_TIMEOUT",
371
371
  HTTP_CLIENT_UNSUPPORTED_PROTOCOL = "ERR_HTTP_CLIENT_UNSUPPORTED_PROTOCOL",
372
372
  HTTP_CLIENT_INVALID_HEADER = "ERR_HTTP_CLIENT_INVALID_HEADER",
373
- HTTP_CLIENT_INVALID_CONTENT_TYPE = "ERR_HTTP_CLIENT_INVALID_CONTENT_TYPE"
373
+ HTTP_CLIENT_INVALID_CONTENT_TYPE = "ERR_HTTP_CLIENT_INVALID_CONTENT_TYPE",
374
+ OAUTH_CONFIGURATION_INVALID = "OAUTH_CONFIGURATION_INVALID",
375
+ OAUTH_ENDPOINT_NOT_ALLOWED = "OAUTH_ENDPOINT_NOT_ALLOWED",
376
+ OAUTH_REDIRECT_URI_NOT_ALLOWED = "OAUTH_REDIRECT_URI_NOT_ALLOWED",
377
+ OAUTH_STATE_MISMATCH = "OAUTH_STATE_MISMATCH",
378
+ OAUTH_PKCE_INVALID = "OAUTH_PKCE_INVALID",
379
+ OAUTH_PROVIDER_REJECTED = "OAUTH_PROVIDER_REJECTED",
380
+ OAUTH_PROVIDER_RESPONSE_INVALID = "OAUTH_PROVIDER_RESPONSE_INVALID",
381
+ OAUTH_RESPONSE_TOO_LARGE = "OAUTH_RESPONSE_TOO_LARGE",
382
+ OAUTH_NETWORK = "OAUTH_NETWORK"
374
383
  }
375
384
  /**
376
385
  * Determines whether a value is a valid Zudojs error code.
@@ -410,6 +410,17 @@ export var ErrorCode;
410
410
  ErrorCode["HTTP_CLIENT_UNSUPPORTED_PROTOCOL"] = "ERR_HTTP_CLIENT_UNSUPPORTED_PROTOCOL";
411
411
  ErrorCode["HTTP_CLIENT_INVALID_HEADER"] = "ERR_HTTP_CLIENT_INVALID_HEADER";
412
412
  ErrorCode["HTTP_CLIENT_INVALID_CONTENT_TYPE"] = "ERR_HTTP_CLIENT_INVALID_CONTENT_TYPE";
413
+ // OAuth2 client. Values match `@zudojs/auth-oauth`'s `OAuthErrorCode`
414
+ // strings exactly (no `ERR_` prefix) so that package can adopt them.
415
+ ErrorCode["OAUTH_CONFIGURATION_INVALID"] = "OAUTH_CONFIGURATION_INVALID";
416
+ ErrorCode["OAUTH_ENDPOINT_NOT_ALLOWED"] = "OAUTH_ENDPOINT_NOT_ALLOWED";
417
+ ErrorCode["OAUTH_REDIRECT_URI_NOT_ALLOWED"] = "OAUTH_REDIRECT_URI_NOT_ALLOWED";
418
+ ErrorCode["OAUTH_STATE_MISMATCH"] = "OAUTH_STATE_MISMATCH";
419
+ ErrorCode["OAUTH_PKCE_INVALID"] = "OAUTH_PKCE_INVALID";
420
+ ErrorCode["OAUTH_PROVIDER_REJECTED"] = "OAUTH_PROVIDER_REJECTED";
421
+ ErrorCode["OAUTH_PROVIDER_RESPONSE_INVALID"] = "OAUTH_PROVIDER_RESPONSE_INVALID";
422
+ ErrorCode["OAUTH_RESPONSE_TOO_LARGE"] = "OAUTH_RESPONSE_TOO_LARGE";
423
+ ErrorCode["OAUTH_NETWORK"] = "OAUTH_NETWORK";
413
424
  })(ErrorCode || (ErrorCode = {}));
414
425
  /** Precomputed set of every error code value. */
415
426
  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
@@ -6,4 +6,6 @@
6
6
  export * from "./authentication.error.js";
7
7
  export * from "./authorization.error.js";
8
8
  export * from "./rateLimit.error.js";
9
+ export * from "./authError.base.js";
10
+ export * from "./oauthError.base.js";
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -6,4 +6,6 @@
6
6
  export * from "./authentication.error.js";
7
7
  export * from "./authorization.error.js";
8
8
  export * from "./rateLimit.error.js";
9
+ export * from "./authError.base.js";
10
+ export * from "./oauthError.base.js";
9
11
  //# sourceMappingURL=index.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