@cosmneo/onion-lasagna 0.2.0 → 0.3.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.
Files changed (93) hide show
  1. package/dist/chunk-36IO4BM7.js +113 -0
  2. package/dist/chunk-36IO4BM7.js.map +1 -0
  3. package/dist/chunk-4BVOLXDJ.js +54 -0
  4. package/dist/chunk-4BVOLXDJ.js.map +1 -0
  5. package/dist/{chunk-GGSAAZPM.js → chunk-AUMHMWDD.js} +19 -20
  6. package/dist/chunk-AUMHMWDD.js.map +1 -0
  7. package/dist/chunk-CBTICRSM.js +34 -0
  8. package/dist/chunk-CBTICRSM.js.map +1 -0
  9. package/dist/chunk-H5TNDC5U.js +138 -0
  10. package/dist/chunk-H5TNDC5U.js.map +1 -0
  11. package/dist/chunk-KJ4JGZOE.js +96 -0
  12. package/dist/chunk-KJ4JGZOE.js.map +1 -0
  13. package/dist/chunk-MF2JDREK.js +168 -0
  14. package/dist/chunk-MF2JDREK.js.map +1 -0
  15. package/dist/chunk-RVSBIYY4.js +1 -0
  16. package/dist/chunk-RVSBIYY4.js.map +1 -0
  17. package/dist/{chunk-PUVAB3JX.js → chunk-XIRJ73IO.js} +38 -36
  18. package/dist/chunk-XIRJ73IO.js.map +1 -0
  19. package/dist/{chunk-DS7TE6KZ.js → chunk-XP6PLTV2.js} +11 -3
  20. package/dist/chunk-XP6PLTV2.js.map +1 -0
  21. package/dist/chunk-XWKHOLIP.js +191 -0
  22. package/dist/chunk-XWKHOLIP.js.map +1 -0
  23. package/dist/event-router-definition.type-CP9uTlux.d.cts +150 -0
  24. package/dist/event-router-definition.type-D8CG-kjZ.d.ts +150 -0
  25. package/dist/events/asyncapi/index.cjs +143 -0
  26. package/dist/events/asyncapi/index.cjs.map +1 -0
  27. package/dist/events/asyncapi/index.d.cts +184 -0
  28. package/dist/events/asyncapi/index.d.ts +184 -0
  29. package/dist/events/asyncapi/index.js +8 -0
  30. package/dist/events/asyncapi/index.js.map +1 -0
  31. package/dist/events/handler/index.cjs +171 -0
  32. package/dist/events/handler/index.cjs.map +1 -0
  33. package/dist/events/handler/index.d.cts +153 -0
  34. package/dist/events/handler/index.d.ts +153 -0
  35. package/dist/events/handler/index.js +21 -0
  36. package/dist/events/handler/index.js.map +1 -0
  37. package/dist/events/index.cjs +497 -0
  38. package/dist/events/index.cjs.map +1 -0
  39. package/dist/events/index.d.cts +9 -0
  40. package/dist/events/index.d.ts +9 -0
  41. package/dist/events/index.js +41 -0
  42. package/dist/events/index.js.map +1 -0
  43. package/dist/events/server/index.cjs +291 -0
  44. package/dist/events/server/index.cjs.map +1 -0
  45. package/dist/events/server/index.d.cts +281 -0
  46. package/dist/events/server/index.d.ts +281 -0
  47. package/dist/events/server/index.js +16 -0
  48. package/dist/events/server/index.js.map +1 -0
  49. package/dist/events/shared/index.cjs +85 -0
  50. package/dist/events/shared/index.cjs.map +1 -0
  51. package/dist/events/shared/index.d.cts +35 -0
  52. package/dist/events/shared/index.d.ts +35 -0
  53. package/dist/events/shared/index.js +13 -0
  54. package/dist/events/shared/index.js.map +1 -0
  55. package/dist/global.js +3 -3
  56. package/dist/http/index.cjs +563 -93
  57. package/dist/http/index.cjs.map +1 -1
  58. package/dist/http/index.d.cts +5 -3
  59. package/dist/http/index.d.ts +5 -3
  60. package/dist/http/index.js +30 -12
  61. package/dist/http/openapi/index.cjs +43 -35
  62. package/dist/http/openapi/index.cjs.map +1 -1
  63. package/dist/http/openapi/index.d.cts +8 -34
  64. package/dist/http/openapi/index.d.ts +8 -34
  65. package/dist/http/openapi/index.js +2 -2
  66. package/dist/http/route/index.cjs +106 -9
  67. package/dist/http/route/index.cjs.map +1 -1
  68. package/dist/http/route/index.d.cts +133 -227
  69. package/dist/http/route/index.d.ts +133 -227
  70. package/dist/http/route/index.js +5 -2
  71. package/dist/http/server/index.cjs +24 -19
  72. package/dist/http/server/index.cjs.map +1 -1
  73. package/dist/http/server/index.d.cts +4 -261
  74. package/dist/http/server/index.d.ts +4 -261
  75. package/dist/http/server/index.js +2 -2
  76. package/dist/http/shared/index.cjs.map +1 -1
  77. package/dist/http/shared/index.d.cts +10 -14
  78. package/dist/http/shared/index.d.ts +10 -14
  79. package/dist/http/shared/index.js +11 -127
  80. package/dist/http/shared/index.js.map +1 -1
  81. package/dist/index.js +6 -6
  82. package/dist/{router-definition.type-ynBhT16T.d.cts → router-definition.type-BElX-Pl4.d.cts} +169 -256
  83. package/dist/{router-definition.type-DORVlLNk.d.ts → router-definition.type-DxG8ncJZ.d.ts} +169 -256
  84. package/dist/types-B6Q1iCgf.d.ts +262 -0
  85. package/dist/types-afYpL7Ap.d.cts +262 -0
  86. package/dist/types-cke61juH.d.cts +42 -0
  87. package/dist/types-cke61juH.d.ts +42 -0
  88. package/package.json +35 -2
  89. package/dist/chunk-BZULBF4N.js +0 -82
  90. package/dist/chunk-BZULBF4N.js.map +0 -1
  91. package/dist/chunk-DS7TE6KZ.js.map +0 -1
  92. package/dist/chunk-GGSAAZPM.js.map +0 -1
  93. package/dist/chunk-PUVAB3JX.js.map +0 -1
