@geekmidas/constructs 0.0.3 → 0.0.5

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 (142) hide show
  1. package/dist/{AmazonApiGatewayEndpointAdaptor-D_Q_NTMT.cjs → AmazonApiGatewayEndpointAdaptor-BEzWkW5m.cjs} +32 -9
  2. package/dist/AmazonApiGatewayEndpointAdaptor-BEzWkW5m.cjs.map +1 -0
  3. package/dist/{AmazonApiGatewayEndpointAdaptor-DtzgQ9Vb.d.cts → AmazonApiGatewayEndpointAdaptor-BFhJ2Rpz.d.cts} +5 -2
  4. package/dist/{AmazonApiGatewayEndpointAdaptor-DNZLntHj.d.mts → AmazonApiGatewayEndpointAdaptor-BrB3RfbI.d.mts} +5 -2
  5. package/dist/{AmazonApiGatewayEndpointAdaptor-QzIAnWzS.mjs → AmazonApiGatewayEndpointAdaptor-CASfHEj9.mjs} +32 -9
  6. package/dist/AmazonApiGatewayEndpointAdaptor-CASfHEj9.mjs.map +1 -0
  7. package/dist/{AmazonApiGatewayV1EndpointAdaptor-Gw-j61qM.d.cts → AmazonApiGatewayV1EndpointAdaptor-Bd-o8ese.d.cts} +3 -3
  8. package/dist/{AmazonApiGatewayV1EndpointAdaptor-DbJa4cpU.d.mts → AmazonApiGatewayV1EndpointAdaptor-BtNXt0-4.d.mts} +3 -3
  9. package/dist/{AmazonApiGatewayV1EndpointAdaptor-BF5bGWV1.mjs → AmazonApiGatewayV1EndpointAdaptor-RPnkGENb.mjs} +2 -2
  10. package/dist/{AmazonApiGatewayV1EndpointAdaptor-BF5bGWV1.mjs.map → AmazonApiGatewayV1EndpointAdaptor-RPnkGENb.mjs.map} +1 -1
  11. package/dist/{AmazonApiGatewayV1EndpointAdaptor-Bh4tckwd.cjs → AmazonApiGatewayV1EndpointAdaptor-l1df4rFt.cjs} +2 -2
  12. package/dist/{AmazonApiGatewayV1EndpointAdaptor-Bh4tckwd.cjs.map → AmazonApiGatewayV1EndpointAdaptor-l1df4rFt.cjs.map} +1 -1
  13. package/dist/{AmazonApiGatewayV2EndpointAdaptor-BOaOkLXF.mjs → AmazonApiGatewayV2EndpointAdaptor-C5Ctz7C_.mjs} +2 -2
  14. package/dist/{AmazonApiGatewayV2EndpointAdaptor-BOaOkLXF.mjs.map → AmazonApiGatewayV2EndpointAdaptor-C5Ctz7C_.mjs.map} +1 -1
  15. package/dist/{AmazonApiGatewayV2EndpointAdaptor-BlKn-KJ6.d.mts → AmazonApiGatewayV2EndpointAdaptor-DAJdtgek.d.mts} +3 -3
  16. package/dist/{AmazonApiGatewayV2EndpointAdaptor-L4Ywv3Pk.cjs → AmazonApiGatewayV2EndpointAdaptor-DH9yJsC8.cjs} +2 -2
  17. package/dist/{AmazonApiGatewayV2EndpointAdaptor-L4Ywv3Pk.cjs.map → AmazonApiGatewayV2EndpointAdaptor-DH9yJsC8.cjs.map} +1 -1
  18. package/dist/{AmazonApiGatewayV2EndpointAdaptor-LUlpwmUW.d.cts → AmazonApiGatewayV2EndpointAdaptor-DX-Uci5w.d.cts} +3 -3
  19. package/dist/{Endpoint-D1nnEsBU.cjs → Endpoint-BVZJb4OR.cjs} +128 -4
  20. package/dist/Endpoint-BVZJb4OR.cjs.map +1 -0
  21. package/dist/{Endpoint-DYUjJdEs.d.mts → Endpoint-C7jPJzAH.d.mts} +115 -6
  22. package/dist/{Endpoint-DNlmybXV.mjs → Endpoint-DVEmKo6G.mjs} +123 -5
  23. package/dist/Endpoint-DVEmKo6G.mjs.map +1 -0
  24. package/dist/{Endpoint-C7z9YJHK.d.cts → Endpoint-XUMNAXYy.d.cts} +115 -6
  25. package/dist/{EndpointBuilder-CpjIMYb0.mjs → EndpointBuilder-Bhy91l_c.mjs} +2 -2
  26. package/dist/{EndpointBuilder-CpjIMYb0.mjs.map → EndpointBuilder-Bhy91l_c.mjs.map} +1 -1
  27. package/dist/{EndpointBuilder-B2iScUND.d.mts → EndpointBuilder-CFtWQhcv.d.mts} +2 -2
  28. package/dist/{EndpointBuilder-BhRd626m.cjs → EndpointBuilder-DDnqW7rT.cjs} +2 -2
  29. package/dist/{EndpointBuilder-BhRd626m.cjs.map → EndpointBuilder-DDnqW7rT.cjs.map} +1 -1
  30. package/dist/{EndpointBuilder-1fw103D6.d.cts → EndpointBuilder-t6fVEKBH.d.cts} +2 -2
  31. package/dist/{EndpointFactory-DLpEbLzL.mjs → EndpointFactory-CNz3Wa08.mjs} +4 -3
  32. package/dist/EndpointFactory-CNz3Wa08.mjs.map +1 -0
  33. package/dist/{EndpointFactory-ChmVHWim.cjs → EndpointFactory-Cw_6-53M.cjs} +4 -3
  34. package/dist/EndpointFactory-Cw_6-53M.cjs.map +1 -0
  35. package/dist/{EndpointFactory-D576BhaH.d.cts → EndpointFactory-DBRGrXAy.d.mts} +10 -10
  36. package/dist/{EndpointFactory-DZQpM-9K.d.mts → EndpointFactory-DInjHvFR.d.cts} +10 -10
  37. package/dist/{HonoEndpointAdaptor-01cH100U.d.mts → HonoEndpointAdaptor-BY2Ovj31.d.mts} +4 -4
  38. package/dist/{HonoEndpointAdaptor-6LERutxi.cjs → HonoEndpointAdaptor-BsGMcxYb.cjs} +23 -8
  39. package/dist/HonoEndpointAdaptor-BsGMcxYb.cjs.map +1 -0
  40. package/dist/{HonoEndpointAdaptor-ua6mp3gt.d.cts → HonoEndpointAdaptor-OtKS5Dsd.d.cts} +4 -4
  41. package/dist/{HonoEndpointAdaptor-fs2928iO.mjs → HonoEndpointAdaptor-Y4AD3E9g.mjs} +23 -8
  42. package/dist/HonoEndpointAdaptor-Y4AD3E9g.mjs.map +1 -0
  43. package/dist/{Subscriber-G7EUI3yc.cjs → Subscriber-CSt7EOlT.cjs} +1 -1
  44. package/dist/{Subscriber-G7EUI3yc.cjs.map → Subscriber-CSt7EOlT.cjs.map} +1 -1
  45. package/dist/{Subscriber-BTwKnz3c.mjs → Subscriber-DkCDcTUL.mjs} +1 -1
  46. package/dist/{Subscriber-BTwKnz3c.mjs.map → Subscriber-DkCDcTUL.mjs.map} +1 -1
  47. package/dist/{SubscriberBuilder-C1me_972.mjs → SubscriberBuilder-Bn9Hyi28.mjs} +2 -2
  48. package/dist/{SubscriberBuilder-C1me_972.mjs.map → SubscriberBuilder-Bn9Hyi28.mjs.map} +1 -1
  49. package/dist/{SubscriberBuilder-C6iZvuDe.cjs → SubscriberBuilder-RsiOmnwq.cjs} +2 -2
  50. package/dist/{SubscriberBuilder-C6iZvuDe.cjs.map → SubscriberBuilder-RsiOmnwq.cjs.map} +1 -1
  51. package/dist/{TestEndpointAdaptor-B4SvJvK-.cjs → TestEndpointAdaptor-CbNeUT3Y.cjs} +20 -6
  52. package/dist/TestEndpointAdaptor-CbNeUT3Y.cjs.map +1 -0
  53. package/dist/{TestEndpointAdaptor-CelYsQi0.mjs → TestEndpointAdaptor-DD3aygPp.mjs} +20 -6
  54. package/dist/TestEndpointAdaptor-DD3aygPp.mjs.map +1 -0
  55. package/dist/{TestEndpointAdaptor-Da0ooGt2.d.mts → TestEndpointAdaptor-Db0cm1fb.d.mts} +3 -3
  56. package/dist/{TestEndpointAdaptor-CHcgyI3V.d.cts → TestEndpointAdaptor-v7A-7hTs.d.cts} +3 -3
  57. package/dist/adaptors/aws.cjs +4 -4
  58. package/dist/adaptors/aws.d.cts +5 -5
  59. package/dist/adaptors/aws.d.mts +4 -4
  60. package/dist/adaptors/aws.mjs +4 -4
  61. package/dist/adaptors/hono.cjs +3 -3
  62. package/dist/adaptors/hono.d.cts +3 -3
  63. package/dist/adaptors/hono.d.mts +2 -2
  64. package/dist/adaptors/hono.mjs +3 -3
  65. package/dist/adaptors/testing.cjs +2 -2
  66. package/dist/adaptors/testing.d.cts +3 -3
  67. package/dist/adaptors/testing.d.mts +2 -2
  68. package/dist/adaptors/testing.mjs +2 -2
  69. package/dist/crons/Cron.d.cts +1 -1
  70. package/dist/crons/CronBuilder.d.cts +1 -1
  71. package/dist/crons/index.d.cts +5 -5
  72. package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.cjs +2 -2
  73. package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.d.cts +3 -3
  74. package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.d.mts +2 -2
  75. package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.mjs +2 -2
  76. package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.cjs +3 -3
  77. package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.d.cts +4 -4
  78. package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.d.mts +3 -3
  79. package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.mjs +3 -3
  80. package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.cjs +3 -3
  81. package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.d.cts +4 -4
  82. package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.d.mts +3 -3
  83. package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.mjs +3 -3
  84. package/dist/endpoints/Endpoint.cjs +2 -1
  85. package/dist/endpoints/Endpoint.d.cts +3 -3
  86. package/dist/endpoints/Endpoint.d.mts +2 -2
  87. package/dist/endpoints/Endpoint.mjs +2 -2
  88. package/dist/endpoints/EndpointBuilder.cjs +2 -2
  89. package/dist/endpoints/EndpointBuilder.d.cts +3 -3
  90. package/dist/endpoints/EndpointBuilder.d.mts +2 -2
  91. package/dist/endpoints/EndpointBuilder.mjs +2 -2
  92. package/dist/endpoints/EndpointFactory.cjs +3 -3
  93. package/dist/endpoints/EndpointFactory.d.cts +4 -4
  94. package/dist/endpoints/EndpointFactory.d.mts +3 -3
  95. package/dist/endpoints/EndpointFactory.mjs +3 -3
  96. package/dist/endpoints/HonoEndpointAdaptor.cjs +3 -3
  97. package/dist/endpoints/HonoEndpointAdaptor.d.cts +3 -3
  98. package/dist/endpoints/HonoEndpointAdaptor.d.mts +2 -2
  99. package/dist/endpoints/HonoEndpointAdaptor.mjs +3 -3
  100. package/dist/endpoints/TestEndpointAdaptor.cjs +2 -2
  101. package/dist/endpoints/TestEndpointAdaptor.d.cts +3 -3
  102. package/dist/endpoints/TestEndpointAdaptor.d.mts +2 -2
  103. package/dist/endpoints/TestEndpointAdaptor.mjs +2 -2
  104. package/dist/endpoints/helpers.cjs +2 -2
  105. package/dist/endpoints/helpers.d.cts +2 -2
  106. package/dist/endpoints/helpers.d.mts +1 -1
  107. package/dist/endpoints/helpers.mjs +2 -2
  108. package/dist/endpoints/index.cjs +3 -3
  109. package/dist/endpoints/index.cjs.map +1 -1
  110. package/dist/endpoints/index.d.cts +7 -7
  111. package/dist/endpoints/index.d.mts +6 -6
  112. package/dist/endpoints/index.mjs +3 -3
  113. package/dist/endpoints/index.mjs.map +1 -1
  114. package/dist/functions/index.d.cts +1 -1
  115. package/dist/{helpers-CjvCSIF5.cjs → helpers-BSzj2AiQ.cjs} +2 -2
  116. package/dist/{helpers-CjvCSIF5.cjs.map → helpers-BSzj2AiQ.cjs.map} +1 -1
  117. package/dist/{helpers-CP7A0U_s.mjs → helpers-C9WBx7ms.mjs} +2 -2
  118. package/dist/{helpers-CP7A0U_s.mjs.map → helpers-C9WBx7ms.mjs.map} +1 -1
  119. package/dist/index-BxApEwAF.d.cts +9 -0
  120. package/dist/subscribers/Subscriber.cjs +1 -1
  121. package/dist/subscribers/Subscriber.mjs +1 -1
  122. package/dist/subscribers/SubscriberBuilder.cjs +2 -2
  123. package/dist/subscribers/SubscriberBuilder.mjs +2 -2
  124. package/dist/subscribers/index.cjs +2 -2
  125. package/dist/subscribers/index.d.cts +2 -2
  126. package/dist/subscribers/index.d.mts +2 -2
  127. package/dist/subscribers/index.mjs +2 -2
  128. package/package.json +4 -4
  129. package/src/endpoints/EndpointFactory.ts +88 -18
  130. package/src/endpoints/index.ts +1 -1
  131. package/test.ts +92 -0
  132. package/dist/AmazonApiGatewayEndpointAdaptor-D_Q_NTMT.cjs.map +0 -1
  133. package/dist/AmazonApiGatewayEndpointAdaptor-QzIAnWzS.mjs.map +0 -1
  134. package/dist/Endpoint-D1nnEsBU.cjs.map +0 -1
  135. package/dist/Endpoint-DNlmybXV.mjs.map +0 -1
  136. package/dist/EndpointFactory-ChmVHWim.cjs.map +0 -1
  137. package/dist/EndpointFactory-DLpEbLzL.mjs.map +0 -1
  138. package/dist/HonoEndpointAdaptor-6LERutxi.cjs.map +0 -1
  139. package/dist/HonoEndpointAdaptor-fs2928iO.mjs.map +0 -1
  140. package/dist/TestEndpointAdaptor-B4SvJvK-.cjs.map +0 -1
  141. package/dist/TestEndpointAdaptor-CelYsQi0.mjs.map +0 -1
  142. package/dist/index-zOH9f4sh.d.cts +0 -9
