@cosmneo/onion-lasagna 0.3.0 → 0.4.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-XWKHOLIP.js → chunk-4HMXTGHK.js} +2 -2
- package/dist/chunk-4RFWJ5XZ.js +192 -0
- package/dist/chunk-4RFWJ5XZ.js.map +1 -0
- package/dist/{chunk-4BVOLXDJ.js → chunk-4YBAV6LZ.js} +2 -2
- package/dist/chunk-ANLXZHUS.js +230 -0
- package/dist/chunk-ANLXZHUS.js.map +1 -0
- package/dist/chunk-BG2FY27M.js +36 -0
- package/dist/chunk-BG2FY27M.js.map +1 -0
- package/dist/chunk-FEY2GSVT.js +1 -0
- package/dist/chunk-FEY2GSVT.js.map +1 -0
- package/dist/{chunk-2BVCU32G.js → chunk-HNEAH6OZ.js} +121 -2
- package/dist/chunk-HNEAH6OZ.js.map +1 -0
- package/dist/chunk-NQMFWI6Q.js +1 -0
- package/dist/chunk-NQMFWI6Q.js.map +1 -0
- package/dist/chunk-TZRBETT3.js +127 -0
- package/dist/chunk-TZRBETT3.js.map +1 -0
- package/dist/{chunk-MF2JDREK.js → chunk-UNVB4INM.js} +1 -1
- package/dist/{chunk-MF2JDREK.js.map → chunk-UNVB4INM.js.map} +1 -1
- package/dist/chunk-VBG3UYQR.js +119 -0
- package/dist/chunk-VBG3UYQR.js.map +1 -0
- package/dist/events/index.js +3 -4
- package/dist/events/server/index.js +3 -4
- package/dist/events/shared/index.js +2 -3
- package/dist/graphql/field/index.cjs +189 -0
- package/dist/graphql/field/index.cjs.map +1 -0
- package/dist/graphql/field/index.d.cts +214 -0
- package/dist/graphql/field/index.d.ts +214 -0
- package/dist/graphql/field/index.js +25 -0
- package/dist/graphql/field/index.js.map +1 -0
- package/dist/graphql/index.cjs +1148 -0
- package/dist/graphql/index.cjs.map +1 -0
- package/dist/graphql/index.d.cts +8 -0
- package/dist/graphql/index.d.ts +8 -0
- package/dist/graphql/index.js +49 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/sdl/index.cjs +241 -0
- package/dist/graphql/sdl/index.cjs.map +1 -0
- package/dist/graphql/sdl/index.d.cts +77 -0
- package/dist/graphql/sdl/index.d.ts +77 -0
- package/dist/graphql/sdl/index.js +8 -0
- package/dist/graphql/sdl/index.js.map +1 -0
- package/dist/graphql/server/index.cjs +505 -0
- package/dist/graphql/server/index.cjs.map +1 -0
- package/dist/graphql/server/index.d.cts +268 -0
- package/dist/graphql/server/index.d.ts +268 -0
- package/dist/graphql/server/index.js +15 -0
- package/dist/graphql/server/index.js.map +1 -0
- package/dist/graphql/shared/index.cjs +586 -0
- package/dist/graphql/shared/index.cjs.map +1 -0
- package/dist/graphql/shared/index.d.cts +82 -0
- package/dist/graphql/shared/index.d.ts +82 -0
- package/dist/graphql/shared/index.js +16 -0
- package/dist/graphql/shared/index.js.map +1 -0
- package/dist/http/index.cjs.map +1 -1
- package/dist/http/index.js +2 -3
- package/dist/http/route/index.cjs.map +1 -1
- package/dist/http/route/index.d.cts +4 -0
- package/dist/http/route/index.d.ts +4 -0
- package/dist/http/route/index.js +1 -1
- package/dist/http/shared/index.js +1 -2
- package/dist/index.cjs +672 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/dist/schema-definition.type-C9PntBVc.d.cts +166 -0
- package/dist/schema-definition.type-CuhQLDr0.d.ts +166 -0
- package/package.json +31 -1
- package/dist/chunk-2BVCU32G.js.map +0 -1
- package/dist/chunk-H5TNDC5U.js +0 -138
- package/dist/chunk-H5TNDC5U.js.map +0 -1
- /package/dist/{chunk-XWKHOLIP.js.map → chunk-4HMXTGHK.js.map} +0 -0
- /package/dist/{chunk-4BVOLXDJ.js.map → chunk-4YBAV6LZ.js.map} +0 -0
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ControllerError,
|
|
3
|
+
InvalidRequestError,
|
|
4
|
+
UnauthorizedError,
|
|
2
5
|
UseCaseError
|
|
3
6
|
} from "./chunk-T7S574XQ.js";
|
|
7
|
+
import {
|
|
8
|
+
ObjectValidationError
|
|
9
|
+
} from "./chunk-3BY5RBF2.js";
|
|
4
10
|
import {
|
|
5
11
|
CodedError,
|
|
6
12
|
ErrorCodes
|
|
@@ -223,6 +229,113 @@ var AccessDeniedError = class _AccessDeniedError extends CodedError {
|
|
|
223
229
|
}
|
|
224
230
|
};
|
|
225
231
|
|
|
232
|
+
// src/presentation/http/shared/error-mapping.ts
|
|
233
|
+
var MASKED_ERROR_BODY = {
|
|
234
|
+
message: "An unexpected error occurred",
|
|
235
|
+
errorCode: "INTERNAL_ERROR"
|
|
236
|
+
};
|
|
237
|
+
var INTERNAL_ERROR_TYPES = [
|
|
238
|
+
"DomainError",
|
|
239
|
+
"InfraError",
|
|
240
|
+
"ControllerError",
|
|
241
|
+
"NetworkError",
|
|
242
|
+
"PersistenceError",
|
|
243
|
+
"ExternalServiceError",
|
|
244
|
+
"InvariantViolationError"
|
|
245
|
+
];
|
|
246
|
+
function isErrorType(error, typeName) {
|
|
247
|
+
if (!error || typeof error !== "object") return false;
|
|
248
|
+
const constructor = Object.prototype.hasOwnProperty.call(error, "constructor") ? error.constructor : Object.getPrototypeOf(error)?.constructor;
|
|
249
|
+
if (!constructor) return false;
|
|
250
|
+
const name = constructor.name;
|
|
251
|
+
return name === typeName || name === `_${typeName}`;
|
|
252
|
+
}
|
|
253
|
+
function hasValidationErrors(error) {
|
|
254
|
+
if (!error || typeof error !== "object") return false;
|
|
255
|
+
return "validationErrors" in error && Array.isArray(error.validationErrors);
|
|
256
|
+
}
|
|
257
|
+
function buildValidationErrorBody(message, code, validationErrors) {
|
|
258
|
+
return {
|
|
259
|
+
message,
|
|
260
|
+
errorCode: code,
|
|
261
|
+
errorItems: validationErrors.map((e) => ({
|
|
262
|
+
item: e.field,
|
|
263
|
+
message: e.message
|
|
264
|
+
}))
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function buildSimpleErrorBody(message, code) {
|
|
268
|
+
return {
|
|
269
|
+
message,
|
|
270
|
+
errorCode: code
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function getHttpStatusCode(error) {
|
|
274
|
+
if (error instanceof ObjectValidationError) return 400;
|
|
275
|
+
if (error instanceof InvalidRequestError) return 400;
|
|
276
|
+
if (error instanceof UnauthorizedError) return 401;
|
|
277
|
+
if (error instanceof ForbiddenError) return 403;
|
|
278
|
+
if (error instanceof AccessDeniedError) return 403;
|
|
279
|
+
if (error instanceof NotFoundError) return 404;
|
|
280
|
+
if (error instanceof ConflictError) return 409;
|
|
281
|
+
if (error instanceof UnprocessableError) return 422;
|
|
282
|
+
if (error instanceof UseCaseError) return 400;
|
|
283
|
+
if (isErrorType(error, "ObjectValidationError")) return 400;
|
|
284
|
+
if (isErrorType(error, "InvalidRequestError")) return 400;
|
|
285
|
+
if (isErrorType(error, "UnauthorizedError")) return 401;
|
|
286
|
+
if (isErrorType(error, "ForbiddenError")) return 403;
|
|
287
|
+
if (isErrorType(error, "AccessDeniedError")) return 403;
|
|
288
|
+
if (isErrorType(error, "NotFoundError")) return 404;
|
|
289
|
+
if (isErrorType(error, "ConflictError")) return 409;
|
|
290
|
+
if (isErrorType(error, "UnprocessableError")) return 422;
|
|
291
|
+
if (isErrorType(error, "UseCaseError")) return 400;
|
|
292
|
+
return 500;
|
|
293
|
+
}
|
|
294
|
+
function shouldMaskError(error) {
|
|
295
|
+
if (error instanceof DomainError || error instanceof InfraError || error instanceof ControllerError) {
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
for (const errorType of INTERNAL_ERROR_TYPES) {
|
|
299
|
+
if (isErrorType(error, errorType)) {
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
function createErrorResponseBody(error) {
|
|
306
|
+
if (shouldMaskError(error)) {
|
|
307
|
+
return MASKED_ERROR_BODY;
|
|
308
|
+
}
|
|
309
|
+
if (error instanceof ObjectValidationError) {
|
|
310
|
+
return buildValidationErrorBody(error.message, error.code, error.validationErrors);
|
|
311
|
+
}
|
|
312
|
+
if (error instanceof InvalidRequestError) {
|
|
313
|
+
return buildValidationErrorBody(error.message, error.code, error.validationErrors);
|
|
314
|
+
}
|
|
315
|
+
if (isErrorType(error, "ObjectValidationError") && hasValidationErrors(error)) {
|
|
316
|
+
return buildValidationErrorBody(error.message, error.code, error.validationErrors);
|
|
317
|
+
}
|
|
318
|
+
if (isErrorType(error, "InvalidRequestError") && hasValidationErrors(error)) {
|
|
319
|
+
return buildValidationErrorBody(error.message, error.code, error.validationErrors);
|
|
320
|
+
}
|
|
321
|
+
if (error instanceof CodedError) {
|
|
322
|
+
return buildSimpleErrorBody(error.message, error.code);
|
|
323
|
+
}
|
|
324
|
+
if (isErrorType(error, "CodedError")) {
|
|
325
|
+
return buildSimpleErrorBody(error.message, error.code);
|
|
326
|
+
}
|
|
327
|
+
if (error && typeof error === "object" && "message" in error && "code" in error && typeof error.message === "string" && typeof error.code === "string") {
|
|
328
|
+
return buildSimpleErrorBody(error.message, error.code);
|
|
329
|
+
}
|
|
330
|
+
return MASKED_ERROR_BODY;
|
|
331
|
+
}
|
|
332
|
+
function mapErrorToHttpResponse(error) {
|
|
333
|
+
return {
|
|
334
|
+
status: getHttpStatusCode(error),
|
|
335
|
+
body: createErrorResponseBody(error)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
226
339
|
export {
|
|
227
340
|
DomainError,
|
|
228
341
|
InfraError,
|
|
@@ -230,6 +343,12 @@ export {
|
|
|
230
343
|
ConflictError,
|
|
231
344
|
UnprocessableError,
|
|
232
345
|
ForbiddenError,
|
|
233
|
-
AccessDeniedError
|
|
346
|
+
AccessDeniedError,
|
|
347
|
+
isErrorType,
|
|
348
|
+
hasValidationErrors,
|
|
349
|
+
getHttpStatusCode,
|
|
350
|
+
shouldMaskError,
|
|
351
|
+
createErrorResponseBody,
|
|
352
|
+
mapErrorToHttpResponse
|
|
234
353
|
};
|
|
235
|
-
//# sourceMappingURL=chunk-
|
|
354
|
+
//# sourceMappingURL=chunk-HNEAH6OZ.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","../src/presentation/http/shared/error-mapping.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","/**\n * @fileoverview Centralized error mapping for HTTP frameworks.\n *\n * Provides a single source of truth for mapping onion-lasagna errors\n * to HTTP status codes and response bodies.\n *\n * @module http/shared/error-mapping\n */\n\nimport { CodedError } from '../../../global/exceptions/coded-error.error';\nimport { ObjectValidationError } from '../../../global/exceptions/object-validation.error';\nimport { DomainError } from '../../../domain/exceptions/domain.error';\nimport { UseCaseError } from '../../../app/exceptions/use-case.error';\nimport { NotFoundError } from '../../../app/exceptions/not-found.error';\nimport { ConflictError } from '../../../app/exceptions/conflict.error';\nimport { UnprocessableError } from '../../../app/exceptions/unprocessable.error';\nimport { ForbiddenError } from '../../../app/exceptions/forbidden.error';\nimport { UnauthorizedError } from '../../../app/exceptions/unauthorized.error';\nimport { InfraError } from '../../../infra/exceptions/infra.error';\nimport { ControllerError } from '../../exceptions/controller.error';\nimport { AccessDeniedError } from '../../exceptions/access-denied.error';\nimport { InvalidRequestError } from '../../exceptions/invalid-request.error';\nimport type { ErrorResponseBody, MappedErrorResponse } from './types';\n\n// ============================================================================\n// Constants and Interfaces\n// ============================================================================\n\n/**\n * Default masked error response for internal errors.\n */\nconst MASKED_ERROR_BODY: ErrorResponseBody = {\n message: 'An unexpected error occurred',\n errorCode: 'INTERNAL_ERROR',\n};\n\n/**\n * Known internal error type names that should be masked.\n */\nconst INTERNAL_ERROR_TYPES = [\n 'DomainError',\n 'InfraError',\n 'ControllerError',\n 'NetworkError',\n 'PersistenceError',\n 'ExternalServiceError',\n 'InvariantViolationError',\n];\n\n/**\n * Validation error item structure.\n */\ninterface ValidationErrorInput {\n field: string;\n message: string;\n}\n\n/**\n * Validation error structure for string-based checking.\n */\ninterface ValidationErrorItem {\n field: string;\n message: string;\n}\n\n/**\n * Interface for errors with validation items.\n */\ninterface ErrorWithValidation {\n message: string;\n code: string;\n validationErrors: ValidationErrorItem[];\n}\n\n/**\n * Interface for coded errors.\n */\ninterface CodedErrorLike {\n message: string;\n code: string;\n}\n\n// ============================================================================\n// String-based type checking helpers (for bundled code compatibility)\n// ============================================================================\n\n/**\n * Checks if error matches a specific error type by checking its constructor name.\n * This approach avoids issues with multiple class instances in bundled code.\n * Handles both original names and tsup's mangled names (prefixed with _).\n *\n * @param error - The error to check\n * @param typeName - The error type name to match\n * @returns True if error matches the type name\n */\nexport function isErrorType(error: unknown, typeName: string): error is CodedErrorLike {\n if (!error || typeof error !== 'object') return false;\n // Guard against objects without constructor (e.g., Object.create(null))\n const constructor = Object.prototype.hasOwnProperty.call(error, 'constructor')\n ? (error as { constructor?: { name?: string } }).constructor\n : Object.getPrototypeOf(error)?.constructor;\n if (!constructor) return false;\n const name = constructor.name;\n // Check both the original name and the mangled name (tsup prefixes with _)\n return name === typeName || name === `_${typeName}`;\n}\n\n/**\n * Checks if error has validation errors array.\n *\n * @param error - The error to check\n * @returns True if error has validationErrors property\n */\nexport function hasValidationErrors(error: unknown): error is ErrorWithValidation {\n if (!error || typeof error !== 'object') return false;\n return (\n 'validationErrors' in error && Array.isArray((error as ErrorWithValidation).validationErrors)\n );\n}\n\n// ============================================================================\n// Response body builders\n// ============================================================================\n\n/**\n * Builds a validation error response body from error details.\n *\n * @param message - The error message\n * @param code - The error code\n * @param validationErrors - Array of field validation errors\n * @returns Error response body with errorItems\n */\nfunction buildValidationErrorBody(\n message: string,\n code: string,\n validationErrors: readonly ValidationErrorInput[],\n): ErrorResponseBody {\n return {\n message,\n errorCode: code,\n errorItems: validationErrors.map((e) => ({\n item: e.field,\n message: e.message,\n })),\n };\n}\n\n/**\n * Builds a simple error response body from error details.\n *\n * @param message - The error message\n * @param code - The error code\n * @returns Error response body\n */\nfunction buildSimpleErrorBody(message: string, code: string): ErrorResponseBody {\n return {\n message,\n errorCode: code,\n };\n}\n\n// ============================================================================\n// Primary error mapping functions (with bundled code fallback)\n// ============================================================================\n\n/**\n * Maps an error to the appropriate HTTP status code.\n * Uses instanceof first, then falls back to name-based checking for bundled code.\n *\n * @param error - The error to map\n * @returns HTTP status code\n */\nexport function getHttpStatusCode(error: unknown): number {\n // Try instanceof first (faster)\n if (error instanceof ObjectValidationError) return 400;\n if (error instanceof InvalidRequestError) return 400;\n if (error instanceof UnauthorizedError) return 401;\n if (error instanceof ForbiddenError) return 403;\n if (error instanceof AccessDeniedError) return 403;\n if (error instanceof NotFoundError) return 404;\n if (error instanceof ConflictError) return 409;\n if (error instanceof UnprocessableError) return 422;\n if (error instanceof UseCaseError) return 400;\n\n // Fall back to name-based checking for bundled code (e.g., _NotFoundError)\n if (isErrorType(error, 'ObjectValidationError')) return 400;\n if (isErrorType(error, 'InvalidRequestError')) return 400;\n if (isErrorType(error, 'UnauthorizedError')) return 401;\n if (isErrorType(error, 'ForbiddenError')) return 403;\n if (isErrorType(error, 'AccessDeniedError')) return 403;\n if (isErrorType(error, 'NotFoundError')) return 404;\n if (isErrorType(error, 'ConflictError')) return 409;\n if (isErrorType(error, 'UnprocessableError')) return 422;\n if (isErrorType(error, 'UseCaseError')) return 400;\n\n return 500;\n}\n\n/**\n * Checks if an error should have its details masked in the response.\n *\n * Security-sensitive errors (domain, infrastructure, controller) are masked\n * to prevent leaking implementation details.\n * Uses instanceof first, then falls back to name-based checking for bundled code.\n *\n * @param error - The error to check\n * @returns True if error details should be hidden\n */\nexport function shouldMaskError(error: unknown): boolean {\n // Try instanceof first (faster)\n if (\n error instanceof DomainError ||\n error instanceof InfraError ||\n error instanceof ControllerError\n ) {\n return true;\n }\n\n // Fall back to name-based checking for bundled code\n for (const errorType of INTERNAL_ERROR_TYPES) {\n if (isErrorType(error, errorType)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Creates the response body for an error.\n * Uses instanceof first, then falls back to name-based checking for bundled code.\n *\n * @param error - The error to create body for\n * @returns Error response body\n */\nexport function createErrorResponseBody(error: unknown): ErrorResponseBody {\n // Masked errors - hide internal details\n if (shouldMaskError(error)) {\n return MASKED_ERROR_BODY;\n }\n\n // Validation errors - include field details (try instanceof first)\n if (error instanceof ObjectValidationError) {\n return buildValidationErrorBody(error.message, error.code, error.validationErrors);\n }\n if (error instanceof InvalidRequestError) {\n return buildValidationErrorBody(error.message, error.code, error.validationErrors);\n }\n\n // Validation errors - fall back to name-based checking for bundled code\n if (isErrorType(error, 'ObjectValidationError') && hasValidationErrors(error)) {\n return buildValidationErrorBody(error.message, error.code, error.validationErrors);\n }\n if (isErrorType(error, 'InvalidRequestError') && hasValidationErrors(error)) {\n return buildValidationErrorBody(error.message, error.code, error.validationErrors);\n }\n\n // Other coded errors - expose message and code (try instanceof first)\n if (error instanceof CodedError) {\n return buildSimpleErrorBody(error.message, error.code);\n }\n\n // Other coded errors - fall back to name-based checking\n if (isErrorType(error, 'CodedError')) {\n return buildSimpleErrorBody(error.message, error.code);\n }\n\n // Check for any error with message and code properties\n if (\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n 'code' in error &&\n typeof (error as CodedErrorLike).message === 'string' &&\n typeof (error as CodedErrorLike).code === 'string'\n ) {\n return buildSimpleErrorBody((error as CodedErrorLike).message, (error as CodedErrorLike).code);\n }\n\n // Unknown errors - mask\n return MASKED_ERROR_BODY;\n}\n\n/**\n * Maps an error to a complete HTTP response structure.\n *\n * Mapping strategy (checked in order):\n * 1. `ObjectValidationError` / `InvalidRequestError` → 400 Bad Request (with field errors)\n * 2. `UseCaseError` → 400 Bad Request\n * 3. `UnauthorizedError` → 401 Unauthorized\n * 4. `ForbiddenError` / `AccessDeniedError` → 403 Forbidden\n * 5. `NotFoundError` → 404 Not Found\n * 6. `ConflictError` → 409 Conflict\n * 7. `UnprocessableError` → 422 Unprocessable Entity\n * 8. `DomainError` / `InfraError` / `ControllerError` → 500 Internal Server Error (masked)\n * 9. Unknown → 500 Internal Server Error (masked)\n *\n * @param error - The error to map\n * @returns Mapped error response with status and body\n */\nexport function mapErrorToHttpResponse(error: unknown): MappedErrorResponse {\n return {\n status: getHttpStatusCode(error),\n body: createErrorResponseBody(error),\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=chunk-NQMFWI6Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isFieldDefinition,
|
|
3
|
+
isSchemaDefinition
|
|
4
|
+
} from "./chunk-BG2FY27M.js";
|
|
5
|
+
|
|
6
|
+
// src/presentation/graphql/field/define-field.ts
|
|
7
|
+
function createFieldDefinition(operation, input) {
|
|
8
|
+
const definition = {
|
|
9
|
+
operation,
|
|
10
|
+
input: input.input ?? void 0,
|
|
11
|
+
output: input.output ?? void 0,
|
|
12
|
+
context: input.context ?? void 0,
|
|
13
|
+
docs: {
|
|
14
|
+
summary: input.docs?.summary,
|
|
15
|
+
description: input.docs?.description,
|
|
16
|
+
tags: input.docs?.tags,
|
|
17
|
+
deprecated: input.docs?.deprecated ?? false,
|
|
18
|
+
deprecationReason: input.docs?.deprecationReason
|
|
19
|
+
},
|
|
20
|
+
_isGraphQLField: true,
|
|
21
|
+
_types: void 0
|
|
22
|
+
};
|
|
23
|
+
return Object.freeze(definition);
|
|
24
|
+
}
|
|
25
|
+
function defineQuery(config = {}) {
|
|
26
|
+
return createFieldDefinition("query", config);
|
|
27
|
+
}
|
|
28
|
+
function defineMutation(config = {}) {
|
|
29
|
+
return createFieldDefinition("mutation", config);
|
|
30
|
+
}
|
|
31
|
+
function defineSubscription(config = {}) {
|
|
32
|
+
return createFieldDefinition("subscription", config);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/presentation/graphql/field/define-schema.ts
|
|
36
|
+
function defineGraphQLSchema(fields, options) {
|
|
37
|
+
const defaults = options?.defaults;
|
|
38
|
+
const processedFields = defaults?.context || defaults?.tags ? applySchemaDefaults(fields, defaults) : fields;
|
|
39
|
+
const definition = {
|
|
40
|
+
fields: processedFields,
|
|
41
|
+
defaults,
|
|
42
|
+
_isGraphQLSchema: true
|
|
43
|
+
};
|
|
44
|
+
return deepFreeze(definition);
|
|
45
|
+
}
|
|
46
|
+
function applySchemaDefaults(config, defaults) {
|
|
47
|
+
const result = {};
|
|
48
|
+
for (const [key, value] of Object.entries(config)) {
|
|
49
|
+
if (isFieldDefinition(value)) {
|
|
50
|
+
result[key] = applyDefaultsToField(value, defaults);
|
|
51
|
+
} else if (isSchemaDefinition(value)) {
|
|
52
|
+
result[key] = {
|
|
53
|
+
...value,
|
|
54
|
+
fields: applySchemaDefaults(value.fields, defaults)
|
|
55
|
+
};
|
|
56
|
+
} else if (typeof value === "object" && value !== null) {
|
|
57
|
+
result[key] = applySchemaDefaults(value, defaults);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
function applyDefaultsToField(field, defaults) {
|
|
63
|
+
const needsContext = defaults.context && !field.context;
|
|
64
|
+
const needsTags = defaults.tags && defaults.tags.length > 0;
|
|
65
|
+
if (!needsContext && !needsTags) return field;
|
|
66
|
+
return Object.freeze({
|
|
67
|
+
...field,
|
|
68
|
+
context: field.context ?? defaults.context ?? void 0,
|
|
69
|
+
docs: {
|
|
70
|
+
...field.docs,
|
|
71
|
+
tags: mergeTags(defaults.tags, field.docs.tags)
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function mergeTags(schemaTags, fieldTags) {
|
|
76
|
+
if (!schemaTags || schemaTags.length === 0) return fieldTags;
|
|
77
|
+
if (!fieldTags || fieldTags.length === 0) return schemaTags;
|
|
78
|
+
const merged = [...schemaTags];
|
|
79
|
+
for (const tag of fieldTags) {
|
|
80
|
+
if (!merged.includes(tag)) {
|
|
81
|
+
merged.push(tag);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return merged;
|
|
85
|
+
}
|
|
86
|
+
function deepFreeze(obj) {
|
|
87
|
+
const propNames = Object.getOwnPropertyNames(obj);
|
|
88
|
+
for (const name of propNames) {
|
|
89
|
+
const value = obj[name];
|
|
90
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
91
|
+
deepFreeze(value);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return Object.freeze(obj);
|
|
95
|
+
}
|
|
96
|
+
function extractFields(input) {
|
|
97
|
+
return isSchemaDefinition(input) ? input.fields : input;
|
|
98
|
+
}
|
|
99
|
+
function isSubGroup(value) {
|
|
100
|
+
return typeof value === "object" && value !== null && !isFieldDefinition(value) && !isSchemaDefinition(value);
|
|
101
|
+
}
|
|
102
|
+
function deepMergeConfigs(a, b) {
|
|
103
|
+
const result = { ...a };
|
|
104
|
+
for (const key of Object.keys(b)) {
|
|
105
|
+
const aVal = result[key];
|
|
106
|
+
const bVal = b[key];
|
|
107
|
+
if (isSubGroup(aVal) && isSubGroup(bVal)) {
|
|
108
|
+
result[key] = deepMergeConfigs(aVal, bVal);
|
|
109
|
+
} else {
|
|
110
|
+
result[key] = bVal;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
function mergeGraphQLSchemas(...schemas) {
|
|
116
|
+
const merged = schemas.map(extractFields).reduce(deepMergeConfigs);
|
|
117
|
+
return defineGraphQLSchema(merged);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export {
|
|
121
|
+
defineQuery,
|
|
122
|
+
defineMutation,
|
|
123
|
+
defineSubscription,
|
|
124
|
+
defineGraphQLSchema,
|
|
125
|
+
mergeGraphQLSchemas
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=chunk-TZRBETT3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presentation/graphql/field/define-field.ts","../src/presentation/graphql/field/define-schema.ts"],"sourcesContent":["/**\n * @fileoverview Factory functions for creating GraphQL field definitions.\n *\n * `defineQuery`, `defineMutation`, and `defineSubscription` are the main entry\n * points for defining GraphQL fields. They mirror `defineRoute` /\n * `defineEventHandler` from the HTTP and event layers.\n *\n * @module graphql/field/define-field\n */\n\nimport type { SchemaAdapter, InferOutput } from '../../http/schema/types';\nimport type { GraphQLFieldDefinition, GraphQLOperationType } from './types';\n\n// ============================================================================\n// Input Types\n// ============================================================================\n\n/**\n * Input for defineQuery / defineMutation.\n *\n * @example Query with input and output\n * ```typescript\n * defineQuery({\n * input: zodSchema(z.object({ userId: z.string() })),\n * output: zodSchema(userSchema),\n * docs: { description: 'Get a user by ID' },\n * })\n * ```\n *\n * @example Mutation with context\n * ```typescript\n * defineMutation({\n * input: zodSchema(z.object({ name: z.string() })),\n * output: zodSchema(z.object({ id: z.string() })),\n * context: zodSchema(executionContextSchema),\n * docs: { description: 'Create a project' },\n * })\n * ```\n */\ninterface DefineFieldInput<\n TInput extends SchemaAdapter | undefined = undefined,\n TOutput extends SchemaAdapter | undefined = undefined,\n TContext extends SchemaAdapter | undefined = undefined,\n> {\n /** Input arguments validation schema. */\n readonly input?: TInput;\n\n /** Output type validation schema. */\n readonly output?: TOutput;\n\n /** Context validation schema (e.g., JWT payload from middleware). */\n readonly context?: TContext;\n\n /** Field documentation. */\n readonly docs?: {\n readonly summary?: string;\n readonly description?: string;\n readonly tags?: readonly string[];\n readonly deprecated?: boolean;\n readonly deprecationReason?: string;\n };\n}\n\n// ============================================================================\n// Return type helpers\n// ============================================================================\n\ntype ResolveInput<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\ntype ResolveOutput<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\ntype ResolveContext<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\n\n// ============================================================================\n// Internal Factory\n// ============================================================================\n\nfunction createFieldDefinition<\n TOperation extends GraphQLOperationType,\n TInput extends SchemaAdapter | undefined = undefined,\n TOutput extends SchemaAdapter | undefined = undefined,\n TContext extends SchemaAdapter | undefined = undefined,\n>(\n operation: TOperation,\n input: DefineFieldInput<TInput, TOutput, TContext>,\n): GraphQLFieldDefinition<\n TOperation,\n ResolveInput<TInput>,\n ResolveOutput<TOutput>,\n ResolveContext<TContext>\n> {\n const definition = {\n operation,\n input: input.input ?? undefined,\n output: input.output ?? undefined,\n context: input.context ?? undefined,\n docs: {\n summary: input.docs?.summary,\n description: input.docs?.description,\n tags: input.docs?.tags,\n deprecated: input.docs?.deprecated ?? false,\n deprecationReason: input.docs?.deprecationReason,\n },\n _isGraphQLField: true as const,\n _types: undefined as unknown,\n };\n\n return Object.freeze(definition) as GraphQLFieldDefinition<\n TOperation,\n ResolveInput<TInput>,\n ResolveOutput<TOutput>,\n ResolveContext<TContext>\n >;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Creates a GraphQL query field definition.\n *\n * @param config - Field configuration with optional schemas\n * @returns A frozen GraphQLFieldDefinition with operation 'query'\n *\n * @example\n * ```typescript\n * const getUser = defineQuery({\n * input: zodSchema(z.object({ userId: z.string() })),\n * output: zodSchema(userSchema),\n * docs: { description: 'Get a user by ID' },\n * });\n * ```\n */\nexport function defineQuery<\n TInput extends SchemaAdapter | undefined = undefined,\n TOutput extends SchemaAdapter | undefined = undefined,\n TContext extends SchemaAdapter | undefined = undefined,\n>(\n config: DefineFieldInput<TInput, TOutput, TContext> = {},\n): GraphQLFieldDefinition<\n 'query',\n ResolveInput<TInput>,\n ResolveOutput<TOutput>,\n ResolveContext<TContext>\n> {\n return createFieldDefinition('query', config);\n}\n\n/**\n * Creates a GraphQL mutation field definition.\n *\n * @param config - Field configuration with optional schemas\n * @returns A frozen GraphQLFieldDefinition with operation 'mutation'\n *\n * @example\n * ```typescript\n * const createProject = defineMutation({\n * input: zodSchema(z.object({ name: z.string() })),\n * output: zodSchema(z.object({ id: z.string() })),\n * context: zodSchema(executionContextSchema),\n * docs: { description: 'Create a new project' },\n * });\n * ```\n */\nexport function defineMutation<\n TInput extends SchemaAdapter | undefined = undefined,\n TOutput extends SchemaAdapter | undefined = undefined,\n TContext extends SchemaAdapter | undefined = undefined,\n>(\n config: DefineFieldInput<TInput, TOutput, TContext> = {},\n): GraphQLFieldDefinition<\n 'mutation',\n ResolveInput<TInput>,\n ResolveOutput<TOutput>,\n ResolveContext<TContext>\n> {\n return createFieldDefinition('mutation', config);\n}\n\n/**\n * Creates a GraphQL subscription field definition.\n *\n * Subscription handlers return an `AsyncIterable` of values rather than\n * a single response. The output schema validates each emitted value.\n *\n * @param config - Field configuration with optional schemas\n * @returns A frozen GraphQLFieldDefinition with operation 'subscription'\n *\n * @example\n * ```typescript\n * const onTicketCreated = defineSubscription({\n * input: zodSchema(z.object({ projectId: z.string() })),\n * output: zodSchema(ticketSchema),\n * docs: { description: 'Subscribe to new tickets in a project' },\n * });\n * ```\n */\nexport function defineSubscription<\n TInput extends SchemaAdapter | undefined = undefined,\n TOutput extends SchemaAdapter | undefined = undefined,\n TContext extends SchemaAdapter | undefined = undefined,\n>(\n config: DefineFieldInput<TInput, TOutput, TContext> = {},\n): GraphQLFieldDefinition<\n 'subscription',\n ResolveInput<TInput>,\n ResolveOutput<TOutput>,\n ResolveContext<TContext>\n> {\n return createFieldDefinition('subscription', config);\n}\n","/**\n * @fileoverview Factory function for creating GraphQL schema definitions.\n *\n * The `defineGraphQLSchema` function groups fields into a hierarchical structure\n * with optional schema-level defaults for context and tags.\n *\n * @module graphql/field/define-schema\n */\n\nimport type {\n GraphQLSchemaConfig,\n GraphQLSchemaDefaults,\n GraphQLSchemaDefinition,\n GraphQLFieldDefinition,\n DeepMergeSchemas,\n DeepMergeSchemasAll,\n} from './types';\nimport { isFieldDefinition, isSchemaDefinition } from './types';\n\n/**\n * Options for schema definition.\n */\nexport interface DefineGraphQLSchemaOptions {\n /**\n * Default values applied to all child fields.\n *\n * @example\n * ```typescript\n * defineGraphQLSchema({\n * getUser: getUserQuery,\n * createUser: createUserMutation,\n * }, {\n * defaults: {\n * context: zodSchema(executionContextSchema),\n * tags: ['Users'],\n * },\n * })\n * ```\n */\n readonly defaults?: GraphQLSchemaDefaults;\n}\n\n/**\n * Creates a GraphQL schema definition from a configuration object.\n *\n * A schema is a hierarchical grouping of fields that provides:\n * - Organized API structure with nested namespaces\n * - Type-safe resolver registration\n * - Schema-level defaults for context and tags\n *\n * @param fields - Object containing fields and nested groups\n * @param options - Optional schema configuration\n * @returns A frozen GraphQLSchemaDefinition object\n *\n * @example Basic schema\n * ```typescript\n * const schema = defineGraphQLSchema({\n * users: {\n * get: getUserQuery,\n * list: listUsersQuery,\n * create: createUserMutation,\n * },\n * });\n * ```\n *\n * @example With defaults\n * ```typescript\n * const schema = defineGraphQLSchema({\n * get: getUserQuery,\n * list: listUsersQuery,\n * }, {\n * defaults: {\n * context: zodSchema(executionContextSchema),\n * tags: ['Users'],\n * },\n * });\n * ```\n */\nexport function defineGraphQLSchema<T extends GraphQLSchemaConfig>(\n fields: T,\n options?: DefineGraphQLSchemaOptions,\n): GraphQLSchemaDefinition<T> {\n const defaults = options?.defaults;\n\n // Apply defaults to fields if context or tags are provided\n const processedFields =\n defaults?.context || defaults?.tags ? (applySchemaDefaults(fields, defaults) as T) : fields;\n\n const definition: GraphQLSchemaDefinition<T> = {\n fields: processedFields,\n defaults,\n _isGraphQLSchema: true,\n };\n\n return deepFreeze(definition);\n}\n\n/**\n * Recursively applies schema-level defaults to all fields in the tree.\n */\nfunction applySchemaDefaults(\n config: GraphQLSchemaConfig,\n defaults: GraphQLSchemaDefaults,\n): GraphQLSchemaConfig {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(config)) {\n if (isFieldDefinition(value)) {\n result[key] = applyDefaultsToField(value, defaults);\n } else if (isSchemaDefinition(value)) {\n result[key] = {\n ...value,\n fields: applySchemaDefaults(value.fields, defaults),\n };\n } else if (typeof value === 'object' && value !== null) {\n result[key] = applySchemaDefaults(value as GraphQLSchemaConfig, defaults);\n }\n }\n\n return result as GraphQLSchemaConfig;\n}\n\n/**\n * Applies schema-level defaults to a single field definition.\n */\nfunction applyDefaultsToField(\n field: GraphQLFieldDefinition,\n defaults: GraphQLSchemaDefaults,\n): GraphQLFieldDefinition {\n const needsContext = defaults.context && !field.context;\n const needsTags = defaults.tags && defaults.tags.length > 0;\n\n if (!needsContext && !needsTags) return field;\n\n return Object.freeze({\n ...field,\n context: field.context ?? defaults.context ?? undefined,\n docs: {\n ...field.docs,\n tags: mergeTags(defaults.tags, field.docs.tags),\n },\n }) as GraphQLFieldDefinition;\n}\n\n/**\n * Merges schema-level tags with field-level tags, avoiding duplicates.\n */\nfunction mergeTags(\n schemaTags?: readonly string[],\n fieldTags?: readonly string[],\n): readonly string[] | undefined {\n if (!schemaTags || schemaTags.length === 0) return fieldTags;\n if (!fieldTags || fieldTags.length === 0) return schemaTags;\n\n const merged = [...schemaTags];\n for (const tag of fieldTags) {\n if (!merged.includes(tag)) {\n merged.push(tag);\n }\n }\n return merged;\n}\n\n/**\n * Deep freezes an object and all its nested objects.\n */\nfunction deepFreeze<T extends object>(obj: T): T {\n const propNames = Object.getOwnPropertyNames(obj) as (keyof T)[];\n\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// mergeGraphQLSchemas — variadic deep merge\n// ============================================================================\n\ntype SchemaInput<T extends GraphQLSchemaConfig> = T | GraphQLSchemaDefinition<T>;\n\n/** Extracts the raw GraphQLSchemaConfig from either a plain config or a GraphQLSchemaDefinition. */\nfunction extractFields<T extends GraphQLSchemaConfig>(input: SchemaInput<T>): T {\n return isSchemaDefinition(input) ? input.fields : input;\n}\n\n/** Returns true if `value` is a plain sub-group object (not a FieldDefinition, not a SchemaDefinition). */\nfunction isSubGroup(value: unknown): value is GraphQLSchemaConfig {\n return (\n typeof value === 'object' &&\n value !== null &&\n !isFieldDefinition(value) &&\n !isSchemaDefinition(value)\n );\n}\n\n/** Recursively deep-merges two schema configs. Sub-groups are merged; leaves are overwritten. */\nfunction deepMergeConfigs(a: GraphQLSchemaConfig, b: GraphQLSchemaConfig): GraphQLSchemaConfig {\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 (isSubGroup(aVal) && isSubGroup(bVal)) {\n result[key] = deepMergeConfigs(aVal, bVal);\n } else {\n result[key] = bVal;\n }\n }\n\n return result as GraphQLSchemaConfig;\n}\n\n// Overloads for 2–8 schemas (clean IDE experience)\nexport function mergeGraphQLSchemas<T1 extends GraphQLSchemaConfig, T2 extends GraphQLSchemaConfig>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n): GraphQLSchemaDefinition<DeepMergeSchemas<T1, T2>>;\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n): GraphQLSchemaDefinition<DeepMergeSchemasAll<[T1, T2, T3]>>;\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n T4 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n s4: SchemaInput<T4>,\n): GraphQLSchemaDefinition<DeepMergeSchemasAll<[T1, T2, T3, T4]>>;\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n T4 extends GraphQLSchemaConfig,\n T5 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n s4: SchemaInput<T4>,\n s5: SchemaInput<T5>,\n): GraphQLSchemaDefinition<DeepMergeSchemasAll<[T1, T2, T3, T4, T5]>>;\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n T4 extends GraphQLSchemaConfig,\n T5 extends GraphQLSchemaConfig,\n T6 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n s4: SchemaInput<T4>,\n s5: SchemaInput<T5>,\n s6: SchemaInput<T6>,\n): GraphQLSchemaDefinition<DeepMergeSchemasAll<[T1, T2, T3, T4, T5, T6]>>;\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n T4 extends GraphQLSchemaConfig,\n T5 extends GraphQLSchemaConfig,\n T6 extends GraphQLSchemaConfig,\n T7 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n s4: SchemaInput<T4>,\n s5: SchemaInput<T5>,\n s6: SchemaInput<T6>,\n s7: SchemaInput<T7>,\n): GraphQLSchemaDefinition<DeepMergeSchemasAll<[T1, T2, T3, T4, T5, T6, T7]>>;\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n T4 extends GraphQLSchemaConfig,\n T5 extends GraphQLSchemaConfig,\n T6 extends GraphQLSchemaConfig,\n T7 extends GraphQLSchemaConfig,\n T8 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n s4: SchemaInput<T4>,\n s5: SchemaInput<T5>,\n s6: SchemaInput<T6>,\n s7: SchemaInput<T7>,\n s8: SchemaInput<T8>,\n): GraphQLSchemaDefinition<DeepMergeSchemasAll<[T1, T2, T3, T4, T5, T6, T7, T8]>>;\n// Add overloads for 9-12 following the same pattern as define-router.ts\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n T4 extends GraphQLSchemaConfig,\n T5 extends GraphQLSchemaConfig,\n T6 extends GraphQLSchemaConfig,\n T7 extends GraphQLSchemaConfig,\n T8 extends GraphQLSchemaConfig,\n T9 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n s4: SchemaInput<T4>,\n s5: SchemaInput<T5>,\n s6: SchemaInput<T6>,\n s7: SchemaInput<T7>,\n s8: SchemaInput<T8>,\n s9: SchemaInput<T9>,\n): GraphQLSchemaDefinition<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<DeepMergeSchemas<DeepMergeSchemas<T1, T2>, T3>, T4>,\n T5\n >,\n T6\n >,\n T7\n >,\n T8\n >,\n T9\n >\n>;\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n T4 extends GraphQLSchemaConfig,\n T5 extends GraphQLSchemaConfig,\n T6 extends GraphQLSchemaConfig,\n T7 extends GraphQLSchemaConfig,\n T8 extends GraphQLSchemaConfig,\n T9 extends GraphQLSchemaConfig,\n T10 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n s4: SchemaInput<T4>,\n s5: SchemaInput<T5>,\n s6: SchemaInput<T6>,\n s7: SchemaInput<T7>,\n s8: SchemaInput<T8>,\n s9: SchemaInput<T9>,\n s10: SchemaInput<T10>,\n): GraphQLSchemaDefinition<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<DeepMergeSchemas<DeepMergeSchemas<T1, T2>, T3>, T4>,\n T5\n >,\n T6\n >,\n T7\n >,\n T8\n >,\n T9\n >,\n T10\n >\n>;\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n T4 extends GraphQLSchemaConfig,\n T5 extends GraphQLSchemaConfig,\n T6 extends GraphQLSchemaConfig,\n T7 extends GraphQLSchemaConfig,\n T8 extends GraphQLSchemaConfig,\n T9 extends GraphQLSchemaConfig,\n T10 extends GraphQLSchemaConfig,\n T11 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n s4: SchemaInput<T4>,\n s5: SchemaInput<T5>,\n s6: SchemaInput<T6>,\n s7: SchemaInput<T7>,\n s8: SchemaInput<T8>,\n s9: SchemaInput<T9>,\n s10: SchemaInput<T10>,\n s11: SchemaInput<T11>,\n): GraphQLSchemaDefinition<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<DeepMergeSchemas<DeepMergeSchemas<T1, T2>, T3>, T4>,\n T5\n >,\n T6\n >,\n T7\n >,\n T8\n >,\n T9\n >,\n T10\n >,\n T11\n >\n>;\nexport function mergeGraphQLSchemas<\n T1 extends GraphQLSchemaConfig,\n T2 extends GraphQLSchemaConfig,\n T3 extends GraphQLSchemaConfig,\n T4 extends GraphQLSchemaConfig,\n T5 extends GraphQLSchemaConfig,\n T6 extends GraphQLSchemaConfig,\n T7 extends GraphQLSchemaConfig,\n T8 extends GraphQLSchemaConfig,\n T9 extends GraphQLSchemaConfig,\n T10 extends GraphQLSchemaConfig,\n T11 extends GraphQLSchemaConfig,\n T12 extends GraphQLSchemaConfig,\n>(\n s1: SchemaInput<T1>,\n s2: SchemaInput<T2>,\n s3: SchemaInput<T3>,\n s4: SchemaInput<T4>,\n s5: SchemaInput<T5>,\n s6: SchemaInput<T6>,\n s7: SchemaInput<T7>,\n s8: SchemaInput<T8>,\n s9: SchemaInput<T9>,\n s10: SchemaInput<T10>,\n s11: SchemaInput<T11>,\n s12: SchemaInput<T12>,\n): GraphQLSchemaDefinition<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<\n DeepMergeSchemas<DeepMergeSchemas<DeepMergeSchemas<T1, T2>, T3>, T4>,\n T5\n >,\n T6\n >,\n T7\n >,\n T8\n >,\n T9\n >,\n T10\n >,\n T11\n >,\n T12\n >\n>;\n\n// Variadic fallback for 13+\nexport function mergeGraphQLSchemas(\n ...schemas: SchemaInput<GraphQLSchemaConfig>[]\n): GraphQLSchemaDefinition<GraphQLSchemaConfig>;\n\n// Implementation\nexport function mergeGraphQLSchemas(\n ...schemas: SchemaInput<GraphQLSchemaConfig>[]\n): GraphQLSchemaDefinition<GraphQLSchemaConfig> {\n const merged = schemas.map(extractFields).reduce(deepMergeConfigs);\n return defineGraphQLSchema(merged);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +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 (v2).\n *\n * The `defineRoute` function is the main entry point for defining routes.\n * Request schemas are grouped under a `request` field, while responses\n * are defined via the `responses` field with per-status-code config.\n *\n * Each schema field accepts either a bare `SchemaAdapter` or an object with\n * metadata: `{ schema, description?, contentType?, required? }`.\n *\n * The success response type is inferred from the first 2xx status with a schema.\n *\n * @module unified/route/define-route\n */\n\nimport type { SchemaAdapter, InferOutput } from '../schema/types';\nimport { isSchemaAdapter } from '../schema/types';\nimport type {\n HttpMethod,\n RouteDefinition,\n PathParams,\n SchemaFieldInput,\n RouteFieldMeta,\n ResponseFieldConfig,\n ResponsesDefinition,\n ExtractSuccessSchema,\n} from './types';\n\n// ============================================================================\n// Input Types\n// ============================================================================\n\n/**\n * Complete input for defineRoute.\n *\n * @example GET with query and response\n * ```typescript\n * defineRoute({\n * method: 'GET',\n * path: '/api/users',\n * request: {\n * query: zodSchema(z.object({ page: z.coerce.number().default(1) })),\n * },\n * responses: {\n * 200: { schema: zodSchema(userListSchema) },\n * },\n * docs: { summary: 'List users', tags: ['Users'] },\n * })\n * ```\n *\n * @example POST with body and multiple statuses\n * ```typescript\n * defineRoute({\n * method: 'POST',\n * path: '/api/users',\n * request: {\n * body: zodSchema(createUserSchema),\n * },\n * responses: {\n * 201: { schema: zodSchema(userSchema), description: 'Created' },\n * 400: { description: 'Validation error' },\n * 409: { description: 'Email already in use' },\n * },\n * })\n * ```\n *\n * @example DELETE with 204\n * ```typescript\n * defineRoute({\n * method: 'DELETE',\n * path: '/api/users/:userId',\n * responses: {\n * 204: { description: 'User deleted' },\n * 404: { description: 'User not found' },\n * },\n * })\n * ```\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 ResponsesDefinition | undefined = undefined,\n> {\n /** HTTP method for this route. */\n readonly method: TMethod;\n\n /** URL path pattern with optional parameters (`:param` or `{param}`). */\n readonly path: TPath;\n\n /** Request schemas (body, query, params, headers, context). */\n readonly request?: {\n /** Request body schema (or `{ schema, description?, contentType?, required? }`). */\n readonly body?: TBody extends SchemaAdapter ? SchemaFieldInput<TBody> : TBody;\n\n /** Query parameters schema (or `{ schema, description? }`). */\n readonly query?: TQuery extends SchemaAdapter ? SchemaFieldInput<TQuery> : TQuery;\n\n /** Path parameters schema (or `{ schema, description? }`). If omitted, inferred from path template. */\n readonly params?: TParams extends SchemaAdapter ? SchemaFieldInput<TParams> : TParams;\n\n /** Headers schema (or `{ schema, description? }`). */\n readonly headers?: THeaders extends SchemaAdapter ? SchemaFieldInput<THeaders> : THeaders;\n\n /** Context schema (or `{ schema, description? }`). */\n readonly context?: TContext extends SchemaAdapter ? SchemaFieldInput<TContext> : TContext;\n };\n\n /**\n * Per-status response definitions.\n *\n * Each key is an HTTP status code. Each value can have:\n * - `schema` — response body schema (drives type inference for 2xx)\n * - `description` — OpenAPI response description\n * - `contentType` — response content type (default: `application/json`)\n */\n readonly responses?: TResponses;\n\n /** OpenAPI documentation. */\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// Helpers\n// ============================================================================\n\n/**\n * Extracts the SchemaAdapter from a field that may be bare or wrapped in config.\n */\nfunction extractSchema<T extends SchemaAdapter>(\n field: SchemaFieldInput<T> | undefined,\n): T | undefined {\n if (field == null) return undefined;\n if (isSchemaAdapter(field)) return field as T;\n return (field as { schema: T }).schema;\n}\n\n/**\n * Extracts metadata from a field config, if it was passed as an object.\n */\nfunction extractMeta(\n field: SchemaFieldInput | undefined,\n): { description?: string; contentType?: string; required?: boolean } | undefined {\n if (field == null || isSchemaAdapter(field)) return undefined;\n const config = field as { description?: string; contentType?: string; required?: boolean };\n if (config.description == null && config.contentType == null && config.required == null) {\n return undefined;\n }\n return {\n description: config.description,\n contentType: config.contentType,\n required: config.required,\n };\n}\n\n/**\n * Normalizes responses record keys to strings.\n */\nfunction normalizeResponses(\n responses: Record<string | number, ResponseFieldConfig>,\n): Record<string, ResponseFieldConfig> {\n const result: Record<string, ResponseFieldConfig> = {};\n for (const [key, value] of Object.entries(responses)) {\n result[String(key)] = value;\n }\n return result;\n}\n\n// ============================================================================\n// Return type helpers\n// ============================================================================\n\ntype ResolveBody<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\ntype ResolveQuery<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\ntype ResolveParams<T, TPath extends string> = T extends SchemaAdapter\n ? InferOutput<T>\n : PathParams<TPath>;\ntype ResolveHeaders<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\ntype ResolveContext<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\n\ntype ResolveResponse<T> = T extends ResponsesDefinition\n ? ExtractSuccessSchema<T> extends SchemaAdapter\n ? InferOutput<ExtractSuccessSchema<T>>\n : undefined\n : undefined;\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. The resulting definition\n * can be used for type-safe client generation, server-side route registration,\n * and OpenAPI specification generation.\n *\n * @param input - Route configuration with request schemas and responses\n * @returns A frozen RouteDefinition object with full type information\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 ResponsesDefinition | undefined = undefined,\n>(\n input: DefineRouteInput<TMethod, TPath, TBody, TQuery, TParams, THeaders, TContext, TResponses>,\n): RouteDefinition<\n TMethod,\n TPath,\n ResolveBody<TBody>,\n ResolveQuery<TQuery>,\n ResolveParams<TParams, TPath>,\n ResolveHeaders<THeaders>,\n ResolveContext<TContext>,\n ResolveResponse<TResponses>\n> {\n const req = input.request;\n\n // Build _meta from any config-wrapped fields\n const bodyMeta = extractMeta(req?.body as SchemaFieldInput | undefined);\n const queryMeta = extractMeta(req?.query as SchemaFieldInput | undefined);\n const paramsMeta = extractMeta(req?.params as SchemaFieldInput | undefined);\n const headersMeta = extractMeta(req?.headers as SchemaFieldInput | undefined);\n const contextMeta = extractMeta(req?.context as SchemaFieldInput | undefined);\n\n const _meta: RouteFieldMeta = {\n ...(bodyMeta ? { body: bodyMeta } : {}),\n ...(queryMeta ? { query: { description: queryMeta.description } } : {}),\n ...(paramsMeta ? { params: { description: paramsMeta.description } } : {}),\n ...(headersMeta ? { headers: { description: headersMeta.description } } : {}),\n ...(contextMeta ? { context: { description: contextMeta.description } } : {}),\n };\n\n const definition = {\n method: input.method,\n path: input.path,\n request: {\n body: extractSchema(req?.body as SchemaFieldInput | undefined) ?? undefined,\n query: extractSchema(req?.query as SchemaFieldInput | undefined) ?? undefined,\n params: extractSchema(req?.params as SchemaFieldInput | undefined) ?? undefined,\n headers: extractSchema(req?.headers as SchemaFieldInput | undefined) ?? undefined,\n context: extractSchema(req?.context as SchemaFieldInput | undefined) ?? undefined,\n },\n responses: input.responses ? normalizeResponses(input.responses) : undefined,\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 _meta,\n _types: undefined as unknown,\n };\n\n return Object.freeze(definition) as RouteDefinition<\n TMethod,\n TPath,\n ResolveBody<TBody>,\n ResolveQuery<TQuery>,\n ResolveParams<TParams, TPath>,\n ResolveHeaders<THeaders>,\n ResolveContext<TContext>,\n ResolveResponse<TResponses>\n >;\n}\n","/**\n * @fileoverview Factory function for creating router definitions.\n *\n * The `defineRouter` function groups routes into a hierarchical structure\n * with optional router-level defaults for context and tags.\n *\n * @module unified/route/define-router\n */\n\nimport type {\n RouterConfig,\n RouterDefaults,\n RouterDefinition,\n RouteDefinition,\n DeepMergeTwo,\n DeepMergeAll,\n} 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 readonly basePath?: string;\n\n /**\n * Default values applied to all child routes.\n *\n * @example\n * ```typescript\n * defineRouter({\n * list: listUsersRoute,\n * get: getUserRoute,\n * }, {\n * defaults: {\n * context: zodSchema(executionContextSchema),\n * tags: ['Users'],\n * },\n * })\n * ```\n */\n readonly defaults?: RouterDefaults;\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 * - Router-level defaults for context and tags\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 * const api = defineRouter({\n * users: {\n * list: listUsersRoute,\n * get: getUserRoute,\n * create: createUserRoute,\n * },\n * });\n * ```\n *\n * @example With router-level defaults\n * ```typescript\n * const api = defineRouter({\n * list: listUsersRoute,\n * get: getUserRoute,\n * }, {\n * basePath: '/api/v1',\n * defaults: {\n * context: zodSchema(executionContextSchema),\n * tags: ['Users'],\n * },\n * });\n * // Context is applied to all routes that don't define their own.\n * // Tags are merged with each route's existing tags.\n * ```\n */\nexport function defineRouter<T extends RouterConfig>(\n routes: T,\n options?: DefineRouterOptions,\n): RouterDefinition<T> {\n const defaults = options?.defaults;\n\n // Apply defaults to routes if context or tags are provided\n const processedRoutes =\n defaults?.context || defaults?.tags ? (applyRouterDefaults(routes, defaults) as T) : routes;\n\n const definition: RouterDefinition<T> = {\n routes: processedRoutes,\n basePath: options?.basePath,\n defaults,\n _isRouter: true,\n };\n\n // Deep freeze the router definition\n return deepFreeze(definition);\n}\n\n/**\n * Recursively applies router-level defaults to all routes in the tree.\n */\nfunction applyRouterDefaults(routes: RouterConfig, defaults: RouterDefaults): RouterConfig {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(routes)) {\n if (isRouteDefinition(value)) {\n result[key] = applyDefaultsToRoute(value, defaults);\n } else if (isRouterDefinition(value)) {\n result[key] = {\n ...value,\n routes: applyRouterDefaults(value.routes, defaults),\n };\n } else if (typeof value === 'object' && value !== null) {\n result[key] = applyRouterDefaults(value as RouterConfig, defaults);\n }\n }\n\n return result as RouterConfig;\n}\n\n/**\n * Applies router-level defaults to a single route definition.\n */\nfunction applyDefaultsToRoute(route: RouteDefinition, defaults: RouterDefaults): RouteDefinition {\n const needsContext = defaults.context && !route.request.context;\n const needsTags = defaults.tags && defaults.tags.length > 0;\n\n if (!needsContext && !needsTags) return route;\n\n return Object.freeze({\n ...route,\n request: {\n ...route.request,\n context: route.request.context ?? defaults.context ?? undefined,\n },\n docs: {\n ...route.docs,\n tags: mergeTags(defaults.tags, route.docs.tags),\n },\n }) as RouteDefinition;\n}\n\n/**\n * Merges router-level tags with route-level tags, avoiding duplicates.\n */\nfunction mergeTags(\n routerTags?: readonly string[],\n routeTags?: readonly string[],\n): readonly string[] | undefined {\n if (!routerTags || routerTags.length === 0) return routeTags;\n if (!routeTags || routeTags.length === 0) return routerTags;\n\n // Merge, preserving order: router tags first, then route-specific tags (no duplicates)\n const merged = [...routerTags];\n for (const tag of routeTags) {\n if (!merged.includes(tag)) {\n merged.push(tag);\n }\n }\n return merged;\n}\n\n/**\n * Deep freezes an object and all its nested objects.\n */\nfunction deepFreeze<T extends object>(obj: T): T {\n const propNames = Object.getOwnPropertyNames(obj) as (keyof T)[];\n\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)\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":"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","names":[]}
|
|
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 (v2).\n *\n * The `defineRoute` function is the main entry point for defining routes.\n * Request schemas are grouped under a `request` field, while responses\n * are defined via the `responses` field with per-status-code config.\n *\n * Each schema field accepts either a bare `SchemaAdapter` or an object with\n * metadata: `{ schema, description?, contentType?, required? }`.\n *\n * The success response type is inferred from the first 2xx status with a schema.\n *\n * @module unified/route/define-route\n */\n\nimport type { SchemaAdapter, InferOutput } from '../schema/types';\nimport { isSchemaAdapter } from '../schema/types';\nimport type {\n HttpMethod,\n RouteDefinition,\n PathParams,\n SchemaFieldInput,\n RouteFieldMeta,\n ResponseFieldConfig,\n ResponsesDefinition,\n ExtractSuccessSchema,\n} from './types';\n\n// ============================================================================\n// Input Types\n// ============================================================================\n\n/**\n * Complete input for defineRoute.\n *\n * @example GET with query and response\n * ```typescript\n * defineRoute({\n * method: 'GET',\n * path: '/api/users',\n * request: {\n * query: zodSchema(z.object({ page: z.coerce.number().default(1) })),\n * },\n * responses: {\n * 200: { schema: zodSchema(userListSchema) },\n * },\n * docs: { summary: 'List users', tags: ['Users'] },\n * })\n * ```\n *\n * @example POST with body and multiple statuses\n * ```typescript\n * defineRoute({\n * method: 'POST',\n * path: '/api/users',\n * request: {\n * body: zodSchema(createUserSchema),\n * },\n * responses: {\n * 201: { schema: zodSchema(userSchema), description: 'Created' },\n * 400: { description: 'Validation error' },\n * 409: { description: 'Email already in use' },\n * },\n * })\n * ```\n *\n * @example DELETE with 204\n * ```typescript\n * defineRoute({\n * method: 'DELETE',\n * path: '/api/users/:userId',\n * responses: {\n * 204: { description: 'User deleted' },\n * 404: { description: 'User not found' },\n * },\n * })\n * ```\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 ResponsesDefinition | undefined = undefined,\n> {\n /** HTTP method for this route. */\n readonly method: TMethod;\n\n /** URL path pattern with optional parameters (`:param` or `{param}`). */\n readonly path: TPath;\n\n /** Request schemas (body, query, params, headers, context). */\n readonly request?: {\n /** Request body schema (or `{ schema, description?, contentType?, required? }`). */\n readonly body?: TBody extends SchemaAdapter ? SchemaFieldInput<TBody> : TBody;\n\n /** Query parameters schema (or `{ schema, description? }`). */\n readonly query?: TQuery extends SchemaAdapter ? SchemaFieldInput<TQuery> : TQuery;\n\n /** Path parameters schema (or `{ schema, description? }`). If omitted, inferred from path template. */\n readonly params?: TParams extends SchemaAdapter ? SchemaFieldInput<TParams> : TParams;\n\n /** Headers schema (or `{ schema, description? }`). */\n readonly headers?: THeaders extends SchemaAdapter ? SchemaFieldInput<THeaders> : THeaders;\n\n /** Context schema (or `{ schema, description? }`). */\n readonly context?: TContext extends SchemaAdapter ? SchemaFieldInput<TContext> : TContext;\n };\n\n /**\n * Per-status response definitions.\n *\n * Each key is an HTTP status code. Each value can have:\n * - `schema` — response body schema (drives type inference for 2xx)\n * - `description` — OpenAPI response description\n * - `contentType` — response content type (default: `application/json`)\n */\n readonly responses?: TResponses;\n\n /** OpenAPI documentation. */\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// Helpers\n// ============================================================================\n\n/**\n * Extracts the SchemaAdapter from a field that may be bare or wrapped in config.\n */\nfunction extractSchema<T extends SchemaAdapter>(\n field: SchemaFieldInput<T> | undefined,\n): T | undefined {\n if (field == null) return undefined;\n if (isSchemaAdapter(field)) return field as T;\n return (field as { schema: T }).schema;\n}\n\n/**\n * Extracts metadata from a field config, if it was passed as an object.\n */\nfunction extractMeta(\n field: SchemaFieldInput | undefined,\n): { description?: string; contentType?: string; required?: boolean } | undefined {\n if (field == null || isSchemaAdapter(field)) return undefined;\n const config = field as { description?: string; contentType?: string; required?: boolean };\n if (config.description == null && config.contentType == null && config.required == null) {\n return undefined;\n }\n return {\n description: config.description,\n contentType: config.contentType,\n required: config.required,\n };\n}\n\n/**\n * Normalizes responses record keys to strings.\n */\nfunction normalizeResponses(\n responses: Record<string | number, ResponseFieldConfig>,\n): Record<string, ResponseFieldConfig> {\n const result: Record<string, ResponseFieldConfig> = {};\n for (const [key, value] of Object.entries(responses)) {\n result[String(key)] = value;\n }\n return result;\n}\n\n// ============================================================================\n// Return type helpers\n// ============================================================================\n\ntype ResolveBody<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\ntype ResolveQuery<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\ntype ResolveParams<T, TPath extends string> = T extends SchemaAdapter\n ? InferOutput<T>\n : PathParams<TPath>;\ntype ResolveHeaders<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\ntype ResolveContext<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\n\ntype ResolveResponse<T> = T extends ResponsesDefinition\n ? ExtractSuccessSchema<T> extends SchemaAdapter\n ? InferOutput<ExtractSuccessSchema<T>>\n : undefined\n : undefined;\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. The resulting definition\n * can be used for type-safe client generation, server-side route registration,\n * and OpenAPI specification generation.\n *\n * @param input - Route configuration with request schemas and responses\n * @returns A frozen RouteDefinition object with full type information\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 ResponsesDefinition | undefined = undefined,\n>(\n input: DefineRouteInput<TMethod, TPath, TBody, TQuery, TParams, THeaders, TContext, TResponses>,\n): RouteDefinition<\n TMethod,\n TPath,\n ResolveBody<TBody>,\n ResolveQuery<TQuery>,\n ResolveParams<TParams, TPath>,\n ResolveHeaders<THeaders>,\n ResolveContext<TContext>,\n ResolveResponse<TResponses>\n> {\n const req = input.request;\n\n // Build _meta from any config-wrapped fields\n const bodyMeta = extractMeta(req?.body as SchemaFieldInput | undefined);\n const queryMeta = extractMeta(req?.query as SchemaFieldInput | undefined);\n const paramsMeta = extractMeta(req?.params as SchemaFieldInput | undefined);\n const headersMeta = extractMeta(req?.headers as SchemaFieldInput | undefined);\n const contextMeta = extractMeta(req?.context as SchemaFieldInput | undefined);\n\n const _meta: RouteFieldMeta = {\n ...(bodyMeta ? { body: bodyMeta } : {}),\n ...(queryMeta ? { query: { description: queryMeta.description } } : {}),\n ...(paramsMeta ? { params: { description: paramsMeta.description } } : {}),\n ...(headersMeta ? { headers: { description: headersMeta.description } } : {}),\n ...(contextMeta ? { context: { description: contextMeta.description } } : {}),\n };\n\n const definition = {\n method: input.method,\n path: input.path,\n request: {\n body: extractSchema(req?.body as SchemaFieldInput | undefined) ?? undefined,\n query: extractSchema(req?.query as SchemaFieldInput | undefined) ?? undefined,\n params: extractSchema(req?.params as SchemaFieldInput | undefined) ?? undefined,\n headers: extractSchema(req?.headers as SchemaFieldInput | undefined) ?? undefined,\n context: extractSchema(req?.context as SchemaFieldInput | undefined) ?? undefined,\n },\n responses: input.responses ? normalizeResponses(input.responses) : undefined,\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 _meta,\n _types: undefined as unknown,\n };\n\n return Object.freeze(definition) as RouteDefinition<\n TMethod,\n TPath,\n ResolveBody<TBody>,\n ResolveQuery<TQuery>,\n ResolveParams<TParams, TPath>,\n ResolveHeaders<THeaders>,\n ResolveContext<TContext>,\n ResolveResponse<TResponses>\n >;\n}\n","/**\n * @fileoverview Factory function for creating router definitions.\n *\n * The `defineRouter` function groups routes into a hierarchical structure\n * with optional router-level defaults for context and tags.\n *\n * @module unified/route/define-router\n */\n\nimport type {\n RouterConfig,\n RouterDefaults,\n RouterDefinition,\n RouteDefinition,\n DeepMergeTwo,\n DeepMergeAll,\n} 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 readonly basePath?: string;\n\n /**\n * Default values applied to all child routes.\n *\n * @example\n * ```typescript\n * defineRouter({\n * list: listUsersRoute,\n * get: getUserRoute,\n * }, {\n * defaults: {\n * context: zodSchema(executionContextSchema),\n * tags: ['Users'],\n * },\n * })\n * ```\n */\n readonly defaults?: RouterDefaults;\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 * - Router-level defaults for context and tags\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 * const api = defineRouter({\n * users: {\n * list: listUsersRoute,\n * get: getUserRoute,\n * create: createUserRoute,\n * },\n * });\n * ```\n *\n * @example With router-level defaults\n * ```typescript\n * const api = defineRouter({\n * list: listUsersRoute,\n * get: getUserRoute,\n * }, {\n * basePath: '/api/v1',\n * defaults: {\n * context: zodSchema(executionContextSchema),\n * tags: ['Users'],\n * },\n * });\n * // Context is applied to all routes that don't define their own.\n * // Tags are merged with each route's existing tags.\n * ```\n */\nexport function defineRouter<T extends RouterConfig>(\n routes: T,\n options?: DefineRouterOptions,\n): RouterDefinition<T> {\n const defaults = options?.defaults;\n\n // Apply defaults to routes if context or tags are provided\n const processedRoutes =\n defaults?.context || defaults?.tags ? (applyRouterDefaults(routes, defaults) as T) : routes;\n\n const definition: RouterDefinition<T> = {\n routes: processedRoutes,\n basePath: options?.basePath,\n defaults,\n _isRouter: true,\n };\n\n // Deep freeze the router definition\n return deepFreeze(definition);\n}\n\n/**\n * Recursively applies router-level defaults to all routes in the tree.\n */\nfunction applyRouterDefaults(routes: RouterConfig, defaults: RouterDefaults): RouterConfig {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(routes)) {\n if (isRouteDefinition(value)) {\n result[key] = applyDefaultsToRoute(value, defaults);\n } else if (isRouterDefinition(value)) {\n result[key] = {\n ...value,\n routes: applyRouterDefaults(value.routes, defaults),\n };\n } else if (typeof value === 'object' && value !== null) {\n result[key] = applyRouterDefaults(value as RouterConfig, defaults);\n }\n }\n\n return result as RouterConfig;\n}\n\n/**\n * Applies router-level defaults to a single route definition.\n */\nfunction applyDefaultsToRoute(route: RouteDefinition, defaults: RouterDefaults): RouteDefinition {\n const needsContext = defaults.context && !route.request.context;\n const needsTags = defaults.tags && defaults.tags.length > 0;\n\n if (!needsContext && !needsTags) return route;\n\n return Object.freeze({\n ...route,\n request: {\n ...route.request,\n context: route.request.context ?? defaults.context ?? undefined,\n },\n docs: {\n ...route.docs,\n tags: mergeTags(defaults.tags, route.docs.tags),\n },\n }) as RouteDefinition;\n}\n\n/**\n * Merges router-level tags with route-level tags, avoiding duplicates.\n */\nfunction mergeTags(\n routerTags?: readonly string[],\n routeTags?: readonly string[],\n): readonly string[] | undefined {\n if (!routerTags || routerTags.length === 0) return routeTags;\n if (!routeTags || routeTags.length === 0) return routerTags;\n\n // Merge, preserving order: router tags first, then route-specific tags (no duplicates)\n const merged = [...routerTags];\n for (const tag of routeTags) {\n if (!merged.includes(tag)) {\n merged.push(tag);\n }\n }\n return merged;\n}\n\n/**\n * Deep freezes an object and all its nested objects.\n */\nfunction deepFreeze<T extends object>(obj: T): T {\n const propNames = Object.getOwnPropertyNames(obj) as (keyof T)[];\n\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)\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\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 T9 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 r9: RouterInput<T9>,\n): RouterDefinition<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<DeepMergeTwo<DeepMergeTwo<DeepMergeTwo<T1, T2>, T3>, T4>, T5>,\n T6\n >,\n T7\n >,\n T8\n >,\n T9\n >\n>;\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 T9 extends RouterConfig,\n T10 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 r9: RouterInput<T9>,\n r10: RouterInput<T10>,\n): RouterDefinition<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<DeepMergeTwo<DeepMergeTwo<DeepMergeTwo<T1, T2>, T3>, T4>, T5>,\n T6\n >,\n T7\n >,\n T8\n >,\n T9\n >,\n T10\n >\n>;\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 T9 extends RouterConfig,\n T10 extends RouterConfig,\n T11 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 r9: RouterInput<T9>,\n r10: RouterInput<T10>,\n r11: RouterInput<T11>,\n): RouterDefinition<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<DeepMergeTwo<DeepMergeTwo<DeepMergeTwo<T1, T2>, T3>, T4>, T5>,\n T6\n >,\n T7\n >,\n T8\n >,\n T9\n >,\n T10\n >,\n T11\n >\n>;\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 T9 extends RouterConfig,\n T10 extends RouterConfig,\n T11 extends RouterConfig,\n T12 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 r9: RouterInput<T9>,\n r10: RouterInput<T10>,\n r11: RouterInput<T11>,\n r12: RouterInput<T12>,\n): RouterDefinition<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<\n DeepMergeTwo<DeepMergeTwo<DeepMergeTwo<DeepMergeTwo<T1, T2>, T3>, T4>, T5>,\n T6\n >,\n T7\n >,\n T8\n >,\n T9\n >,\n T10\n >,\n T11\n >,\n T12\n >\n>;\n\n// Variadic fallback for 13+\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":"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","names":[]}
|