@devp0nt/error0 1.0.0-next.50 → 1.0.0-next.51
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/cjs/index.cjs +15 -15
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +11 -2
- package/dist/cjs/plugins/{cause-serialize.cjs → cause.cjs} +34 -12
- package/dist/cjs/plugins/cause.cjs.map +1 -0
- package/dist/cjs/plugins/cause.d.cts +15 -0
- package/dist/cjs/plugins/code.cjs +10 -2
- package/dist/cjs/plugins/code.cjs.map +1 -1
- package/dist/cjs/plugins/code.d.cts +2 -1
- package/dist/cjs/plugins/expected.cjs +19 -6
- package/dist/cjs/plugins/expected.cjs.map +1 -1
- package/dist/cjs/plugins/expected.d.cts +4 -3
- package/dist/cjs/plugins/meta.cjs +3 -3
- package/dist/cjs/plugins/meta.cjs.map +1 -1
- package/dist/cjs/plugins/meta.d.cts +2 -2
- package/dist/cjs/plugins/stack-merge.cjs +3 -3
- package/dist/cjs/plugins/stack-merge.cjs.map +1 -1
- package/dist/cjs/plugins/stack-merge.d.cts +2 -2
- package/dist/cjs/plugins/status.cjs +7 -1
- package/dist/cjs/plugins/status.cjs.map +1 -1
- package/dist/cjs/plugins/status.d.cts +2 -1
- package/dist/cjs/plugins/tags.cjs +3 -3
- package/dist/cjs/plugins/tags.cjs.map +1 -1
- package/dist/cjs/plugins/tags.d.cts +2 -2
- package/dist/esm/index.d.ts +11 -2
- package/dist/esm/index.js +15 -15
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/plugins/cause.d.ts +15 -0
- package/dist/esm/plugins/cause.js +39 -0
- package/dist/esm/plugins/cause.js.map +1 -0
- package/dist/esm/plugins/code.d.ts +2 -1
- package/dist/esm/plugins/code.js +10 -2
- package/dist/esm/plugins/code.js.map +1 -1
- package/dist/esm/plugins/expected.d.ts +4 -3
- package/dist/esm/plugins/expected.js +19 -6
- package/dist/esm/plugins/expected.js.map +1 -1
- package/dist/esm/plugins/meta.d.ts +2 -2
- package/dist/esm/plugins/meta.js +3 -3
- package/dist/esm/plugins/meta.js.map +1 -1
- package/dist/esm/plugins/stack-merge.d.ts +2 -2
- package/dist/esm/plugins/stack-merge.js +3 -3
- package/dist/esm/plugins/stack-merge.js.map +1 -1
- package/dist/esm/plugins/status.d.ts +2 -1
- package/dist/esm/plugins/status.js +7 -1
- package/dist/esm/plugins/status.js.map +1 -1
- package/dist/esm/plugins/tags.d.ts +2 -2
- package/dist/esm/plugins/tags.js +3 -3
- package/dist/esm/plugins/tags.js.map +1 -1
- package/package.json +1 -1
- package/src/index.test.ts +9 -2
- package/src/index.ts +31 -16
- package/src/plugins/{cause-serialize.test.ts → cause.test.ts} +56 -3
- package/src/plugins/cause.ts +45 -0
- package/src/plugins/code.ts +10 -2
- package/src/plugins/expected.test.ts +22 -3
- package/src/plugins/expected.ts +23 -6
- package/src/plugins/meta.ts +3 -3
- package/src/plugins/stack-merge.test.ts +0 -7
- package/src/plugins/stack-merge.ts +4 -4
- package/src/plugins/status.ts +8 -2
- package/src/plugins/tags.ts +4 -4
- package/dist/cjs/plugins/cause-serialize.cjs.map +0 -1
- package/dist/cjs/plugins/cause-serialize.d.cts +0 -7
- package/dist/esm/plugins/cause-serialize.d.ts +0 -7
- package/dist/esm/plugins/cause-serialize.js +0 -17
- package/dist/esm/plugins/cause-serialize.js.map +0 -1
- package/src/plugins/cause-serialize.ts +0 -15
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Error0 } from "../index.js";
|
|
2
|
+
const causePlugin = ({
|
|
3
|
+
isPublic = false,
|
|
4
|
+
variants = void 0
|
|
5
|
+
} = {}) => Error0.plugin().cause({
|
|
6
|
+
serialize: ({ cause, isPublic: _isPublic, is, serialize }) => {
|
|
7
|
+
if (!isPublic && _isPublic) {
|
|
8
|
+
return void 0;
|
|
9
|
+
}
|
|
10
|
+
if (variants) {
|
|
11
|
+
for (const variant of Object.values(variants)) {
|
|
12
|
+
if (cause instanceof variant) {
|
|
13
|
+
return variant.serialize(cause);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (is(cause)) {
|
|
18
|
+
return serialize(cause);
|
|
19
|
+
}
|
|
20
|
+
return void 0;
|
|
21
|
+
},
|
|
22
|
+
deserialize: ({ cause, fromSerialized, isSerialized }) => {
|
|
23
|
+
if (variants) {
|
|
24
|
+
for (const variant of Object.values(variants)) {
|
|
25
|
+
if (variant.isSerialized(cause)) {
|
|
26
|
+
return variant.from(cause);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (isSerialized(cause)) {
|
|
31
|
+
return fromSerialized(cause);
|
|
32
|
+
}
|
|
33
|
+
return cause;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
export {
|
|
37
|
+
causePlugin
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=cause.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/cause.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\ntype Variant = {\n new (...args: any[]): unknown\n [Symbol.hasInstance]: (value: any) => boolean\n isSerialized: (serializedCause: any) => boolean\n serialize: (error: any) => unknown\n from: (error: any) => unknown\n}\n\nexport const causePlugin = <TVariants extends Record<string, Variant> = Record<never, Variant>>({\n isPublic = false,\n variants = undefined,\n}: { isPublic?: boolean; variants?: TVariants } = {}) =>\n Error0.plugin().cause({\n serialize: ({ cause, isPublic: _isPublic, is, serialize }) => {\n if (!isPublic && _isPublic) {\n return undefined\n }\n if (variants) {\n for (const variant of Object.values(variants)) {\n if (cause instanceof variant) {\n return variant.serialize(cause)\n }\n }\n }\n if (is(cause)) {\n return serialize(cause)\n }\n return undefined\n },\n deserialize: ({ cause, fromSerialized, isSerialized }) => {\n if (variants) {\n for (const variant of Object.values(variants)) {\n if (variant.isSerialized(cause)) {\n return variant.from(cause)\n }\n }\n }\n if (isSerialized(cause)) {\n return fromSerialized(cause)\n }\n return cause\n },\n })\n"],"mappings":"AAAA,SAAS,cAAc;AAUhB,MAAM,cAAc,CAAqE;AAAA,EAC9F,WAAW;AAAA,EACX,WAAW;AACb,IAAkD,CAAC,MACjD,OAAO,OAAO,EAAE,MAAM;AAAA,EACpB,WAAW,CAAC,EAAE,OAAO,UAAU,WAAW,IAAI,UAAU,MAAM;AAC5D,QAAI,CAAC,YAAY,WAAW;AAC1B,aAAO;AAAA,IACT;AACA,QAAI,UAAU;AACZ,iBAAW,WAAW,OAAO,OAAO,QAAQ,GAAG;AAC7C,YAAI,iBAAiB,SAAS;AAC5B,iBAAO,QAAQ,UAAU,KAAK;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AACA,QAAI,GAAG,KAAK,GAAG;AACb,aAAO,UAAU,KAAK;AAAA,IACxB;AACA,WAAO;AAAA,EACT;AAAA,EACA,aAAa,CAAC,EAAE,OAAO,gBAAgB,aAAa,MAAM;AACxD,QAAI,UAAU;AACZ,iBAAW,WAAW,OAAO,OAAO,QAAQ,GAAG;AAC7C,YAAI,QAAQ,aAAa,KAAK,GAAG;AAC/B,iBAAO,QAAQ,KAAK,KAAK;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AACA,QAAI,aAAa,KAAK,GAAG;AACvB,aAAO,eAAe,KAAK;AAAA,IAC7B;AACA,WAAO;AAAA,EACT;AACF,CAAC;","names":[]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { PluginError0, ErrorPluginPropOptions, Error0 } from '../index.js';
|
|
2
2
|
|
|
3
|
-
declare const codePlugin: <TCode extends string>({ codes }?: {
|
|
3
|
+
declare const codePlugin: <TCode extends string>({ codes, isPublic, }?: {
|
|
4
4
|
codes?: TCode[];
|
|
5
|
+
isPublic?: boolean;
|
|
5
6
|
}) => PluginError0<Record<never, never> & Record<"code", ErrorPluginPropOptions<TCode, TCode, Error0, TCode | undefined>>, Record<never, never>>;
|
|
6
7
|
|
|
7
8
|
export { codePlugin };
|
package/dist/esm/plugins/code.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { Error0 } from "../index.js";
|
|
2
|
-
const codePlugin = ({
|
|
2
|
+
const codePlugin = ({
|
|
3
|
+
codes,
|
|
4
|
+
isPublic = false
|
|
5
|
+
} = {}) => {
|
|
3
6
|
const isCode = (value) => typeof value === "string" && (!codes || codes.includes(value));
|
|
4
7
|
return Error0.plugin().prop("code", {
|
|
5
8
|
init: (code) => code,
|
|
6
9
|
resolve: ({ flow }) => flow.find(Boolean),
|
|
7
|
-
serialize: ({ resolved, isPublic }) =>
|
|
10
|
+
serialize: ({ resolved, isPublic: _isPublic }) => {
|
|
11
|
+
if (!isPublic && _isPublic) {
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
return resolved;
|
|
15
|
+
},
|
|
8
16
|
deserialize: ({ value, record }) => isCode(value) ? value : void 0
|
|
9
17
|
});
|
|
10
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/plugins/code.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const codePlugin = <TCode extends string>({
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/code.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const codePlugin = <TCode extends string>({\n codes,\n isPublic = false,\n}: { codes?: TCode[]; isPublic?: boolean } = {}) => {\n const isCode = (value: unknown): value is TCode =>\n typeof value === 'string' && (!codes || codes.includes(value as TCode))\n return Error0.plugin().prop('code', {\n init: (code: TCode) => code,\n resolve: ({ flow }) => flow.find(Boolean),\n serialize: ({ resolved, isPublic: _isPublic }) => {\n if (!isPublic && _isPublic) {\n return undefined\n }\n return resolved\n },\n deserialize: ({ value, record }) => (isCode(value) ? value : undefined),\n })\n}\n"],"mappings":"AAAA,SAAS,cAAc;AAEhB,MAAM,aAAa,CAAuB;AAAA,EAC/C;AAAA,EACA,WAAW;AACb,IAA6C,CAAC,MAAM;AAClD,QAAM,SAAS,CAAC,UACd,OAAO,UAAU,aAAa,CAAC,SAAS,MAAM,SAAS,KAAc;AACvE,SAAO,OAAO,OAAO,EAAE,KAAK,QAAQ;AAAA,IAClC,MAAM,CAAC,SAAgB;AAAA,IACvB,SAAS,CAAC,EAAE,KAAK,MAAM,KAAK,KAAK,OAAO;AAAA,IACxC,WAAW,CAAC,EAAE,UAAU,UAAU,UAAU,MAAM;AAChD,UAAI,CAAC,YAAY,WAAW;AAC1B,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA,aAAa,CAAC,EAAE,OAAO,OAAO,MAAO,OAAO,KAAK,IAAI,QAAQ;AAAA,EAC/D,CAAC;AACH;","names":[]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { PluginError0, ErrorPluginPropOptions
|
|
1
|
+
import { Error0, PluginError0, ErrorPluginPropOptions } from '../index.js';
|
|
2
2
|
|
|
3
|
-
declare const expectedPlugin: ({
|
|
4
|
-
|
|
3
|
+
declare const expectedPlugin: <TError extends Error0>({ isPublic, override, }?: {
|
|
4
|
+
isPublic?: boolean;
|
|
5
|
+
override?: (error: TError) => boolean | undefined;
|
|
5
6
|
}) => PluginError0<Record<never, never> & Record<"expected", ErrorPluginPropOptions<boolean, boolean, Error0, boolean>>, Record<never, never> & Record<"isExpected", (error: Error0 & {
|
|
6
7
|
expected: boolean;
|
|
7
8
|
} & {} & {
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { Error0 } from "../index.js";
|
|
2
|
-
const isExpected = (
|
|
2
|
+
const isExpected = ({
|
|
3
|
+
flow,
|
|
4
|
+
error,
|
|
5
|
+
override
|
|
6
|
+
}) => {
|
|
7
|
+
if (override) {
|
|
8
|
+
const overridden = override(error);
|
|
9
|
+
if (overridden !== void 0) {
|
|
10
|
+
return overridden;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
3
13
|
let expected = false;
|
|
4
14
|
for (const value of flow) {
|
|
5
15
|
if (value === false) {
|
|
@@ -11,18 +21,21 @@ const isExpected = (flow) => {
|
|
|
11
21
|
}
|
|
12
22
|
return expected;
|
|
13
23
|
};
|
|
14
|
-
const expectedPlugin = ({
|
|
24
|
+
const expectedPlugin = ({
|
|
25
|
+
isPublic = false,
|
|
26
|
+
override
|
|
27
|
+
} = {}) => Error0.plugin().prop("expected", {
|
|
15
28
|
init: (input) => input,
|
|
16
|
-
resolve: ({ flow }) => isExpected(flow),
|
|
17
|
-
serialize: ({ resolved, isPublic }) => {
|
|
18
|
-
if (
|
|
29
|
+
resolve: ({ flow, error }) => isExpected({ flow, error, override }),
|
|
30
|
+
serialize: ({ resolved, isPublic: _isPublic }) => {
|
|
31
|
+
if (isPublic && _isPublic) {
|
|
19
32
|
return void 0;
|
|
20
33
|
}
|
|
21
34
|
return resolved;
|
|
22
35
|
},
|
|
23
36
|
deserialize: ({ value }) => typeof value === "boolean" ? value : void 0
|
|
24
37
|
}).method("isExpected", (error) => {
|
|
25
|
-
return isExpected(error.flow("expected"));
|
|
38
|
+
return isExpected({ flow: error.flow("expected"), error, override });
|
|
26
39
|
});
|
|
27
40
|
export {
|
|
28
41
|
expectedPlugin
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/plugins/expected.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nconst isExpected = (flow: unknown[]) => {\n let expected = false\n for (const value of flow) {\n if (value === false) {\n return false\n }\n if (value === true) {\n expected = true\n }\n }\n return expected\n}\n\nexport const expectedPlugin = ({
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/expected.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nconst isExpected = ({\n flow,\n error,\n override,\n}: {\n flow: unknown[]\n error: Error0\n override?: (error: any) => boolean | undefined\n}) => {\n if (override) {\n const overridden = override(error)\n if (overridden !== undefined) {\n return overridden\n }\n }\n let expected = false\n for (const value of flow) {\n if (value === false) {\n return false\n }\n if (value === true) {\n expected = true\n }\n }\n return expected\n}\n\nexport const expectedPlugin = <TError extends Error0>({\n isPublic = false,\n override,\n}: { isPublic?: boolean; override?: (error: TError) => boolean | undefined } = {}) =>\n Error0.plugin()\n .prop('expected', {\n init: (input: boolean) => input,\n resolve: ({ flow, error }) => isExpected({ flow, error, override }),\n serialize: ({ resolved, isPublic: _isPublic }) => {\n if (isPublic && _isPublic) {\n return undefined\n }\n return resolved\n },\n deserialize: ({ value }) => (typeof value === 'boolean' ? value : undefined),\n })\n .method('isExpected', (error) => {\n return isExpected({ flow: error.flow('expected'), error, override })\n })\n"],"mappings":"AAAA,SAAS,cAAc;AAEvB,MAAM,aAAa,CAAC;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AACF,MAIM;AACJ,MAAI,UAAU;AACZ,UAAM,aAAa,SAAS,KAAK;AACjC,QAAI,eAAe,QAAW;AAC5B,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,WAAW;AACf,aAAW,SAAS,MAAM;AACxB,QAAI,UAAU,OAAO;AACnB,aAAO;AAAA,IACT;AACA,QAAI,UAAU,MAAM;AAClB,iBAAW;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;AAEO,MAAM,iBAAiB,CAAwB;AAAA,EACpD,WAAW;AAAA,EACX;AACF,IAA+E,CAAC,MAC9E,OAAO,OAAO,EACX,KAAK,YAAY;AAAA,EAChB,MAAM,CAAC,UAAmB;AAAA,EAC1B,SAAS,CAAC,EAAE,MAAM,MAAM,MAAM,WAAW,EAAE,MAAM,OAAO,SAAS,CAAC;AAAA,EAClE,WAAW,CAAC,EAAE,UAAU,UAAU,UAAU,MAAM;AAChD,QAAI,YAAY,WAAW;AACzB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EACA,aAAa,CAAC,EAAE,MAAM,MAAO,OAAO,UAAU,YAAY,QAAQ;AACpE,CAAC,EACA,OAAO,cAAc,CAAC,UAAU;AAC/B,SAAO,WAAW,EAAE,MAAM,MAAM,KAAK,UAAU,GAAG,OAAO,SAAS,CAAC;AACrE,CAAC;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PluginError0, ErrorPluginPropOptions, Error0 } from '../index.js';
|
|
2
2
|
|
|
3
|
-
declare const metaPlugin: ({
|
|
4
|
-
|
|
3
|
+
declare const metaPlugin: ({ isPublic }?: {
|
|
4
|
+
isPublic?: boolean;
|
|
5
5
|
}) => PluginError0<Record<never, never> & Record<"meta", ErrorPluginPropOptions<Record<string, unknown>, Record<string, unknown>, Error0, Record<string, unknown> | undefined>>, Record<never, never>>;
|
|
6
6
|
|
|
7
7
|
export { metaPlugin };
|
package/dist/esm/plugins/meta.js
CHANGED
|
@@ -22,7 +22,7 @@ const toJsonSafe = (input) => {
|
|
|
22
22
|
return void 0;
|
|
23
23
|
};
|
|
24
24
|
const isMetaRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
|
-
const metaPlugin = ({
|
|
25
|
+
const metaPlugin = ({ isPublic = false } = {}) => Error0.plugin().prop("meta", {
|
|
26
26
|
init: (input) => input,
|
|
27
27
|
resolve: ({ flow }) => {
|
|
28
28
|
const values = flow.filter(isMetaRecord);
|
|
@@ -35,8 +35,8 @@ const metaPlugin = ({ hideWhenPublic = true } = {}) => Error0.plugin().prop("met
|
|
|
35
35
|
}
|
|
36
36
|
return merged;
|
|
37
37
|
},
|
|
38
|
-
serialize: ({ resolved, isPublic }) => {
|
|
39
|
-
if (
|
|
38
|
+
serialize: ({ resolved, isPublic: _isPublic }) => {
|
|
39
|
+
if (!isPublic && _isPublic) {
|
|
40
40
|
return void 0;
|
|
41
41
|
}
|
|
42
42
|
return toJsonSafe(resolved);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/plugins/meta.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\ntype Json = null | boolean | number | string | Json[] | { [key: string]: Json }\n\nconst toJsonSafe = (input: unknown): Json | undefined => {\n if (input === null) {\n return null\n }\n if (typeof input === 'string' || typeof input === 'number' || typeof input === 'boolean') {\n return input\n }\n if (Array.isArray(input)) {\n return input.map((value) => toJsonSafe(value)) as Json[]\n }\n if (typeof input === 'object') {\n const output: Record<string, Json> = {}\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n const jsonValue = toJsonSafe(value)\n if (jsonValue !== undefined) {\n output[key] = jsonValue\n }\n }\n return output\n }\n return undefined\n}\n\nconst isMetaRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nexport const metaPlugin = ({
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/meta.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\ntype Json = null | boolean | number | string | Json[] | { [key: string]: Json }\n\nconst toJsonSafe = (input: unknown): Json | undefined => {\n if (input === null) {\n return null\n }\n if (typeof input === 'string' || typeof input === 'number' || typeof input === 'boolean') {\n return input\n }\n if (Array.isArray(input)) {\n return input.map((value) => toJsonSafe(value)) as Json[]\n }\n if (typeof input === 'object') {\n const output: Record<string, Json> = {}\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n const jsonValue = toJsonSafe(value)\n if (jsonValue !== undefined) {\n output[key] = jsonValue\n }\n }\n return output\n }\n return undefined\n}\n\nconst isMetaRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nexport const metaPlugin = ({ isPublic = false }: { isPublic?: boolean } = {}) =>\n Error0.plugin().prop('meta', {\n init: (input: Record<string, unknown>) => input,\n resolve: ({ flow }) => {\n const values = flow.filter(isMetaRecord)\n if (values.length === 0) {\n return undefined\n }\n\n // Merge cause meta into the current error; nearer errors win on conflicts.\n const merged: Record<string, unknown> = {}\n for (const value of [...values].reverse()) {\n Object.assign(merged, value)\n }\n return merged\n },\n serialize: ({ resolved, isPublic: _isPublic }) => {\n if (!isPublic && _isPublic) {\n return undefined\n }\n return toJsonSafe(resolved)\n },\n deserialize: ({ value }) => {\n if (!isMetaRecord(value)) {\n return undefined\n }\n return value\n },\n })\n"],"mappings":"AAAA,SAAS,cAAc;AAIvB,MAAM,aAAa,CAAC,UAAqC;AACvD,MAAI,UAAU,MAAM;AAClB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AACxF,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,UAAU,WAAW,KAAK,CAAC;AAAA,EAC/C;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAA+B,CAAC;AACtC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAgC,GAAG;AAC3E,YAAM,YAAY,WAAW,KAAK;AAClC,UAAI,cAAc,QAAW;AAC3B,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,MAAM,eAAe,CAAC,UACpB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAE9D,MAAM,aAAa,CAAC,EAAE,WAAW,MAAM,IAA4B,CAAC,MACzE,OAAO,OAAO,EAAE,KAAK,QAAQ;AAAA,EAC3B,MAAM,CAAC,UAAmC;AAAA,EAC1C,SAAS,CAAC,EAAE,KAAK,MAAM;AACrB,UAAM,SAAS,KAAK,OAAO,YAAY;AACvC,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO;AAAA,IACT;AAGA,UAAM,SAAkC,CAAC;AACzC,eAAW,SAAS,CAAC,GAAG,MAAM,EAAE,QAAQ,GAAG;AACzC,aAAO,OAAO,QAAQ,KAAK;AAAA,IAC7B;AACA,WAAO;AAAA,EACT;AAAA,EACA,WAAW,CAAC,EAAE,UAAU,UAAU,UAAU,MAAM;AAChD,QAAI,CAAC,YAAY,WAAW;AAC1B,aAAO;AAAA,IACT;AACA,WAAO,WAAW,QAAQ;AAAA,EAC5B;AAAA,EACA,aAAa,CAAC,EAAE,MAAM,MAAM;AAC1B,QAAI,CAAC,aAAa,KAAK,GAAG;AACxB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF,CAAC;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PluginError0 } from '../index.js';
|
|
2
2
|
|
|
3
|
-
declare const stackMergePlugin: ({
|
|
4
|
-
|
|
3
|
+
declare const stackMergePlugin: ({ isPublic, delimiter, }?: {
|
|
4
|
+
isPublic?: boolean;
|
|
5
5
|
delimiter?: string;
|
|
6
6
|
}) => PluginError0<Record<never, never>, Record<never, never>>;
|
|
7
7
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Error0 } from "../index.js";
|
|
2
2
|
const stackMergePlugin = ({
|
|
3
|
-
|
|
3
|
+
isPublic = false,
|
|
4
4
|
delimiter = "\n"
|
|
5
5
|
} = {}) => Error0.plugin().stack({
|
|
6
|
-
serialize: ({ error, isPublic }) => {
|
|
7
|
-
if (
|
|
6
|
+
serialize: ({ error, isPublic: _isPublic }) => {
|
|
7
|
+
if (!isPublic && _isPublic) {
|
|
8
8
|
return void 0;
|
|
9
9
|
}
|
|
10
10
|
return error.causes().map((cause) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/plugins/stack-merge.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const stackMergePlugin = ({\n
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/stack-merge.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const stackMergePlugin = ({\n isPublic = false,\n delimiter = '\\n',\n}: { isPublic?: boolean; delimiter?: string } = {}) =>\n Error0.plugin().stack({\n serialize: ({ error, isPublic: _isPublic }) => {\n if (!isPublic && _isPublic) {\n return undefined\n }\n return error\n .causes()\n .map((cause) => {\n return cause instanceof Error ? cause.stack : undefined\n })\n .filter((value): value is string => typeof value === 'string')\n .join(delimiter)\n },\n })\n"],"mappings":"AAAA,SAAS,cAAc;AAEhB,MAAM,mBAAmB,CAAC;AAAA,EAC/B,WAAW;AAAA,EACX,YAAY;AACd,IAAgD,CAAC,MAC/C,OAAO,OAAO,EAAE,MAAM;AAAA,EACpB,WAAW,CAAC,EAAE,OAAO,UAAU,UAAU,MAAM;AAC7C,QAAI,CAAC,YAAY,WAAW;AAC1B,aAAO;AAAA,IACT;AACA,WAAO,MACJ,OAAO,EACP,IAAI,CAAC,UAAU;AACd,aAAO,iBAAiB,QAAQ,MAAM,QAAQ;AAAA,IAChD,CAAC,EACA,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ,EAC5D,KAAK,SAAS;AAAA,EACnB;AACF,CAAC;","names":[]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PluginError0, ErrorPluginPropOptions, Error0 } from '../index.js';
|
|
2
2
|
|
|
3
|
-
declare const statusPlugin: <TStatuses extends Record<string, number> = Record<never, number>>({ statuses, strict, }?: {
|
|
3
|
+
declare const statusPlugin: <TStatuses extends Record<string, number> = Record<never, number>>({ isPublic, statuses, strict, }?: {
|
|
4
|
+
isPublic?: boolean;
|
|
4
5
|
statuses?: TStatuses;
|
|
5
6
|
strict?: boolean;
|
|
6
7
|
}) => PluginError0<Record<never, never> & Record<"status", ErrorPluginPropOptions<number | Extract<keyof TStatuses, string>, number | undefined, Error0, number | undefined>>, Record<never, never>>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Error0 } from "../index.js";
|
|
2
2
|
const statusPlugin = ({
|
|
3
|
+
isPublic = false,
|
|
3
4
|
statuses,
|
|
4
5
|
strict = false
|
|
5
6
|
} = {}) => {
|
|
@@ -20,7 +21,12 @@ const statusPlugin = ({
|
|
|
20
21
|
return Error0.plugin().prop("status", {
|
|
21
22
|
init: (status) => convertStatusValue(status),
|
|
22
23
|
resolve: ({ flow }) => flow.find(Boolean),
|
|
23
|
-
serialize: ({ resolved }) =>
|
|
24
|
+
serialize: ({ resolved, isPublic: _isPublic }) => {
|
|
25
|
+
if (!isPublic && _isPublic) {
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
28
|
+
return resolved;
|
|
29
|
+
},
|
|
24
30
|
deserialize: ({ value }) => typeof value === "number" ? value : void 0
|
|
25
31
|
});
|
|
26
32
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/plugins/status.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const statusPlugin = <TStatuses extends Record<string, number> = Record<never, number>>({\n statuses,\n strict = false,\n}: { statuses?: TStatuses; strict?: boolean } = {}) => {\n const statusValues = statuses ? Object.values(statuses) : undefined\n const isStatusValue = (value: unknown): value is number =>\n typeof value === 'number' && (!statusValues || !strict || statusValues.includes(value))\n const normalizeStatusValue = (value: unknown): number | undefined => {\n return isStatusValue(value) ? value : undefined\n }\n const convertStatusValue = (value: number | string): number | undefined => {\n if (typeof value === 'number') {\n return normalizeStatusValue(value)\n }\n if (statuses && value in statuses) {\n return statuses[value as keyof TStatuses]\n }\n return undefined\n }\n\n return Error0.plugin().prop('status', {\n init: (status: number | Extract<keyof TStatuses, string>) => convertStatusValue(status),\n resolve: ({ flow }) => flow.find(Boolean),\n serialize: ({ resolved }) => resolved,\n deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),\n })\n}\n"],"mappings":"AAAA,SAAS,cAAc;AAEhB,MAAM,eAAe,CAAmE;AAAA,EAC7F;AAAA,EACA,SAAS;AACX,
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/status.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const statusPlugin = <TStatuses extends Record<string, number> = Record<never, number>>({\n isPublic = false,\n statuses,\n strict = false,\n}: { isPublic?: boolean; statuses?: TStatuses; strict?: boolean } = {}) => {\n const statusValues = statuses ? Object.values(statuses) : undefined\n const isStatusValue = (value: unknown): value is number =>\n typeof value === 'number' && (!statusValues || !strict || statusValues.includes(value))\n const normalizeStatusValue = (value: unknown): number | undefined => {\n return isStatusValue(value) ? value : undefined\n }\n const convertStatusValue = (value: number | string): number | undefined => {\n if (typeof value === 'number') {\n return normalizeStatusValue(value)\n }\n if (statuses && value in statuses) {\n return statuses[value as keyof TStatuses]\n }\n return undefined\n }\n\n return Error0.plugin().prop('status', {\n init: (status: number | Extract<keyof TStatuses, string>) => convertStatusValue(status),\n resolve: ({ flow }) => flow.find(Boolean),\n serialize: ({ resolved, isPublic: _isPublic }) => {\n if (!isPublic && _isPublic) {\n return undefined\n }\n return resolved\n },\n deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),\n })\n}\n"],"mappings":"AAAA,SAAS,cAAc;AAEhB,MAAM,eAAe,CAAmE;AAAA,EAC7F,WAAW;AAAA,EACX;AAAA,EACA,SAAS;AACX,IAAoE,CAAC,MAAM;AACzE,QAAM,eAAe,WAAW,OAAO,OAAO,QAAQ,IAAI;AAC1D,QAAM,gBAAgB,CAAC,UACrB,OAAO,UAAU,aAAa,CAAC,gBAAgB,CAAC,UAAU,aAAa,SAAS,KAAK;AACvF,QAAM,uBAAuB,CAAC,UAAuC;AACnE,WAAO,cAAc,KAAK,IAAI,QAAQ;AAAA,EACxC;AACA,QAAM,qBAAqB,CAAC,UAA+C;AACzE,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,qBAAqB,KAAK;AAAA,IACnC;AACA,QAAI,YAAY,SAAS,UAAU;AACjC,aAAO,SAAS,KAAwB;AAAA,IAC1C;AACA,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,OAAO,EAAE,KAAK,UAAU;AAAA,IACpC,MAAM,CAAC,WAAsD,mBAAmB,MAAM;AAAA,IACtF,SAAS,CAAC,EAAE,KAAK,MAAM,KAAK,KAAK,OAAO;AAAA,IACxC,WAAW,CAAC,EAAE,UAAU,UAAU,UAAU,MAAM;AAChD,UAAI,CAAC,YAAY,WAAW;AAC1B,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA,aAAa,CAAC,EAAE,MAAM,MAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,EACnE,CAAC;AACH;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PluginError0, ErrorPluginPropOptions, Error0 } from '../index.js';
|
|
2
2
|
|
|
3
|
-
declare const tagsPlugin: <TTag extends string>({
|
|
4
|
-
|
|
3
|
+
declare const tagsPlugin: <TTag extends string>({ isPublic, tags, strict, }?: {
|
|
4
|
+
isPublic?: boolean;
|
|
5
5
|
tags?: TTag[] | readonly TTag[];
|
|
6
6
|
strict?: boolean;
|
|
7
7
|
}) => PluginError0<Record<never, never> & Record<"tags", ErrorPluginPropOptions<string[], string[], Error0, string[] | undefined>>, Record<never, never> & Record<"hasTag", {
|
package/dist/esm/plugins/tags.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Error0 } from "../index.js";
|
|
2
2
|
const tagsPlugin = ({
|
|
3
|
-
|
|
3
|
+
isPublic = false,
|
|
4
4
|
tags,
|
|
5
5
|
strict = true
|
|
6
6
|
} = {}) => {
|
|
@@ -29,8 +29,8 @@ const tagsPlugin = ({
|
|
|
29
29
|
}
|
|
30
30
|
return merged.length > 0 ? Array.from(new Set(merged)) : void 0;
|
|
31
31
|
},
|
|
32
|
-
serialize: ({ resolved, isPublic }) => {
|
|
33
|
-
if (
|
|
32
|
+
serialize: ({ resolved, isPublic: _isPublic }) => {
|
|
33
|
+
if (!isPublic && _isPublic) {
|
|
34
34
|
return void 0;
|
|
35
35
|
}
|
|
36
36
|
return resolved;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/plugins/tags.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const tagsPlugin = <TTag extends string>({\n
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/tags.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const tagsPlugin = <TTag extends string>({\n isPublic = false,\n tags,\n strict = true,\n}: { isPublic?: boolean; tags?: TTag[] | readonly TTag[]; strict?: boolean } = {}) => {\n function hasTag(error: Error0, tag: TTag): boolean\n function hasTag(error: Error0, tag: TTag[], policy: 'every' | 'some'): boolean\n function hasTag(error: Error0, tag: TTag | TTag[], policy?: 'every' | 'some'): boolean {\n const tags = (error as any).tags as string[] | undefined\n if (!tags) {\n return false\n }\n if (Array.isArray(tag)) {\n if (policy === 'every') {\n return tag.every((item) => tags.includes(item))\n }\n return tag.some((item) => tags.includes(item))\n }\n return tags.includes(tag)\n }\n const isTag = (value: unknown): value is TTag =>\n typeof value === 'string' && (!tags || !strict || tags.includes(value as TTag))\n return Error0.plugin()\n .prop('tags', {\n init: (input: string[]) => input,\n resolve: ({ flow }) => {\n const merged: string[] = []\n for (const value of flow) {\n if (Array.isArray(value)) {\n merged.push(...value)\n }\n }\n return merged.length > 0 ? Array.from(new Set(merged)) : undefined\n },\n serialize: ({ resolved, isPublic: _isPublic }) => {\n if (!isPublic && _isPublic) {\n return undefined\n }\n return resolved\n },\n deserialize: ({ value }) => {\n if (!Array.isArray(value)) {\n return undefined\n }\n return value.filter((item) => isTag(item))\n },\n })\n .method('hasTag', hasTag)\n}\n"],"mappings":"AAAA,SAAS,cAAc;AAEhB,MAAM,aAAa,CAAsB;AAAA,EAC9C,WAAW;AAAA,EACX;AAAA,EACA,SAAS;AACX,IAA+E,CAAC,MAAM;AAGpF,WAAS,OAAO,OAAe,KAAoB,QAAoC;AACrF,UAAMA,QAAQ,MAAc;AAC5B,QAAI,CAACA,OAAM;AACT,aAAO;AAAA,IACT;AACA,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,UAAI,WAAW,SAAS;AACtB,eAAO,IAAI,MAAM,CAAC,SAASA,MAAK,SAAS,IAAI,CAAC;AAAA,MAChD;AACA,aAAO,IAAI,KAAK,CAAC,SAASA,MAAK,SAAS,IAAI,CAAC;AAAA,IAC/C;AACA,WAAOA,MAAK,SAAS,GAAG;AAAA,EAC1B;AACA,QAAM,QAAQ,CAAC,UACb,OAAO,UAAU,aAAa,CAAC,QAAQ,CAAC,UAAU,KAAK,SAAS,KAAa;AAC/E,SAAO,OAAO,OAAO,EAClB,KAAK,QAAQ;AAAA,IACZ,MAAM,CAAC,UAAoB;AAAA,IAC3B,SAAS,CAAC,EAAE,KAAK,MAAM;AACrB,YAAM,SAAmB,CAAC;AAC1B,iBAAW,SAAS,MAAM;AACxB,YAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAO,KAAK,GAAG,KAAK;AAAA,QACtB;AAAA,MACF;AACA,aAAO,OAAO,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI;AAAA,IAC3D;AAAA,IACA,WAAW,CAAC,EAAE,UAAU,UAAU,UAAU,MAAM;AAChD,UAAI,CAAC,YAAY,WAAW;AAC1B,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA,aAAa,CAAC,EAAE,MAAM,MAAM;AAC1B,UAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,eAAO;AAAA,MACT;AACA,aAAO,MAAM,OAAO,CAAC,SAAS,MAAM,IAAI,CAAC;AAAA,IAC3C;AAAA,EACF,CAAC,EACA,OAAO,UAAU,MAAM;AAC5B;","names":["tags"]}
|
package/package.json
CHANGED
package/src/index.test.ts
CHANGED
|
@@ -203,13 +203,20 @@ describe('Error0', () => {
|
|
|
203
203
|
expect('code' in json).toBe(false)
|
|
204
204
|
})
|
|
205
205
|
|
|
206
|
-
it('serialize keeps stack by default without stack plugin', () => {
|
|
206
|
+
it('serialize keeps stack by default without stack plugin when not public', () => {
|
|
207
207
|
const AppError = Error0.use(statusPlugin)
|
|
208
208
|
const error = new AppError('test', { status: 500 })
|
|
209
|
-
const json = AppError.serialize(error)
|
|
209
|
+
const json = AppError.serialize(error, false)
|
|
210
210
|
expect(json.stack).toBe(error.stack)
|
|
211
211
|
})
|
|
212
212
|
|
|
213
|
+
it('serialize does not keep stack when public', () => {
|
|
214
|
+
const AppError = Error0.use(statusPlugin)
|
|
215
|
+
const error = new AppError('test', { status: 500 })
|
|
216
|
+
const json = AppError.serialize(error, true)
|
|
217
|
+
expect('stack' in json).toBe(false)
|
|
218
|
+
})
|
|
219
|
+
|
|
213
220
|
it('stack plugin can customize stack serialization without defining prop plugin', () => {
|
|
214
221
|
const AppError = Error0.use('stack', { serialize: ({ value }) => (value ? `custom:${value}` : undefined) })
|
|
215
222
|
const error = new AppError('test')
|
package/src/index.ts
CHANGED
|
@@ -79,11 +79,22 @@ export type ErrorPluginStackSerialize<TError extends Error0> = (options: {
|
|
|
79
79
|
}) => unknown
|
|
80
80
|
export type ErrorPluginStack<TError extends Error0 = Error0> = { serialize: ErrorPluginStackSerialize<TError> }
|
|
81
81
|
export type ErrorPluginCauseSerialize<TError extends Error0> = (options: {
|
|
82
|
-
|
|
82
|
+
cause: unknown
|
|
83
83
|
error: TError
|
|
84
84
|
isPublic: boolean
|
|
85
|
+
is: (cause: unknown) => boolean
|
|
86
|
+
serialize: (cause: unknown) => Record<string, unknown>
|
|
85
87
|
}) => unknown
|
|
86
|
-
export type
|
|
88
|
+
export type ErrorPluginCauseDeserialize = (options: {
|
|
89
|
+
cause: unknown
|
|
90
|
+
error: Record<string, unknown>
|
|
91
|
+
isSerialized: (serializedCause: unknown) => boolean
|
|
92
|
+
fromSerialized: (serializedCause: unknown) => Error0
|
|
93
|
+
}) => unknown
|
|
94
|
+
export type ErrorPluginCause<TError extends Error0 = Error0> = {
|
|
95
|
+
serialize: ErrorPluginCauseSerialize<TError>
|
|
96
|
+
deserialize: ErrorPluginCauseDeserialize
|
|
97
|
+
}
|
|
87
98
|
export type ErrorPluginMessageSerialize<TError extends Error0> = (options: {
|
|
88
99
|
value: string
|
|
89
100
|
error: TError
|
|
@@ -897,8 +908,6 @@ export class Error0 extends Error {
|
|
|
897
908
|
console.error(`Error0: failed to deserialize property ${key}`, errorRecord)
|
|
898
909
|
}
|
|
899
910
|
}
|
|
900
|
-
// we do not serialize causes
|
|
901
|
-
// ;(recreated as unknown as { cause?: unknown }).cause = errorRecord.cause
|
|
902
911
|
if ('stack' in errorRecord) {
|
|
903
912
|
try {
|
|
904
913
|
if (typeof errorRecord.stack === 'string') {
|
|
@@ -910,13 +919,14 @@ export class Error0 extends Error {
|
|
|
910
919
|
}
|
|
911
920
|
}
|
|
912
921
|
const causePlugin = plugin.cause
|
|
913
|
-
if (causePlugin
|
|
922
|
+
if (causePlugin && 'cause' in errorRecord) {
|
|
914
923
|
try {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
924
|
+
;(recreated as { cause?: unknown }).cause = causePlugin.deserialize({
|
|
925
|
+
cause: errorRecord.cause,
|
|
926
|
+
error: errorRecord,
|
|
927
|
+
isSerialized: (serializedCause) => this.isSerialized(serializedCause),
|
|
928
|
+
fromSerialized: (serializedCause) => this._fromSerialized(serializedCause),
|
|
929
|
+
})
|
|
920
930
|
} catch {
|
|
921
931
|
// eslint-disable-next-line no-console
|
|
922
932
|
console.error('Error0: failed to deserialize cause', errorRecord)
|
|
@@ -1098,8 +1108,13 @@ export class Error0 extends Error {
|
|
|
1098
1108
|
if (typeof key === 'undefined') {
|
|
1099
1109
|
throw new Error('Error0.use("cause", value) requires cause plugin value')
|
|
1100
1110
|
}
|
|
1101
|
-
if (
|
|
1102
|
-
|
|
1111
|
+
if (
|
|
1112
|
+
typeof key !== 'object' ||
|
|
1113
|
+
key === null ||
|
|
1114
|
+
typeof (key as { serialize?: unknown }).serialize !== 'function' ||
|
|
1115
|
+
typeof (key as { deserialize?: unknown }).deserialize !== 'function'
|
|
1116
|
+
) {
|
|
1117
|
+
throw new Error('Error0.use("cause", value) expects { serialize: function, deserialize: function }')
|
|
1103
1118
|
}
|
|
1104
1119
|
return this._useWithPlugin({
|
|
1105
1120
|
cause: key as ErrorPluginCause,
|
|
@@ -1166,8 +1181,6 @@ export class Error0 extends Error {
|
|
|
1166
1181
|
}
|
|
1167
1182
|
const json: Record<string, unknown> = {
|
|
1168
1183
|
name: error0.name,
|
|
1169
|
-
// we do not serialize causes, it is enough that we have floated props and adapt helper
|
|
1170
|
-
// cause: error0.cause,
|
|
1171
1184
|
}
|
|
1172
1185
|
if (serializedMessage !== undefined) {
|
|
1173
1186
|
json.message = serializedMessage
|
|
@@ -1206,7 +1219,7 @@ export class Error0 extends Error {
|
|
|
1206
1219
|
if (stackPlugin) {
|
|
1207
1220
|
serializedStack = stackPlugin.serialize({ value: error0.stack, error: error0, isPublic })
|
|
1208
1221
|
} else {
|
|
1209
|
-
serializedStack = error0.stack
|
|
1222
|
+
serializedStack = isPublic ? undefined : error0.stack
|
|
1210
1223
|
}
|
|
1211
1224
|
if (serializedStack !== undefined) {
|
|
1212
1225
|
json.stack = serializedStack
|
|
@@ -1219,9 +1232,11 @@ export class Error0 extends Error {
|
|
|
1219
1232
|
if (causePlugin?.serialize) {
|
|
1220
1233
|
try {
|
|
1221
1234
|
const serializedCause = causePlugin.serialize({
|
|
1222
|
-
|
|
1235
|
+
cause: (error0 as { cause?: unknown }).cause,
|
|
1223
1236
|
error: error0,
|
|
1224
1237
|
isPublic,
|
|
1238
|
+
is: (cause) => this.is(cause),
|
|
1239
|
+
serialize: (cause) => this.serialize(cause, isPublic),
|
|
1225
1240
|
})
|
|
1226
1241
|
if (serializedCause !== undefined) {
|
|
1227
1242
|
json.cause = serializedCause
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from 'bun:test'
|
|
2
2
|
import { Error0 } from '../index.js'
|
|
3
|
-
import {
|
|
3
|
+
import { causePlugin } from './cause.js'
|
|
4
4
|
|
|
5
|
-
describe('
|
|
5
|
+
describe('causePlugin', () => {
|
|
6
6
|
const statusPlugin = Error0.plugin().use('prop', 'status', {
|
|
7
7
|
init: (input: number) => input,
|
|
8
8
|
resolve: ({ flow }) => flow.find((value) => typeof value === 'number'),
|
|
@@ -23,7 +23,7 @@ describe('causeSerializePlugin', () => {
|
|
|
23
23
|
it('serializes and deserializes nested Error0 causes', () => {
|
|
24
24
|
const AppError = Error0.use(statusPlugin)
|
|
25
25
|
.use(codePlugin)
|
|
26
|
-
.use(
|
|
26
|
+
.use(causePlugin({ isPublic: true }))
|
|
27
27
|
const deepCauseError = new AppError('deep cause')
|
|
28
28
|
const causeError = new AppError('cause', { status: 409, code: 'NOT_FOUND', cause: deepCauseError })
|
|
29
29
|
const error = new AppError('root', { status: 500, cause: causeError })
|
|
@@ -50,4 +50,57 @@ describe('causeSerializePlugin', () => {
|
|
|
50
50
|
expect((recreated.cause as any).cause.code).toBe(undefined)
|
|
51
51
|
expect((recreated.cause as any).cause.cause).toBeUndefined()
|
|
52
52
|
})
|
|
53
|
+
|
|
54
|
+
it('supports variants', () => {
|
|
55
|
+
class DbError extends Error {
|
|
56
|
+
query: string
|
|
57
|
+
constructor(message: string, options: { cause?: unknown; query: string }) {
|
|
58
|
+
super(message, { cause: options.cause })
|
|
59
|
+
this.query = options.query
|
|
60
|
+
this.name = 'DbError'
|
|
61
|
+
}
|
|
62
|
+
static serialize(error: DbError): Record<string, unknown> {
|
|
63
|
+
return {
|
|
64
|
+
message: error.message,
|
|
65
|
+
query: error.query,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
static from(error: unknown): DbError {
|
|
69
|
+
if (error instanceof DbError) {
|
|
70
|
+
return error
|
|
71
|
+
}
|
|
72
|
+
const object = typeof error === 'object' && error !== null ? (error as Record<string, unknown>) : {}
|
|
73
|
+
const message =
|
|
74
|
+
typeof object.message === 'string' ? object.message : typeof error === 'string' ? error : 'Unknown error'
|
|
75
|
+
const query = typeof object.query === 'string' ? object.query : 'NOT_FOUND'
|
|
76
|
+
return new DbError(message, { cause: error, query })
|
|
77
|
+
}
|
|
78
|
+
static isSerialized(serializedCause: unknown): boolean {
|
|
79
|
+
return (
|
|
80
|
+
typeof serializedCause === 'object' &&
|
|
81
|
+
serializedCause !== null &&
|
|
82
|
+
'query' in serializedCause &&
|
|
83
|
+
typeof serializedCause.query === 'string'
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const AppError = Error0.use(statusPlugin)
|
|
88
|
+
.use(codePlugin)
|
|
89
|
+
.use(
|
|
90
|
+
causePlugin({
|
|
91
|
+
variants: {
|
|
92
|
+
DbError,
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
)
|
|
96
|
+
const dbError = new DbError('test', { query: 'SELECT * FROM users' })
|
|
97
|
+
const error = new AppError('root', { status: 500, cause: dbError })
|
|
98
|
+
const json = AppError.serialize(error, false)
|
|
99
|
+
expect(json.cause).toBeDefined()
|
|
100
|
+
expect((json.cause as any).query).toBe('SELECT * FROM users')
|
|
101
|
+
const recreated = AppError.from(json)
|
|
102
|
+
expect(recreated).toBeInstanceOf(AppError)
|
|
103
|
+
expect(recreated.cause).toBeInstanceOf(DbError)
|
|
104
|
+
expect((recreated.cause as any).query).toBe('SELECT * FROM users')
|
|
105
|
+
})
|
|
53
106
|
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Error0 } from '../index.js'
|
|
2
|
+
|
|
3
|
+
type Variant = {
|
|
4
|
+
new (...args: any[]): unknown
|
|
5
|
+
[Symbol.hasInstance]: (value: any) => boolean
|
|
6
|
+
isSerialized: (serializedCause: any) => boolean
|
|
7
|
+
serialize: (error: any) => unknown
|
|
8
|
+
from: (error: any) => unknown
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const causePlugin = <TVariants extends Record<string, Variant> = Record<never, Variant>>({
|
|
12
|
+
isPublic = false,
|
|
13
|
+
variants = undefined,
|
|
14
|
+
}: { isPublic?: boolean; variants?: TVariants } = {}) =>
|
|
15
|
+
Error0.plugin().cause({
|
|
16
|
+
serialize: ({ cause, isPublic: _isPublic, is, serialize }) => {
|
|
17
|
+
if (!isPublic && _isPublic) {
|
|
18
|
+
return undefined
|
|
19
|
+
}
|
|
20
|
+
if (variants) {
|
|
21
|
+
for (const variant of Object.values(variants)) {
|
|
22
|
+
if (cause instanceof variant) {
|
|
23
|
+
return variant.serialize(cause)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (is(cause)) {
|
|
28
|
+
return serialize(cause)
|
|
29
|
+
}
|
|
30
|
+
return undefined
|
|
31
|
+
},
|
|
32
|
+
deserialize: ({ cause, fromSerialized, isSerialized }) => {
|
|
33
|
+
if (variants) {
|
|
34
|
+
for (const variant of Object.values(variants)) {
|
|
35
|
+
if (variant.isSerialized(cause)) {
|
|
36
|
+
return variant.from(cause)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (isSerialized(cause)) {
|
|
41
|
+
return fromSerialized(cause)
|
|
42
|
+
}
|
|
43
|
+
return cause
|
|
44
|
+
},
|
|
45
|
+
})
|
package/src/plugins/code.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { Error0 } from '../index.js'
|
|
2
2
|
|
|
3
|
-
export const codePlugin = <TCode extends string>({
|
|
3
|
+
export const codePlugin = <TCode extends string>({
|
|
4
|
+
codes,
|
|
5
|
+
isPublic = false,
|
|
6
|
+
}: { codes?: TCode[]; isPublic?: boolean } = {}) => {
|
|
4
7
|
const isCode = (value: unknown): value is TCode =>
|
|
5
8
|
typeof value === 'string' && (!codes || codes.includes(value as TCode))
|
|
6
9
|
return Error0.plugin().prop('code', {
|
|
7
10
|
init: (code: TCode) => code,
|
|
8
11
|
resolve: ({ flow }) => flow.find(Boolean),
|
|
9
|
-
serialize: ({ resolved, isPublic }) =>
|
|
12
|
+
serialize: ({ resolved, isPublic: _isPublic }) => {
|
|
13
|
+
if (!isPublic && _isPublic) {
|
|
14
|
+
return undefined
|
|
15
|
+
}
|
|
16
|
+
return resolved
|
|
17
|
+
},
|
|
10
18
|
deserialize: ({ value, record }) => (isCode(value) ? value : undefined),
|
|
11
19
|
})
|
|
12
20
|
}
|