@cosmneo/onion-lasagna 0.1.11 → 0.2.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/{chunk-TVTA63VN.js → chunk-2BVCU32G.js} +22 -51
- package/dist/chunk-2BVCU32G.js.map +1 -0
- package/dist/{chunk-B5SIIVRK.js → chunk-3BY5RBF2.js} +3 -3
- package/dist/chunk-3BY5RBF2.js.map +1 -0
- package/dist/chunk-3QKQCJSP.js +14 -0
- package/dist/chunk-3QKQCJSP.js.map +1 -0
- package/dist/{chunk-SI7FI7MT.js → chunk-42JMR62O.js} +3 -3
- package/dist/chunk-42JMR62O.js.map +1 -0
- package/dist/{chunk-273YQRYN.js → chunk-A4JUAZK4.js} +3 -3
- package/dist/chunk-A4JUAZK4.js.map +1 -0
- package/dist/{chunk-AJ6QXXLC.js → chunk-BZULBF4N.js} +4 -4
- package/dist/chunk-BZULBF4N.js.map +1 -0
- package/dist/{chunk-5TNCT634.js → chunk-DS7TE6KZ.js} +3 -7
- package/dist/chunk-DS7TE6KZ.js.map +1 -0
- package/dist/{chunk-PWTJLVHY.js → chunk-GGSAAZPM.js} +71 -30
- package/dist/chunk-GGSAAZPM.js.map +1 -0
- package/dist/{chunk-Q5QCTEOD.js → chunk-PUVAB3JX.js} +3 -3
- package/dist/chunk-PUVAB3JX.js.map +1 -0
- package/dist/{chunk-RRXLBGQB.js → chunk-T7S574XQ.js} +68 -4
- package/dist/chunk-T7S574XQ.js.map +1 -0
- package/dist/{chunk-OKFXZHBC.js → chunk-ZG26OQFN.js} +2 -2
- package/dist/chunk-ZG26OQFN.js.map +1 -0
- package/dist/{backend/core/global.cjs → global.cjs} +6 -6
- package/dist/global.cjs.map +1 -0
- package/dist/{backend/core/global.d.cts → global.d.cts} +201 -20
- package/dist/{backend/core/global.d.ts → global.d.ts} +201 -20
- package/dist/global.js +27 -0
- package/dist/http/index.cjs +146 -245
- package/dist/http/index.cjs.map +1 -1
- package/dist/http/index.d.cts +2 -5
- package/dist/http/index.d.ts +2 -5
- package/dist/http/index.js +20 -26
- package/dist/http/openapi/index.cjs +4 -4
- package/dist/http/openapi/index.cjs.map +1 -1
- package/dist/http/openapi/index.d.cts +1 -1
- package/dist/http/openapi/index.d.ts +1 -1
- package/dist/http/openapi/index.js +2 -3
- package/dist/http/route/index.cjs +5 -10
- package/dist/http/route/index.cjs.map +1 -1
- package/dist/http/route/index.d.cts +10 -10
- package/dist/http/route/index.d.ts +10 -10
- package/dist/http/route/index.js +2 -5
- package/dist/http/schema/index.cjs +5 -98
- package/dist/http/schema/index.cjs.map +1 -1
- package/dist/http/schema/index.d.cts +0 -4
- package/dist/http/schema/index.d.ts +0 -4
- package/dist/http/schema/index.js +2 -15
- package/dist/http/schema/types.cjs +3 -3
- package/dist/http/schema/types.cjs.map +1 -1
- package/dist/http/schema/types.js +1 -2
- package/dist/http/server/index.cjs +97 -45
- package/dist/http/server/index.cjs.map +1 -1
- package/dist/http/server/index.d.cts +261 -93
- package/dist/http/server/index.d.ts +261 -93
- package/dist/http/server/index.js +6 -61
- package/dist/http/server/index.js.map +1 -1
- package/dist/http/{frameworks/fastify.cjs → shared/index.cjs} +32 -88
- package/dist/http/shared/index.cjs.map +1 -0
- package/dist/http/shared/index.d.cts +148 -0
- package/dist/http/shared/index.d.ts +148 -0
- package/dist/{chunk-ZEVR3B76.js → http/shared/index.js} +16 -89
- package/dist/http/shared/index.js.map +1 -0
- package/dist/{backend/core/onion-layers.cjs → index.cjs} +93 -42
- package/dist/index.cjs.map +1 -0
- package/dist/{backend/core/onion-layers.d.ts → index.d.cts} +182 -6
- package/dist/{backend/core/onion-layers.d.cts → index.d.ts} +182 -6
- package/dist/{backend/core/onion-layers.js → index.js} +49 -37
- package/dist/index.js.map +1 -0
- package/dist/ports.cjs +19 -0
- package/dist/ports.cjs.map +1 -0
- package/dist/ports.d.cts +250 -0
- package/dist/ports.d.ts +250 -0
- package/dist/ports.js +1 -0
- package/dist/{router-definition.type-29Mmkxs9.d.ts → router-definition.type-DORVlLNk.d.ts} +1 -5
- package/dist/{router-definition.type-B69DjYzf.d.cts → router-definition.type-ynBhT16T.d.cts} +1 -5
- package/dist/types.cjs +19 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +23 -0
- package/dist/types.d.ts +23 -0
- package/dist/types.js +1 -0
- package/package.json +34 -139
- package/dist/backend/core/global.cjs.map +0 -1
- package/dist/backend/core/global.js +0 -35
- package/dist/backend/core/global.js.map +0 -1
- package/dist/backend/core/onion-layers.cjs.map +0 -1
- package/dist/backend/core/onion-layers.js.map +0 -1
- package/dist/backend/core/presentation.cjs +0 -265
- package/dist/backend/core/presentation.cjs.map +0 -1
- package/dist/backend/core/presentation.d.cts +0 -176
- package/dist/backend/core/presentation.d.ts +0 -176
- package/dist/backend/core/presentation.js +0 -16
- package/dist/chunk-273YQRYN.js.map +0 -1
- package/dist/chunk-3WMF4XXN.js +0 -44
- package/dist/chunk-3WMF4XXN.js.map +0 -1
- package/dist/chunk-55ROG54O.js +0 -1
- package/dist/chunk-5TNCT634.js.map +0 -1
- package/dist/chunk-AJ6QXXLC.js.map +0 -1
- package/dist/chunk-B5SIIVRK.js.map +0 -1
- package/dist/chunk-CGZBV6BD.js +0 -54
- package/dist/chunk-HRS3NSHF.js +0 -51
- package/dist/chunk-HRS3NSHF.js.map +0 -1
- package/dist/chunk-L2SRWSPH.js +0 -216
- package/dist/chunk-L2SRWSPH.js.map +0 -1
- package/dist/chunk-OKFXZHBC.js.map +0 -1
- package/dist/chunk-PWTJLVHY.js.map +0 -1
- package/dist/chunk-Q5QCTEOD.js.map +0 -1
- package/dist/chunk-QN7JZMFW.js +0 -40
- package/dist/chunk-QN7JZMFW.js.map +0 -1
- package/dist/chunk-RRXLBGQB.js.map +0 -1
- package/dist/chunk-SI7FI7MT.js.map +0 -1
- package/dist/chunk-TVTA63VN.js.map +0 -1
- package/dist/chunk-ZEVR3B76.js.map +0 -1
- package/dist/create-server-routes-COxEzdnw.d.ts +0 -84
- package/dist/create-server-routes-XBGmav3T.d.cts +0 -84
- package/dist/http/client/index.cjs +0 -257
- package/dist/http/client/index.cjs.map +0 -1
- package/dist/http/client/index.d.cts +0 -89
- package/dist/http/client/index.d.ts +0 -89
- package/dist/http/client/index.js +0 -11
- package/dist/http/client/index.js.map +0 -1
- package/dist/http/frameworks/elysia.cjs +0 -789
- package/dist/http/frameworks/elysia.cjs.map +0 -1
- package/dist/http/frameworks/elysia.d.cts +0 -182
- package/dist/http/frameworks/elysia.d.ts +0 -182
- package/dist/http/frameworks/elysia.js +0 -141
- package/dist/http/frameworks/elysia.js.map +0 -1
- package/dist/http/frameworks/fastify.cjs.map +0 -1
- package/dist/http/frameworks/fastify.d.cts +0 -159
- package/dist/http/frameworks/fastify.d.ts +0 -159
- package/dist/http/frameworks/fastify.js +0 -77
- package/dist/http/frameworks/fastify.js.map +0 -1
- package/dist/http/frameworks/hono.cjs +0 -801
- package/dist/http/frameworks/hono.cjs.map +0 -1
- package/dist/http/frameworks/hono.d.cts +0 -163
- package/dist/http/frameworks/hono.d.ts +0 -163
- package/dist/http/frameworks/hono.js +0 -153
- package/dist/http/frameworks/hono.js.map +0 -1
- package/dist/http/frameworks/nestjs.cjs +0 -267
- package/dist/http/frameworks/nestjs.cjs.map +0 -1
- package/dist/http/frameworks/nestjs.d.cts +0 -201
- package/dist/http/frameworks/nestjs.d.ts +0 -201
- package/dist/http/frameworks/nestjs.js +0 -89
- package/dist/http/frameworks/nestjs.js.map +0 -1
- package/dist/http/react-query/index.cjs +0 -365
- package/dist/http/react-query/index.cjs.map +0 -1
- package/dist/http/react-query/index.d.cts +0 -167
- package/dist/http/react-query/index.d.ts +0 -167
- package/dist/http/react-query/index.js +0 -122
- package/dist/http/react-query/index.js.map +0 -1
- package/dist/http/schema/typebox.cjs +0 -76
- package/dist/http/schema/typebox.cjs.map +0 -1
- package/dist/http/schema/typebox.d.cts +0 -73
- package/dist/http/schema/typebox.d.ts +0 -73
- package/dist/http/schema/typebox.js +0 -10
- package/dist/http/schema/typebox.js.map +0 -1
- package/dist/http/schema/zod.cjs +0 -69
- package/dist/http/schema/zod.cjs.map +0 -1
- package/dist/http/schema/zod.d.cts +0 -77
- package/dist/http/schema/zod.d.ts +0 -77
- package/dist/http/schema/zod.js +0 -10
- package/dist/http/schema/zod.js.map +0 -1
- package/dist/types-CirYpK38.d.ts +0 -262
- package/dist/types-Co3cPRWT.d.cts +0 -262
- package/dist/types-D1yDxONt.d.ts +0 -157
- package/dist/types-DF29z22c.d.cts +0 -157
- package/dist/types-Dw-b8WZ7.d.cts +0 -50
- package/dist/types-Dw-b8WZ7.d.ts +0 -50
- package/dist/validation-error.type-CJLDfv3Q.d.cts +0 -203
- package/dist/validation-error.type-CJLDfv3Q.d.ts +0 -203
- /package/dist/{backend/core/presentation.js.map → global.js.map} +0 -0
- /package/dist/{chunk-55ROG54O.js.map → ports.js.map} +0 -0
- /package/dist/{chunk-CGZBV6BD.js.map → types.js.map} +0 -0
|
@@ -1,40 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UseCaseError
|
|
3
|
+
} from "./chunk-T7S574XQ.js";
|
|
1
4
|
import {
|
|
2
5
|
CodedError,
|
|
3
6
|
ErrorCodes
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
|
|
6
|
-
// src/backend/core/onion-layers/app/exceptions/use-case.error.ts
|
|
7
|
-
var UseCaseError = class _UseCaseError extends CodedError {
|
|
8
|
-
/**
|
|
9
|
-
* Creates a new UseCaseError instance.
|
|
10
|
-
*
|
|
11
|
-
* @param options - Error configuration
|
|
12
|
-
* @param options.message - Human-readable error description
|
|
13
|
-
* @param options.code - Machine-readable error code (default: 'USE_CASE_ERROR')
|
|
14
|
-
* @param options.cause - Optional underlying error
|
|
15
|
-
*/
|
|
16
|
-
constructor({
|
|
17
|
-
message,
|
|
18
|
-
code = ErrorCodes.App.USE_CASE_ERROR,
|
|
19
|
-
cause
|
|
20
|
-
}) {
|
|
21
|
-
super({ message, code, cause });
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Creates a UseCaseError from a caught error.
|
|
25
|
-
*
|
|
26
|
-
* @param cause - The original caught error
|
|
27
|
-
* @returns A new UseCaseError instance with the cause attached
|
|
28
|
-
*/
|
|
29
|
-
static fromError(cause) {
|
|
30
|
-
return new _UseCaseError({
|
|
31
|
-
message: cause instanceof Error ? cause.message : "Use case error",
|
|
32
|
-
cause
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
};
|
|
7
|
+
} from "./chunk-A4JUAZK4.js";
|
|
36
8
|
|
|
37
|
-
// src/
|
|
9
|
+
// src/domain/exceptions/domain.error.ts
|
|
38
10
|
var DomainError = class _DomainError extends CodedError {
|
|
39
11
|
/**
|
|
40
12
|
* Creates a new DomainError instance.
|
|
@@ -65,7 +37,7 @@ var DomainError = class _DomainError extends CodedError {
|
|
|
65
37
|
}
|
|
66
38
|
};
|
|
67
39
|
|
|
68
|
-
// src/
|
|
40
|
+
// src/infra/exceptions/infra.error.ts
|
|
69
41
|
var InfraError = class _InfraError extends CodedError {
|
|
70
42
|
/**
|
|
71
43
|
* Creates a new InfraError instance.
|
|
@@ -96,7 +68,7 @@ var InfraError = class _InfraError extends CodedError {
|
|
|
96
68
|
}
|
|
97
69
|
};
|
|
98
70
|
|
|
99
|
-
// src/
|
|
71
|
+
// src/app/exceptions/not-found.error.ts
|
|
100
72
|
var NotFoundError = class _NotFoundError extends UseCaseError {
|
|
101
73
|
/**
|
|
102
74
|
* Creates a new NotFoundError instance.
|
|
@@ -127,7 +99,7 @@ var NotFoundError = class _NotFoundError extends UseCaseError {
|
|
|
127
99
|
}
|
|
128
100
|
};
|
|
129
101
|
|
|
130
|
-
// src/
|
|
102
|
+
// src/app/exceptions/conflict.error.ts
|
|
131
103
|
var ConflictError = class _ConflictError extends UseCaseError {
|
|
132
104
|
/**
|
|
133
105
|
* Creates a new ConflictError instance.
|
|
@@ -158,7 +130,7 @@ var ConflictError = class _ConflictError extends UseCaseError {
|
|
|
158
130
|
}
|
|
159
131
|
};
|
|
160
132
|
|
|
161
|
-
// src/
|
|
133
|
+
// src/app/exceptions/unprocessable.error.ts
|
|
162
134
|
var UnprocessableError = class _UnprocessableError extends UseCaseError {
|
|
163
135
|
/**
|
|
164
136
|
* Creates a new UnprocessableError instance.
|
|
@@ -189,7 +161,7 @@ var UnprocessableError = class _UnprocessableError extends UseCaseError {
|
|
|
189
161
|
}
|
|
190
162
|
};
|
|
191
163
|
|
|
192
|
-
// src/
|
|
164
|
+
// src/app/exceptions/forbidden.error.ts
|
|
193
165
|
var ForbiddenError = class _ForbiddenError extends UseCaseError {
|
|
194
166
|
/**
|
|
195
167
|
* Creates a new ForbiddenError instance.
|
|
@@ -220,45 +192,44 @@ var ForbiddenError = class _ForbiddenError extends UseCaseError {
|
|
|
220
192
|
}
|
|
221
193
|
};
|
|
222
194
|
|
|
223
|
-
// src/
|
|
224
|
-
var
|
|
195
|
+
// src/presentation/exceptions/access-denied.error.ts
|
|
196
|
+
var AccessDeniedError = class _AccessDeniedError extends CodedError {
|
|
225
197
|
/**
|
|
226
|
-
* Creates a new
|
|
198
|
+
* Creates a new AccessDeniedError instance.
|
|
227
199
|
*
|
|
228
200
|
* @param options - Error configuration
|
|
229
|
-
* @param options.message - Description of why
|
|
230
|
-
* @param options.code - Machine-readable error code (default: '
|
|
201
|
+
* @param options.message - Description of why access was denied
|
|
202
|
+
* @param options.code - Machine-readable error code (default: 'ACCESS_DENIED')
|
|
231
203
|
* @param options.cause - Optional underlying error
|
|
232
204
|
*/
|
|
233
205
|
constructor({
|
|
234
206
|
message,
|
|
235
|
-
code = ErrorCodes.
|
|
207
|
+
code = ErrorCodes.Presentation.ACCESS_DENIED,
|
|
236
208
|
cause
|
|
237
209
|
}) {
|
|
238
210
|
super({ message, code, cause });
|
|
239
211
|
}
|
|
240
212
|
/**
|
|
241
|
-
* Creates an
|
|
213
|
+
* Creates an AccessDeniedError from a caught error.
|
|
242
214
|
*
|
|
243
215
|
* @param cause - The original caught error
|
|
244
|
-
* @returns A new
|
|
216
|
+
* @returns A new AccessDeniedError instance with the cause attached
|
|
245
217
|
*/
|
|
246
218
|
static fromError(cause) {
|
|
247
|
-
return new
|
|
248
|
-
message: cause instanceof Error ? cause.message : "
|
|
219
|
+
return new _AccessDeniedError({
|
|
220
|
+
message: cause instanceof Error ? cause.message : "Access denied",
|
|
249
221
|
cause
|
|
250
222
|
});
|
|
251
223
|
}
|
|
252
224
|
};
|
|
253
225
|
|
|
254
226
|
export {
|
|
255
|
-
UseCaseError,
|
|
256
227
|
DomainError,
|
|
257
228
|
InfraError,
|
|
258
229
|
NotFoundError,
|
|
259
230
|
ConflictError,
|
|
260
231
|
UnprocessableError,
|
|
261
232
|
ForbiddenError,
|
|
262
|
-
|
|
233
|
+
AccessDeniedError
|
|
263
234
|
};
|
|
264
|
-
//# sourceMappingURL=chunk-
|
|
235
|
+
//# sourceMappingURL=chunk-2BVCU32G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/domain/exceptions/domain.error.ts","../src/infra/exceptions/infra.error.ts","../src/app/exceptions/not-found.error.ts","../src/app/exceptions/conflict.error.ts","../src/app/exceptions/unprocessable.error.ts","../src/app/exceptions/forbidden.error.ts","../src/presentation/exceptions/access-denied.error.ts"],"sourcesContent":["import { CodedError } from '../../global/exceptions/coded-error.error';\nimport { ErrorCodes, type DomainErrorCode } from '../../global/exceptions/error-codes.const';\n\n/**\n * Base error class for domain layer failures.\n *\n * Domain errors represent violations of business rules, invariants,\n * or aggregate consistency. They originate from the core domain logic\n * and should be caught and handled by the application layer.\n *\n * **When to throw:**\n * - Business rule violations (e.g., \"Cannot withdraw more than balance\")\n * - Invariant violations (e.g., \"Email format invalid\")\n * - Aggregate consistency failures\n *\n * **Child classes:**\n * - {@link InvariantViolationError} - Value object or entity invariant failures\n * - {@link PartialLoadError} - Incomplete aggregate reconstitution\n *\n * @example\n * ```typescript\n * if (account.balance < amount) {\n * throw new DomainError({\n * message: 'Insufficient funds for withdrawal',\n * code: 'INSUFFICIENT_FUNDS',\n * });\n * }\n * ```\n */\nexport class DomainError extends CodedError {\n /**\n * Creates a new DomainError instance.\n *\n * @param options - Error configuration\n * @param options.message - Human-readable error description\n * @param options.code - Machine-readable error code (default: 'DOMAIN_ERROR')\n * @param options.cause - Optional underlying error\n */\n constructor({\n message,\n code = ErrorCodes.Domain.DOMAIN_ERROR,\n cause,\n }: {\n message: string;\n code?: DomainErrorCode | string;\n cause?: unknown;\n }) {\n super({ message, code, cause });\n }\n\n /**\n * Creates a DomainError from a caught error.\n *\n * @param cause - The original caught error\n * @returns A new DomainError instance with the cause attached\n */\n static override fromError(cause: unknown): DomainError {\n return new DomainError({\n message: cause instanceof Error ? cause.message : 'Domain error',\n cause,\n });\n }\n}\n","import { CodedError } from '../../global/exceptions/coded-error.error';\nimport { ErrorCodes, type InfraErrorCode } from '../../global/exceptions/error-codes.const';\n\n/**\n * Base error class for infrastructure layer failures.\n *\n * Infrastructure errors represent failures in external dependencies\n * such as databases, network services, file systems, or third-party APIs.\n * They are automatically created by {@link BaseOutboundAdapter} when\n * repository or gateway methods fail.\n *\n * **When to throw:**\n * - Database connection or query failures\n * - Network timeouts or connection errors\n * - External API failures\n * - File system errors\n *\n * **Child classes:**\n * - {@link DbError} - Database operation failures\n * - {@link NetworkError} - Network connectivity issues\n * - {@link TimeoutError} - Operation timeout\n * - {@link ExternalServiceError} - Third-party service failures\n *\n * @example\n * ```typescript\n * // In a repository extending BaseOutboundAdapter\n * protected override createInfraError(error: unknown, methodName: string): InfraError {\n * return new DbError({\n * message: `Database error in ${methodName}`,\n * cause: error,\n * });\n * }\n * ```\n */\nexport class InfraError extends CodedError {\n /**\n * Creates a new InfraError instance.\n *\n * @param options - Error configuration\n * @param options.message - Human-readable error description\n * @param options.code - Machine-readable error code (default: 'INFRA_ERROR')\n * @param options.cause - Optional underlying error\n */\n constructor({\n message,\n code = ErrorCodes.Infra.INFRA_ERROR,\n cause,\n }: {\n message: string;\n code?: InfraErrorCode | string;\n cause?: unknown;\n }) {\n super({ message, code, cause });\n }\n\n /**\n * Creates an InfraError from a caught error.\n *\n * @param cause - The original caught error\n * @returns A new InfraError instance with the cause attached\n */\n static override fromError(cause: unknown): InfraError {\n return new InfraError({\n message: cause instanceof Error ? cause.message : 'Infrastructure error',\n cause,\n });\n }\n}\n","import { ErrorCodes, type AppErrorCode } from '../../global/exceptions/error-codes.const';\nimport { UseCaseError } from './use-case.error';\n\n/**\n * Error thrown when a requested resource does not exist.\n *\n * Indicates that the entity or resource referenced by the request\n * could not be found in the system.\n *\n * **When to throw:**\n * - Entity lookup by ID returns null\n * - Referenced resource doesn't exist\n * - Parent entity for a child operation not found\n *\n * @example\n * ```typescript\n * const user = await this.userRepo.findById(userId);\n * if (!user) {\n * throw new NotFoundError({\n * message: `User with ID ${userId} not found`,\n * code: 'USER_NOT_FOUND',\n * });\n * }\n * ```\n *\n * @extends UseCaseError\n */\nexport class NotFoundError extends UseCaseError {\n /**\n * Creates a new NotFoundError instance.\n *\n * @param options - Error configuration\n * @param options.message - Description of what was not found\n * @param options.code - Machine-readable error code (default: 'NOT_FOUND')\n * @param options.cause - Optional underlying error\n */\n constructor({\n message,\n code = ErrorCodes.App.NOT_FOUND,\n cause,\n }: {\n message: string;\n code?: AppErrorCode | string;\n cause?: unknown;\n }) {\n super({ message, code, cause });\n }\n\n /**\n * Creates a NotFoundError from a caught error.\n *\n * @param cause - The original caught error\n * @returns A new NotFoundError instance with the cause attached\n */\n static override fromError(cause: unknown): NotFoundError {\n return new NotFoundError({\n message: cause instanceof Error ? cause.message : 'Resource not found',\n cause,\n });\n }\n}\n","import { ErrorCodes, type AppErrorCode } from '../../global/exceptions/error-codes.const';\nimport { UseCaseError } from './use-case.error';\n\n/**\n * Error thrown when an operation conflicts with existing state.\n *\n * Indicates that the requested operation cannot be completed because\n * it would violate uniqueness constraints or cause state conflicts.\n *\n * **When to throw:**\n * - Duplicate unique field (e.g., email already registered)\n * - Optimistic locking conflict (stale version)\n * - Concurrent modification detected\n *\n * @example\n * ```typescript\n * const existing = await this.userRepo.findByEmail(email);\n * if (existing) {\n * throw new ConflictError({\n * message: 'Email already registered',\n * code: 'EMAIL_ALREADY_EXISTS',\n * });\n * }\n * ```\n *\n * @extends UseCaseError\n */\nexport class ConflictError extends UseCaseError {\n /**\n * Creates a new ConflictError instance.\n *\n * @param options - Error configuration\n * @param options.message - Description of the conflict\n * @param options.code - Machine-readable error code (default: 'CONFLICT')\n * @param options.cause - Optional underlying error\n */\n constructor({\n message,\n code = ErrorCodes.App.CONFLICT,\n cause,\n }: {\n message: string;\n code?: AppErrorCode | string;\n cause?: unknown;\n }) {\n super({ message, code, cause });\n }\n\n /**\n * Creates a ConflictError from a caught error.\n *\n * @param cause - The original caught error\n * @returns A new ConflictError instance with the cause attached\n */\n static override fromError(cause: unknown): ConflictError {\n return new ConflictError({\n message: cause instanceof Error ? cause.message : 'Conflict error',\n cause,\n });\n }\n}\n","import { ErrorCodes, type AppErrorCode } from '../../global/exceptions/error-codes.const';\nimport { UseCaseError } from './use-case.error';\n\n/**\n * Error thrown when a request is valid but cannot be processed.\n *\n * The request is syntactically correct and passes validation, but\n * business logic prevents the operation from being completed.\n *\n * **When to throw:**\n * - Business rule prevents operation (e.g., insufficient balance)\n * - Invalid state transition (e.g., canceling a completed order)\n * - Preconditions not met for the operation\n *\n * @example\n * ```typescript\n * if (account.balance < amount) {\n * throw new UnprocessableError({\n * message: 'Insufficient balance for withdrawal',\n * code: 'INSUFFICIENT_BALANCE',\n * });\n * }\n * ```\n *\n * @extends UseCaseError\n */\nexport class UnprocessableError extends UseCaseError {\n /**\n * Creates a new UnprocessableError instance.\n *\n * @param options - Error configuration\n * @param options.message - Description of why the request cannot be processed\n * @param options.code - Machine-readable error code (default: 'UNPROCESSABLE')\n * @param options.cause - Optional underlying error\n */\n constructor({\n message,\n code = ErrorCodes.App.UNPROCESSABLE,\n cause,\n }: {\n message: string;\n code?: AppErrorCode | string;\n cause?: unknown;\n }) {\n super({ message, code, cause });\n }\n\n /**\n * Creates an UnprocessableError from a caught error.\n *\n * @param cause - The original caught error\n * @returns A new UnprocessableError instance with the cause attached\n */\n static override fromError(cause: unknown): UnprocessableError {\n return new UnprocessableError({\n message: cause instanceof Error ? cause.message : 'Unprocessable request',\n cause,\n });\n }\n}\n","import { ErrorCodes, type AppErrorCode } from '../../global/exceptions/error-codes.const';\nimport { UseCaseError } from './use-case.error';\n\n/**\n * Error thrown when authorization for an operation is denied.\n *\n * Indicates that the user is authenticated but lacks permission to perform\n * the requested operation. Commonly thrown from the `authorize()` method\n * of use cases when access checks fail.\n *\n * **When to throw:**\n * - User doesn't own the resource they're trying to modify\n * - User lacks required role or permission\n * - Organization/tenant isolation violation\n * - Business rule restricts the operation for this user\n *\n * **Difference from AccessDeniedError:**\n * - `ForbiddenError` is an application layer error (use case authorization)\n * - `AccessDeniedError` is a presentation layer error (controller/guard level)\n *\n * @example Use case authorization\n * ```typescript\n * class UpdateActivityUseCase extends BaseInboundAdapter<Input, Output, AuthContext> {\n * protected async authorize(input: Input): Promise<AuthContext> {\n * const activity = await this.activityRepo.findById(input.activityId);\n * if (!activity) {\n * throw new NotFoundError({ message: 'Activity not found' });\n * }\n * if (activity.organizationId !== input.organizationId) {\n * throw new ForbiddenError({\n * message: 'Not authorized to modify this activity',\n * code: 'ACTIVITY_ACCESS_DENIED',\n * });\n * }\n * return { activity };\n * }\n * }\n * ```\n *\n * @example Role-based authorization\n * ```typescript\n * if (!user.roles.includes('admin')) {\n * throw new ForbiddenError({\n * message: 'Admin role required for this operation',\n * });\n * }\n * ```\n *\n * @extends UseCaseError\n */\nexport class ForbiddenError extends UseCaseError {\n /**\n * Creates a new ForbiddenError instance.\n *\n * @param options - Error configuration\n * @param options.message - Description of why authorization was denied\n * @param options.code - Machine-readable error code (default: 'FORBIDDEN')\n * @param options.cause - Optional underlying error\n */\n constructor({\n message,\n code = ErrorCodes.App.FORBIDDEN,\n cause,\n }: {\n message: string;\n code?: AppErrorCode | string;\n cause?: unknown;\n }) {\n super({ message, code, cause });\n }\n\n /**\n * Creates a ForbiddenError from a caught error.\n *\n * @param cause - The original caught error\n * @returns A new ForbiddenError instance with the cause attached\n */\n static override fromError(cause: unknown): ForbiddenError {\n return new ForbiddenError({\n message: cause instanceof Error ? cause.message : 'Operation forbidden',\n cause,\n });\n }\n}\n","import { CodedError } from '../../global/exceptions/coded-error.error';\nimport { ErrorCodes, type PresentationErrorCode } from '../../global/exceptions/error-codes.const';\n\n/**\n * Error thrown when access to a resource is denied.\n *\n * Indicates that the requester does not have permission to perform\n * the requested operation. Thrown by {@link GuardedController} when\n * an access guard returns `isAllowed: false`.\n *\n * **When thrown:**\n * - Access guard denies the request\n * - User lacks required permissions\n * - Resource ownership check fails\n *\n * @example GuardedController usage\n * ```typescript\n * const controller = GuardedController.create({\n * accessGuard: (req) => ({\n * isAllowed: req.user?.role === 'admin',\n * reason: 'Admin access required',\n * }),\n * // ... other config\n * });\n * ```\n *\n * @example Manual usage\n * ```typescript\n * if (!user.canAccess(resource)) {\n * throw new AccessDeniedError({\n * message: 'You do not have access to this resource',\n * code: 'RESOURCE_ACCESS_DENIED',\n * });\n * }\n * ```\n *\n * @extends CodedError\n */\nexport class AccessDeniedError extends CodedError {\n /**\n * Creates a new AccessDeniedError instance.\n *\n * @param options - Error configuration\n * @param options.message - Description of why access was denied\n * @param options.code - Machine-readable error code (default: 'ACCESS_DENIED')\n * @param options.cause - Optional underlying error\n */\n constructor({\n message,\n code = ErrorCodes.Presentation.ACCESS_DENIED,\n cause,\n }: {\n message: string;\n code?: PresentationErrorCode | string;\n cause?: unknown;\n }) {\n super({ message, code, cause });\n }\n\n /**\n * Creates an AccessDeniedError from a caught error.\n *\n * @param cause - The original caught error\n * @returns A new AccessDeniedError instance with the cause attached\n */\n static override fromError(cause: unknown): AccessDeniedError {\n return new AccessDeniedError({\n message: cause instanceof Error ? cause.message : 'Access denied',\n cause,\n });\n }\n}\n"],"mappings":";;;;;;;;;AA6BO,IAAM,cAAN,MAAM,qBAAoB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS1C,YAAY;AAAA,IACV;AAAA,IACA,OAAO,WAAW,OAAO;AAAA,IACzB;AAAA,EACF,GAIG;AACD,UAAM,EAAE,SAAS,MAAM,MAAM,CAAC;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAgB,UAAU,OAA6B;AACrD,WAAO,IAAI,aAAY;AAAA,MACrB,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AC5BO,IAAM,aAAN,MAAM,oBAAmB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASzC,YAAY;AAAA,IACV;AAAA,IACA,OAAO,WAAW,MAAM;AAAA,IACxB;AAAA,EACF,GAIG;AACD,UAAM,EAAE,SAAS,MAAM,MAAM,CAAC;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAgB,UAAU,OAA4B;AACpD,WAAO,IAAI,YAAW;AAAA,MACpB,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ACxCO,IAAM,gBAAN,MAAM,uBAAsB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS9C,YAAY;AAAA,IACV;AAAA,IACA,OAAO,WAAW,IAAI;AAAA,IACtB;AAAA,EACF,GAIG;AACD,UAAM,EAAE,SAAS,MAAM,MAAM,CAAC;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAgB,UAAU,OAA+B;AACvD,WAAO,IAAI,eAAc;AAAA,MACvB,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ACjCO,IAAM,gBAAN,MAAM,uBAAsB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS9C,YAAY;AAAA,IACV;AAAA,IACA,OAAO,WAAW,IAAI;AAAA,IACtB;AAAA,EACF,GAIG;AACD,UAAM,EAAE,SAAS,MAAM,MAAM,CAAC;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAgB,UAAU,OAA+B;AACvD,WAAO,IAAI,eAAc;AAAA,MACvB,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AClCO,IAAM,qBAAN,MAAM,4BAA2B,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASnD,YAAY;AAAA,IACV;AAAA,IACA,OAAO,WAAW,IAAI;AAAA,IACtB;AAAA,EACF,GAIG;AACD,UAAM,EAAE,SAAS,MAAM,MAAM,CAAC;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAgB,UAAU,OAAoC;AAC5D,WAAO,IAAI,oBAAmB;AAAA,MAC5B,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ACTO,IAAM,iBAAN,MAAM,wBAAuB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS/C,YAAY;AAAA,IACV;AAAA,IACA,OAAO,WAAW,IAAI;AAAA,IACtB;AAAA,EACF,GAIG;AACD,UAAM,EAAE,SAAS,MAAM,MAAM,CAAC;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAgB,UAAU,OAAgC;AACxD,WAAO,IAAI,gBAAe;AAAA,MACxB,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AC7CO,IAAM,oBAAN,MAAM,2BAA0B,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAShD,YAAY;AAAA,IACV;AAAA,IACA,OAAO,WAAW,aAAa;AAAA,IAC/B;AAAA,EACF,GAIG;AACD,UAAM,EAAE,SAAS,MAAM,MAAM,CAAC;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAgB,UAAU,OAAmC;AAC3D,WAAO,IAAI,mBAAkB;AAAA,MAC3B,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CodedError,
|
|
3
3
|
ErrorCodes
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-A4JUAZK4.js";
|
|
5
5
|
|
|
6
|
-
// src/
|
|
6
|
+
// src/global/exceptions/object-validation.error.ts
|
|
7
7
|
var ObjectValidationError = class _ObjectValidationError extends CodedError {
|
|
8
8
|
/**
|
|
9
9
|
* Array of field-level validation errors.
|
|
@@ -49,4 +49,4 @@ var ObjectValidationError = class _ObjectValidationError extends CodedError {
|
|
|
49
49
|
export {
|
|
50
50
|
ObjectValidationError
|
|
51
51
|
};
|
|
52
|
-
//# sourceMappingURL=chunk-
|
|
52
|
+
//# sourceMappingURL=chunk-3BY5RBF2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/global/exceptions/object-validation.error.ts"],"sourcesContent":["import { CodedError } from './coded-error.error';\nimport { ErrorCodes, type GlobalErrorCode } from './error-codes.const';\nimport type { ValidationError } from '../interfaces/types/validation-error.type';\n\n/**\n * Error thrown when object validation fails.\n *\n * Contains structured validation errors with field paths and messages,\n * making it easy to report specific validation failures to clients.\n * Thrown by all validator implementations (Zod, ArkType, TypeBox, Valibot).\n *\n * **Flow:**\n * 1. Validator throws `ObjectValidationError` with field-level errors\n * 2. Controller catches and converts to {@link InvalidRequestError}\n * 3. HTTP layer maps to 400 Bad Request with error details\n *\n * @example\n * ```typescript\n * try {\n * const dto = CreateUserDto.create(invalidData);\n * } catch (error) {\n * if (error instanceof ObjectValidationError) {\n * console.log(error.validationErrors);\n * // [\n * // { field: 'email', message: 'Invalid email format' },\n * // { field: 'age', message: 'Must be a positive number' }\n * // ]\n * }\n * }\n * ```\n */\nexport class ObjectValidationError extends CodedError {\n /**\n * Array of field-level validation errors.\n *\n * Each entry contains:\n * - `field`: Dot-notation path to the invalid field (e.g., 'user.email')\n * - `message`: Human-readable validation failure message\n */\n validationErrors: ValidationError[];\n\n /**\n * Creates a new ObjectValidationError instance.\n *\n * @param options - Error configuration\n * @param options.message - Human-readable summary message\n * @param options.code - Machine-readable error code (default: 'OBJECT_VALIDATION_ERROR')\n * @param options.cause - Optional underlying error from validation library\n * @param options.validationErrors - Array of field-level validation errors\n */\n constructor({\n message,\n code = ErrorCodes.Global.OBJECT_VALIDATION_ERROR,\n cause,\n validationErrors,\n }: {\n message: string;\n code?: GlobalErrorCode | string;\n cause?: unknown;\n validationErrors: ValidationError[];\n }) {\n super({ message, code, cause });\n this.validationErrors = validationErrors;\n }\n\n /**\n * Creates an ObjectValidationError from a caught error.\n *\n * @param cause - The original caught error\n * @returns A new ObjectValidationError instance with the cause attached\n */\n static override fromError(cause: unknown): ObjectValidationError {\n return new ObjectValidationError({\n message: cause instanceof Error ? cause.message : 'Validation failed',\n cause,\n validationErrors: [],\n });\n }\n}\n"],"mappings":";;;;;;AA+BO,IAAM,wBAAN,MAAM,+BAA8B,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,YAAY;AAAA,IACV;AAAA,IACA,OAAO,WAAW,OAAO;AAAA,IACzB;AAAA,IACA;AAAA,EACF,GAKG;AACD,UAAM,EAAE,SAAS,MAAM,MAAM,CAAC;AAC9B,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAgB,UAAU,OAAuC;AAC/D,WAAO,IAAI,uBAAsB;AAAA,MAC/B,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,MACA,kBAAkB,CAAC;AAAA,IACrB,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/global/utils/field-changed.util.ts
|
|
2
|
+
function fieldChanged({
|
|
3
|
+
value,
|
|
4
|
+
newValue,
|
|
5
|
+
partialUpdate = true
|
|
6
|
+
}) {
|
|
7
|
+
if (partialUpdate && newValue === void 0) return false;
|
|
8
|
+
return value !== newValue;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
fieldChanged
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=chunk-3QKQCJSP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/global/utils/field-changed.util.ts"],"sourcesContent":["/**\n * Checks if a field has changed by comparing the current value with a new value.\n *\n * Useful for detecting changes in update operations, supporting both partial\n * and full update semantics.\n *\n * **Modes:**\n * - **Partial update** (default): `undefined` means \"keep existing value\" (no change)\n * - **Full update**: `undefined` means \"set to undefined\" (is a change if value !== undefined)\n *\n * **Limitation:** Uses strict equality (`!==`) for comparison, which only works\n * correctly for primitives and reference equality. For objects or arrays, this\n * compares references, not content. Two objects with identical content but\n * different references will be considered \"changed\".\n *\n * For complex objects, either:\n * - Compare by a unique identifier (e.g., `value.id !== newValue?.id`)\n * - Use value objects with `.equals()` methods\n * - Implement deep equality checking in your update logic\n *\n * @typeParam T - The type of the field being compared\n * @param options - Comparison options\n * @param options.value - The current field value\n * @param options.newValue - The incoming value (may be undefined)\n * @param options.partialUpdate - Whether to use partial update semantics (default: true)\n * @returns `true` if the field has changed, `false` otherwise\n *\n * @example Partial update (PATCH semantics)\n * ```typescript\n * // undefined means \"don't change\"\n * fieldChanged({ value: 'John', newValue: undefined }); // false\n * fieldChanged({ value: 'John', newValue: 'Jane' }); // true\n * fieldChanged({ value: 'John', newValue: 'John' }); // false\n * ```\n *\n * @example Full update (PUT semantics)\n * ```typescript\n * // undefined means \"set to undefined\"\n * fieldChanged({ value: 'John', newValue: undefined, partialUpdate: false }); // true\n * ```\n *\n * @example Object comparison (reference-based)\n * ```typescript\n * const obj1 = { name: 'John' };\n * const obj2 = { name: 'John' };\n * fieldChanged({ value: obj1, newValue: obj2 }); // true (different references)\n * fieldChanged({ value: obj1, newValue: obj1 }); // false (same reference)\n * ```\n */\nexport function fieldChanged<T>({\n value,\n newValue,\n partialUpdate = true,\n}: {\n value: T;\n newValue: T | undefined;\n partialUpdate?: boolean;\n}): boolean {\n if (partialUpdate && newValue === undefined) return false;\n // For full updates, undefined means \"set to undefined\" which is a change if value !== undefined\n return value !== newValue;\n}\n"],"mappings":";AAiDO,SAAS,aAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,gBAAgB;AAClB,GAIY;AACV,MAAI,iBAAiB,aAAa,OAAW,QAAO;AAEpD,SAAO,UAAU;AACnB;","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/presentation/http/schema/types/validation.type.ts
|
|
2
2
|
function isValidationSuccess(result) {
|
|
3
3
|
return result.success === true;
|
|
4
4
|
}
|
|
@@ -6,7 +6,7 @@ function isValidationFailure(result) {
|
|
|
6
6
|
return result.success === false;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
// src/
|
|
9
|
+
// src/presentation/http/schema/types/schema-adapter.type.ts
|
|
10
10
|
function isSchemaAdapter(value) {
|
|
11
11
|
return typeof value === "object" && value !== null && "validate" in value && typeof value.validate === "function" && "toJsonSchema" in value && typeof value.toJsonSchema === "function";
|
|
12
12
|
}
|
|
@@ -37,4 +37,4 @@ export {
|
|
|
37
37
|
createPassthroughAdapter,
|
|
38
38
|
createRejectingAdapter
|
|
39
39
|
};
|
|
40
|
-
//# sourceMappingURL=chunk-
|
|
40
|
+
//# sourceMappingURL=chunk-42JMR62O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presentation/http/schema/types/validation.type.ts","../src/presentation/http/schema/types/schema-adapter.type.ts"],"sourcesContent":["/**\n * @fileoverview Validation result types for schema adapters.\n *\n * These types represent the outcome of schema validation operations,\n * providing a consistent interface across all validation libraries.\n *\n * @module unified/schema/types/validation\n */\n\n/**\n * Represents a single validation error with path and message.\n */\nexport interface ValidationIssue {\n /**\n * Path to the invalid value in the data structure.\n * Empty array indicates the root value is invalid.\n *\n * @example ['body', 'user', 'email'] - nested object path\n * @example ['items', '0', 'name'] - array element path\n * @example [] - root level error\n */\n readonly path: readonly string[];\n\n /**\n * Human-readable error message describing the validation failure.\n */\n readonly message: string;\n\n /**\n * Optional error code for programmatic handling.\n * Codes are library-specific but follow common patterns:\n * - 'required' - missing required field\n * - 'type' - wrong type\n * - 'format' - invalid format (email, uuid, etc.)\n * - 'minimum' / 'maximum' - number out of range\n * - 'minLength' / 'maxLength' - string length violation\n * - 'pattern' - regex pattern mismatch\n * - 'enum' - value not in allowed list\n */\n readonly code?: string;\n\n /**\n * Expected type or value (when available).\n */\n readonly expected?: string;\n\n /**\n * Received type or value (when available).\n */\n readonly received?: string;\n}\n\n/**\n * Successful validation result containing the validated and typed data.\n */\nexport interface ValidationSuccess<T> {\n readonly success: true;\n readonly data: T;\n}\n\n/**\n * Failed validation result containing all validation issues.\n */\nexport interface ValidationFailure {\n readonly success: false;\n readonly issues: readonly ValidationIssue[];\n}\n\n/**\n * Result of a schema validation operation.\n * Discriminated union that allows type-safe handling of success/failure cases.\n *\n * @example\n * ```typescript\n * const result = schema.validate(data);\n * if (result.success) {\n * // result.data is typed as T\n * console.log(result.data);\n * } else {\n * // result.issues contains validation errors\n * console.log(result.issues);\n * }\n * ```\n */\nexport type ValidationResult<T> = ValidationSuccess<T> | ValidationFailure;\n\n/**\n * Type guard to check if a validation result is successful.\n */\nexport function isValidationSuccess<T>(\n result: ValidationResult<T>,\n): result is ValidationSuccess<T> {\n return result.success === true;\n}\n\n/**\n * Type guard to check if a validation result is a failure.\n */\nexport function isValidationFailure<T>(result: ValidationResult<T>): result is ValidationFailure {\n return result.success === false;\n}\n","/**\n * @fileoverview Schema adapter interface for validation library abstraction.\n *\n * The SchemaAdapter interface provides a unified API for working with\n * different validation libraries (Zod, TypeBox, Valibot, ArkType).\n * Each adapter wraps a library-specific schema and provides:\n * - Runtime validation\n * - JSON Schema conversion for OpenAPI\n * - TypeScript type inference via phantom types\n *\n * @module unified/schema/types/schema-adapter\n */\n\nimport type { JsonSchema } from './json-schema.type';\nimport type { ValidationResult } from './validation.type';\n\n/**\n * Schema adapter interface that abstracts validation library specifics.\n *\n * This interface is the core abstraction that enables the unified route\n * system to work with any validation library. Implementations wrap a\n * library-specific schema (Zod, TypeBox, etc.) and provide a consistent API.\n *\n * @typeParam TOutput - The TypeScript type of successfully validated data.\n * For schemas with transforms, this is the output type.\n * @typeParam TInput - The TypeScript type expected as input (before transforms).\n * Defaults to TOutput for schemas without transforms.\n *\n * @example Creating an adapter (using Zod)\n * ```typescript\n * import { z } from 'zod';\n * import { zodSchema } from '@cosmneo/onion-lasagna/http/schema/zod';\n *\n * const userSchema = zodSchema(z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * }));\n *\n * // Validate data\n * const result = userSchema.validate(input);\n * if (result.success) {\n * // result.data is typed as { name: string; email: string }\n * }\n *\n * // Get JSON Schema for OpenAPI\n * const jsonSchema = userSchema.toJsonSchema();\n * ```\n */\nexport interface SchemaAdapter<TOutput = unknown, TInput = TOutput> {\n /**\n * Validates unknown data against the schema.\n *\n * This method performs full validation including:\n * - Type checking\n * - Constraint validation (min/max, patterns, etc.)\n * - Nested object/array validation\n * - Custom validations defined in the schema\n *\n * For schemas with transforms, the returned data will be the transformed value.\n *\n * @param data - Unknown data to validate\n * @returns Validation result with typed data on success or issues on failure\n *\n * @example\n * ```typescript\n * const result = schema.validate({ name: 'John', age: 25 });\n * if (result.success) {\n * console.log(result.data.name); // TypeScript knows this is a string\n * } else {\n * console.log(result.issues); // Array of validation errors\n * }\n * ```\n */\n validate(data: unknown): ValidationResult<TOutput>;\n\n /**\n * Converts the schema to JSON Schema format for OpenAPI generation.\n *\n * The returned schema should be compatible with JSON Schema Draft 7\n * and OpenAPI 3.0/3.1 specifications. Complex schemas with transforms\n * or refinements may produce simplified JSON Schema representations.\n *\n * @param options - Optional configuration for schema generation\n * @returns JSON Schema representation of this schema\n *\n * @example\n * ```typescript\n * const jsonSchema = schema.toJsonSchema();\n * // {\n * // type: 'object',\n * // properties: {\n * // name: { type: 'string', minLength: 1 },\n * // email: { type: 'string', format: 'email' }\n * // },\n * // required: ['name', 'email']\n * // }\n * ```\n */\n toJsonSchema(options?: JsonSchemaOptions): JsonSchema;\n\n /**\n * Phantom type marker for output type inference.\n * This property is never accessed at runtime - it exists only for TypeScript.\n *\n * @internal\n */\n readonly _output: TOutput;\n\n /**\n * Phantom type marker for input type inference.\n * This property is never accessed at runtime - it exists only for TypeScript.\n * Useful for schemas with transforms where input and output types differ.\n *\n * @internal\n */\n readonly _input: TInput;\n\n /**\n * Optional: Reference to the underlying schema for advanced use cases.\n * The type depends on the validation library being used.\n *\n * @internal\n */\n readonly _schema?: unknown;\n}\n\n/**\n * Options for JSON Schema generation.\n */\nexport interface JsonSchemaOptions {\n /**\n * Strategy for handling $ref references.\n * - 'none': Inline all schemas, no $refs\n * - 'root': Create $refs only at the root level\n * - 'all': Use $refs wherever possible (most compact)\n *\n * @default 'none'\n */\n readonly refStrategy?: 'none' | 'root' | 'all';\n\n /**\n * Base path for $ref references.\n * @default '#/$defs/'\n */\n readonly basePath?: string;\n\n /**\n * Custom definitions to include in the schema.\n */\n readonly definitions?: Record<string, JsonSchema>;\n\n /**\n * Whether to include schema metadata (title, description, etc.).\n * @default true\n */\n readonly includeMetadata?: boolean;\n}\n\n/**\n * Infers the output type from a SchemaAdapter.\n *\n * @example\n * ```typescript\n * const userSchema = zodSchema(z.object({ name: z.string() }));\n * type User = InferOutput<typeof userSchema>;\n * // { name: string }\n * ```\n */\nexport type InferOutput<T extends SchemaAdapter<unknown, unknown>> =\n T extends SchemaAdapter<infer O, unknown> ? O : never;\n\n/**\n * Infers the input type from a SchemaAdapter.\n * For schemas without transforms, this is the same as InferOutput.\n *\n * @example\n * ```typescript\n * const dateSchema = zodSchema(z.string().transform(s => new Date(s)));\n * type DateInput = InferInput<typeof dateSchema>;\n * // string (the input before transform)\n * type DateOutput = InferOutput<typeof dateSchema>;\n * // Date (the output after transform)\n * ```\n */\nexport type InferInput<T extends SchemaAdapter<unknown, unknown>> =\n T extends SchemaAdapter<unknown, infer I> ? I : never;\n\n/**\n * Type guard to check if a value is a SchemaAdapter.\n */\nexport function isSchemaAdapter(value: unknown): value is SchemaAdapter {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'validate' in value &&\n typeof (value as SchemaAdapter).validate === 'function' &&\n 'toJsonSchema' in value &&\n typeof (value as SchemaAdapter).toJsonSchema === 'function'\n );\n}\n\n/**\n * Creates a schema adapter that always passes validation.\n * Useful for routes that accept any input.\n *\n * @example\n * ```typescript\n * const anySchema = createPassthroughAdapter<unknown>();\n * anySchema.validate(anything); // always succeeds\n * ```\n */\nexport function createPassthroughAdapter<T = unknown>(): SchemaAdapter<T, T> {\n return {\n validate: (data) => ({ success: true, data: data as T }),\n toJsonSchema: () => ({}),\n _output: undefined as T,\n _input: undefined as T,\n };\n}\n\n/**\n * Creates a schema adapter that always fails validation with a message.\n * Useful for deprecating routes or marking them as not accepting input.\n *\n * @example\n * ```typescript\n * const noBodySchema = createRejectingAdapter('This endpoint does not accept a request body');\n * ```\n */\nexport function createRejectingAdapter<T = never>(message: string): SchemaAdapter<T, T> {\n return {\n validate: () => ({\n success: false,\n issues: [{ path: [], message }],\n }),\n toJsonSchema: () => ({ not: {} }),\n _output: undefined as T,\n _input: undefined as T,\n };\n}\n"],"mappings":";AAyFO,SAAS,oBACd,QACgC;AAChC,SAAO,OAAO,YAAY;AAC5B;AAKO,SAAS,oBAAuB,QAA0D;AAC/F,SAAO,OAAO,YAAY;AAC5B;;;AC0FO,SAAS,gBAAgB,OAAwC;AACtE,SACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACd,OAAQ,MAAwB,aAAa,cAC7C,kBAAkB,SAClB,OAAQ,MAAwB,iBAAiB;AAErD;AAYO,SAAS,2BAA6D;AAC3E,SAAO;AAAA,IACL,UAAU,CAAC,UAAU,EAAE,SAAS,MAAM,KAAgB;AAAA,IACtD,cAAc,OAAO,CAAC;AAAA,IACtB,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACF;AAWO,SAAS,uBAAkC,SAAsC;AACtF,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,MACf,SAAS;AAAA,MACT,QAAQ,CAAC,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC;AAAA,IAChC;AAAA,IACA,cAAc,OAAO,EAAE,KAAK,CAAC,EAAE;AAAA,IAC/B,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACF;","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/global/exceptions/coded-error.error.ts
|
|
2
2
|
var CodedError = class extends Error {
|
|
3
3
|
/** Machine-readable error code for programmatic handling. */
|
|
4
4
|
code;
|
|
@@ -54,7 +54,7 @@ var CodedError = class extends Error {
|
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
// src/
|
|
57
|
+
// src/global/exceptions/error-codes.const.ts
|
|
58
58
|
var ErrorCodes = {
|
|
59
59
|
/**
|
|
60
60
|
* Domain layer error codes.
|
|
@@ -128,4 +128,4 @@ export {
|
|
|
128
128
|
CodedError,
|
|
129
129
|
ErrorCodes
|
|
130
130
|
};
|
|
131
|
-
//# sourceMappingURL=chunk-
|
|
131
|
+
//# sourceMappingURL=chunk-A4JUAZK4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/global/exceptions/coded-error.error.ts","../src/global/exceptions/error-codes.const.ts"],"sourcesContent":["import type { ErrorCode } from './error-codes.const';\n\n/**\n * Base error class for all application errors with a machine-readable code.\n *\n * Abstract class that extends the native `Error` with:\n * - A `code` property for programmatic error handling\n * - Optional `cause` for error chaining (ES2022 compatible)\n * - A `fromError` static factory pattern for error transformation\n *\n * **Why abstract:** Prevents non-declarative error usage. All errors must\n * be explicitly defined as subclasses to ensure consistent error taxonomy.\n *\n * @example Subclass implementation\n * ```typescript\n * class DbError extends InfraError {\n * static override fromError(cause: unknown): DbError {\n * return new DbError({\n * message: cause instanceof Error ? cause.message : 'Database error',\n * cause,\n * });\n * }\n * }\n * ```\n *\n * @example Usage with wrapErrorAsync\n * ```typescript\n * await wrapErrorAsync(\n * () => this.db.query(...),\n * DbError.fromError,\n * );\n * ```\n */\nexport abstract class CodedError extends Error {\n /** Machine-readable error code for programmatic handling. */\n public readonly code: ErrorCode | string;\n\n /**\n * Creates a new CodedError instance.\n *\n * @param options - Error configuration\n * @param options.message - Human-readable error message\n * @param options.code - Machine-readable error code from ErrorCodes registry or custom string\n * @param options.cause - Optional underlying error that caused this error\n */\n constructor({\n message,\n code,\n cause,\n }: {\n message: string;\n code: ErrorCode | string;\n cause?: unknown;\n }) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n if (cause !== undefined) {\n Object.defineProperty(this, 'cause', {\n value: cause,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n }\n }\n\n /**\n * Factory method to create a typed error from a caught error.\n *\n * Subclasses should override this to provide proper error transformation.\n * Designed for use with {@link wrapErrorAsync} and {@link wrapError}.\n *\n * @param _cause - The original caught error\n * @returns A new CodedError instance\n * @throws {Error} If not overridden by subclass\n *\n * @example\n * ```typescript\n * class NotFoundError extends UseCaseError {\n * static override fromError(cause: unknown): NotFoundError {\n * return new NotFoundError({\n * message: 'Resource not found',\n * cause,\n * });\n * }\n * }\n * ```\n */\n static fromError(_cause: unknown): CodedError {\n throw new Error(`${this.name}.fromError() must be implemented by subclass`);\n }\n}\n","/**\n * Centralized registry of all error codes used across the application.\n *\n * Error codes are grouped by architectural layer to maintain clear boundaries\n * and make it easy to identify where an error originated.\n *\n * @example Using error codes in custom errors\n * ```typescript\n * import { ErrorCodes } from '@cosmneo/onion-lasagna/global';\n *\n * throw new NotFoundError({\n * message: 'User not found',\n * code: ErrorCodes.App.NOT_FOUND,\n * });\n * ```\n *\n * @example Checking error codes programmatically\n * ```typescript\n * if (error.code === ErrorCodes.App.NOT_FOUND) {\n * // Handle not found case\n * }\n * ```\n */\nexport const ErrorCodes = {\n /**\n * Domain layer error codes.\n * Used for business rule violations and invariant failures.\n */\n Domain: {\n /** Generic domain error */\n DOMAIN_ERROR: 'DOMAIN_ERROR',\n /** Business invariant was violated */\n INVARIANT_VIOLATION: 'INVARIANT_VIOLATION',\n /** Aggregate was partially loaded (missing required relations) */\n PARTIAL_LOAD: 'PARTIAL_LOAD',\n },\n\n /**\n * Application layer (use case) error codes.\n * Used for orchestration failures and business operation errors.\n */\n App: {\n /** Generic use case error */\n USE_CASE_ERROR: 'USE_CASE_ERROR',\n /** Requested resource was not found */\n NOT_FOUND: 'NOT_FOUND',\n /** Resource state conflict (e.g., duplicate, already exists) */\n CONFLICT: 'CONFLICT',\n /** Request is valid but cannot be processed due to business rules */\n UNPROCESSABLE: 'UNPROCESSABLE',\n /** Authorization denied - user lacks permission for this operation */\n FORBIDDEN: 'FORBIDDEN',\n /** Authentication required or invalid - user is not authenticated */\n UNAUTHORIZED: 'UNAUTHORIZED',\n },\n\n /**\n * Infrastructure layer error codes.\n * Used for data access, external services, and I/O failures.\n */\n Infra: {\n /** Generic infrastructure error */\n INFRA_ERROR: 'INFRA_ERROR',\n /** Database operation failed */\n DB_ERROR: 'DB_ERROR',\n /** Network connectivity or communication error */\n NETWORK_ERROR: 'NETWORK_ERROR',\n /** Operation timed out */\n TIMEOUT_ERROR: 'TIMEOUT_ERROR',\n /** External/third-party service error */\n EXTERNAL_SERVICE_ERROR: 'EXTERNAL_SERVICE_ERROR',\n },\n\n /**\n * Presentation layer error codes.\n * Used for controller, request handling, and authorization errors.\n */\n Presentation: {\n /** Generic controller error */\n CONTROLLER_ERROR: 'CONTROLLER_ERROR',\n /** Request denied due to authorization failure */\n ACCESS_DENIED: 'ACCESS_DENIED',\n /** Request validation failed (malformed input) */\n INVALID_REQUEST: 'INVALID_REQUEST',\n },\n\n /**\n * Global/cross-cutting error codes.\n * Used for validation and other cross-layer concerns.\n */\n Global: {\n /** Object/schema validation failed */\n OBJECT_VALIDATION_ERROR: 'OBJECT_VALIDATION_ERROR',\n },\n} as const;\n\n/**\n * Type representing all possible domain error codes.\n */\nexport type DomainErrorCode = (typeof ErrorCodes.Domain)[keyof typeof ErrorCodes.Domain];\n\n/**\n * Type representing all possible application error codes.\n */\nexport type AppErrorCode = (typeof ErrorCodes.App)[keyof typeof ErrorCodes.App];\n\n/**\n * Type representing all possible infrastructure error codes.\n */\nexport type InfraErrorCode = (typeof ErrorCodes.Infra)[keyof typeof ErrorCodes.Infra];\n\n/**\n * Type representing all possible presentation error codes.\n */\nexport type PresentationErrorCode =\n (typeof ErrorCodes.Presentation)[keyof typeof ErrorCodes.Presentation];\n\n/**\n * Type representing all possible global error codes.\n */\nexport type GlobalErrorCode = (typeof ErrorCodes.Global)[keyof typeof ErrorCodes.Global];\n\n/**\n * Union type of all error codes across all layers.\n *\n * Use this when you need to accept any valid error code.\n *\n * @example\n * ```typescript\n * function logError(code: ErrorCode, message: string) {\n * console.error(`[${code}] ${message}`);\n * }\n * ```\n */\nexport type ErrorCode =\n | DomainErrorCode\n | AppErrorCode\n | InfraErrorCode\n | PresentationErrorCode\n | GlobalErrorCode;\n"],"mappings":";AAiCO,IAAe,aAAf,cAAkC,MAAM;AAAA;AAAA,EAE7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUhB,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIG;AACD,UAAM,OAAO;AACb,SAAK,OAAO,KAAK,YAAY;AAC7B,SAAK,OAAO;AACZ,QAAI,UAAU,QAAW;AACvB,aAAO,eAAe,MAAM,SAAS;AAAA,QACnC,OAAO;AAAA,QACP,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,cAAc;AAAA,MAChB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,OAAO,UAAU,QAA6B;AAC5C,UAAM,IAAI,MAAM,GAAG,KAAK,IAAI,8CAA8C;AAAA,EAC5E;AACF;;;ACrEO,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxB,QAAQ;AAAA;AAAA,IAEN,cAAc;AAAA;AAAA,IAEd,qBAAqB;AAAA;AAAA,IAErB,cAAc;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK;AAAA;AAAA,IAEH,gBAAgB;AAAA;AAAA,IAEhB,WAAW;AAAA;AAAA,IAEX,UAAU;AAAA;AAAA,IAEV,eAAe;AAAA;AAAA,IAEf,WAAW;AAAA;AAAA,IAEX,cAAc;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO;AAAA;AAAA,IAEL,aAAa;AAAA;AAAA,IAEb,UAAU;AAAA;AAAA,IAEV,eAAe;AAAA;AAAA,IAEf,eAAe;AAAA;AAAA,IAEf,wBAAwB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAc;AAAA;AAAA,IAEZ,kBAAkB;AAAA;AAAA,IAElB,eAAe;AAAA;AAAA,IAEf,iBAAiB;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ;AAAA;AAAA,IAEN,yBAAyB;AAAA,EAC3B;AACF;","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isRouteDefinition,
|
|
3
3
|
isRouterDefinition
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DS7TE6KZ.js";
|
|
5
5
|
|
|
6
|
-
// src/
|
|
6
|
+
// src/presentation/http/route/define-route.ts
|
|
7
7
|
function defineRoute(input) {
|
|
8
8
|
const definition = {
|
|
9
9
|
method: input.method,
|
|
@@ -30,7 +30,7 @@ function defineRoute(input) {
|
|
|
30
30
|
return Object.freeze(definition);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
// src/
|
|
33
|
+
// src/presentation/http/route/define-router.ts
|
|
34
34
|
function defineRouter(routes, options) {
|
|
35
35
|
const definition = {
|
|
36
36
|
routes,
|
|
@@ -79,4 +79,4 @@ export {
|
|
|
79
79
|
defineRouter,
|
|
80
80
|
mergeRouters
|
|
81
81
|
};
|
|
82
|
-
//# sourceMappingURL=chunk-
|
|
82
|
+
//# sourceMappingURL=chunk-BZULBF4N.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presentation/http/route/define-route.ts","../src/presentation/http/route/define-router.ts"],"sourcesContent":["/**\n * @fileoverview Factory function for creating route definitions.\n *\n * The `defineRoute` function is the main entry point for defining routes\n * in the unified system. It provides type inference and validation\n * for route configurations.\n *\n * @module unified/route/define-route\n */\n\nimport type { SchemaAdapter, InferOutput } from '../schema/types';\nimport type { HttpMethod, ResponsesConfig, RouteDefinition, PathParams } from './types';\n\n// ============================================================================\n// Input Types\n// ============================================================================\n\n/**\n * Request body configuration input.\n */\ninterface BodyInput<TSchema extends SchemaAdapter> {\n readonly schema: TSchema;\n readonly contentType?: string;\n readonly required?: boolean;\n readonly description?: string;\n}\n\n/**\n * Query parameters configuration input.\n */\ninterface QueryInput<TSchema extends SchemaAdapter> {\n readonly schema: TSchema;\n readonly description?: string;\n}\n\n/**\n * Path parameters configuration input.\n */\ninterface ParamsInput<TSchema extends SchemaAdapter> {\n readonly schema: TSchema;\n readonly description?: string;\n}\n\n/**\n * Headers configuration input.\n */\ninterface HeadersInput<TSchema extends SchemaAdapter> {\n readonly schema: TSchema;\n readonly description?: string;\n}\n\n/**\n * Context configuration input.\n * Used to validate and type context data from middleware (e.g., JWT payload).\n */\ninterface ContextInput<TSchema extends SchemaAdapter> {\n readonly schema: TSchema;\n readonly description?: string;\n}\n\n/**\n * Response configuration input.\n */\ninterface ResponseInput<TSchema extends SchemaAdapter | undefined = undefined> {\n readonly description: string;\n readonly schema?: TSchema;\n readonly contentType?: string;\n}\n\n/**\n * Complete input for defineRoute.\n */\ninterface DefineRouteInput<\n TMethod extends HttpMethod,\n TPath extends string,\n TBody extends SchemaAdapter | undefined = undefined,\n TQuery extends SchemaAdapter | undefined = undefined,\n TParams extends SchemaAdapter | undefined = undefined,\n THeaders extends SchemaAdapter | undefined = undefined,\n TContext extends SchemaAdapter | undefined = undefined,\n TResponses extends Record<number, ResponseInput<SchemaAdapter | undefined>> = Record<\n number,\n never\n >,\n> {\n readonly method: TMethod;\n readonly path: TPath;\n readonly request?: {\n readonly body?: TBody extends SchemaAdapter ? BodyInput<TBody> : undefined;\n readonly query?: TQuery extends SchemaAdapter ? QueryInput<TQuery> : undefined;\n readonly params?: TParams extends SchemaAdapter ? ParamsInput<TParams> : undefined;\n readonly headers?: THeaders extends SchemaAdapter ? HeadersInput<THeaders> : undefined;\n readonly context?: TContext extends SchemaAdapter ? ContextInput<TContext> : undefined;\n };\n readonly responses: TResponses;\n readonly docs?: {\n readonly summary?: string;\n readonly description?: string;\n readonly tags?: readonly string[];\n readonly operationId?: string;\n readonly deprecated?: boolean;\n readonly security?: readonly Record<string, readonly string[]>[];\n readonly externalDocs?: {\n readonly url: string;\n readonly description?: string;\n };\n };\n}\n\n// ============================================================================\n// Output Types\n// ============================================================================\n\n/**\n * Converts response inputs to response config type.\n */\ntype ToResponsesConfig<T extends Record<number, ResponseInput<SchemaAdapter | undefined>>> = {\n [K in keyof T]: T[K] extends ResponseInput<infer TSchema>\n ? {\n description: string;\n schema: TSchema extends SchemaAdapter ? TSchema : undefined;\n contentType?: string;\n }\n : never;\n};\n\n// ============================================================================\n// Factory Function\n// ============================================================================\n\n/**\n * Creates a route definition from the provided configuration.\n *\n * This is the main entry point for defining routes in the unified system.\n * The resulting definition can be used for:\n * - Type-safe client generation\n * - Server-side route registration\n * - OpenAPI specification generation\n *\n * @param input - Route configuration\n * @returns A frozen RouteDefinition object with full type information\n *\n * @example Basic GET route\n * ```typescript\n * import { defineRoute } from '@cosmneo/onion-lasagna/http';\n * import { zodSchema, z } from '@cosmneo/onion-lasagna/http/schema/zod';\n *\n * const listUsers = defineRoute({\n * method: 'GET',\n * path: '/api/users',\n * request: {\n * query: {\n * schema: zodSchema(z.object({\n * page: z.coerce.number().optional().default(1),\n * limit: z.coerce.number().optional().default(20),\n * })),\n * },\n * },\n * responses: {\n * 200: {\n * description: 'List of users',\n * schema: zodSchema(z.object({\n * users: z.array(z.object({\n * id: z.string(),\n * name: z.string(),\n * })),\n * total: z.number(),\n * })),\n * },\n * },\n * docs: {\n * summary: 'List all users',\n * tags: ['Users'],\n * },\n * });\n * ```\n *\n * @example POST route with path parameters\n * ```typescript\n * const createTask = defineRoute({\n * method: 'POST',\n * path: '/api/projects/:projectId/tasks',\n * request: {\n * body: {\n * schema: zodSchema(z.object({\n * title: z.string().min(1).max(200),\n * description: z.string().optional(),\n * })),\n * },\n * },\n * responses: {\n * 201: {\n * description: 'Task created',\n * schema: zodSchema(z.object({\n * id: z.string().uuid(),\n * title: z.string(),\n * projectId: z.string(),\n * createdAt: z.string().datetime(),\n * })),\n * },\n * 400: {\n * description: 'Invalid request',\n * },\n * 404: {\n * description: 'Project not found',\n * },\n * },\n * docs: {\n * summary: 'Create a new task',\n * tags: ['Tasks'],\n * operationId: 'createTask',\n * },\n * });\n * ```\n */\nexport function defineRoute<\n TMethod extends HttpMethod,\n TPath extends string,\n TBody extends SchemaAdapter | undefined = undefined,\n TQuery extends SchemaAdapter | undefined = undefined,\n TParams extends SchemaAdapter | undefined = undefined,\n THeaders extends SchemaAdapter | undefined = undefined,\n TContext extends SchemaAdapter | undefined = undefined,\n TResponses extends Record<number, ResponseInput<SchemaAdapter | undefined>> = Record<\n number,\n never\n >,\n>(\n input: DefineRouteInput<TMethod, TPath, TBody, TQuery, TParams, THeaders, TContext, TResponses>,\n): RouteDefinition<\n TMethod,\n TPath,\n TBody extends SchemaAdapter ? InferOutput<TBody> : undefined,\n TQuery extends SchemaAdapter ? InferOutput<TQuery> : undefined,\n TParams extends SchemaAdapter ? InferOutput<TParams> : PathParams<TPath>,\n THeaders extends SchemaAdapter ? InferOutput<THeaders> : undefined,\n TContext extends SchemaAdapter ? InferOutput<TContext> : undefined,\n ToResponsesConfig<TResponses> & ResponsesConfig\n> {\n const definition = {\n method: input.method,\n path: input.path,\n request: {\n body: input.request?.body ?? undefined,\n query: input.request?.query ?? undefined,\n params: input.request?.params ?? undefined,\n headers: input.request?.headers ?? undefined,\n context: input.request?.context ?? undefined,\n },\n responses: input.responses as ToResponsesConfig<TResponses> & ResponsesConfig,\n docs: {\n summary: input.docs?.summary,\n description: input.docs?.description,\n tags: input.docs?.tags,\n operationId: input.docs?.operationId,\n deprecated: input.docs?.deprecated ?? false,\n security: input.docs?.security,\n externalDocs: input.docs?.externalDocs,\n },\n _types: undefined as unknown,\n } as RouteDefinition<\n TMethod,\n TPath,\n TBody extends SchemaAdapter ? InferOutput<TBody> : undefined,\n TQuery extends SchemaAdapter ? InferOutput<TQuery> : undefined,\n TParams extends SchemaAdapter ? InferOutput<TParams> : PathParams<TPath>,\n THeaders extends SchemaAdapter ? InferOutput<THeaders> : undefined,\n TContext extends SchemaAdapter ? InferOutput<TContext> : undefined,\n ToResponsesConfig<TResponses> & ResponsesConfig\n >;\n\n // Freeze the definition to prevent accidental mutation\n return Object.freeze(definition);\n}\n","/**\n * @fileoverview Factory function for creating router definitions.\n *\n * The `defineRouter` function groups routes into a hierarchical structure\n * that enables organized API clients, OpenAPI tag grouping, and structured\n * server route registration.\n *\n * @module unified/route/define-router\n */\n\nimport type { RouterConfig, RouterDefinition, DeepMergeTwo, DeepMergeAll } from './types';\nimport { isRouteDefinition, isRouterDefinition } from './types';\n\n/**\n * Options for router definition.\n */\nexport interface DefineRouterOptions {\n /**\n * Base path prefix for all routes in this router.\n * Will be prepended to all route paths.\n *\n * @example\n * ```typescript\n * defineRouter({ ... }, { basePath: '/api/v1' })\n * ```\n */\n readonly basePath?: string;\n\n /**\n * Default tags for all routes in this router.\n * Will be merged with route-specific tags.\n */\n readonly tags?: readonly string[];\n}\n\n/**\n * Creates a router definition from a configuration object.\n *\n * A router is a hierarchical grouping of routes that provides:\n * - Organized API structure with nested namespaces\n * - Typed client method generation\n * - OpenAPI tag grouping\n * - Server route registration\n *\n * @param routes - Object containing routes and nested routers\n * @param options - Optional router configuration\n * @returns A frozen RouterDefinition object\n *\n * @example Basic router\n * ```typescript\n * import { defineRouter } from '@cosmneo/onion-lasagna/http';\n *\n * const api = defineRouter({\n * users: {\n * list: listUsersRoute,\n * get: getUserRoute,\n * create: createUserRoute,\n * update: updateUserRoute,\n * delete: deleteUserRoute,\n * },\n * posts: {\n * list: listPostsRoute,\n * get: getPostRoute,\n * create: createPostRoute,\n * },\n * });\n *\n * // Client usage:\n * // client.users.list({ query: { page: 1 } })\n * // client.users.get({ params: { id: '123' } })\n * // client.posts.create({ body: { title: 'Hello' } })\n * ```\n *\n * @example Nested routers\n * ```typescript\n * const projectRouter = defineRouter({\n * list: listProjectsRoute,\n * get: getProjectRoute,\n * tasks: {\n * list: listTasksRoute,\n * create: createTaskRoute,\n * update: updateTaskRoute,\n * },\n * members: {\n * list: listMembersRoute,\n * add: addMemberRoute,\n * remove: removeMemberRoute,\n * },\n * });\n *\n * const api = defineRouter({\n * projects: projectRouter.routes,\n * users: userRouter.routes,\n * });\n *\n * // Client usage:\n * // client.projects.tasks.create({ params: { projectId: '123' }, body: { ... } })\n * ```\n *\n * @example With base path and tags\n * ```typescript\n * const adminApi = defineRouter({\n * users: {\n * list: listUsersRoute,\n * ban: banUserRoute,\n * },\n * analytics: {\n * dashboard: getDashboardRoute,\n * reports: getReportsRoute,\n * },\n * }, {\n * basePath: '/admin',\n * tags: ['Admin'],\n * });\n * ```\n */\nexport function defineRouter<T extends RouterConfig>(\n routes: T,\n options?: DefineRouterOptions,\n): RouterDefinition<T> {\n const definition: RouterDefinition<T> = {\n routes,\n basePath: options?.basePath,\n tags: options?.tags,\n _isRouter: true,\n };\n\n // Deep freeze the router definition\n return deepFreeze(definition);\n}\n\n/**\n * Deep freezes an object and all its nested objects.\n */\nfunction deepFreeze<T extends object>(obj: T): T {\n // Get all property names\n const propNames = Object.getOwnPropertyNames(obj) as (keyof T)[];\n\n // Freeze properties before freezing self\n for (const name of propNames) {\n const value = obj[name];\n if (value && typeof value === 'object' && !Object.isFrozen(value)) {\n deepFreeze(value);\n }\n }\n\n return Object.freeze(obj);\n}\n\n// ============================================================================\n// mergeRouters — variadic deep merge\n// ============================================================================\n\ntype RouterInput<T extends RouterConfig> = T | RouterDefinition<T>;\n\n/** Extracts the raw RouterConfig from either a plain config or a RouterDefinition. */\nfunction extractRoutes<T extends RouterConfig>(input: RouterInput<T>): T {\n return isRouterDefinition(input) ? input.routes : input;\n}\n\n/** Returns true if `value` is a plain sub-router object (not a RouteDefinition, not a RouterDefinition). */\nfunction isSubRouter(value: unknown): value is RouterConfig {\n return (\n typeof value === 'object' &&\n value !== null &&\n !isRouteDefinition(value) &&\n !isRouterDefinition(value)\n );\n}\n\n/** Recursively deep-merges two router configs. Sub-routers are merged; leaves are overwritten. */\nfunction deepMergeConfigs(a: RouterConfig, b: RouterConfig): RouterConfig {\n const result: Record<string, unknown> = { ...a };\n\n for (const key of Object.keys(b)) {\n const aVal = result[key];\n const bVal = b[key];\n\n if (isSubRouter(aVal) && isSubRouter(bVal)) {\n result[key] = deepMergeConfigs(aVal, bVal);\n } else {\n result[key] = bVal;\n }\n }\n\n return result as RouterConfig;\n}\n\n// Overloads for 2–8 routers (clean IDE experience)\n\n/**\n * Merges multiple routers into a single router with deep merge.\n *\n * Overlapping sub-router keys are recursively merged instead of overwritten.\n * Leaf routes (RouteDefinition) use last-one-wins semantics.\n *\n * @example\n * ```typescript\n * const api = mergeRouters(\n * userRouter,\n * organizationRouter,\n * feedbackRouter,\n * );\n * // If all three define `users`, their sub-routes are deep-merged.\n * ```\n */\nexport function mergeRouters<T1 extends RouterConfig, T2 extends RouterConfig>(\n r1: RouterInput<T1>,\n r2: RouterInput<T2>,\n): RouterDefinition<DeepMergeTwo<T1, T2>>;\nexport function mergeRouters<\n T1 extends RouterConfig,\n T2 extends RouterConfig,\n T3 extends RouterConfig,\n>(\n r1: RouterInput<T1>,\n r2: RouterInput<T2>,\n r3: RouterInput<T3>,\n): RouterDefinition<DeepMergeAll<[T1, T2, T3]>>;\nexport function mergeRouters<\n T1 extends RouterConfig,\n T2 extends RouterConfig,\n T3 extends RouterConfig,\n T4 extends RouterConfig,\n>(\n r1: RouterInput<T1>,\n r2: RouterInput<T2>,\n r3: RouterInput<T3>,\n r4: RouterInput<T4>,\n): RouterDefinition<DeepMergeAll<[T1, T2, T3, T4]>>;\nexport function mergeRouters<\n T1 extends RouterConfig,\n T2 extends RouterConfig,\n T3 extends RouterConfig,\n T4 extends RouterConfig,\n T5 extends RouterConfig,\n>(\n r1: RouterInput<T1>,\n r2: RouterInput<T2>,\n r3: RouterInput<T3>,\n r4: RouterInput<T4>,\n r5: RouterInput<T5>,\n): RouterDefinition<DeepMergeAll<[T1, T2, T3, T4, T5]>>;\nexport function mergeRouters<\n T1 extends RouterConfig,\n T2 extends RouterConfig,\n T3 extends RouterConfig,\n T4 extends RouterConfig,\n T5 extends RouterConfig,\n T6 extends RouterConfig,\n>(\n r1: RouterInput<T1>,\n r2: RouterInput<T2>,\n r3: RouterInput<T3>,\n r4: RouterInput<T4>,\n r5: RouterInput<T5>,\n r6: RouterInput<T6>,\n): RouterDefinition<DeepMergeAll<[T1, T2, T3, T4, T5, T6]>>;\nexport function mergeRouters<\n T1 extends RouterConfig,\n T2 extends RouterConfig,\n T3 extends RouterConfig,\n T4 extends RouterConfig,\n T5 extends RouterConfig,\n T6 extends RouterConfig,\n T7 extends RouterConfig,\n>(\n r1: RouterInput<T1>,\n r2: RouterInput<T2>,\n r3: RouterInput<T3>,\n r4: RouterInput<T4>,\n r5: RouterInput<T5>,\n r6: RouterInput<T6>,\n r7: RouterInput<T7>,\n): RouterDefinition<DeepMergeAll<[T1, T2, T3, T4, T5, T6, T7]>>;\nexport function mergeRouters<\n T1 extends RouterConfig,\n T2 extends RouterConfig,\n T3 extends RouterConfig,\n T4 extends RouterConfig,\n T5 extends RouterConfig,\n T6 extends RouterConfig,\n T7 extends RouterConfig,\n T8 extends RouterConfig,\n>(\n r1: RouterInput<T1>,\n r2: RouterInput<T2>,\n r3: RouterInput<T3>,\n r4: RouterInput<T4>,\n r5: RouterInput<T5>,\n r6: RouterInput<T6>,\n r7: RouterInput<T7>,\n r8: RouterInput<T8>,\n): RouterDefinition<DeepMergeAll<[T1, T2, T3, T4, T5, T6, T7, T8]>>;\n\n// Variadic fallback for 9+\nexport function mergeRouters(\n ...routers: RouterInput<RouterConfig>[]\n): RouterDefinition<RouterConfig>;\n\n// Implementation\nexport function mergeRouters(\n ...routers: RouterInput<RouterConfig>[]\n): RouterDefinition<RouterConfig> {\n const merged = routers.map(extractRoutes).reduce(deepMergeConfigs);\n return defineRouter(merged);\n}\n"],"mappings":";;;;;;AAuNO,SAAS,YAad,OAUA;AACA,QAAM,aAAa;AAAA,IACjB,QAAQ,MAAM;AAAA,IACd,MAAM,MAAM;AAAA,IACZ,SAAS;AAAA,MACP,MAAM,MAAM,SAAS,QAAQ;AAAA,MAC7B,OAAO,MAAM,SAAS,SAAS;AAAA,MAC/B,QAAQ,MAAM,SAAS,UAAU;AAAA,MACjC,SAAS,MAAM,SAAS,WAAW;AAAA,MACnC,SAAS,MAAM,SAAS,WAAW;AAAA,IACrC;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,MAAM;AAAA,MACJ,SAAS,MAAM,MAAM;AAAA,MACrB,aAAa,MAAM,MAAM;AAAA,MACzB,MAAM,MAAM,MAAM;AAAA,MAClB,aAAa,MAAM,MAAM;AAAA,MACzB,YAAY,MAAM,MAAM,cAAc;AAAA,MACtC,UAAU,MAAM,MAAM;AAAA,MACtB,cAAc,MAAM,MAAM;AAAA,IAC5B;AAAA,IACA,QAAQ;AAAA,EACV;AAYA,SAAO,OAAO,OAAO,UAAU;AACjC;;;AC7JO,SAAS,aACd,QACA,SACqB;AACrB,QAAM,aAAkC;AAAA,IACtC;AAAA,IACA,UAAU,SAAS;AAAA,IACnB,MAAM,SAAS;AAAA,IACf,WAAW;AAAA,EACb;AAGA,SAAO,WAAW,UAAU;AAC9B;AAKA,SAAS,WAA6B,KAAW;AAE/C,QAAM,YAAY,OAAO,oBAAoB,GAAG;AAGhD,aAAW,QAAQ,WAAW;AAC5B,UAAM,QAAQ,IAAI,IAAI;AACtB,QAAI,SAAS,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG;AACjE,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,GAAG;AAC1B;AASA,SAAS,cAAsC,OAA0B;AACvE,SAAO,mBAAmB,KAAK,IAAI,MAAM,SAAS;AACpD;AAGA,SAAS,YAAY,OAAuC;AAC1D,SACE,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,kBAAkB,KAAK,KACxB,CAAC,mBAAmB,KAAK;AAE7B;AAGA,SAAS,iBAAiB,GAAiB,GAA+B;AACxE,QAAM,SAAkC,EAAE,GAAG,EAAE;AAE/C,aAAW,OAAO,OAAO,KAAK,CAAC,GAAG;AAChC,UAAM,OAAO,OAAO,GAAG;AACvB,UAAM,OAAO,EAAE,GAAG;AAElB,QAAI,YAAY,IAAI,KAAK,YAAY,IAAI,GAAG;AAC1C,aAAO,GAAG,IAAI,iBAAiB,MAAM,IAAI;AAAA,IAC3C,OAAO;AACL,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AACT;AAmHO,SAAS,gBACX,SAC6B;AAChC,QAAM,SAAS,QAAQ,IAAI,aAAa,EAAE,OAAO,gBAAgB;AACjE,SAAO,aAAa,MAAM;AAC5B;","names":[]}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
3
|
-
|
|
4
|
-
// src/backend/core/onion-layers/presentation/http/route/types/path-params.type.ts
|
|
1
|
+
// src/presentation/http/route/types/path-params.type.ts
|
|
5
2
|
function pathToRegex(path) {
|
|
6
3
|
const pattern = path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "([^/]+)").replace(/\\\{([a-zA-Z_][a-zA-Z0-9_]*)\\\}/g, "([^/]+)");
|
|
7
4
|
return new RegExp(`^${pattern}/?$`);
|
|
@@ -26,7 +23,7 @@ function normalizePath(path) {
|
|
|
26
23
|
return path.replace(/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g, ":$1");
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
// src/
|
|
26
|
+
// src/presentation/http/route/types/router-definition.type.ts
|
|
30
27
|
function isRouteDefinition(value) {
|
|
31
28
|
return typeof value === "object" && value !== null && "method" in value && "path" in value && "responses" in value && "_types" in value;
|
|
32
29
|
}
|
|
@@ -49,7 +46,6 @@ function collectRoutes(config, basePath = "") {
|
|
|
49
46
|
}
|
|
50
47
|
|
|
51
48
|
export {
|
|
52
|
-
DEFAULT_CONTENT_TYPE,
|
|
53
49
|
pathToRegex,
|
|
54
50
|
getPathParamNames,
|
|
55
51
|
hasPathParams,
|
|
@@ -59,4 +55,4 @@ export {
|
|
|
59
55
|
isRouterDefinition,
|
|
60
56
|
collectRoutes
|
|
61
57
|
};
|
|
62
|
-
//# sourceMappingURL=chunk-
|
|
58
|
+
//# sourceMappingURL=chunk-DS7TE6KZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presentation/http/route/types/path-params.type.ts","../src/presentation/http/route/types/router-definition.type.ts"],"sourcesContent":["/**\n * @fileoverview Path parameter extraction types.\n *\n * These types enable TypeScript to extract path parameter names from\n * URL path templates at compile time, providing full type safety for\n * path parameters in routes.\n *\n * @module unified/route/types/path-params\n */\n\n/**\n * Extracts parameter names from a path template string.\n *\n * Supports both `:param` and `{param}` syntaxes for maximum compatibility\n * with different routing conventions.\n *\n * @example Colon syntax (Express-style)\n * ```typescript\n * type Params = ExtractPathParamNames<'/users/:userId/posts/:postId'>;\n * // 'userId' | 'postId'\n * ```\n *\n * @example Brace syntax (OpenAPI-style)\n * ```typescript\n * type Params = ExtractPathParamNames<'/users/{userId}/posts/{postId}'>;\n * // 'userId' | 'postId'\n * ```\n *\n * @example No parameters\n * ```typescript\n * type Params = ExtractPathParamNames<'/users'>;\n * // never\n * ```\n */\nexport type ExtractPathParamNames<T extends string> =\n // Match :param followed by more path\n T extends `${string}:${infer Param}/${infer Rest}`\n ? Param | ExtractPathParamNames<`/${Rest}`>\n : // Match :param at end\n T extends `${string}:${infer Param}`\n ? Param\n : // Match {param} followed by more path\n T extends `${string}{${infer Param}}/${infer Rest}`\n ? Param | ExtractPathParamNames<`/${Rest}`>\n : // Match {param} at end\n T extends `${string}{${infer Param}}`\n ? Param\n : never;\n\n/**\n * Creates an object type with all path parameters as string properties.\n *\n * @example\n * ```typescript\n * type Params = PathParams<'/projects/:projectId/tasks/:taskId'>;\n * // { projectId: string; taskId: string }\n *\n * type NoParams = PathParams<'/projects'>;\n * // Record<string, never> (empty object type)\n * ```\n */\nexport type PathParams<T extends string> =\n ExtractPathParamNames<T> extends never\n ? Record<string, never>\n : Record<ExtractPathParamNames<T>, string>;\n\n/**\n * Checks if a path has any parameters.\n *\n * @example\n * ```typescript\n * type HasParams = HasPathParams<'/users/:id'>; // true\n * type NoParams = HasPathParams<'/users'>; // false\n * ```\n */\nexport type HasPathParams<T extends string> = ExtractPathParamNames<T> extends never ? false : true;\n\n/**\n * Converts a path template with parameters to a regex pattern.\n * This is used internally for route matching.\n *\n * @example\n * ```typescript\n * pathToRegex('/users/:id/posts/:postId')\n * // /^\\/users\\/([^\\/]+)\\/posts\\/([^\\/]+)\\/?$/\n * ```\n */\nexport function pathToRegex(path: string): RegExp {\n const pattern = path\n // Escape special regex characters except : and {}\n .replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n // Replace :param with capture group\n .replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '([^/]+)')\n // Replace {param} with capture group\n .replace(/\\\\\\{([a-zA-Z_][a-zA-Z0-9_]*)\\\\\\}/g, '([^/]+)');\n\n return new RegExp(`^${pattern}/?$`);\n}\n\n/**\n * Extracts parameter names from a path string at runtime.\n *\n * @example\n * ```typescript\n * getPathParamNames('/users/:userId/posts/:postId')\n * // ['userId', 'postId']\n * ```\n */\nexport function getPathParamNames(path: string): string[] {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- regex capture group always exists\n const colonParams = [...path.matchAll(/:([a-zA-Z_][a-zA-Z0-9_]*)/g)].map((m) => m[1]!);\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- regex capture group always exists\n const braceParams = [...path.matchAll(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g)].map((m) => m[1]!);\n return [...colonParams, ...braceParams];\n}\n\n/**\n * Checks if a path has any parameters at runtime.\n */\nexport function hasPathParams(path: string): boolean {\n return /:([a-zA-Z_][a-zA-Z0-9_]*)/.test(path) || /\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/.test(path);\n}\n\n/**\n * Replaces path parameters with actual values.\n *\n * @example\n * ```typescript\n * buildPath('/users/:userId/posts/:postId', { userId: '123', postId: '456' })\n * // '/users/123/posts/456'\n *\n * buildPath('/users/{userId}', { userId: '123' })\n * // '/users/123'\n * ```\n */\nexport function buildPath(template: string, params: Record<string, string>): string {\n let result = template;\n\n // Replace :param syntax\n for (const [key, value] of Object.entries(params)) {\n result = result.replace(`:${key}`, encodeURIComponent(value));\n result = result.replace(`{${key}}`, encodeURIComponent(value));\n }\n\n return result;\n}\n\n/**\n * Normalizes a path template to use consistent :param syntax.\n *\n * @example\n * ```typescript\n * normalizePath('/users/{userId}')\n * // '/users/:userId'\n * ```\n */\nexport function normalizePath(path: string): string {\n return path.replace(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g, ':$1');\n}\n","/**\n * @fileoverview Router definition types for grouping routes.\n *\n * A router is a hierarchical grouping of routes that enables:\n * - Organized API structure\n * - Nested client method generation\n * - Grouped OpenAPI tags\n *\n * @module unified/route/types/router-definition\n */\n\nimport type { RouteDefinition, ResponsesConfig } from './route-definition.type';\nimport type { HttpMethod } from './http.type';\n\n// ============================================================================\n// Router Types\n// ============================================================================\n\n/**\n * A router entry can be either a route definition or a nested router.\n * Uses permissive types to allow any valid route definition.\n */\nexport type RouterEntry =\n | RouteDefinition<\n HttpMethod,\n string,\n unknown,\n unknown,\n unknown,\n unknown,\n unknown,\n ResponsesConfig\n >\n | RouterConfig;\n\n/**\n * Configuration for a router (group of routes).\n */\nexport interface RouterConfig {\n readonly [key: string]: RouterEntry;\n}\n\n/**\n * A fully defined router.\n */\nexport interface RouterDefinition<T extends RouterConfig = RouterConfig> {\n /**\n * The routes and nested routers in this router.\n */\n readonly routes: T;\n\n /**\n * Base path prefix for all routes in this router.\n */\n readonly basePath?: string;\n\n /**\n * Default tags for all routes in this router.\n */\n readonly tags?: readonly string[];\n\n /**\n * Marker to identify this as a router.\n * @internal\n */\n readonly _isRouter: true;\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Checks if a value is a RouteDefinition.\n */\nexport function isRouteDefinition(value: unknown): value is RouteDefinition {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'method' in value &&\n 'path' in value &&\n 'responses' in value &&\n '_types' in value\n );\n}\n\n/**\n * Checks if a value is a RouterDefinition.\n */\nexport function isRouterDefinition(value: unknown): value is RouterDefinition {\n return (\n typeof value === 'object' &&\n value !== null &&\n '_isRouter' in value &&\n (value as RouterDefinition)._isRouter === true\n );\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Flattens a router into a map of path keys to route definitions.\n *\n * @example\n * ```typescript\n * const router = defineRouter({\n * users: {\n * list: listUsersRoute,\n * get: getUserRoute,\n * },\n * posts: {\n * create: createPostRoute,\n * },\n * });\n *\n * type Flat = FlattenRouter<typeof router>;\n * // {\n * // 'users.list': typeof listUsersRoute,\n * // 'users.get': typeof getUserRoute,\n * // 'posts.create': typeof createPostRoute,\n * // }\n * ```\n */\nexport type FlattenRouter<\n T extends RouterConfig,\n Prefix extends string = '',\n> = T extends RouterConfig\n ? {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends RouteDefinition<any, any, any, any, any, any, any, any>\n ? { [P in `${Prefix}${K & string}`]: T[K] }\n : T[K] extends RouterConfig\n ? FlattenRouter<T[K], `${Prefix}${K & string}.`>\n : never;\n }[keyof T] extends infer U\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n U extends Record<string, RouteDefinition<any, any, any, any, any, any, any, any>>\n ? U\n : never\n : never\n : never;\n\n/**\n * Gets all route keys from a router.\n *\n * @example\n * ```typescript\n * type Keys = RouterKeys<typeof router>;\n * // 'users.list' | 'users.get' | 'posts.create'\n * ```\n */\nexport type RouterKeys<T extends RouterConfig, Prefix extends string = ''> = T extends RouterConfig\n ? {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends RouteDefinition<any, any, any, any, any, any, any, any>\n ? `${Prefix}${K & string}`\n : T[K] extends RouterConfig\n ? RouterKeys<T[K], `${Prefix}${K & string}.`>\n : never;\n }[keyof T]\n : never;\n\n/**\n * Gets a route by its dotted key path.\n *\n * @example\n * ```typescript\n * type UserGet = GetRoute<typeof router, 'users.get'>;\n * // typeof getUserRoute\n * ```\n */\nexport type GetRoute<\n T extends RouterConfig,\n K extends string,\n> = K extends `${infer Head}.${infer Tail}`\n ? Head extends keyof T\n ? T[Head] extends RouterConfig\n ? GetRoute<T[Head], Tail>\n : never\n : never\n : K extends keyof T\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T[K] extends RouteDefinition<any, any, any, any, any, any, any, any>\n ? T[K]\n : never\n : never;\n\n// ============================================================================\n// Deep Merge Types\n// ============================================================================\n\n/**\n * Deep-merges two router configs at the type level.\n *\n * - If both sides are sub-routers (extend RouterConfig), recurse.\n * - Otherwise last-one-wins (B overrides A).\n * - RouteDefinition does NOT extend RouterConfig (it has `method`, `path`, etc.)\n * so the conditional correctly distinguishes leaves from sub-routers.\n */\nexport type DeepMergeTwo<A extends RouterConfig, B extends RouterConfig> = {\n readonly [K in keyof A | keyof B]: K extends keyof A\n ? K extends keyof B\n ? A[K] extends RouterConfig\n ? B[K] extends RouterConfig\n ? DeepMergeTwo<A[K], B[K]>\n : B[K]\n : B[K]\n : A[K]\n : K extends keyof B\n ? B[K]\n : never;\n};\n\n/**\n * Recursively deep-merges N router configs left-to-right.\n */\nexport type DeepMergeAll<T extends readonly RouterConfig[]> = T extends readonly [\n infer Only extends RouterConfig,\n]\n ? Only\n : T extends readonly [\n infer First extends RouterConfig,\n infer Second extends RouterConfig,\n ...infer Rest extends readonly RouterConfig[],\n ]\n ? DeepMergeAll<[DeepMergeTwo<First, Second>, ...Rest]>\n : RouterConfig;\n\n/**\n * Recursively flattens complex types for clean IDE hover display.\n * Applied at return sites (not inside recursion) so DTS emit stays fast —\n * TypeScript only resolves when concrete types are provided.\n *\n * Works with any recursive object type: router configs, client types,\n * React Query hooks, etc. Functions and primitives pass through unchanged.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type PrettifyDeep<T> = T extends (...args: any[]) => any\n ? T\n : T extends object\n ? { readonly [K in keyof T]: PrettifyDeep<T[K]> }\n : T;\n\n/**\n * Collects all routes from a router into an array.\n */\nexport function collectRoutes(\n config: RouterConfig,\n basePath = '',\n): { key: string; route: RouteDefinition }[] {\n const routes: { key: string; route: RouteDefinition }[] = [];\n\n for (const [key, value] of Object.entries(config)) {\n const fullKey = basePath ? `${basePath}.${key}` : key;\n\n if (isRouteDefinition(value)) {\n routes.push({ key: fullKey, route: value });\n } else if (isRouterDefinition(value)) {\n routes.push(...collectRoutes(value.routes, fullKey));\n } else if (typeof value === 'object' && value !== null) {\n routes.push(...collectRoutes(value as RouterConfig, fullKey));\n }\n }\n\n return routes;\n}\n"],"mappings":";AAuFO,SAAS,YAAY,MAAsB;AAChD,QAAM,UAAU,KAEb,QAAQ,uBAAuB,MAAM,EAErC,QAAQ,8BAA8B,SAAS,EAE/C,QAAQ,qCAAqC,SAAS;AAEzD,SAAO,IAAI,OAAO,IAAI,OAAO,KAAK;AACpC;AAWO,SAAS,kBAAkB,MAAwB;AAExD,QAAM,cAAc,CAAC,GAAG,KAAK,SAAS,4BAA4B,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAE;AAErF,QAAM,cAAc,CAAC,GAAG,KAAK,SAAS,+BAA+B,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAE;AACxF,SAAO,CAAC,GAAG,aAAa,GAAG,WAAW;AACxC;AAKO,SAAS,cAAc,MAAuB;AACnD,SAAO,4BAA4B,KAAK,IAAI,KAAK,+BAA+B,KAAK,IAAI;AAC3F;AAcO,SAAS,UAAU,UAAkB,QAAwC;AAClF,MAAI,SAAS;AAGb,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,aAAS,OAAO,QAAQ,IAAI,GAAG,IAAI,mBAAmB,KAAK,CAAC;AAC5D,aAAS,OAAO,QAAQ,IAAI,GAAG,KAAK,mBAAmB,KAAK,CAAC;AAAA,EAC/D;AAEA,SAAO;AACT;AAWO,SAAS,cAAc,MAAsB;AAClD,SAAO,KAAK,QAAQ,iCAAiC,KAAK;AAC5D;;;ACnFO,SAAS,kBAAkB,OAA0C;AAC1E,SACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,UAAU,SACV,eAAe,SACf,YAAY;AAEhB;AAKO,SAAS,mBAAmB,OAA2C;AAC5E,SACE,OAAO,UAAU,YACjB,UAAU,QACV,eAAe,SACd,MAA2B,cAAc;AAE9C;AAwJO,SAAS,cACd,QACA,WAAW,IACgC;AAC3C,QAAM,SAAoD,CAAC;AAE3D,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAM,UAAU,WAAW,GAAG,QAAQ,IAAI,GAAG,KAAK;AAElD,QAAI,kBAAkB,KAAK,GAAG;AAC5B,aAAO,KAAK,EAAE,KAAK,SAAS,OAAO,MAAM,CAAC;AAAA,IAC5C,WAAW,mBAAmB,KAAK,GAAG;AACpC,aAAO,KAAK,GAAG,cAAc,MAAM,QAAQ,OAAO,CAAC;AAAA,IACrD,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AACtD,aAAO,KAAK,GAAG,cAAc,OAAuB,OAAO,CAAC;AAAA,IAC9D;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1,26 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ControllerError,
|
|
3
|
+
InvalidRequestError,
|
|
4
|
+
UnauthorizedError
|
|
5
|
+
} from "./chunk-T7S574XQ.js";
|
|
6
|
+
import {
|
|
7
|
+
wrapError
|
|
8
|
+
} from "./chunk-ZG26OQFN.js";
|
|
1
9
|
import {
|
|
2
10
|
collectRoutes,
|
|
3
11
|
isRouterDefinition,
|
|
4
12
|
normalizePath
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import {
|
|
7
|
-
ControllerError,
|
|
8
|
-
InvalidRequestError
|
|
9
|
-
} from "./chunk-RRXLBGQB.js";
|
|
13
|
+
} from "./chunk-DS7TE6KZ.js";
|
|
10
14
|
|
|
11
|
-
// src/
|
|
15
|
+
// src/presentation/http/server/types.ts
|
|
12
16
|
function isSimpleHandlerConfig(config) {
|
|
13
17
|
return "handler" in config && typeof config.handler === "function";
|
|
14
18
|
}
|
|
15
19
|
|
|
16
|
-
// src/
|
|
17
|
-
function createServerRoutes(router, handlers, options) {
|
|
18
|
-
return createServerRoutesInternal(
|
|
19
|
-
router,
|
|
20
|
-
handlers,
|
|
21
|
-
options
|
|
22
|
-
);
|
|
23
|
-
}
|
|
20
|
+
// src/presentation/http/server/create-server-routes.ts
|
|
24
21
|
function createServerRoutesInternal(router, handlers, options) {
|
|
25
22
|
const routes = isRouterDefinition(router) ? router.routes : router;
|
|
26
23
|
const collectedRoutes = collectRoutes(routes);
|
|
@@ -85,19 +82,23 @@ function createRouteHandler(route, config, options) {
|
|
|
85
82
|
},
|
|
86
83
|
handler: async (rawRequest, ctx) => {
|
|
87
84
|
const rawContext = options?.createContext ? options.createContext(rawRequest) : ctx ?? { requestId: generateRequestId() };
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
const validatedContext = route.request.context?.schema ? wrapError(
|
|
86
|
+
() => {
|
|
87
|
+
const result = validateContextData(route, rawContext);
|
|
88
|
+
if (!result.success) {
|
|
89
|
+
const errors = result.errors ?? [];
|
|
90
|
+
throw new InvalidRequestError({
|
|
91
|
+
message: "Context validation failed",
|
|
92
|
+
validationErrors: errors.map((e) => ({
|
|
93
|
+
field: e.path.join("."),
|
|
94
|
+
message: e.message
|
|
95
|
+
}))
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return result.data;
|
|
99
|
+
},
|
|
100
|
+
() => new UnauthorizedError({ message: "Authentication required" })
|
|
101
|
+
) : rawContext;
|
|
101
102
|
let validatedRequest;
|
|
102
103
|
if (shouldValidateRequest) {
|
|
103
104
|
const validationResult = validateRequestData(route, rawRequest);
|
|
@@ -352,8 +353,48 @@ function generateRequestId() {
|
|
|
352
353
|
return `req_${crypto.randomUUID()}`;
|
|
353
354
|
}
|
|
354
355
|
|
|
356
|
+
// src/presentation/http/server/server-routes-builder.ts
|
|
357
|
+
var ServerRoutesBuilderImpl = class _ServerRoutesBuilderImpl {
|
|
358
|
+
router;
|
|
359
|
+
handlers;
|
|
360
|
+
constructor(router, handlers) {
|
|
361
|
+
this.router = router;
|
|
362
|
+
this.handlers = handlers ?? /* @__PURE__ */ new Map();
|
|
363
|
+
}
|
|
364
|
+
handle(key, handlerOrConfig) {
|
|
365
|
+
const config = typeof handlerOrConfig === "function" ? { handler: handlerOrConfig } : handlerOrConfig;
|
|
366
|
+
const newHandlers = new Map(this.handlers);
|
|
367
|
+
newHandlers.set(key, config);
|
|
368
|
+
return new _ServerRoutesBuilderImpl(
|
|
369
|
+
this.router,
|
|
370
|
+
newHandlers
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
handleWithUseCase(key, config) {
|
|
374
|
+
const newHandlers = new Map(this.handlers);
|
|
375
|
+
newHandlers.set(key, config);
|
|
376
|
+
return new _ServerRoutesBuilderImpl(
|
|
377
|
+
this.router,
|
|
378
|
+
newHandlers
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
// The build method's type is determined by the interface conditional type
|
|
382
|
+
// At runtime, it always works the same way - the conditional type only affects compile-time
|
|
383
|
+
build(options) {
|
|
384
|
+
return createServerRoutesInternal(this.router, Object.fromEntries(this.handlers), options);
|
|
385
|
+
}
|
|
386
|
+
buildPartial(options) {
|
|
387
|
+
return createServerRoutesInternal(this.router, Object.fromEntries(this.handlers), {
|
|
388
|
+
...options,
|
|
389
|
+
allowPartial: true
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
function serverRoutes(router) {
|
|
394
|
+
return new ServerRoutesBuilderImpl(router);
|
|
395
|
+
}
|
|
396
|
+
|
|
355
397
|
export {
|
|
356
|
-
|
|
357
|
-
createServerRoutesInternal
|
|
398
|
+
serverRoutes
|
|
358
399
|
};
|
|
359
|
-
//# sourceMappingURL=chunk-
|
|
400
|
+
//# sourceMappingURL=chunk-GGSAAZPM.js.map
|