wellcrafted 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/error/index.d.ts +2 -2
- package/dist/error/index.js +2 -2
- package/dist/{error-Dy9wXt5_.js → error-DLVm7dsh.js} +45 -2
- package/dist/error-DLVm7dsh.js.map +1 -0
- package/dist/index-BNWUFJ69.d.ts +129 -0
- package/dist/index-BNWUFJ69.d.ts.map +1 -0
- package/dist/json.d.ts +1 -1
- package/dist/json.js +1 -1
- package/dist/logger/index.d.ts +37 -3
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +37 -3
- package/dist/logger/index.js.map +1 -1
- package/dist/testing.js +1 -1
- package/package.json +1 -1
- package/dist/error-Dy9wXt5_.js.map +0 -1
- package/dist/index-B9PnZCTt.d.ts +0 -73
- package/dist/index-B9PnZCTt.d.ts.map +0 -1
package/dist/error/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "../result-DKwq9BCr.js";
|
|
2
2
|
import { AnyTaggedError, DefineErrorsReturn, ErrorBody, ErrorsConfig, InferError, InferErrors, ValidatedConfig } from "../types-tXXk7K9Q.js";
|
|
3
|
-
import { defineErrors, extractErrorMessage } from "../index-
|
|
4
|
-
export { AnyTaggedError, DefineErrorsReturn, ErrorBody, ErrorsConfig, InferError, InferErrors, ValidatedConfig, defineErrors, extractErrorMessage };
|
|
3
|
+
import { DefineHttpErrorsReturn, HttpErrorFactory, HttpErrorsConfig, InferHttpError, InferHttpErrors, ValidatedHttpConfig, defineErrors, defineHttpErrors, extractErrorMessage } from "../index-BNWUFJ69.js";
|
|
4
|
+
export { AnyTaggedError, DefineErrorsReturn, DefineHttpErrorsReturn, ErrorBody, ErrorsConfig, HttpErrorFactory, HttpErrorsConfig, InferError, InferErrors, InferHttpError, InferHttpErrors, ValidatedConfig, ValidatedHttpConfig, defineErrors, defineHttpErrors, extractErrorMessage };
|
package/dist/error/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "../result-C5cJ1_WU.js";
|
|
2
|
-
import { defineErrors, extractErrorMessage } from "../error-
|
|
2
|
+
import { defineErrors, defineHttpErrors, extractErrorMessage } from "../error-DLVm7dsh.js";
|
|
3
3
|
|
|
4
|
-
export { defineErrors, extractErrorMessage };
|
|
4
|
+
export { defineErrors, defineHttpErrors, extractErrorMessage };
|
|
@@ -66,6 +66,49 @@ function defineErrors(config) {
|
|
|
66
66
|
return result;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/error/defineHttpErrors.ts
|
|
71
|
+
/**
|
|
72
|
+
* Defines a set of typed HTTP error factories, each paired with its HTTP
|
|
73
|
+
* status code.
|
|
74
|
+
*
|
|
75
|
+
* Like `defineErrors`, each factory stamps `name` onto the error and wraps it
|
|
76
|
+
* in `Err`. Unlike `defineErrors`, each factory function carries a static
|
|
77
|
+
* `.status` property with the literal HTTP status code — the status is never
|
|
78
|
+
* included in the serialized error body.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const AssetError = defineHttpErrors({
|
|
83
|
+
* MissingFile: [400, () => ({ message: 'Missing file' })],
|
|
84
|
+
* FileTooLarge: [413, ({ size }: { size: number }) => ({ message: `File too large: ${size}`, size })],
|
|
85
|
+
* StorageLimitExceeded: [402, () => ({ message: 'Storage limit exceeded' })],
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* type AssetError = InferHttpErrors<typeof AssetError>;
|
|
89
|
+
*
|
|
90
|
+
* // In a Hono route handler:
|
|
91
|
+
* return c.json(AssetError.MissingFile(), AssetError.MissingFile.status);
|
|
92
|
+
* // wire body: { error: { name: 'MissingFile', message: 'Missing file' }, data: null }
|
|
93
|
+
* // http status: 400
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
function defineHttpErrors(config) {
|
|
97
|
+
const result = {};
|
|
98
|
+
for (const [name, [status, factory]] of Object.entries(config)) {
|
|
99
|
+
const fn = (...args) => {
|
|
100
|
+
const body = factory(...args);
|
|
101
|
+
return Err(Object.freeze({
|
|
102
|
+
...body,
|
|
103
|
+
name
|
|
104
|
+
}));
|
|
105
|
+
};
|
|
106
|
+
fn.status = status;
|
|
107
|
+
result[name] = fn;
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
|
|
69
112
|
//#endregion
|
|
70
113
|
//#region src/error/extractErrorMessage.ts
|
|
71
114
|
/**
|
|
@@ -103,5 +146,5 @@ function extractErrorMessage(error) {
|
|
|
103
146
|
}
|
|
104
147
|
|
|
105
148
|
//#endregion
|
|
106
|
-
export { defineErrors, extractErrorMessage };
|
|
107
|
-
//# sourceMappingURL=error-
|
|
149
|
+
export { defineErrors, defineHttpErrors, extractErrorMessage };
|
|
150
|
+
//# sourceMappingURL=error-DLVm7dsh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-DLVm7dsh.js","names":["config: TConfig & ValidatedConfig<TConfig>","result: Record<string, unknown>","config: TConfig & ValidatedHttpConfig<TConfig>","result: Record<string, unknown>","error: unknown"],"sources":["../src/error/defineErrors.ts","../src/error/defineHttpErrors.ts","../src/error/extractErrorMessage.ts"],"sourcesContent":["import { Err } from \"../result/result.js\";\nimport type {\n\tDefineErrorsReturn,\n\tErrorsConfig,\n\tValidatedConfig,\n} from \"./types.js\";\n\n/**\n * Defines a set of typed error factories using Rust-style namespaced variants.\n *\n * Each key is a short variant name (the namespace provides context). Every\n * factory returns `Err<...>` directly — ready for `trySync`/`tryAsync` catch\n * handlers. The variant name is stamped as `name` on the error object.\n *\n * @example\n * ```ts\n * const HttpError = defineErrors({\n * Connection: ({ cause }: { cause: unknown }) => ({\n * message: `Failed to connect: ${extractErrorMessage(cause)}`,\n * cause,\n * }),\n * Response: ({ status }: { status: number; bodyMessage?: string }) => ({\n * message: `HTTP ${status}`,\n * status,\n * }),\n * Parse: ({ cause }: { cause: unknown }) => ({\n * message: `Failed to parse response body: ${extractErrorMessage(cause)}`,\n * cause,\n * }),\n * });\n *\n * type HttpError = InferErrors<typeof HttpError>;\n *\n * const result = HttpError.Connection({ cause: 'timeout' }); // Err<...>\n * ```\n *\n * Inspired by Rust's {@link https://docs.rs/thiserror | thiserror} crate. The\n * mapping is nearly 1:1:\n *\n * - `enum HttpError` → `const HttpError = defineErrors(...)`\n * - Variant `Connection { cause: String }` → key `Connection: ({ cause }: { cause: unknown }) => (...)`\n * - `#[error(\"Failed: {cause}\")]` → `` message: `Failed: ${extractErrorMessage(cause)}` ``\n * - `HttpError::Connection { ... }` → `HttpError.Connection({ ... })`\n * - `match error { Connection { .. } => }` → `switch (error.name) { case 'Connection': }`\n *\n * The equivalent Rust `thiserror` enum:\n * ```rust\n * #[derive(Error, Debug)]\n * enum HttpError {\n * #[error(\"Failed to connect: {cause}\")]\n * Connection { cause: String },\n *\n * #[error(\"HTTP {status}\")]\n * Response { status: u16, body_message: Option<String> },\n *\n * #[error(\"Failed to parse response body: {cause}\")]\n * Parse { cause: String },\n * }\n * ```\n */\nexport function defineErrors<const TConfig extends ErrorsConfig>(\n\tconfig: TConfig & ValidatedConfig<TConfig>,\n): DefineErrorsReturn<TConfig> {\n\tconst result: Record<string, unknown> = {};\n\n\tfor (const [name, ctor] of Object.entries(config)) {\n\t\tresult[name] = (...args: unknown[]) => {\n\t\t\tconst body = (ctor as (...a: unknown[]) => Record<string, unknown>)(\n\t\t\t\t...args,\n\t\t\t);\n\t\t\treturn Err(Object.freeze({ ...body, name }));\n\t\t};\n\t}\n\n\treturn result as DefineErrorsReturn<TConfig>;\n}\n","import { Err } from \"../result/result.js\";\nimport type { ErrorBody } from \"./types.js\";\n\n// =============================================================================\n// Config types\n// =============================================================================\n\n/** Config: each key maps to `[httpStatus, factory]`. */\n// biome-ignore lint/suspicious/noExplicitAny: required for TypeScript's function type inference\nexport type HttpErrorsConfig = Record<string, [number, (...args: any[]) => ErrorBody]>;\n\n/**\n * Per-key validation applied to the factory portion of each tuple.\n * Mirrors `ValidatedConfig` from `defineErrors`: prevents `name` in the\n * factory return body since the factory key is stamped as `name`.\n */\ntype ValidateHttpErrorBody<K extends string> = {\n\tmessage: string;\n\tname?: `The 'name' key is reserved as '${K}'. Remove it.`;\n};\n\nexport type ValidatedHttpConfig<T extends HttpErrorsConfig> = {\n\t// biome-ignore lint/suspicious/noExplicitAny: required for TypeScript's function type inference\n\t[K in keyof T & string]: T[K] extends [infer TStatus, (...args: infer A) => infer R]\n\t\t? [TStatus, (...args: A) => R & ValidateHttpErrorBody<K>]\n\t\t: T[K];\n};\n\n// =============================================================================\n// Return types\n// =============================================================================\n\n/**\n * A factory function with a static `.status` property holding the literal\n * HTTP status code. The status is never included in the serialized error body.\n */\n// biome-ignore lint/suspicious/noExplicitAny: required for TypeScript's function type inference\nexport type HttpErrorFactory<TName extends string, TStatus extends number, TFn extends (...args: any[]) => ErrorBody> =\n\t((...args: Parameters<TFn>) => Err<Readonly<{ name: TName } & ReturnType<TFn>>>) & {\n\t\treadonly status: TStatus;\n\t};\n\n/** Return type of `defineHttpErrors`. Maps each config key to its `HttpErrorFactory`. */\nexport type DefineHttpErrorsReturn<TConfig extends HttpErrorsConfig> = {\n\t// biome-ignore lint/suspicious/noExplicitAny: required for conditional type inference\n\t[K in keyof TConfig & string]: TConfig[K] extends [infer TStatus extends number, infer TFn extends (...args: any[]) => ErrorBody]\n\t\t? HttpErrorFactory<K, TStatus, TFn>\n\t\t: never;\n};\n\n// =============================================================================\n// Type utilities\n// =============================================================================\n\n/** Extract the error instance type from a single `HttpErrorFactory`. */\n// biome-ignore lint/suspicious/noExplicitAny: required for conditional type inference\nexport type InferHttpError<T> = T extends (...args: any[]) => Err<infer R> ? R : never;\n\n/** Extract the union of all error instance types from a `defineHttpErrors` return. */\nexport type InferHttpErrors<T> = {\n\t// biome-ignore lint/suspicious/noExplicitAny: required for conditional type inference\n\t[K in keyof T]: T[K] extends (...args: any[]) => Err<infer R> ? R : never;\n}[keyof T];\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Defines a set of typed HTTP error factories, each paired with its HTTP\n * status code.\n *\n * Like `defineErrors`, each factory stamps `name` onto the error and wraps it\n * in `Err`. Unlike `defineErrors`, each factory function carries a static\n * `.status` property with the literal HTTP status code — the status is never\n * included in the serialized error body.\n *\n * @example\n * ```ts\n * const AssetError = defineHttpErrors({\n * MissingFile: [400, () => ({ message: 'Missing file' })],\n * FileTooLarge: [413, ({ size }: { size: number }) => ({ message: `File too large: ${size}`, size })],\n * StorageLimitExceeded: [402, () => ({ message: 'Storage limit exceeded' })],\n * });\n *\n * type AssetError = InferHttpErrors<typeof AssetError>;\n *\n * // In a Hono route handler:\n * return c.json(AssetError.MissingFile(), AssetError.MissingFile.status);\n * // wire body: { error: { name: 'MissingFile', message: 'Missing file' }, data: null }\n * // http status: 400\n * ```\n */\nexport function defineHttpErrors<const TConfig extends HttpErrorsConfig>(\n\tconfig: TConfig & ValidatedHttpConfig<TConfig>,\n): DefineHttpErrorsReturn<TConfig> {\n\tconst result: Record<string, unknown> = {};\n\n\tfor (const [name, [status, factory]] of Object.entries(config)) {\n\t\tconst fn = (...args: unknown[]) => {\n\t\t\tconst body = (factory as (...a: unknown[]) => Record<string, unknown>)(\n\t\t\t\t...args,\n\t\t\t);\n\t\t\treturn Err(Object.freeze({ ...body, name }));\n\t\t};\n\t\t(fn as unknown as { status: number }).status = status;\n\t\tresult[name] = fn;\n\t}\n\n\treturn result as unknown as DefineHttpErrorsReturn<TConfig>;\n}\n","/**\n * Extracts a readable error message from an unknown error value\n *\n * @param error - The unknown error to extract a message from\n * @returns A string representation of the error\n */\nexport function extractErrorMessage(error: unknown): string {\n\t// Handle Error instances\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\n\t// Handle primitives\n\tif (typeof error === \"string\") return error;\n\tif (\n\t\ttypeof error === \"number\" ||\n\t\ttypeof error === \"boolean\" ||\n\t\ttypeof error === \"bigint\"\n\t)\n\t\treturn String(error);\n\tif (typeof error === \"symbol\") return error.toString();\n\tif (error === null) return \"null\";\n\tif (error === undefined) return \"undefined\";\n\n\t// Handle arrays\n\tif (Array.isArray(error)) return JSON.stringify(error);\n\n\t// Handle plain objects\n\tif (typeof error === \"object\") {\n\t\tconst errorObj = error as Record<string, unknown>;\n\n\t\t// Check common error properties\n\t\tconst messageProps = [\n\t\t\t\"message\",\n\t\t\t\"error\",\n\t\t\t\"description\",\n\t\t\t\"title\",\n\t\t\t\"reason\",\n\t\t\t\"details\",\n\t\t] as const;\n\t\tfor (const prop of messageProps) {\n\t\t\tif (prop in errorObj && typeof errorObj[prop] === \"string\") {\n\t\t\t\treturn errorObj[prop];\n\t\t\t}\n\t\t}\n\n\t\t// Fallback to JSON stringification\n\t\ttry {\n\t\t\treturn JSON.stringify(error);\n\t\t} catch {\n\t\t\treturn String(error);\n\t\t}\n\t}\n\n\t// Final fallback\n\treturn String(error);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,SAAgB,aACfA,QAC8B;CAC9B,MAAMC,SAAkC,CAAE;AAE1C,MAAK,MAAM,CAAC,MAAM,KAAK,IAAI,OAAO,QAAQ,OAAO,CAChD,QAAO,QAAQ,CAAC,GAAG,SAAoB;EACtC,MAAM,OAAO,AAAC,KACb,GAAG,KACH;AACD,SAAO,IAAI,OAAO,OAAO;GAAE,GAAG;GAAM;EAAM,EAAC,CAAC;CAC5C;AAGF,QAAO;AACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACkBD,SAAgB,iBACfC,QACkC;CAClC,MAAMC,SAAkC,CAAE;AAE1C,MAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,QAAQ,CAAC,IAAI,OAAO,QAAQ,OAAO,EAAE;EAC/D,MAAM,KAAK,CAAC,GAAG,SAAoB;GAClC,MAAM,OAAO,AAAC,QACb,GAAG,KACH;AACD,UAAO,IAAI,OAAO,OAAO;IAAE,GAAG;IAAM;GAAM,EAAC,CAAC;EAC5C;AACD,EAAC,GAAqC,SAAS;AAC/C,SAAO,QAAQ;CACf;AAED,QAAO;AACP;;;;;;;;;;ACxGD,SAAgB,oBAAoBC,OAAwB;AAE3D,KAAI,iBAAiB,MACpB,QAAO,MAAM;AAId,YAAW,UAAU,SAAU,QAAO;AACtC,YACQ,UAAU,mBACV,UAAU,oBACV,UAAU,SAEjB,QAAO,OAAO,MAAM;AACrB,YAAW,UAAU,SAAU,QAAO,MAAM,UAAU;AACtD,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,iBAAqB,QAAO;AAGhC,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,KAAK,UAAU,MAAM;AAGtD,YAAW,UAAU,UAAU;EAC9B,MAAM,WAAW;EAGjB,MAAM,eAAe;GACpB;GACA;GACA;GACA;GACA;GACA;EACA;AACD,OAAK,MAAM,QAAQ,aAClB,KAAI,QAAQ,mBAAmB,SAAS,UAAU,SACjD,QAAO,SAAS;AAKlB,MAAI;AACH,UAAO,KAAK,UAAU,MAAM;EAC5B,QAAO;AACP,UAAO,OAAO,MAAM;EACpB;CACD;AAGD,QAAO,OAAO,MAAM;AACpB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Err } from "./result-DKwq9BCr.js";
|
|
2
|
+
import { DefineErrorsReturn, ErrorBody, ErrorsConfig, ValidatedConfig } from "./types-tXXk7K9Q.js";
|
|
3
|
+
|
|
4
|
+
//#region src/error/defineErrors.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Defines a set of typed error factories using Rust-style namespaced variants.
|
|
8
|
+
*
|
|
9
|
+
* Each key is a short variant name (the namespace provides context). Every
|
|
10
|
+
* factory returns `Err<...>` directly — ready for `trySync`/`tryAsync` catch
|
|
11
|
+
* handlers. The variant name is stamped as `name` on the error object.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const HttpError = defineErrors({
|
|
16
|
+
* Connection: ({ cause }: { cause: unknown }) => ({
|
|
17
|
+
* message: `Failed to connect: ${extractErrorMessage(cause)}`,
|
|
18
|
+
* cause,
|
|
19
|
+
* }),
|
|
20
|
+
* Response: ({ status }: { status: number; bodyMessage?: string }) => ({
|
|
21
|
+
* message: `HTTP ${status}`,
|
|
22
|
+
* status,
|
|
23
|
+
* }),
|
|
24
|
+
* Parse: ({ cause }: { cause: unknown }) => ({
|
|
25
|
+
* message: `Failed to parse response body: ${extractErrorMessage(cause)}`,
|
|
26
|
+
* cause,
|
|
27
|
+
* }),
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* type HttpError = InferErrors<typeof HttpError>;
|
|
31
|
+
*
|
|
32
|
+
* const result = HttpError.Connection({ cause: 'timeout' }); // Err<...>
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* Inspired by Rust's {@link https://docs.rs/thiserror | thiserror} crate. The
|
|
36
|
+
* mapping is nearly 1:1:
|
|
37
|
+
*
|
|
38
|
+
* - `enum HttpError` → `const HttpError = defineErrors(...)`
|
|
39
|
+
* - Variant `Connection { cause: String }` → key `Connection: ({ cause }: { cause: unknown }) => (...)`
|
|
40
|
+
* - `#[error("Failed: {cause}")]` → `` message: `Failed: ${extractErrorMessage(cause)}` ``
|
|
41
|
+
* - `HttpError::Connection { ... }` → `HttpError.Connection({ ... })`
|
|
42
|
+
* - `match error { Connection { .. } => }` → `switch (error.name) { case 'Connection': }`
|
|
43
|
+
*
|
|
44
|
+
* The equivalent Rust `thiserror` enum:
|
|
45
|
+
* ```rust
|
|
46
|
+
* #[derive(Error, Debug)]
|
|
47
|
+
* enum HttpError {
|
|
48
|
+
* #[error("Failed to connect: {cause}")]
|
|
49
|
+
* Connection { cause: String },
|
|
50
|
+
*
|
|
51
|
+
* #[error("HTTP {status}")]
|
|
52
|
+
* Response { status: u16, body_message: Option<String> },
|
|
53
|
+
*
|
|
54
|
+
* #[error("Failed to parse response body: {cause}")]
|
|
55
|
+
* Parse { cause: String },
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
declare function defineErrors<const TConfig extends ErrorsConfig>(config: TConfig & ValidatedConfig<TConfig>): DefineErrorsReturn<TConfig>;
|
|
60
|
+
//# sourceMappingURL=defineErrors.d.ts.map
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/error/defineHttpErrors.d.ts
|
|
63
|
+
/** Config: each key maps to `[httpStatus, factory]`. */
|
|
64
|
+
type HttpErrorsConfig = Record<string, [number, (...args: any[]) => ErrorBody]>;
|
|
65
|
+
/**
|
|
66
|
+
* Per-key validation applied to the factory portion of each tuple.
|
|
67
|
+
* Mirrors `ValidatedConfig` from `defineErrors`: prevents `name` in the
|
|
68
|
+
* factory return body since the factory key is stamped as `name`.
|
|
69
|
+
*/
|
|
70
|
+
type ValidateHttpErrorBody<K extends string> = {
|
|
71
|
+
message: string;
|
|
72
|
+
name?: `The 'name' key is reserved as '${K}'. Remove it.`;
|
|
73
|
+
};
|
|
74
|
+
type ValidatedHttpConfig<T extends HttpErrorsConfig> = { [K in keyof T & string]: T[K] extends [infer TStatus, (...args: infer A) => infer R] ? [TStatus, (...args: A) => R & ValidateHttpErrorBody<K>] : T[K] };
|
|
75
|
+
/**
|
|
76
|
+
* A factory function with a static `.status` property holding the literal
|
|
77
|
+
* HTTP status code. The status is never included in the serialized error body.
|
|
78
|
+
*/
|
|
79
|
+
type HttpErrorFactory<TName extends string, TStatus extends number, TFn extends (...args: any[]) => ErrorBody> = ((...args: Parameters<TFn>) => Err<Readonly<{
|
|
80
|
+
name: TName;
|
|
81
|
+
} & ReturnType<TFn>>>) & {
|
|
82
|
+
readonly status: TStatus;
|
|
83
|
+
};
|
|
84
|
+
/** Return type of `defineHttpErrors`. Maps each config key to its `HttpErrorFactory`. */
|
|
85
|
+
type DefineHttpErrorsReturn<TConfig extends HttpErrorsConfig> = { [K in keyof TConfig & string]: TConfig[K] extends [infer TStatus extends number, infer TFn extends (...args: any[]) => ErrorBody] ? HttpErrorFactory<K, TStatus, TFn> : never };
|
|
86
|
+
/** Extract the error instance type from a single `HttpErrorFactory`. */
|
|
87
|
+
type InferHttpError<T> = T extends ((...args: any[]) => Err<infer R>) ? R : never;
|
|
88
|
+
/** Extract the union of all error instance types from a `defineHttpErrors` return. */
|
|
89
|
+
type InferHttpErrors<T> = { [K in keyof T]: T[K] extends ((...args: any[]) => Err<infer R>) ? R : never }[keyof T];
|
|
90
|
+
/**
|
|
91
|
+
* Defines a set of typed HTTP error factories, each paired with its HTTP
|
|
92
|
+
* status code.
|
|
93
|
+
*
|
|
94
|
+
* Like `defineErrors`, each factory stamps `name` onto the error and wraps it
|
|
95
|
+
* in `Err`. Unlike `defineErrors`, each factory function carries a static
|
|
96
|
+
* `.status` property with the literal HTTP status code — the status is never
|
|
97
|
+
* included in the serialized error body.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const AssetError = defineHttpErrors({
|
|
102
|
+
* MissingFile: [400, () => ({ message: 'Missing file' })],
|
|
103
|
+
* FileTooLarge: [413, ({ size }: { size: number }) => ({ message: `File too large: ${size}`, size })],
|
|
104
|
+
* StorageLimitExceeded: [402, () => ({ message: 'Storage limit exceeded' })],
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* type AssetError = InferHttpErrors<typeof AssetError>;
|
|
108
|
+
*
|
|
109
|
+
* // In a Hono route handler:
|
|
110
|
+
* return c.json(AssetError.MissingFile(), AssetError.MissingFile.status);
|
|
111
|
+
* // wire body: { error: { name: 'MissingFile', message: 'Missing file' }, data: null }
|
|
112
|
+
* // http status: 400
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
declare function defineHttpErrors<const TConfig extends HttpErrorsConfig>(config: TConfig & ValidatedHttpConfig<TConfig>): DefineHttpErrorsReturn<TConfig>;
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/error/extractErrorMessage.d.ts
|
|
118
|
+
/**
|
|
119
|
+
* Extracts a readable error message from an unknown error value
|
|
120
|
+
*
|
|
121
|
+
* @param error - The unknown error to extract a message from
|
|
122
|
+
* @returns A string representation of the error
|
|
123
|
+
*/
|
|
124
|
+
declare function extractErrorMessage(error: unknown): string;
|
|
125
|
+
//# sourceMappingURL=extractErrorMessage.d.ts.map
|
|
126
|
+
|
|
127
|
+
//#endregion
|
|
128
|
+
export { DefineHttpErrorsReturn, HttpErrorFactory, HttpErrorsConfig, InferHttpError, InferHttpErrors, ValidatedHttpConfig, defineErrors, defineHttpErrors, extractErrorMessage };
|
|
129
|
+
//# sourceMappingURL=index-BNWUFJ69.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-BNWUFJ69.d.ts","names":[],"sources":["../src/error/defineErrors.ts","../src/error/defineHttpErrors.ts","../src/error/extractErrorMessage.ts"],"sourcesContent":[],"mappings":";;;;;;;;AA4DA;;;;;;;;AAEqB;;;;ACrDrB;;;;AAAqC;AAAkD;AAYvF;;;;;;;;;;;;;AAIO;AAYP;;;;;;;;;;;AAE0B;AAI1B;;;;;;AAEwH,iBDexG,YCfwG,CAAA,sBDerE,YCfqE,CAAA,CAAA,MAAA,EDgB/G,OChB+G,GDgBrG,eChBqG,CDgBrF,OChBqF,CAAA,CAAA,EDiBrH,kBCjBqH,CDiBlG,OCjBkG,CAAA;;;;;KApC5G,gBAAA,GAAmB,4CAA4C;ADmD3E;;;;;KC5CK,qBD6Cc,CAAA,UAAA,MAAA,CAAA,GAAA;EAAe,OACZ,EAAA,MAAA;EAAO,IAA1B,CAAA,EAAA,kCC5CuC,CD4CvC,eAAA;AAAkB,CAAA;KCzCT,8BAA8B,kCAE7B,aAAa,EAAE,6DACvB,mBAAmB,MAAM,IAAI,sBAAsB,MACpD,EAAE;AAhBN;;;;AAAqC,KA4BzB,gBA5ByB,CAAA,cAAA,MAAA,EAAA,gBAAA,MAAA,EAAA,YAAA,CAAA,GAAA,IAAA,EAAA,GAAA,EAAA,EAAA,GA4BsE,SA5BtE,CAAA,GAAA,CAAA,CAAA,GAAA,IAAA,EA6BzB,UA7ByB,CA6Bd,GA7Bc,CAAA,EAAA,GA6BL,GA7BK,CA6BD,QA7BC,CAAA;EAOhC,IAAA,EAsBgD,KAtBhD;AAKL,CAAA,GAiB+D,UAjBnD,CAiB8D,GAjB9D,CAAA,CAAA,CAAA,CAAA,GAAA;EAAmB,SAAA,MAAA,EAkBZ,OAlBY;CAAA;;AAEL,KAoBd,sBApBc,CAAA,gBAoByB,gBApBzB,CAAA,GAAA,QAAE,MAsBf,OAtBe,GAAA,MAAA,GAsBI,OAtBJ,CAsBY,CAtBZ,CAAA,SAAA,CAAA,KAAA,iBAAA,MAAA,EAAA,KAAA,aAAA,CAAA,GAAA,IAAA,EAAA,GAAA,EAAA,EAAA,GAsB4F,SAtB5F,CAAA,GAuBxB,gBAvBwB,CAuBP,CAvBO,EAuBJ,OAvBI,EAuBK,GAvBL,CAAA,GAAA,KAAA,EAAC;;AACC,KAgClB,cAhCkB,CAAA,CAAA,CAAA,GAgCE,CAhCF,UAAA,CAAA,GAAA,IAAA,EAAA,GAAA,EAAA,EAAA,GAgCgC,GAhChC,CAAA,KAAA,EAAA,CAAA,IAgC+C,CAhC/C,GAAA,KAAA;;AAAI,KAmCtB,eAnCsB,CAAA,CAAA,CAAA,GAAA,QAC9B,MAoCS,CApCT,GAoCa,CApCb,CAoCe,CApCf,CAAA,UAAA,CAAA,GAAA,IAAA,EAAA,GAAA,EAAA,EAAA,GAoC8C,GApC9C,CAAA,KAAA,EAAA,CAAA,IAoC6D,CApC7D,GAAA,KAAA,EAAC,CAAA,MAqCG,CArCF,CAAA;AAAC;AAYP;;;;;;;;;;;AAE0B;AAI1B;;;;;;;;;;;AAGoB;AAUR,iBAqCI,gBArCU,CAAA,sBAqC6B,gBArC7B,CAAA,CAAA,MAAA,EAsCjB,OAtCiB,GAsCP,mBAtCO,CAsCa,OAtCb,CAAA,CAAA,EAuCvB,sBAvCuB,CAuCA,OAvCA,CAAA;;;;;;;ADI1B;;AAAmD,iBEtDnC,mBAAA,CFsDmC,KAAA,EAAA,OAAA,CAAA,EAAA,MAAA"}
|
package/dist/json.d.ts
CHANGED
package/dist/json.js
CHANGED
package/dist/logger/index.d.ts
CHANGED
|
@@ -123,6 +123,18 @@ type Logger = {
|
|
|
123
123
|
* annotation form would widen an unnecessary Partial into the value type.
|
|
124
124
|
*
|
|
125
125
|
* No dispose handler — `console` is not a resource.
|
|
126
|
+
*
|
|
127
|
+
* ### CLI authors: stream routing
|
|
128
|
+
*
|
|
129
|
+
* `console[level]` routes by level, not uniformly to stdout:
|
|
130
|
+
* - `console.log` (not used here) is the only method that writes stdout.
|
|
131
|
+
* - `console.info`, `console.debug`, `console.warn`, `console.error`,
|
|
132
|
+
* `console.trace` all write **stderr** in Node/Bun.
|
|
133
|
+
*
|
|
134
|
+
* For a CLI that emits structured program output on stdout and diagnostics
|
|
135
|
+
* on stderr, this default is correct — every logger event goes to stderr.
|
|
136
|
+
* Authors who expect `log.info` to print to stdout will be surprised; write
|
|
137
|
+
* a custom sink that routes to `process.stdout` if that's the requirement.
|
|
126
138
|
*/
|
|
127
139
|
declare const consoleSink: (event: LogEvent) => void;
|
|
128
140
|
//# sourceMappingURL=console-sink.d.ts.map
|
|
@@ -147,16 +159,38 @@ declare const consoleSink: (event: LogEvent) => void;
|
|
|
147
159
|
* plus the level string); spelling them out beats an `emitErr("warn")`
|
|
148
160
|
* riddle.
|
|
149
161
|
*
|
|
162
|
+
* ### Source convention
|
|
163
|
+
*
|
|
164
|
+
* `source` is a free-form string, but downstream filtering and tail-log
|
|
165
|
+
* grep become much easier when call sites converge on a shape. Recommended:
|
|
166
|
+
*
|
|
167
|
+
* '<package>/<module>' e.g. 'workspace/sync-supervisor'
|
|
168
|
+
* '<app>/<feature>' e.g. 'fuji/daemon-route'
|
|
169
|
+
* '<package>/<area>' e.g. 'auth/oauth-app'
|
|
170
|
+
*
|
|
171
|
+
* Keep it lowercase-kebab. Avoid bare factory names like
|
|
172
|
+
* `'attachSqliteMaterializer'`: they don't carry package context.
|
|
173
|
+
*
|
|
174
|
+
* ### Module-scope vs injected
|
|
175
|
+
*
|
|
176
|
+
* `const log = createLogger('source')` at module scope is fine for
|
|
177
|
+
* process-singleton modules (CLI commands, app bootstrap, leaf utilities
|
|
178
|
+
* the host does not customize per instance). For anything that can be
|
|
179
|
+
* instantiated more than once per process with different routing needs
|
|
180
|
+
* (per-document attach primitives, per-account auth state, per-workspace
|
|
181
|
+
* services), accept `log?: Logger` at the factory boundary and default
|
|
182
|
+
* to `createLogger('source')` for development ergonomics.
|
|
183
|
+
*
|
|
150
184
|
* @example Library code (caller wires the sink)
|
|
151
185
|
* function attachThing(ydoc: Doc, opts: { log?: Logger }) {
|
|
152
|
-
* const log = opts.log ?? createLogger('thing');
|
|
186
|
+
* const log = opts.log ?? createLogger('workspace/thing');
|
|
153
187
|
* // ...
|
|
154
188
|
* }
|
|
155
189
|
*
|
|
156
190
|
* @example App wiring (share one sink, multiple loggers)
|
|
157
191
|
* const sink = composeSinks(consoleSink, myCustomSink);
|
|
158
|
-
* attachThing(ydoc, { log: createLogger('thing', sink) });
|
|
159
|
-
* attachOther(ydoc, { log: createLogger('other', sink) });
|
|
192
|
+
* attachThing(ydoc, { log: createLogger('workspace/thing', sink) });
|
|
193
|
+
* attachOther(ydoc, { log: createLogger('workspace/other', sink) });
|
|
160
194
|
*/
|
|
161
195
|
declare function createLogger(source: string, sink?: LogSink): Logger;
|
|
162
196
|
//# sourceMappingURL=create-logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/logger/types.ts","../../src/logger/console-sink.ts","../../src/logger/create-logger.ts","../../src/logger/memory-sink.ts","../../src/logger/compose-sinks.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAoBA;AAgBA;;AAA+B,KApCnB,QAAA,GAoCmB,OAAA,GAAA,OAAA,GAAA,MAAA,GAAA,MAAA,GAAA,OAAA;;;AAA4B;AAuC3D;;;;;AAAgD;AAahD;;;;AAEwB;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/logger/types.ts","../../src/logger/console-sink.ts","../../src/logger/create-logger.ts","../../src/logger/memory-sink.ts","../../src/logger/compose-sinks.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAoBA;AAgBA;;AAA+B,KApCnB,QAAA,GAoCmB,OAAA,GAAA,OAAA,GAAA,MAAA,GAAA,MAAA,GAAA,OAAA;;;AAA4B;AAuC3D;;;;;AAAgD;AAahD;;;;AAEwB;;;;ACnExB;KDHY,QAAA;;SAEJ;EE4BQ,MAAA,EAAA,MAAA;EAAY,OAAA,EAAA,MAAA;EAAA,IAErB,CAAA,EAAA,OAAA;CAAqB;AACnB;;;;ACvCT;;;;AAA+D,KHsBnD,OAAA,GGtBmD,CAAA,CAAA,KAAA,EHsBhC,QGtBgC,EAAA,GAAA,IAAA,CAAA,GHsBX,OGtBW,CHsBH,eGtBG,CAAA;;;;ACM/D;;;;AAA0D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KJuD9C,aAAA,GAAgB,iBAAiB,IAAI;;;;;;;;;;;;KAarC,MAAA;aACA;YACD;;;;;;;;;;;;;AA1FX;AAoBA;AAgBA;;;;;AAA2D;AAuC3D;;;;;AAAgD;AAahD;;;;AAEwB;;;;ACnExB;;;;AC2BgB,cD3BH,WC2Be,EAAA,CAAA,KAAA,EDpBR,QCoBQ,EAAA,GAAA,IAAA;;;;;;;;;AFlD5B;AAoBA;AAgBA;;;;;AAA2D;AAuC3D;;;;;AAAgD;AAahD;;;;AAEwB;;;;ACnExB;;;;AC2BA;;;;AAGS;;;;ACvCT;;;;AAA+D;;;;ACM/D;;;;AAA0D;iBF8B1C,YAAA,wBAET,UACJ;;;;;;;;AFrDH;AAoBA;AAgBA;;;;;AAA2D;AAuC3D;;;;;AAAgD;AAahD;;;;AAEwB,iBG5ER,UAAA,CAAA,CH4EQ,EAAA;QG5Ec;UAAiB;;AFSvD;;;;;;;;ADvBA;AAoBA;AAgBA;;;;;AAA2D;AAuC3D;;;;;AAAgD;AAahD;;;;AAEwB;;;;ACnExB;;iBGHgB,YAAA,WAAuB,YAAY"}
|
package/dist/logger/index.js
CHANGED
|
@@ -20,6 +20,18 @@ import { tapErr } from "../tap-err-CP-re1HT.js";
|
|
|
20
20
|
* annotation form would widen an unnecessary Partial into the value type.
|
|
21
21
|
*
|
|
22
22
|
* No dispose handler — `console` is not a resource.
|
|
23
|
+
*
|
|
24
|
+
* ### CLI authors: stream routing
|
|
25
|
+
*
|
|
26
|
+
* `console[level]` routes by level, not uniformly to stdout:
|
|
27
|
+
* - `console.log` (not used here) is the only method that writes stdout.
|
|
28
|
+
* - `console.info`, `console.debug`, `console.warn`, `console.error`,
|
|
29
|
+
* `console.trace` all write **stderr** in Node/Bun.
|
|
30
|
+
*
|
|
31
|
+
* For a CLI that emits structured program output on stdout and diagnostics
|
|
32
|
+
* on stderr, this default is correct — every logger event goes to stderr.
|
|
33
|
+
* Authors who expect `log.info` to print to stdout will be surprised; write
|
|
34
|
+
* a custom sink that routes to `process.stdout` if that's the requirement.
|
|
23
35
|
*/
|
|
24
36
|
const consoleSink = (event) => {
|
|
25
37
|
const prefix = `[${event.source}]`;
|
|
@@ -51,16 +63,38 @@ function unwrapLoggable(err) {
|
|
|
51
63
|
* plus the level string); spelling them out beats an `emitErr("warn")`
|
|
52
64
|
* riddle.
|
|
53
65
|
*
|
|
66
|
+
* ### Source convention
|
|
67
|
+
*
|
|
68
|
+
* `source` is a free-form string, but downstream filtering and tail-log
|
|
69
|
+
* grep become much easier when call sites converge on a shape. Recommended:
|
|
70
|
+
*
|
|
71
|
+
* '<package>/<module>' e.g. 'workspace/sync-supervisor'
|
|
72
|
+
* '<app>/<feature>' e.g. 'fuji/daemon-route'
|
|
73
|
+
* '<package>/<area>' e.g. 'auth/oauth-app'
|
|
74
|
+
*
|
|
75
|
+
* Keep it lowercase-kebab. Avoid bare factory names like
|
|
76
|
+
* `'attachSqliteMaterializer'`: they don't carry package context.
|
|
77
|
+
*
|
|
78
|
+
* ### Module-scope vs injected
|
|
79
|
+
*
|
|
80
|
+
* `const log = createLogger('source')` at module scope is fine for
|
|
81
|
+
* process-singleton modules (CLI commands, app bootstrap, leaf utilities
|
|
82
|
+
* the host does not customize per instance). For anything that can be
|
|
83
|
+
* instantiated more than once per process with different routing needs
|
|
84
|
+
* (per-document attach primitives, per-account auth state, per-workspace
|
|
85
|
+
* services), accept `log?: Logger` at the factory boundary and default
|
|
86
|
+
* to `createLogger('source')` for development ergonomics.
|
|
87
|
+
*
|
|
54
88
|
* @example Library code (caller wires the sink)
|
|
55
89
|
* function attachThing(ydoc: Doc, opts: { log?: Logger }) {
|
|
56
|
-
* const log = opts.log ?? createLogger('thing');
|
|
90
|
+
* const log = opts.log ?? createLogger('workspace/thing');
|
|
57
91
|
* // ...
|
|
58
92
|
* }
|
|
59
93
|
*
|
|
60
94
|
* @example App wiring (share one sink, multiple loggers)
|
|
61
95
|
* const sink = composeSinks(consoleSink, myCustomSink);
|
|
62
|
-
* attachThing(ydoc, { log: createLogger('thing', sink) });
|
|
63
|
-
* attachOther(ydoc, { log: createLogger('other', sink) });
|
|
96
|
+
* attachThing(ydoc, { log: createLogger('workspace/thing', sink) });
|
|
97
|
+
* attachOther(ydoc, { log: createLogger('workspace/other', sink) });
|
|
64
98
|
*/
|
|
65
99
|
function createLogger(source, sink = consoleSink) {
|
|
66
100
|
const emit = (level, message, data) => {
|
package/dist/logger/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["err: LoggableError","source: string","sink: LogSink","level: LogLevel","message: string","data?: unknown","events: LogEvent[]","event: LogEvent"],"sources":["../../src/logger/console-sink.ts","../../src/logger/create-logger.ts","../../src/logger/memory-sink.ts","../../src/logger/compose-sinks.ts"],"sourcesContent":["import type { LogSink } from \"./types.js\";\n\n/**\n * Default sink. Writes to `console.*` with a `[source]` prefix.\n *\n * Kept as a singleton value (not a factory) because it takes no config —\n * adding `createConsoleSink({ format })` would be ceremony for a pattern\n * the user can trivially replace by writing their own sink.\n *\n * `console[event.level]` routes directly without a detached lookup table.\n * `LogLevel` is a subset of the Console method keys, so TS errors at this\n * access if a future level drifts (e.g. adding `fatal`). Calling the method\n * through `console[...]` preserves the `this` binding — avoids \"Illegal\n * invocation\" in runtimes that require it.\n *\n * `satisfies LogSink` (not `: LogSink`) keeps the inferred callable type\n * precise — `LogSink` is an intersection with optional dispose, and the\n * annotation form would widen an unnecessary Partial into the value type.\n *\n * No dispose handler — `console` is not a resource.\n */\nexport const consoleSink = ((event) => {\n\tconst prefix = `[${event.source}]`;\n\tif (event.data === undefined) {\n\t\tconsole[event.level](prefix, event.message);\n\t} else {\n\t\tconsole[event.level](prefix, event.message, event.data);\n\t}\n}) satisfies LogSink;\n","import type { AnyTaggedError } from \"../error/types.js\";\nimport { consoleSink } from \"./console-sink.js\";\nimport type { LoggableError, LogLevel, Logger, LogSink } from \"./types.js\";\n\n/** Narrow `LoggableError` to the raw tagged object. See `LoggableError` in `types.ts` for the discriminator rationale. */\nfunction unwrapLoggable(err: LoggableError): AnyTaggedError {\n\treturn \"name\" in err ? err : err.error;\n}\n\n/**\n * Create a logger bound to a `source` namespace and a sink.\n *\n * Design choices:\n * - **Positional args, not a bag.** Two arguments, both with obvious meaning;\n * a `{ source, sink }` object would be ceremony.\n * - **`sink` defaults to `consoleSink`.** Most callers during development\n * want console output with zero setup. Production apps swap it out via DI\n * at the attach/wire-up site.\n * - **No global default logger.** There is no `setDefaultLogger()` and no\n * module-level registry. Every consumer takes a `log?: Logger` option\n * and defaults to `createLogger('<source>')` if omitted. Globals make\n * test isolation and sink composition painful.\n * - **Method shorthand in the return object** over higher-order factories.\n * The five methods differ in two simple ways (error-unary vs free-form,\n * plus the level string); spelling them out beats an `emitErr(\"warn\")`\n * riddle.\n *\n * @example Library code (caller wires the sink)\n * function attachThing(ydoc: Doc, opts: { log?: Logger }) {\n * const log = opts.log ?? createLogger('thing');\n * // ...\n * }\n *\n * @example App wiring (share one sink, multiple loggers)\n * const sink = composeSinks(consoleSink, myCustomSink);\n * attachThing(ydoc, { log: createLogger('thing', sink) });\n * attachOther(ydoc, { log: createLogger('other', sink) });\n */\nexport function createLogger(\n\tsource: string,\n\tsink: LogSink = consoleSink,\n): Logger {\n\tconst emit = (level: LogLevel, message: string, data?: unknown): void => {\n\t\tsink({ ts: Date.now(), level, source, message, data });\n\t};\n\treturn {\n\t\terror(err) {\n\t\t\tconst tagged = unwrapLoggable(err);\n\t\t\temit(\"error\", tagged.message, tagged);\n\t\t},\n\t\twarn(err) {\n\t\t\tconst tagged = unwrapLoggable(err);\n\t\t\temit(\"warn\", tagged.message, tagged);\n\t\t},\n\t\tinfo(message, data) {\n\t\t\temit(\"info\", message, data);\n\t\t},\n\t\tdebug(message, data) {\n\t\t\temit(\"debug\", message, data);\n\t\t},\n\t\ttrace(message, data) {\n\t\t\temit(\"trace\", message, data);\n\t\t},\n\t};\n}\n","import type { LogEvent, LogSink } from \"./types.js\";\n\n/**\n * In-memory sink for tests. Returns `{ sink, events }` so callers can\n * both wire the sink and inspect captured events without a module-level\n * spy or `console.*` interception.\n *\n * A factory (not a singleton) so each test gets an isolated array; sharing\n * state across tests would leak events.\n *\n * Returning `{ sink, events }` (rather than an array with a method) keeps\n * the two roles separate — `sink` goes to `createLogger`, `events` goes to\n * assertions.\n *\n * Uses `satisfies LogSink` on the sink expression rather than `: LogSink =`\n * to preserve the precise inferred callable type.\n *\n * @example\n * const { sink, events } = memorySink();\n * const log = createLogger(\"test\", sink);\n * log.warn(MyError.Thing({ cause: new Error(\"boom\") }));\n * expect(events).toHaveLength(1);\n * expect(events[0]).toMatchObject({ level: \"warn\", source: \"test\" });\n */\nexport function memorySink(): { sink: LogSink; events: LogEvent[] } {\n\tconst events: LogEvent[] = [];\n\tconst sink = ((event) => {\n\t\tevents.push(event);\n\t}) satisfies LogSink;\n\treturn { sink, events };\n}\n","import type { LogEvent, LogSink } from \"./types.js\";\n\n/**\n * Fan one event out to every sink in order.\n *\n * Disposal: the returned sink has a `[Symbol.asyncDispose]` that forwards\n * to each member via optional chaining. Members without dispose (e.g.\n * `consoleSink`) are silent no-ops; members that own resources (file,\n * network) flush and close. Mix pure and stateful sinks freely — no\n * wrapping required.\n *\n * Fan-out is sequential and unguarded. If a member sink throws on emit,\n * later members do not receive the event — by design, since swallowing\n * sink errors hides real bugs. Wrap individual sinks yourself for\n * best-effort delivery.\n *\n * Dispose is sequential and awaits each member. If one throws, later\n * members don't get their chance; callers who want best-effort cleanup\n * should wrap the composed dispose themselves.\n *\n * Built with `Object.assign` + `satisfies LogSink` rather than mutating a\n * pre-typed `const` — avoids the widening that comes with `: LogSink =` and\n * keeps the inferred type precise (callable + definite dispose, not\n * callable + Partial dispose).\n *\n * @example\n * await using file = jsonlFileSink(\"/tmp/app.jsonl\");\n * const sink = composeSinks(consoleSink, file);\n * const log = createLogger(\"source\", sink);\n */\nexport function composeSinks(...sinks: LogSink[]): LogSink {\n\tconst emit = (event: LogEvent) => {\n\t\tfor (const sink of sinks) sink(event);\n\t};\n\tconst dispose = async (): Promise<void> => {\n\t\tfor (const sink of sinks) await sink[Symbol.asyncDispose]?.();\n\t};\n\treturn Object.assign(emit, {\n\t\t[Symbol.asyncDispose]: dispose,\n\t}) satisfies LogSink;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":["err: LoggableError","source: string","sink: LogSink","level: LogLevel","message: string","data?: unknown","events: LogEvent[]","event: LogEvent"],"sources":["../../src/logger/console-sink.ts","../../src/logger/create-logger.ts","../../src/logger/memory-sink.ts","../../src/logger/compose-sinks.ts"],"sourcesContent":["import type { LogSink } from \"./types.js\";\n\n/**\n * Default sink. Writes to `console.*` with a `[source]` prefix.\n *\n * Kept as a singleton value (not a factory) because it takes no config —\n * adding `createConsoleSink({ format })` would be ceremony for a pattern\n * the user can trivially replace by writing their own sink.\n *\n * `console[event.level]` routes directly without a detached lookup table.\n * `LogLevel` is a subset of the Console method keys, so TS errors at this\n * access if a future level drifts (e.g. adding `fatal`). Calling the method\n * through `console[...]` preserves the `this` binding — avoids \"Illegal\n * invocation\" in runtimes that require it.\n *\n * `satisfies LogSink` (not `: LogSink`) keeps the inferred callable type\n * precise — `LogSink` is an intersection with optional dispose, and the\n * annotation form would widen an unnecessary Partial into the value type.\n *\n * No dispose handler — `console` is not a resource.\n *\n * ### CLI authors: stream routing\n *\n * `console[level]` routes by level, not uniformly to stdout:\n * - `console.log` (not used here) is the only method that writes stdout.\n * - `console.info`, `console.debug`, `console.warn`, `console.error`,\n * `console.trace` all write **stderr** in Node/Bun.\n *\n * For a CLI that emits structured program output on stdout and diagnostics\n * on stderr, this default is correct — every logger event goes to stderr.\n * Authors who expect `log.info` to print to stdout will be surprised; write\n * a custom sink that routes to `process.stdout` if that's the requirement.\n */\nexport const consoleSink = ((event) => {\n\tconst prefix = `[${event.source}]`;\n\tif (event.data === undefined) {\n\t\tconsole[event.level](prefix, event.message);\n\t} else {\n\t\tconsole[event.level](prefix, event.message, event.data);\n\t}\n}) satisfies LogSink;\n","import type { AnyTaggedError } from \"../error/types.js\";\nimport { consoleSink } from \"./console-sink.js\";\nimport type { LoggableError, LogLevel, Logger, LogSink } from \"./types.js\";\n\n/** Narrow `LoggableError` to the raw tagged object. See `LoggableError` in `types.ts` for the discriminator rationale. */\nfunction unwrapLoggable(err: LoggableError): AnyTaggedError {\n\treturn \"name\" in err ? err : err.error;\n}\n\n/**\n * Create a logger bound to a `source` namespace and a sink.\n *\n * Design choices:\n * - **Positional args, not a bag.** Two arguments, both with obvious meaning;\n * a `{ source, sink }` object would be ceremony.\n * - **`sink` defaults to `consoleSink`.** Most callers during development\n * want console output with zero setup. Production apps swap it out via DI\n * at the attach/wire-up site.\n * - **No global default logger.** There is no `setDefaultLogger()` and no\n * module-level registry. Every consumer takes a `log?: Logger` option\n * and defaults to `createLogger('<source>')` if omitted. Globals make\n * test isolation and sink composition painful.\n * - **Method shorthand in the return object** over higher-order factories.\n * The five methods differ in two simple ways (error-unary vs free-form,\n * plus the level string); spelling them out beats an `emitErr(\"warn\")`\n * riddle.\n *\n * ### Source convention\n *\n * `source` is a free-form string, but downstream filtering and tail-log\n * grep become much easier when call sites converge on a shape. Recommended:\n *\n * '<package>/<module>' e.g. 'workspace/sync-supervisor'\n * '<app>/<feature>' e.g. 'fuji/daemon-route'\n * '<package>/<area>' e.g. 'auth/oauth-app'\n *\n * Keep it lowercase-kebab. Avoid bare factory names like\n * `'attachSqliteMaterializer'`: they don't carry package context.\n *\n * ### Module-scope vs injected\n *\n * `const log = createLogger('source')` at module scope is fine for\n * process-singleton modules (CLI commands, app bootstrap, leaf utilities\n * the host does not customize per instance). For anything that can be\n * instantiated more than once per process with different routing needs\n * (per-document attach primitives, per-account auth state, per-workspace\n * services), accept `log?: Logger` at the factory boundary and default\n * to `createLogger('source')` for development ergonomics.\n *\n * @example Library code (caller wires the sink)\n * function attachThing(ydoc: Doc, opts: { log?: Logger }) {\n * const log = opts.log ?? createLogger('workspace/thing');\n * // ...\n * }\n *\n * @example App wiring (share one sink, multiple loggers)\n * const sink = composeSinks(consoleSink, myCustomSink);\n * attachThing(ydoc, { log: createLogger('workspace/thing', sink) });\n * attachOther(ydoc, { log: createLogger('workspace/other', sink) });\n */\nexport function createLogger(\n\tsource: string,\n\tsink: LogSink = consoleSink,\n): Logger {\n\tconst emit = (level: LogLevel, message: string, data?: unknown): void => {\n\t\tsink({ ts: Date.now(), level, source, message, data });\n\t};\n\treturn {\n\t\terror(err) {\n\t\t\tconst tagged = unwrapLoggable(err);\n\t\t\temit(\"error\", tagged.message, tagged);\n\t\t},\n\t\twarn(err) {\n\t\t\tconst tagged = unwrapLoggable(err);\n\t\t\temit(\"warn\", tagged.message, tagged);\n\t\t},\n\t\tinfo(message, data) {\n\t\t\temit(\"info\", message, data);\n\t\t},\n\t\tdebug(message, data) {\n\t\t\temit(\"debug\", message, data);\n\t\t},\n\t\ttrace(message, data) {\n\t\t\temit(\"trace\", message, data);\n\t\t},\n\t};\n}\n","import type { LogEvent, LogSink } from \"./types.js\";\n\n/**\n * In-memory sink for tests. Returns `{ sink, events }` so callers can\n * both wire the sink and inspect captured events without a module-level\n * spy or `console.*` interception.\n *\n * A factory (not a singleton) so each test gets an isolated array; sharing\n * state across tests would leak events.\n *\n * Returning `{ sink, events }` (rather than an array with a method) keeps\n * the two roles separate — `sink` goes to `createLogger`, `events` goes to\n * assertions.\n *\n * Uses `satisfies LogSink` on the sink expression rather than `: LogSink =`\n * to preserve the precise inferred callable type.\n *\n * @example\n * const { sink, events } = memorySink();\n * const log = createLogger(\"test\", sink);\n * log.warn(MyError.Thing({ cause: new Error(\"boom\") }));\n * expect(events).toHaveLength(1);\n * expect(events[0]).toMatchObject({ level: \"warn\", source: \"test\" });\n */\nexport function memorySink(): { sink: LogSink; events: LogEvent[] } {\n\tconst events: LogEvent[] = [];\n\tconst sink = ((event) => {\n\t\tevents.push(event);\n\t}) satisfies LogSink;\n\treturn { sink, events };\n}\n","import type { LogEvent, LogSink } from \"./types.js\";\n\n/**\n * Fan one event out to every sink in order.\n *\n * Disposal: the returned sink has a `[Symbol.asyncDispose]` that forwards\n * to each member via optional chaining. Members without dispose (e.g.\n * `consoleSink`) are silent no-ops; members that own resources (file,\n * network) flush and close. Mix pure and stateful sinks freely — no\n * wrapping required.\n *\n * Fan-out is sequential and unguarded. If a member sink throws on emit,\n * later members do not receive the event — by design, since swallowing\n * sink errors hides real bugs. Wrap individual sinks yourself for\n * best-effort delivery.\n *\n * Dispose is sequential and awaits each member. If one throws, later\n * members don't get their chance; callers who want best-effort cleanup\n * should wrap the composed dispose themselves.\n *\n * Built with `Object.assign` + `satisfies LogSink` rather than mutating a\n * pre-typed `const` — avoids the widening that comes with `: LogSink =` and\n * keeps the inferred type precise (callable + definite dispose, not\n * callable + Partial dispose).\n *\n * @example\n * await using file = jsonlFileSink(\"/tmp/app.jsonl\");\n * const sink = composeSinks(consoleSink, file);\n * const log = createLogger(\"source\", sink);\n */\nexport function composeSinks(...sinks: LogSink[]): LogSink {\n\tconst emit = (event: LogEvent) => {\n\t\tfor (const sink of sinks) sink(event);\n\t};\n\tconst dispose = async (): Promise<void> => {\n\t\tfor (const sink of sinks) await sink[Symbol.asyncDispose]?.();\n\t};\n\treturn Object.assign(emit, {\n\t\t[Symbol.asyncDispose]: dispose,\n\t}) satisfies LogSink;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,cAAe,CAAC,UAAU;CACtC,MAAM,SAAS,CAAC,CAAC,EAAE,MAAM,OAAO,CAAC,CAAC;AAClC,KAAI,MAAM,gBACT,SAAQ,MAAM,OAAO,QAAQ,MAAM,QAAQ;KAE3C,SAAQ,MAAM,OAAO,QAAQ,MAAM,SAAS,MAAM,KAAK;AAExD;;;;;ACnCD,SAAS,eAAeA,KAAoC;AAC3D,QAAO,UAAU,MAAM,MAAM,IAAI;AACjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDD,SAAgB,aACfC,QACAC,OAAgB,aACP;CACT,MAAM,OAAO,CAACC,OAAiBC,SAAiBC,SAAyB;AACxE,OAAK;GAAE,IAAI,KAAK,KAAK;GAAE;GAAO;GAAQ;GAAS;EAAM,EAAC;CACtD;AACD,QAAO;EACN,MAAM,KAAK;GACV,MAAM,SAAS,eAAe,IAAI;AAClC,QAAK,SAAS,OAAO,SAAS,OAAO;EACrC;EACD,KAAK,KAAK;GACT,MAAM,SAAS,eAAe,IAAI;AAClC,QAAK,QAAQ,OAAO,SAAS,OAAO;EACpC;EACD,KAAK,SAAS,MAAM;AACnB,QAAK,QAAQ,SAAS,KAAK;EAC3B;EACD,MAAM,SAAS,MAAM;AACpB,QAAK,SAAS,SAAS,KAAK;EAC5B;EACD,MAAM,SAAS,MAAM;AACpB,QAAK,SAAS,SAAS,KAAK;EAC5B;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;AC9DD,SAAgB,aAAoD;CACnE,MAAMC,SAAqB,CAAE;CAC7B,MAAM,OAAQ,CAAC,UAAU;AACxB,SAAO,KAAK,MAAM;CAClB;AACD,QAAO;EAAE;EAAM;CAAQ;AACvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAD,SAAgB,aAAa,GAAG,OAA2B;CAC1D,MAAM,OAAO,CAACC,UAAoB;AACjC,OAAK,MAAM,QAAQ,MAAO,MAAK,MAAM;CACrC;CACD,MAAM,UAAU,YAA2B;AAC1C,OAAK,MAAM,QAAQ,MAAO,OAAM,KAAK,OAAO,iBAAiB;CAC7D;AACD,QAAO,OAAO,OAAO,MAAM,GACzB,OAAO,eAAe,QACvB,EAAC;AACF"}
|
package/dist/testing.js
CHANGED
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error-Dy9wXt5_.js","names":["config: TConfig & ValidatedConfig<TConfig>","result: Record<string, unknown>","error: unknown"],"sources":["../src/error/defineErrors.ts","../src/error/extractErrorMessage.ts"],"sourcesContent":["import { Err } from \"../result/result.js\";\nimport type {\n\tDefineErrorsReturn,\n\tErrorsConfig,\n\tValidatedConfig,\n} from \"./types.js\";\n\n/**\n * Defines a set of typed error factories using Rust-style namespaced variants.\n *\n * Each key is a short variant name (the namespace provides context). Every\n * factory returns `Err<...>` directly — ready for `trySync`/`tryAsync` catch\n * handlers. The variant name is stamped as `name` on the error object.\n *\n * @example\n * ```ts\n * const HttpError = defineErrors({\n * Connection: ({ cause }: { cause: unknown }) => ({\n * message: `Failed to connect: ${extractErrorMessage(cause)}`,\n * cause,\n * }),\n * Response: ({ status }: { status: number; bodyMessage?: string }) => ({\n * message: `HTTP ${status}`,\n * status,\n * }),\n * Parse: ({ cause }: { cause: unknown }) => ({\n * message: `Failed to parse response body: ${extractErrorMessage(cause)}`,\n * cause,\n * }),\n * });\n *\n * type HttpError = InferErrors<typeof HttpError>;\n *\n * const result = HttpError.Connection({ cause: 'timeout' }); // Err<...>\n * ```\n *\n * Inspired by Rust's {@link https://docs.rs/thiserror | thiserror} crate. The\n * mapping is nearly 1:1:\n *\n * - `enum HttpError` → `const HttpError = defineErrors(...)`\n * - Variant `Connection { cause: String }` → key `Connection: ({ cause }: { cause: unknown }) => (...)`\n * - `#[error(\"Failed: {cause}\")]` → `` message: `Failed: ${extractErrorMessage(cause)}` ``\n * - `HttpError::Connection { ... }` → `HttpError.Connection({ ... })`\n * - `match error { Connection { .. } => }` → `switch (error.name) { case 'Connection': }`\n *\n * The equivalent Rust `thiserror` enum:\n * ```rust\n * #[derive(Error, Debug)]\n * enum HttpError {\n * #[error(\"Failed to connect: {cause}\")]\n * Connection { cause: String },\n *\n * #[error(\"HTTP {status}\")]\n * Response { status: u16, body_message: Option<String> },\n *\n * #[error(\"Failed to parse response body: {cause}\")]\n * Parse { cause: String },\n * }\n * ```\n */\nexport function defineErrors<const TConfig extends ErrorsConfig>(\n\tconfig: TConfig & ValidatedConfig<TConfig>,\n): DefineErrorsReturn<TConfig> {\n\tconst result: Record<string, unknown> = {};\n\n\tfor (const [name, ctor] of Object.entries(config)) {\n\t\tresult[name] = (...args: unknown[]) => {\n\t\t\tconst body = (ctor as (...a: unknown[]) => Record<string, unknown>)(\n\t\t\t\t...args,\n\t\t\t);\n\t\t\treturn Err(Object.freeze({ ...body, name }));\n\t\t};\n\t}\n\n\treturn result as DefineErrorsReturn<TConfig>;\n}\n","/**\n * Extracts a readable error message from an unknown error value\n *\n * @param error - The unknown error to extract a message from\n * @returns A string representation of the error\n */\nexport function extractErrorMessage(error: unknown): string {\n\t// Handle Error instances\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\n\t// Handle primitives\n\tif (typeof error === \"string\") return error;\n\tif (\n\t\ttypeof error === \"number\" ||\n\t\ttypeof error === \"boolean\" ||\n\t\ttypeof error === \"bigint\"\n\t)\n\t\treturn String(error);\n\tif (typeof error === \"symbol\") return error.toString();\n\tif (error === null) return \"null\";\n\tif (error === undefined) return \"undefined\";\n\n\t// Handle arrays\n\tif (Array.isArray(error)) return JSON.stringify(error);\n\n\t// Handle plain objects\n\tif (typeof error === \"object\") {\n\t\tconst errorObj = error as Record<string, unknown>;\n\n\t\t// Check common error properties\n\t\tconst messageProps = [\n\t\t\t\"message\",\n\t\t\t\"error\",\n\t\t\t\"description\",\n\t\t\t\"title\",\n\t\t\t\"reason\",\n\t\t\t\"details\",\n\t\t] as const;\n\t\tfor (const prop of messageProps) {\n\t\t\tif (prop in errorObj && typeof errorObj[prop] === \"string\") {\n\t\t\t\treturn errorObj[prop];\n\t\t\t}\n\t\t}\n\n\t\t// Fallback to JSON stringification\n\t\ttry {\n\t\t\treturn JSON.stringify(error);\n\t\t} catch {\n\t\t\treturn String(error);\n\t\t}\n\t}\n\n\t// Final fallback\n\treturn String(error);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,SAAgB,aACfA,QAC8B;CAC9B,MAAMC,SAAkC,CAAE;AAE1C,MAAK,MAAM,CAAC,MAAM,KAAK,IAAI,OAAO,QAAQ,OAAO,CAChD,QAAO,QAAQ,CAAC,GAAG,SAAoB;EACtC,MAAM,OAAO,AAAC,KACb,GAAG,KACH;AACD,SAAO,IAAI,OAAO,OAAO;GAAE,GAAG;GAAM;EAAM,EAAC,CAAC;CAC5C;AAGF,QAAO;AACP;;;;;;;;;;ACrED,SAAgB,oBAAoBC,OAAwB;AAE3D,KAAI,iBAAiB,MACpB,QAAO,MAAM;AAId,YAAW,UAAU,SAAU,QAAO;AACtC,YACQ,UAAU,mBACV,UAAU,oBACV,UAAU,SAEjB,QAAO,OAAO,MAAM;AACrB,YAAW,UAAU,SAAU,QAAO,MAAM,UAAU;AACtD,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,iBAAqB,QAAO;AAGhC,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,KAAK,UAAU,MAAM;AAGtD,YAAW,UAAU,UAAU;EAC9B,MAAM,WAAW;EAGjB,MAAM,eAAe;GACpB;GACA;GACA;GACA;GACA;GACA;EACA;AACD,OAAK,MAAM,QAAQ,aAClB,KAAI,QAAQ,mBAAmB,SAAS,UAAU,SACjD,QAAO,SAAS;AAKlB,MAAI;AACH,UAAO,KAAK,UAAU,MAAM;EAC5B,QAAO;AACP,UAAO,OAAO,MAAM;EACpB;CACD;AAGD,QAAO,OAAO,MAAM;AACpB"}
|
package/dist/index-B9PnZCTt.d.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { DefineErrorsReturn, ErrorsConfig, ValidatedConfig } from "./types-tXXk7K9Q.js";
|
|
2
|
-
|
|
3
|
-
//#region src/error/defineErrors.d.ts
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Defines a set of typed error factories using Rust-style namespaced variants.
|
|
7
|
-
*
|
|
8
|
-
* Each key is a short variant name (the namespace provides context). Every
|
|
9
|
-
* factory returns `Err<...>` directly — ready for `trySync`/`tryAsync` catch
|
|
10
|
-
* handlers. The variant name is stamped as `name` on the error object.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* const HttpError = defineErrors({
|
|
15
|
-
* Connection: ({ cause }: { cause: unknown }) => ({
|
|
16
|
-
* message: `Failed to connect: ${extractErrorMessage(cause)}`,
|
|
17
|
-
* cause,
|
|
18
|
-
* }),
|
|
19
|
-
* Response: ({ status }: { status: number; bodyMessage?: string }) => ({
|
|
20
|
-
* message: `HTTP ${status}`,
|
|
21
|
-
* status,
|
|
22
|
-
* }),
|
|
23
|
-
* Parse: ({ cause }: { cause: unknown }) => ({
|
|
24
|
-
* message: `Failed to parse response body: ${extractErrorMessage(cause)}`,
|
|
25
|
-
* cause,
|
|
26
|
-
* }),
|
|
27
|
-
* });
|
|
28
|
-
*
|
|
29
|
-
* type HttpError = InferErrors<typeof HttpError>;
|
|
30
|
-
*
|
|
31
|
-
* const result = HttpError.Connection({ cause: 'timeout' }); // Err<...>
|
|
32
|
-
* ```
|
|
33
|
-
*
|
|
34
|
-
* Inspired by Rust's {@link https://docs.rs/thiserror | thiserror} crate. The
|
|
35
|
-
* mapping is nearly 1:1:
|
|
36
|
-
*
|
|
37
|
-
* - `enum HttpError` → `const HttpError = defineErrors(...)`
|
|
38
|
-
* - Variant `Connection { cause: String }` → key `Connection: ({ cause }: { cause: unknown }) => (...)`
|
|
39
|
-
* - `#[error("Failed: {cause}")]` → `` message: `Failed: ${extractErrorMessage(cause)}` ``
|
|
40
|
-
* - `HttpError::Connection { ... }` → `HttpError.Connection({ ... })`
|
|
41
|
-
* - `match error { Connection { .. } => }` → `switch (error.name) { case 'Connection': }`
|
|
42
|
-
*
|
|
43
|
-
* The equivalent Rust `thiserror` enum:
|
|
44
|
-
* ```rust
|
|
45
|
-
* #[derive(Error, Debug)]
|
|
46
|
-
* enum HttpError {
|
|
47
|
-
* #[error("Failed to connect: {cause}")]
|
|
48
|
-
* Connection { cause: String },
|
|
49
|
-
*
|
|
50
|
-
* #[error("HTTP {status}")]
|
|
51
|
-
* Response { status: u16, body_message: Option<String> },
|
|
52
|
-
*
|
|
53
|
-
* #[error("Failed to parse response body: {cause}")]
|
|
54
|
-
* Parse { cause: String },
|
|
55
|
-
* }
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
declare function defineErrors<const TConfig extends ErrorsConfig>(config: TConfig & ValidatedConfig<TConfig>): DefineErrorsReturn<TConfig>;
|
|
59
|
-
//# sourceMappingURL=defineErrors.d.ts.map
|
|
60
|
-
//#endregion
|
|
61
|
-
//#region src/error/extractErrorMessage.d.ts
|
|
62
|
-
/**
|
|
63
|
-
* Extracts a readable error message from an unknown error value
|
|
64
|
-
*
|
|
65
|
-
* @param error - The unknown error to extract a message from
|
|
66
|
-
* @returns A string representation of the error
|
|
67
|
-
*/
|
|
68
|
-
declare function extractErrorMessage(error: unknown): string;
|
|
69
|
-
//# sourceMappingURL=extractErrorMessage.d.ts.map
|
|
70
|
-
|
|
71
|
-
//#endregion
|
|
72
|
-
export { defineErrors, extractErrorMessage };
|
|
73
|
-
//# sourceMappingURL=index-B9PnZCTt.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-B9PnZCTt.d.ts","names":[],"sources":["../src/error/defineErrors.ts","../src/error/extractErrorMessage.ts"],"sourcesContent":[],"mappings":";;;;;;AA4DA;;;;;;;;AAEqB;;;;ACxDrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBDsDgB,mCAAmC,sBAC1C,UAAU,gBAAgB,WAChC,mBAAmB;;;;;;;AAFtB;;;AACS,iBCvDO,mBAAA,CDuDP,KAAA,EAAA,OAAA,CAAA,EAAA,MAAA"}
|