@@ -44,12 +44,16 @@ var Endpoint = class Endpoint extends require_Function.Function {
44
44
  tags;
45
45
  /** The HTTP success status code to return (default: 200) */
46
46
  status;
47
+ /** Default headers to apply to all responses */
48
+ defaultHeaders = {};
47
49
  /** Function to extract session data from the request context */
48
50
  getSession = () => ({});
49
51
  /** Function to determine if the request is authorized */
50
52
  authorize = () => true;
51
53
  /** Optional rate limiting configuration */
52
54
  rateLimit;
55
+ /** The endpoint handler function */
56
+ endpointFn;
53
57
  /**
54
58
  * Builds a complete OpenAPI 3.1 schema from an array of endpoints.
55
59
  *
@@ -126,6 +130,67 @@ var Endpoint = class Endpoint extends require_Function.Function {
126
130
  };
127
131
  }
128
132
  /**
133
+ * Parses cookie string and creates a cookie lookup function.
134
+ *
135
+ * @param cookieHeader - The Cookie header value
136
+ * @returns Function to retrieve cookie values by name
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * const cookieFn = Endpoint.createCookies('session=abc123; theme=dark');
141
+ * cookieFn('session'); // Returns 'abc123'
142
+ * cookieFn('theme'); // Returns 'dark'
143
+ * ```
144
+ */
145
+ static createCookies(cookieHeader) {
146
+ const cookieMap = /* @__PURE__ */ new Map();
147
+ if (cookieHeader) {
148
+ const cookies = cookieHeader.split(";");
149
+ for (const cookie of cookies) {
150
+ const [name, ...valueParts] = cookie.trim().split("=");
151
+ if (name) {
152
+ const value = valueParts.join("=");
153
+ cookieMap.set(name, decodeURIComponent(value));
154
+ }
155
+ }
156
+ }
157
+ return function get(name) {
158
+ return cookieMap.get(name);
159
+ };
160
+ }
161
+ /**
162
+ * Formats a cookie as a Set-Cookie header string.
163
+ *
164
+ * @param name - Cookie name
165
+ * @param value - Cookie value
166
+ * @param options - Cookie options (httpOnly, secure, sameSite, etc.)
167
+ * @returns Formatted Set-Cookie header string
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const header = Endpoint.formatCookieHeader('session', 'abc123', {
172
+ * httpOnly: true,
173
+ * secure: true,
174
+ * sameSite: 'strict',
175
+ * maxAge: 3600
176
+ * });
177
+ * // Returns: "session=abc123; Max-Age=3600; HttpOnly; Secure; SameSite=Strict"
178
+ * ```
179
+ */
180
+ static formatCookieHeader(name, value, options) {
181
+ let cookie = `${name}=${value}`;
182
+ if (options) {
183
+ if (options.domain) cookie += `; Domain=${options.domain}`;
184
+ if (options.path) cookie += `; Path=${options.path}`;
185
+ if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
186
+ if (options.maxAge !== void 0) cookie += `; Max-Age=${options.maxAge}`;
187
+ if (options.httpOnly) cookie += "; HttpOnly";
188
+ if (options.secure) cookie += "; Secure";
189
+ if (options.sameSite) cookie += `; SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`;
190
+ }
191
+ return cookie;
192
+ }
193
+ /**
129
194
  * Extracts and refines input data from the endpoint context.
130
195
  *
131
196
  * @param ctx - The endpoint execution context
@@ -140,14 +205,16 @@ var Endpoint = class Endpoint extends require_Function.Function {
140
205
  ]);
141
206
  return input;
142
207
  }
143
- handler = (ctx) => {
144
- return this.fn({
208
+ handler = (ctx, response) => {
209
+ for (const [key, value] of Object.entries(this.defaultHeaders)) response.header(key, value);
210
+ return this.endpointFn({
145
211
  ...this.refineInput(ctx),
146
212
  services: ctx.services,
147
213
  logger: ctx.logger,
148
214
  header: ctx.header,
215
+ cookie: ctx.cookie,
149
216
  session: ctx.session
150
- });
217
+ }, response);
151
218
  };
152
219
  /**
153
220
  * Type guard to check if an object is an Endpoint instance.
@@ -159,6 +226,12 @@ var Endpoint = class Endpoint extends require_Function.Function {
159
226
  return Boolean(obj && obj.__IS_FUNCTION__ === true && obj.type === require_Construct.ConstructType.Endpoint);
160
227
  }
161
228
  /**
229
+ * Helper to check if response has metadata
230
+ */
231
+ static hasMetadata(response) {
232
+ return response !== null && typeof response === "object" && "data" in response && "metadata" in response;
233
+ }
234
+ /**
162
235
  * Converts Express-style route params to OpenAPI format.
163
236
  * @returns Route with ':param' converted to '{param}'
164
237
  * @internal
@@ -251,12 +324,57 @@ var Endpoint = class Endpoint extends require_Function.Function {
251
324
  this.description = description;
252
325
  this.tags = tags;
253
326
  this.status = status;
327
+ this.endpointFn = fn;
254
328
  if (getSession) this.getSession = getSession;
255
329
  if (authorize) this.authorize = authorize;
256
330
  if (rateLimit) this.rateLimit = rateLimit;
257
331
  }
258
332
  };
259
333
  /**
334
+ * Response builder for fluent API in handlers
335
+ */
336
+ var ResponseBuilder = class {
337
+ metadata = {
338
+ headers: {},
339
+ cookies: /* @__PURE__ */ new Map()
340
+ };
341
+ header(key, value) {
342
+ this.metadata.headers[key] = value;
343
+ return this;
344
+ }
345
+ cookie(name, value, options) {
346
+ this.metadata.cookies.set(name, {
347
+ value,
348
+ options
349
+ });
350
+ return this;
351
+ }
352
+ deleteCookie(name, options) {
353
+ this.metadata.cookies.set(name, {
354
+ value: "",
355
+ options: {
356
+ ...options,
357
+ maxAge: 0,
358
+ expires: /* @__PURE__ */ new Date(0)
359
+ }
360
+ });
361
+ return this;
362
+ }
363
+ status(code) {
364
+ this.metadata.status = code;
365
+ return this;
366
+ }
367
+ send(data) {
368
+ return {
369
+ data,
370
+ metadata: this.metadata
371
+ };
372
+ }
373
+ getMetadata() {
374
+ return this.metadata;
375
+ }
376
+ };
377
+ /**
260
378
  * HTTP success status codes that can be returned by endpoints.
261
379
  */
262
380
  let SuccessStatus = /* @__PURE__ */ function(SuccessStatus$1) {
@@ -282,10 +400,16 @@ Object.defineProperty(exports, 'Endpoint', {
282
400
  return Endpoint;
283
401
  }
284
402
  });
403
+ Object.defineProperty(exports, 'ResponseBuilder', {
404
+ enumerable: true,
405
+ get: function () {
406
+ return ResponseBuilder;
407
+ }
408
+ });
285
409
  Object.defineProperty(exports, 'SuccessStatus', {
286
410
  enumerable: true,
287
411
  get: function () {
288
412
  return SuccessStatus;
289
413
  }
290
414
  });
291
- //# sourceMappingURL=Endpoint-D1nnEsBU.cjs.map
415
+ //# sourceMappingURL=Endpoint-BVZJb4OR.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Endpoint-BVZJb4OR.cjs","names":["Function","endpoints: Endpoint<any, any, any, any, any, any>[]","options?: OpenApiSchemaOptions","input: unknown","key: K","body: unknown","status: number","headers: Record<string, string>","key: string","cookieHeader: string | undefined","name: string","value: string","options?: CookieOptions","ctx: EndpointContext<TInput, TServices, TLogger, TSession>","response: ResponseBuilder","obj: any","ConstructType","response: T | ResponseWithMetadata<T>","componentCollector?: ComponentCollector","operation: OpenAPIV3_1.OperationObject","pathParameters: OpenAPIV3_1.ParameterObject[]","queryParameters: OpenAPIV3_1.ParameterObject[]","routeObject: any","options?: Pick<CookieOptions, 'domain' | 'path'>","code: SuccessStatus","data: T"],"sources":["../src/endpoints/Endpoint.ts"],"sourcesContent":["import type { Logger } from '@geekmidas/logger';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport pick from 'lodash.pick';\nimport set from 'lodash.set';\nimport type { OpenAPIV3_1 } from 'openapi-types';\n\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { ConstructType } from '../Construct';\nimport { Function, type FunctionHandler } from '../functions';\n\nimport type {\n EventPublisher,\n ExtractPublisherMessage,\n MappedEvent,\n} from '@geekmidas/events';\nimport type { RateLimitConfig } from '@geekmidas/rate-limit';\nimport type {\n InferComposableStandardSchema,\n InferStandardSchema,\n} from '@geekmidas/schema';\nimport {\n convertSchemaWithComponents,\n convertStandardSchemaToJsonSchema,\n} from '@geekmidas/schema/conversion';\nimport {\n type ComponentCollector,\n type OpenApiSchemaOptions,\n buildOpenApiSchema,\n} from '@geekmidas/schema/openapi';\nimport type { HttpMethod, LowerHttpMethod, RemoveUndefined } from '../types';\n\n/**\n * Represents an HTTP endpoint that can handle requests with type-safe input/output validation,\n * dependency injection, session management, and authorization.\n *\n * @template TRoute - The route path string with parameter placeholders (e.g., '/users/:id')\n * @template TMethod - The HTTP method (GET, POST, PUT, DELETE, PATCH)\n * @template TInput - The input schema definition for body, query, and path parameters\n * @template OutSchema - The output schema for response validation\n * @template TServices - Array of service dependencies to inject\n * @template TLogger - The logger instance type\n * @template TSession - The session data type\n *\n * @extends Function - Base function construct for handler execution\n *\n * @example\n * ```typescript\n * const endpoint = new Endpoint({\n * route: '/users/:id',\n * method: 'GET',\n * input: { params: userIdSchema },\n * output: userSchema,\n * fn: async ({ params }) => getUserById(params.id)\n * });\n * ```\n */\nexport class Endpoint<\n TRoute extends string,\n TMethod extends HttpMethod,\n TInput extends EndpointSchemas = {},\n OutSchema extends StandardSchemaV1 | undefined = undefined,\n TServices extends Service[] = [],\n TLogger extends Logger = Logger,\n TSession = unknown,\n TEventPublisher extends EventPublisher<any> | undefined = undefined,\n TEventPublisherServiceName extends string = string,\n> extends Function<\n TInput,\n TServices,\n TLogger,\n OutSchema,\n FunctionHandler<TInput, TServices, TLogger, OutSchema>,\n TEventPublisher,\n TEventPublisherServiceName\n> {\n operationId?: string;\n /** The route path pattern with parameter placeholders */\n route: TRoute;\n /** The HTTP method for this endpoint */\n method: TMethod;\n /** Optional description for OpenAPI documentation */\n description?: string;\n /** Optional tags for OpenAPI documentation */\n tags?: string[];\n /** The HTTP success status code to return (default: 200) */\n public readonly status: SuccessStatus;\n /** Default headers to apply to all responses */\n public readonly defaultHeaders: Record<string, string> = {};\n /** Function to extract session data from the request context */\n public getSession: SessionFn<TServices, TLogger, TSession> = () =>\n ({}) as TSession;\n /** Function to determine if the request is authorized */\n public authorize: AuthorizeFn<TServices, TLogger, TSession> = () => true;\n /** Optional rate limiting configuration */\n public rateLimit?: RateLimitConfig;\n /** The endpoint handler function */\n private endpointFn!: EndpointHandler<\n TInput,\n TServices,\n TLogger,\n OutSchema,\n TSession\n >;\n\n /**\n * Builds a complete OpenAPI 3.1 schema from an array of endpoints.\n *\n * @param endpoints - Array of endpoint instances to document\n * @param options - Optional configuration for OpenAPI generation\n * @returns OpenAPI 3.1 specification object\n *\n * @example\n * ```typescript\n * const schema = await Endpoint.buildOpenApiSchema([\n * getUserEndpoint,\n * createUserEndpoint\n * ], {\n * title: 'User API',\n * version: '1.0.0'\n * });\n * ```\n */\n static async buildOpenApiSchema(\n endpoints: Endpoint<any, any, any, any, any, any>[],\n options?: OpenApiSchemaOptions,\n ) {\n return buildOpenApiSchema(endpoints, options);\n }\n\n /**\n * Gets the full path including HTTP method and route.\n * @returns Formatted string like 'GET /users/{id}'\n */\n get fullPath() {\n return `${this.method} ${this._path}` as const;\n }\n\n /**\n * Parses and validates input data for a specific input type (body, query, params).\n *\n * @param input - The raw input data to validate\n * @param key - The input type key ('body', 'query', or 'params')\n * @returns The validated input data for the specified key\n * @throws {UnprocessableEntityError} When validation fails\n */\n async parseInput<K extends keyof TInput>(\n input: unknown,\n key: K,\n ): Promise<InferComposableStandardSchema<TInput[K]>> {\n const schema = this.input?.[key];\n return Endpoint.parseSchema(schema as StandardSchemaV1, input) as Promise<\n InferComposableStandardSchema<TInput[K]>\n >;\n }\n\n /**\n * Parses and validates the request body against the body schema.\n *\n * @param body - The raw request body to validate\n * @returns The validated body data\n * @throws {UnprocessableEntityError} When body validation fails\n */\n async parseBody(body: unknown): Promise<InferStandardSchema<TInput['body']>> {\n return this.parseInput(body, 'body') as Promise<\n InferStandardSchema<TInput['body']>\n >;\n }\n\n static isSuccessStatus(status: number): boolean {\n return status >= 200 && status < 300;\n }\n\n /**\n * Creates a case-insensitive header lookup function from a headers object.\n *\n * @param headers - Object containing header key-value pairs\n * @returns Function to retrieve header values by case-insensitive key\n *\n * @example\n * ```typescript\n * const headerFn = Endpoint.createHeaders({ 'Content-Type': 'application/json' });\n * headerFn('content-type'); // Returns 'application/json'\n * ```\n */\n static createHeaders(headers: Record<string, string>): HeaderFn {\n const headerMap = new Map<string, string>();\n for (const [k, v] of Object.entries(headers)) {\n const key = k.toLowerCase();\n headerMap.set(key, v);\n }\n\n return function get(key: string): string | undefined {\n return headerMap.get(key.toLowerCase());\n };\n }\n\n /**\n * Parses cookie string and creates a cookie lookup function.\n *\n * @param cookieHeader - The Cookie header value\n * @returns Function to retrieve cookie values by name\n *\n * @example\n * ```typescript\n * const cookieFn = Endpoint.createCookies('session=abc123; theme=dark');\n * cookieFn('session'); // Returns 'abc123'\n * cookieFn('theme'); // Returns 'dark'\n * ```\n */\n static createCookies(cookieHeader: string | undefined): CookieFn {\n const cookieMap = new Map<string, string>();\n\n if (cookieHeader) {\n // Parse cookie string: \"name1=value1; name2=value2\"\n const cookies = cookieHeader.split(';');\n for (const cookie of cookies) {\n const [name, ...valueParts] = cookie.trim().split('=');\n if (name) {\n const value = valueParts.join('='); // Handle values with = in them\n cookieMap.set(name, decodeURIComponent(value));\n }\n }\n }\n\n return function get(name: string): string | undefined {\n return cookieMap.get(name);\n };\n }\n\n /**\n * Formats a cookie as a Set-Cookie header string.\n *\n * @param name - Cookie name\n * @param value - Cookie value\n * @param options - Cookie options (httpOnly, secure, sameSite, etc.)\n * @returns Formatted Set-Cookie header string\n *\n * @example\n * ```typescript\n * const header = Endpoint.formatCookieHeader('session', 'abc123', {\n * httpOnly: true,\n * secure: true,\n * sameSite: 'strict',\n * maxAge: 3600\n * });\n * // Returns: \"session=abc123; Max-Age=3600; HttpOnly; Secure; SameSite=Strict\"\n * ```\n */\n static formatCookieHeader(\n name: string,\n value: string,\n options?: CookieOptions,\n ): string {\n let cookie = `${name}=${value}`;\n\n if (options) {\n if (options.domain) cookie += `; Domain=${options.domain}`;\n if (options.path) cookie += `; Path=${options.path}`;\n if (options.expires)\n cookie += `; Expires=${options.expires.toUTCString()}`;\n if (options.maxAge !== undefined) cookie += `; Max-Age=${options.maxAge}`;\n if (options.httpOnly) cookie += '; HttpOnly';\n if (options.secure) cookie += '; Secure';\n if (options.sameSite) {\n cookie += `; SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`;\n }\n }\n\n return cookie;\n }\n\n /**\n * Extracts and refines input data from the endpoint context.\n *\n * @param ctx - The endpoint execution context\n * @returns Object containing only the input data (body, query, params)\n * @internal\n */\n refineInput(\n ctx: EndpointContext<TInput, TServices, TLogger, TSession>,\n ): InferComposableStandardSchema<TInput> {\n const input = pick(ctx, [\n 'body',\n 'query',\n 'params',\n ]) as InferComposableStandardSchema<TInput>;\n\n return input;\n }\n\n handler = (\n ctx: EndpointContext<TInput, TServices, TLogger, TSession>,\n response: ResponseBuilder,\n ): OutSchema extends StandardSchemaV1\n ?\n | InferStandardSchema<OutSchema>\n | ResponseWithMetadata<InferStandardSchema<OutSchema>>\n | Promise<InferStandardSchema<OutSchema>>\n | Promise<ResponseWithMetadata<InferStandardSchema<OutSchema>>>\n :\n | any\n | ResponseWithMetadata<any>\n | Promise<any>\n | Promise<ResponseWithMetadata<any>> => {\n // Apply default headers to response builder\n for (const [key, value] of Object.entries(this.defaultHeaders)) {\n response.header(key, value);\n }\n\n return this.endpointFn(\n {\n ...this.refineInput(ctx),\n services: ctx.services,\n logger: ctx.logger,\n header: ctx.header,\n cookie: ctx.cookie,\n session: ctx.session,\n } as EndpointContext<TInput, TServices, TLogger, TSession>,\n response,\n );\n };\n\n /**\n * Type guard to check if an object is an Endpoint instance.\n *\n * @param obj - The object to check\n * @returns True if the object is an Endpoint\n */\n static isEndpoint(obj: any): obj is Endpoint<any, any, any, any> {\n return Boolean(\n obj &&\n (obj as Function).__IS_FUNCTION__ === true &&\n obj.type === ConstructType.Endpoint,\n );\n }\n\n /**\n * Helper to check if response has metadata\n */\n static hasMetadata<T>(\n response: T | ResponseWithMetadata<T>,\n ): response is ResponseWithMetadata<T> {\n return (\n response !== null &&\n typeof response === 'object' &&\n 'data' in response &&\n 'metadata' in response\n );\n }\n\n /**\n * Converts Express-style route params to OpenAPI format.\n * @returns Route with ':param' converted to '{param}'\n * @internal\n */\n get _path() {\n return this.route.replace(/:(\\w+)/g, '{$1}') as ConvertRouteParams<TRoute>;\n }\n\n /**\n * Generates OpenAPI 3.1 schema for this endpoint.\n *\n * @returns OpenAPI route definition with operation details\n */\n async toOpenApi3Route(\n componentCollector?: ComponentCollector,\n ): Promise<EndpointOpenApiSchema<TRoute, TMethod>> {\n const operation: OpenAPIV3_1.OperationObject = {\n operationId: this.operationId,\n ...(this.description && { description: this.description }),\n ...(this.tags && this.tags.length > 0 && { tags: this.tags }),\n responses: {\n '200': {\n description: 'Successful response',\n } as OpenAPIV3_1.ResponseObject,\n },\n };\n\n // Add response schema\n if (this.outputSchema) {\n const responseSchema = await convertSchemaWithComponents(\n this.outputSchema,\n componentCollector,\n );\n if (responseSchema) {\n set(\n operation,\n ['responses', '200', 'content', 'application/json', 'schema'],\n responseSchema,\n );\n }\n }\n\n // Separate path and query parameters\n const pathParameters: OpenAPIV3_1.ParameterObject[] = [];\n const queryParameters: OpenAPIV3_1.ParameterObject[] = [];\n\n // Since the EndpointBuilder doesn't have body/search/params methods yet,\n // and the input is a composite type, we need to check if input exists\n // and has the expected shape\n if (this.input && typeof this.input === 'object') {\n // Add request body for methods that support it\n if (\n ['POST', 'PUT', 'PATCH'].includes(this.method) &&\n 'body' in this.input &&\n this.input.body\n ) {\n const bodySchema = await convertSchemaWithComponents(\n this.input.body as StandardSchemaV1,\n componentCollector,\n );\n if (bodySchema) {\n set(operation, ['requestBody'], {\n required: true,\n content: {\n 'application/json': {\n schema: bodySchema,\n },\n },\n });\n }\n }\n\n // Add path parameters\n if ('params' in this.input && this.input.params) {\n const paramsSchema = await convertStandardSchemaToJsonSchema(\n this.input.params as StandardSchemaV1,\n );\n if (\n paramsSchema &&\n paramsSchema.type === 'object' &&\n paramsSchema.properties\n ) {\n for (const [name, schema] of Object.entries(\n paramsSchema.properties,\n )) {\n pathParameters.push({\n name,\n in: 'path',\n required: paramsSchema.required?.includes(name) ?? true,\n schema: schema as any,\n });\n }\n }\n }\n\n // Add query parameters\n if ('query' in this.input && this.input.query) {\n const querySchema = await convertStandardSchemaToJsonSchema(\n this.input.query,\n );\n if (\n querySchema &&\n querySchema.type === 'object' &&\n querySchema.properties\n ) {\n for (const [name, schema] of Object.entries(querySchema.properties)) {\n queryParameters.push({\n name,\n in: 'query',\n required: querySchema.required?.includes(name) ?? false,\n schema: schema as any,\n });\n }\n }\n }\n }\n\n // Only add query parameters to the operation\n if (queryParameters.length > 0) {\n operation.parameters = queryParameters;\n }\n\n // Build the route object with path parameters at the route level\n const routeObject: any = {};\n if (pathParameters.length > 0) {\n routeObject.parameters = pathParameters;\n }\n routeObject[this.method.toLowerCase()] = operation;\n\n return {\n [this._path]: routeObject,\n } as EndpointOpenApiSchema<TRoute, TMethod>;\n }\n\n /**\n * Creates a new Endpoint instance.\n *\n * @param options - Configuration options for the endpoint\n * @param options.fn - The handler function to execute\n * @param options.method - HTTP method\n * @param options.route - Route path with parameter placeholders\n * @param options.description - Optional description for documentation\n * @param options.input - Input schemas for validation\n * @param options.logger - Logger instance\n * @param options.output - Output schema for response validation\n * @param options.services - Service dependencies\n * @param options.timeout - Execution timeout in milliseconds\n * @param options.getSession - Session extraction function\n * @param options.authorize - Authorization check function\n * @param options.status - Success HTTP status code (default: 200)\n */\n constructor({\n fn,\n method,\n route,\n description,\n tags,\n input,\n logger,\n output: outputSchema,\n services,\n timeout,\n getSession,\n authorize,\n rateLimit,\n status = SuccessStatus.OK,\n publisherService,\n events,\n }: EndpointOptions<\n TRoute,\n TMethod,\n TInput,\n OutSchema,\n TServices,\n TLogger,\n TSession,\n OutSchema,\n TEventPublisher,\n TEventPublisherServiceName\n >) {\n super(\n fn as unknown as FunctionHandler<TInput, TServices, TLogger, OutSchema>,\n timeout,\n ConstructType.Endpoint,\n input,\n outputSchema,\n services,\n logger,\n publisherService,\n events,\n );\n\n this.route = route;\n this.method = method;\n this.description = description;\n this.tags = tags;\n this.status = status;\n this.endpointFn = fn;\n\n if (getSession) {\n this.getSession = getSession;\n }\n\n if (authorize) {\n this.authorize = authorize;\n }\n\n if (rateLimit) {\n this.rateLimit = rateLimit;\n }\n }\n}\n\n/**\n * Defines the input schema structure for an endpoint.\n *\n * @template TBody - Schema for request body validation\n * @template TSearch - Schema for query string validation\n * @template TParams - Schema for URL path parameters validation\n *\n * @example\n * ```typescript\n * type UserInput = EndpointInput<\n * typeof createUserBodySchema,\n * typeof userQuerySchema,\n * typeof userParamsSchema\n * >;\n * ```\n */\nexport type EndpointInput<\n TBody extends StandardSchemaV1 | undefined = undefined,\n TSearch extends StandardSchemaV1 | undefined = undefined,\n TParams extends StandardSchemaV1 | undefined = undefined,\n> = RemoveUndefined<{\n body: TBody;\n search: TSearch;\n params: TParams;\n}>;\n\n/**\n * Configuration options for creating an Endpoint instance.\n *\n * @template TRoute - The route path string\n * @template TMethod - The HTTP method\n * @template TInput - Input schema definitions\n * @template TOutput - Output schema definition\n * @template TServices - Service dependencies array\n * @template TLogger - Logger type\n * @template TSession - Session data type\n */\nexport interface EndpointOptions<\n TRoute extends string,\n TMethod extends HttpMethod,\n TInput extends EndpointSchemas = {},\n TOutput extends StandardSchemaV1 | undefined = undefined,\n TServices extends Service[] = [],\n TLogger extends Logger = Logger,\n TSession = unknown,\n OutSchema extends StandardSchemaV1 | undefined = undefined,\n TEventPublisher extends EventPublisher<any> | undefined = undefined,\n TEventPublisherServiceName extends string = string,\n> {\n /** The route path with parameter placeholders */\n route: TRoute;\n /** The HTTP method for this endpoint */\n method: TMethod;\n /** The handler function that implements the endpoint logic */\n fn: EndpointHandler<TInput, TServices, TLogger, TOutput, TSession>;\n /** Optional authorization check function */\n authorize: AuthorizeFn<TServices, TLogger, TSession> | undefined;\n /** Optional description for documentation */\n description: string | undefined;\n /** Optional tags for OpenAPI documentation */\n tags?: string[];\n /** Optional execution timeout in milliseconds */\n timeout: number | undefined;\n /** Input validation schemas */\n input: TInput | undefined;\n /** Output validation schema */\n output: TOutput | undefined;\n /** Service dependencies to inject */\n services: TServices;\n /** Logger instance */\n logger: TLogger;\n /** Optional session extraction function */\n getSession: SessionFn<TServices, TLogger, TSession> | undefined;\n /** Optional rate limiting configuration */\n rateLimit?: RateLimitConfig;\n /** Success HTTP status code */\n status: SuccessStatus | undefined;\n /**\n * Event publisher service for publishing events from this endpoint\n */\n publisherService?: Service<TEventPublisherServiceName, TEventPublisher>;\n\n events?: MappedEvent<TEventPublisher, OutSchema>[];\n}\n\n/**\n * Defines the possible input schema types for an endpoint.\n * Each property represents a different part of the HTTP request.\n */\nexport type EndpointSchemas = Partial<{\n /** Schema for URL path parameters (e.g., /users/:id) */\n params: StandardSchemaV1;\n /** Schema for query string parameters */\n query: StandardSchemaV1;\n /** Schema for request body (POST, PUT, PATCH) */\n body: StandardSchemaV1;\n}>;\n\nexport type AuthorizeContext<\n TServices extends Service[] = [],\n TLogger extends Logger = Logger,\n TSession = unknown,\n> = {\n services: ServiceRecord<TServices>;\n logger: TLogger;\n header: HeaderFn;\n cookie: CookieFn;\n session: TSession;\n};\n/**\n * Function type for endpoint authorization checks.\n *\n * @template TServices - Available service dependencies\n * @template TLogger - Logger type\n * @template TSession - Session data type\n *\n * @param ctx - Context containing services, logger, headers, and session\n * @returns Boolean indicating if the request is authorized\n *\n * @example\n * ```typescript\n * const authorize: AuthorizeFn = ({ session }) => {\n * return session.userId !== undefined;\n * };\n * ```\n */\nexport type AuthorizeFn<\n TServices extends Service[] = [],\n TLogger extends Logger = Logger,\n TSession = unknown,\n> = (\n ctx: AuthorizeContext<TServices, TLogger, TSession>,\n) => Promise<boolean> | boolean;\n\nexport type SessionContext<\n TServices extends Service[] = [],\n TLogger extends Logger = Logger,\n> = {\n services: ServiceRecord<TServices>;\n logger: TLogger;\n header: HeaderFn;\n cookie: CookieFn;\n};\n/**\n * Function type for extracting session data from a request.\n *\n * @template TServices - Available service dependencies\n * @template TLogger - Logger type\n * @template TSession - Session data type to extract\n *\n * @param ctx - Context containing services, logger, and headers\n * @returns The extracted session data\n *\n * @example\n * ```typescript\n * const getSession: SessionFn<Services, Logger, UserSession> = async ({ header, services }) => {\n * const token = header('authorization');\n * return await services.auth.verifyToken(token);\n * };\n * ```\n */\nexport type SessionFn<\n TServices extends Service[] = [],\n TLogger extends Logger = Logger,\n TSession = unknown,\n> = (ctx: SessionContext<TServices, TLogger>) => Promise<TSession> | TSession;\n\n/**\n * Utility type that converts Express-style route parameters to OpenAPI format.\n * Transforms ':param' syntax to '{param}' syntax.\n *\n * @template T - The route string to convert\n *\n * @example\n * ```typescript\n * type Route1 = ConvertRouteParams<'/users/:id'>; // '/users/{id}'\n * type Route2 = ConvertRouteParams<'/users/:userId/posts/:postId'>; // '/users/{userId}/posts/{postId}'\n * ```\n */\nexport type ConvertRouteParams<T extends string> =\n T extends `${infer Start}:${infer Param}/${infer Rest}`\n ? `${Start}{${Param}}/${ConvertRouteParams<Rest>}`\n : T extends `${infer Start}:${infer Param}`\n ? `${Start}{${Param}}`\n : T;\n\n/**\n * Type representing the OpenAPI schema structure for an endpoint.\n *\n * @template TRoute - The route path\n * @template TMethod - The HTTP method\n *\n * @example\n * ```typescript\n * type Schema = EndpointOpenApiSchema<'/users/:id', 'GET'>;\n * // Results in: { '/users/{id}': { get: OperationObject, parameters?: ParameterObject[] } }\n * ```\n */\nexport type EndpointOpenApiSchema<\n TRoute extends string,\n TMethod extends HttpMethod,\n> = {\n [key in ConvertRouteParams<TRoute>]: {\n [key in LowerHttpMethod<TMethod>]: OpenAPIV3_1.OperationObject<{}>;\n } & {\n parameters?: OpenAPIV3_1.ParameterObject[];\n };\n};\n\nexport type SingleHeaderFn = (key: string) => string | undefined;\nexport type MultiHeaderFn = () => EndpointHeaders;\n/**\n * Type representing HTTP headers as a Map.\n */\nexport type EndpointHeaders = Map<string, string>;\n\n/**\n * Function type for retrieving HTTP header values.\n *\n * @param key - The header name (case-insensitive)\n * @returns The header value or undefined if not found\n */\nexport type HeaderFn = SingleHeaderFn;\n\n/**\n * Function type for retrieving cookie values.\n *\n * @param name - The cookie name\n * @returns The cookie value or undefined if not found\n *\n * @example\n * ```typescript\n * const sessionId = cookie('session');\n * ```\n */\nexport type CookieFn = (name: string) => string | undefined;\n\n/**\n * Cookie options matching standard Set-Cookie attributes\n */\nexport interface CookieOptions {\n domain?: string;\n path?: string;\n expires?: Date;\n maxAge?: number;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: 'strict' | 'lax' | 'none';\n}\n\n/**\n * Response metadata that handlers can set\n */\nexport interface ResponseMetadata {\n headers?: Record<string, string>;\n cookies?: Map<string, { value: string; options?: CookieOptions }>;\n status?: SuccessStatus;\n}\n\n/**\n * Return type for handlers that want to set response metadata\n */\nexport interface ResponseWithMetadata<T> {\n data: T;\n metadata: ResponseMetadata;\n}\n\n/**\n * Response builder for fluent API in handlers\n */\nexport class ResponseBuilder {\n private metadata: ResponseMetadata = {\n headers: {},\n cookies: new Map(),\n };\n\n header(key: string, value: string): this {\n this.metadata.headers![key] = value;\n return this;\n }\n\n cookie(name: string, value: string, options?: CookieOptions): this {\n this.metadata.cookies!.set(name, { value, options });\n return this;\n }\n\n deleteCookie(\n name: string,\n options?: Pick<CookieOptions, 'domain' | 'path'>,\n ): this {\n this.metadata.cookies!.set(name, {\n value: '',\n options: { ...options, maxAge: 0, expires: new Date(0) },\n });\n return this;\n }\n\n status(code: SuccessStatus): this {\n this.metadata.status = code;\n return this;\n }\n\n send<T>(data: T): ResponseWithMetadata<T> {\n return { data, metadata: this.metadata };\n }\n\n getMetadata(): ResponseMetadata {\n return this.metadata;\n }\n}\n\n/**\n * The execution context provided to endpoint handlers.\n * Contains all parsed input data, services, logger, headers, cookies, and session.\n *\n * @template Input - The input schemas (body, query, params)\n * @template TServices - Available service dependencies\n * @template TLogger - Logger type\n * @template TSession - Session data type\n */\nexport type EndpointContext<\n Input extends EndpointSchemas | undefined = undefined,\n TServices extends Service[] = [],\n TLogger extends Logger = Logger,\n TSession = unknown,\n> = {\n /** Injected service instances */\n services: ServiceRecord<TServices>;\n /** Logger instance for this request */\n logger: TLogger;\n /** Function to retrieve request headers */\n header: HeaderFn;\n /** Function to retrieve request cookies */\n cookie: CookieFn;\n /** Session data extracted by getSession */\n session: TSession;\n} & InferComposableStandardSchema<Input>;\n\n/**\n * Handler function type for endpoint implementations.\n *\n * @template TInput - Input schemas for validation\n * @template TServices - Available service dependencies\n * @template TLogger - Logger type\n * @template OutSchema - Output schema for response validation\n * @template TSession - Session data type\n *\n * @param ctx - The endpoint execution context\n * @param response - Response builder for setting cookies, headers, and status\n * @returns The response data (validated if OutSchema is provided) or ResponseWithMetadata\n *\n * @example\n * ```typescript\n * // Simple response\n * const handler: EndpointHandler<Input, [UserService], Logger, UserSchema> =\n * async ({ params, services }) => {\n * return await services.users.findById(params.id);\n * };\n *\n * // With response builder\n * const handler: EndpointHandler<Input, [UserService], Logger, UserSchema> =\n * async ({ params, services }, response) => {\n * const user = await services.users.findById(params.id);\n * return response.header('X-User-Id', user.id).send(user);\n * };\n * ```\n */\nexport type EndpointHandler<\n TInput extends EndpointSchemas | undefined = undefined,\n TServices extends Service[] = [],\n TLogger extends Logger = Logger,\n OutSchema extends StandardSchemaV1 | undefined = undefined,\n TSession = unknown,\n> = (\n ctx: EndpointContext<TInput, TServices, TLogger, TSession>,\n response: ResponseBuilder,\n) => OutSchema extends StandardSchemaV1\n ?\n | InferStandardSchema<OutSchema>\n | ResponseWithMetadata<InferStandardSchema<OutSchema>>\n | Promise<InferStandardSchema<OutSchema>>\n | Promise<ResponseWithMetadata<InferStandardSchema<OutSchema>>>\n :\n | unknown\n | ResponseWithMetadata<unknown>\n | Promise<unknown>\n | Promise<ResponseWithMetadata<unknown>>;\n\n/**\n * HTTP success status codes that can be returned by endpoints.\n */\nexport enum SuccessStatus {\n /** Standard response for successful HTTP requests */\n OK = 200,\n /** Request has been fulfilled and resulted in a new resource being created */\n Created = 201,\n /** Request has been accepted for processing, but processing is not complete */\n Accepted = 202,\n /** Server successfully processed the request but is not returning any content */\n NoContent = 204,\n /** Server successfully processed the request and is not returning any content, client should reset the document view */\n ResetContent = 205,\n /** Server is delivering only part of the resource due to a range header */\n PartialContent = 206,\n}\n\nexport type EndpointOutput<T> = T extends Endpoint<\n any,\n any,\n any,\n infer OutSchema,\n any,\n any,\n any,\n any\n>\n ? InferStandardSchema<OutSchema>\n : never;\n\nexport type EndpointEvent<T> = T extends Endpoint<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n infer TEventPublisher\n>\n ? ExtractPublisherMessage<TEventPublisher>\n : never;\n"],"mappings":"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"}
@@ -48,12 +48,16 @@ declare class Endpoint<TRoute extends string, TMethod extends HttpMethod, TInput
48
48
  tags?: string[];
49
49
  /** The HTTP success status code to return (default: 200) */
50
50
  readonly status: SuccessStatus;
51
+ /** Default headers to apply to all responses */
52
+ readonly defaultHeaders: Record<string, string>;
51
53
  /** Function to extract session data from the request context */
52
54
  getSession: SessionFn<TServices, TLogger, TSession>;
53
55
  /** Function to determine if the request is authorized */
54
56
  authorize: AuthorizeFn<TServices, TLogger, TSession>;
55
57
  /** Optional rate limiting configuration */
56
58
  rateLimit?: RateLimitConfig;
59
+ /** The endpoint handler function */
60
+ private endpointFn;
57
61
  /**
58
62
  * Builds a complete OpenAPI 3.1 schema from an array of endpoints.
59
63
  *
@@ -109,6 +113,40 @@ declare class Endpoint<TRoute extends string, TMethod extends HttpMethod, TInput
109
113
  * ```
110
114
  */
111
115
  static createHeaders(headers: Record<string, string>): HeaderFn;
116
+ /**
117
+ * Parses cookie string and creates a cookie lookup function.
118
+ *
119
+ * @param cookieHeader - The Cookie header value
120
+ * @returns Function to retrieve cookie values by name
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * const cookieFn = Endpoint.createCookies('session=abc123; theme=dark');
125
+ * cookieFn('session'); // Returns 'abc123'
126
+ * cookieFn('theme'); // Returns 'dark'
127
+ * ```
128
+ */
129
+ static createCookies(cookieHeader: string | undefined): CookieFn;
130
+ /**
131
+ * Formats a cookie as a Set-Cookie header string.
132
+ *
133
+ * @param name - Cookie name
134
+ * @param value - Cookie value
135
+ * @param options - Cookie options (httpOnly, secure, sameSite, etc.)
136
+ * @returns Formatted Set-Cookie header string
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * const header = Endpoint.formatCookieHeader('session', 'abc123', {
141
+ * httpOnly: true,
142
+ * secure: true,
143
+ * sameSite: 'strict',
144
+ * maxAge: 3600
145
+ * });
146
+ * // Returns: "session=abc123; Max-Age=3600; HttpOnly; Secure; SameSite=Strict"
147
+ * ```
148
+ */
149
+ static formatCookieHeader(name: string, value: string, options?: CookieOptions): string;
112
150
  /**
113
151
  * Extracts and refines input data from the endpoint context.
114
152
  *
@@ -117,7 +155,7 @@ declare class Endpoint<TRoute extends string, TMethod extends HttpMethod, TInput
117
155
  * @internal
118
156
  */
119
157
  refineInput(ctx: EndpointContext<TInput, TServices, TLogger, TSession>): InferComposableStandardSchema<TInput>;
120
- handler: EndpointHandler<TInput, TServices, TLogger, OutSchema, TSession>;
158
+ handler: (ctx: EndpointContext<TInput, TServices, TLogger, TSession>, response: ResponseBuilder) => OutSchema extends StandardSchemaV1 ? InferStandardSchema<OutSchema> | ResponseWithMetadata<InferStandardSchema<OutSchema>> | Promise<InferStandardSchema<OutSchema>> | Promise<ResponseWithMetadata<InferStandardSchema<OutSchema>>> : any | ResponseWithMetadata<any> | Promise<any> | Promise<ResponseWithMetadata<any>>;
121
159
  /**
122
160
  * Type guard to check if an object is an Endpoint instance.
123
161
  *
@@ -125,6 +163,10 @@ declare class Endpoint<TRoute extends string, TMethod extends HttpMethod, TInput
125
163
  * @returns True if the object is an Endpoint
126
164
  */
127
165
  static isEndpoint(obj: any): obj is Endpoint<any, any, any, any>;
166
+ /**
167
+ * Helper to check if response has metadata
168
+ */
169
+ static hasMetadata<T>(response: T | ResponseWithMetadata<T>): response is ResponseWithMetadata<T>;
128
170
  /**
129
171
  * Converts Express-style route params to OpenAPI format.
130
172
  * @returns Route with ':param' converted to '{param}'
@@ -256,6 +298,7 @@ type AuthorizeContext<TServices extends Service[] = [], TLogger extends Logger =
256
298
  services: ServiceRecord<TServices>;
257
299
  logger: TLogger;
258
300
  header: HeaderFn;
301
+ cookie: CookieFn;
259
302
  session: TSession;
260
303
  };
261
304
  /**
@@ -280,6 +323,7 @@ type SessionContext<TServices extends Service[] = [], TLogger extends Logger = L
280
323
  services: ServiceRecord<TServices>;
281
324
  logger: TLogger;
282
325
  header: HeaderFn;
326
+ cookie: CookieFn;
283
327
  };
284
328
  /**
285
329
  * Function type for extracting session data from a request.
@@ -341,9 +385,63 @@ type EndpointHeaders = Map<string, string>;
341
385
  * @returns The header value or undefined if not found
342
386
  */
343
387
  type HeaderFn = SingleHeaderFn;
388
+ /**
389
+ * Function type for retrieving cookie values.
390
+ *
391
+ * @param name - The cookie name
392
+ * @returns The cookie value or undefined if not found
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * const sessionId = cookie('session');
397
+ * ```
398
+ */
399
+ type CookieFn = (name: string) => string | undefined;
400
+ /**
401
+ * Cookie options matching standard Set-Cookie attributes
402
+ */
403
+ interface CookieOptions {
404
+ domain?: string;
405
+ path?: string;
406
+ expires?: Date;
407
+ maxAge?: number;
408
+ httpOnly?: boolean;
409
+ secure?: boolean;
410
+ sameSite?: 'strict' | 'lax' | 'none';
411
+ }
412
+ /**
413
+ * Response metadata that handlers can set
414
+ */
415
+ interface ResponseMetadata {
416
+ headers?: Record<string, string>;
417
+ cookies?: Map<string, {
418
+ value: string;
419
+ options?: CookieOptions;
420
+ }>;
421
+ status?: SuccessStatus;
422
+ }
423
+ /**
424
+ * Return type for handlers that want to set response metadata
425
+ */
426
+ interface ResponseWithMetadata<T> {
427
+ data: T;
428
+ metadata: ResponseMetadata;
429
+ }
430
+ /**
431
+ * Response builder for fluent API in handlers
432
+ */
433
+ declare class ResponseBuilder {
434
+ private metadata;
435
+ header(key: string, value: string): this;
436
+ cookie(name: string, value: string, options?: CookieOptions): this;
437
+ deleteCookie(name: string, options?: Pick<CookieOptions, 'domain' | 'path'>): this;
438
+ status(code: SuccessStatus): this;
439
+ send<T>(data: T): ResponseWithMetadata<T>;
440
+ getMetadata(): ResponseMetadata;
441
+ }
344
442
  /**
345
443
  * The execution context provided to endpoint handlers.
346
- * Contains all parsed input data, services, logger, headers, and session.
444
+ * Contains all parsed input data, services, logger, headers, cookies, and session.
347
445
  *
348
446
  * @template Input - The input schemas (body, query, params)
349
447
  * @template TServices - Available service dependencies
@@ -357,6 +455,8 @@ type EndpointContext<Input extends EndpointSchemas | undefined = undefined, TSer
357
455
  logger: TLogger;
358
456
  /** Function to retrieve request headers */
359
457
  header: HeaderFn;
458
+ /** Function to retrieve request cookies */
459
+ cookie: CookieFn;
360
460
  /** Session data extracted by getSession */
361
461
  session: TSession;
362
462
  } & InferComposableStandardSchema<Input>;
