@danceroutine/tango-core 0.1.0 → 1.0.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/LICENSE +21 -0
- package/README.md +82 -0
- package/dist/{TangoError-NPkVPfuH.js → TangoError-DdQVQNZU.js} +5 -1
- package/dist/TangoError-DdQVQNZU.js.map +1 -0
- package/dist/errors/AuthenticationError.d.ts +6 -1
- package/dist/errors/ConflictError.d.ts +6 -1
- package/dist/errors/NotFoundError.d.ts +6 -1
- package/dist/errors/PermissionDenied.d.ts +6 -1
- package/dist/errors/TangoError.d.ts +13 -1
- package/dist/errors/ValidationError.d.ts +5 -1
- package/dist/errors/factories/HttpErrorFactory.d.ts +20 -11
- package/dist/errors/factories/index.d.ts +1 -1
- package/dist/errors/index.d.ts +2 -2
- package/dist/errors/index.js +4 -4
- package/dist/{errors-CzSQXdgI.js → errors-_tWsmNyZ.js} +81 -34
- package/dist/errors-_tWsmNyZ.js.map +1 -0
- package/dist/http/TangoBody.d.ts +43 -18
- package/dist/http/TangoHeaders.d.ts +10 -5
- package/dist/http/TangoQueryParams.d.ts +69 -0
- package/dist/http/TangoRequest.d.ts +82 -0
- package/dist/http/TangoResponse.d.ts +184 -49
- package/dist/http/index.d.ts +2 -1
- package/dist/http/index.js +4 -4
- package/dist/{http-DOLwwAYt.js → http-D20MQa6p.js} +675 -295
- package/dist/http-D20MQa6p.js.map +1 -0
- package/dist/index.d.ts +9 -7
- package/dist/index.js +7 -6
- package/dist/logging/ConsoleLogger.d.ts +15 -0
- package/dist/logging/Logger.d.ts +13 -0
- package/dist/logging/getLogger.d.ts +23 -0
- package/dist/logging/index.d.ts +3 -0
- package/dist/logging/index.js +3 -0
- package/dist/logging-BWeD4HOO.js +48 -0
- package/dist/logging-BWeD4HOO.js.map +1 -0
- package/dist/runtime/binary/isArrayBuffer.d.ts +3 -0
- package/dist/runtime/binary/isBlob.d.ts +3 -0
- package/dist/runtime/binary/isUint8Array.d.ts +3 -0
- package/dist/runtime/date/isDate.d.ts +3 -0
- package/dist/runtime/error/isError.d.ts +3 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/object/index.d.ts +1 -0
- package/dist/runtime/object/isNil.d.ts +4 -0
- package/dist/runtime/object/isObject.d.ts +3 -0
- package/dist/runtime/web/isFile.d.ts +3 -0
- package/dist/runtime/web/isFormData.d.ts +3 -0
- package/dist/runtime/web/isReadableStream.d.ts +3 -0
- package/dist/runtime/web/isURLSearchParams.d.ts +3 -0
- package/dist/{runtime-DPpCYEe_.js → runtime-B8KkgD3R.js} +14 -4
- package/dist/runtime-B8KkgD3R.js.map +1 -0
- package/dist/sql/SqlDialect.d.ts +5 -0
- package/dist/sql/SqlIdentifierRole.d.ts +13 -0
- package/dist/sql/SqlSafetyEngine.d.ts +50 -0
- package/dist/sql/TrustedSqlFragment.d.ts +5 -0
- package/dist/sql/ValidatedSqlIdentifier.d.ts +7 -0
- package/dist/sql/index.d.ts +13 -0
- package/dist/sql/index.js +3 -0
- package/dist/sql/isTrustedSqlFragment.d.ts +5 -0
- package/dist/sql/quoteSqlIdentifier.d.ts +6 -0
- package/dist/sql/trustedSql.d.ts +5 -0
- package/dist/sql/validateSqlIdentifier.d.ts +6 -0
- package/dist/sql-D3frkfy-.js +116 -0
- package/dist/sql-D3frkfy-.js.map +1 -0
- package/package.json +65 -54
- package/dist/TangoError-NPkVPfuH.js.map +0 -1
- package/dist/errors/factories/HttpErrorFactory.js +0 -91
- package/dist/errors-CzSQXdgI.js.map +0 -1
- package/dist/http/TangoHeaders.js +0 -396
- package/dist/http/TangoResponse.js +0 -556
- package/dist/http-DOLwwAYt.js.map +0 -1
- package/dist/result/Err.d.ts +0 -21
- package/dist/result/Ok.d.ts +0 -21
- package/dist/result/Result.d.ts +0 -33
- package/dist/result/index.d.ts +0 -8
- package/dist/result/index.js +0 -3
- package/dist/result-CBqw9Hlg.js +0 -82
- package/dist/result-CBqw9Hlg.js.map +0 -1
- package/dist/runtime-DPpCYEe_.js.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Pedro Del Moral Lopez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @danceroutine/tango-core
|
|
2
|
+
|
|
3
|
+
`@danceroutine/tango-core` contains Tango's low-level HTTP, error, logging, and runtime primitives.
|
|
4
|
+
|
|
5
|
+
This package exists so that the rest of Tango can rely on one shared vocabulary for common server-side concerns. It is also useful outside Tango when you want fetch-compatible request and response helpers, application error types that map cleanly to HTTP, logger wiring, and runtime guards for common boundary checks. Keeping those concerns in one package makes the higher-level packages easier to understand because they do not each need their own local version of the same foundational ideas.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @danceroutine/tango-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## What lives here
|
|
14
|
+
|
|
15
|
+
The package groups together four kinds of primitives:
|
|
16
|
+
|
|
17
|
+
- HTTP types such as `TangoRequest`, `TangoResponse`, `TangoHeaders`, `TangoBody`, and `TangoQueryParams`
|
|
18
|
+
- application error types such as `ValidationError`, `NotFoundError`, `PermissionDenied`, `AuthenticationError`, and `ConflictError`
|
|
19
|
+
- logger utilities such as `ConsoleLogger`, `getLogger`, and `setLoggerFactory`
|
|
20
|
+
- runtime guards for common server-side values
|
|
21
|
+
|
|
22
|
+
These are the pieces other Tango packages build on when they need to describe a request, return a response, raise a domain error, narrow an unknown runtime value, or normalize query input.
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { NotFoundError, TangoResponse, HttpErrorFactory } from '@danceroutine/tango-core';
|
|
28
|
+
|
|
29
|
+
export async function getPost(id: string): Promise<Response> {
|
|
30
|
+
const post = null;
|
|
31
|
+
|
|
32
|
+
if (!post) {
|
|
33
|
+
const error = HttpErrorFactory.toHttpError(new NotFoundError(`Post ${id} not found`));
|
|
34
|
+
return TangoResponse.problem(error.body, { status: error.status });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return TangoResponse.json({ id, title: 'Hello' });
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The error type expresses application intent, the HTTP mapping stays consistent, and the response helper keeps the endpoint code direct.
|
|
42
|
+
|
|
43
|
+
## Import style
|
|
44
|
+
|
|
45
|
+
Most projects will use curated root imports:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { TangoResponse, ValidationError, getLogger } from '@danceroutine/tango-core';
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If you are used to Django-style domain drill-down imports, the package also exposes namespace imports and subpaths that keep related primitives grouped together:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import '@danceroutine/tango-core/errors';
|
|
55
|
+
import '@danceroutine/tango-core/http';
|
|
56
|
+
import '@danceroutine/tango-core/logging';
|
|
57
|
+
import '@danceroutine/tango-core/runtime';
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Available subpaths include `http`, `errors`, `logging`, and `runtime`.
|
|
61
|
+
|
|
62
|
+
## Documentation
|
|
63
|
+
|
|
64
|
+
- Official documentation: <https://tangowebframework.dev>
|
|
65
|
+
- Architecture topic: <https://tangowebframework.dev/topics/architecture>
|
|
66
|
+
- Resources API reference: <https://tangowebframework.dev/reference/resources-api>
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pnpm --filter @danceroutine/tango-core build
|
|
72
|
+
pnpm --filter @danceroutine/tango-core typecheck
|
|
73
|
+
pnpm --filter @danceroutine/tango-core test
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For the wider contributor workflow, use:
|
|
77
|
+
|
|
78
|
+
- <https://tangowebframework.dev/contributing>
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
MIT
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
//#region src/errors/TangoError.ts
|
|
3
3
|
var TangoError = class extends Error {
|
|
4
4
|
__tangoErrorBrand = "tango.error";
|
|
5
|
+
/** Runtime guard for Tango-branded errors. */
|
|
5
6
|
static isTangoError(err) {
|
|
6
7
|
return !!err && err.__tangoErrorBrand === "tango.error";
|
|
7
8
|
}
|
|
9
|
+
/** Runtime guard for plain problem-details objects. */
|
|
8
10
|
static isProblemDetails(err) {
|
|
9
11
|
return !!err && typeof err === "object" && err !== null && "code" in err && typeof err.code === "string" && "message" in err && typeof err.message === "string";
|
|
10
12
|
}
|
|
13
|
+
/** Convert this error to wire-level envelope format. */
|
|
11
14
|
toErrorEnvelope() {
|
|
12
15
|
return { error: {
|
|
13
16
|
code: this.getErrorName(),
|
|
@@ -15,6 +18,7 @@ var TangoError = class extends Error {
|
|
|
15
18
|
details: this.getDetails()
|
|
16
19
|
} };
|
|
17
20
|
}
|
|
21
|
+
/** Convert this error to legacy `HttpError` shape. */
|
|
18
22
|
toHttpError() {
|
|
19
23
|
return {
|
|
20
24
|
status: this.status,
|
|
@@ -28,4 +32,4 @@ var TangoError = class extends Error {
|
|
|
28
32
|
|
|
29
33
|
//#endregion
|
|
30
34
|
export { TangoError };
|
|
31
|
-
//# sourceMappingURL=TangoError-
|
|
35
|
+
//# sourceMappingURL=TangoError-DdQVQNZU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TangoError-DdQVQNZU.js","names":["err: unknown"],"sources":["../src/errors/TangoError.ts"],"sourcesContent":["import type { HttpError } from './HttpError';\n\n/** Structured error detail payload for API responses. */\nexport type ErrorDetails = Record<string, string[]> | null | undefined;\n\n/** Canonical error payload shape used by Tango HTTP error responses. */\nexport type ProblemDetails<TDetails extends ErrorDetails = null> = {\n code: string;\n message: string;\n details?: TDetails;\n fields?: Record<string, string[]>;\n};\n\n/** Envelope shape for serialized error responses. */\nexport type ErrorEnvelope<TDetails extends ErrorDetails = null> = {\n error: ProblemDetails<TDetails>;\n};\n\n/**\n * Base branded framework error.\n *\n * Subclasses provide HTTP status, stable code, and structured details.\n */\nexport abstract class TangoError extends Error {\n // String brand avoids instanceof and survives cross-package boundaries.\n readonly __tangoErrorBrand = 'tango.error' as const;\n abstract status: number;\n\n protected abstract getDetails(): ErrorDetails;\n\n protected abstract getErrorName(): string;\n\n /** Runtime guard for Tango-branded errors. */\n static isTangoError(err: unknown): err is TangoError {\n return !!err && (err as { __tangoErrorBrand?: string }).__tangoErrorBrand === 'tango.error';\n }\n\n /** Runtime guard for plain problem-details objects. */\n static isProblemDetails(err: unknown): err is ProblemDetails {\n return (\n !!err &&\n typeof err === 'object' &&\n err !== null &&\n 'code' in err &&\n typeof (err as { code: unknown }).code === 'string' &&\n 'message' in err &&\n typeof (err as { message: unknown }).message === 'string'\n );\n }\n\n /** Convert this error to wire-level envelope format. */\n toErrorEnvelope(): ErrorEnvelope<ErrorDetails> {\n return {\n error: {\n code: this.getErrorName(),\n message: this.message,\n details: this.getDetails(),\n },\n };\n }\n\n /** Convert this error to legacy `HttpError` shape. */\n toHttpError(): HttpError {\n return {\n status: this.status,\n body: {\n error: this.message,\n details: this.getDetails(),\n },\n };\n }\n}\n"],"mappings":";;IAuBsB,aAAf,cAAkC,MAAM;CAE3C,oBAA6B;;CAQ7B,OAAO,aAAaA,KAAiC;AACjD,WAAS,OAAQ,IAAuC,sBAAsB;CACjF;;CAGD,OAAO,iBAAiBA,KAAqC;AACzD,WACM,cACK,QAAQ,YACf,QAAQ,QACR,UAAU,cACF,IAA0B,SAAS,YAC3C,aAAa,cACL,IAA6B,YAAY;CAExD;;CAGD,kBAA+C;AAC3C,SAAO,EACH,OAAO;GACH,MAAM,KAAK,cAAc;GACzB,SAAS,KAAK;GACd,SAAS,KAAK,YAAY;EAC7B,EACJ;CACJ;;CAGD,cAAyB;AACrB,SAAO;GACH,QAAQ,KAAK;GACb,MAAM;IACF,OAAO,KAAK;IACZ,SAAS,KAAK,YAAY;GAC7B;EACJ;CACJ;AACJ"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { TangoError, type ErrorDetails } from './TangoError';
|
|
2
|
+
/** Error for missing/invalid authentication (HTTP 401). */
|
|
2
3
|
export declare class AuthenticationError extends TangoError {
|
|
3
4
|
static readonly BRAND: "tango.error.authentication";
|
|
4
5
|
readonly __tangoBrand: typeof AuthenticationError.BRAND;
|
|
5
6
|
status: number;
|
|
6
|
-
|
|
7
|
+
/** Create an authentication error with optional custom message. */
|
|
7
8
|
constructor(message?: string);
|
|
9
|
+
/**
|
|
10
|
+
* Narrow an unknown value to `AuthenticationError`.
|
|
11
|
+
*/
|
|
12
|
+
static isAuthenticationError(value: unknown): value is AuthenticationError;
|
|
8
13
|
protected getErrorName(): string;
|
|
9
14
|
protected getDetails(): ErrorDetails;
|
|
10
15
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { TangoError, type ErrorDetails } from './TangoError';
|
|
2
|
+
/** Error for conflicting resource state (HTTP 409). */
|
|
2
3
|
export declare class ConflictError extends TangoError {
|
|
3
4
|
static readonly BRAND: "tango.error.conflict";
|
|
4
5
|
readonly __tangoBrand: typeof ConflictError.BRAND;
|
|
5
6
|
status: number;
|
|
6
|
-
|
|
7
|
+
/** Create a conflict error with optional custom message. */
|
|
7
8
|
constructor(message?: string);
|
|
9
|
+
/**
|
|
10
|
+
* Narrow an unknown value to `ConflictError`.
|
|
11
|
+
*/
|
|
12
|
+
static isConflictError(value: unknown): value is ConflictError;
|
|
8
13
|
protected getErrorName(): string;
|
|
9
14
|
protected getDetails(): ErrorDetails;
|
|
10
15
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { TangoError, type ErrorDetails } from './TangoError';
|
|
2
|
+
/** Error for missing resources (HTTP 404). */
|
|
2
3
|
export declare class NotFoundError extends TangoError {
|
|
3
4
|
static readonly BRAND: "tango.error.not_found";
|
|
4
5
|
readonly __tangoBrand: typeof NotFoundError.BRAND;
|
|
5
6
|
status: number;
|
|
6
|
-
|
|
7
|
+
/** Create a not-found error with optional custom message. */
|
|
7
8
|
constructor(message?: string);
|
|
9
|
+
/**
|
|
10
|
+
* Narrow an unknown value to `NotFoundError`.
|
|
11
|
+
*/
|
|
12
|
+
static isNotFoundError(value: unknown): value is NotFoundError;
|
|
8
13
|
protected getErrorName(): string;
|
|
9
14
|
protected getDetails(): ErrorDetails;
|
|
10
15
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { TangoError, type ErrorDetails } from './TangoError';
|
|
2
|
+
/** Error for authorization failures (HTTP 403). */
|
|
2
3
|
export declare class PermissionDenied extends TangoError {
|
|
3
4
|
static readonly BRAND: "tango.error.permission_denied";
|
|
4
5
|
readonly __tangoBrand: typeof PermissionDenied.BRAND;
|
|
5
6
|
status: number;
|
|
6
|
-
|
|
7
|
+
/** Create a permission-denied error with optional custom message. */
|
|
7
8
|
constructor(message?: string);
|
|
9
|
+
/**
|
|
10
|
+
* Narrow an unknown value to `PermissionDenied`.
|
|
11
|
+
*/
|
|
12
|
+
static isPermissionDenied(value: unknown): value is PermissionDenied;
|
|
8
13
|
protected getErrorName(): string;
|
|
9
14
|
protected getDetails(): ErrorDetails;
|
|
10
15
|
}
|
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
import type { HttpError } from './HttpError';
|
|
2
|
+
/** Structured error detail payload for API responses. */
|
|
2
3
|
export type ErrorDetails = Record<string, string[]> | null | undefined;
|
|
4
|
+
/** Canonical error payload shape used by Tango HTTP error responses. */
|
|
3
5
|
export type ProblemDetails<TDetails extends ErrorDetails = null> = {
|
|
4
6
|
code: string;
|
|
5
7
|
message: string;
|
|
6
8
|
details?: TDetails;
|
|
7
9
|
fields?: Record<string, string[]>;
|
|
8
10
|
};
|
|
11
|
+
/** Envelope shape for serialized error responses. */
|
|
9
12
|
export type ErrorEnvelope<TDetails extends ErrorDetails = null> = {
|
|
10
13
|
error: ProblemDetails<TDetails>;
|
|
11
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Base branded framework error.
|
|
17
|
+
*
|
|
18
|
+
* Subclasses provide HTTP status, stable code, and structured details.
|
|
19
|
+
*/
|
|
12
20
|
export declare abstract class TangoError extends Error {
|
|
13
|
-
readonly __tangoErrorBrand:
|
|
21
|
+
readonly __tangoErrorBrand: "tango.error";
|
|
14
22
|
abstract status: number;
|
|
15
23
|
protected abstract getDetails(): ErrorDetails;
|
|
16
24
|
protected abstract getErrorName(): string;
|
|
25
|
+
/** Runtime guard for Tango-branded errors. */
|
|
17
26
|
static isTangoError(err: unknown): err is TangoError;
|
|
27
|
+
/** Runtime guard for plain problem-details objects. */
|
|
18
28
|
static isProblemDetails(err: unknown): err is ProblemDetails;
|
|
29
|
+
/** Convert this error to wire-level envelope format. */
|
|
19
30
|
toErrorEnvelope(): ErrorEnvelope<ErrorDetails>;
|
|
31
|
+
/** Convert this error to legacy `HttpError` shape. */
|
|
20
32
|
toHttpError(): HttpError;
|
|
21
33
|
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { TangoError, type ErrorDetails } from './TangoError';
|
|
2
|
+
/** Error for request validation failures (HTTP 400). */
|
|
2
3
|
export declare class ValidationError extends TangoError {
|
|
3
4
|
details?: ErrorDetails;
|
|
4
|
-
readonly __tangoValidationErrorBrand:
|
|
5
|
+
readonly __tangoValidationErrorBrand: "tango.error.validation";
|
|
5
6
|
status: number;
|
|
6
7
|
constructor(message: string, details?: ErrorDetails);
|
|
8
|
+
/**
|
|
9
|
+
* Narrow an unknown value to `ValidationError`, including common legacy shapes.
|
|
10
|
+
*/
|
|
7
11
|
static isValidationError(err: unknown): err is ValidationError;
|
|
8
12
|
protected getErrorName(): string;
|
|
9
13
|
protected getDetails(): ErrorDetails;
|
|
@@ -25,6 +25,9 @@ export interface HttpErrorFactoryConfig {
|
|
|
25
25
|
* status: 400,
|
|
26
26
|
* body: { error: 'Validation failed', details: err.flatten().fieldErrors },
|
|
27
27
|
* }));
|
|
28
|
+
*
|
|
29
|
+
* // Quick one-shot conversion with dev defaults
|
|
30
|
+
* const httpError = HttpErrorFactory.toHttpError(new NotFoundError('missing'));
|
|
28
31
|
* ```
|
|
29
32
|
*/
|
|
30
33
|
export declare class HttpErrorFactory {
|
|
@@ -33,18 +36,24 @@ export declare class HttpErrorFactory {
|
|
|
33
36
|
private handlers;
|
|
34
37
|
private exposeErrors;
|
|
35
38
|
constructor(config?: HttpErrorFactoryConfig);
|
|
39
|
+
/**
|
|
40
|
+
* Narrow an unknown value to `HttpErrorFactory`.
|
|
41
|
+
*/
|
|
36
42
|
static isHttpErrorFactory(value: unknown): value is HttpErrorFactory;
|
|
37
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Convert an unknown error into an `HttpError` using dev-friendly defaults.
|
|
45
|
+
* Shorthand for `new HttpErrorFactory().create(error)`.
|
|
46
|
+
*/
|
|
47
|
+
static toHttpError(error: unknown): HttpError;
|
|
48
|
+
private static isZodLikeValidationError;
|
|
49
|
+
private static zodLikeErrorDetails;
|
|
50
|
+
/**
|
|
51
|
+
* Register a custom mapper for an application or third-party error type.
|
|
52
|
+
*/
|
|
53
|
+
registerHandler<T extends Error>(errorClass: new (...args: any[]) => T, handler: (error: T) => HttpError): this;
|
|
54
|
+
/**
|
|
55
|
+
* Convert an unknown error into the normalized HTTP error shape Tango uses.
|
|
56
|
+
*/
|
|
38
57
|
create(error: unknown): HttpError;
|
|
39
58
|
private isErrorClassInstance;
|
|
40
59
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Default factory instance with dev-friendly defaults (exposes error messages).
|
|
43
|
-
* Use this directly or create a custom HttpErrorFactory for production.
|
|
44
|
-
*/
|
|
45
|
-
export declare const defaultHttpErrorFactory: HttpErrorFactory;
|
|
46
|
-
/**
|
|
47
|
-
* Convenience function that converts unknown errors to an HttpError
|
|
48
|
-
* using the default factory instance.
|
|
49
|
-
*/
|
|
50
|
-
export declare function toHttpError(error: unknown): HttpError;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Domain boundary barrel: centralizes this subdomain's public contract.
|
|
3
3
|
*/
|
|
4
|
-
export { HttpErrorFactory,
|
|
4
|
+
export { HttpErrorFactory, type HttpErrorFactoryConfig } from './HttpErrorFactory';
|
package/dist/errors/index.d.ts
CHANGED
|
@@ -9,6 +9,6 @@ import { ValidationError } from './ValidationError';
|
|
|
9
9
|
import { NotFoundError } from './NotFoundError';
|
|
10
10
|
import { PermissionDenied } from './PermissionDenied';
|
|
11
11
|
import { AuthenticationError } from './AuthenticationError';
|
|
12
|
-
import { HttpErrorFactory,
|
|
13
|
-
export { AuthenticationError, ConflictError, HttpErrorFactory, NotFoundError, PermissionDenied, TangoError, ValidationError, factories,
|
|
12
|
+
import { HttpErrorFactory, type HttpErrorFactoryConfig } from './factories/HttpErrorFactory';
|
|
13
|
+
export { AuthenticationError, ConflictError, HttpErrorFactory, NotFoundError, PermissionDenied, TangoError, ValidationError, factories, };
|
|
14
14
|
export type { ErrorDetails, ErrorEnvelope, HttpError, HttpErrorFactoryConfig, ProblemDetails };
|
package/dist/errors/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { TangoError } from "../TangoError-
|
|
2
|
-
import "../runtime-
|
|
3
|
-
import { AuthenticationError, ConflictError, HttpErrorFactory, NotFoundError, PermissionDenied, ValidationError,
|
|
1
|
+
import { TangoError } from "../TangoError-DdQVQNZU.js";
|
|
2
|
+
import "../runtime-B8KkgD3R.js";
|
|
3
|
+
import { AuthenticationError, ConflictError, HttpErrorFactory, NotFoundError, PermissionDenied, ValidationError, factories_exports } from "../errors-_tWsmNyZ.js";
|
|
4
4
|
|
|
5
|
-
export { AuthenticationError, ConflictError, HttpErrorFactory, NotFoundError, PermissionDenied, TangoError, ValidationError,
|
|
5
|
+
export { AuthenticationError, ConflictError, HttpErrorFactory, NotFoundError, PermissionDenied, TangoError, ValidationError, factories_exports as factories };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __export } from "./chunk-BkvOhyD0.js";
|
|
2
|
-
import { TangoError } from "./TangoError-
|
|
3
|
-
import { isError, isObject } from "./runtime-
|
|
2
|
+
import { TangoError } from "./TangoError-DdQVQNZU.js";
|
|
3
|
+
import { isError, isObject } from "./runtime-B8KkgD3R.js";
|
|
4
4
|
|
|
5
5
|
//#region src/errors/factories/HttpErrorFactory.ts
|
|
6
6
|
var HttpErrorFactory = class HttpErrorFactory {
|
|
@@ -11,15 +11,52 @@ var HttpErrorFactory = class HttpErrorFactory {
|
|
|
11
11
|
constructor(config = {}) {
|
|
12
12
|
this.exposeErrors = config.exposeErrors ?? true;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Narrow an unknown value to `HttpErrorFactory`.
|
|
16
|
+
*/
|
|
14
17
|
static isHttpErrorFactory(value) {
|
|
15
18
|
return typeof value === "object" && value !== null && value.__tangoBrand === HttpErrorFactory.BRAND;
|
|
16
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Convert an unknown error into an `HttpError` using dev-friendly defaults.
|
|
22
|
+
* Shorthand for `new HttpErrorFactory().create(error)`.
|
|
23
|
+
*/
|
|
24
|
+
static toHttpError(error) {
|
|
25
|
+
return new HttpErrorFactory().create(error);
|
|
26
|
+
}
|
|
27
|
+
static isZodLikeValidationError(error) {
|
|
28
|
+
return isError(error) && isObject(error) && Array.isArray(error.issues) && error.name === "ZodError";
|
|
29
|
+
}
|
|
30
|
+
static zodLikeErrorDetails(error) {
|
|
31
|
+
const details = {};
|
|
32
|
+
for (const issue of error.issues) {
|
|
33
|
+
const key = Array.isArray(issue.path) && issue.path.length > 0 ? String(issue.path.join(".")) : "_schema";
|
|
34
|
+
const message = typeof issue.message === "string" ? issue.message : "Invalid value";
|
|
35
|
+
const existing = details[key];
|
|
36
|
+
if (existing) existing.push(message);
|
|
37
|
+
else details[key] = [message];
|
|
38
|
+
}
|
|
39
|
+
return details;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Register a custom mapper for an application or third-party error type.
|
|
43
|
+
*/
|
|
17
44
|
registerHandler(errorClass, handler) {
|
|
18
45
|
this.handlers.set(errorClass, handler);
|
|
19
46
|
return this;
|
|
20
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Convert an unknown error into the normalized HTTP error shape Tango uses.
|
|
50
|
+
*/
|
|
21
51
|
create(error) {
|
|
22
52
|
if (TangoError.isTangoError(error)) return error.toHttpError();
|
|
53
|
+
if (HttpErrorFactory.isZodLikeValidationError(error)) return {
|
|
54
|
+
status: 400,
|
|
55
|
+
body: {
|
|
56
|
+
error: error.message || "Validation failed",
|
|
57
|
+
details: HttpErrorFactory.zodLikeErrorDetails(error)
|
|
58
|
+
}
|
|
59
|
+
};
|
|
23
60
|
for (const [ErrorClass, handler] of this.handlers) if (this.isErrorClassInstance(error, ErrorClass)) return handler(error);
|
|
24
61
|
if (isError(error)) return {
|
|
25
62
|
status: 500,
|
|
@@ -37,28 +74,21 @@ var HttpErrorFactory = class HttpErrorFactory {
|
|
|
37
74
|
};
|
|
38
75
|
}
|
|
39
76
|
isErrorClassInstance(error, ErrorClass) {
|
|
40
|
-
if (!
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
77
|
+
if (!isError(error)) return false;
|
|
78
|
+
const expectedBrand = ErrorClass.BRAND;
|
|
79
|
+
if (typeof expectedBrand === "string" && isObject(error) && error.__tangoBrand === expectedBrand) return true;
|
|
80
|
+
const constructorName = error.constructor?.name;
|
|
81
|
+
if (typeof constructorName === "string" && constructorName === ErrorClass.name) return true;
|
|
82
|
+
const errorName = error.name;
|
|
83
|
+
if (typeof errorName === "string" && errorName === ErrorClass.name) return true;
|
|
46
84
|
return false;
|
|
47
85
|
}
|
|
48
86
|
};
|
|
49
|
-
const defaultHttpErrorFactory = new HttpErrorFactory();
|
|
50
|
-
function toHttpError(error) {
|
|
51
|
-
return defaultHttpErrorFactory.create(error);
|
|
52
|
-
}
|
|
53
87
|
|
|
54
88
|
//#endregion
|
|
55
89
|
//#region src/errors/factories/index.ts
|
|
56
90
|
var factories_exports = {};
|
|
57
|
-
__export(factories_exports, {
|
|
58
|
-
HttpErrorFactory: () => HttpErrorFactory,
|
|
59
|
-
defaultHttpErrorFactory: () => defaultHttpErrorFactory,
|
|
60
|
-
toHttpError: () => toHttpError
|
|
61
|
-
});
|
|
91
|
+
__export(factories_exports, { HttpErrorFactory: () => HttpErrorFactory });
|
|
62
92
|
|
|
63
93
|
//#endregion
|
|
64
94
|
//#region src/errors/ConflictError.ts
|
|
@@ -66,14 +96,18 @@ var ConflictError = class ConflictError extends TangoError {
|
|
|
66
96
|
static BRAND = "tango.error.conflict";
|
|
67
97
|
__tangoBrand = ConflictError.BRAND;
|
|
68
98
|
status = 409;
|
|
69
|
-
|
|
70
|
-
return typeof value === "object" && value !== null && value.__tangoBrand === ConflictError.BRAND;
|
|
71
|
-
}
|
|
99
|
+
/** Create a conflict error with optional custom message. */
|
|
72
100
|
constructor(message = "Resource conflict") {
|
|
73
101
|
super(message);
|
|
74
102
|
this.name = "ConflictError";
|
|
75
103
|
Object.setPrototypeOf(this, ConflictError.prototype);
|
|
76
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Narrow an unknown value to `ConflictError`.
|
|
107
|
+
*/
|
|
108
|
+
static isConflictError(value) {
|
|
109
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === ConflictError.BRAND;
|
|
110
|
+
}
|
|
77
111
|
getErrorName() {
|
|
78
112
|
return "conflict";
|
|
79
113
|
}
|
|
@@ -93,6 +127,9 @@ var ValidationError = class ValidationError extends TangoError {
|
|
|
93
127
|
this.name = "ValidationError";
|
|
94
128
|
Object.setPrototypeOf(this, ValidationError.prototype);
|
|
95
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Narrow an unknown value to `ValidationError`, including common legacy shapes.
|
|
132
|
+
*/
|
|
96
133
|
static isValidationError(err) {
|
|
97
134
|
return !!err && typeof err === "object" && err.__tangoValidationErrorBrand === "tango.error.validation" || typeof err === "object" && err !== null && "fields" in err && typeof err.fields === "object";
|
|
98
135
|
}
|
|
@@ -110,14 +147,18 @@ var NotFoundError = class NotFoundError extends TangoError {
|
|
|
110
147
|
static BRAND = "tango.error.not_found";
|
|
111
148
|
__tangoBrand = NotFoundError.BRAND;
|
|
112
149
|
status = 404;
|
|
113
|
-
|
|
114
|
-
return typeof value === "object" && value !== null && value.__tangoBrand === NotFoundError.BRAND;
|
|
115
|
-
}
|
|
150
|
+
/** Create a not-found error with optional custom message. */
|
|
116
151
|
constructor(message = "Resource not found") {
|
|
117
152
|
super(message);
|
|
118
153
|
this.name = "NotFoundError";
|
|
119
154
|
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
120
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Narrow an unknown value to `NotFoundError`.
|
|
158
|
+
*/
|
|
159
|
+
static isNotFoundError(value) {
|
|
160
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === NotFoundError.BRAND;
|
|
161
|
+
}
|
|
121
162
|
getErrorName() {
|
|
122
163
|
return "not_found";
|
|
123
164
|
}
|
|
@@ -132,14 +173,18 @@ var PermissionDenied = class PermissionDenied extends TangoError {
|
|
|
132
173
|
static BRAND = "tango.error.permission_denied";
|
|
133
174
|
__tangoBrand = PermissionDenied.BRAND;
|
|
134
175
|
status = 403;
|
|
135
|
-
|
|
136
|
-
return typeof value === "object" && value !== null && value.__tangoBrand === PermissionDenied.BRAND;
|
|
137
|
-
}
|
|
176
|
+
/** Create a permission-denied error with optional custom message. */
|
|
138
177
|
constructor(message = "Permission denied") {
|
|
139
178
|
super(message);
|
|
140
179
|
this.name = "PermissionDenied";
|
|
141
180
|
Object.setPrototypeOf(this, PermissionDenied.prototype);
|
|
142
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Narrow an unknown value to `PermissionDenied`.
|
|
184
|
+
*/
|
|
185
|
+
static isPermissionDenied(value) {
|
|
186
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === PermissionDenied.BRAND;
|
|
187
|
+
}
|
|
143
188
|
getErrorName() {
|
|
144
189
|
return "permission_denied";
|
|
145
190
|
}
|
|
@@ -154,14 +199,18 @@ var AuthenticationError = class AuthenticationError extends TangoError {
|
|
|
154
199
|
static BRAND = "tango.error.authentication";
|
|
155
200
|
__tangoBrand = AuthenticationError.BRAND;
|
|
156
201
|
status = 401;
|
|
157
|
-
|
|
158
|
-
return typeof value === "object" && value !== null && value.__tangoBrand === AuthenticationError.BRAND;
|
|
159
|
-
}
|
|
202
|
+
/** Create an authentication error with optional custom message. */
|
|
160
203
|
constructor(message = "Authentication required") {
|
|
161
204
|
super(message);
|
|
162
205
|
this.name = "AuthenticationError";
|
|
163
206
|
Object.setPrototypeOf(this, AuthenticationError.prototype);
|
|
164
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Narrow an unknown value to `AuthenticationError`.
|
|
210
|
+
*/
|
|
211
|
+
static isAuthenticationError(value) {
|
|
212
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === AuthenticationError.BRAND;
|
|
213
|
+
}
|
|
165
214
|
getErrorName() {
|
|
166
215
|
return "authentication_error";
|
|
167
216
|
}
|
|
@@ -181,11 +230,9 @@ __export(errors_exports, {
|
|
|
181
230
|
PermissionDenied: () => PermissionDenied,
|
|
182
231
|
TangoError: () => TangoError,
|
|
183
232
|
ValidationError: () => ValidationError,
|
|
184
|
-
|
|
185
|
-
factories: () => factories_exports,
|
|
186
|
-
toHttpError: () => toHttpError
|
|
233
|
+
factories: () => factories_exports
|
|
187
234
|
});
|
|
188
235
|
|
|
189
236
|
//#endregion
|
|
190
|
-
export { AuthenticationError, ConflictError, HttpErrorFactory, NotFoundError, PermissionDenied, ValidationError,
|
|
191
|
-
//# sourceMappingURL=errors-
|
|
237
|
+
export { AuthenticationError, ConflictError, HttpErrorFactory, NotFoundError, PermissionDenied, ValidationError, errors_exports, factories_exports };
|
|
238
|
+
//# sourceMappingURL=errors-_tWsmNyZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors-_tWsmNyZ.js","names":["config: HttpErrorFactoryConfig","value: unknown","error: unknown","error: ZodLikeError","details: Record<string, string[]>","errorClass: new (...args: any[]) => T","handler: (error: T) => HttpError","ErrorClass: new (...args: any[]) => Error","message: string","value: unknown","message: string","details?: ErrorDetails","err: unknown","message: string","value: unknown","message: string","value: unknown","message: string","value: unknown"],"sources":["../src/errors/factories/HttpErrorFactory.ts","../src/errors/factories/index.ts","../src/errors/ConflictError.ts","../src/errors/ValidationError.ts","../src/errors/NotFoundError.ts","../src/errors/PermissionDenied.ts","../src/errors/AuthenticationError.ts","../src/errors/index.ts"],"sourcesContent":["import { TangoError } from '../TangoError';\nimport type { HttpError } from '../HttpError';\nimport { isError, isObject } from '../../runtime/index';\n\nexport interface HttpErrorFactoryConfig {\n /**\n * When true, raw error messages are included in HTTP responses.\n * When false, generic messages are returned for non-TangoError exceptions.\n * Defaults to true (dev-friendly). Set to false in production.\n */\n exposeErrors?: boolean;\n}\n\ntype ZodLikeIssue = {\n path?: unknown[];\n message?: unknown;\n};\n\ntype ZodLikeError = Error & {\n issues: ZodLikeIssue[];\n};\n\n/**\n * Converts errors into structured HTTP error responses.\n * Supports TangoError subclasses out of the box, and custom error handlers\n * can be registered for third-party or application-specific error types.\n *\n * @example\n * ```typescript\n * // Development (default) — exposes real error messages\n * const devFactory = new HttpErrorFactory();\n *\n * // Production — hides internal error details\n * const prodFactory = new HttpErrorFactory({ exposeErrors: false });\n *\n * // Register a custom handler for a third-party error\n * prodFactory.registerHandler(ZodError, (err) => ({\n * status: 400,\n * body: { error: 'Validation failed', details: err.flatten().fieldErrors },\n * }));\n *\n * // Quick one-shot conversion with dev defaults\n * const httpError = HttpErrorFactory.toHttpError(new NotFoundError('missing'));\n * ```\n */\nexport class HttpErrorFactory {\n static readonly BRAND = 'tango.error_factory.http' as const;\n readonly __tangoBrand: typeof HttpErrorFactory.BRAND = HttpErrorFactory.BRAND;\n\n // oxlint-disable-next-line typescript/no-explicit-any\n private handlers = new Map<new (...args: any[]) => Error, (error: Error) => HttpError>();\n private exposeErrors: boolean;\n\n constructor(config: HttpErrorFactoryConfig = {}) {\n this.exposeErrors = config.exposeErrors ?? true;\n }\n\n /**\n * Narrow an unknown value to `HttpErrorFactory`.\n */\n static isHttpErrorFactory(value: unknown): value is HttpErrorFactory {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === HttpErrorFactory.BRAND\n );\n }\n\n /**\n * Convert an unknown error into an `HttpError` using dev-friendly defaults.\n * Shorthand for `new HttpErrorFactory().create(error)`.\n */\n static toHttpError(error: unknown): HttpError {\n return new HttpErrorFactory().create(error);\n }\n\n private static isZodLikeValidationError(error: unknown): error is ZodLikeError {\n return (\n isError(error) &&\n isObject(error) &&\n Array.isArray((error as { issues?: unknown }).issues) &&\n (error as { name?: unknown }).name === 'ZodError'\n );\n }\n\n private static zodLikeErrorDetails(error: ZodLikeError): Record<string, string[]> {\n const details: Record<string, string[]> = {};\n\n for (const issue of error.issues) {\n const key = Array.isArray(issue.path) && issue.path.length > 0 ? String(issue.path.join('.')) : '_schema';\n const message = typeof issue.message === 'string' ? issue.message : 'Invalid value';\n\n const existing = details[key];\n if (existing) {\n existing.push(message);\n } else {\n details[key] = [message];\n }\n }\n\n return details;\n }\n\n /**\n * Register a custom mapper for an application or third-party error type.\n */\n\n // oxlint-disable-next-line typescript/no-explicit-any\n registerHandler<T extends Error>(errorClass: new (...args: any[]) => T, handler: (error: T) => HttpError): this {\n this.handlers.set(errorClass, handler as (error: Error) => HttpError);\n return this;\n }\n\n /**\n * Convert an unknown error into the normalized HTTP error shape Tango uses.\n */\n create(error: unknown): HttpError {\n if (TangoError.isTangoError(error)) {\n return error.toHttpError();\n }\n\n if (HttpErrorFactory.isZodLikeValidationError(error)) {\n return {\n status: 400,\n body: {\n error: error.message || 'Validation failed',\n details: HttpErrorFactory.zodLikeErrorDetails(error),\n },\n };\n }\n\n for (const [ErrorClass, handler] of this.handlers) {\n if (this.isErrorClassInstance(error, ErrorClass)) {\n return handler(error);\n }\n }\n\n if (isError(error)) {\n return {\n status: 500,\n body: {\n error: this.exposeErrors ? error.message : 'Internal Server Error',\n details: null,\n },\n };\n }\n\n return {\n status: 500,\n body: {\n error: 'Unknown error occurred',\n details: null,\n },\n };\n }\n\n // oxlint-disable-next-line typescript/no-explicit-any\n private isErrorClassInstance(error: unknown, ErrorClass: new (...args: any[]) => Error): error is Error {\n if (!isError(error)) {\n return false;\n }\n\n const expectedBrand = (ErrorClass as { BRAND?: unknown }).BRAND;\n if (\n typeof expectedBrand === 'string' &&\n isObject(error) &&\n (error as { __tangoBrand?: unknown }).__tangoBrand === expectedBrand\n ) {\n return true;\n }\n\n const constructorName = (error as { constructor?: { name?: unknown } }).constructor?.name;\n if (typeof constructorName === 'string' && constructorName === ErrorClass.name) {\n return true;\n }\n\n const errorName = (error as { name?: unknown }).name;\n if (typeof errorName === 'string' && errorName === ErrorClass.name) {\n return true;\n }\n\n return false;\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { HttpErrorFactory, type HttpErrorFactoryConfig } from './HttpErrorFactory';\n","import { TangoError, type ErrorDetails } from './TangoError';\n\n/** Error for conflicting resource state (HTTP 409). */\nexport class ConflictError extends TangoError {\n static readonly BRAND = 'tango.error.conflict' as const;\n readonly __tangoBrand: typeof ConflictError.BRAND = ConflictError.BRAND;\n status = 409;\n\n /** Create a conflict error with optional custom message. */\n constructor(message: string = 'Resource conflict') {\n super(message);\n this.name = 'ConflictError';\n Object.setPrototypeOf(this, ConflictError.prototype);\n }\n\n /**\n * Narrow an unknown value to `ConflictError`.\n */\n static isConflictError(value: unknown): value is ConflictError {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === ConflictError.BRAND\n );\n }\n\n protected override getErrorName(): string {\n return 'conflict';\n }\n\n protected override getDetails(): ErrorDetails {\n return undefined;\n }\n}\n","import { TangoError, type ErrorDetails } from './TangoError';\n\n/** Error for request validation failures (HTTP 400). */\nexport class ValidationError extends TangoError {\n readonly __tangoValidationErrorBrand = 'tango.error.validation' as const;\n status = 400;\n\n constructor(\n message: string,\n public details?: ErrorDetails\n ) {\n super(message);\n this.name = 'ValidationError';\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n\n /**\n * Narrow an unknown value to `ValidationError`, including common legacy shapes.\n */\n static isValidationError(err: unknown): err is ValidationError {\n return (\n (!!err &&\n typeof err === 'object' &&\n (err as { __tangoValidationErrorBrand?: string }).__tangoValidationErrorBrand ===\n 'tango.error.validation') ||\n (typeof err === 'object' &&\n err !== null &&\n 'fields' in err &&\n typeof (err as { fields: unknown }).fields === 'object')\n );\n }\n\n protected override getErrorName(): string {\n return 'ValidationError';\n }\n\n protected override getDetails(): ErrorDetails {\n return this.details;\n }\n}\n","import { TangoError, type ErrorDetails } from './TangoError';\n\n/** Error for missing resources (HTTP 404). */\nexport class NotFoundError extends TangoError {\n static readonly BRAND = 'tango.error.not_found' as const;\n readonly __tangoBrand: typeof NotFoundError.BRAND = NotFoundError.BRAND;\n status = 404;\n\n /** Create a not-found error with optional custom message. */\n constructor(message: string = 'Resource not found') {\n super(message);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n\n /**\n * Narrow an unknown value to `NotFoundError`.\n */\n static isNotFoundError(value: unknown): value is NotFoundError {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === NotFoundError.BRAND\n );\n }\n\n protected override getErrorName(): string {\n return 'not_found';\n }\n\n protected override getDetails(): ErrorDetails {\n return undefined;\n }\n}\n","import { TangoError, type ErrorDetails } from './TangoError';\n\n/** Error for authorization failures (HTTP 403). */\nexport class PermissionDenied extends TangoError {\n static readonly BRAND = 'tango.error.permission_denied' as const;\n readonly __tangoBrand: typeof PermissionDenied.BRAND = PermissionDenied.BRAND;\n status = 403;\n\n /** Create a permission-denied error with optional custom message. */\n constructor(message: string = 'Permission denied') {\n super(message);\n this.name = 'PermissionDenied';\n Object.setPrototypeOf(this, PermissionDenied.prototype);\n }\n\n /**\n * Narrow an unknown value to `PermissionDenied`.\n */\n static isPermissionDenied(value: unknown): value is PermissionDenied {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PermissionDenied.BRAND\n );\n }\n\n protected override getErrorName(): string {\n return 'permission_denied';\n }\n\n protected override getDetails(): ErrorDetails {\n return undefined;\n }\n}\n","import { TangoError, type ErrorDetails } from './TangoError';\n\n/** Error for missing/invalid authentication (HTTP 401). */\nexport class AuthenticationError extends TangoError {\n static readonly BRAND = 'tango.error.authentication' as const;\n readonly __tangoBrand: typeof AuthenticationError.BRAND = AuthenticationError.BRAND;\n status = 401;\n\n /** Create an authentication error with optional custom message. */\n constructor(message: string = 'Authentication required') {\n super(message);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n\n /**\n * Narrow an unknown value to `AuthenticationError`.\n */\n static isAuthenticationError(value: unknown): value is AuthenticationError {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === AuthenticationError.BRAND\n );\n }\n\n protected override getErrorName(): string {\n return 'authentication_error';\n }\n\n protected override getDetails(): ErrorDetails {\n return undefined;\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nimport type { HttpError } from './HttpError';\nimport * as factories from './factories/index';\nimport { TangoError, type ErrorDetails, type ErrorEnvelope, type ProblemDetails } from './TangoError';\nimport { ConflictError } from './ConflictError';\nimport { ValidationError } from './ValidationError';\nimport { NotFoundError } from './NotFoundError';\nimport { PermissionDenied } from './PermissionDenied';\nimport { AuthenticationError } from './AuthenticationError';\nimport { HttpErrorFactory, type HttpErrorFactoryConfig } from './factories/HttpErrorFactory';\n\nexport {\n AuthenticationError,\n ConflictError,\n HttpErrorFactory,\n NotFoundError,\n PermissionDenied,\n TangoError,\n ValidationError,\n factories,\n};\n\nexport type { ErrorDetails, ErrorEnvelope, HttpError, HttpErrorFactoryConfig, ProblemDetails };\n"],"mappings":"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"}
|