@@ -0,0 +1,113 @@
1
+ import {
2
+ isEventHandlerDefinition,
3
+ isEventRouterDefinition
4
+ } from "./chunk-CBTICRSM.js";
5
+
6
+ // src/presentation/events/handler/define-event-handler.ts
7
+ function defineEventHandler(input) {
8
+ const definition = {
9
+ eventType: input.eventType,
10
+ payload: input.payload ?? void 0,
11
+ context: input.context ?? void 0,
12
+ docs: {
13
+ summary: input.docs?.summary,
14
+ description: input.docs?.description,
15
+ tags: input.docs?.tags,
16
+ deprecated: input.docs?.deprecated ?? false
17
+ },
18
+ _types: void 0
19
+ };
20
+ return Object.freeze(definition);
21
+ }
22
+
23
+ // src/presentation/events/handler/define-event-router.ts
24
+ function defineEventRouter(handlers, options) {
25
+ const defaults = options?.defaults;
26
+ const processedHandlers = defaults?.context || defaults?.tags ? applyEventRouterDefaults(handlers, defaults) : handlers;
27
+ const definition = {
28
+ handlers: processedHandlers,
29
+ defaults,
30
+ _isEventRouter: true
31
+ };
32
+ return deepFreeze(definition);
33
+ }
34
+ function applyEventRouterDefaults(handlers, defaults) {
35
+ const result = {};
36
+ for (const [key, value] of Object.entries(handlers)) {
37
+ if (isEventHandlerDefinition(value)) {
38
+ result[key] = applyDefaultsToEventHandler(value, defaults);
39
+ } else if (isEventRouterDefinition(value)) {
40
+ result[key] = {
41
+ ...value,
42
+ handlers: applyEventRouterDefaults(value.handlers, defaults)
43
+ };
44
+ } else if (typeof value === "object" && value !== null) {
45
+ result[key] = applyEventRouterDefaults(value, defaults);
46
+ }
47
+ }
48
+ return result;
49
+ }
50
+ function applyDefaultsToEventHandler(handler, defaults) {
51
+ const needsContext = defaults.context && !handler.context;
52
+ const needsTags = defaults.tags && defaults.tags.length > 0;
53
+ if (!needsContext && !needsTags) return handler;
54
+ return Object.freeze({
55
+ ...handler,
56
+ context: handler.context ?? defaults.context ?? void 0,
57
+ docs: {
58
+ ...handler.docs,
59
+ tags: mergeTags(defaults.tags, handler.docs.tags)
60
+ }
61
+ });
62
+ }
63
+ function mergeTags(routerTags, handlerTags) {
64
+ if (!routerTags || routerTags.length === 0) return handlerTags;
65
+ if (!handlerTags || handlerTags.length === 0) return routerTags;
66
+ const merged = [...routerTags];
67
+ for (const tag of handlerTags) {
68
+ if (!merged.includes(tag)) {
69
+ merged.push(tag);
70
+ }
71
+ }
72
+ return merged;
73
+ }
74
+ function deepFreeze(obj) {
75
+ const propNames = Object.getOwnPropertyNames(obj);
76
+ for (const name of propNames) {
77
+ const value = obj[name];
78
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
79
+ deepFreeze(value);
80
+ }
81
+ }
82
+ return Object.freeze(obj);
83
+ }
84
+ function extractHandlers(input) {
85
+ return isEventRouterDefinition(input) ? input.handlers : input;
86
+ }
87
+ function isSubRouter(value) {
88
+ return typeof value === "object" && value !== null && !isEventHandlerDefinition(value) && !isEventRouterDefinition(value);
89
+ }
90
+ function deepMergeConfigs(a, b) {
91
+ const result = { ...a };
92
+ for (const key of Object.keys(b)) {
93
+ const aVal = result[key];
94
+ const bVal = b[key];
95
+ if (isSubRouter(aVal) && isSubRouter(bVal)) {
96
+ result[key] = deepMergeConfigs(aVal, bVal);
97
+ } else {
98
+ result[key] = bVal;
99
+ }
100
+ }
101
+ return result;
102
+ }
103
+ function mergeEventRouters(...routers) {
104
+ const merged = routers.map(extractHandlers).reduce(deepMergeConfigs);
105
+ return defineEventRouter(merged);
106
+ }
107
+
108
+ export {
109
+ defineEventHandler,
110
+ defineEventRouter,
111
+ mergeEventRouters
112
+ };
113
+ //# sourceMappingURL=chunk-36IO4BM7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presentation/events/handler/define-event-handler.ts","../src/presentation/events/handler/define-event-router.ts"],"sourcesContent":["/**\n * @fileoverview Factory function for creating event handler definitions.\n *\n * The `defineEventHandler` function is the main entry point for defining\n * event handlers. It mirrors `defineRoute` from the HTTP layer but is\n * tailored for asynchronous event processing.\n *\n * @module events/handler/define-event-handler\n */\n\nimport type { SchemaAdapter, InferOutput } from '../../http/schema/types';\nimport type { EventHandlerDefinition } from './types';\n\n// ============================================================================\n// Input Types\n// ============================================================================\n\n/**\n * Input for defineEventHandler.\n *\n * @example Basic handler with payload schema\n * ```typescript\n * defineEventHandler({\n * eventType: 'ticket.created',\n * payload: zodSchema(z.object({ ticketId: z.string() })),\n * docs: { summary: 'Handle ticket creation', tags: ['ticket'] },\n * })\n * ```\n *\n * @example With context schema\n * ```typescript\n * defineEventHandler({\n * eventType: 'order.shipped',\n * payload: zodSchema(orderShippedSchema),\n * context: zodSchema(eventMetadataSchema),\n * })\n * ```\n */\ninterface DefineEventHandlerInput<\n TEventType extends string,\n TPayload extends SchemaAdapter | undefined = undefined,\n TContext extends SchemaAdapter | undefined = undefined,\n> {\n /** Event type string used for routing (e.g., 'ticket.created'). */\n readonly eventType: TEventType;\n\n /** Payload validation schema. */\n readonly payload?: TPayload;\n\n /** Context validation schema (validates event metadata). */\n readonly context?: TContext;\n\n /** Handler documentation. */\n readonly docs?: {\n readonly summary?: string;\n readonly description?: string;\n readonly tags?: readonly string[];\n readonly deprecated?: boolean;\n };\n}\n\n// ============================================================================\n// Return type helpers\n// ============================================================================\n\ntype ResolvePayload<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\ntype ResolveContext<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;\n\n// ============================================================================\n// Factory Function\n// ============================================================================\n\n/**\n * Creates an event handler definition from the provided configuration.\n *\n * This is the main entry point for defining event handlers. The resulting\n * definition can be used for type-safe handler registration, payload\n * validation, and documentation generation.\n *\n * @param input - Event handler configuration with optional schemas\n * @returns A frozen EventHandlerDefinition object with full type information\n */\nexport function defineEventHandler<\n TEventType extends string,\n TPayload extends SchemaAdapter | undefined = undefined,\n TContext extends SchemaAdapter | undefined = undefined,\n>(\n input: DefineEventHandlerInput<TEventType, TPayload, TContext>,\n): EventHandlerDefinition<TEventType, ResolvePayload<TPayload>, ResolveContext<TContext>> {\n const definition = {\n eventType: input.eventType,\n payload: input.payload ?? 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 },\n _types: undefined as unknown,\n };\n\n return Object.freeze(definition) as EventHandlerDefinition<\n TEventType,\n ResolvePayload<TPayload>,\n ResolveContext<TContext>\n >;\n}\n","/**\n * @fileoverview Factory function for creating event router definitions.\n *\n * The `defineEventRouter` function groups event handlers into a hierarchical\n * structure with optional router-level defaults for context and tags.\n *\n * @module events/handler/define-event-router\n */\n\nimport type {\n EventRouterConfig,\n EventRouterDefaults,\n EventRouterDefinition,\n EventHandlerDefinition,\n DeepMergeTwo,\n DeepMergeAll,\n} from './types';\nimport { isEventHandlerDefinition, isEventRouterDefinition } from './types';\n\n/**\n * Options for event router definition.\n */\nexport interface DefineEventRouterOptions {\n /**\n * Default values applied to all child handlers.\n *\n * @example\n * ```typescript\n * defineEventRouter({\n * created: onTicketCreated,\n * assigned: onTicketAssigned,\n * }, {\n * defaults: {\n * context: zodSchema(eventMetadataSchema),\n * tags: ['ticket'],\n * },\n * })\n * ```\n */\n readonly defaults?: EventRouterDefaults;\n}\n\n/**\n * Creates an event router definition from a configuration object.\n *\n * @param handlers - Object containing event handlers and nested routers\n * @param options - Optional router configuration\n * @returns A frozen EventRouterDefinition object\n *\n * @example Basic router\n * ```typescript\n * const ticketEvents = defineEventRouter({\n * created: onTicketCreated,\n * assigned: onTicketAssigned,\n * transferred: onTicketTransferred,\n * });\n * ```\n *\n * @example Nested router\n * ```typescript\n * const events = defineEventRouter({\n * ticket: {\n * created: onTicketCreated,\n * assigned: onTicketAssigned,\n * },\n * ecosystem: {\n * memberAdded: onMemberAdded,\n * },\n * });\n * ```\n */\nexport function defineEventRouter<T extends EventRouterConfig>(\n handlers: T,\n options?: DefineEventRouterOptions,\n): EventRouterDefinition<T> {\n const defaults = options?.defaults;\n\n const processedHandlers =\n defaults?.context || defaults?.tags\n ? (applyEventRouterDefaults(handlers, defaults) as T)\n : handlers;\n\n const definition: EventRouterDefinition<T> = {\n handlers: processedHandlers,\n defaults,\n _isEventRouter: true,\n };\n\n return deepFreeze(definition);\n}\n\n/**\n * Recursively applies router-level defaults to all handlers in the tree.\n */\nfunction applyEventRouterDefaults(\n handlers: EventRouterConfig,\n defaults: EventRouterDefaults,\n): EventRouterConfig {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(handlers)) {\n if (isEventHandlerDefinition(value)) {\n result[key] = applyDefaultsToEventHandler(value, defaults);\n } else if (isEventRouterDefinition(value)) {\n result[key] = {\n ...value,\n handlers: applyEventRouterDefaults(value.handlers, defaults),\n };\n } else if (typeof value === 'object' && value !== null) {\n result[key] = applyEventRouterDefaults(value as EventRouterConfig, defaults);\n }\n }\n\n return result as EventRouterConfig;\n}\n\n/**\n * Applies router-level defaults to a single event handler definition.\n */\nfunction applyDefaultsToEventHandler(\n handler: EventHandlerDefinition,\n defaults: EventRouterDefaults,\n): EventHandlerDefinition {\n const needsContext = defaults.context && !handler.context;\n const needsTags = defaults.tags && defaults.tags.length > 0;\n\n if (!needsContext && !needsTags) return handler;\n\n return Object.freeze({\n ...handler,\n context: handler.context ?? defaults.context ?? undefined,\n docs: {\n ...handler.docs,\n tags: mergeTags(defaults.tags, handler.docs.tags),\n },\n }) as EventHandlerDefinition;\n}\n\n/**\n * Merges router-level tags with handler-level tags, avoiding duplicates.\n */\nfunction mergeTags(\n routerTags?: readonly string[],\n handlerTags?: readonly string[],\n): readonly string[] | undefined {\n if (!routerTags || routerTags.length === 0) return handlerTags;\n if (!handlerTags || handlerTags.length === 0) return routerTags;\n\n const merged = [...routerTags];\n for (const tag of handlerTags) {\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// mergeEventRouters — variadic deep merge\n// ============================================================================\n\ntype EventRouterInput<T extends EventRouterConfig> = T | EventRouterDefinition<T>;\n\n/** Extracts the raw EventRouterConfig from either a plain config or an EventRouterDefinition. */\nfunction extractHandlers<T extends EventRouterConfig>(input: EventRouterInput<T>): T {\n return isEventRouterDefinition(input) ? input.handlers : input;\n}\n\n/** Returns true if `value` is a plain sub-router object. */\nfunction isSubRouter(value: unknown): value is EventRouterConfig {\n return (\n typeof value === 'object' &&\n value !== null &&\n !isEventHandlerDefinition(value) &&\n !isEventRouterDefinition(value)\n );\n}\n\n/** Recursively deep-merges two router configs. Sub-routers are merged; leaves are overwritten. */\nfunction deepMergeConfigs(a: EventRouterConfig, b: EventRouterConfig): EventRouterConfig {\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 EventRouterConfig;\n}\n\n// Overloads for 2–8 routers\nexport function mergeEventRouters<T1 extends EventRouterConfig, T2 extends EventRouterConfig>(\n r1: EventRouterInput<T1>,\n r2: EventRouterInput<T2>,\n): EventRouterDefinition<DeepMergeTwo<T1, T2>>;\nexport function mergeEventRouters<\n T1 extends EventRouterConfig,\n T2 extends EventRouterConfig,\n T3 extends EventRouterConfig,\n>(\n r1: EventRouterInput<T1>,\n r2: EventRouterInput<T2>,\n r3: EventRouterInput<T3>,\n): EventRouterDefinition<DeepMergeAll<[T1, T2, T3]>>;\nexport function mergeEventRouters<\n T1 extends EventRouterConfig,\n T2 extends EventRouterConfig,\n T3 extends EventRouterConfig,\n T4 extends EventRouterConfig,\n>(\n r1: EventRouterInput<T1>,\n r2: EventRouterInput<T2>,\n r3: EventRouterInput<T3>,\n r4: EventRouterInput<T4>,\n): EventRouterDefinition<DeepMergeAll<[T1, T2, T3, T4]>>;\nexport function mergeEventRouters<\n T1 extends EventRouterConfig,\n T2 extends EventRouterConfig,\n T3 extends EventRouterConfig,\n T4 extends EventRouterConfig,\n T5 extends EventRouterConfig,\n>(\n r1: EventRouterInput<T1>,\n r2: EventRouterInput<T2>,\n r3: EventRouterInput<T3>,\n r4: EventRouterInput<T4>,\n r5: EventRouterInput<T5>,\n): EventRouterDefinition<DeepMergeAll<[T1, T2, T3, T4, T5]>>;\nexport function mergeEventRouters<\n T1 extends EventRouterConfig,\n T2 extends EventRouterConfig,\n T3 extends EventRouterConfig,\n T4 extends EventRouterConfig,\n T5 extends EventRouterConfig,\n T6 extends EventRouterConfig,\n>(\n r1: EventRouterInput<T1>,\n r2: EventRouterInput<T2>,\n r3: EventRouterInput<T3>,\n r4: EventRouterInput<T4>,\n r5: EventRouterInput<T5>,\n r6: EventRouterInput<T6>,\n): EventRouterDefinition<DeepMergeAll<[T1, T2, T3, T4, T5, T6]>>;\nexport function mergeEventRouters<\n T1 extends EventRouterConfig,\n T2 extends EventRouterConfig,\n T3 extends EventRouterConfig,\n T4 extends EventRouterConfig,\n T5 extends EventRouterConfig,\n T6 extends EventRouterConfig,\n T7 extends EventRouterConfig,\n>(\n r1: EventRouterInput<T1>,\n r2: EventRouterInput<T2>,\n r3: EventRouterInput<T3>,\n r4: EventRouterInput<T4>,\n r5: EventRouterInput<T5>,\n r6: EventRouterInput<T6>,\n r7: EventRouterInput<T7>,\n): EventRouterDefinition<DeepMergeAll<[T1, T2, T3, T4, T5, T6, T7]>>;\nexport function mergeEventRouters<\n T1 extends EventRouterConfig,\n T2 extends EventRouterConfig,\n T3 extends EventRouterConfig,\n T4 extends EventRouterConfig,\n T5 extends EventRouterConfig,\n T6 extends EventRouterConfig,\n T7 extends EventRouterConfig,\n T8 extends EventRouterConfig,\n>(\n r1: EventRouterInput<T1>,\n r2: EventRouterInput<T2>,\n r3: EventRouterInput<T3>,\n r4: EventRouterInput<T4>,\n r5: EventRouterInput<T5>,\n r6: EventRouterInput<T6>,\n r7: EventRouterInput<T7>,\n r8: EventRouterInput<T8>,\n): EventRouterDefinition<DeepMergeAll<[T1, T2, T3, T4, T5, T6, T7, T8]>>;\n\n// Variadic fallback for 9+\nexport function mergeEventRouters(\n ...routers: EventRouterInput<EventRouterConfig>[]\n): EventRouterDefinition<EventRouterConfig>;\n\n// Implementation\nexport function mergeEventRouters(\n ...routers: EventRouterInput<EventRouterConfig>[]\n): EventRouterDefinition<EventRouterConfig> {\n const merged = routers.map(extractHandlers).reduce(deepMergeConfigs);\n return defineEventRouter(merged);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,54 @@
1
+ import {
2
+ isErrorType
3
+ } from "./chunk-H5TNDC5U.js";
4
+
5
+ // src/presentation/events/shared/error-mapping.ts
6
+ var DLQ_ERROR_TYPES = [
7
+ "ObjectValidationError",
8
+ "InvalidRequestError",
9
+ "UseCaseError",
10
+ "DomainError",
11
+ "UnprocessableError",
12
+ "AccessDeniedError",
13
+ "ForbiddenError",
14
+ "UnauthorizedError",
15
+ "InvariantViolationError"
16
+ ];
17
+ var RETRY_ERROR_TYPES = [
18
+ "NotFoundError",
19
+ "ConflictError",
20
+ "InfraError",
21
+ "DbError",
22
+ "NetworkError",
23
+ "TimeoutError",
24
+ "ExternalServiceError",
25
+ "PersistenceError"
26
+ ];
27
+ function mapErrorToEventResult(error) {
28
+ for (const typeName of DLQ_ERROR_TYPES) {
29
+ if (isErrorType(error, typeName)) {
30
+ return {
31
+ outcome: "dlq",
32
+ reason: error.message
33
+ };
34
+ }
35
+ }
36
+ for (const typeName of RETRY_ERROR_TYPES) {
37
+ if (isErrorType(error, typeName)) {
38
+ return {
39
+ outcome: "retry",
40
+ reason: error.message
41
+ };
42
+ }
43
+ }
44
+ const message = error instanceof Error ? error.message : "Unknown error occurred during event processing";
45
+ return {
46
+ outcome: "retry",
47
+ reason: message
48
+ };
49
+ }
50
+
51
+ export {
52
+ mapErrorToEventResult
53
+ };
54
+ //# sourceMappingURL=chunk-4BVOLXDJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presentation/events/shared/error-mapping.ts"],"sourcesContent":["/**\n * @fileoverview Error-to-EventResult mapping for event handlers.\n *\n * Maps caught errors to event processing outcomes (ack/retry/dlq).\n * Uses the same name-based type checking as the HTTP error mapper\n * for compatibility with bundled/minified code.\n *\n * @module events/shared/error-mapping\n */\n\nimport { isErrorType } from '../../http/shared/error-mapping';\nimport type { EventResult } from './types';\n\n// ============================================================================\n// Error Type Classifications\n// ============================================================================\n\n/**\n * Error types that indicate permanent failures (send to DLQ).\n * These errors will not resolve on retry.\n */\nconst DLQ_ERROR_TYPES = [\n 'ObjectValidationError',\n 'InvalidRequestError',\n 'UseCaseError',\n 'DomainError',\n 'UnprocessableError',\n 'AccessDeniedError',\n 'ForbiddenError',\n 'UnauthorizedError',\n 'InvariantViolationError',\n];\n\n/**\n * Error types that indicate transient failures (retry).\n * These errors may resolve on subsequent attempts.\n */\nconst RETRY_ERROR_TYPES = [\n 'NotFoundError',\n 'ConflictError',\n 'InfraError',\n 'DbError',\n 'NetworkError',\n 'TimeoutError',\n 'ExternalServiceError',\n 'PersistenceError',\n];\n\n// ============================================================================\n// Mapping Function\n// ============================================================================\n\n/**\n * Maps a caught error to an EventResult.\n *\n * Default mapping strategy:\n *\n * | Error Type | Outcome | Rationale |\n * |------------------------------------------------|---------|----------------------------------------|\n * | ObjectValidationError, InvalidRequestError | dlq | Bad payload — retrying won't help |\n * | UseCaseError | dlq | Business rule rejection — permanent |\n * | DomainError, InvariantViolationError | dlq | Domain invariant — permanent |\n * | UnprocessableError | dlq | Valid but not processable — permanent |\n * | AccessDeniedError, ForbiddenError, Unauthorized | dlq | Permission — permanent |\n * | NotFoundError | retry | Entity might not exist yet |\n * | ConflictError | retry | Concurrent write — may resolve |\n * | InfraError, DbError, NetworkError, TimeoutError | retry | Infrastructure/transient |\n * | Unknown | retry | Conservative — don't lose messages |\n *\n * @param error - The caught error\n * @returns An EventResult indicating how the message should be handled\n */\nexport function mapErrorToEventResult(error: unknown): EventResult {\n // Check DLQ errors first (permanent failures)\n for (const typeName of DLQ_ERROR_TYPES) {\n if (isErrorType(error, typeName)) {\n return {\n outcome: 'dlq',\n reason: (error as { message: string }).message,\n };\n }\n }\n\n // Check retry errors (transient failures)\n for (const typeName of RETRY_ERROR_TYPES) {\n if (isErrorType(error, typeName)) {\n return {\n outcome: 'retry',\n reason: (error as { message: string }).message,\n };\n }\n }\n\n // Unknown errors — retry conservatively to avoid losing messages\n const message =\n error instanceof Error ? error.message : 'Unknown error occurred during event processing';\n\n return {\n outcome: 'retry',\n reason: message,\n };\n}\n"],"mappings":";;;;;AAqBA,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAMA,IAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA0BO,SAAS,sBAAsB,OAA6B;AAEjE,aAAW,YAAY,iBAAiB;AACtC,QAAI,YAAY,OAAO,QAAQ,GAAG;AAChC,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAS,MAA8B;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAGA,aAAW,YAAY,mBAAmB;AACxC,QAAI,YAAY,OAAO,QAAQ,GAAG;AAChC,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAS,MAA8B;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,UACJ,iBAAiB,QAAQ,MAAM,UAAU;AAE3C,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACF;","names":[]}
@@ -8,9 +8,10 @@ import {
8
8
  } from "./chunk-ZG26OQFN.js";