@@ -370,17 +470,26 @@ type EndpointContext<Input extends EndpointSchemas | undefined = undefined, TSer
370
470
  * @template TSession - Session data type
371
471
  *
372
472
  * @param ctx - The endpoint execution context
373
- * @returns The response data (validated if OutSchema is provided)
473
+ * @param response - Response builder for setting cookies, headers, and status
474
+ * @returns The response data (validated if OutSchema is provided) or ResponseWithMetadata
374
475
  *
375
476
  * @example
376
477
  * ```typescript
478
+ * // Simple response
377
479
  * const handler: EndpointHandler<Input, [UserService], Logger, UserSchema> =
378
480
  * async ({ params, services }) => {
379
481
  * return await services.users.findById(params.id);
380
482
  * };
483
+ *
484
+ * // With response builder
485
+ * const handler: EndpointHandler<Input, [UserService], Logger, UserSchema> =
486
+ * async ({ params, services }, response) => {
487
+ * const user = await services.users.findById(params.id);
488
+ * return response.header('X-User-Id', user.id).send(user);
489
+ * };
381
490
  * ```
382
491
  */
383
- type EndpointHandler<TInput extends EndpointSchemas | undefined = undefined, TServices extends Service[] = [], TLogger extends Logger = Logger, OutSchema extends StandardSchemaV1 | undefined = undefined, TSession = unknown> = (ctx: EndpointContext<TInput, TServices, TLogger, TSession>) => OutSchema extends StandardSchemaV1 ? InferStandardSchema<OutSchema> | Promise<InferStandardSchema<OutSchema>> : any | Promise<any>;
492
+ type EndpointHandler<TInput extends EndpointSchemas | undefined = undefined, TServices extends Service[] = [], TLogger extends Logger = Logger, OutSchema extends StandardSchemaV1 | undefined = undefined, TSession = unknown> = (ctx: EndpointContext<TInput, TServices, TLogger, TSession>, response: ResponseBuilder) => OutSchema extends StandardSchemaV1 ? InferStandardSchema<OutSchema> | ResponseWithMetadata<InferStandardSchema<OutSchema>> | Promise<InferStandardSchema<OutSchema>> | Promise<ResponseWithMetadata<InferStandardSchema<OutSchema>>> : unknown | ResponseWithMetadata<unknown> | Promise<unknown> | Promise<ResponseWithMetadata<unknown>>;
384
493
  /**
385
494
  * HTTP success status codes that can be returned by endpoints.
386
495
  */
