@nice-code/error 0.0.15

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.
Files changed (32) hide show
  1. package/README.md +1 -0
  2. package/build/index.js +2747 -0
  3. package/build/types/NiceError/NiceError.d.ts +141 -0
  4. package/build/types/NiceError/NiceError.enums.d.ts +5 -0
  5. package/build/types/NiceError/NiceError.types.d.ts +199 -0
  6. package/build/types/NiceError/NiceErrorHydrated.d.ts +49 -0
  7. package/build/types/NiceError/nice_error.static.d.ts +2 -0
  8. package/build/types/NiceErrorDefined/NiceErrorDefined.d.ts +135 -0
  9. package/build/types/NiceErrorDefined/defineNiceError.d.ts +7 -0
  10. package/build/types/NiceErrorDefined/err.d.ts +59 -0
  11. package/build/types/example/error_usage_example.d.ts +41 -0
  12. package/build/types/index.d.ts +16 -0
  13. package/build/types/internal/index.d.ts +1 -0
  14. package/build/types/internal/nice_core_errors.d.ts +44 -0
  15. package/build/types/test/helpers/nice_error_testing.static.d.ts +3 -0
  16. package/build/types/test/helpers/test_utils.d.ts +1 -0
  17. package/build/types/utils/castAndHydrate.d.ts +35 -0
  18. package/build/types/utils/castNiceError.d.ts +15 -0
  19. package/build/types/utils/handleWith.d.ts +49 -0
  20. package/build/types/utils/inspectPotentialError/inspectPotentialError.d.ts +2 -0
  21. package/build/types/utils/inspectPotentialError/inspectPotentialError.enums.d.ts +9 -0
  22. package/build/types/utils/inspectPotentialError/inspectPotentialError.types.d.ts +51 -0
  23. package/build/types/utils/isNiceErrorObject.d.ts +12 -0
  24. package/build/types/utils/isRegularErrorObject.d.ts +2 -0
  25. package/build/types/utils/jsErrorOrCastJsError.d.ts +1 -0
  26. package/build/types/utils/logger.d.ts +3 -0
  27. package/build/types/utils/matchFirst.d.ts +35 -0
  28. package/build/types/utils/packError/causePack.d.ts +2 -0
  29. package/build/types/utils/packError/msgPack.d.ts +2 -0
  30. package/build/types/utils/packError/packError.d.ts +3 -0
  31. package/build/types/utils/packError/packError.enums.d.ts +5 -0
  32. package/package.json +37 -0
