@shirudo/ddd-kit 2.0.0 → 3.0.0-rc.10

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/dist/http.d.ts CHANGED
@@ -1,69 +1,81 @@
1
- import { ValidationError } from '@shirudo/base-error';
2
- import { ProblemDetailsExtensions, ProblemDetails } from '@shirudo/base-error/problem-details';
3
-
4
- /** Extension member that carries the collected field issues. */
5
- type ValidationProblemMember = "errors" | "invalid-params";
1
+ import { PublicIssue, ValidationError } from "@shirudo/base-error";
2
+ import { ProblemDetailsResult, ToProblemContext } from "@shirudo/base-error/public-error";
3
+ //#region src/presentation/http/problem-details.d.ts
4
+ /** Details member carried by a {@link toProblemDetails} body. */
5
+ interface ValidationProblemDetails {
6
+ /** The whitelisted field issues, straight from `publicIssues()`. */
7
+ readonly issues: readonly PublicIssue[];
8
+ }
6
9
  /**
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.
10
+ * Options for {@link toProblemDetails}: the transport members the boundary
11
+ * may set, plus {@link extensions} for extra public body members.
10
12
  */
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;
13
+ interface ValidationProblemOptions<TExtensions extends object = Record<never, never>> {
14
+ /** URI reference identifying the problem type. Defaults to `"about:blank"`. */
15
+ type?: string;
16
+ /** Short, human-readable summary. Default `"Validation Failed"`. */
17
+ title?: string;
18
+ /** HTTP status code. Default `422`. */
19
+ status?: number;
20
+ /** Human-readable explanation specific to this occurrence. */
21
+ detail?: string;
22
+ /** URI reference identifying this specific occurrence. */
23
+ instance?: string;
24
+ /**
25
+ * Extra public extension members merged alongside the documented body
26
+ * members. Constrained by base-error's `toProblem` contract: JSON-safe,
27
+ * string-keyed, and free of the reserved member names, checked at
28
+ * compile time and re-validated at runtime (a colliding or
29
+ * non-JSON-safe set is dropped and recorded in `outcome.omitted`).
30
+ */
31
+ extensions?: ToProblemContext<TExtensions>["extensions"];
34
32
  }
35
33
  /**
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()`.
34
+ * The kit-named result of {@link toProblemDetails}: base-error's
35
+ * `ProblemDetailsResult` specialized to the validation shortcut. Exists so
36
+ * consumers can annotate boundaries from the kit entry alone; importing
37
+ * base-error stays an opt-in, never a prerequisite.
38
+ */
39
+ type ValidationProblemResult<TExtensions extends object = Record<never, never>> = ProblemDetailsResult<ValidationProblemDetails, string, TExtensions>;
40
+ /**
41
+ * Projects a base-error {@link ValidationError} to an RFC 9457 Problem
42
+ * Details result by delegating to base-error's `toProblem` transport stage:
43
+ * one pipeline, one wire profile, one hardening implementation. The body
44
+ * carries the error's public `code`, and the whitelisted issues ride under
45
+ * `details.issues` (`{ message, path, code?, pointer? }`, never raw
46
+ * validator extras), the same shape `toPublicErrorView` uses.
48
47
  *
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.
48
+ * All of `toProblem`'s wire-safety guarantees apply: the body is deeply
49
+ * frozen with a null prototype (cannot carry or receive prototype
50
+ * pollution), every member is JSON-safe (a non-serializable value drops
51
+ * that member and records it in `result.outcome.omitted` instead of
52
+ * corrupting the wire), and extensions cannot collide with the reserved
53
+ * members. The result also carries the HTTP `status` and ready-made
54
+ * `headers`, so the boundary does not restate them.
52
55
  *
53
56
  * This is a presentation/transport concern and ships from the opt-in
54
57
  * `@shirudo/ddd-kit/http` entry point: the core kit stays transport-free.
58
+ * For catalog-driven mapping across ALL your public errors use
59
+ * base-error's `definePublicErrors` + `project` + `toProblem` directly;
60
+ * this helper is the narrow validation shortcut.
55
61
  *
56
62
  * @example
57
63
  * ```ts
58
64
  * import { toProblemDetails } from "@shirudo/ddd-kit/http";
59
65
  *
60
66
  * if (result.isErr()) {
61
- * return Response.json(toProblemDetails(result.error), { status: 422 });
67
+ * const problem = toProblemDetails(result.error);
68
+ * return Response.json(problem.body, {
69
+ * status: problem.status,
70
+ * headers: problem.headers,
71
+ * });
62
72
  * }
63
- * // → { type: "about:blank", title: "Validation Failed", status: 422,
64
- * // errors: [{ message: "must be a valid email", path: ["email"], pointer: "email" }] }
73
+ * // body → { type: "about:blank", title: "Validation Failed", status: 422,
74
+ * // code: "VALIDATION_FAILED",
75
+ * // details: { issues: [{ message: "must be a valid email", ... }] } }
65
76
  * ```
66
77
  */
67
- declare function toProblemDetails(error: ValidationError, options?: ValidationProblemOptions): ProblemDetails<never, ProblemDetailsExtensions>;
68
-
69
- export { type ValidationProblemMember, type ValidationProblemOptions, toProblemDetails };
78
+ declare function toProblemDetails<TExtensions extends object = Record<never, never>>(error: ValidationError, options?: ValidationProblemOptions<TExtensions>): ValidationProblemResult<TExtensions>;
79
+ //#endregion
80
+ export { ValidationProblemDetails, ValidationProblemOptions, ValidationProblemResult, toProblemDetails };
81
+ //# 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/presentation/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/presentation/http/problem-details.ts"],"sourcesContent":["// HTTP / transport presenters. Opt-in entry point (`@shirudo/ddd-kit/http`)\n// so the core kit stays free of transport concerns. Import only when you map\n// domain results to HTTP responses.\nimport 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2FA,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"}