@shirudo/ddd-kit 1.3.0 → 2.0.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.
@@ -0,0 +1,54 @@
1
+ import { PublicIssue } from '@shirudo/base-error';
2
+ import { PublicErrorView } from '@shirudo/base-error/presentation';
3
+
4
+ /** Details shape carried by the view for a {@link toPublicErrorView} result. */
5
+ interface PublicErrorViewDetails {
6
+ /** Whitelisted field issues, present only for a `ValidationError`. */
7
+ readonly issues: readonly PublicIssue[];
8
+ }
9
+ /** Options for {@link toPublicErrorView}. */
10
+ interface PublicErrorViewOptions {
11
+ /**
12
+ * BCP 47 locale tag stamped on the view. The built-in messages are English;
13
+ * pass a locale only when you supply your own message resolution upstream.
14
+ * Default `"en"`.
15
+ */
16
+ locale?: string;
17
+ }
18
+ /**
19
+ * Maps a kit error (or any caught value) to a base-error
20
+ * {@link PublicErrorView}: a **transport-neutral**, client-safe representation
21
+ * (`code`, `message`, `locale`, optional `details`). It is deliberately *not* a
22
+ * transport adapter: it carries no HTTP status, header, or exit code, because
23
+ * those are the consumer's concern. Feed the view into base-error's
24
+ * `defineProblemDetailsAdapter` (HTTP / RFC 9457), a gRPC status mapper, or a
25
+ * CLI exit-code table, whichever boundary you are at.
26
+ *
27
+ * Total over `unknown`: an unmapped or non-kit value degrades to a generic
28
+ * `INTERNAL_ERROR` view rather than leaking the technical message or throwing.
29
+ * The kit's class-based errors match by their pinned `error.name`, so it
30
+ * survives minification and duplicate installs (no `instanceof`). A
31
+ * `ValidationError` is detected by its `publicIssues()` whitelist (base-error
32
+ * names it after its code), and those issues ride along in `details.issues`.
33
+ *
34
+ * For richer, multi-locale messages, register the kit's errors in a base-error
35
+ * `PublicErrorRegistry` and use `PublicErrorPresenter` instead; this helper is
36
+ * the lean, single-locale default.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { toPublicErrorView } from "@shirudo/ddd-kit/presentation";
41
+ * import { defineProblemDetailsAdapter } from "@shirudo/base-error/problem-details";
42
+ *
43
+ * const adapter = defineProblemDetailsAdapter({
44
+ * definitions: { AggregateNotFoundError: { type: "about:blank", status: 404 } },
45
+ * fallback: { type: "about:blank", status: 500 },
46
+ * });
47
+ *
48
+ * const { body, status } = adapter.map(toPublicErrorView(error));
49
+ * return Response.json(body, { status });
50
+ * ```
51
+ */
52
+ declare function toPublicErrorView(error: unknown, options?: PublicErrorViewOptions): PublicErrorView<PublicErrorViewDetails>;
53
+
54
+ export { type PublicErrorViewDetails, type PublicErrorViewOptions, toPublicErrorView };
@@ -0,0 +1,45 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/presentation/public-error-view.ts
5
+ var KIT_PUBLIC_MESSAGES = {
6
+ AggregateNotFoundError: "The requested resource could not be found.",
7
+ ConcurrencyConflictError: "The resource was modified by another request. Please reload and try again.",
8
+ DuplicateAggregateError: "The resource already exists."
9
+ };
10
+ var VALIDATION_MESSAGE = "The submitted data is invalid.";
11
+ var FALLBACK_CODE = "INTERNAL_ERROR";
12
+ var FALLBACK_MESSAGE = "An unexpected error occurred.";
13
+ var DEFAULT_LOCALE = "en";
14
+ function toPublicErrorView(error, options = {}) {
15
+ const locale = options.locale ?? DEFAULT_LOCALE;
16
+ const name = errorName(error);
17
+ if (hasPublicIssues(error)) {
18
+ return {
19
+ code: name ?? "VALIDATION_FAILED",
20
+ message: VALIDATION_MESSAGE,
21
+ locale,
22
+ details: { issues: error.publicIssues() }
23
+ };
24
+ }
25
+ const message = name ? KIT_PUBLIC_MESSAGES[name] : void 0;
26
+ if (message === void 0) {
27
+ return { code: FALLBACK_CODE, message: FALLBACK_MESSAGE, locale };
28
+ }
29
+ return { code: name, message, locale };
30
+ }
31
+ __name(toPublicErrorView, "toPublicErrorView");
32
+ function errorName(error) {
33
+ if (typeof error !== "object" || error === null) return void 0;
34
+ const { name } = error;
35
+ return typeof name === "string" ? name : void 0;
36
+ }
37
+ __name(errorName, "errorName");
38
+ function hasPublicIssues(error) {
39
+ return typeof error.publicIssues === "function";
40
+ }
41
+ __name(hasPublicIssues, "hasPublicIssues");
42
+
43
+ export { toPublicErrorView };
44
+ //# sourceMappingURL=presentation.js.map
45
+ //# sourceMappingURL=presentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presentation/public-error-view.ts"],"names":[],"mappings":";;;;AAaA,IAAM,mBAAA,GAAwD;AAAA,EAC7D,sBAAA,EAAwB,4CAAA;AAAA,EACxB,wBAAA,EACC,4EAAA;AAAA,EACD,uBAAA,EAAyB;AAC1B,CAAA;AAGA,IAAM,kBAAA,GAAqB,gCAAA;AAE3B,IAAM,aAAA,GAAgB,gBAAA;AACtB,IAAM,gBAAA,GAAmB,+BAAA;AAEzB,IAAM,cAAA,GAAiB,IAAA;AAoDhB,SAAS,iBAAA,CACf,KAAA,EACA,OAAA,GAAkC,EAAC,EACO;AAC1C,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,cAAA;AACjC,EAAA,MAAM,IAAA,GAAO,UAAU,KAAK,CAAA;AAK5B,EAAA,IAAI,eAAA,CAAgB,KAAK,CAAA,EAAG;AAC3B,IAAA,OAAO;AAAA,MACN,MAAM,IAAA,IAAQ,mBAAA;AAAA,MACd,OAAA,EAAS,kBAAA;AAAA,MACT,MAAA;AAAA,MACA,OAAA,EAAS,EAAE,MAAA,EAAQ,KAAA,CAAM,cAAa;AAAE,KACzC;AAAA,EACD;AAEA,EAAA,MAAM,OAAA,GAAU,IAAA,GAAO,mBAAA,CAAoB,IAAI,CAAA,GAAI,MAAA;AACnD,EAAA,IAAI,YAAY,MAAA,EAAW;AAC1B,IAAA,OAAO,EAAE,IAAA,EAAM,aAAA,EAAe,OAAA,EAAS,kBAAkB,MAAA,EAAO;AAAA,EACjE;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAgB,OAAA,EAAS,MAAA,EAAO;AAChD;AAzBgB,MAAA,CAAA,iBAAA,EAAA,mBAAA,CAAA;AA4BhB,SAAS,UAAU,KAAA,EAAoC;AACtD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,MAAM,OAAO,MAAA;AACxD,EAAA,MAAM,EAAE,MAAK,GAAI,KAAA;AACjB,EAAA,OAAO,OAAO,IAAA,KAAS,QAAA,GAAW,IAAA,GAAO,MAAA;AAC1C;AAJS,MAAA,CAAA,SAAA,EAAA,WAAA,CAAA;AAOT,SAAS,gBACR,KAAA,EAC6C;AAC7C,EAAA,OACC,OAAQ,MAAqC,YAAA,KAAiB,UAAA;AAEhE;AANS,MAAA,CAAA,eAAA,EAAA,iBAAA,CAAA","file":"presentation.js","sourcesContent":["import type { PublicIssue } from \"@shirudo/base-error\";\nimport type { PublicErrorView } from \"@shirudo/base-error/presentation\";\n\n/**\n * Safe, client-facing English messages for the kit's known errors. They carry\n * no occurrence data (no id, version, or technical detail), so they never leak\n * across the boundary. This is the transport-neutral *presentation* layer: the\n * technical error classes stay free of these strings (removed from the core in\n * 2.0); here is their opt-in home.\n *\n * Keyed by the kit's pinned `error.name` (stable across minification and\n * duplicate installs), so matching does not depend on `instanceof`.\n */\nconst KIT_PUBLIC_MESSAGES: Readonly<Record<string, string>> = {\n\tAggregateNotFoundError: \"The requested resource could not be found.\",\n\tConcurrencyConflictError:\n\t\t\"The resource was modified by another request. Please reload and try again.\",\n\tDuplicateAggregateError: \"The resource already exists.\",\n};\n\n/** Message for a `ValidationError`, which is detected by capability, not name. */\nconst VALIDATION_MESSAGE = \"The submitted data is invalid.\";\n/** Public code and message used for any unmapped or non-kit error. */\nconst FALLBACK_CODE = \"INTERNAL_ERROR\";\nconst FALLBACK_MESSAGE = \"An unexpected error occurred.\";\n/** BCP 47 locale the built-in messages are written in. */\nconst DEFAULT_LOCALE = \"en\";\n\n/** Details shape carried by the view for a {@link toPublicErrorView} result. */\nexport interface PublicErrorViewDetails {\n\t/** Whitelisted field issues, present only for a `ValidationError`. */\n\treadonly issues: readonly PublicIssue[];\n}\n\n/** Options for {@link toPublicErrorView}. */\nexport interface PublicErrorViewOptions {\n\t/**\n\t * BCP 47 locale tag stamped on the view. The built-in messages are English;\n\t * pass a locale only when you supply your own message resolution upstream.\n\t * Default `\"en\"`.\n\t */\n\tlocale?: string;\n}\n\n/**\n * Maps a kit error (or any caught value) to a base-error\n * {@link PublicErrorView}: a **transport-neutral**, client-safe representation\n * (`code`, `message`, `locale`, optional `details`). It is deliberately *not* a\n * transport adapter: it carries no HTTP status, header, or exit code, because\n * those are the consumer's concern. Feed the view into base-error's\n * `defineProblemDetailsAdapter` (HTTP / RFC 9457), a gRPC status mapper, or a\n * CLI exit-code table, whichever boundary you are at.\n *\n * Total over `unknown`: an unmapped or non-kit value degrades to a generic\n * `INTERNAL_ERROR` view rather than leaking the technical message or throwing.\n * The kit's class-based errors match by their pinned `error.name`, so it\n * survives minification and duplicate installs (no `instanceof`). A\n * `ValidationError` is detected by its `publicIssues()` whitelist (base-error\n * names it after its code), and those issues ride along in `details.issues`.\n *\n * For richer, multi-locale messages, register the kit's errors in a base-error\n * `PublicErrorRegistry` and use `PublicErrorPresenter` instead; this helper is\n * the lean, single-locale default.\n *\n * @example\n * ```ts\n * import { toPublicErrorView } from \"@shirudo/ddd-kit/presentation\";\n * import { defineProblemDetailsAdapter } from \"@shirudo/base-error/problem-details\";\n *\n * const adapter = defineProblemDetailsAdapter({\n * definitions: { AggregateNotFoundError: { type: \"about:blank\", status: 404 } },\n * fallback: { type: \"about:blank\", status: 500 },\n * });\n *\n * const { body, status } = adapter.map(toPublicErrorView(error));\n * return Response.json(body, { status });\n * ```\n */\nexport function toPublicErrorView(\n\terror: unknown,\n\toptions: PublicErrorViewOptions = {},\n): PublicErrorView<PublicErrorViewDetails> {\n\tconst locale = options.locale ?? DEFAULT_LOCALE;\n\tconst name = errorName(error);\n\n\t// ValidationError (and subclasses) are detected by the publicIssues()\n\t// whitelist, not by name: base-error names a ValidationError after its code\n\t// (\"VALIDATION_FAILED\"), so a name match would miss it.\n\tif (hasPublicIssues(error)) {\n\t\treturn {\n\t\t\tcode: name ?? \"VALIDATION_FAILED\",\n\t\t\tmessage: VALIDATION_MESSAGE,\n\t\t\tlocale,\n\t\t\tdetails: { issues: error.publicIssues() },\n\t\t};\n\t}\n\n\tconst message = name ? KIT_PUBLIC_MESSAGES[name] : undefined;\n\tif (message === undefined) {\n\t\treturn { code: FALLBACK_CODE, message: FALLBACK_MESSAGE, locale };\n\t}\n\n\treturn { code: name as string, message, locale };\n}\n\n/** Reads a string `name` off a caught value without assuming it is an Error. */\nfunction errorName(error: unknown): string | undefined {\n\tif (typeof error !== \"object\" || error === null) return undefined;\n\tconst { name } = error as { name?: unknown };\n\treturn typeof name === \"string\" ? name : undefined;\n}\n\n/** Duck-types base-error's `ValidationError.publicIssues()` accessor. */\nfunction hasPublicIssues(\n\terror: unknown,\n): error is { publicIssues(): PublicIssue[] } {\n\treturn (\n\t\ttypeof (error as { publicIssues?: unknown }).publicIssues === \"function\"\n\t);\n}\n"]}
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as IAggregateRoot, a as Id, A as AnyDomainEvent } from './aggregate-BGdgvqKh.js';
1
+ import { I as IAggregateRoot, a as Id, A as AnyDomainEvent } from './aggregate-CePTINEt.js';
2
2
  import '@shirudo/result';
