@shirudo/ddd-kit 2.2.0 → 3.0.0-rc.4

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.
@@ -0,0 +1,110 @@
1
+ //#region src/utils/array/deep-equal.d.ts
2
+ /**
3
+ * Performs a deep equality check between two values.
4
+ *
5
+ * This function compares values recursively, handling:
6
+ * - Primitives (with special handling for NaN)
7
+ * - Arrays (nested arrays supported)
8
+ * - Objects (plain objects and class instances)
9
+ * - TypedArrays (Uint8Array, Int32Array, etc.)
10
+ * - DataView
11
+ * - Maps and Sets
12
+ * - Dates and RegExp
13
+ * - Wrapper objects (Boolean, Number, String)
14
+ * - Circular references (detected and handled)
15
+ *
16
+ * @param a - The first value to compare
17
+ * @param b - The second value to compare
18
+ * @returns `true` if the values are deeply equal, `false` otherwise
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * deepEqual([1, 2, 3], [1, 2, 3]); // true
23
+ * deepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] }); // true
24
+ * deepEqual(NaN, NaN); // true
25
+ * deepEqual([1, 2], [1, 2, 3]); // false
26
+ * ```
27
+ */
28
+ declare function deepEqual(a: unknown, b: unknown): boolean;
29
+ //#endregion
30
+ //#region src/utils/array/deep-omit.d.ts
31
+ type DeepOmitKey = string | symbol;
32
+ type DeepOmitPathSegment = string | number | symbol;
33
+ interface DeepOmitOptions {
34
+ /**
35
+ * Keys to ignore everywhere in the object tree.
36
+ * Only applies to object properties, not Map/Set/TypedArray contents.
37
+ */
38
+ readonly ignoreKeys?: readonly DeepOmitKey[];
39
+ /**
40
+ * Fine-grained control: key + path (without current key).
41
+ * Example path: ["user", "meta", 0, "data"]
42
+ */
43
+ readonly ignoreKeyPredicate?: (key: DeepOmitKey, path: readonly DeepOmitPathSegment[]) => boolean;
44
+ }
45
+ /**
46
+ * Creates a deep copy of `value` with certain keys removed according to the
47
+ * provided rules.
48
+ *
49
+ * Walks the object tree and skips keys that match `ignoreKeys` /
50
+ * `ignoreKeyPredicate`. Built-in atomic types that `deepEqual` compares by
51
+ * value (Date, RegExp, Map, Set, TypedArrays, DataView) are cloned by type
52
+ * rather than walked, since their internal structure has no key filtering to
53
+ * apply. Types that `deepEqual` compares by reference (Error, ArrayBuffer,
54
+ * SharedArrayBuffer, Promise, WeakMap, WeakSet) are passed through by
55
+ * reference, so `deepEqualExcept(x, x)` stays reflexive. Cycles are
56
+ * preserved: a cycle `a → a` clones to `a' → a'`. Arrays retain sparse
57
+ * holes and all non-ignored own properties, including symbol keys.
58
+ *
59
+ * **Shared references.** Without `ignoreKeyPredicate`, an object reached
60
+ * via several paths dedupes to a single clone. With a predicate, each
61
+ * path gets its own clone, because the predicate may decide differently per
62
+ * path, so memoising the first path's result would be wrong. This is
63
+ * inherently exponential for diamond-shaped sharing (a node reachable
64
+ * via 2^n paths is cloned 2^n times); the walk aborts with a descriptive
65
+ * error after {@link PATH_SENSITIVE_VISIT_BUDGET} node visits instead of
66
+ * hanging the process.
67
+ *
68
+ * **Prototype-pollution safety.** `__proto__` and `constructor` keys
69
+ * encountered as *own* properties of the input (typical of `JSON.parse`
70
+ * output) are copied as inert data properties via `Object.defineProperty`
71
+ * so the clone graph cannot bleed into `Object.prototype`.
72
+ *
73
+ * **Class instances.** When the input is a class instance, the clone is
74
+ * built via `Object.create(proto)` so the prototype is preserved, but the
75
+ * constructor is NOT re-invoked, so class invariants enforced by the
76
+ * constructor are not re-checked. `deepOmit` is therefore best used for
77
+ * comparison/serialisation (`voEqualsExcept`, `deepEqualExcept`), not as
78
+ * a general-purpose clone for behaviour-carrying objects.
79
+ *
80
+ * @param value - The value to create a deep copy from
81
+ * @param options - Options specifying which keys to ignore
82
+ * @returns A deep copy of `value` with specified keys removed
83
+ */
84
+ declare function deepOmit<T>(value: T, options: DeepOmitOptions): T;
85
+ //#endregion
86
+ //#region src/utils/array/deep-equal-except.d.ts
87
+ type DeepEqualExceptOptions = DeepOmitOptions;
88
+ /**
89
+ * Performs a deep equality comparison between two values after omitting specified keys.
90
+ *
91
+ * This function first removes the specified keys from both values using `deepOmit`,
92
+ * then performs a deep equality check using `deepEqual`.
93
+ *
94
+ * @param a - The first value to compare
95
+ * @param b - The second value to compare
96
+ * @param options - Options specifying which keys to omit before comparison
97
+ * @returns `true` if the values are deeply equal after omitting specified keys, `false` otherwise
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * const obj1 = { id: 1, name: "Alice", updatedAt: "2024-01-01" };
102
+ * const obj2 = { id: 2, name: "Alice", updatedAt: "2024-01-02" };
103
+ *
104
+ * deepEqualExcept(obj1, obj2, { ignoreKeys: ["id", "updatedAt"] }); // true
105
+ * ```
106
+ */
107
+ declare function deepEqualExcept(a: unknown, b: unknown, options: DeepEqualExceptOptions): boolean;
108
+ //#endregion
109
+ export { DeepOmitPathSegment as a, DeepOmitOptions as i, deepEqualExcept as n, deepOmit as o, DeepOmitKey as r, deepEqual as s, DeepEqualExceptOptions as t };
110
+ //# sourceMappingURL=utils.d.ts.map
package/dist/http.d.ts CHANGED
@@ -1,69 +1,82 @@
1
- import { ValidationError } from '@shirudo/base-error';
2
- import { ProblemDetailsExtensions, ProblemDetails } from '@shirudo/base-error/problem-details';
1
+ import { PublicIssue, ValidationError } from "@shirudo/base-error";
2
+ import { ProblemDetailsResult, ToProblemContext } from "@shirudo/base-error/public-error";
3
3
 
