@nice-code/error 0.2.9 → 0.2.11
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 +237 -5
- package/build/index.js +952 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,237 @@
|
|
|
1
|
-
# @nice-code/error
|
|
2
|
-
|
|
3
|
-
Typed, serializable errors with domain hierarchies, pattern matching, and safe transport across API boundaries.
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
# @nice-code/error
|
|
2
|
+
|
|
3
|
+
Typed, serializable errors with domain hierarchies, pattern matching, and safe transport across API boundaries.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @nice-code/error
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Core concepts
|
|
12
|
+
|
|
13
|
+
- **Domain** — a named group of related errors (`err_user_auth`, `err_payment`)
|
|
14
|
+
- **Schema** — maps error id strings to message, HTTP status, and optional typed context
|
|
15
|
+
- **Error id** — identifies what went wrong within a domain; multiple ids can be active on one error
|
|
16
|
+
- **Context** — structured data attached to an id (e.g. `{ username: string }`)
|
|
17
|
+
- **Hydration** — deserializing context from a JSON round-trip back to the original typed value
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Defining an error domain
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { defineNiceError, err } from "@nice-code/error";
|
|
25
|
+
|
|
26
|
+
export const err_auth = defineNiceError({
|
|
27
|
+
domain: "err_auth",
|
|
28
|
+
schema: {
|
|
29
|
+
// No context — second arg to fromId is not accepted
|
|
30
|
+
account_locked: err({
|
|
31
|
+
message: "Account is locked",
|
|
32
|
+
httpStatusCode: 403,
|
|
33
|
+
}),
|
|
34
|
+
|
|
35
|
+
// Required context — second arg to fromId is required
|
|
36
|
+
invalid_credentials: err<{ username: string }>({
|
|
37
|
+
message: ({ username }) => `Invalid credentials for: ${username}`,
|
|
38
|
+
httpStatusCode: 401,
|
|
39
|
+
context: { required: true },
|
|
40
|
+
}),
|
|
41
|
+
|
|
42
|
+
// Optional context
|
|
43
|
+
rate_limited: err<{ retryAfter: number }>({
|
|
44
|
+
message: (ctx) => ctx ? `Retry after ${ctx.retryAfter}s` : "Rate limited",
|
|
45
|
+
httpStatusCode: 429,
|
|
46
|
+
context: {},
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
} as const);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### `err()` — schema entry helper
|
|
53
|
+
|
|
54
|
+
| Schema entry | `fromId("id")` | `fromId("id", ctx)` |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `err()` / `err({ message, httpStatusCode })` | ✓ | ✗ |
|
|
57
|
+
| `err<C>({ context: {} })` | ✓ optional | ✓ optional |
|
|
58
|
+
| `err<C>({ context: { required: true } })` | ✗ | ✓ required |
|
|
59
|
+
|
|
60
|
+
### Custom serialization (non-JSON-safe context)
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
fs_error: err<{ cause: NodeJS.ErrnoException }>({
|
|
64
|
+
message: ({ cause }) => `FS error: ${cause.message}`,
|
|
65
|
+
context: {
|
|
66
|
+
required: true,
|
|
67
|
+
serialization: {
|
|
68
|
+
toJsonSerializable: ({ cause }) => ({ code: cause.code, message: cause.message }),
|
|
69
|
+
fromJsonSerializable: (obj) => ({ cause: Object.assign(new Error(obj.message), obj) }),
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Creating errors
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
// Single id — context required per schema
|
|
81
|
+
const err1 = err_auth.fromId("invalid_credentials", { username: "alice" });
|
|
82
|
+
|
|
83
|
+
// Multi-id in one error
|
|
84
|
+
const err2 = err_auth.fromContext({
|
|
85
|
+
invalid_credentials: { username: "bob" },
|
|
86
|
+
rate_limited: { retryAfter: 30 },
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Chain additional ids after construction
|
|
90
|
+
const err3 = err_auth.fromId("account_locked").addId("rate_limited");
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Reading errors
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
// Check and narrow type
|
|
99
|
+
if (err1.hasId("invalid_credentials")) {
|
|
100
|
+
const { username } = err1.getContext("invalid_credentials"); // typed
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check multiple ids at once
|
|
104
|
+
if (err2.hasOneOfIds(["invalid_credentials", "rate_limited"])) {
|
|
105
|
+
// ACTIVE_IDS narrowed to that subset
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Pattern match — calls first matching handler, returns its result
|
|
109
|
+
import { matchFirst } from "@nice-code/error";
|
|
110
|
+
|
|
111
|
+
const message = matchFirst(err1, {
|
|
112
|
+
invalid_credentials: ({ username }) => `Wrong password for ${username}`,
|
|
113
|
+
account_locked: () => "Account locked",
|
|
114
|
+
_: () => "Unknown auth error",
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Domain hierarchies
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
const err_app = defineNiceError({ domain: "err_app", schema: {} } as const);
|
|
124
|
+
|
|
125
|
+
const err_auth = err_app.createChildDomain({
|
|
126
|
+
domain: "err_auth",
|
|
127
|
+
schema: { /* ... */ },
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const err_auth_registration = err_auth.createChildDomain({
|
|
131
|
+
domain: "err_auth_registration",
|
|
132
|
+
schema: { /* ... */ },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Ancestry checks
|
|
136
|
+
err_app.isParentOf(err_auth_registration); // true
|
|
137
|
+
err_auth.isParentOf(err_auth_registration); // true
|
|
138
|
+
err_auth_registration.isParentOf(err_auth); // false
|
|
139
|
+
|
|
140
|
+
// Type guard — exact domain match only
|
|
141
|
+
err_auth.isExact(someError); // true only for err_auth domain
|
|
142
|
+
err_auth.isThisOrChild(someError); // true for err_auth and all children
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Serialization & transport
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
// Serialize to a plain JSON object (safe to send over HTTP)
|
|
151
|
+
const json = error.toJsonObject();
|
|
152
|
+
const str = error.toJsonString();
|
|
153
|
+
const response = error.toHttpResponse(); // Response with correct HTTP status
|
|
154
|
+
|
|
155
|
+
// On the receiving side — reconstruct from anything
|
|
156
|
+
import { castNiceError } from "@nice-code/error";
|
|
157
|
+
|
|
158
|
+
const caught = castNiceError(unknownValue); // always returns NiceError
|
|
159
|
+
|
|
160
|
+
if (err_auth.isExact(caught)) {
|
|
161
|
+
const hydrated = err_auth.hydrate(caught); // deserializes context
|
|
162
|
+
const { username } = hydrated.getContext("invalid_credentials");
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`castNiceError` handles: `NiceError` instances, serialized JSON objects, native `Error`, error-like objects, nullish values, and primitives.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Error handling with handlers
|
|
171
|
+
|
|
172
|
+
### Functional style (composable)
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { forDomain, forId, forIds } from "@nice-code/error";
|
|
176
|
+
|
|
177
|
+
const handled = error.handleWithSync([
|
|
178
|
+
forId(err_auth, "invalid_credentials", (h) => {
|
|
179
|
+
const { username } = h.getContext("invalid_credentials");
|
|
180
|
+
return res.status(401).json({ error: `Bad credentials for ${username}` });
|
|
181
|
+
}),
|
|
182
|
+
forIds(err_auth, ["account_locked", "rate_limited"], (h) => {
|
|
183
|
+
return res.status(h.httpStatusCode).json({ error: h.message });
|
|
184
|
+
}),
|
|
185
|
+
forDomain(err_payment, (h) => {
|
|
186
|
+
return res.status(500).json({ error: "Payment failed" });
|
|
187
|
+
}),
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
// Async handlers
|
|
191
|
+
await error.handleWithAsync([
|
|
192
|
+
forDomain(err_payment, async (h) => {
|
|
193
|
+
await db.logFailure(h.message);
|
|
194
|
+
await notify(h.toJsonObject());
|
|
195
|
+
}),
|
|
196
|
+
]);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Class-based style (reusable handler)
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { NiceErrorHandler } from "@nice-code/error";
|
|
203
|
+
|
|
204
|
+
const handler = new NiceErrorHandler()
|
|
205
|
+
.forId(err_auth, "invalid_credentials", (h) => "unauthorized")
|
|
206
|
+
.forDomain(err_payment, (h) => "payment_error")
|
|
207
|
+
.setDefaultHandler((e) => "unknown_error");
|
|
208
|
+
|
|
209
|
+
handler.handleErrorWithPromiseInspection(error);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Utility types
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import type { InferNiceError, InferNiceErrorHydrated } from "@nice-code/error";
|
|
218
|
+
|
|
219
|
+
type TAuthError = InferNiceError<typeof err_auth>;
|
|
220
|
+
// → NiceError<{ domain: "err_auth"; ... }, keyof schema>
|
|
221
|
+
|
|
222
|
+
type TAuthErrorHydrated = InferNiceErrorHydrated<typeof err_auth>;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Packing for transport
|
|
228
|
+
|
|
229
|
+
Useful when errors must travel through systems that don't preserve custom properties (e.g. some RPC layers).
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
import { EErrorPackType } from "@nice-code/error";
|
|
233
|
+
|
|
234
|
+
error.pack(EErrorPackType.msg_pack); // embeds JSON in error.message
|
|
235
|
+
error.pack(EErrorPackType.cause_pack); // embeds JSON in error.cause
|
|
236
|
+
error.unpack(); // restore original
|
|
237
|
+
```
|
package/build/index.js
CHANGED
|
@@ -1,19 +1,952 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
// src/internal/nice_core_errors.ts
|
|
2
|
+
import { StatusCodes } from "http-status-codes";
|
|
3
|
+
|
|
4
|
+
// src/utils/jsErrorOrCastJsError.ts
|
|
5
|
+
function jsErrorOrCastJsError(error, logMessage = true) {
|
|
6
|
+
if (error instanceof Error) {
|
|
7
|
+
return Object.assign(error, {
|
|
8
|
+
message: error.message
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
const message = error?.message ?? (typeof error === "string" ? error : "No error message found");
|
|
12
|
+
if (logMessage) {
|
|
13
|
+
console.error(`An unknown and unstructured error was thrown: ${message}`, error);
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
...new Error(message),
|
|
17
|
+
...error
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/NiceError/nice_error.static.ts
|
|
22
|
+
var DUR_OBJ_PACK_PREFIX = "NE_DUROBJ[[";
|
|
23
|
+
var DUR_OBJ_PACK_SUFFIX = "]]NE_DUROBJ";
|
|
24
|
+
|
|
25
|
+
// src/utils/packError/packError.enums.ts
|
|
26
|
+
var EErrorPackType;
|
|
27
|
+
((EErrorPackType2) => {
|
|
28
|
+
EErrorPackType2["no_pack"] = "no_pack";
|
|
29
|
+
EErrorPackType2["msg_pack"] = "msg_pack";
|
|
30
|
+
EErrorPackType2["cause_pack"] = "cause_pack";
|
|
31
|
+
})(EErrorPackType ||= {});
|
|
32
|
+
|
|
33
|
+
// src/utils/packError/causePack.ts
|
|
34
|
+
var causePack = (error) => {
|
|
35
|
+
error._packedState = { cause: error.cause, packedAs: "cause_pack" /* cause_pack */ };
|
|
36
|
+
error.cause = `${DUR_OBJ_PACK_PREFIX}${JSON.stringify(error.toJsonObject())}${DUR_OBJ_PACK_SUFFIX}`;
|
|
37
|
+
return error;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/utils/packError/msgPack.ts
|
|
41
|
+
var msgPack = (error) => {
|
|
42
|
+
error._packedState = { message: error.cleanMessage, packedAs: "msg_pack" /* msg_pack */ };
|
|
43
|
+
error.message = `${DUR_OBJ_PACK_PREFIX}${JSON.stringify(error.toJsonObject())}${DUR_OBJ_PACK_SUFFIX}`;
|
|
44
|
+
return error;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/utils/packError/packError.ts
|
|
48
|
+
var packError = (error, packType = "msg_pack") => {
|
|
49
|
+
if (packType === "no_pack") {
|
|
50
|
+
return error;
|
|
51
|
+
}
|
|
52
|
+
if (packType === "msg_pack") {
|
|
53
|
+
return msgPack(error);
|
|
54
|
+
}
|
|
55
|
+
return causePack(error);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// src/NiceError/NiceError.ts
|
|
59
|
+
class NiceError extends Error {
|
|
60
|
+
name = "NiceError";
|
|
61
|
+
def;
|
|
62
|
+
ids;
|
|
63
|
+
wasntNice;
|
|
64
|
+
httpStatusCode;
|
|
65
|
+
timeCreated;
|
|
66
|
+
cleanMessage;
|
|
67
|
+
originError;
|
|
68
|
+
_packedState;
|
|
69
|
+
_errorDataMap;
|
|
70
|
+
constructor(options) {
|
|
71
|
+
const messagePure = options.message;
|
|
72
|
+
const prefixedMessage = `[${options.def.domain}](${options.ids.join(",")}) ${messagePure}`;
|
|
73
|
+
super(prefixedMessage);
|
|
74
|
+
this.cleanMessage = messagePure;
|
|
75
|
+
this.def = options.def;
|
|
76
|
+
this.ids = options.ids;
|
|
77
|
+
this._errorDataMap = options.errorData;
|
|
78
|
+
this.wasntNice = options.wasntNice ?? false;
|
|
79
|
+
this.httpStatusCode = options.httpStatusCode ?? 500;
|
|
80
|
+
if (options.originError != null) {
|
|
81
|
+
this.originError = options.originError;
|
|
82
|
+
}
|
|
83
|
+
this.timeCreated = options.timeCreated ?? Date.now();
|
|
84
|
+
}
|
|
85
|
+
hasId(id) {
|
|
86
|
+
return id in this._errorDataMap;
|
|
87
|
+
}
|
|
88
|
+
hasOneOfIds(ids) {
|
|
89
|
+
return ids.some((id) => (id in this._errorDataMap));
|
|
90
|
+
}
|
|
91
|
+
get hasMultiple() {
|
|
92
|
+
return Object.keys(this._errorDataMap).length > 1;
|
|
93
|
+
}
|
|
94
|
+
getIds() {
|
|
95
|
+
return Object.keys(this._errorDataMap);
|
|
96
|
+
}
|
|
97
|
+
getContext(id) {
|
|
98
|
+
const errorData = this._errorDataMap[id];
|
|
99
|
+
const state = errorData?.contextState;
|
|
100
|
+
if (state == null) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (state.kind === "unhydrated") {
|
|
104
|
+
throw new Error(`[NiceError.getContext] Context for id "${String(id)}" is in the "unhydrated" state. ` + `The error was reconstructed from a serialized payload but has not been deserialized yet. ` + `Call \`niceErrorDefined.hydrate(error)\` to reconstruct the typed context.`);
|
|
105
|
+
}
|
|
106
|
+
return state.value;
|
|
107
|
+
}
|
|
108
|
+
getErrorDataForId(id) {
|
|
109
|
+
return this._errorDataMap[id];
|
|
110
|
+
}
|
|
111
|
+
withOriginError(error) {
|
|
112
|
+
this.originError = jsErrorOrCastJsError(error);
|
|
113
|
+
if (this._packedState?.packedAs !== "cause_pack" /* cause_pack */) {
|
|
114
|
+
this.cause = this.originError;
|
|
115
|
+
}
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
matches(other) {
|
|
119
|
+
const myDef = this.def;
|
|
120
|
+
const otherDef = other.def;
|
|
121
|
+
if (myDef.domain !== otherDef.domain)
|
|
122
|
+
return false;
|
|
123
|
+
const myIds = this.getIds().map(String).sort();
|
|
124
|
+
const otherIds = other.getIds().map(String).sort();
|
|
125
|
+
if (myIds.length !== otherIds.length)
|
|
126
|
+
return false;
|
|
127
|
+
return myIds.every((id, i) => id === otherIds[i]);
|
|
128
|
+
}
|
|
129
|
+
toJsonObject() {
|
|
130
|
+
const originError = this.originError ? {
|
|
131
|
+
name: this.originError.name,
|
|
132
|
+
message: this.originError.cleanMessage ?? this.originError.message,
|
|
133
|
+
stack: this.originError.stack,
|
|
134
|
+
cause: this.originError.cause
|
|
135
|
+
} : undefined;
|
|
136
|
+
const def = {
|
|
137
|
+
domain: this.def.domain,
|
|
138
|
+
allDomains: this.def.allDomains
|
|
139
|
+
};
|
|
140
|
+
if (this.def.defaultHttpStatusCode != null) {
|
|
141
|
+
def["defaultHttpStatusCode"] = this.def.defaultHttpStatusCode;
|
|
142
|
+
}
|
|
143
|
+
if (this.def.defaultMessage != null) {
|
|
144
|
+
def["defaultMessage"] = this.def.defaultMessage;
|
|
145
|
+
}
|
|
146
|
+
const errorData = {};
|
|
147
|
+
for (const rawId of Object.keys(this._errorDataMap)) {
|
|
148
|
+
const id = rawId;
|
|
149
|
+
const data = this._errorDataMap[id];
|
|
150
|
+
if (data == null)
|
|
151
|
+
continue;
|
|
152
|
+
let contextState;
|
|
153
|
+
if (data.contextState.kind === "hydrated" /* hydrated */) {
|
|
154
|
+
contextState = {
|
|
155
|
+
kind: "unhydrated" /* unhydrated */,
|
|
156
|
+
serialized: data.contextState.serialized
|
|
157
|
+
};
|
|
158
|
+
} else {
|
|
159
|
+
contextState = data.contextState;
|
|
160
|
+
}
|
|
161
|
+
errorData[id] = {
|
|
162
|
+
contextState,
|
|
163
|
+
message: data.cleanMessage ?? data.message,
|
|
164
|
+
httpStatusCode: data.httpStatusCode,
|
|
165
|
+
timeAdded: data.timeAdded
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
name: "NiceError",
|
|
170
|
+
def,
|
|
171
|
+
ids: this.ids,
|
|
172
|
+
errorData,
|
|
173
|
+
wasntNice: this.wasntNice,
|
|
174
|
+
message: this.cleanMessage,
|
|
175
|
+
httpStatusCode: this.httpStatusCode,
|
|
176
|
+
timeCreated: this.timeCreated,
|
|
177
|
+
...this.stack != null ? { stack: this.stack } : {},
|
|
178
|
+
originError
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
toJsonString() {
|
|
182
|
+
return JSON.stringify(this.toJsonObject());
|
|
183
|
+
}
|
|
184
|
+
toHttpResponse() {
|
|
185
|
+
return new Response(this.toJsonString(), {
|
|
186
|
+
status: this.httpStatusCode,
|
|
187
|
+
headers: { "Content-Type": "application/json" }
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
hydrate(definedNiceError) {
|
|
191
|
+
return definedNiceError.hydrate(this);
|
|
192
|
+
}
|
|
193
|
+
handleWithSync(handlerInput, handlerOptions = {}) {
|
|
194
|
+
const handlersArray = Array.isArray(handlerInput) ? handlerInput : [handlerInput];
|
|
195
|
+
for (const handler of handlersArray) {
|
|
196
|
+
const result = handler.handleErrorWithPromiseInspection(this, handlerOptions);
|
|
197
|
+
if (result.matched) {
|
|
198
|
+
if (result.isPromise) {
|
|
199
|
+
console.warn(`[NiceError.handleWith] Handler ${result.target.identifier} returned a Promise but was called via \`handleWith\` (synchronous). ` + `The Promise will not be awaited. Use \`handleWithAsync\` for async handlers.`);
|
|
200
|
+
}
|
|
201
|
+
return result.isPromise ? undefined : result.handlerResponse;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (handlerOptions.throwOnUnhandled === true)
|
|
205
|
+
throw this;
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
async handleWithAsync(handlerInput, handlerOptions = {}) {
|
|
209
|
+
const handlersArray = Array.isArray(handlerInput) ? handlerInput : [handlerInput];
|
|
210
|
+
for (const handler of handlersArray) {
|
|
211
|
+
const result = handler.handleErrorWithPromiseInspection(this, handlerOptions);
|
|
212
|
+
if (result.matched) {
|
|
213
|
+
return result.isPromise ? await result.handlerPromise : result.handlerResponse;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (handlerOptions.throwOnUnhandled === true)
|
|
217
|
+
throw this;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
get isPacked() {
|
|
221
|
+
return this._packedState != null;
|
|
222
|
+
}
|
|
223
|
+
pack(packType = "msg_pack") {
|
|
224
|
+
if (this.isPacked)
|
|
225
|
+
return this;
|
|
226
|
+
return packError(this, packType);
|
|
227
|
+
}
|
|
228
|
+
unpack() {
|
|
229
|
+
if (this._packedState == null)
|
|
230
|
+
return this;
|
|
231
|
+
if (this._packedState.packedAs === "msg_pack" /* msg_pack */) {
|
|
232
|
+
this.message = this._packedState.message;
|
|
233
|
+
}
|
|
234
|
+
if (this._packedState.packedAs === "cause_pack" /* cause_pack */) {
|
|
235
|
+
this.cause = this._packedState.cause;
|
|
236
|
+
}
|
|
237
|
+
this._packedState = undefined;
|
|
238
|
+
delete this._packedState;
|
|
239
|
+
return this;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/NiceError/NiceErrorHydrated.ts
|
|
244
|
+
class NiceErrorHydrated extends NiceError {
|
|
245
|
+
def;
|
|
246
|
+
niceErrorDefined;
|
|
247
|
+
constructor(options) {
|
|
248
|
+
super(options);
|
|
249
|
+
this.def = options.def;
|
|
250
|
+
this.niceErrorDefined = options.niceErrorDefined;
|
|
251
|
+
}
|
|
252
|
+
addContext(context) {
|
|
253
|
+
const newIds = Object.keys(context);
|
|
254
|
+
const newErrorData = {};
|
|
255
|
+
for (const id of newIds) {
|
|
256
|
+
newErrorData[id] = this.niceErrorDefined.reconcileErrorDataForId(id, context[id]);
|
|
257
|
+
}
|
|
258
|
+
const mergedErrorData = {
|
|
259
|
+
...this._errorDataMap,
|
|
260
|
+
...newErrorData
|
|
261
|
+
};
|
|
262
|
+
const mergedIds = Array.from(new Set([...this.getIds(), ...Object.keys(context)]));
|
|
263
|
+
return new NiceErrorHydrated({
|
|
264
|
+
def: this.def,
|
|
265
|
+
niceErrorDefined: this.niceErrorDefined,
|
|
266
|
+
ids: mergedIds,
|
|
267
|
+
errorData: mergedErrorData,
|
|
268
|
+
message: this.cleanMessage,
|
|
269
|
+
wasntNice: this.wasntNice,
|
|
270
|
+
httpStatusCode: this.httpStatusCode,
|
|
271
|
+
originError: this.originError
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
addId(...args) {
|
|
275
|
+
const [id, context] = args;
|
|
276
|
+
const reconciledData = this.niceErrorDefined.reconcileErrorDataForId(id, context);
|
|
277
|
+
const errorDataMap = {};
|
|
278
|
+
errorDataMap[id] = reconciledData;
|
|
279
|
+
const mergedContexts = {
|
|
280
|
+
...this._errorDataMap,
|
|
281
|
+
...errorDataMap
|
|
282
|
+
};
|
|
283
|
+
const mergedIds = Array.from(new Set([...this.getIds(), id]));
|
|
284
|
+
return new NiceErrorHydrated({
|
|
285
|
+
def: this.def,
|
|
286
|
+
niceErrorDefined: this.niceErrorDefined,
|
|
287
|
+
ids: mergedIds,
|
|
288
|
+
errorData: mergedContexts,
|
|
289
|
+
message: this.cleanMessage,
|
|
290
|
+
wasntNice: this.wasntNice,
|
|
291
|
+
httpStatusCode: this.httpStatusCode,
|
|
292
|
+
originError: this.originError
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/NiceErrorDefined/NiceErrorDefined.ts
|
|
298
|
+
class NiceErrorDomain {
|
|
299
|
+
domain;
|
|
300
|
+
allDomains;
|
|
301
|
+
defaultHttpStatusCode;
|
|
302
|
+
defaultMessage;
|
|
303
|
+
_schema;
|
|
304
|
+
_definedChildNiceErrors = [];
|
|
305
|
+
_definedParentNiceError;
|
|
306
|
+
_setPack;
|
|
307
|
+
_packAsFn;
|
|
308
|
+
constructor(definition) {
|
|
309
|
+
this.domain = definition.domain;
|
|
310
|
+
this.allDomains = definition.allDomains;
|
|
311
|
+
this._schema = definition.schema;
|
|
312
|
+
if (definition.packAs != null) {
|
|
313
|
+
this._packAsFn = definition.packAs;
|
|
314
|
+
}
|
|
315
|
+
if (definition.defaultHttpStatusCode != null) {
|
|
316
|
+
this.defaultHttpStatusCode = definition.defaultHttpStatusCode;
|
|
317
|
+
}
|
|
318
|
+
if (definition.defaultMessage != null) {
|
|
319
|
+
this.defaultMessage = definition.defaultMessage;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
createChildDomain(subErrorDef) {
|
|
323
|
+
const child = new NiceErrorDomain({
|
|
324
|
+
domain: subErrorDef.domain,
|
|
325
|
+
allDomains: [subErrorDef.domain, ...this.allDomains],
|
|
326
|
+
schema: subErrorDef.schema,
|
|
327
|
+
defaultHttpStatusCode: subErrorDef.defaultHttpStatusCode,
|
|
328
|
+
defaultMessage: subErrorDef.defaultMessage
|
|
329
|
+
});
|
|
330
|
+
this.addChildNiceErrorDefined(child);
|
|
331
|
+
child.addParentNiceErrorDefined(this);
|
|
332
|
+
if (subErrorDef.packAs != null) {
|
|
333
|
+
child._packAsFn = subErrorDef.packAs;
|
|
334
|
+
} else if (this._setPack) {
|
|
335
|
+
child.packAs(this._setPack);
|
|
336
|
+
} else if (this._packAsFn) {
|
|
337
|
+
child._packAsFn = this._packAsFn;
|
|
338
|
+
}
|
|
339
|
+
return child;
|
|
340
|
+
}
|
|
341
|
+
addParentNiceErrorDefined(parentError) {
|
|
342
|
+
if (this._definedParentNiceError?.domain === parentError.domain) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
this._definedParentNiceError = {
|
|
346
|
+
domain: parentError.domain,
|
|
347
|
+
definedError: parentError
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
addChildNiceErrorDefined(child) {
|
|
351
|
+
if (this._definedChildNiceErrors.some((linked) => linked.domain === child.domain)) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
this._definedChildNiceErrors.push({
|
|
355
|
+
domain: child.domain,
|
|
356
|
+
definedError: child
|
|
357
|
+
});
|
|
358
|
+
if (this._definedParentNiceError) {
|
|
359
|
+
this._definedParentNiceError.definedError.addChildNiceErrorDefined(child);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
packAs(pack) {
|
|
363
|
+
this._setPack = pack;
|
|
364
|
+
return this;
|
|
365
|
+
}
|
|
366
|
+
createError(input) {
|
|
367
|
+
const err = new NiceErrorHydrated(input);
|
|
368
|
+
const packType = this._setPack ?? this._packAsFn?.();
|
|
369
|
+
if (packType != null && packType !== "no_pack") {
|
|
370
|
+
return err.pack(packType);
|
|
371
|
+
}
|
|
372
|
+
return err;
|
|
373
|
+
}
|
|
374
|
+
hydrate(error) {
|
|
375
|
+
const errDef = error.def;
|
|
376
|
+
if (errDef.domain !== this.domain) {
|
|
377
|
+
throw new Error(`[NiceErrorDefined.hydrate] Domain mismatch: this definition is "${this.domain}" ` + `but the error belongs to "${errDef.domain}". ` + `Call \`niceErrorDefined.is(error)\` before hydrating to ensure compatibility.`);
|
|
378
|
+
}
|
|
379
|
+
const finalError = error instanceof NiceError ? error : new NiceError(error);
|
|
380
|
+
const reconciledErrorData = {};
|
|
381
|
+
for (const id of finalError.getIds()) {
|
|
382
|
+
const existingData = finalError.getErrorDataForId(id);
|
|
383
|
+
if (existingData == null)
|
|
384
|
+
continue;
|
|
385
|
+
let contextState = existingData.contextState;
|
|
386
|
+
if (contextState.kind === "unhydrated") {
|
|
387
|
+
const entry = this._schema[id];
|
|
388
|
+
const deserialize = entry?.context?.serialization?.fromJsonSerializable;
|
|
389
|
+
if (deserialize != null) {
|
|
390
|
+
contextState = {
|
|
391
|
+
kind: "hydrated" /* hydrated */,
|
|
392
|
+
value: deserialize(contextState.serialized),
|
|
393
|
+
serialized: contextState.serialized
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
reconciledErrorData[id] = {
|
|
398
|
+
contextState,
|
|
399
|
+
message: existingData.cleanMessage ?? existingData.message,
|
|
400
|
+
httpStatusCode: existingData.httpStatusCode,
|
|
401
|
+
timeAdded: existingData.timeAdded
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
return new NiceErrorHydrated({
|
|
405
|
+
def: this._buildDef(),
|
|
406
|
+
niceErrorDefined: this,
|
|
407
|
+
ids: finalError.ids,
|
|
408
|
+
errorData: reconciledErrorData,
|
|
409
|
+
message: finalError.cleanMessage ?? finalError.message,
|
|
410
|
+
httpStatusCode: finalError.httpStatusCode,
|
|
411
|
+
wasntNice: finalError.wasntNice,
|
|
412
|
+
originError: finalError.originError,
|
|
413
|
+
timeCreated: finalError.timeCreated
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
fromId(...args) {
|
|
417
|
+
const [id, context] = args;
|
|
418
|
+
const reconciledData = this.reconcileErrorDataForId(id, context);
|
|
419
|
+
const errorData = {};
|
|
420
|
+
errorData[id] = reconciledData;
|
|
421
|
+
return this.createError({
|
|
422
|
+
def: this._buildDef(),
|
|
423
|
+
niceErrorDefined: this,
|
|
424
|
+
ids: [id],
|
|
425
|
+
errorData,
|
|
426
|
+
message: reconciledData.cleanMessage ?? reconciledData.message,
|
|
427
|
+
httpStatusCode: reconciledData.httpStatusCode
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
fromContext(context) {
|
|
431
|
+
const ids = Object.keys(context);
|
|
432
|
+
if (ids.length === 0) {
|
|
433
|
+
throw new Error("[NiceErrorDefined.fromContext] context object must contain at least one error id.");
|
|
434
|
+
}
|
|
435
|
+
const errorData = {};
|
|
436
|
+
for (const id of ids) {
|
|
437
|
+
errorData[id] = this.reconcileErrorDataForId(id, context[id]);
|
|
438
|
+
}
|
|
439
|
+
const primaryId = ids[0];
|
|
440
|
+
return this.createError({
|
|
441
|
+
def: this._buildDef(),
|
|
442
|
+
niceErrorDefined: this,
|
|
443
|
+
ids,
|
|
444
|
+
errorData,
|
|
445
|
+
message: errorData[primaryId].cleanMessage ?? errorData[primaryId].message,
|
|
446
|
+
httpStatusCode: errorData[primaryId].httpStatusCode
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
isExact(error) {
|
|
450
|
+
if (!(error instanceof NiceError))
|
|
451
|
+
return false;
|
|
452
|
+
const errDef = error.def;
|
|
453
|
+
return errDef.domain === this.domain;
|
|
454
|
+
}
|
|
455
|
+
isThisOrChild(error) {
|
|
456
|
+
if (!(error instanceof NiceError))
|
|
457
|
+
return false;
|
|
458
|
+
const errDef = error.def;
|
|
459
|
+
return errDef.domain === this.domain || this.allDomains.includes(errDef.domain);
|
|
460
|
+
}
|
|
461
|
+
isParentOf(target) {
|
|
462
|
+
const allDomains = target instanceof NiceError ? target.def.allDomains : target.allDomains;
|
|
463
|
+
return Array.isArray(allDomains) && allDomains.includes(this.domain);
|
|
464
|
+
}
|
|
465
|
+
_buildDef() {
|
|
466
|
+
return {
|
|
467
|
+
domain: this.domain,
|
|
468
|
+
allDomains: this.allDomains,
|
|
469
|
+
schema: this._schema
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
_resolveMessage(id, context) {
|
|
473
|
+
const entry = this._schema[id];
|
|
474
|
+
if (typeof entry?.message === "function") {
|
|
475
|
+
return entry.message(context);
|
|
476
|
+
}
|
|
477
|
+
if (typeof entry?.message === "string") {
|
|
478
|
+
return entry.message;
|
|
479
|
+
}
|
|
480
|
+
return this.defaultMessage ?? `[${this.domain}::${id}] An error occurred.`;
|
|
481
|
+
}
|
|
482
|
+
_resolveHttpStatusCode(id, context) {
|
|
483
|
+
const entry = this._schema[id];
|
|
484
|
+
let httpStatusCode;
|
|
485
|
+
if (typeof entry?.httpStatusCode === "function") {
|
|
486
|
+
httpStatusCode = entry.httpStatusCode(context);
|
|
487
|
+
}
|
|
488
|
+
if (typeof entry?.httpStatusCode === "number") {
|
|
489
|
+
httpStatusCode = entry.httpStatusCode;
|
|
490
|
+
}
|
|
491
|
+
return typeof httpStatusCode === "number" ? httpStatusCode : this.defaultHttpStatusCode ?? 500;
|
|
492
|
+
}
|
|
493
|
+
reconcileErrorDataForId(id, context) {
|
|
494
|
+
const message = this._resolveMessage(id, context);
|
|
495
|
+
const httpStatusCode = this._resolveHttpStatusCode(id, context);
|
|
496
|
+
const entry = this._schema[id];
|
|
497
|
+
let contextState;
|
|
498
|
+
if (context != null && entry?.context?.serialization != null) {
|
|
499
|
+
const serialized = entry.context.serialization.toJsonSerializable(context);
|
|
500
|
+
contextState = { kind: "hydrated" /* hydrated */, value: context, serialized };
|
|
501
|
+
} else {
|
|
502
|
+
contextState = { kind: "serde_unset" /* serde_unset */, value: context };
|
|
503
|
+
}
|
|
504
|
+
return { contextState, message, httpStatusCode, timeAdded: Date.now() };
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/NiceErrorDefined/defineNiceError.ts
|
|
509
|
+
var defineNiceError = (definition) => {
|
|
510
|
+
return new NiceErrorDomain({
|
|
511
|
+
domain: definition.domain,
|
|
512
|
+
allDomains: [definition.domain],
|
|
513
|
+
schema: definition.schema,
|
|
514
|
+
...definition.packAs != null ? { packAs: definition.packAs } : {}
|
|
515
|
+
});
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
// src/NiceErrorDefined/err.ts
|
|
519
|
+
function err(meta) {
|
|
520
|
+
return meta ?? {};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/internal/nice_core_errors.ts
|
|
524
|
+
var err_nice = defineNiceError({
|
|
525
|
+
domain: "err_nice",
|
|
526
|
+
schema: {}
|
|
527
|
+
});
|
|
528
|
+
var EErrId_CastNotNice;
|
|
529
|
+
((EErrId_CastNotNice2) => {
|
|
530
|
+
EErrId_CastNotNice2["js_error"] = "native_error";
|
|
531
|
+
EErrId_CastNotNice2["js_error_like_object"] = "js_error_like_object";
|
|
532
|
+
EErrId_CastNotNice2["nullish_value"] = "nullish_value";
|
|
533
|
+
EErrId_CastNotNice2["js_data_type"] = "js_data_type";
|
|
534
|
+
EErrId_CastNotNice2["js_other"] = "js_other";
|
|
535
|
+
})(EErrId_CastNotNice ||= {});
|
|
536
|
+
var err_cast_not_nice = err_nice.createChildDomain({
|
|
537
|
+
domain: "err_cast_not_nice",
|
|
538
|
+
defaultHttpStatusCode: StatusCodes.UNPROCESSABLE_ENTITY,
|
|
539
|
+
schema: {
|
|
540
|
+
["native_error" /* js_error */]: err({
|
|
541
|
+
context: {
|
|
542
|
+
required: true
|
|
543
|
+
},
|
|
544
|
+
message: ({ jsError }) => `A native JavaScript Error was encountered during casting: ${jsError.message}`,
|
|
545
|
+
httpStatusCode: StatusCodes.INTERNAL_SERVER_ERROR
|
|
546
|
+
}),
|
|
547
|
+
["js_error_like_object" /* js_error_like_object */]: err({
|
|
548
|
+
context: {
|
|
549
|
+
required: true
|
|
550
|
+
},
|
|
551
|
+
message: ({ jsErrorObject }) => `An object resembling a JavaScript Error was encountered during casting: [${jsErrorObject.name}] ${jsErrorObject.message}`,
|
|
552
|
+
httpStatusCode: StatusCodes.INTERNAL_SERVER_ERROR
|
|
553
|
+
}),
|
|
554
|
+
["nullish_value" /* nullish_value */]: err({
|
|
555
|
+
context: {
|
|
556
|
+
required: true
|
|
557
|
+
},
|
|
558
|
+
message: ({ value }) => `A nullish value [${value === null ? "null" : "undefined"}] was encountered during casting`
|
|
559
|
+
}),
|
|
560
|
+
["js_data_type" /* js_data_type */]: err({
|
|
561
|
+
context: {
|
|
562
|
+
required: true
|
|
563
|
+
},
|
|
564
|
+
message: ({ jsDataType, jsDataValue }) => {
|
|
565
|
+
let inspectedValue;
|
|
566
|
+
try {
|
|
567
|
+
inspectedValue = JSON.stringify(jsDataValue);
|
|
568
|
+
} catch {}
|
|
569
|
+
return `A value of type [${jsDataType}] with value [${inspectedValue ?? "UNSERIALIZABLE"}] was encountered during casting, which is not a valid error type`;
|
|
570
|
+
}
|
|
571
|
+
}),
|
|
572
|
+
["js_other" /* js_other */]: err({
|
|
573
|
+
context: {
|
|
574
|
+
required: true
|
|
575
|
+
},
|
|
576
|
+
message: ({ jsDataValue }) => {
|
|
577
|
+
let inspectedValue;
|
|
578
|
+
try {
|
|
579
|
+
inspectedValue = JSON.stringify(jsDataValue);
|
|
580
|
+
} catch {}
|
|
581
|
+
return `An unhandled type [${typeof jsDataValue}] with value [${inspectedValue ?? "UNSERIALIZABLE"}] was encountered during casting, which is not a valid error type`;
|
|
582
|
+
}
|
|
583
|
+
})
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
var err_nice_handler = err_nice.createChildDomain({
|
|
587
|
+
domain: "err_nice_handler",
|
|
588
|
+
schema: {}
|
|
589
|
+
});
|
|
590
|
+
// src/NiceErrorHandler/NiceErrorHandler.types.ts
|
|
591
|
+
var EErrorHandlerTargetType;
|
|
592
|
+
((EErrorHandlerTargetType2) => {
|
|
593
|
+
EErrorHandlerTargetType2["ids"] = "ids";
|
|
594
|
+
EErrorHandlerTargetType2["domain"] = "domain";
|
|
595
|
+
EErrorHandlerTargetType2["default"] = "default";
|
|
596
|
+
})(EErrorHandlerTargetType ||= {});
|
|
597
|
+
|
|
598
|
+
// src/NiceErrorHandler/NiceErrorHandler.ts
|
|
599
|
+
class NiceErrorHandler {
|
|
600
|
+
handlerConfigs = [];
|
|
601
|
+
_defaultRequester;
|
|
602
|
+
handleErrorWithPromiseInspection(error, options) {
|
|
603
|
+
for (const handlerConfig of this.handlerConfigs) {
|
|
604
|
+
if (!handlerConfig._matcher(error))
|
|
605
|
+
continue;
|
|
606
|
+
const errorResult = handlerConfig._requester(error);
|
|
607
|
+
if (errorResult instanceof Promise) {
|
|
608
|
+
return {
|
|
609
|
+
isPromise: true,
|
|
610
|
+
matched: true,
|
|
611
|
+
target: handlerConfig.target,
|
|
612
|
+
handlerPromise: errorResult
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
isPromise: false,
|
|
617
|
+
matched: true,
|
|
618
|
+
target: handlerConfig.target,
|
|
619
|
+
handlerResponse: errorResult
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
if (this._defaultRequester) {
|
|
623
|
+
const defaultResult = this._defaultRequester(error);
|
|
624
|
+
if (defaultResult instanceof Promise) {
|
|
625
|
+
return {
|
|
626
|
+
isPromise: true,
|
|
627
|
+
matched: true,
|
|
628
|
+
target: {
|
|
629
|
+
type: "default" /* default */,
|
|
630
|
+
identifier: "[matched:default]"
|
|
631
|
+
},
|
|
632
|
+
handlerPromise: defaultResult
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
isPromise: false,
|
|
637
|
+
matched: true,
|
|
638
|
+
target: { type: "default" /* default */, identifier: "[matched:default]" },
|
|
639
|
+
handlerResponse: defaultResult
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
if (options?.throwOnUnhandled === true) {
|
|
643
|
+
throw error;
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
matched: false,
|
|
647
|
+
attemptedTargets: this.handlerConfigs.map((config) => config.target)
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
forDomain(domain, handler) {
|
|
651
|
+
this.handlerConfigs.push({
|
|
652
|
+
target: {
|
|
653
|
+
type: "domain" /* domain */,
|
|
654
|
+
domain: domain.domain,
|
|
655
|
+
identifier: `[matched:domain:${domain.domain}]`
|
|
656
|
+
},
|
|
657
|
+
_matcher: (error) => domain.isExact(error),
|
|
658
|
+
_requester: (error) => handler(domain.hydrate(error))
|
|
659
|
+
});
|
|
660
|
+
return this;
|
|
661
|
+
}
|
|
662
|
+
forId(domain, id, handler) {
|
|
663
|
+
this.handlerConfigs.push({
|
|
664
|
+
target: {
|
|
665
|
+
type: "ids" /* ids */,
|
|
666
|
+
domain: domain.domain,
|
|
667
|
+
ids: [id],
|
|
668
|
+
identifier: `[matched:ids:${domain.domain}:${id}]`
|
|
669
|
+
},
|
|
670
|
+
_matcher: (error) => domain.isExact(error) && error.hasId(id),
|
|
671
|
+
_requester: (error) => handler(domain.hydrate(error))
|
|
672
|
+
});
|
|
673
|
+
return this;
|
|
674
|
+
}
|
|
675
|
+
forIds(domain, ids, handler) {
|
|
676
|
+
this.handlerConfigs.push({
|
|
677
|
+
target: {
|
|
678
|
+
type: "ids" /* ids */,
|
|
679
|
+
domain: domain.domain,
|
|
680
|
+
ids,
|
|
681
|
+
identifier: `[matched:ids:${domain.domain}:${ids.join(",")}]`
|
|
682
|
+
},
|
|
683
|
+
_matcher: (error) => domain.isExact(error) && ids.some((id) => error.hasId(id)),
|
|
684
|
+
_requester: (error) => handler(domain.hydrate(error))
|
|
685
|
+
});
|
|
686
|
+
return this;
|
|
687
|
+
}
|
|
688
|
+
setDefaultHandler(handler) {
|
|
689
|
+
this._defaultRequester = handler;
|
|
690
|
+
return this;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/NiceErrorHandler/handleWith.ts
|
|
695
|
+
function forDomain(domain, handler) {
|
|
696
|
+
return new NiceErrorHandler().forDomain(domain, handler);
|
|
697
|
+
}
|
|
698
|
+
function forId(domain, id, handler) {
|
|
699
|
+
return new NiceErrorHandler().forId(domain, id, handler);
|
|
700
|
+
}
|
|
701
|
+
function forIds(domain, ids, handler) {
|
|
702
|
+
return new NiceErrorHandler().forIds(domain, ids, handler);
|
|
703
|
+
}
|
|
704
|
+
// src/utils/isNiceErrorObject.ts
|
|
705
|
+
function isNiceErrorObject(obj) {
|
|
706
|
+
if (typeof obj !== "object" || obj == null)
|
|
707
|
+
return false;
|
|
708
|
+
const o = obj;
|
|
709
|
+
if (o["name"] !== "NiceError" || typeof o["message"] !== "string" || typeof o["wasntNice"] !== "boolean" || typeof o["httpStatusCode"] !== "number") {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
const def = o["def"];
|
|
713
|
+
if (typeof def !== "object" || def == null)
|
|
714
|
+
return false;
|
|
715
|
+
const d = def;
|
|
716
|
+
if (typeof d["domain"] !== "string" || !Array.isArray(d["allDomains"]))
|
|
717
|
+
return false;
|
|
718
|
+
const errorData = o["errorData"];
|
|
719
|
+
if (errorData != null) {
|
|
720
|
+
if (typeof errorData !== "object")
|
|
721
|
+
return false;
|
|
722
|
+
for (const entry of Object.values(errorData)) {
|
|
723
|
+
if (entry == null)
|
|
724
|
+
continue;
|
|
725
|
+
if (typeof entry !== "object")
|
|
726
|
+
return false;
|
|
727
|
+
const e = entry;
|
|
728
|
+
const state = e["contextState"];
|
|
729
|
+
if (state == null || typeof state !== "object")
|
|
730
|
+
return false;
|
|
731
|
+
const kind = state["kind"];
|
|
732
|
+
if (kind !== "serde_unset" /* serde_unset */ && kind !== "unhydrated" /* unhydrated */)
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// src/utils/isRegularErrorObject.ts
|
|
740
|
+
function isRegularErrorJsonObject(obj) {
|
|
741
|
+
if (typeof obj !== "object" || obj == null)
|
|
742
|
+
return false;
|
|
743
|
+
const o = obj;
|
|
744
|
+
return typeof o["name"] === "string" && typeof o["message"] === "string";
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/utils/logger.ts
|
|
748
|
+
import { Logger } from "tslog";
|
|
749
|
+
var logger_NiceError = new Logger({
|
|
750
|
+
name: "NiceErrorLogger"
|
|
751
|
+
});
|
|
752
|
+
var logger_NiceError_testing = logger_NiceError.getSubLogger({
|
|
753
|
+
name: "NiceErrorTestingLogger"
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
// src/utils/inspectPotentialError/inspectPotentialError.ts
|
|
757
|
+
function interpretMessagePackedError(parsedError) {
|
|
758
|
+
let packedErrorStr;
|
|
759
|
+
if (typeof parsedError.message === "string" && parsedError.message.includes(DUR_OBJ_PACK_PREFIX) && parsedError.message.includes(DUR_OBJ_PACK_SUFFIX)) {
|
|
760
|
+
packedErrorStr = parsedError.message;
|
|
761
|
+
}
|
|
762
|
+
if (typeof parsedError.cause === "string" && parsedError.cause.includes(DUR_OBJ_PACK_PREFIX) && parsedError.cause.includes(DUR_OBJ_PACK_SUFFIX)) {
|
|
763
|
+
packedErrorStr = parsedError.cause;
|
|
764
|
+
}
|
|
765
|
+
if (packedErrorStr != null) {
|
|
766
|
+
const jsonStr = packedErrorStr.split(DUR_OBJ_PACK_PREFIX)[1].split(DUR_OBJ_PACK_SUFFIX)[0];
|
|
767
|
+
try {
|
|
768
|
+
const errorObj = JSON.parse(jsonStr);
|
|
769
|
+
if (isNiceErrorObject(errorObj)) {
|
|
770
|
+
return {
|
|
771
|
+
type: "niceErrorObject" /* niceErrorObject */,
|
|
772
|
+
niceErrorObject: errorObj
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
} catch {}
|
|
776
|
+
}
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
var inspectPotentialError = (potentialError) => {
|
|
780
|
+
if (potentialError == null) {
|
|
781
|
+
return {
|
|
782
|
+
type: "nullish" /* nullish */,
|
|
783
|
+
value: potentialError
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
if (typeof potentialError === "number") {
|
|
787
|
+
return {
|
|
788
|
+
type: "jsDataType" /* jsDataType */,
|
|
789
|
+
jsDataType: "number",
|
|
790
|
+
jsDataValue: potentialError
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
if (typeof potentialError === "boolean") {
|
|
794
|
+
return {
|
|
795
|
+
type: "jsDataType" /* jsDataType */,
|
|
796
|
+
jsDataType: "boolean",
|
|
797
|
+
jsDataValue: potentialError
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
let parsedError = potentialError;
|
|
801
|
+
if (typeof potentialError === "string") {
|
|
802
|
+
if (potentialError.includes("{") && potentialError.includes("name")) {
|
|
803
|
+
try {
|
|
804
|
+
parsedError = JSON.parse(potentialError);
|
|
805
|
+
} catch {
|
|
806
|
+
return {
|
|
807
|
+
type: "jsDataType" /* jsDataType */,
|
|
808
|
+
jsDataType: "string",
|
|
809
|
+
jsDataValue: potentialError
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
} else {
|
|
813
|
+
return {
|
|
814
|
+
type: "jsDataType" /* jsDataType */,
|
|
815
|
+
jsDataType: "string",
|
|
816
|
+
jsDataValue: potentialError
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
if (typeof parsedError !== "object" || parsedError == null) {
|
|
821
|
+
logger_NiceError.warn({
|
|
822
|
+
message: "Received a potential error that is a primitive data type other than string, number, or boolean. This is unexpected and may indicate an issue with error handling in the code.",
|
|
823
|
+
potentialError
|
|
824
|
+
});
|
|
825
|
+
return {
|
|
826
|
+
jsDataValue: potentialError,
|
|
827
|
+
type: "jsOther" /* jsOther */
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
if (parsedError instanceof NiceError) {
|
|
831
|
+
return {
|
|
832
|
+
type: "niceError" /* niceError */,
|
|
833
|
+
niceError: parsedError
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
if (isNiceErrorObject(parsedError)) {
|
|
837
|
+
return {
|
|
838
|
+
type: "niceErrorObject" /* niceErrorObject */,
|
|
839
|
+
niceErrorObject: parsedError
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
if (parsedError instanceof Error) {
|
|
843
|
+
const durObjResult = interpretMessagePackedError(parsedError);
|
|
844
|
+
if (durObjResult != null) {
|
|
845
|
+
return durObjResult;
|
|
846
|
+
}
|
|
847
|
+
return {
|
|
848
|
+
type: "jsError" /* jsError */,
|
|
849
|
+
jsError: parsedError
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
if (isRegularErrorJsonObject(parsedError)) {
|
|
853
|
+
const durObjResult = interpretMessagePackedError(parsedError);
|
|
854
|
+
if (durObjResult != null) {
|
|
855
|
+
return durObjResult;
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
type: "jsErrorObject" /* jsErrorObject */,
|
|
859
|
+
jsErrorObject: parsedError
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
return {
|
|
863
|
+
type: "jsDataType" /* jsDataType */,
|
|
864
|
+
jsDataType: "object",
|
|
865
|
+
jsDataValue: parsedError
|
|
866
|
+
};
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
// src/utils/castNiceError.ts
|
|
870
|
+
var castNiceError = (error) => {
|
|
871
|
+
const inspected = inspectPotentialError(error);
|
|
872
|
+
switch (inspected.type) {
|
|
873
|
+
case "niceError" /* niceError */:
|
|
874
|
+
return inspected.niceError;
|
|
875
|
+
case "niceErrorObject" /* niceErrorObject */: {
|
|
876
|
+
const obj = inspected.niceErrorObject;
|
|
877
|
+
return new NiceError(obj);
|
|
878
|
+
}
|
|
879
|
+
case "jsError" /* jsError */: {
|
|
880
|
+
return err_cast_not_nice.fromContext({
|
|
881
|
+
["native_error" /* js_error */]: inspected
|
|
882
|
+
}).withOriginError(inspected.jsError);
|
|
883
|
+
}
|
|
884
|
+
case "jsErrorObject" /* jsErrorObject */: {
|
|
885
|
+
const err2 = err_cast_not_nice.fromContext({
|
|
886
|
+
["js_error_like_object" /* js_error_like_object */]: inspected
|
|
887
|
+
});
|
|
888
|
+
err2.cause = inspected.jsErrorObject;
|
|
889
|
+
return err2;
|
|
890
|
+
}
|
|
891
|
+
case "nullish" /* nullish */:
|
|
892
|
+
return err_cast_not_nice.fromContext({
|
|
893
|
+
["nullish_value" /* nullish_value */]: inspected
|
|
894
|
+
});
|
|
895
|
+
case "jsDataType" /* jsDataType */: {
|
|
896
|
+
return err_cast_not_nice.fromContext({
|
|
897
|
+
["js_data_type" /* js_data_type */]: inspected
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
default:
|
|
901
|
+
return err_cast_not_nice.fromContext({
|
|
902
|
+
["js_other" /* js_other */]: inspected
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
// src/utils/castAndHydrate.ts
|
|
908
|
+
function castAndHydrate(value, niceErrorDefined) {
|
|
909
|
+
const casted = castNiceError(value);
|
|
910
|
+
if (niceErrorDefined.isExact(casted)) {
|
|
911
|
+
return niceErrorDefined.hydrate(casted);
|
|
912
|
+
}
|
|
913
|
+
return casted;
|
|
914
|
+
}
|
|
915
|
+
// src/utils/matchFirst.ts
|
|
916
|
+
function matchFirst(error, handlers) {
|
|
917
|
+
for (const id of error.getIds()) {
|
|
918
|
+
const handler = handlers[id];
|
|
919
|
+
if (typeof handler === "function") {
|
|
920
|
+
const context = error.getContext(id);
|
|
921
|
+
return handler(context);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (typeof handlers._ === "function") {
|
|
925
|
+
return handlers._();
|
|
926
|
+
}
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
export {
|
|
930
|
+
msgPack,
|
|
931
|
+
matchFirst,
|
|
932
|
+
isRegularErrorJsonObject,
|
|
933
|
+
isNiceErrorObject,
|
|
934
|
+
forIds,
|
|
935
|
+
forId,
|
|
936
|
+
forDomain,
|
|
937
|
+
err_nice_handler,
|
|
938
|
+
err_nice,
|
|
939
|
+
err_cast_not_nice,
|
|
940
|
+
err,
|
|
941
|
+
defineNiceError,
|
|
942
|
+
causePack,
|
|
943
|
+
castNiceError,
|
|
944
|
+
castAndHydrate,
|
|
945
|
+
NiceErrorHydrated,
|
|
946
|
+
NiceErrorHandler,
|
|
947
|
+
NiceErrorDomain,
|
|
948
|
+
NiceError,
|
|
949
|
+
EErrorPackType,
|
|
950
|
+
EErrorHandlerTargetType,
|
|
951
|
+
EErrId_CastNotNice
|
|
952
|
+
};
|