@ddd-ts-kit/http 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/error/problem.d.ts +3 -0
- package/dist/error/problem.d.ts.map +1 -1
- package/dist/error/to-problem.d.ts.map +1 -1
- package/dist/error/to-problem.js +10 -1
- package/dist/error/to-problem.js.map +1 -1
- package/dist/openapi/problem-schema.d.ts.map +1 -1
- package/dist/openapi/problem-schema.js +5 -0
- package/dist/openapi/problem-schema.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -60,6 +60,27 @@ A handler throws a `DomainError`; its `code` is mapped to a status in one place
|
|
|
60
60
|
"detail": "Order 7 does not exist.", "requestId": "..." }
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
A refusal built by a factory of a `ConceptError` carries facts, and they are private: the
|
|
64
|
+
document holds, under `facts`, only the ones its factory exposed by name, and no `facts` member
|
|
65
|
+
when it exposed none.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
static slotTaken(calendarId: string, startsAt: Date) {
|
|
69
|
+
return AppointmentError.refusal('SLOT_TAKEN', 'The slot is already held.', { calendarId, startsAt }).exposing('startsAt');
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{ "type": "urn:problem:slot-taken", "title": "Conflict", "status": 409, "code": "SLOT_TAKEN",
|
|
75
|
+
"detail": "The slot is already held.", "facts": { "startsAt": "2026-03-02T09:00:00.000Z" }, "requestId": "..." }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`facts` is an optional object of the `Problem` schema of the OpenAPI document. A client
|
|
79
|
+
branches on `code` and reads data from `facts`, never from `detail`, which is a sentence for a
|
|
80
|
+
human and may change. The message is answered as it was written: it says nothing the caller
|
|
81
|
+
did not send, and nothing about another person. A refusal mapped to a 5xx answers neither its
|
|
82
|
+
message nor its facts.
|
|
83
|
+
|
|
63
84
|
Defaults: `VALIDATION_FAILED` 422 (with `issues`, paths prefixed by the part:
|
|
64
85
|
`body.items.0.sku`), `NOT_FOUND` 404, `CONFLICT` 409, `INVARIANT_VIOLATION` 422, `UNAUTHORIZED`
|
|
65
86
|
401, `FORBIDDEN` 403. A domain code that is not mapped answers 422.
|
package/dist/error/problem.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RefusalFacts } from '@ddd-ts-kit/kernel';
|
|
1
2
|
import type { ValidationIssue } from '@ddd-ts-kit/validation';
|
|
2
3
|
export interface Problem {
|
|
3
4
|
readonly type: string;
|
|
@@ -6,6 +7,8 @@ export interface Problem {
|
|
|
6
7
|
readonly code: string;
|
|
7
8
|
readonly detail?: string;
|
|
8
9
|
readonly issues?: readonly ValidationIssue[];
|
|
10
|
+
/** The facts the refusal exposes, by name. Absent when its factory exposed none: facts are private by default. */
|
|
11
|
+
readonly facts?: RefusalFacts;
|
|
9
12
|
readonly requestId: string;
|
|
10
13
|
}
|
|
11
14
|
//# sourceMappingURL=problem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"problem.d.ts","sourceRoot":"","sources":["../../src/error/problem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
|
|
1
|
+
{"version":3,"file":"problem.d.ts","sourceRoot":"","sources":["../../src/error/problem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,kHAAkH;IAClH,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-problem.d.ts","sourceRoot":"","sources":["../../src/error/to-problem.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"to-problem.d.ts","sourceRoot":"","sources":["../../src/error/to-problem.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAsBzF"}
|
package/dist/error/to-problem.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DomainError } from '@ddd-ts-kit/kernel';
|
|
1
|
+
import { ConceptError, DomainError } from '@ddd-ts-kit/kernel';
|
|
2
2
|
import { ValidationError } from '@ddd-ts-kit/validation';
|
|
3
3
|
import { INTERNAL_ERROR_CODE } from './internal-error-code.js';
|
|
4
4
|
import { statusTitle } from './status-title.js';
|
|
@@ -18,9 +18,18 @@ export function toProblem(error, mapper, requestId) {
|
|
|
18
18
|
code: error.code,
|
|
19
19
|
detail: error.message,
|
|
20
20
|
...(error instanceof ValidationError ? { issues: error.issues } : {}),
|
|
21
|
+
...exposedFacts(error),
|
|
21
22
|
requestId,
|
|
22
23
|
};
|
|
23
24
|
}
|
|
25
|
+
/** Only what the factory of the refusal exposed by name: a fact never leaves the process by default. */
|
|
26
|
+
function exposedFacts(error) {
|
|
27
|
+
if (!(error instanceof ConceptError)) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
const facts = error.exposedFacts;
|
|
31
|
+
return Object.keys(facts).length === 0 ? {} : { facts };
|
|
32
|
+
}
|
|
24
33
|
// The message of an unexpected error quotes data and internals: it never leaves the process.
|
|
25
34
|
function internalProblem(requestId) {
|
|
26
35
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-problem.js","sourceRoot":"","sources":["../../src/error/to-problem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"to-problem.js","sourceRoot":"","sources":["../../src/error/to-problem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAqB,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,MAAmB,EAAE,SAAiB;IAC9E,IAAI,CAAC,CAAC,KAAK,YAAY,WAAW,CAAC,EAAE,CAAC;QACpC,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE3C,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAClB,8EAA8E;QAC9E,OAAO,EAAE,GAAG,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;IACjG,CAAC;IAED,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QAC/B,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC;QAC1B,MAAM;QACN,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,OAAO;QACrB,GAAG,CAAC,KAAK,YAAY,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,GAAG,YAAY,CAAC,KAAK,CAAC;QACtB,SAAS;KACV,CAAC;AACJ,CAAC;AAED,wGAAwG;AACxG,SAAS,YAAY,CAAC,KAAkB;IACtC,IAAI,CAAC,CAAC,KAAK,YAAY,YAAY,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC;IAEjC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;AAC1D,CAAC;AAED,6FAA6F;AAC7F,SAAS,eAAe,CAAC,SAAiB;IACxC,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,uBAAuB;QAC9B,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,mBAAmB;QACzB,SAAS;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"problem-schema.d.ts","sourceRoot":"","sources":["../../src/openapi/problem-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"problem-schema.d.ts","sourceRoot":"","sources":["../../src/openapi/problem-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,eAAO,MAAM,cAAc,EAAE,UA8B5B,CAAC"}
|
|
@@ -21,6 +21,11 @@ export const PROBLEM_SCHEMA = {
|
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
|
+
facts: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
description: 'The facts of the refusal its code exposes, by name. Absent when it exposes none.',
|
|
27
|
+
additionalProperties: true,
|
|
28
|
+
},
|
|
24
29
|
requestId: { type: 'string' },
|
|
25
30
|
},
|
|
26
31
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"problem-schema.js","sourceRoot":"","sources":["../../src/openapi/problem-schema.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,iEAAiE;IAC9E,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC;IAC1D,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sCAAsC,EAAE;QAC7E,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;QAC3B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sDAAsD,EAAE;QAC7F,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC1B,MAAM,EAAE;YACN,IAAI,EAAE,OAAO;YACb,WAAW,EAAE,6CAA6C;YAC1D,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC;gBACrC,UAAU,EAAE;oBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4CAA4C,EAAE;oBACnF,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACxB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC5B;aACF;SACF;QACD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC9B;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"problem-schema.js","sourceRoot":"","sources":["../../src/openapi/problem-schema.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,iEAAiE;IAC9E,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC;IAC1D,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sCAAsC,EAAE;QAC7E,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;QAC3B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sDAAsD,EAAE;QAC7F,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC1B,MAAM,EAAE;YACN,IAAI,EAAE,OAAO;YACb,WAAW,EAAE,6CAA6C;YAC1D,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC;gBACrC,UAAU,EAAE;oBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4CAA4C,EAAE;oBACnF,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACxB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC5B;aACF;SACF;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,kFAAkF;YAC/F,oBAAoB,EAAE,IAAI;SAC3B;QACD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC9B;CACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ddd-ts-kit/http",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Framework-agnostic route definitions with Express, Fastify and NestJS adapters and OpenAPI 3.1 generation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"dist"
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@ddd-ts-kit/kernel": "0.
|
|
50
|
-
"@ddd-ts-kit/validation": "0.
|
|
49
|
+
"@ddd-ts-kit/kernel": "0.3.0",
|
|
50
|
+
"@ddd-ts-kit/validation": "0.3.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@nestjs/common": ">=11.0.0",
|