4
- /** Extension member that carries the collected field issues. */
5
- type ValidationProblemMember = "errors" | "invalid-params";
4
+ //#region src/http/problem-details.d.ts
5
+ /** Details member carried by a {@link toProblemDetails} body. */
6
+ interface ValidationProblemDetails {
7
+ /** The whitelisted field issues, straight from `publicIssues()`. */
8
+ readonly issues: readonly PublicIssue[];
9
+ }
6
10
  /**
7
- * Options for {@link toProblemDetails}: the standard RFC 9457 members the
8
- * boundary may set, plus {@link member} to choose the wire key for the issues
9
- * and {@link extensions} for extra public members merged alongside them.
11
+ * Options for {@link toProblemDetails}: the transport members the boundary
12
+ * may set, plus {@link extensions} for extra public body members.
10
13
  */
11
- interface ValidationProblemOptions {
12
- /** URI reference identifying the problem type. Defaults to `"about:blank"`. */
13
- type?: string;
14
- /** Short, human-readable summary. Default `"Validation Failed"`. */
15
- title?: string;
16
- /** HTTP status code. Default `422`. */
17
- status?: number;
18
- /** Human-readable explanation specific to this occurrence. */
19
- detail?: string;
20
- /** URI reference identifying this specific occurrence. */
21
- instance?: string;
22
- /**
23
- * Extension member that carries the field issues. Default `"errors"`
24
- * (`{ message, path, code?, pointer? }` entries). RFC 9457 does not
25
- * standardize a multi-error member; `errors` is the common convention.
26
- */
27
- member?: ValidationProblemMember;
28
- /**
29
- * Extra public extension members merged alongside the issues. JSON-safe
30
- * by contract (RFC 9457 bodies must serialize); a trace id, for example,
31
- * is passed here, not as a recognized top-level field.
32
- */
33
- extensions?: ProblemDetailsExtensions;
14
+ interface ValidationProblemOptions<TExtensions extends object = Record<never, never>> {
15
+ /** URI reference identifying the problem type. Defaults to `"about:blank"`. */
16
+ type?: string;
17
+ /** Short, human-readable summary. Default `"Validation Failed"`. */
18
+ title?: string;
19
+ /** HTTP status code. Default `422`. */
20
+ status?: number;
21
+ /** Human-readable explanation specific to this occurrence. */
22
+ detail?: string;
23
+ /** URI reference identifying this specific occurrence. */
24
+ instance?: string;
25
+ /**
26
+ * Extra public extension members merged alongside the documented body
27
+ * members. Constrained by base-error's `toProblem` contract: JSON-safe,
28
+ * string-keyed, and free of the reserved member names, checked at
29
+ * compile time and re-validated at runtime (a colliding or
30
+ * non-JSON-safe set is dropped and recorded in `outcome.omitted`).
31
+ */
32
+ extensions?: ToProblemContext<TExtensions>["extensions"];
34
33
  }
35
34
  /**
36
- * Projects a base-error {@link ValidationError} to an RFC 9457 Problem Details
37
- * object with the collected field issues attached under an extension member.
38
- * The return type is base-error's own
39
- * {@link ProblemDetails} (from `@shirudo/base-error/problem-details`), so the
40
- * RFC 9457 shape stays a single source of truth across the ecosystem.
41
- *
42
- * base-error is **safe by default**: the issues only cross to a client through
43
- * the `publicIssues()` whitelist (`{ message, path, code?, pointer? }`, never
44
- * raw validator extras). This helper performs that explicit projection and
45
- * applies sensible validation defaults (`422`, `"Validation Failed"`), so the
46
- * common boundary case is a one-liner instead of a footgun. The full-fidelity
47
- * issues remain available for observability via `error.toLogObject()`.
35
+ * The kit-named result of {@link toProblemDetails}: base-error's
36
+ * `ProblemDetailsResult` specialized to the validation shortcut. Exists so
37
+ * consumers can annotate boundaries from the kit entry alone; importing
38
+ * base-error stays an opt-in, never a prerequisite.
39
+ */
40
+ type ValidationProblemResult<TExtensions extends object = Record<never, never>> = ProblemDetailsResult<ValidationProblemDetails, string, TExtensions>;
41
+ /**
42
+ * Projects a base-error {@link ValidationError} to an RFC 9457 Problem
43
+ * Details result by delegating to base-error's `toProblem` transport stage:
44
+ * one pipeline, one wire profile, one hardening implementation. The body
45
+ * carries the error's public `code`, and the whitelisted issues ride under
46
+ * `details.issues` (`{ message, path, code?, pointer? }`, never raw
47
+ * validator extras), the same shape `toPublicErrorView` uses.
48
48
  *
49
- * For the general error-to-Problem-Details mapping (a public-code catalog with
50
- * per-code `type` / `status`), use base-error's `defineProblemDetailsAdapter`
51
- * over a `PublicErrorView`. This helper is the narrow validation shortcut.
49
+ * All of `toProblem`'s wire-safety guarantees apply: the body is deeply
50
+ * frozen with a null prototype (cannot carry or receive prototype
51
+ * pollution), every member is JSON-safe (a non-serializable value drops
52
+ * that member and records it in `result.outcome.omitted` instead of
53
+ * corrupting the wire), and extensions cannot collide with the reserved
54
+ * members. The result also carries the HTTP `status` and ready-made
55
+ * `headers`, so the boundary does not restate them.
52
56
  *
53
57
  * This is a presentation/transport concern and ships from the opt-in
54
58
  * `@shirudo/ddd-kit/http` entry point: the core kit stays transport-free.
59
+ * For catalog-driven mapping across ALL your public errors use
60
+ * base-error's `definePublicErrors` + `project` + `toProblem` directly;
61
+ * this helper is the narrow validation shortcut.
55
62
  *
56
63
  * @example
57
64
  * ```ts
58
65
  * import { toProblemDetails } from "@shirudo/ddd-kit/http";
59
66
  *
60
67
  * if (result.isErr()) {
61
- * return Response.json(toProblemDetails(result.error), { status: 422 });
68
+ * const problem = toProblemDetails(result.error);
69
+ * return Response.json(problem.body, {
70
+ * status: problem.status,
71
+ * headers: problem.headers,
72
+ * });
62
73
  * }
63
- * // → { type: "about:blank", title: "Validation Failed", status: 422,
64
- * // errors: [{ message: "must be a valid email", path: ["email"], pointer: "email" }] }
74
+ * // body → { type: "about:blank", title: "Validation Failed", status: 422,
75
+ * // code: "VALIDATION_FAILED",
76
+ * // details: { issues: [{ message: "must be a valid email", ... }] } }
65
77
  * ```
66
78
  */
67
- declare function toProblemDetails(error: ValidationError, options?: ValidationProblemOptions): ProblemDetails<never, ProblemDetailsExtensions>;
68
-
69
- export { type ValidationProblemMember, type ValidationProblemOptions, toProblemDetails };
79
+ declare function toProblemDetails<TExtensions extends object = Record<never, never>>(error: ValidationError, options?: ValidationProblemOptions<TExtensions>): ValidationProblemResult<TExtensions>;
80
+ //#endregion
81
+ export { type ValidationProblemDetails, type ValidationProblemOptions, type ValidationProblemResult, toProblemDetails };
82
+ //# sourceMappingURL=http.d.ts.map
package/dist/http.js CHANGED
@@ -1,26 +1,60 @@
1
- var __defProp = Object.defineProperty;
2
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
1
+ import { toProblem } from "@shirudo/base-error/public-error";
3
2
 
4
- // src/http/problem-details.ts
3
+ //#region src/http/problem-details.ts
4
+ /**
5
+ * Projects a base-error {@link ValidationError} to an RFC 9457 Problem
6
+ * Details result by delegating to base-error's `toProblem` transport stage:
7
+ * one pipeline, one wire profile, one hardening implementation. The body
8
+ * carries the error's public `code`, and the whitelisted issues ride under
9
+ * `details.issues` (`{ message, path, code?, pointer? }`, never raw
10
+ * validator extras), the same shape `toPublicErrorView` uses.
11
+ *
12
+ * All of `toProblem`'s wire-safety guarantees apply: the body is deeply
13
+ * frozen with a null prototype (cannot carry or receive prototype
14
+ * pollution), every member is JSON-safe (a non-serializable value drops
15
+ * that member and records it in `result.outcome.omitted` instead of
16
+ * corrupting the wire), and extensions cannot collide with the reserved
17
+ * members. The result also carries the HTTP `status` and ready-made
18
+ * `headers`, so the boundary does not restate them.
19
+ *
20
+ * This is a presentation/transport concern and ships from the opt-in
21
+ * `@shirudo/ddd-kit/http` entry point: the core kit stays transport-free.
22
+ * For catalog-driven mapping across ALL your public errors use
23
+ * base-error's `definePublicErrors` + `project` + `toProblem` directly;
24
+ * this helper is the narrow validation shortcut.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { toProblemDetails } from "@shirudo/ddd-kit/http";
29
+ *
30
+ * if (result.isErr()) {
31
+ * const problem = toProblemDetails(result.error);
32
+ * return Response.json(problem.body, {
33
+ * status: problem.status,
34
+ * headers: problem.headers,
35
+ * });
36
+ * }
37
+ * // body → { type: "about:blank", title: "Validation Failed", status: 422,
38
+ * // code: "VALIDATION_FAILED",
39
+ * // details: { issues: [{ message: "must be a valid email", ... }] } }
40
+ * ```
41
+ */
5
42
  function toProblemDetails(error, options = {}) {
6
- const {
7
- member = "errors",
8
- extensions,
9
- type = "about:blank",
10
- title = "Validation Failed",
11
- status = 422,
12
- detail,
13
- instance
14
- } = options;
15
- const problem = { type, title, status };
16
- if (detail !== void 0) problem.detail = detail;
17
- if (instance !== void 0) problem.instance = instance;
18
- if (extensions) Object.assign(problem, extensions);
19
- problem[member] = error.publicIssues();
20
- return problem;
43
+ const { type = "about:blank", title = "Validation Failed", status = 422, detail, instance, extensions } = options;
44
+ return toProblem({
45
+ status,
46
+ type,
47
+ title
48
+ }, {
49
+ code: error.code,
50
+ details: { issues: error.publicIssues() }
51
+ }, {
52
+ detail,
53
+ instance,
54
+ extensions
55
+ });
21
56
  }
22
- __name(toProblemDetails, "toProblemDetails");
23
57
 
58
+ //#endregion
24
59
  export { toProblemDetails };
25
- //# sourceMappingURL=http.js.map
26
60
  //# sourceMappingURL=http.js.map
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/http/problem-details.ts"],"names":[],"mappings":";;;;AAuEO,SAAS,gBAAA,CACf,KAAA,EACA,OAAA,GAAoC,EAAC,EACa;AAClD,EAAA,MAAM;AAAA,IACL,MAAA,GAAS,QAAA;AAAA,IACT,UAAA;AAAA,IACA,IAAA,GAAO,aAAA;AAAA,IACP,KAAA,GAAQ,mBAAA;AAAA,IACR,MAAA,GAAS,GAAA;AAAA,IACT,MAAA;AAAA,IACA;AAAA,GACD,GAAI,OAAA;AAEJ,EAAA,MAAM,OAAA,GAAmC,EAAE,IAAA,EAAM,KAAA,EAAO,MAAA,EAAO;AAC/D,EAAA,IAAI,MAAA,KAAW,MAAA,EAAW,OAAA,CAAQ,MAAA,GAAS,MAAA;AAC3C,EAAA,IAAI,QAAA,KAAa,MAAA,EAAW,OAAA,CAAQ,QAAA,GAAW,QAAA;AAC/C,EAAA,IAAI,UAAA,EAAY,MAAA,CAAO,MAAA,CAAO,OAAA,EAAS,UAAU,CAAA;AAIjD,EAAA,OAAA,CAAQ,MAAM,CAAA,GAAI,KAAA,CAAM,YAAA,EAAa;AAErC,EAAA,OAAO,OAAA;AACR;AAxBgB,MAAA,CAAA,gBAAA,EAAA,kBAAA,CAAA","file":"http.js","sourcesContent":["import type { ValidationError } from \"@shirudo/base-error\";\nimport type {\n\tProblemDetails,\n\tProblemDetailsExtensions,\n} from \"@shirudo/base-error/problem-details\";\n\n/** Extension member that carries the collected field issues. */\nexport type ValidationProblemMember = \"errors\" | \"invalid-params\";\n\n/**\n * Options for {@link toProblemDetails}: the standard RFC 9457 members the\n * boundary may set, plus {@link member} to choose the wire key for the issues\n * and {@link extensions} for extra public members merged alongside them.\n */\nexport interface ValidationProblemOptions {\n\t/** URI reference identifying the problem type. Defaults to `\"about:blank\"`. */\n\ttype?: string;\n\t/** Short, human-readable summary. Default `\"Validation Failed\"`. */\n\ttitle?: string;\n\t/** HTTP status code. Default `422`. */\n\tstatus?: number;\n\t/** Human-readable explanation specific to this occurrence. */\n\tdetail?: string;\n\t/** URI reference identifying this specific occurrence. */\n\tinstance?: string;\n\t/**\n\t * Extension member that carries the field issues. Default `\"errors\"`\n\t * (`{ message, path, code?, pointer? }` entries). RFC 9457 does not\n\t * standardize a multi-error member; `errors` is the common convention.\n\t */\n\tmember?: ValidationProblemMember;\n\t/**\n\t * Extra public extension members merged alongside the issues. JSON-safe\n\t * by contract (RFC 9457 bodies must serialize); a trace id, for example,\n\t * is passed here, not as a recognized top-level field.\n\t */\n\textensions?: ProblemDetailsExtensions;\n}\n\n/**\n * Projects a base-error {@link ValidationError} to an RFC 9457 Problem Details\n * object with the collected field issues attached under an extension member.\n * The return type is base-error's own\n * {@link ProblemDetails} (from `@shirudo/base-error/problem-details`), so the\n * RFC 9457 shape stays a single source of truth across the ecosystem.\n *\n * base-error is **safe by default**: the issues only cross to a client through\n * the `publicIssues()` whitelist (`{ message, path, code?, pointer? }`, never\n * raw validator extras). This helper performs that explicit projection and\n * applies sensible validation defaults (`422`, `\"Validation Failed\"`), so the\n * common boundary case is a one-liner instead of a footgun. The full-fidelity\n * issues remain available for observability via `error.toLogObject()`.\n *\n * For the general error-to-Problem-Details mapping (a public-code catalog with\n * per-code `type` / `status`), use base-error's `defineProblemDetailsAdapter`\n * over a `PublicErrorView`. This helper is the narrow validation shortcut.\n *\n * This is a presentation/transport concern and ships from the opt-in\n * `@shirudo/ddd-kit/http` entry point: the core kit stays transport-free.\n *\n * @example\n * ```ts\n * import { toProblemDetails } from \"@shirudo/ddd-kit/http\";\n *\n * if (result.isErr()) {\n * return Response.json(toProblemDetails(result.error), { status: 422 });\n * }\n * // → { type: \"about:blank\", title: \"Validation Failed\", status: 422,\n * // errors: [{ message: \"must be a valid email\", path: [\"email\"], pointer: \"email\" }] }\n * ```\n */\nexport function toProblemDetails(\n\terror: ValidationError,\n\toptions: ValidationProblemOptions = {},\n): ProblemDetails<never, ProblemDetailsExtensions> {\n\tconst {\n\t\tmember = \"errors\",\n\t\textensions,\n\t\ttype = \"about:blank\",\n\t\ttitle = \"Validation Failed\",\n\t\tstatus = 422,\n\t\tdetail,\n\t\tinstance,\n\t} = options;\n\n\tconst problem: Record<string, unknown> = { type, title, status };\n\tif (detail !== undefined) problem.detail = detail;\n\tif (instance !== undefined) problem.instance = instance;\n\tif (extensions) Object.assign(problem, extensions);\n\t// `PublicIssue.path` is `ReadonlyArray<PropertyKey>`, so the issue array is\n\t// not statically a `ProblemDetailsJsonValue`; the wire form only ever\n\t// carries string/number path segments, so this is JSON-safe in practice.\n\tproblem[member] = error.publicIssues();\n\n\treturn problem as ProblemDetails<never, ProblemDetailsExtensions>;\n}\n"]}
1
+ {"version":3,"file":"http.js","names":[],"sources":["../src/http/problem-details.ts"],"sourcesContent":["import type { PublicIssue, ValidationError } from \"@shirudo/base-error\";\nimport {\n\ttype ProblemDetailsResult,\n\ttype ToProblemContext,\n\ttoProblem,\n} from \"@shirudo/base-error/public-error\";\n\n/** Details member carried by a {@link toProblemDetails} body. */\nexport interface ValidationProblemDetails {\n\t/** The whitelisted field issues, straight from `publicIssues()`. */\n\treadonly issues: readonly PublicIssue[];\n}\n\n/**\n * Options for {@link toProblemDetails}: the transport members the boundary\n * may set, plus {@link extensions} for extra public body members.\n */\nexport interface ValidationProblemOptions<\n\tTExtensions extends object = Record<never, never>,\n> {\n\t/** URI reference identifying the problem type. Defaults to `\"about:blank\"`. */\n\ttype?: string;\n\t/** Short, human-readable summary. Default `\"Validation Failed\"`. */\n\ttitle?: string;\n\t/** HTTP status code. Default `422`. */\n\tstatus?: number;\n\t/** Human-readable explanation specific to this occurrence. */\n\tdetail?: string;\n\t/** URI reference identifying this specific occurrence. */\n\tinstance?: string;\n\t/**\n\t * Extra public extension members merged alongside the documented body\n\t * members. Constrained by base-error's `toProblem` contract: JSON-safe,\n\t * string-keyed, and free of the reserved member names, checked at\n\t * compile time and re-validated at runtime (a colliding or\n\t * non-JSON-safe set is dropped and recorded in `outcome.omitted`).\n\t */\n\textensions?: ToProblemContext<TExtensions>[\"extensions\"];\n}\n\n/**\n * The kit-named result of {@link toProblemDetails}: base-error's\n * `ProblemDetailsResult` specialized to the validation shortcut. Exists so\n * consumers can annotate boundaries from the kit entry alone; importing\n * base-error stays an opt-in, never a prerequisite.\n */\nexport type ValidationProblemResult<\n\tTExtensions extends object = Record<never, never>,\n> = ProblemDetailsResult<ValidationProblemDetails, string, TExtensions>;\n\n/**\n * Projects a base-error {@link ValidationError} to an RFC 9457 Problem\n * Details result by delegating to base-error's `toProblem` transport stage:\n * one pipeline, one wire profile, one hardening implementation. The body\n * carries the error's public `code`, and the whitelisted issues ride under\n * `details.issues` (`{ message, path, code?, pointer? }`, never raw\n * validator extras), the same shape `toPublicErrorView` uses.\n *\n * All of `toProblem`'s wire-safety guarantees apply: the body is deeply\n * frozen with a null prototype (cannot carry or receive prototype\n * pollution), every member is JSON-safe (a non-serializable value drops\n * that member and records it in `result.outcome.omitted` instead of\n * corrupting the wire), and extensions cannot collide with the reserved\n * members. The result also carries the HTTP `status` and ready-made\n * `headers`, so the boundary does not restate them.\n *\n * This is a presentation/transport concern and ships from the opt-in\n * `@shirudo/ddd-kit/http` entry point: the core kit stays transport-free.\n * For catalog-driven mapping across ALL your public errors use\n * base-error's `definePublicErrors` + `project` + `toProblem` directly;\n * this helper is the narrow validation shortcut.\n *\n * @example\n * ```ts\n * import { toProblemDetails } from \"@shirudo/ddd-kit/http\";\n *\n * if (result.isErr()) {\n * const problem = toProblemDetails(result.error);\n * return Response.json(problem.body, {\n * status: problem.status,\n * headers: problem.headers,\n * });\n * }\n * // body → { type: \"about:blank\", title: \"Validation Failed\", status: 422,\n * // code: \"VALIDATION_FAILED\",\n * // details: { issues: [{ message: \"must be a valid email\", ... }] } }\n * ```\n */\nexport function toProblemDetails<\n\tTExtensions extends object = Record<never, never>,\n>(\n\terror: ValidationError,\n\toptions: ValidationProblemOptions<TExtensions> = {},\n): ValidationProblemResult<TExtensions> {\n\tconst {\n\t\ttype = \"about:blank\",\n\t\ttitle = \"Validation Failed\",\n\t\tstatus = 422,\n\t\tdetail,\n\t\tinstance,\n\t\textensions,\n\t} = options;\n\n\treturn toProblem<ValidationProblemDetails, string, TExtensions>(\n\t\t{ status, type, title },\n\t\t{ code: error.code, details: { issues: error.publicIssues() } },\n\t\t{ detail, instance, extensions },\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwFA,SAAgB,iBAGf,OACA,UAAiD,CAAC,GACX;CACvC,MAAM,EACL,OAAO,eACP,QAAQ,qBACR,SAAS,KACT,QACA,UACA,eACG;CAEJ,OAAO,UACN;EAAE;EAAQ;EAAM;CAAM,GACtB;EAAE,MAAM,MAAM;EAAM,SAAS,EAAE,QAAQ,MAAM,aAAa,EAAE;CAAE,GAC9D;EAAE;EAAQ;EAAU;CAAW,CAChC;AACD"}