kontract 0.1.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 (138) hide show
  1. package/README.md +435 -0
  2. package/dist/builder/define-controller.d.ts +115 -0
  3. package/dist/builder/define-controller.d.ts.map +1 -0
  4. package/dist/builder/define-controller.js +80 -0
  5. package/dist/builder/define-controller.js.map +1 -0
  6. package/dist/builder/define-endpoint.d.ts +157 -0
  7. package/dist/builder/define-endpoint.d.ts.map +1 -0
  8. package/dist/builder/define-endpoint.js +103 -0
  9. package/dist/builder/define-endpoint.js.map +1 -0
  10. package/dist/builder/define-route.d.ts +191 -0
  11. package/dist/builder/define-route.d.ts.map +1 -0
  12. package/dist/builder/define-route.js +124 -0
  13. package/dist/builder/define-route.js.map +1 -0
  14. package/dist/builder/index.d.ts +5 -0
  15. package/dist/builder/index.d.ts.map +1 -0
  16. package/dist/builder/index.js +7 -0
  17. package/dist/builder/index.js.map +1 -0
  18. package/dist/builder/openapi-builder.d.ts +120 -0
  19. package/dist/builder/openapi-builder.d.ts.map +1 -0
  20. package/dist/builder/openapi-builder.js +349 -0
  21. package/dist/builder/openapi-builder.js.map +1 -0
  22. package/dist/builder/path-params.d.ts +129 -0
  23. package/dist/builder/path-params.d.ts.map +1 -0
  24. package/dist/builder/path-params.js +85 -0
  25. package/dist/builder/path-params.js.map +1 -0
  26. package/dist/builder/types.d.ts +149 -0
  27. package/dist/builder/types.d.ts.map +1 -0
  28. package/dist/builder/types.js +6 -0
  29. package/dist/builder/types.js.map +1 -0
  30. package/dist/config/defaults.d.ts +10 -0
  31. package/dist/config/defaults.d.ts.map +1 -0
  32. package/dist/config/defaults.js +28 -0
  33. package/dist/config/defaults.js.map +1 -0
  34. package/dist/config/define-config.d.ts +50 -0
  35. package/dist/config/define-config.d.ts.map +1 -0
  36. package/dist/config/define-config.js +80 -0
  37. package/dist/config/define-config.js.map +1 -0
  38. package/dist/config/index.d.ts +4 -0
  39. package/dist/config/index.d.ts.map +1 -0
  40. package/dist/config/index.js +5 -0
  41. package/dist/config/index.js.map +1 -0
  42. package/dist/config/types.d.ts +103 -0
  43. package/dist/config/types.d.ts.map +1 -0
  44. package/dist/config/types.js +2 -0
  45. package/dist/config/types.js.map +1 -0
  46. package/dist/decorators/api.d.ts +35 -0
  47. package/dist/decorators/api.d.ts.map +1 -0
  48. package/dist/decorators/api.js +34 -0
  49. package/dist/decorators/api.js.map +1 -0
  50. package/dist/decorators/controller.d.ts +35 -0
  51. package/dist/decorators/controller.d.ts.map +1 -0
  52. package/dist/decorators/controller.js +34 -0
  53. package/dist/decorators/controller.js.map +1 -0
  54. package/dist/decorators/endpoint.d.ts +93 -0
  55. package/dist/decorators/endpoint.d.ts.map +1 -0
  56. package/dist/decorators/endpoint.js +108 -0
  57. package/dist/decorators/endpoint.js.map +1 -0
  58. package/dist/decorators/index.d.ts +5 -0
  59. package/dist/decorators/index.d.ts.map +1 -0
  60. package/dist/decorators/index.js +6 -0
  61. package/dist/decorators/index.js.map +1 -0
  62. package/dist/decorators/route.d.ts +93 -0
  63. package/dist/decorators/route.d.ts.map +1 -0
  64. package/dist/decorators/route.js +108 -0
  65. package/dist/decorators/route.js.map +1 -0
  66. package/dist/errors/base.d.ts +8 -0
  67. package/dist/errors/base.d.ts.map +1 -0
  68. package/dist/errors/base.js +13 -0
  69. package/dist/errors/base.js.map +1 -0
  70. package/dist/errors/configuration.d.ts +22 -0
  71. package/dist/errors/configuration.d.ts.map +1 -0
  72. package/dist/errors/configuration.js +33 -0
  73. package/dist/errors/configuration.js.map +1 -0
  74. package/dist/errors/index.d.ts +4 -0
  75. package/dist/errors/index.d.ts.map +1 -0
  76. package/dist/errors/index.js +4 -0
  77. package/dist/errors/index.js.map +1 -0
  78. package/dist/errors/validation.d.ts +46 -0
  79. package/dist/errors/validation.d.ts.map +1 -0
  80. package/dist/errors/validation.js +52 -0
  81. package/dist/errors/validation.js.map +1 -0
  82. package/dist/index.d.ts +48 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +44 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/metadata/index.d.ts +2 -0
  87. package/dist/metadata/index.d.ts.map +1 -0
  88. package/dist/metadata/index.js +2 -0
  89. package/dist/metadata/index.js.map +1 -0
  90. package/dist/metadata/storage.d.ts +50 -0
  91. package/dist/metadata/storage.d.ts.map +1 -0
  92. package/dist/metadata/storage.js +100 -0
  93. package/dist/metadata/storage.js.map +1 -0
  94. package/dist/metadata/types.d.ts +142 -0
  95. package/dist/metadata/types.d.ts.map +1 -0
  96. package/dist/metadata/types.js +2 -0
  97. package/dist/metadata/types.js.map +1 -0
  98. package/dist/response/helpers.d.ts +132 -0
  99. package/dist/response/helpers.d.ts.map +1 -0
  100. package/dist/response/helpers.js +197 -0
  101. package/dist/response/helpers.js.map +1 -0
  102. package/dist/response/index.d.ts +4 -0
  103. package/dist/response/index.d.ts.map +1 -0
  104. package/dist/response/index.js +4 -0
  105. package/dist/response/index.js.map +1 -0
  106. package/dist/response/types.d.ts +59 -0
  107. package/dist/response/types.d.ts.map +1 -0
  108. package/dist/response/types.js +26 -0
  109. package/dist/response/types.js.map +1 -0
  110. package/dist/runtime/adapter-types.d.ts +119 -0
  111. package/dist/runtime/adapter-types.d.ts.map +1 -0
  112. package/dist/runtime/adapter-types.js +2 -0
  113. package/dist/runtime/adapter-types.js.map +1 -0
  114. package/dist/runtime/index.d.ts +12 -0
  115. package/dist/runtime/index.d.ts.map +1 -0
  116. package/dist/runtime/index.js +10 -0
  117. package/dist/runtime/index.js.map +1 -0
  118. package/dist/runtime/response-helpers.d.ts +138 -0
  119. package/dist/runtime/response-helpers.d.ts.map +1 -0
  120. package/dist/runtime/response-helpers.js +105 -0
  121. package/dist/runtime/response-helpers.js.map +1 -0
  122. package/dist/runtime/route-utils.d.ts +22 -0
  123. package/dist/runtime/route-utils.d.ts.map +1 -0
  124. package/dist/runtime/route-utils.js +47 -0
  125. package/dist/runtime/route-utils.js.map +1 -0
  126. package/dist/runtime/types.d.ts +125 -0
  127. package/dist/runtime/types.d.ts.map +1 -0
  128. package/dist/runtime/types.js +2 -0
  129. package/dist/runtime/types.js.map +1 -0
  130. package/dist/validation/index.d.ts +3 -0
  131. package/dist/validation/index.d.ts.map +1 -0
  132. package/dist/validation/index.js +3 -0
  133. package/dist/validation/index.js.map +1 -0
  134. package/dist/validation/types.d.ts +55 -0
  135. package/dist/validation/types.d.ts.map +1 -0
  136. package/dist/validation/types.js +2 -0
  137. package/dist/validation/types.js.map +1 -0
  138. package/package.json +93 -0
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Type-safe endpoint builder with automatic type inference.
3
+ *
4
+ * Unlike decorators, this function-based API provides full TypeScript
5
+ * type inference for body, query, and params without manual annotations.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const createUser = defineEndpoint({
10
+ * route: 'POST /api/users',
11
+ * body: CreateUserRequest,
12
+ * responses: { 201: { schema: User } },
13
+ * }, async ({ body }) => {
14
+ * // body is automatically typed as Static<typeof CreateUserRequest>
15
+ * return created(User, await db.users.create(body))
16
+ * })
17
+ * ```
18
+ */
19
+ import type { TSchema, Static } from '@sinclair/typebox';
20
+ import type { AnyResponse } from '../response/types.js';
21
+ import type { HttpMethod, AuthLevel, ResponseDefinition } from '../metadata/types.js';
22
+ /**
23
+ * Route string format: "METHOD /path"
24
+ */
25
+ type RouteString = `${'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'} /${string}`;
26
+ /**
27
+ * Response configuration for endpoints.
28
+ */
29
+ type ResponsesConfig = Record<number, TSchema | null | {
30
+ schema: TSchema | null;
31
+ description?: string;
32
+ }>;
33
+ /**
34
+ * Configuration for defineEndpoint.
35
+ */
36
+ export interface EndpointConfig<TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined> {
37
+ /** Route in format "METHOD /path" (e.g., "POST /api/users") */
38
+ route: RouteString;
39
+ /** Short summary for OpenAPI docs */
40
+ summary?: string;
41
+ /** Detailed description for OpenAPI docs */
42
+ description?: string;
43
+ /** Unique operation ID for OpenAPI */
44
+ operationId?: string;
45
+ /** Mark endpoint as deprecated */
46
+ deprecated?: boolean;
47
+ /** Authentication requirement */
48
+ auth?: AuthLevel;
49
+ /** Request body schema */
50
+ body?: TBody;
51
+ /** Query parameters schema */
52
+ query?: TQuery;
53
+ /** Path parameters schema */
54
+ params?: TParams;
55
+ /** Response definitions by status code */
56
+ responses: ResponsesConfig;
57
+ }
58
+ /**
59
+ * Context object passed to endpoint handlers.
60
+ * Types are automatically inferred from the endpoint configuration.
61
+ */
62
+ export interface HandlerContext<TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined> {
63
+ /** Validated request body (typed from body schema) */
64
+ body: TBody extends TSchema ? Static<TBody> : undefined;
65
+ /** Validated query parameters (typed from query schema) */
66
+ query: TQuery extends TSchema ? Static<TQuery> : undefined;
67
+ /** Validated path parameters (typed from params schema) */
68
+ params: TParams extends TSchema ? Static<TParams> : undefined;
69
+ /** Authenticated user (available when auth is 'required' or 'optional') */
70
+ user: unknown;
71
+ /** Raw framework request object (FastifyRequest, Hono Context, etc.) */
72
+ raw: unknown;
73
+ }
74
+ /**
75
+ * Handler function type with inferred parameter types.
76
+ */
77
+ export type EndpointHandler<TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined> = (ctx: HandlerContext<TBody, TQuery, TParams>) => Promise<AnyResponse> | AnyResponse;
78
+ /**
79
+ * Endpoint definition returned by defineEndpoint.
80
+ * Contains configuration and handler for registration with adapters.
81
+ */
82
+ export interface EndpointDefinition<TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined> {
83
+ /** Type discriminator */
84
+ readonly __type: 'endpoint';
85
+ /** Parsed HTTP method */
86
+ readonly method: HttpMethod;
87
+ /** Parsed path */
88
+ readonly path: string;
89
+ /** Original configuration */
90
+ readonly config: EndpointConfig<TBody, TQuery, TParams>;
91
+ /** Normalized response definitions */
92
+ readonly responses: Record<number, ResponseDefinition>;
93
+ /** Handler function */
94
+ readonly handler: EndpointHandler<TBody, TQuery, TParams>;
95
+ }
96
+ /**
97
+ * Define a type-safe API endpoint with automatic type inference.
98
+ *
99
+ * This function-based approach provides full TypeScript type inference
100
+ * for request body, query parameters, and path parameters without
101
+ * requiring manual `Static<typeof Schema>` annotations.
102
+ *
103
+ * @param config - Endpoint configuration including route, schemas, and responses
104
+ * @param handler - Async function that handles the request
105
+ * @returns EndpointDefinition for registration with framework adapters
106
+ *
107
+ * @example Basic GET endpoint
108
+ * ```typescript
109
+ * const getUser = defineEndpoint({
110
+ * route: 'GET /api/users/:id',
111
+ * params: Type.Object({ id: Type.String({ format: 'uuid' }) }),
112
+ * responses: {
113
+ * 200: { schema: User },
114
+ * 404: null,
115
+ * },
116
+ * }, async ({ params }) => {
117
+ * // params.id is typed as string
118
+ * const user = await findUser(params.id)
119
+ * return user ? ok(User, user) : apiError.notFound()
120
+ * })
121
+ * ```
122
+ *
123
+ * @example POST with body and auth
124
+ * ```typescript
125
+ * const createUser = defineEndpoint({
126
+ * route: 'POST /api/users',
127
+ * auth: 'required',
128
+ * body: CreateUserRequest,
129
+ * responses: { 201: { schema: User } },
130
+ * }, async ({ body, user }) => {
131
+ * // body is typed as { name: string; email: string }
132
+ * return created(User, await db.users.create(body))
133
+ * })
134
+ * ```
135
+ *
136
+ * @example GET with query parameters
137
+ * ```typescript
138
+ * const listUsers = defineEndpoint({
139
+ * route: 'GET /api/users',
140
+ * query: Type.Object({
141
+ * page: Type.Optional(Type.Integer({ minimum: 1, default: 1 })),
142
+ * limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
143
+ * }),
144
+ * responses: { 200: { schema: UserList } },
145
+ * }, async ({ query }) => {
146
+ * // query.page and query.limit are typed
147
+ * return ok(UserList, await db.users.list(query))
148
+ * })
149
+ * ```
150
+ */
151
+ export declare function defineEndpoint<TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined>(config: EndpointConfig<TBody, TQuery, TParams>, handler: EndpointHandler<TBody, TQuery, TParams>): EndpointDefinition<TBody, TQuery, TParams>;
152
+ /**
153
+ * Type guard to check if a value is an EndpointDefinition.
154
+ */
155
+ export declare function isEndpointDefinition(value: unknown): value is EndpointDefinition;
156
+ export {};
157
+ //# sourceMappingURL=define-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-endpoint.d.ts","sourceRoot":"","sources":["../../src/builder/define-endpoint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Parse a route string into method and path.
3
+ */
4
+ function parseRoute(route) {
5
+ const spaceIndex = route.indexOf(' ');
6
+ const method = route.slice(0, spaceIndex).toLowerCase();
7
+ const path = route.slice(spaceIndex + 1);
8
+ return [method, path];
9
+ }
10
+ /**
11
+ * Normalize response definitions to consistent format.
12
+ */
13
+ function normalizeResponses(responses) {
14
+ const normalized = {};
15
+ for (const [status, value] of Object.entries(responses)) {
16
+ if (value === null) {
17
+ normalized[Number(status)] = { schema: null };
18
+ }
19
+ else if (typeof value === 'object' && 'schema' in value) {
20
+ normalized[Number(status)] = value;
21
+ }
22
+ else {
23
+ normalized[Number(status)] = { schema: value };
24
+ }
25
+ }
26
+ return normalized;
27
+ }
28
+ /**
29
+ * Define a type-safe API endpoint with automatic type inference.
30
+ *
31
+ * This function-based approach provides full TypeScript type inference
32
+ * for request body, query parameters, and path parameters without
33
+ * requiring manual `Static<typeof Schema>` annotations.
34
+ *
35
+ * @param config - Endpoint configuration including route, schemas, and responses
36
+ * @param handler - Async function that handles the request
37
+ * @returns EndpointDefinition for registration with framework adapters
38
+ *
39
+ * @example Basic GET endpoint
40
+ * ```typescript
41
+ * const getUser = defineEndpoint({
42
+ * route: 'GET /api/users/:id',
43
+ * params: Type.Object({ id: Type.String({ format: 'uuid' }) }),
44
+ * responses: {
45
+ * 200: { schema: User },
46
+ * 404: null,
47
+ * },
48
+ * }, async ({ params }) => {
49
+ * // params.id is typed as string
50
+ * const user = await findUser(params.id)
51
+ * return user ? ok(User, user) : apiError.notFound()
52
+ * })
53
+ * ```
54
+ *
55
+ * @example POST with body and auth
56
+ * ```typescript
57
+ * const createUser = defineEndpoint({
58
+ * route: 'POST /api/users',
59
+ * auth: 'required',
60
+ * body: CreateUserRequest,
61
+ * responses: { 201: { schema: User } },
62
+ * }, async ({ body, user }) => {
63
+ * // body is typed as { name: string; email: string }
64
+ * return created(User, await db.users.create(body))
65
+ * })
66
+ * ```
67
+ *
68
+ * @example GET with query parameters
69
+ * ```typescript
70
+ * const listUsers = defineEndpoint({
71
+ * route: 'GET /api/users',
72
+ * query: Type.Object({
73
+ * page: Type.Optional(Type.Integer({ minimum: 1, default: 1 })),
74
+ * limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
75
+ * }),
76
+ * responses: { 200: { schema: UserList } },
77
+ * }, async ({ query }) => {
78
+ * // query.page and query.limit are typed
79
+ * return ok(UserList, await db.users.list(query))
80
+ * })
81
+ * ```
82
+ */
83
+ export function defineEndpoint(config, handler) {
84
+ const [method, path] = parseRoute(config.route);
85
+ return {
86
+ __type: 'endpoint',
87
+ method,
88
+ path,
89
+ config,
90
+ responses: normalizeResponses(config.responses),
91
+ handler,
92
+ };
93
+ }
94
+ /**
95
+ * Type guard to check if a value is an EndpointDefinition.
96
+ */
97
+ export function isEndpointDefinition(value) {
98
+ return (typeof value === 'object'
99
+ && value !== null
100
+ && '__type' in value
101
+ && value.__type === 'endpoint');
102
+ }
103
+ //# sourceMappingURL=define-endpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-endpoint.js","sourceRoot":"","sources":["../../src/builder/define-endpoint.ts"],"names":[],"mappings":"AAmHA;;GAEG;AACH,SAAS,UAAU,CAAC,KAAkB;IACpC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACrC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,WAAW,EAAgB,CAAA;IACrE,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAA;IACxC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;AACvB,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,SAA0B;IACpD,MAAM,UAAU,GAAuC,EAAE,CAAA;IAEzD,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACxD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QAC/C,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC1D,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAA2B,CAAA;QAC1D,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAgB,EAAE,CAAA;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,UAAU,cAAc,CAK5B,MAA8C,EAC9C,OAAgD;IAEhD,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAE/C,OAAO;QACL,MAAM,EAAE,UAAU;QAClB,MAAM;QACN,IAAI;QACJ,MAAM;QACN,SAAS,EAAE,kBAAkB,CAAC,MAAM,CAAC,SAAS,CAAC;QAC/C,OAAO;KACR,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;WACtB,KAAK,KAAK,IAAI;WACd,QAAQ,IAAI,KAAK;WAChB,KAA6B,CAAC,MAAM,KAAK,UAAU,CACxD,CAAA;AACH,CAAC"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Type-safe route builder with automatic type inference.
3
+ *
4
+ * Unlike decorators, this function-based API provides full TypeScript
5
+ * type inference for body, query, and params without manual annotations.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const createUser = defineRoute({
10
+ * route: 'POST /api/users',
11
+ * body: CreateUserRequest,
12
+ * responses: { 201: { schema: User } },
13
+ * }, async ({ body }) => {
14
+ * // body is automatically typed as Static<typeof CreateUserRequest>
15
+ * return created(User, await db.users.create(body))
16
+ * })
17
+ * ```
18
+ */
19
+ import type { TSchema, Static } from '@sinclair/typebox';
20
+ import type { AnyResponse } from '../response/types.js';
21
+ import type { HttpMethod, AuthLevel, ResponseDefinition } from '../metadata/types.js';
22
+ /**
23
+ * Route string format: "METHOD /path"
24
+ */
25
+ type RouteString = `${'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'} /${string}`;
26
+ /**
27
+ * Extract the path portion from a route string.
28
+ * @example ExtractPathFromRoute<'GET /users/:id'> = '/users/:id'
29
+ */
30
+ type ExtractPathFromRoute<T extends string> = T extends `${string} ${infer Path}` ? Path : string;
31
+ /**
32
+ * Response configuration for routes.
33
+ */
34
+ type ResponsesConfig = Record<number, TSchema | null | {
35
+ schema: TSchema | null;
36
+ description?: string;
37
+ }>;
38
+ /**
39
+ * Configuration for defineRoute.
40
+ *
41
+ * @typeParam TRoute - The route string type (e.g., 'GET /users/:id') for path param inference
42
+ */
43
+ export interface RouteConfig<TRoute extends RouteString = RouteString, TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined> {
44
+ /** Route in format "METHOD /path" (e.g., "POST /api/users") */
45
+ route: TRoute;
46
+ /** Short summary for OpenAPI docs */
47
+ summary?: string;
48
+ /** Detailed description for OpenAPI docs */
49
+ description?: string;
50
+ /** Unique operation ID for OpenAPI */
51
+ operationId?: string;
52
+ /** Mark route as deprecated */
53
+ deprecated?: boolean;
54
+ /** Authentication requirement */
55
+ auth?: AuthLevel;
56
+ /** Request body schema */
57
+ body?: TBody;
58
+ /** Query parameters schema */
59
+ query?: TQuery;
60
+ /** Path parameters schema */
61
+ params?: TParams;
62
+ /** Response definitions by status code */
63
+ responses: ResponsesConfig;
64
+ }
65
+ /**
66
+ * Context object passed to route handlers.
67
+ * Types are automatically inferred from the route configuration.
68
+ */
69
+ export interface HandlerContext<TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined> {
70
+ /** Validated request body (typed from body schema) */
71
+ body: TBody extends TSchema ? Static<TBody> : undefined;
72
+ /** Validated query parameters (typed from query schema) */
73
+ query: TQuery extends TSchema ? Static<TQuery> : undefined;
74
+ /** Validated path parameters (typed from params schema) */
75
+ params: TParams extends TSchema ? Static<TParams> : undefined;
76
+ /** Authenticated user (available when auth is 'required' or 'optional') */
77
+ user: unknown;
78
+ /** Raw framework request object (FastifyRequest, Hono Context, etc.) */
79
+ raw: unknown;
80
+ }
81
+ /**
82
+ * Handler function type with inferred parameter types.
83
+ */
84
+ export type RouteHandler<TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined> = (ctx: HandlerContext<TBody, TQuery, TParams>) => Promise<AnyResponse> | AnyResponse;
85
+ /**
86
+ * Route definition returned by defineRoute.
87
+ * Contains configuration and optionally a handler for registration with adapters.
88
+ *
89
+ * When used without a handler, the route is a "contract" that can be shared
90
+ * with clients for type-safe API calls.
91
+ *
92
+ * @typeParam TPath - The route path as a literal string type (e.g., '/users/:id')
93
+ * Used for path parameter inference when TParams is not explicitly provided.
94
+ */
95
+ export interface RouteDefinition<TPath extends string = string, TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined> {
96
+ /** Type discriminator */
97
+ readonly __type: 'route';
98
+ /** Parsed HTTP method */
99
+ readonly method: HttpMethod;
100
+ /** Parsed path (typed as literal for param inference) */
101
+ readonly path: TPath;
102
+ /** Original configuration */
103
+ readonly config: RouteConfig<RouteString, TBody, TQuery, TParams>;
104
+ /** Normalized response definitions */
105
+ readonly responses: Record<number, ResponseDefinition>;
106
+ /** Handler function (optional - omitted for contract-only definitions) */
107
+ readonly handler?: RouteHandler<TBody, TQuery, TParams>;
108
+ }
109
+ /**
110
+ * Define a type-safe API route with automatic type inference.
111
+ *
112
+ * This function-based approach provides full TypeScript type inference
113
+ * for request body, query parameters, and path parameters without
114
+ * requiring manual `Static<typeof Schema>` annotations.
115
+ *
116
+ * @param config - Route configuration including path, schemas, and responses
117
+ * @param handler - Optional async function that handles the request.
118
+ * When omitted, creates a "contract-only" definition that can be shared with clients.
119
+ * @returns RouteDefinition for registration with framework adapters or client generation
120
+ *
121
+ * @example Basic GET route with handler (server-side)
122
+ * ```typescript
123
+ * const getUser = defineRoute({
124
+ * route: 'GET /api/users/:id',
125
+ * params: Type.Object({ id: Type.String({ format: 'uuid' }) }),
126
+ * responses: {
127
+ * 200: { schema: User },
128
+ * 404: null,
129
+ * },
130
+ * }, async ({ params }) => {
131
+ * // params.id is typed as string
132
+ * const user = await findUser(params.id)
133
+ * return user ? ok(User, user) : apiError.notFound()
134
+ * })
135
+ * ```
136
+ *
137
+ * @example Contract-only route (for client generation)
138
+ * ```typescript
139
+ * // In shared contracts file - can be imported by both server and client
140
+ * export const getUserContract = defineRoute({
141
+ * route: 'GET /api/users/:id',
142
+ * params: Type.Object({ id: Type.String({ format: 'uuid' }) }),
143
+ * responses: {
144
+ * 200: { schema: User },
145
+ * 404: { schema: ApiError },
146
+ * },
147
+ * })
148
+ *
149
+ * // On server - attach handler
150
+ * const getUser = defineRoute(getUserContract.config, async ({ params }) => { ... })
151
+ *
152
+ * // On client - use contract for type-safe calls
153
+ * const client = createClient({ controllers: { users: usersContracts }, ... })
154
+ * const result = await client.users.getUser({ params: { id: '123' } })
155
+ * ```
156
+ *
157
+ * @example POST with body and auth
158
+ * ```typescript
159
+ * const createUser = defineRoute({
160
+ * route: 'POST /api/users',
161
+ * auth: 'required',
162
+ * body: CreateUserRequest,
163
+ * responses: { 201: { schema: User } },
164
+ * }, async ({ body, user }) => {
165
+ * // body is typed as { name: string; email: string }
166
+ * return created(User, await db.users.create(body))
167
+ * })
168
+ * ```
169
+ *
170
+ * @example GET with query parameters
171
+ * ```typescript
172
+ * const listUsers = defineRoute({
173
+ * route: 'GET /api/users',
174
+ * query: Type.Object({
175
+ * page: Type.Optional(Type.Integer({ minimum: 1, default: 1 })),
176
+ * limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
177
+ * }),
178
+ * responses: { 200: { schema: UserList } },
179
+ * }, async ({ query }) => {
180
+ * // query.page and query.limit are typed
181
+ * return ok(UserList, await db.users.list(query))
182
+ * })
183
+ * ```
184
+ */
185
+ export declare function defineRoute<TRoute extends RouteString, TBody extends TSchema | undefined = undefined, TQuery extends TSchema | undefined = undefined, TParams extends TSchema | undefined = undefined>(config: RouteConfig<TRoute, TBody, TQuery, TParams>, handler?: RouteHandler<TBody, TQuery, TParams>): RouteDefinition<ExtractPathFromRoute<TRoute>, TBody, TQuery, TParams>;
186
+ /**
187
+ * Type guard to check if a value is a RouteDefinition.
188
+ */
189
+ export declare function isRouteDefinition(value: unknown): value is RouteDefinition;
190
+ export {};
191
+ //# sourceMappingURL=define-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-route.d.ts","sourceRoot":"","sources":["../../src/builder/define-route.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Parse a route string into method and path.
3
+ */
4
+ function parseRoute(route) {
5
+ const spaceIndex = route.indexOf(' ');
6
+ const method = route.slice(0, spaceIndex).toLowerCase();
7
+ const path = route.slice(spaceIndex + 1);
8
+ return [method, path];
9
+ }
10
+ /**
11
+ * Normalize response definitions to consistent format.
12
+ */
13
+ function normalizeResponses(responses) {
14
+ const normalized = {};
15
+ for (const [status, value] of Object.entries(responses)) {
16
+ if (value === null) {
17
+ normalized[Number(status)] = { schema: null };
18
+ }
19
+ else if (typeof value === 'object' && 'schema' in value) {
20
+ normalized[Number(status)] = value;
21
+ }
22
+ else {
23
+ normalized[Number(status)] = { schema: value };
24
+ }
25
+ }
26
+ return normalized;
27
+ }
28
+ /**
29
+ * Define a type-safe API route with automatic type inference.
30
+ *
31
+ * This function-based approach provides full TypeScript type inference
32
+ * for request body, query parameters, and path parameters without
33
+ * requiring manual `Static<typeof Schema>` annotations.
34
+ *
35
+ * @param config - Route configuration including path, schemas, and responses
36
+ * @param handler - Optional async function that handles the request.
37
+ * When omitted, creates a "contract-only" definition that can be shared with clients.
38
+ * @returns RouteDefinition for registration with framework adapters or client generation
39
+ *
40
+ * @example Basic GET route with handler (server-side)
41
+ * ```typescript
42
+ * const getUser = defineRoute({
43
+ * route: 'GET /api/users/:id',
44
+ * params: Type.Object({ id: Type.String({ format: 'uuid' }) }),
45
+ * responses: {
46
+ * 200: { schema: User },
47
+ * 404: null,
48
+ * },
49
+ * }, async ({ params }) => {
50
+ * // params.id is typed as string
51
+ * const user = await findUser(params.id)
52
+ * return user ? ok(User, user) : apiError.notFound()
53
+ * })
54
+ * ```
55
+ *
56
+ * @example Contract-only route (for client generation)
57
+ * ```typescript
58
+ * // In shared contracts file - can be imported by both server and client
59
+ * export const getUserContract = defineRoute({
60
+ * route: 'GET /api/users/:id',
61
+ * params: Type.Object({ id: Type.String({ format: 'uuid' }) }),
62
+ * responses: {
63
+ * 200: { schema: User },
64
+ * 404: { schema: ApiError },
65
+ * },
66
+ * })
67
+ *
68
+ * // On server - attach handler
69
+ * const getUser = defineRoute(getUserContract.config, async ({ params }) => { ... })
70
+ *
71
+ * // On client - use contract for type-safe calls
72
+ * const client = createClient({ controllers: { users: usersContracts }, ... })
73
+ * const result = await client.users.getUser({ params: { id: '123' } })
74
+ * ```
75
+ *
76
+ * @example POST with body and auth
77
+ * ```typescript
78
+ * const createUser = defineRoute({
79
+ * route: 'POST /api/users',
80
+ * auth: 'required',
81
+ * body: CreateUserRequest,
82
+ * responses: { 201: { schema: User } },
83
+ * }, async ({ body, user }) => {
84
+ * // body is typed as { name: string; email: string }
85
+ * return created(User, await db.users.create(body))
86
+ * })
87
+ * ```
88
+ *
89
+ * @example GET with query parameters
90
+ * ```typescript
91
+ * const listUsers = defineRoute({
92
+ * route: 'GET /api/users',
93
+ * query: Type.Object({
94
+ * page: Type.Optional(Type.Integer({ minimum: 1, default: 1 })),
95
+ * limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
96
+ * }),
97
+ * responses: { 200: { schema: UserList } },
98
+ * }, async ({ query }) => {
99
+ * // query.page and query.limit are typed
100
+ * return ok(UserList, await db.users.list(query))
101
+ * })
102
+ * ```
103
+ */
104
+ export function defineRoute(config, handler) {
105
+ const [method, path] = parseRoute(config.route);
106
+ return {
107
+ __type: 'route',
108
+ method,
109
+ path: path,
110
+ config,
111
+ responses: normalizeResponses(config.responses),
112
+ handler,
113
+ };
114
+ }
115
+ /**
116
+ * Type guard to check if a value is a RouteDefinition.
117
+ */
118
+ export function isRouteDefinition(value) {
119
+ return (typeof value === 'object'
120
+ && value !== null
121
+ && '__type' in value
122
+ && value.__type === 'route');
123
+ }
124
+ //# sourceMappingURL=define-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-route.js","sourceRoot":"","sources":["../../src/builder/define-route.ts"],"names":[],"mappings":"AAmIA;;GAEG;AACH,SAAS,UAAU,CAAC,KAAkB;IACpC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACrC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,WAAW,EAAgB,CAAA;IACrE,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAA;IACxC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;AACvB,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,SAA0B;IACpD,MAAM,UAAU,GAAuC,EAAE,CAAA;IAEzD,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACxD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QAC/C,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC1D,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAA2B,CAAA;QAC1D,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAgB,EAAE,CAAA;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AACH,MAAM,UAAU,WAAW,CAMzB,MAAmD,EACnD,OAA8C;IAE9C,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAE/C,OAAO;QACL,MAAM,EAAE,OAAO;QACf,MAAM;QACN,IAAI,EAAE,IAAoC;QAC1C,MAAM;QACN,SAAS,EAAE,kBAAkB,CAAC,MAAM,CAAC,SAAS,CAAC;QAC/C,OAAO;KACR,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;WACtB,KAAK,KAAK,IAAI;WACd,QAAQ,IAAI,KAAK;WAChB,KAA6B,CAAC,MAAM,KAAK,OAAO,CACrD,CAAA;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type { OpenApiVersion, OpenApiDocument, OpenApiInfo, OpenApiServer, OpenApiTag, OpenApiPathItem, OpenApiOperation, OpenApiParameter, OpenApiRequestBody, OpenApiResponse, OpenApiMediaType, OpenApiHeader, OpenApiSchema, OpenApiSecurityScheme, OpenApiOAuthFlows, OpenApiOAuthFlow, } from './types.js';
2
+ export { defineRoute, isRouteDefinition, type RouteConfig, type HandlerContext, type RouteHandler, type RouteDefinition, } from './define-route.js';
3
+ export { defineController, isControllerDefinition, getControllerRoutes, type ControllerConfig, type ControllerDefinition, type RouteRecord, type AnyRouteDefinition, } from './define-controller.js';
4
+ export { extractParamNames, createParamsSchema, getParamsSchema, type ExtractRouteParams, type HasPathParams, type ParamsFromPath, type InferParams, } from './path-params.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/builder/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,eAAe,EACf,WAAW,EACX,aAAa,EACb,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAA;AAGnB,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAA;AAG/B,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,7 @@
1
+ // Route builder
2
+ export { defineRoute, isRouteDefinition, } from './define-route.js';
3
+ // Controller builder
4
+ export { defineController, isControllerDefinition, getControllerRoutes, } from './define-controller.js';
5
+ // Path parameter utilities
6
+ export { extractParamNames, createParamsSchema, getParamsSchema, } from './path-params.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.ts"],"names":[],"mappings":"AAmBA,gBAAgB;AAChB,OAAO,EACL,WAAW,EACX,iBAAiB,GAKlB,MAAM,mBAAmB,CAAA;AAE1B,qBAAqB;AACrB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,GAKpB,MAAM,wBAAwB,CAAA;AAE/B,2BAA2B;AAC3B,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,GAKhB,MAAM,kBAAkB,CAAA"}