3
3
  import '@shirudo/base-error';
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shirudo/ddd-kit",
3
- "version": "1.3.0",
3
+ "version": "2.0.0",
4
4
  "description": "Composable TypeScript toolkit for tactical DDD",
5
5
  "type": "module",
6
6
  "repository": {
@@ -11,22 +11,29 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "exports": {
13
13
  ".": {
14
- "import": "./dist/index.js",
15
- "types": "./dist/index.d.ts"
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
16
  },
17
17
  "./utils": {
18
- "import": "./dist/utils.js",
19
- "types": "./dist/utils.d.ts"
18
+ "types": "./dist/utils.d.ts",
19
+ "import": "./dist/utils.js"
20
20
  },
21
21
  "./http": {
22
- "import": "./dist/http.js",
23
- "types": "./dist/http.d.ts"
22
+ "types": "./dist/http.d.ts",
23
+ "import": "./dist/http.js"
24
+ },
25
+ "./presentation": {
26
+ "types": "./dist/presentation.d.ts",
27
+ "import": "./dist/presentation.js"
24
28
  },
25
29
  "./testing": {
26
- "import": "./dist/testing.js",
27
- "types": "./dist/testing.d.ts"
30
+ "types": "./dist/testing.d.ts",
31
+ "import": "./dist/testing.js"
28
32
  }
29
33
  },
34
+ "engines": {
35
+ "node": ">=20"
36
+ },
30
37
  "sideEffects": false,
31
38
  "files": [
32
39
  "dist"
@@ -56,7 +63,7 @@
56
63
  },
57
64
  "devDependencies": {
58
65
  "@biomejs/biome": "2.1.4",
59
- "@shirudo/base-error": "^5.0.0",
66
+ "@shirudo/base-error": "^7.1.1",
60
67
  "@shirudo/result": "^1.0.0",
61
68
  "tsup": "^8.5.0",
62
69
  "typedoc": "^0.28.19",
@@ -77,7 +84,7 @@
77
84
  "url": "https://github.com/shi-rudo/ddd-kit-ts/issues"
78
85
  },
79
86
  "peerDependencies": {
80
- "@shirudo/base-error": "^5.0.0",
87
+ "@shirudo/base-error": "^7.1.1",
81
88
  "@shirudo/result": "^1.0.0"
82
89
  }
83
90
  }