9
9
  import {
10
10
  collectRoutes,
11
+ generateOperationId,
11
12
  isRouterDefinition,
12
13
  normalizePath
13
- } from "./chunk-DS7TE6KZ.js";
14
+ } from "./chunk-XP6PLTV2.js";
14
15
 
15
16
  // src/presentation/http/server/types.ts
16
17
  function isSimpleHandlerConfig(config) {
@@ -39,7 +40,7 @@ function createServerRoutesInternal(router, handlers, options) {
39
40
  `Missing handler for route "${key}". All routes must have a handler configuration.`
40
41
  );
41
42
  }
42
- result.push(createRouteHandler(route, handlerConfig, resolvedOptions));
43
+ result.push(createRouteHandler(key, route, handlerConfig, resolvedOptions));
43
44
  }
44
45
  return result;
45
46
  }
@@ -64,7 +65,7 @@ function sortRoutesBySpecificity(routes) {
64
65
  return 0;
65
66
  });
66
67
  }
67
- function createRouteHandler(route, config, options) {
68
+ function createRouteHandler(key, route, config, options) {
68
69
  const middleware = config.middleware ?? [];
69
70
  const globalMiddleware = options?.middleware ?? [];
70
71
  const allMiddleware = [...globalMiddleware, ...middleware];
@@ -74,7 +75,7 @@ function createRouteHandler(route, config, options) {
74
75
  method: route.method,
75
76
  path: normalizePath(route.path),
76
77
  metadata: {
77
- operationId: route.docs.operationId,
78
+ operationId: route.docs.operationId ?? generateOperationId(key),
78
79
  summary: route.docs.summary,
79
80
  description: route.docs.description,
80
81
  tags: route.docs.tags,
@@ -82,7 +83,7 @@ function createRouteHandler(route, config, options) {
82
83
  },
83
84
  handler: async (rawRequest, ctx) => {
84
85
  const rawContext = options?.createContext ? options.createContext(rawRequest) : ctx ?? { requestId: generateRequestId() };
85
- const validatedContext = route.request.context?.schema ? wrapError(
86
+ const validatedContext = route.request.context ? wrapError(
86
87
  () => {
87
88
  const result = validateContextData(route, rawContext);
88
89
  if (!result.success) {
@@ -186,8 +187,8 @@ function createRouteHandler(route, config, options) {
186
187
  function validateRequestData(route, rawRequest) {
187
188
  const errors = [];
188
189
  const data = {};
189
- if (route.request.body?.schema) {
190
- const result = route.request.body.schema.validate(rawRequest.body);
190
+ if (route.request.body) {
191
+ const result = route.request.body.validate(rawRequest.body);
191
192
  if (result.success) {
192
193
  data.body = result.data;
193
194
  } else {
@@ -199,9 +200,9 @@ function validateRequestData(route, rawRequest) {
199
200
  );
200
201
  }
201
202
  }
202
- if (route.request.query?.schema) {
203
+ if (route.request.query) {
203
204
  const queryObj = normalizeQuery(rawRequest.query);
204
- const result = route.request.query.schema.validate(queryObj);
205
+ const result = route.request.query.validate(queryObj);
205
206
  if (result.success) {
206
207
  data.query = result.data;
207
208
  } else {
@@ -213,8 +214,8 @@ function validateRequestData(route, rawRequest) {
213
214
  );
214
215
  }
215
216
  }
216
- if (route.request.params?.schema) {
217
- const result = route.request.params.schema.validate(rawRequest.params ?? {});
217
+ if (route.request.params) {
218
+ const result = route.request.params.validate(rawRequest.params ?? {});
218
219
  if (result.success) {
219
220
  data.pathParams = result.data;
220
221
  } else {
@@ -228,9 +229,9 @@ function validateRequestData(route, rawRequest) {
228
229
  } else {
229
230
  data.pathParams = normalizePathParams(rawRequest.params);
230
231
  }
231
- if (route.request.headers?.schema) {
232
+ if (route.request.headers) {
232
233
  const headersObj = normalizeHeaders(rawRequest.headers);
233
- const result = route.request.headers.schema.validate(headersObj);
234
+ const result = route.request.headers.validate(headersObj);
234
235
  if (result.success) {
235
236
  data.headers = result.data;
236
237
  } else {
@@ -248,13 +249,11 @@ function validateRequestData(route, rawRequest) {
248
249
  return { success: true, data };
249
250
  }
250
251
  function validateResponseData(route, response) {
251
- const statusCode = String(response.status);
252
- const responses = route.responses;
253
- const responseConfig = responses[statusCode];
254
- if (!responseConfig) {
252
+ if (!route.responses) {
255
253
  return { success: true };
256
254
  }
257
- const schema = responseConfig.schema;
255
+ const entry = route.responses[String(response.status)];
256
+ const schema = entry?.schema;
258
257
  if (!schema) {
259
258
  return { success: true };
260
259
  }
@@ -269,7 +268,7 @@ function validateResponseData(route, response) {
269
268
  return { success: false, errors };
270
269
  }
271
270
  function validateContextData(route, context) {
272
- const contextSchema = route.request.context?.schema;
271
+ const contextSchema = route.request.context;
273
272
  if (!contextSchema) {
274
273
  return { success: true, data: context };
275
274
  }
@@ -397,4 +396,4 @@ function serverRoutes(router) {
397
396
  export {
398
397
  serverRoutes
399
398
  };
400
- //# sourceMappingURL=chunk-GGSAAZPM.js.map
399
+ //# sourceMappingURL=chunk-AUMHMWDD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presentation/http/server/types.ts","../src/presentation/http/server/create-server-routes.ts","../src/presentation/http/server/server-routes-builder.ts"],"sourcesContent":["/**\n * @fileoverview Server types for the unified route system.\n *\n * @module unified/server/types\n */\n\nimport type {\n HttpMethod,\n RouteDefinition,\n RouterConfig,\n RouterKeys,\n GetRoute,\n} from '../route/types';\n\n// ============================================================================\n// Validated Request\n// ============================================================================\n\n/**\n * A validated request with typed data.\n * This is what handlers receive after validation passes.\n */\nexport interface ValidatedRequest<TRoute extends RouteDefinition> {\n /**\n * Validated request body.\n */\n readonly body: TRoute['_types']['body'];\n\n /**\n * Validated query parameters.\n */\n readonly query: TRoute['_types']['query'];\n\n /**\n * Validated path parameters.\n */\n readonly pathParams: TRoute['_types']['pathParams'];\n\n /**\n * Validated headers.\n */\n readonly headers: TRoute['_types']['headers'];\n\n /**\n * Raw request object for advanced use cases.\n */\n readonly raw: {\n readonly method: string;\n readonly url: string;\n readonly headers: Record<string, string>;\n };\n}\n\n/**\n * Typed context based on route definition.\n * If the route defines a context schema, this will be the validated type.\n * Otherwise, it falls back to the generic HandlerContext.\n */\nexport type TypedContext<TRoute extends RouteDefinition> =\n TRoute['_types']['context'] extends undefined ? HandlerContext : TRoute['_types']['context'];\n\n// ============================================================================\n// Handler Types\n// ============================================================================\n\n/**\n * Context passed to handlers.\n * Can be extended with custom context via serverRoutes options.\n */\nexport interface HandlerContext {\n /**\n * Request ID for tracing.\n */\n readonly requestId?: string;\n\n /**\n * Additional context data.\n */\n readonly [key: string]: unknown;\n}\n\n/**\n * Response from a handler.\n */\nexport interface HandlerResponse<TData = unknown> {\n /**\n * HTTP status code.\n */\n readonly status: number;\n\n /**\n * Response body.\n */\n readonly body?: TData;\n\n /**\n * Response headers.\n */\n readonly headers?: Record<string, string>;\n}\n\n// ============================================================================\n// Use Case Port\n// ============================================================================\n\n/**\n * Use case port interface for unified routes.\n *\n * This is a simplified version that accepts any input/output types (plain objects).\n * It's structurally compatible with `BaseInboundPort`, so existing use case\n * implementations work without changes.\n *\n * @typeParam TInput - Input type (plain object or void for no input)\n * @typeParam TOutput - Output type (plain object or void for no output)\n *\n * @example\n * ```typescript\n * // Define plain types for use case contracts\n * type CreateProjectInput = {\n * name: string;\n * description?: string;\n * };\n *\n * type CreateProjectOutput = {\n * projectId: string;\n * };\n *\n * // Use case implements this interface\n * class CreateProjectUseCase implements UseCasePort<CreateProjectInput, CreateProjectOutput> {\n * async execute(input: CreateProjectInput): Promise<CreateProjectOutput> {\n * // ... implementation\n * return { projectId: '...' };\n * }\n * }\n * ```\n */\n\nexport interface UseCasePort<TInput = void, TOutput = void> {\n execute(input?: TInput): Promise<TOutput>;\n}\n\n// ============================================================================\n// Server Configuration\n// ============================================================================\n\n/**\n * Handler configuration for a single route.\n *\n * Mirrors the BaseController pattern with three components:\n * - `requestMapper`: Maps validated HTTP request to use case input\n * - `useCase`: The use case to execute\n * - `responseMapper`: Maps use case output to HTTP response\n *\n * @typeParam TRoute - The route definition type\n * @typeParam TInput - Use case input type (plain object)\n * @typeParam TOutput - Use case output type (plain object)\n *\n * @example\n * ```typescript\n * const config: RouteHandlerConfig<typeof createProjectRoute, CreateProjectInput, CreateProjectOutput> = {\n * requestMapper: (req) => ({\n * name: req.body.name,\n * description: req.body.description,\n * }),\n * useCase: createProjectUseCase,\n * responseMapper: (out) => ({\n * status: 201,\n * body: { projectId: out.projectId },\n * }),\n * };\n * ```\n */\n\nexport interface RouteHandlerConfig<TRoute extends RouteDefinition, TInput = void, TOutput = void> {\n /**\n * Maps the validated HTTP request to use case input.\n * The request has already been validated by the route's schemas.\n * Context is typed based on the route's context schema (if defined).\n */\n readonly requestMapper: (req: ValidatedRequest<TRoute>, ctx: TypedContext<TRoute>) => TInput;\n\n /**\n * The use case to execute.\n * Can be any object with an `execute` method matching `UseCasePort`.\n */\n readonly useCase: UseCasePort<TInput, TOutput>;\n\n /**\n * Maps the use case output to an HTTP response.\n * Determines the status code and response body.\n */\n readonly responseMapper: (output: TOutput) => HandlerResponse;\n\n /**\n * Middleware to run before the handler.\n */\n readonly middleware?: readonly MiddlewareFunction[];\n}\n\n/**\n * Middleware function type.\n */\nexport type MiddlewareFunction = (\n request: unknown,\n context: HandlerContext,\n next: () => Promise<HandlerResponse>,\n) => Promise<HandlerResponse>;\n\n// ============================================================================\n// Simple Handler Types\n// ============================================================================\n\n/**\n * Simple handler function that directly returns a response.\n * Use this for simple routes that don't need the use case pattern.\n */\nexport type SimpleHandlerFn<TRoute extends RouteDefinition> = (\n req: ValidatedRequest<TRoute>,\n ctx: TypedContext<TRoute>,\n) => Promise<HandlerResponse> | HandlerResponse;\n\n/**\n * Configuration for a simple handler (no use case).\n */\nexport interface SimpleHandlerConfig<TRoute extends RouteDefinition> {\n readonly handler: SimpleHandlerFn<TRoute>;\n readonly middleware?: readonly MiddlewareFunction[];\n}\n\n/**\n * Union of all handler config types.\n * Used internally to store handlers in the builder.\n */\nexport type AnyHandlerConfig<TRoute extends RouteDefinition, TInput = unknown, TOutput = unknown> =\n | RouteHandlerConfig<TRoute, TInput, TOutput>\n | SimpleHandlerConfig<TRoute>;\n\n/**\n * Type guard to check if config is a simple handler.\n */\nexport function isSimpleHandlerConfig(\n config: AnyHandlerConfig<RouteDefinition, unknown, unknown>,\n): config is SimpleHandlerConfig<RouteDefinition> {\n return 'handler' in config && typeof config.handler === 'function';\n}\n\n/**\n * Configuration mapping route keys to handlers.\n *\n * Each route key maps to a `RouteHandlerConfig` with:\n * - The route definition for that key (provides request/response types)\n * - User-defined input/output types for the use case\n *\n * The `TInput` and `TOutput` types are inferred from the `useCase` property,\n * so you don't need to specify them explicitly.\n */\n// TInput/TOutput are user-defined per route - any is required for heterogeneous route configs\nexport type ServerRoutesConfig<T extends RouterConfig> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in RouterKeys<T>]: RouteHandlerConfig<GetRoute<T, K>, any, any>;\n};\n\n/**\n * Options for creating server routes.\n */\nexport interface CreateServerRoutesOptions {\n /**\n * Global middleware to run before all handlers.\n */\n readonly middleware?: readonly MiddlewareFunction[];\n\n /**\n * Whether to validate incoming requests against route schemas.\n * When enabled, invalid requests throw InvalidRequestError.\n * @default true\n */\n readonly validateRequest?: boolean;\n\n /**\n * Whether to validate outgoing responses against route schemas.\n * When enabled, invalid responses throw ControllerError.\n * Useful for catching bugs and ensuring API contract compliance.\n * @default true\n */\n readonly validateResponse?: boolean;\n\n /**\n * Context factory to create handler context.\n */\n readonly createContext?: (rawRequest: unknown) => HandlerContext;\n\n /**\n * Allow partial handler configuration (not all routes need handlers).\n * When true, missing handlers are silently skipped.\n * When false (default), missing handlers throw an error.\n * @default false\n * @internal Used by builder pattern's buildPartial()\n */\n readonly allowPartial?: boolean;\n}\n\n// ============================================================================\n// Route Input (for framework adapters)\n// ============================================================================\n\n/**\n * Route input compatible with framework adapters.\n * This is the output of serverRoutes().build().\n */\nexport interface UnifiedRouteInput {\n /**\n * HTTP method.\n */\n readonly method: HttpMethod;\n\n /**\n * URL path pattern.\n */\n readonly path: string;\n\n /**\n * Handler function.\n */\n readonly handler: (\n rawRequest: RawHttpRequest,\n context?: HandlerContext,\n ) => Promise<HandlerResponse>;\n\n /**\n * Route metadata for documentation.\n */\n readonly metadata: {\n readonly operationId?: string;\n readonly summary?: string;\n readonly description?: string;\n readonly tags?: readonly string[];\n readonly deprecated?: boolean;\n };\n}\n\n/**\n * Raw HTTP request from the framework.\n */\nexport interface RawHttpRequest {\n readonly method: string;\n readonly url: string;\n readonly headers: Record<string, string | string[] | undefined>;\n readonly body?: unknown;\n readonly query?: Record<string, string | string[] | undefined>;\n readonly params?: Record<string, string>;\n}\n","/**\n * @fileoverview Internal implementation for creating server routes with auto-validation.\n *\n * Generates server-side route handlers from a router definition.\n * Each handler automatically validates incoming requests and outgoing\n * responses against the route's schemas.\n *\n * @module unified/server/create-server-routes\n * @internal\n */\n\nimport type { SchemaAdapter, ValidationIssue } from '../schema/types';\nimport type { RouterConfig, RouterDefinition, RouteDefinition } from '../route/types';\nimport { isRouterDefinition, collectRoutes, normalizePath } from '../route/types';\nimport type {\n AnyHandlerConfig,\n CreateServerRoutesOptions,\n HandlerContext,\n HandlerResponse,\n RawHttpRequest,\n UnifiedRouteInput,\n ValidatedRequest,\n} from './types';\nimport { isSimpleHandlerConfig } from './types';\nimport { InvalidRequestError } from '../../exceptions/invalid-request.error';\nimport { ControllerError } from '../../exceptions/controller.error';\nimport { UnauthorizedError } from '../../../app/exceptions/unauthorized.error';\nimport { wrapError } from '../../../global/utils/wrap-error.util';\nimport { generateOperationId } from '../route/utils';\n\n/**\n * Internal implementation for creating server routes.\n * Used by the builder pattern (serverRoutes).\n *\n * @internal\n */\nexport function createServerRoutesInternal<T extends RouterConfig>(\n router: T | RouterDefinition<T>,\n handlers: Record<string, AnyHandlerConfig<RouteDefinition, unknown, unknown>>,\n options?: CreateServerRoutesOptions,\n): UnifiedRouteInput[] {\n const routes = isRouterDefinition(router) ? router.routes : router;\n const collectedRoutes = collectRoutes(routes);\n\n // Sort routes by specificity: static segments before parameterized\n // This ensures /api/users/me is registered before /api/users/:userId\n const sortedRoutes = sortRoutesBySpecificity(collectedRoutes);\n\n const result: UnifiedRouteInput[] = [];\n\n // Default validation options to true, allowPartial to false\n const resolvedOptions: CreateServerRoutesOptions = {\n ...options,\n validateRequest: options?.validateRequest ?? true,\n validateResponse: options?.validateResponse ?? true,\n allowPartial: options?.allowPartial ?? false,\n };\n\n for (const { key, route } of sortedRoutes) {\n const handlerConfig = handlers[key] as AnyHandlerConfig<RouteDefinition, any, any> | undefined;\n\n if (!handlerConfig) {\n if (resolvedOptions.allowPartial) {\n // Skip routes without handlers when allowPartial is true\n continue;\n }\n throw new Error(\n `Missing handler for route \"${key}\". All routes must have a handler configuration.`,\n );\n }\n\n result.push(createRouteHandler(key, route, handlerConfig, resolvedOptions));\n }\n\n return result;\n}\n\n/**\n * Sorts routes by path specificity to ensure correct route matching.\n *\n * Static path segments are sorted before parameterized segments at each position.\n * This ensures that `/api/users/me` is registered before `/api/users/:userId`,\n * preventing the parameterized route from incorrectly matching the static path.\n *\n * @example\n * Given routes:\n * - /api/users/:userId (parameterized)\n * - /api/users/me (static)\n *\n * After sorting:\n * - /api/users/me (registered first - matches exactly)\n * - /api/users/:userId (registered second - catches remaining)\n */\nfunction sortRoutesBySpecificity<T extends { route: { path: string } }>(routes: T[]): T[] {\n return [...routes].sort((a, b) => {\n const aSegments = a.route.path.split('/').filter(Boolean);\n const bSegments = b.route.path.split('/').filter(Boolean);\n\n const maxLen = Math.max(aSegments.length, bSegments.length);\n\n for (let i = 0; i < maxLen; i++) {\n const aSeg = aSegments[i];\n const bSeg = bSegments[i];\n\n // Missing segment (shorter path) - shorter paths first for same prefix\n if (aSeg === undefined && bSeg !== undefined) return -1;\n if (aSeg !== undefined && bSeg === undefined) return 1;\n if (aSeg === undefined || bSeg === undefined) return 0;\n\n // Check if segment is parameterized (supports both :param and {param} formats)\n const aIsParam = aSeg.startsWith(':') || (aSeg.startsWith('{') && aSeg.endsWith('}'));\n const bIsParam = bSeg.startsWith(':') || (bSeg.startsWith('{') && bSeg.endsWith('}'));\n\n // Static segments come before parameterized segments\n if (!aIsParam && bIsParam) return -1;\n if (aIsParam && !bIsParam) return 1;\n\n // Both static or both parameterized - compare alphabetically for stable sorting\n const cmp = aSeg.localeCompare(bSeg);\n if (cmp !== 0) return cmp;\n }\n\n return 0;\n });\n}\n\n/**\n * Creates a single route handler with validation.\n *\n * Supports two handler patterns:\n * - Simple handler: handler(req, ctx) → response\n * - Use case pattern: requestMapper → useCase.execute → responseMapper\n */\nfunction createRouteHandler(\n key: string,\n route: RouteDefinition,\n // TInput/TOutput are user-defined and erased at this level - any is required for type compatibility\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config: AnyHandlerConfig<RouteDefinition, any, any>,\n options: CreateServerRoutesOptions,\n): UnifiedRouteInput {\n const middleware = config.middleware ?? [];\n const globalMiddleware = options?.middleware ?? [];\n const allMiddleware = [...globalMiddleware, ...middleware];\n\n const shouldValidateRequest = options.validateRequest ?? true;\n const shouldValidateResponse = options.validateResponse ?? true;\n\n return {\n method: route.method,\n path: normalizePath(route.path),\n metadata: {\n operationId: route.docs.operationId ?? generateOperationId(key),\n summary: route.docs.summary,\n description: route.docs.description,\n tags: route.docs.tags as string[],\n deprecated: route.docs.deprecated,\n },\n handler: async (rawRequest: RawHttpRequest, ctx?: HandlerContext): Promise<HandlerResponse> => {\n // Create context\n const rawContext: HandlerContext = options?.createContext\n ? options.createContext(rawRequest)\n : (ctx ?? { requestId: generateRequestId() });\n\n // Validate context (if schema defined)\n // Context validation failures are treated as authentication errors (401)\n // because context typically carries auth data (user, session, token).\n // A missing or invalid context means the caller is not properly authenticated.\n const validatedContext: unknown = route.request.context\n ? wrapError(\n () => {\n const result = validateContextData(route, rawContext);\n if (!result.success) {\n const errors = result.errors ?? [];\n throw new InvalidRequestError({\n message: 'Context validation failed',\n validationErrors: errors.map((e) => ({\n field: e.path.join('.'),\n message: e.message,\n })),\n });\n }\n return result.data;\n },\n () => new UnauthorizedError({ message: 'Authentication required' }),\n )\n : rawContext;\n\n // Validate request (if enabled)\n // Use internal type since specific route types are erased in this function\n let validatedRequest: ValidatedRequestInternal;\n\n if (shouldValidateRequest) {\n const validationResult = validateRequestData(route, rawRequest);\n\n if (!validationResult.success) {\n const errors = validationResult.errors ?? [];\n throw new InvalidRequestError({\n message: 'Request validation failed',\n validationErrors: errors.map((e) => ({\n field: e.path.join('.'),\n message: e.message,\n })),\n });\n }\n\n const data = validationResult.data ?? {};\n\n validatedRequest = {\n body: data.body,\n query: data.query,\n pathParams: data.pathParams,\n headers: data.headers,\n raw: {\n method: rawRequest.method,\n url: rawRequest.url,\n headers: normalizeHeaders(rawRequest.headers),\n },\n };\n } else {\n // Skip validation - pass through normalized data\n\n validatedRequest = {\n body: rawRequest.body,\n query: normalizeQuery(rawRequest.query),\n pathParams: normalizePathParams(rawRequest.params),\n headers: normalizeHeaders(rawRequest.headers),\n raw: {\n method: rawRequest.method,\n url: rawRequest.url,\n headers: normalizeHeaders(rawRequest.headers),\n },\n };\n }\n\n // Execute the pipeline based on handler type\n // Errors from the use case/handler propagate to the framework's error handler\n const executePipeline = async (): Promise<HandlerResponse> => {\n if (isSimpleHandlerConfig(config)) {\n // Simple handler: direct call\n return config.handler(\n validatedRequest as unknown as ValidatedRequest<RouteDefinition>,\n validatedContext as HandlerContext,\n );\n } else {\n // Use case handler: requestMapper → useCase → responseMapper\n const { requestMapper, useCase, responseMapper } = config;\n\n // Map request to use case input\n // Cast is safe: ValidatedRequestInternal has same shape as ValidatedRequest<TRoute>\n // Type erasure in this function requires the cast for TypeScript\n // validatedContext is typed correctly based on route's context schema\n const input = requestMapper(\n validatedRequest as unknown as ValidatedRequest<RouteDefinition>,\n validatedContext as HandlerContext,\n );\n\n // Execute use case\n const output = await useCase.execute(input);\n\n // Map output to HTTP response\n return responseMapper(output);\n }\n };\n\n let response: HandlerResponse;\n\n if (allMiddleware.length === 0) {\n response = await executePipeline();\n } else {\n // Build middleware chain\n // Note: Middleware receives the raw context before validation\n let index = 0;\n const next = async (): Promise<HandlerResponse> => {\n if (index >= allMiddleware.length) {\n return executePipeline();\n }\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- index bounds checked above\n const mw = allMiddleware[index++]!;\n return mw(rawRequest, rawContext, next);\n };\n\n response = await next();\n }\n\n // Always validate status code (must be 100-599)\n validateStatusCode(response.status);\n\n // Validate response schema (if enabled)\n if (shouldValidateResponse) {\n const responseValidationResult = validateResponseData(route, response);\n\n if (!responseValidationResult.success) {\n const errors = responseValidationResult.errors ?? [];\n throw new ControllerError({\n message: 'Response validation failed',\n code: 'RESPONSE_VALIDATION_ERROR',\n cause: new Error(errors.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ')),\n });\n }\n }\n\n return response;\n },\n };\n}\n\n/**\n * Validates request data against route request schemas.\n */\nfunction validateRequestData(\n route: RouteDefinition,\n rawRequest: RawHttpRequest,\n): ValidationResultInternal {\n const errors: ValidationIssue[] = [];\n const data: {\n body?: unknown;\n query?: unknown;\n pathParams?: unknown;\n headers?: unknown;\n } = {};\n\n // Validate body\n if (route.request.body) {\n const result = (route.request.body as SchemaAdapter).validate(rawRequest.body);\n if (result.success) {\n data.body = result.data;\n } else {\n errors.push(\n ...result.issues.map((issue) => ({\n ...issue,\n path: ['body', ...issue.path],\n })),\n );\n }\n }\n\n // Validate query\n if (route.request.query) {\n const queryObj = normalizeQuery(rawRequest.query);\n const result = (route.request.query as SchemaAdapter).validate(queryObj);\n if (result.success) {\n data.query = result.data;\n } else {\n errors.push(\n ...result.issues.map((issue) => ({\n ...issue,\n path: ['query', ...issue.path],\n })),\n );\n }\n }\n\n // Validate path params\n if (route.request.params) {\n const result = (route.request.params as SchemaAdapter).validate(rawRequest.params ?? {});\n if (result.success) {\n data.pathParams = result.data;\n } else {\n errors.push(\n ...result.issues.map((issue) => ({\n ...issue,\n path: ['pathParams', ...issue.path],\n })),\n );\n }\n } else {\n // Normalize raw params if no schema (ensure all values are strings)\n data.pathParams = normalizePathParams(rawRequest.params);\n }\n\n // Validate headers\n if (route.request.headers) {\n const headersObj = normalizeHeaders(rawRequest.headers);\n const result = (route.request.headers as SchemaAdapter).validate(headersObj);\n if (result.success) {\n data.headers = result.data;\n } else {\n errors.push(\n ...result.issues.map((issue) => ({\n ...issue,\n path: ['headers', ...issue.path],\n })),\n );\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors };\n }\n\n return { success: true, data };\n}\n\n/**\n * Validates response data against the route's response schema.\n *\n * Looks up the matching status code in `route.responses` and validates\n * the response body against its schema, if one is defined.\n */\nfunction validateResponseData(\n route: RouteDefinition,\n response: HandlerResponse,\n): ValidationResultInternal {\n if (!route.responses) {\n return { success: true };\n }\n\n const entry = route.responses[String(response.status)];\n const schema = entry?.schema as SchemaAdapter | undefined;\n\n if (!schema) {\n return { success: true };\n }\n\n const result = schema.validate(response.body);\n\n if (result.success) {\n return { success: true };\n }\n\n const errors = result.issues.map((issue) => ({\n ...issue,\n path: ['response', ...issue.path],\n }));\n\n return { success: false, errors };\n}\n\n/**\n * Validates context data against route context schema.\n */\nfunction validateContextData(\n route: RouteDefinition,\n context: HandlerContext,\n): ContextValidationResultInternal {\n const contextSchema = route.request.context as SchemaAdapter | undefined;\n\n // No context schema defined - skip validation\n if (!contextSchema) {\n return { success: true, data: context };\n }\n\n // Validate context against schema\n const result = contextSchema.validate(context);\n\n if (result.success) {\n return { success: true, data: result.data };\n }\n\n // Prefix errors with 'context.' for clarity\n const errors = result.issues.map((issue) => ({\n ...issue,\n path: ['context', ...issue.path],\n }));\n\n return { success: false, errors };\n}\n\ninterface ValidationResultInternal {\n success: boolean;\n errors?: ValidationIssue[];\n data?: {\n body?: unknown;\n query?: unknown;\n pathParams?: unknown;\n headers?: unknown;\n };\n}\n\ninterface ContextValidationResultInternal {\n success: boolean;\n errors?: ValidationIssue[];\n data?: unknown;\n}\n\n/**\n * Internal validated request type with unknown fields.\n * Used inside createRouteHandler where specific types are erased.\n * The requestMapper receives the properly typed ValidatedRequest<TRoute>.\n */\ninterface ValidatedRequestInternal {\n readonly body: unknown;\n readonly query: unknown;\n readonly pathParams: unknown;\n readonly headers: unknown;\n readonly raw: {\n readonly method: string;\n readonly url: string;\n readonly headers: Record<string, string>;\n };\n}\n\n/**\n * Validates that an HTTP status code is in the valid range (100-599).\n *\n * @throws {ControllerError} If the status code is invalid\n */\nfunction validateStatusCode(status: number): void {\n if (!Number.isInteger(status) || status < 100 || status > 599) {\n throw new ControllerError({\n message: `Invalid HTTP status code: ${status}. Status must be an integer between 100 and 599.`,\n code: 'INVALID_STATUS_CODE',\n });\n }\n}\n\n/**\n * Normalizes query parameters, preserving arrays for duplicate keys.\n *\n * When a query parameter appears multiple times (e.g., `?tag=a&tag=b`),\n * the framework provides an array. This function preserves that array\n * so schema validation can properly validate array vs single-value params.\n *\n * Empty strings are allowed (e.g., `?flag=` results in `{ flag: '' }`).\n * Undefined values are filtered out from arrays.\n */\nfunction normalizeQuery(\n query?: Record<string, string | string[] | undefined>,\n): Record<string, string | string[]> {\n if (!query) return {};\n\n const result: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue;\n\n if (Array.isArray(value)) {\n // Filter out undefined values but preserve the array structure\n const definedValues = value.filter((v): v is string => v !== undefined);\n if (definedValues.length === 1 && definedValues[0] !== undefined) {\n // Single value in array - unwrap for convenience\n result[key] = definedValues[0];\n } else if (definedValues.length > 1) {\n // Multiple values - preserve as array for schema validation\n result[key] = definedValues;\n }\n // Empty array (all undefined) - skip this key\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Normalizes path parameters to ensure all values are non-empty strings.\n *\n * @throws {InvalidRequestError} If any path parameter is empty\n */\nfunction normalizePathParams(params?: Record<string, string>): Record<string, string> {\n if (!params) return {};\n\n const result: Record<string, string> = {};\n const emptyParams: string[] = [];\n\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n const stringValue = String(value);\n if (stringValue === '') {\n emptyParams.push(key);\n } else {\n result[key] = stringValue;\n }\n }\n }\n\n // Throw error for empty path params instead of silently filtering\n if (emptyParams.length > 0) {\n throw new InvalidRequestError({\n message: 'Path parameters cannot be empty',\n validationErrors: emptyParams.map((param) => ({\n field: `pathParams.${param}`,\n message: 'Path parameter cannot be empty',\n })),\n });\n }\n\n return result;\n}\n\n/**\n * Normalizes headers to a flat object.\n *\n * Per RFC 7230, multiple header values are joined with \", \" (comma + space).\n * Headers are lowercased for consistency.\n */\nfunction normalizeHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n\n if (Array.isArray(value)) {\n // Filter undefined values and join per RFC 7230\n const definedValues = value.filter((v): v is string => v !== undefined);\n if (definedValues.length > 0) {\n result[key.toLowerCase()] = definedValues.join(', ');\n }\n } else {\n result[key.toLowerCase()] = value;\n }\n }\n return result;\n}\n\n/**\n * Generates a unique request ID using crypto-secure UUID.\n */\nfunction generateRequestId(): string {\n return `req_${crypto.randomUUID()}`;\n}\n","/**\n * @fileoverview Builder pattern for creating type-safe server routes.\n *\n * The `serverRoutes` function returns a builder that provides 100% type inference\n * for all handler parameters - no manual type annotations required.\n *\n * @module unified/server/server-routes-builder\n */\n\nimport type { RouterConfig, RouterDefinition, GetRoute, RouterKeys } from '../route/types';\nimport type {\n AnyHandlerConfig,\n CreateServerRoutesOptions,\n HandlerResponse,\n MiddlewareFunction,\n RouteHandlerConfig,\n SimpleHandlerConfig,\n SimpleHandlerFn,\n TypedContext,\n UnifiedRouteInput,\n UseCasePort,\n ValidatedRequest,\n} from './types';\nimport { createServerRoutesInternal } from './create-server-routes';\nimport type { RouteDefinition } from '../route/types';\n\n// ============================================================================\n// Builder Types\n// ============================================================================\n\n/**\n * Error type displayed when attempting to build() with missing handlers.\n * The `___missingRoutes` property shows which routes are missing.\n */\nexport interface MissingHandlersError<TMissing extends string> {\n /**\n * This error indicates that not all routes have handlers.\n * Use buildPartial() to build with only the defined handlers,\n * or add handlers for the missing routes.\n */\n (options?: never): never;\n /** Routes that are missing handlers */\n readonly ___missingRoutes: TMissing;\n}\n\n/**\n * Handler configuration for the builder pattern.\n * Identical to RouteHandlerConfig but with proper TypedContext.\n */\nexport interface BuilderHandlerConfig<TRoute extends RouteDefinition, TInput, TOutput> {\n /**\n * Maps the validated HTTP request to use case input.\n * Both `req` and `ctx` are fully typed based on route schemas.\n */\n readonly requestMapper: (req: ValidatedRequest<TRoute>, ctx: TypedContext<TRoute>) => TInput;\n\n /**\n * The use case to execute.\n */\n readonly useCase: UseCasePort<TInput, TOutput>;\n\n /**\n * Maps the use case output to an HTTP response.\n */\n readonly responseMapper: (output: TOutput) => HandlerResponse;\n\n /**\n * Middleware to run before the handler.\n */\n readonly middleware?: readonly MiddlewareFunction[];\n}\n\n/**\n * Builder interface for creating type-safe server routes.\n *\n * Each `.handle()` call captures the specific route type and provides\n * full type inference for requestMapper, useCase, and responseMapper.\n *\n * @typeParam T - The router configuration type\n * @typeParam THandled - Union of route keys that have handlers (accumulates)\n *\n * @example\n * ```typescript\n * const routes = serverRoutes(projectRouter)\n * .handle('projects.create', {\n * requestMapper: (req, ctx) => ({\n * name: req.body.name, // Fully typed!\n * createdBy: ctx.userId, // Fully typed!\n * }),\n * useCase: createProjectUseCase,\n * responseMapper: (output) => ({\n * status: 201 as const,\n * body: { projectId: output.projectId },\n * }),\n * })\n * .handle('projects.list', { ... })\n * .build();\n * ```\n */\nexport interface ServerRoutesBuilder<T extends RouterConfig, THandled extends string = never> {\n /**\n * Register a simple handler for a route.\n * The handler receives validated request and context, returns response directly.\n *\n * @param key - The route key (e.g., 'projects.get')\n * @param handlerOrConfig - Simple handler function or configuration with handler and optional middleware\n * @returns A new builder with the route key added to handled routes\n */\n handle<K extends Exclude<RouterKeys<T>, THandled>>(\n key: K,\n handlerOrConfig: SimpleHandlerFn<GetRoute<T, K>> | SimpleHandlerConfig<GetRoute<T, K>>,\n ): ServerRoutesBuilder<T, THandled | K>;\n\n /**\n * Register a handler using the use case pattern.\n * Follows: requestMapper → useCase.execute() → responseMapper\n *\n * @param key - The route key (e.g., 'projects.create')\n * @param config - Handler configuration with requestMapper, useCase, responseMapper\n * @returns A new builder with the route key added to handled routes\n */\n handleWithUseCase<K extends Exclude<RouterKeys<T>, THandled>, TInput, TOutput>(\n key: K,\n config: BuilderHandlerConfig<GetRoute<T, K>, TInput, TOutput>,\n ): ServerRoutesBuilder<T, THandled | K>;\n\n /**\n * Build the routes array for framework registration.\n *\n * This method is only available when ALL routes have handlers.\n * If some routes are missing handlers, use `buildPartial()` instead.\n *\n * @param options - Optional configuration (validation, middleware)\n * @returns Array of route inputs for framework registration\n *\n * @throws {Error} At compile time if routes are missing (type error)\n */\n build: [Exclude<RouterKeys<T>, THandled>] extends [never]\n ? (options?: CreateServerRoutesOptions) => UnifiedRouteInput[]\n : MissingHandlersError<Exclude<RouterKeys<T>, THandled>>;\n\n /**\n * Build routes for only the defined handlers.\n *\n * Use this when you only want to register handlers for some routes,\n * not all routes in the router. No compile-time enforcement.\n *\n * @param options - Optional configuration (validation, middleware)\n * @returns Array of route inputs for framework registration\n */\n buildPartial(options?: CreateServerRoutesOptions): UnifiedRouteInput[];\n}\n\n// ============================================================================\n// Builder Implementation\n// ============================================================================\n\n/**\n * Internal builder implementation.\n *\n * Uses an immutable pattern where each handle() call returns a new\n * builder instance with the updated handlers map.\n */\nclass ServerRoutesBuilderImpl<T extends RouterConfig, THandled extends string = never> {\n private readonly router: T | RouterDefinition<T>;\n private readonly handlers: Map<string, AnyHandlerConfig<RouteDefinition, unknown, unknown>>;\n\n constructor(\n router: T | RouterDefinition<T>,\n handlers?: Map<string, AnyHandlerConfig<RouteDefinition, unknown, unknown>>,\n ) {\n this.router = router;\n this.handlers = handlers ?? new Map();\n }\n\n handle<K extends Exclude<RouterKeys<T>, THandled>>(\n key: K,\n handlerOrConfig: SimpleHandlerFn<GetRoute<T, K>> | SimpleHandlerConfig<GetRoute<T, K>>,\n ): ServerRoutesBuilder<T, THandled | K> {\n // Normalize function to config object\n const config: SimpleHandlerConfig<RouteDefinition> =\n typeof handlerOrConfig === 'function'\n ? { handler: handlerOrConfig as SimpleHandlerFn<RouteDefinition> }\n : (handlerOrConfig as SimpleHandlerConfig<RouteDefinition>);\n\n const newHandlers = new Map(this.handlers);\n newHandlers.set(key as string, config);\n\n return new ServerRoutesBuilderImpl<T, THandled | K>(\n this.router,\n newHandlers,\n ) as unknown as ServerRoutesBuilder<T, THandled | K>;\n }\n\n handleWithUseCase<K extends Exclude<RouterKeys<T>, THandled>, TInput, TOutput>(\n key: K,\n config: BuilderHandlerConfig<GetRoute<T, K>, TInput, TOutput>,\n ): ServerRoutesBuilder<T, THandled | K> {\n // Create new handlers map (immutable pattern)\n const newHandlers = new Map(this.handlers);\n newHandlers.set(key as string, config as RouteHandlerConfig<RouteDefinition, unknown, unknown>);\n\n // Return new builder with updated type\n // Cast through unknown is safe: the type system tracks THandled | K through the interface\n // The conditional type on `build` cannot be proven at compile time, hence the cast\n return new ServerRoutesBuilderImpl<T, THandled | K>(\n this.router,\n newHandlers,\n ) as unknown as ServerRoutesBuilder<T, THandled | K>;\n }\n\n // The build method's type is determined by the interface conditional type\n // At runtime, it always works the same way - the conditional type only affects compile-time\n build(options?: CreateServerRoutesOptions): UnifiedRouteInput[] {\n return createServerRoutesInternal(this.router, Object.fromEntries(this.handlers), options);\n }\n\n buildPartial(options?: CreateServerRoutesOptions): UnifiedRouteInput[] {\n return createServerRoutesInternal(this.router, Object.fromEntries(this.handlers), {\n ...options,\n allowPartial: true,\n });\n }\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Creates a type-safe server routes builder for a router.\n *\n * The builder pattern provides 100% type inference for all handler parameters:\n * - `req.body`, `req.query`, `req.pathParams`, `req.headers` are typed from route schemas\n * - `ctx` is typed from the route's context schema\n * - `output` in responseMapper is typed from the use case\n *\n * @param router - Router definition or router config\n * @returns Builder for registering handlers\n *\n * @example Basic usage\n * ```typescript\n * import { serverRoutes } from '@cosmneo/onion-lasagna/http/server';\n * import { projectRouter } from './router';\n *\n * const routes = serverRoutes(projectRouter)\n * .handle('projects.create', {\n * requestMapper: (req, ctx) => ({\n * name: req.body.name,\n * createdBy: ctx.userId,\n * }),\n * useCase: createProjectUseCase,\n * responseMapper: (output) => ({\n * status: 201 as const,\n * body: { projectId: output.projectId },\n * }),\n * })\n * .handle('projects.list', {\n * requestMapper: (req) => ({\n * page: req.query.page ?? 1,\n * limit: req.query.limit ?? 20,\n * }),\n * useCase: listProjectsUseCase,\n * responseMapper: (output) => ({\n * status: 200 as const,\n * body: output.projects,\n * }),\n * })\n * .build();\n *\n * // Register with framework\n * registerHonoRoutes(app, routes);\n * ```\n *\n * @example Partial build (only some routes)\n * ```typescript\n * const routes = serverRoutes(projectRouter)\n * .handle('projects.create', { ... })\n * // Skip other routes\n * .buildPartial(); // No type error even with missing routes\n * ```\n *\n * @example With options\n * ```typescript\n * const routes = serverRoutes(projectRouter)\n * .handle('projects.create', { ... })\n * .handle('projects.list', { ... })\n * .build({\n * validateRequest: true,\n * validateResponse: process.env.NODE_ENV !== 'production',\n * middleware: [loggingMiddleware],\n * });\n * ```\n */\nexport function serverRoutes<T extends RouterConfig>(\n router: T | RouterDefinition<T>,\n): ServerRoutesBuilder<T, never> {\n // Cast through unknown is safe: initial builder has no handlers (THandled = never)\n return new ServerRoutesBuilderImpl(router) as unknown as ServerRoutesBuilder<T, never>;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,34 @@
1
+ // src/presentation/events/handler/types/event-router-definition.type.ts
2
+ function isEventHandlerDefinition(value) {
3
+ return typeof value === "object" && value !== null && "eventType" in value && "_types" in value;
4
+ }
5
+ function isEventRouterDefinition(value) {
6
+ return typeof value === "object" && value !== null && "_isEventRouter" in value && value._isEventRouter === true;
7
+ }
8
+ function collectEventHandlers(config, basePath = "") {
9
+ const handlers = [];
10
+ for (const [key, value] of Object.entries(config)) {
11
+ const fullKey = basePath ? `${basePath}.${key}` : key;
12
+ if (isEventHandlerDefinition(value)) {
13
+ handlers.push({ key: fullKey, handler: value });
14
+ } else if (isEventRouterDefinition(value)) {
15
+ handlers.push(...collectEventHandlers(value.handlers, fullKey));
16
+ } else if (typeof value === "object" && value !== null) {
17
+ handlers.push(...collectEventHandlers(value, fullKey));
18
+ }
19
+ }
20
+ return handlers;
21
+ }
22
+
23
+ // src/presentation/events/handler/utils.ts
24
+ function generateHandlerId(key) {
25
+ return key.replace(/\.(\w)/g, (_, char) => char.toUpperCase());
26
+ }
27
+
28
+ export {
29
+ isEventHandlerDefinition,
30
+ isEventRouterDefinition,
31
+ collectEventHandlers,
32
+ generateHandlerId
33
+ };
34
+ //# sourceMappingURL=chunk-CBTICRSM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presentation/events/handler/types/event-router-definition.type.ts","../src/presentation/events/handler/utils.ts"],"sourcesContent":["/**\n * @fileoverview Event router definition types for grouping event handlers.\n *\n * Mirrors the HTTP router definition pattern with hierarchical grouping,\n * dotted-key access, and deep merge support.\n *\n * @module events/handler/types/event-router-definition\n */\n\nimport type { EventHandlerDefinition } from './event-handler-definition.type';\nimport type { SchemaAdapter } from '../../../http/schema/types';\n\n// ============================================================================\n// Router Types\n// ============================================================================\n\n/**\n * A router entry can be an event handler definition, a nested config, or a router definition.\n */\nexport type EventRouterEntry =\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n EventHandlerDefinition<string, any, any> | EventRouterConfig | EventRouterDefinition;\n\n/**\n * Configuration for an event router (group of event handlers).\n */\nexport interface EventRouterConfig {\n readonly [key: string]: EventRouterEntry;\n}\n\n/**\n * Router-level defaults applied to all child event handlers.\n */\nexport interface EventRouterDefaults {\n /** Default tags for all handlers. Merged with handler-specific tags. */\n readonly tags?: readonly string[];\n\n /** Default context schema. Applied to handlers that don't define their own. */\n readonly context?: SchemaAdapter;\n}\n\n/**\n * A fully defined event router.\n */\nexport interface EventRouterDefinition<T extends EventRouterConfig = EventRouterConfig> {\n /** The handlers and nested routers in this router. */\n readonly handlers: T;\n\n /** Default values applied to all child handlers. */\n readonly defaults?: EventRouterDefaults;\n\n /**\n * Marker to identify this as an event router.\n * @internal\n */\n readonly _isEventRouter: true;\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Checks if a value is an EventHandlerDefinition.\n */\nexport function isEventHandlerDefinition(value: unknown): value is EventHandlerDefinition {\n return typeof value === 'object' && value !== null && 'eventType' in value && '_types' in value;\n}\n\n/**\n * Checks if a value is an EventRouterDefinition.\n */\nexport function isEventRouterDefinition(value: unknown): value is EventRouterDefinition {\n return (\n typeof value === 'object' &&\n value !== null &&\n '_isEventRouter' in value &&\n (value as EventRouterDefinition)._isEventRouter === true\n );\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Flattens an event router into a map of dotted keys to handler definitions.\n */\nexport type FlattenEventRouter<\n T extends EventRouterConfig,\n Prefix extends string = '',\n> = T extends EventRouterConfig\n ? {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends EventHandlerDefinition<any, any, any>\n ? { [P in `${Prefix}${K & string}`]: T[K] }\n : T[K] extends EventRouterConfig\n ? FlattenEventRouter<T[K], `${Prefix}${K & string}.`>\n : never;\n }[keyof T] extends infer U\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n U extends Record<string, EventHandlerDefinition<any, any, any>>\n ? U\n : never\n : never\n : never;\n\n/**\n * Gets all handler keys from an event router.\n */\nexport type EventRouterKeys<\n T extends EventRouterConfig,\n Prefix extends string = '',\n> = T extends EventRouterConfig\n ? {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends EventHandlerDefinition<any, any, any>\n ? `${Prefix}${K & string}`\n : T[K] extends EventRouterConfig\n ? EventRouterKeys<T[K], `${Prefix}${K & string}.`>\n : never;\n }[keyof T]\n : never;\n\n/**\n * Gets an event handler by its dotted key path.\n */\nexport type GetEventHandler<\n T extends EventRouterConfig,\n K extends string,\n> = K extends `${infer Head}.${infer Tail}`\n ? Head extends keyof T\n ? T[Head] extends EventRouterConfig\n ? GetEventHandler<T[Head], Tail>\n : never\n : never\n : K extends keyof T\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T[K] extends EventHandlerDefinition<any, any, any>\n ? T[K]\n : never\n : never;\n\n// ============================================================================\n// Deep Merge Types\n// ============================================================================\n\n/**\n * Deep-merges two event router configs at the type level.\n */\nexport type DeepMergeTwo<A extends EventRouterConfig, B extends EventRouterConfig> = {\n readonly [K in keyof A | keyof B]: K extends keyof A\n ? K extends keyof B\n ? A[K] extends EventRouterConfig\n ? B[K] extends EventRouterConfig\n ? DeepMergeTwo<A[K], B[K]>\n : B[K]\n : B[K]\n : A[K]\n : K extends keyof B\n ? B[K]\n : never;\n};\n\n/**\n * Recursively deep-merges N event router configs left-to-right.\n */\nexport type DeepMergeAll<T extends readonly EventRouterConfig[]> = T extends readonly [\n infer Only extends EventRouterConfig,\n]\n ? Only\n : T extends readonly [\n infer First extends EventRouterConfig,\n infer Second extends EventRouterConfig,\n ...infer Rest extends readonly EventRouterConfig[],\n ]\n ? DeepMergeAll<[DeepMergeTwo<First, Second>, ...Rest]>\n : EventRouterConfig;\n\n// ============================================================================\n// Runtime Utilities\n// ============================================================================\n\n/**\n * Collects all event handlers from a router into a flat array.\n */\nexport function collectEventHandlers(\n config: EventRouterConfig,\n basePath = '',\n): { key: string; handler: EventHandlerDefinition }[] {\n const handlers: { key: string; handler: EventHandlerDefinition }[] = [];\n\n for (const [key, value] of Object.entries(config)) {\n const fullKey = basePath ? `${basePath}.${key}` : key;\n\n if (isEventHandlerDefinition(value)) {\n handlers.push({ key: fullKey, handler: value });\n } else if (isEventRouterDefinition(value)) {\n handlers.push(...collectEventHandlers(value.handlers, fullKey));\n } else if (typeof value === 'object' && value !== null) {\n handlers.push(...collectEventHandlers(value as EventRouterConfig, fullKey));\n }\n }\n\n return handlers;\n}\n","/**\n * @fileoverview Utility functions for event handler routing.\n *\n * @module events/handler/utils\n */\n\n/**\n * Generates a handler ID from a dotted key path.\n *\n * Converts dot-separated keys to camelCase:\n * - `\"ticket.created\"` → `\"ticketCreated\"`\n * - `\"ecosystem.member.added\"` → `\"ecosystemMemberAdded\"`\n *\n * @param key - The dotted key path\n * @returns A camelCase handler ID\n */\nexport function generateHandlerId(key: string): string {\n return key.replace(/\\.(\\w)/g, (_, char: string) => char.toUpperCase());\n}\n"],"mappings":";AAiEO,SAAS,yBAAyB,OAAiD;AACxF,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,eAAe,SAAS,YAAY;AAC5F;AAKO,SAAS,wBAAwB,OAAgD;AACtF,SACE,OAAO,UAAU,YACjB,UAAU,QACV,oBAAoB,SACnB,MAAgC,mBAAmB;AAExD;AA2GO,SAAS,qBACd,QACA,WAAW,IACyC;AACpD,QAAM,WAA+D,CAAC;AAEtE,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAM,UAAU,WAAW,GAAG,QAAQ,IAAI,GAAG,KAAK;AAElD,QAAI,yBAAyB,KAAK,GAAG;AACnC,eAAS,KAAK,EAAE,KAAK,SAAS,SAAS,MAAM,CAAC;AAAA,IAChD,WAAW,wBAAwB,KAAK,GAAG;AACzC,eAAS,KAAK,GAAG,qBAAqB,MAAM,UAAU,OAAO,CAAC;AAAA,IAChE,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AACtD,eAAS,KAAK,GAAG,qBAAqB,OAA4B,OAAO,CAAC;AAAA,IAC5E;AAAA,EACF;AAEA,SAAO;AACT;;;AC7LO,SAAS,kBAAkB,KAAqB;AACrD,SAAO,IAAI,QAAQ,WAAW,CAAC,GAAG,SAAiB,KAAK,YAAY,CAAC;AACvE;","names":[]}