@@ -0,0 +1,141 @@
1
+ import type { NiceErrorDefined } from "../NiceErrorDefined/NiceErrorDefined";
2
+ import type { IErrorCase } from "../utils/handleWith";
3
+ import { EErrorPackType } from "../utils/packError/packError.enums";
4
+ import { type INiceErrorDefinedProps, type INiceErrorJsonObject, type IRegularErrorJsonObject, type TErrorDataForIdMap, type TErrorReconciledData, type TExtractContextType, type TNiceErrorSchema, type TUnknownNiceErrorDef } from "./NiceError.types";
5
+ import type { NiceErrorHydrated } from "./NiceErrorHydrated";
6
+ type ContextOf<S extends TNiceErrorSchema, K extends keyof S> = TExtractContextType<S[K]>;
7
+ /** Full-featured construction from NiceErrorDefined.fromId / fromContext. */
8
+ export interface INiceErrorOptions<ERR_DEF extends INiceErrorDefinedProps, ID extends keyof ERR_DEF["schema"]> {
9
+ def: Omit<ERR_DEF, "schema">;
10
+ /** Primary id is first entry in ids. */
11
+ ids: ID[];
12
+ /** All active ids with their messages, http status codes, and context state. */
13
+ errorData: TErrorDataForIdMap<ERR_DEF["schema"]>;
14
+ message: string;
15
+ wasntNice?: boolean;
16
+ httpStatusCode?: number;
17
+ originError?: IRegularErrorJsonObject | undefined;
18
+ }
19
+ export declare class NiceError<ERR_DEF extends INiceErrorDefinedProps = TUnknownNiceErrorDef,
20
+ /**
21
+ * Union of active error-id keys.
22
+ * - After `fromId(id)`: exactly one key.
23
+ * - After `fromContext({...})`: a union of all supplied keys.
24
+ * - After `hasOneOfIds([a,b])`: narrows to that subset.
25
+ * - Default (bare construction / castNiceError): `TUnknownNiceErrorId`.
26
+ */
27
+ ACTIVE_IDS extends keyof ERR_DEF["schema"] & string = keyof ERR_DEF["schema"] & string> extends Error {
28
+ readonly name: "NiceError";
29
+ readonly def: Omit<ERR_DEF, "schema">;
30
+ /** Primary id is first entry in ids. */
31
+ readonly ids: ACTIVE_IDS[];
32
+ readonly wasntNice: boolean;
33
+ readonly httpStatusCode: number;
34
+ originError?: IRegularErrorJsonObject;
35
+ _packedState?: {
36
+ packedAs: EErrorPackType.cause_pack;
37
+ cause: unknown;
38
+ } | {
39
+ packedAs: EErrorPackType.msg_pack;
40
+ message: string;
41
+ } | undefined;
42
+ /** Internal: all active id → reconciled data pairs. */
43
+ protected readonly _errorDataMap: TErrorDataForIdMap<ERR_DEF["schema"]>;
44
+ constructor(options: INiceErrorOptions<ERR_DEF, ACTIVE_IDS>);
45
+ /**
46
+ * Type guard: returns `true` if this error was created with (or contains) the
47
+ * given `id`. After the guard, `getContext(id)` will be strongly typed.
48
+ */
49
+ hasId<ID extends keyof ERR_DEF["schema"] & string>(id: ID): this is NiceError<ERR_DEF, ID>;
50
+ /**
51
+ * Returns `true` if this error contains **at least one** of the supplied ids.
52
+ * Narrows `ACTIVE_IDS` to the matching subset of `IDS`.
53
+ */
54
+ hasOneOfIds<IDS extends ReadonlyArray<keyof ERR_DEF["schema"] & string>>(ids: IDS): this is NiceError<ERR_DEF, IDS[number]>;
55
+ /** `true` when this error was created with more than one id (via `fromContext`). */
56
+ get hasMultiple(): boolean;
57
+ /** Returns all active error ids on this instance. */
58
+ getIds(): Array<ACTIVE_IDS>;
59
+ /**
60
+ * Returns the typed context value for the given error id.
61
+ *
62
+ * TypeScript will only allow you to call this with an id that is part of
63
+ * `ACTIVE_IDS` (i.e. an id confirmed via `hasId` / `hasOneOfIds`, or passed
64
+ * to `fromId` / `fromContext`).
65
+ *
66
+ * @throws If the context is in the `"unhydrated"` state — the error was
67
+ * reconstructed from a JSON payload and its context has a custom serializer
68
+ * that hasn't been run yet. Call `niceErrorDefined.hydrate(error)` first.
69
+ */
70
+ getContext<ID extends ACTIVE_IDS>(id: ID): ContextOf<ERR_DEF["schema"], ID>;
71
+ getErrorDataForId<ID extends ACTIVE_IDS>(id: ID): TErrorReconciledData<ERR_DEF["schema"], ID> | undefined;
72
+ withOriginError(error: unknown): this;
73
+ /**
74
+ * Returns `true` if `other` has the same domain and the exact same set of
75
+ * active error ids as this error (order-independent).
76
+ *
77
+ * Useful for deduplication, retry logic, and asserting that two errors
78
+ * represent the same "kind" of problem without comparing context values.
79
+ *
80
+ * ```ts
81
+ * const a = err_auth.fromId("invalid_credentials", { username: "alice" });
82
+ * const b = err_auth.fromId("invalid_credentials", { username: "bob" });
83
+ * a.matches(b); // true — same domain + same id set
84
+ *
85
+ * const c = err_auth.fromId("account_locked");
86
+ * a.matches(c); // false — same domain, different id
87
+ * ```
88
+ */
89
+ matches(other: NiceError<any, any>): boolean;
90
+ toJsonObject(): INiceErrorJsonObject<ERR_DEF>;
91
+ hydrate(definedNiceError: NiceErrorDefined<ERR_DEF>): NiceErrorHydrated<ERR_DEF, ACTIVE_IDS>;
92
+ /**
93
+ * Iterates `cases` in order, finds the first whose domain matches this error
94
+ * (via `is()`), optionally further filters by active ids, hydrates the error,
95
+ * calls the handler, and returns `true`. Returns `false` if no case matched.
96
+ *
97
+ * Build cases with `forDomain` (any id in the domain) or `forIds` (specific
98
+ * id subset). Handlers are invoked synchronously — any returned Promise is
99
+ * not awaited. Use `handleWithAsync` when handlers are async.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const handled = error.handleWith([
104
+ * forIds(err_feature, ["not_found"], (h) => {
105
+ * res.status(404).json({ missing: h.getContext("not_found").resource });
106
+ * }),
107
+ * forDomain(err_feature, (h) => {
108
+ * matchFirst(h, {
109
+ * forbidden: ({ userId }) => res.status(403).json({ userId }),
110
+ * _: () => res.status(500).end(),
111
+ * });
112
+ * }),
113
+ * forDomain(err_service, (h) => {
114
+ * res.status(h.httpStatusCode).json({ error: h.message });
115
+ * }),
116
+ * ]);
117
+ * if (!handled) next(error);
118
+ * ```
119
+ */
120
+ handleWith(cases: ReadonlyArray<IErrorCase<any, any>>): boolean;
121
+ /**
122
+ * Same matching logic as `handleWith`, but `await`s the handler's returned
123
+ * Promise before resolving. Use this when your handlers perform async work
124
+ * (database writes, HTTP calls, etc.).
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const handled = await error.handleWithAsync([
129
+ * forDomain(err_payments, async (h) => {
130
+ * await db.logFailedPayment(h);
131
+ * await notifyOps(h.message);
132
+ * }),
133
+ * ]);
134
+ * ```
135
+ */
136
+ handleWithAsync(cases: ReadonlyArray<IErrorCase<any, any>>): Promise<boolean>;
137
+ get isPacked(): boolean;
138
+ pack(packType?: EErrorPackType): this;
139
+ unpack(): this;
140
+ }
141
+ export {};
@@ -0,0 +1,5 @@
1
+ export declare enum EContextSerializedState {
2
+ serde_unset = "serde_unset",
3
+ unhydrated = "unhydrated",
4
+ hydrated = "hydrated"
5
+ }
@@ -0,0 +1,199 @@
1
+ import type { EErrorPackType } from "../utils/packError/packError.enums";
2
+ import type { EContextSerializedState } from "./NiceError.enums";
3
+ export interface IRegularErrorJsonObject extends Omit<Error, "stack"> {
4
+ name: string;
5
+ message: string;
6
+ stack?: string | undefined;
7
+ cause?: unknown;
8
+ }
9
+ export type JSONSerializableValue = string | number | boolean | null | {
10
+ [key: string]: JSONSerializableValue;
11
+ } | JSONSerializableValue[];
12
+ interface ISerializationDefinition<C, D extends JSONSerializableValue> {
13
+ toJsonSerializable: (context: C) => D;
14
+ fromJsonSerializable: (obj: D) => C;
15
+ }
16
+ /** Describes the context attached to a single error id. */
17
+ export interface INiceErrorContextDefinition<C, D extends JSONSerializableValue = JSONSerializableValue> {
18
+ required?: boolean;
19
+ serialization?: ISerializationDefinition<C, D>;
20
+ }
21
+ /**
22
+ * A single entry in a NiceErrorDefined schema.
23
+ * `C` is the context value type (defaults to `never` = no context).
24
+ */
25
+ export interface INiceErrorIdMetadata<C = never, D extends JSONSerializableValue = JSONSerializableValue> {
26
+ context?: [C] extends [never] ? never : INiceErrorContextDefinition<C, D>;
27
+ /** Static message string OR a function that receives the context value and returns a string. */
28
+ message?: [C] extends [never] ? string : string | ((context: C) => string);
29
+ httpStatusCode?: [C] extends [never] ? number : number | ((context: C) => number);
30
+ }
31
+ /** A record mapping error-id string keys to their metadata. */
32
+ export type TNiceErrorSchema = Record<string, INiceErrorIdMetadata<any>>;
33
+ /** Extracts the raw context value type `C` from a single schema entry. */
34
+ export type TExtractContextType<M> = M extends INiceErrorIdMetadata<infer C> ? C : never;
35
+ /**
36
+ * Given a schema entry M, returns the context argument type expected by `fromId`:
37
+ *
38
+ * - `C = never` → `undefined` (no context field on this id)
39
+ * - `C` defined, `required: true` → `C` (context is a required argument)
40
+ * - `C` defined, `required` absent/false → `C | undefined` (context is optional)
41
+ *
42
+ * Note: `required: true` must be a literal `true` for TypeScript to narrow correctly.
43
+ * Use the `err()` helper (which preserves literal types) rather than writing schema
44
+ * entries inline without `as const`.
45
+ */
46
+ export type ExtractFromIdContextArg<M> = M extends INiceErrorIdMetadata<infer C> ? [C] extends [never] ? undefined : M extends {
47
+ context: {
48
+ required: true;
49
+ };
50
+ } ? C : C | undefined : undefined;
51
+ /**
52
+ * No custom serializer is defined for this error id's context.
53
+ * `value` holds the typed context directly (plain JSON-safe value, or `undefined`
54
+ * if the context was optional and not provided).
55
+ *
56
+ * This state is safe across a JSON round-trip because no type information is lost.
57
+ */
58
+ export type TContextStateNoSerialization<C> = {
59
+ kind: EContextSerializedState.serde_unset;
60
+ /** The typed context value (or `undefined` if optional and not provided). */
61
+ value: C | undefined;
62
+ };
63
+ /**
64
+ * A custom serializer is defined, but the context has **not** been deserialized
65
+ * from its JSON-wire form yet.
66
+ *
67
+ * This state occurs after `castNiceError` reconstructs an error from a serialized
68
+ * payload. `value` is absent — the raw serialized representation is in `serialized`.
69
+ *
70
+ * Call `niceErrorDefined.hydrate(error)` to reconstruct the typed context via
71
+ * `fromJsonSerializable` and advance to the `"hydrated"` state.
72
+ */
73
+ export type TContextStateUnhydrated = {
74
+ kind: EContextSerializedState.unhydrated;
75
+ /** The JSON-serializable representation of the original context. */
76
+ serialized: JSONSerializableValue;
77
+ };
78
+ /**
79
+ * A custom serializer is defined, and the context is fully deserialized to its
80
+ * original typed value.
81
+ *
82
+ * This state is set when the error is first created via `fromId`/`fromContext`,
83
+ * or after `niceErrorDefined.hydrate(error)` reconstructs it.
84
+ *
85
+ * Note: `toJsonObject()` intentionally downgrades this to `"unhydrated"` on
86
+ * output so the flag cannot survive a JSON round-trip as a false positive.
87
+ */
88
+ export type TContextStateHydrated<C> = {
89
+ kind: EContextSerializedState.hydrated;
90
+ /** The typed context value — the original value as provided at error creation. */
91
+ value: C;
92
+ /** The JSON-serializable representation (produced by `toJsonSerializable`). */
93
+ serialized: JSONSerializableValue;
94
+ };
95
+ /**
96
+ * Runtime context state — union of all three lifecycle states.
97
+ * Stored in `_errorDataMap` on a live `NiceError` instance.
98
+ */
99
+ export type TContextState<C> = TContextStateNoSerialization<C> | TContextStateUnhydrated | TContextStateHydrated<C>;
100
+ /**
101
+ * Wire-safe context state — excludes `"hydrated"` because `toJsonObject()` downgrades
102
+ * it to `"unhydrated"` before serialization. This is the only state that appears in
103
+ * `INiceErrorJsonObject` and on errors reconstructed via `castNiceError`.
104
+ */
105
+ export type TSerializedContextState<C> = TContextStateNoSerialization<C> | TContextStateUnhydrated;
106
+ /**
107
+ * Runtime reconciled data for a single error id, stored in `_errorDataMap`.
108
+ * Uses the full `TContextState` union (including `"hydrated"`).
109
+ */
110
+ export type TErrorReconciledData<SCHEMA extends TNiceErrorSchema, K extends keyof SCHEMA> = {
111
+ contextState: TContextState<TExtractContextType<SCHEMA[K]>>;
112
+ message: string;
113
+ httpStatusCode: number;
114
+ };
115
+ /**
116
+ * Wire-safe reconciled data — uses `TSerializedContextState` (no `"hydrated"`).
117
+ * This is the shape stored in `INiceErrorJsonObject.errorData` and transmitted
118
+ * over the wire. `"hydrated"` is excluded so that the serialization state can
119
+ * never be trusted after a JSON round-trip.
120
+ */
121
+ export type TSerializedErrorReconciledData<SCHEMA extends TNiceErrorSchema, K extends keyof SCHEMA> = {
122
+ contextState: TSerializedContextState<TExtractContextType<SCHEMA[K]>>;
123
+ message: string;
124
+ httpStatusCode: number;
125
+ };
126
+ export type TErrorDataForIdMap<SCHEMA extends TNiceErrorSchema> = {
127
+ [K in keyof SCHEMA]?: TErrorReconciledData<SCHEMA, K>;
128
+ };
129
+ /** Wire-safe version of `TErrorDataForIdMap`. Used in `INiceErrorJsonObject`. */
130
+ export type TSerializedErrorDataMap<SCHEMA extends TNiceErrorSchema> = {
131
+ [K in keyof SCHEMA]?: TSerializedErrorReconciledData<SCHEMA, K>;
132
+ };
133
+ /**
134
+ * The Partial record that `fromContext` accepts: callers supply one or more
135
+ * { [errorId]: contextValue } entries and NiceError stores them all.
136
+ */
137
+ export type TFromContextInput<SCHEMA extends TNiceErrorSchema> = {
138
+ [K in keyof SCHEMA]?: TExtractContextType<SCHEMA[K]> | undefined;
139
+ };
140
+ /**
141
+ * Resolves the args tuple for `fromId` / `addId`:
142
+ *
143
+ * - No context on this id → `[id]`
144
+ * - Context defined, `required: true` → `[id, context]`
145
+ * - Context defined, `required` absent/false → `[id] | [id, context]`
146
+ */
147
+ export type FromIdArgs<ERR_DEF extends INiceErrorDefinedProps, K extends keyof ERR_DEF["schema"]> = [ExtractFromIdContextArg<ERR_DEF["schema"][K]>] extends [undefined] ? [id: K] : [undefined] extends [ExtractFromIdContextArg<ERR_DEF["schema"][K]>] ? [id: K] | [id: K, context: NonNullable<ExtractFromIdContextArg<ERR_DEF["schema"][K]>>] : [id: K, context: ExtractFromIdContextArg<ERR_DEF["schema"][K]>];
148
+ export interface IPackErrorAs_Base {
149
+ packAs: EErrorPackType;
150
+ }
151
+ export interface IPackErrorAs_MatchEnvVar extends IPackErrorAs_Base {
152
+ matchEnvVar: string;
153
+ }
154
+ export interface IDefineNewNiceErrorDomainOptions<ERR_DOMAIN extends string = string, SCHEMA extends TNiceErrorSchema = TNiceErrorSchema> {
155
+ defaultHttpStatusCode?: number;
156
+ defaultMessage?: string;
157
+ packAs?: () => EErrorPackType | void;
158
+ domain: ERR_DOMAIN;
159
+ schema: SCHEMA;
160
+ }
161
+ export interface INiceErrorDefinedProps<ERR_DOMAINS extends string[] = string[], SCHEMA extends TNiceErrorSchema = TNiceErrorSchema> {
162
+ packAs?: () => EErrorPackType | void;
163
+ defaultHttpStatusCode?: number;
164
+ defaultMessage?: string;
165
+ domain: ERR_DOMAINS[number];
166
+ allDomains: ERR_DOMAINS;
167
+ schema: SCHEMA;
168
+ }
169
+ export interface INiceErrorJsonObject<ERR_DEF extends INiceErrorDefinedProps = INiceErrorDefinedProps, ID extends keyof ERR_DEF["schema"] & string = keyof ERR_DEF["schema"] & string> {
170
+ name: "NiceError";
171
+ def: Omit<ERR_DEF, "schema">;
172
+ ids: ID[];
173
+ /** Wire-safe error data — context is in `"raw_unset"` or `"unhydrated"` state only. */
174
+ errorData: TSerializedErrorDataMap<ERR_DEF["schema"]>;
175
+ wasntNice: boolean;
176
+ message: string;
177
+ httpStatusCode: number;
178
+ /** The stack trace of the NiceError at the point it was created. */
179
+ stack?: string;
180
+ cause?: unknown;
181
+ originError?: IRegularErrorJsonObject | undefined;
182
+ }
183
+ /**
184
+ * The widest ERR_DEF used when creating a NiceError without a definition
185
+ * (bare construction, castNiceError, etc.).
186
+ *
187
+ * Uses the base `INiceErrorDefinedProps` defaults (`string[]` domains,
188
+ * `TNiceErrorSchema` schema) so that type-guard narrowing via `is()` works
189
+ * correctly — `string & "specific_domain"` narrows to `"specific_domain"`
190
+ * instead of collapsing the intersection to `never`.
191
+ */
192
+ export type TUnknownNiceErrorDef = INiceErrorDefinedProps;
193
+ /**
194
+ * Wide id type for bare / cast NiceErrors that have no schema.
195
+ * Using `string` (rather than a literal `"unknown"`) ensures that
196
+ * `is()` type-guard intersections narrow cleanly: `string & K` = `K`.
197
+ */
198
+ export type TUnknownNiceErrorId = string;
199
+ export {};
@@ -0,0 +1,49 @@
1
+ import type { NiceErrorDefined } from "../NiceErrorDefined/NiceErrorDefined";
2
+ import { type INiceErrorOptions, NiceError } from "./NiceError";
3
+ import type { ExtractFromIdContextArg, INiceErrorDefinedProps, TFromContextInput, TUnknownNiceErrorDef } from "./NiceError.types";
4
+ /**
5
+ * Resolves the args tuple for `addId` — mirrors `FromIdArgs` exactly so that
6
+ * optional vs required context is consistent across both `fromId` and `addId`.
7
+ *
8
+ * - No context on this id → `[id]`
9
+ * - Context defined, `required: true` → `[id, context]`
10
+ * - Context defined, `required` absent/false → `[id] | [id, context]`
11
+ */
12
+ type AddIdArgs<ERR_DEF extends INiceErrorDefinedProps, K extends keyof ERR_DEF["schema"] & string> = [ExtractFromIdContextArg<ERR_DEF["schema"][K]>] extends [undefined] ? [id: K] : [undefined] extends [ExtractFromIdContextArg<ERR_DEF["schema"][K]>] ? [id: K] | [id: K, context: NonNullable<ExtractFromIdContextArg<ERR_DEF["schema"][K]>>] : [id: K, context: ExtractFromIdContextArg<ERR_DEF["schema"][K]>];
13
+ /** Full-featured construction from NiceErrorDefined.fromId / fromContext. */
14
+ export interface INiceErrorHydratedOptions<ERR_DEF extends INiceErrorDefinedProps, ID extends keyof ERR_DEF["schema"] & string> extends INiceErrorOptions<ERR_DEF, ID> {
15
+ def: ERR_DEF;
16
+ niceErrorDefined: NiceErrorDefined<ERR_DEF>;
17
+ }
18
+ export declare class NiceErrorHydrated<ERR_DEF extends INiceErrorDefinedProps = TUnknownNiceErrorDef,
19
+ /**
20
+ * Union of active error-id keys.
21
+ * - After `fromId(id)`: exactly one key.
22
+ * - After `fromContext({...})`: a union of all supplied keys.
23
+ * - After `hasOneOfIds([a,b])`: narrows to that subset.
24
+ * - Default (bare construction / castNiceError): `TUnknownNiceErrorId`.
25
+ */
26
+ ACTIVE_IDS extends keyof ERR_DEF["schema"] & string = keyof ERR_DEF["schema"] & string> extends NiceError<ERR_DEF, ACTIVE_IDS> {
27
+ readonly def: ERR_DEF;
28
+ private readonly niceErrorDefined;
29
+ constructor(options: INiceErrorHydratedOptions<ERR_DEF, ACTIVE_IDS>);
30
+ /**
31
+ * Returns a **new** `NiceErrorHydrated` with additional id+context entries merged in.
32
+ * The returned error's `ACTIVE_IDS` is the union of the original ids and the
33
+ * newly supplied keys.
34
+ *
35
+ * ```ts
36
+ * const err = errDef.fromId("id_a", { a: 1 })
37
+ * .addContext({ id_b: { b: "x" } });
38
+ * err.getIds(); // ["id_a", "id_b"]
39
+ * ```
40
+ */
41
+ addContext<INPUT extends TFromContextInput<ERR_DEF["schema"]>>(context: INPUT & Record<Exclude<keyof INPUT, keyof ERR_DEF["schema"] & string>, never>): NiceErrorHydrated<ERR_DEF, ACTIVE_IDS | (keyof INPUT & string)>;
42
+ /**
43
+ * Returns a **new** `NiceErrorHydrated` with an additional error id (and its context,
44
+ * if the schema requires one). Equivalent to `addContext({ [id]: context })`
45
+ * but mirrors the `fromId` ergonomics for single-id additions.
46
+ */
47
+ addId<K extends keyof ERR_DEF["schema"] & string>(...args: AddIdArgs<ERR_DEF, K>): NiceErrorHydrated<ERR_DEF, ACTIVE_IDS | K>;
48
+ }
49
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare const DUR_OBJ_PACK_PREFIX = "NE_DUROBJ[[";
2
+ export declare const DUR_OBJ_PACK_SUFFIX = "]]NE_DUROBJ";
@@ -0,0 +1,135 @@
1
+ import { NiceError } from "../NiceError/NiceError";
2
+ import { type FromIdArgs, type IDefineNewNiceErrorDomainOptions, type INiceErrorDefinedProps, type TErrorReconciledData, type TFromContextInput } from "../NiceError/NiceError.types";
3
+ import { NiceErrorHydrated } from "../NiceError/NiceErrorHydrated";
4
+ import { type EErrorPackType } from "../utils/packError/packError.enums";
5
+ type ChildDef<PARENT_DEF extends INiceErrorDefinedProps, SUB extends IDefineNewNiceErrorDomainOptions> = {
6
+ domain: SUB["domain"];
7
+ allDomains: [SUB["domain"], ...PARENT_DEF["allDomains"]];
8
+ schema: SUB["schema"];
9
+ };
10
+ /**
11
+ * Extracts the union of keys present in a `TFromContextInput` object.
12
+ * e.g. `{ invalid_credentials: {...} }` → `"invalid_credentials"`
13
+ */
14
+ type KeysOfContextInput<INPUT> = keyof INPUT & string;
15
+ /**
16
+ * Infers the strongly-typed `NiceError` class type from a `NiceErrorDefined` instance.
17
+ *
18
+ * `ACTIVE_IDS` is set to the full union of all schema keys. Use `hasId` /
19
+ * `hasOneOfIds` to narrow further at the call site.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const err_user_auth = defineNiceError({ domain: "err_user_auth", schema: { ... } });
24
+ * type TUserAuthError = InferNiceError<typeof err_user_auth>;
25
+ * // → NiceError<{ domain: "err_user_auth"; ... }, keyof schema>
26
+ * ```
27
+ */
28
+ export type InferNiceError<T extends NiceErrorDefined<any>> = T extends NiceErrorDefined<infer ERR_DEF> ? NiceError<ERR_DEF, keyof ERR_DEF["schema"] & string> : never;
29
+ /**
30
+ * Infers the strongly-typed `NiceErrorHydrated` class type from a `NiceErrorDefined` instance.
31
+ *
32
+ * Use this when you need the builder methods (`addId`, `addContext`) as part of
33
+ * the inferred type — e.g. for function return types or variable annotations.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const err_user_auth = defineNiceError({ domain: "err_user_auth", schema: { ... } });
38
+ * type TUserAuthErrorHydrated = InferNiceErrorHydrated<typeof err_user_auth>;
39
+ * // → NiceErrorHydrated<{ domain: "err_user_auth"; ... }, keyof schema>
40
+ * ```
41
+ */
42
+ export type InferNiceErrorHydrated<T extends NiceErrorDefined<any>> = T extends NiceErrorDefined<infer ERR_DEF> ? NiceErrorHydrated<ERR_DEF, keyof ERR_DEF["schema"] & string> : never;
43
+ export declare class NiceErrorDefined<ERR_DEF extends INiceErrorDefinedProps = INiceErrorDefinedProps> {
44
+ readonly domain: ERR_DEF["domain"];
45
+ readonly allDomains: ERR_DEF["allDomains"];
46
+ readonly defaultHttpStatusCode?: number;
47
+ readonly defaultMessage?: string;
48
+ /** Kept for runtime use (message resolution, httpStatusCode, context serialization, etc.). */
49
+ private readonly _schema;
50
+ private _definedChildNiceErrors;
51
+ private _definedParentNiceError?;
52
+ /** Set by `.packAs()` — explicit per-instance override, takes priority over `_packAsFn`. */
53
+ private _setPack?;
54
+ /** Set at definition time — called dynamically each time an error is created. */
55
+ private _packAsFn?;
56
+ constructor(definition: ERR_DEF);
57
+ /**
58
+ * Creates a child domain that inherits this domain in `allDomains`.
59
+ * The child has its own schema and its own domain string.
60
+ */
61
+ createChildDomain<SUB extends IDefineNewNiceErrorDomainOptions>(subErrorDef: SUB & {
62
+ [K in Exclude<keyof SUB, keyof IDefineNewNiceErrorDomainOptions>]: never;
63
+ }): NiceErrorDefined<ChildDef<ERR_DEF, SUB>>;
64
+ protected addParentNiceErrorDefined<PARENT_DEF extends INiceErrorDefinedProps>(parentError: NiceErrorDefined<PARENT_DEF>): void;
65
+ protected addChildNiceErrorDefined<CHILD_DEF extends INiceErrorDefinedProps>(child: NiceErrorDefined<CHILD_DEF>): void;
66
+ packAs(pack: EErrorPackType): this;
67
+ private createError;
68
+ /**
69
+ * Promotes a plain `NiceError<ERR_DEF>` back into a `NiceErrorHydrated` so
70
+ * that builder methods (`addId`, `addContext`, etc.) are available again.
71
+ *
72
+ * For each active id, if the context is in the `"unhydrated"` state (i.e. the
73
+ * error was reconstructed from a JSON payload), `hydrate` calls
74
+ * `fromJsonSerializable` to reconstruct the typed value and advances the state
75
+ * to `"hydrated"`. Ids already in `"hydrated"` or `"raw_unset"` state
76
+ * are passed through unchanged.
77
+ *
78
+ * @throws If `error.def.domain` does not match this definition's domain. Use
79
+ * `niceErrorDefined.is(error)` before calling `hydrate` to ensure compatibility.
80
+ *
81
+ * ```ts
82
+ * const raw = castNiceError(apiResponseBody);
83
+ *
84
+ * if (err_user_auth.is(raw)) {
85
+ * const hydrated = err_user_auth.hydrate(raw);
86
+ * // hydrated.getContext("invalid_credentials") — fully typed, no throw
87
+ * // hydrated.addId / addContext — available again
88
+ * }
89
+ * ```
90
+ */
91
+ hydrate<ACTIVE_IDS extends keyof ERR_DEF["schema"] & string>(error: NiceError<ERR_DEF, ACTIVE_IDS>): NiceErrorHydrated<ERR_DEF, ACTIVE_IDS>;
92
+ /**
93
+ * Creates a `NiceErrorHydrated` for a single error id.
94
+ *
95
+ * - `id` autocompletes to the schema keys.
96
+ * - The second argument `context` is required / optional / absent based on
97
+ * whether the schema entry declares `context.required: true`.
98
+ * - The returned error has `ACTIVE_IDS` narrowed to exactly `K`, so
99
+ * `getContext(id)` is immediately strongly typed.
100
+ */
101
+ fromId<K extends keyof ERR_DEF["schema"] & string>(...args: FromIdArgs<ERR_DEF, K>): NiceErrorHydrated<ERR_DEF, K>;
102
+ fromContext<INPUT extends TFromContextInput<ERR_DEF["schema"]>>(context: INPUT & Record<Exclude<keyof INPUT, keyof ERR_DEF["schema"]>, never>): NiceErrorHydrated<ERR_DEF, KeysOfContextInput<INPUT>>;
103
+ /**
104
+ * Returns `true` if `error` is a `NiceError` whose `def.domain` exactly matches
105
+ * this definition's domain.
106
+ *
107
+ * Use this after `castNiceError` to narrow an unknown error to this specific
108
+ * domain before accessing its typed ids/context:
109
+ *
110
+ * ```ts
111
+ * const caught = castNiceError(e);
112
+ *
113
+ * if (err_user_auth.is(caught)) {
114
+ * // caught is now NiceError<typeof err_user_auth's ERR_DEF>
115
+ * const hydrated = err_user_auth.hydrate(caught);
116
+ * const { username } = hydrated.getContext("invalid_credentials");
117
+ * }
118
+ * ```
119
+ */
120
+ isExact(error: unknown): error is NiceError<ERR_DEF, keyof ERR_DEF["schema"] & string>;
121
+ isThisOrChild(error: unknown): boolean;
122
+ /**
123
+ * Returns `true` if this domain appears anywhere in the target's ancestry
124
+ * chain (including an exact domain match).
125
+ *
126
+ * Accepts either a `NiceErrorDefined` (domain definition) or a `NiceError`
127
+ * instance (extracts the domain from its `def`).
128
+ */
129
+ isParentOf(target: NiceErrorDefined<any> | NiceError<any, any>): boolean;
130
+ private _buildDef;
131
+ private _resolveMessage;
132
+ private _resolveHttpStatusCode;
133
+ reconcileErrorDataForId(id: keyof ERR_DEF["schema"] & string, context: TFromContextInput<ERR_DEF["schema"]>[typeof id]): TErrorReconciledData<ERR_DEF["schema"], typeof id>;
134
+ }
135
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { IDefineNewNiceErrorDomainOptions, TNiceErrorSchema } from "../NiceError/NiceError.types";
2
+ import { NiceErrorDefined } from "./NiceErrorDefined";
3
+ export declare const defineNiceError: <ERR_DOMAIN extends string, SCHEMA extends TNiceErrorSchema>(definition: IDefineNewNiceErrorDomainOptions<ERR_DOMAIN, SCHEMA>) => NiceErrorDefined<{
4
+ domain: ERR_DOMAIN;
5
+ allDomains: [ERR_DOMAIN];
6
+ schema: SCHEMA;
7
+ }>;
@@ -0,0 +1,59 @@
1
+ import type { INiceErrorContextDefinition, INiceErrorIdMetadata, JSONSerializableValue } from "../NiceError/NiceError.types";
2
+ /**
3
+ * Schema entry factory — the idiomatic way to define a single error id in a
4
+ * `NiceErrorDefined` schema.
5
+ *
6
+ * Using `err()` instead of writing schema entries as plain object literals
7
+ * ensures TypeScript resolves the context argument as **required** vs **optional**
8
+ * correctly in `fromId` / `addId` call sites.
9
+ *
10
+ * How `required` affects the call site:
11
+ *
12
+ * | Schema entry | `fromId("id")` | `fromId("id", ctx)` |
13
+ * |------------------------------------|----------------|---------------------|
14
+ * | `err()` / `err({ ... })` | ✓ | ✗ (no context) |
15
+ * | `err<C>({ context: {} })` | ✓ | ✓ (optional ctx) |
16
+ * | `err<C>({ context: { required: true } })` | ✗ | ✓ (required ctx) |
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // No context — `fromId("not_found")` takes no second argument.
21
+ * not_found: err({ message: "Resource not found", httpStatusCode: 404 }),
22
+ *
23
+ * // Optional context — second arg accepted but not required.
24
+ * rate_limited: err<{ retryAfter: number }>({
25
+ * message: (ctx) => ctx ? `Retry after ${ctx.retryAfter}s` : "Rate limited",
26
+ * httpStatusCode: 429,
27
+ * context: {},
28
+ * }),
29
+ *
30
+ * // Required context — `fromId("invalid_input", { field: "email" })` (second arg is required).
31
+ * invalid_input: err<{ field: string }>({
32
+ * message: ({ field }) => `Invalid value for field: ${field}`,
33
+ * httpStatusCode: 422,
34
+ * context: { required: true },
35
+ * }),
36
+ *
37
+ * // Required context with custom serialization (for non-JSON-safe context values).
38
+ * fs_error: err<{ cause: NodeJS.ErrnoException }>({
39
+ * message: ({ cause }) => `File system error: ${cause.message}`,
40
+ * context: {
41
+ * required: true,
42
+ * serialization: {
43
+ * toJsonSerializable: ({ cause }) => ({ code: cause.code, message: cause.message }),
44
+ * fromJsonSerializable: (obj) => ({ cause: Object.assign(new Error(obj.message), obj) }),
45
+ * },
46
+ * },
47
+ * }),
48
+ * ```
49
+ */
50
+ export declare function err<C, D extends JSONSerializableValue = Record<string, any>>(meta: INiceErrorIdMetadata<C, D> & {
51
+ context: INiceErrorContextDefinition<C, D> & {
52
+ required: true;
53
+ };
54
+ }): INiceErrorIdMetadata<C> & {
55
+ context: {
56
+ required: true;
57
+ };
58
+ };
59
+ export declare function err<C = never>(meta?: INiceErrorIdMetadata<C>): INiceErrorIdMetadata<C>;
@@ -0,0 +1,41 @@
1
+ export declare const err_example_app: import("..").NiceErrorDefined<{
2
+ domain: "err_example_app";
3
+ allDomains: ["err_example_app"];
4
+ schema: {};
5
+ }>;
6
+ export declare enum EErrId_UserAuth {
7
+ invalid_credentials = "invalid_credentials",
8
+ account_locked = "account_locked"
9
+ }
10
+ export declare const err_user_auth: import("..").NiceErrorDefined<{
11
+ domain: string;
12
+ allDomains: [string, "err_example_app"];
13
+ schema: {
14
+ invalid_credentials: import("..").INiceErrorIdMetadata<{
15
+ username: string;
16
+ }, import("..").JSONSerializableValue> & {
17
+ context: {
18
+ required: true;
19
+ };
20
+ };
21
+ account_locked: import("..").INiceErrorIdMetadata<any, import("..").JSONSerializableValue>;
22
+ };
23
+ }>;
24
+ export declare enum EErrId_UserAuth_Registration {
25
+ password_error = "password_error",
26
+ password_too_short = "password_too_short"
27
+ }
28
+ export declare const err_user_auth_registration: import("..").NiceErrorDefined<{
29
+ domain: string;
30
+ allDomains: [string, string, "err_example_app"];
31
+ schema: {
32
+ password_too_short: import("..").INiceErrorIdMetadata<{
33
+ minLength: number;
34
+ }, import("..").JSONSerializableValue> & {
35
+ context: {
36
+ required: true;
37
+ };
38
+ };
39
+ password_error: import("..").INiceErrorIdMetadata<any, import("..").JSONSerializableValue>;
40
+ };
41
+ }>;
@@ -0,0 +1,16 @@
1
+ export * from "./internal";
2
+ export * from "./NiceError/NiceError";
3
+ export * from "./NiceError/NiceError.types";
4
+ export * from "./NiceError/NiceErrorHydrated";
5
+ export * from "./NiceErrorDefined/defineNiceError";
6
+ export * from "./NiceErrorDefined/err";
7
+ export * from "./NiceErrorDefined/NiceErrorDefined";
8
+ export * from "./utils/castAndHydrate";
9
+ export * from "./utils/castNiceError";
10
+ export * from "./utils/handleWith";
11
+ export * from "./utils/isNiceErrorObject";
12
+ export * from "./utils/isRegularErrorObject";
13
+ export * from "./utils/matchFirst";
14
+ export * from "./utils/packError/causePack";
15
+ export * from "./utils/packError/msgPack";
16
+ export * from "./utils/packError/packError.enums";
@@ -0,0 +1 @@
1
+ export * from "./nice_core_errors";