@@ -401,5 +510,5 @@ declare enum SuccessStatus {
401
510
  type EndpointOutput<T> = T extends Endpoint<any, any, any, infer OutSchema, any, any, any, any> ? InferStandardSchema<OutSchema> : never;
402
511
  type EndpointEvent<T> = T extends Endpoint<any, any, any, any, any, any, any, infer TEventPublisher> ? ExtractPublisherMessage<TEventPublisher> : never;
403
512
  //#endregion
404
- export { AuthorizeContext, AuthorizeFn, ConvertRouteParams, Endpoint, EndpointContext, EndpointEvent, EndpointHandler, EndpointHeaders, EndpointInput, EndpointOpenApiSchema, EndpointOptions, EndpointOutput, EndpointSchemas, HeaderFn, MultiHeaderFn, SessionContext, SessionFn, SingleHeaderFn, SuccessStatus };
405
- //# sourceMappingURL=Endpoint-DYUjJdEs.d.mts.map
513
+ export { AuthorizeContext, AuthorizeFn, ConvertRouteParams, CookieFn, CookieOptions, Endpoint, EndpointContext, EndpointEvent, EndpointHandler, EndpointHeaders, EndpointInput, EndpointOpenApiSchema, EndpointOptions, EndpointOutput, EndpointSchemas, HeaderFn, MultiHeaderFn, ResponseBuilder, ResponseMetadata, ResponseWithMetadata, SessionContext, SessionFn, SingleHeaderFn, SuccessStatus };
514
+ //# sourceMappingURL=Endpoint-C7jPJzAH.d.mts.map
@@ -43,12 +43,16 @@ var Endpoint = class Endpoint extends Function {
43
43
  tags;
44
44
  /** The HTTP success status code to return (default: 200) */
45
45
  status;
46
+ /** Default headers to apply to all responses */
47
+ defaultHeaders = {};
46
48
  /** Function to extract session data from the request context */
47
49
  getSession = () => ({});
48
50
  /** Function to determine if the request is authorized */
49
51
  authorize = () => true;
50
52
  /** Optional rate limiting configuration */
51
53
  rateLimit;
54
+ /** The endpoint handler function */
55
+ endpointFn;
52
56
  /**
53
57
  * Builds a complete OpenAPI 3.1 schema from an array of endpoints.
54
58
  *
@@ -125,6 +129,67 @@ var Endpoint = class Endpoint extends Function {
125
129
  };
126
130
  }
127
131
  /**
132
+ * Parses cookie string and creates a cookie lookup function.
133
+ *
134
+ * @param cookieHeader - The Cookie header value
135
+ * @returns Function to retrieve cookie values by name
136
+ *
137
+ * @example
138
+ * ```typescript
139
+ * const cookieFn = Endpoint.createCookies('session=abc123; theme=dark');
140
+ * cookieFn('session'); // Returns 'abc123'
141
+ * cookieFn('theme'); // Returns 'dark'
142
+ * ```
143
+ */
144
+ static createCookies(cookieHeader) {
145
+ const cookieMap = /* @__PURE__ */ new Map();
146
+ if (cookieHeader) {
147
+ const cookies = cookieHeader.split(";");
148
+ for (const cookie of cookies) {
149
+ const [name, ...valueParts] = cookie.trim().split("=");
150
+ if (name) {
151
+ const value = valueParts.join("=");
152
+ cookieMap.set(name, decodeURIComponent(value));
153
+ }
154
+ }
155
+ }
156
+ return function get(name) {
157
+ return cookieMap.get(name);
158
+ };
159
+ }
160
+ /**
161
+ * Formats a cookie as a Set-Cookie header string.
162
+ *
163
+ * @param name - Cookie name
164
+ * @param value - Cookie value
165
+ * @param options - Cookie options (httpOnly, secure, sameSite, etc.)
166
+ * @returns Formatted Set-Cookie header string
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * const header = Endpoint.formatCookieHeader('session', 'abc123', {
171
+ * httpOnly: true,
172
+ * secure: true,
173
+ * sameSite: 'strict',
174
+ * maxAge: 3600
175
+ * });
176
+ * // Returns: "session=abc123; Max-Age=3600; HttpOnly; Secure; SameSite=Strict"
177
+ * ```
178
+ */
179
+ static formatCookieHeader(name, value, options) {
180
+ let cookie = `${name}=${value}`;
181
+ if (options) {
182
+ if (options.domain) cookie += `; Domain=${options.domain}`;
183
+ if (options.path) cookie += `; Path=${options.path}`;
184
+ if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
185
+ if (options.maxAge !== void 0) cookie += `; Max-Age=${options.maxAge}`;
186
+ if (options.httpOnly) cookie += "; HttpOnly";
187
+ if (options.secure) cookie += "; Secure";
188
+ if (options.sameSite) cookie += `; SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`;
189
+ }
190
+ return cookie;
191
+ }
192
+ /**
128
193
  * Extracts and refines input data from the endpoint context.
129
194
  *
130
195
  * @param ctx - The endpoint execution context
@@ -139,14 +204,16 @@ var Endpoint = class Endpoint extends Function {
139
204
  ]);
140
205
  return input;
141
206
  }
142
- handler = (ctx) => {
143
- return this.fn({
207
+ handler = (ctx, response) => {
208
+ for (const [key, value] of Object.entries(this.defaultHeaders)) response.header(key, value);
209
+ return this.endpointFn({
144
210
  ...this.refineInput(ctx),
145
211
  services: ctx.services,
146
212
  logger: ctx.logger,
147
213
  header: ctx.header,
214
+ cookie: ctx.cookie,
148
215
  session: ctx.session
149
- });
216
+ }, response);
150
217
  };
151
218
  /**
152
219
  * Type guard to check if an object is an Endpoint instance.
@@ -158,6 +225,12 @@ var Endpoint = class Endpoint extends Function {
158
225
  return Boolean(obj && obj.__IS_FUNCTION__ === true && obj.type === ConstructType.Endpoint);
159
226
  }
160
227
  /**
228
+ * Helper to check if response has metadata
229
+ */
230
+ static hasMetadata(response) {
231
+ return response !== null && typeof response === "object" && "data" in response && "metadata" in response;
232
+ }
233
+ /**
161
234
  * Converts Express-style route params to OpenAPI format.
162
235
  * @returns Route with ':param' converted to '{param}'
163
236
  * @internal
@@ -250,12 +323,57 @@ var Endpoint = class Endpoint extends Function {
250
323
  this.description = description;
251
324
  this.tags = tags;
252
325
  this.status = status;
326
+ this.endpointFn = fn;
253
327
  if (getSession) this.getSession = getSession;
254
328
  if (authorize) this.authorize = authorize;
255
329
  if (rateLimit) this.rateLimit = rateLimit;
256
330
  }
257
331
  };
258
332
  /**
333
+ * Response builder for fluent API in handlers
334
+ */
335
+ var ResponseBuilder = class {
336
+ metadata = {
337
+ headers: {},
338
+ cookies: /* @__PURE__ */ new Map()
339
+ };
340
+ header(key, value) {
341
+ this.metadata.headers[key] = value;
342
+ return this;
343
+ }
344
+ cookie(name, value, options) {
345
+ this.metadata.cookies.set(name, {
346
+ value,
347
+ options
348
+ });
349
+ return this;
350
+ }
351
+ deleteCookie(name, options) {
352
+ this.metadata.cookies.set(name, {
353
+ value: "",
354
+ options: {
355
+ ...options,
356
+ maxAge: 0,
357
+ expires: /* @__PURE__ */ new Date(0)
358
+ }
359
+ });
360
+ return this;
361
+ }
362
+ status(code) {
363
+ this.metadata.status = code;
364
+ return this;
365
+ }
366
+ send(data) {
367
+ return {
368
+ data,
369
+ metadata: this.metadata
370
+ };
371
+ }
372
+ getMetadata() {
373
+ return this.metadata;
374
+ }
375
+ };
376
+ /**
259
377
  * HTTP success status codes that can be returned by endpoints.
260
378
  */
261
379
  let SuccessStatus = /* @__PURE__ */ function(SuccessStatus$1) {
@@ -275,5 +393,5 @@ let SuccessStatus = /* @__PURE__ */ function(SuccessStatus$1) {
275
393
  }({});
276
394
 
277
395
  //#endregion
278
- export { Endpoint, SuccessStatus };
279
- //# sourceMappingURL=Endpoint-DNlmybXV.mjs.map
396
+ export { Endpoint, ResponseBuilder, SuccessStatus };
397
+ //# sourceMappingURL=Endpoint-DVEmKo6G.mjs.map