@veloxts/router 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.
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Procedure builder type definitions
3
+ *
4
+ * Provides the generic type accumulation system that enables type-safe
5
+ * procedure building with automatic inference through the fluent API chain.
6
+ *
7
+ * The key insight is using a "state" generic that accumulates type information
8
+ * as methods are called, without requiring explicit type annotations from users.
9
+ *
10
+ * @module procedure/types
11
+ */
12
+ import type { BaseContext } from '@veloxts/core';
13
+ import type { ZodType, ZodTypeDef } from 'zod';
14
+ import type { CompiledProcedure, MiddlewareFunction, ProcedureHandler, RestRouteOverride } from '../types.js';
15
+ /**
16
+ * Internal state type that accumulates type information through the builder chain
17
+ *
18
+ * This is the core type that enables inference flow. Each builder method returns
19
+ * a new ProcedureBuilder with updated state, preserving type information.
20
+ *
21
+ * @template TInput - The validated input type (unknown if no input schema)
22
+ * @template TOutput - The validated output type (unknown if no output schema)
23
+ * @template TContext - The context type (starts as BaseContext, extended by middleware)
24
+ */
25
+ export interface ProcedureBuilderState<TInput = unknown, TOutput = unknown, TContext extends BaseContext = BaseContext> {
26
+ /** Marker for state identification */
27
+ readonly _brand: 'ProcedureBuilderState';
28
+ /** Phantom type holders - not used at runtime */
29
+ readonly _input: TInput;
30
+ readonly _output: TOutput;
31
+ readonly _context: TContext;
32
+ }
33
+ /**
34
+ * Constraint for valid input/output schemas
35
+ *
36
+ * Accepts any Zod schema type. The generic parameters allow us to
37
+ * extract the inferred type for state accumulation.
38
+ */
39
+ export type ValidSchema<T = unknown> = ZodType<T, ZodTypeDef, unknown>;
40
+ /**
41
+ * Extracts the output type from a Zod schema
42
+ *
43
+ * This is used internally to update builder state when .input() or .output() is called.
44
+ */
45
+ export type InferSchemaOutput<T> = T extends ZodType<infer O, ZodTypeDef, unknown> ? O : never;
46
+ /**
47
+ * Fluent procedure builder interface
48
+ *
49
+ * This interface defines all methods available on the procedure builder.
50
+ * Each method returns a new builder with updated type state, enabling
51
+ * full type inference through the chain.
52
+ *
53
+ * @template TInput - Current input type
54
+ * @template TOutput - Current output type
55
+ * @template TContext - Current context type
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * procedure()
60
+ * .input(z.object({ id: z.string() })) // TInput becomes { id: string }
61
+ * .output(UserSchema) // TOutput becomes User
62
+ * .query(async ({ input, ctx }) => { // input: { id: string }, ctx: BaseContext
63
+ * return user; // must return User
64
+ * })
65
+ * ```
66
+ */
67
+ export interface ProcedureBuilder<TInput = unknown, TOutput = unknown, TContext extends BaseContext = BaseContext> {
68
+ /**
69
+ * Defines the input validation schema for the procedure
70
+ *
71
+ * The input type is automatically inferred from the Zod schema.
72
+ * Can only be called once per procedure.
73
+ *
74
+ * @template TSchema - The Zod schema type
75
+ * @param schema - Zod schema for input validation
76
+ * @returns New builder with updated input type
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * procedure()
81
+ * .input(z.object({
82
+ * id: z.string().uuid(),
83
+ * name: z.string().optional(),
84
+ * }))
85
+ * // input is now typed as { id: string; name?: string }
86
+ * ```
87
+ */
88
+ input<TSchema extends ValidSchema>(schema: TSchema): ProcedureBuilder<InferSchemaOutput<TSchema>, TOutput, TContext>;
89
+ /**
90
+ * Defines the output validation schema for the procedure
91
+ *
92
+ * The output type is automatically inferred from the Zod schema.
93
+ * The handler return type will be validated against this schema.
94
+ *
95
+ * @template TSchema - The Zod schema type
96
+ * @param schema - Zod schema for output validation
97
+ * @returns New builder with updated output type
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * procedure()
102
+ * .output(z.object({
103
+ * id: z.string(),
104
+ * name: z.string(),
105
+ * }))
106
+ * // handler must return { id: string; name: string }
107
+ * ```
108
+ */
109
+ output<TSchema extends ValidSchema>(schema: TSchema): ProcedureBuilder<TInput, InferSchemaOutput<TSchema>, TContext>;
110
+ /**
111
+ * Adds middleware to the procedure chain
112
+ *
113
+ * Middleware executes before the handler and can:
114
+ * - Extend the context with new properties
115
+ * - Perform authentication/authorization
116
+ * - Log requests
117
+ * - Modify input (though input schema runs first)
118
+ * - Transform output
119
+ *
120
+ * Multiple middlewares are executed in order of definition.
121
+ *
122
+ * @template TNewContext - The extended context type
123
+ * @param middleware - Middleware function
124
+ * @returns New builder with updated context type
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * procedure()
129
+ * .use(async ({ ctx, next }) => {
130
+ * // Add user to context
131
+ * const user = await getUser(ctx.request);
132
+ * return next({ ctx: { user } });
133
+ * })
134
+ * .query(async ({ ctx }) => {
135
+ * // ctx.user is now available
136
+ * })
137
+ * ```
138
+ */
139
+ use<TNewContext extends BaseContext = TContext>(middleware: MiddlewareFunction<TInput, TContext, TNewContext, TOutput>): ProcedureBuilder<TInput, TOutput, TNewContext>;
140
+ /**
141
+ * Configures REST route override
142
+ *
143
+ * By default, REST routes are auto-generated from procedure names.
144
+ * Use this to customize the HTTP method or path.
145
+ *
146
+ * @param config - REST route configuration
147
+ * @returns Same builder (no type changes)
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * procedure()
152
+ * .rest({ method: 'POST', path: '/users/:id/activate' })
153
+ * ```
154
+ */
155
+ rest(config: RestRouteOverride): ProcedureBuilder<TInput, TOutput, TContext>;
156
+ /**
157
+ * Finalizes the procedure as a query (read-only operation)
158
+ *
159
+ * Queries map to GET requests in REST and should not modify data.
160
+ * The handler receives the validated input and context.
161
+ *
162
+ * @param handler - The query handler function
163
+ * @returns Compiled procedure ready for registration
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * procedure()
168
+ * .input(z.object({ id: z.string() }))
169
+ * .query(async ({ input, ctx }) => {
170
+ * return ctx.db.user.findUnique({ where: { id: input.id } });
171
+ * })
172
+ * ```
173
+ */
174
+ query(handler: ProcedureHandler<TInput, TOutput, TContext>): CompiledProcedure<TInput, TOutput, TContext>;
175
+ /**
176
+ * Finalizes the procedure as a mutation (write operation)
177
+ *
178
+ * Mutations map to POST/PUT/DELETE in REST and can modify data.
179
+ * The handler receives the validated input and context.
180
+ *
181
+ * @param handler - The mutation handler function
182
+ * @returns Compiled procedure ready for registration
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * procedure()
187
+ * .input(CreateUserSchema)
188
+ * .mutation(async ({ input, ctx }) => {
189
+ * return ctx.db.user.create({ data: input });
190
+ * })
191
+ * ```
192
+ */
193
+ mutation(handler: ProcedureHandler<TInput, TOutput, TContext>): CompiledProcedure<TInput, TOutput, TContext>;
194
+ }
195
+ /**
196
+ * Internal runtime state for the procedure builder
197
+ *
198
+ * This holds the actual values during building, separate from the type state.
199
+ * The type state (generics) and runtime state are kept in sync by the builder.
200
+ */
201
+ export interface BuilderRuntimeState {
202
+ /** Input validation schema */
203
+ inputSchema?: ValidSchema;
204
+ /** Output validation schema */
205
+ outputSchema?: ValidSchema;
206
+ /** Middleware chain */
207
+ middlewares: MiddlewareFunction<unknown, BaseContext, BaseContext, unknown>[];
208
+ /** REST route override */
209
+ restOverride?: RestRouteOverride;
210
+ }
211
+ /**
212
+ * Type for the procedures object passed to defineProcedures
213
+ *
214
+ * Each value must be a CompiledProcedure (result of .query() or .mutation()).
215
+ *
216
+ * NOTE: We use `any` here intentionally for the type parameters because:
217
+ * 1. CompiledProcedure has contravariant input types (handler params)
218
+ * 2. TypeScript's Record type requires assignability in both directions
219
+ * 3. Using `unknown` would prevent any concrete procedure from being assigned
220
+ *
221
+ * The actual type safety is preserved through InferProcedures<T> which captures
222
+ * the concrete types at definition time. This `any` only allows the assignment.
223
+ */
224
+ export type ProcedureDefinitions = Record<string, CompiledProcedure<any, any, any>>;
225
+ /**
226
+ * Type helper to preserve procedure types in a collection
227
+ *
228
+ * This ensures that when you call defineProcedures, the full type information
229
+ * of each procedure is preserved and accessible.
230
+ */
231
+ export type InferProcedures<T extends ProcedureDefinitions> = {
232
+ [K in keyof T]: T[K];
233
+ };
234
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/procedure/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Procedure builder type definitions
3
+ *
4
+ * Provides the generic type accumulation system that enables type-safe
5
+ * procedure building with automatic inference through the fluent API chain.
6
+ *
7
+ * The key insight is using a "state" generic that accumulates type information
8
+ * as methods are called, without requiring explicit type annotations from users.
9
+ *
10
+ * @module procedure/types
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/procedure/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * REST adapter for procedure collections
3
+ *
4
+ * Generates REST routes from procedure definitions using naming conventions
5
+ * or manual overrides. Handles input validation, middleware execution, and
6
+ * output serialization.
7
+ *
8
+ * @module rest/adapter
9
+ */
10
+ import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
11
+ import type { CompiledProcedure, HttpMethod, ProcedureCollection } from '../types.js';
12
+ /**
13
+ * REST route definition generated from a procedure
14
+ */
15
+ export interface RestRoute {
16
+ /** HTTP method */
17
+ readonly method: HttpMethod;
18
+ /** Full path including namespace prefix */
19
+ readonly path: string;
20
+ /** Procedure name for debugging */
21
+ readonly procedureName: string;
22
+ /** The compiled procedure */
23
+ readonly procedure: CompiledProcedure;
24
+ }
25
+ /**
26
+ * Options for REST route registration
27
+ */
28
+ export interface RestAdapterOptions {
29
+ /** API prefix (default: '/api') */
30
+ prefix?: string;
31
+ /** Custom error handler */
32
+ onError?: (error: unknown, request: FastifyRequest, reply: FastifyReply) => void;
33
+ }
34
+ /**
35
+ * Generate REST routes from a procedure collection
36
+ *
37
+ * Routes are generated by:
38
+ * 1. Using manual .rest() override if provided
39
+ * 2. Inferring from naming convention (getX, listX, createX)
40
+ * 3. Skipping if neither applies (tRPC-only procedure)
41
+ *
42
+ * @param collection - Procedure collection to generate routes from
43
+ * @returns Array of REST route definitions
44
+ */
45
+ export declare function generateRestRoutes(collection: ProcedureCollection): RestRoute[];
46
+ /**
47
+ * Register REST routes from procedure collections onto a Fastify instance
48
+ *
49
+ * @param server - Fastify instance to register routes on
50
+ * @param collections - Array of procedure collections
51
+ * @param options - Registration options
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import { createVeloxApp } from '@veloxts/core';
56
+ * import { registerRestRoutes, defineProcedures, procedure } from '@veloxts/router';
57
+ *
58
+ * const app = await createVeloxApp();
59
+ *
60
+ * const users = defineProcedures('users', {
61
+ * getUser: procedure()
62
+ * .input(z.object({ id: z.string() }))
63
+ * .query(async ({ input }) => ({ id: input.id, name: 'John' })),
64
+ *
65
+ * listUsers: procedure()
66
+ * .query(async () => [{ id: '1', name: 'John' }]),
67
+ *
68
+ * createUser: procedure()
69
+ * .input(z.object({ name: z.string() }))
70
+ * .mutation(async ({ input }) => ({ id: 'new', name: input.name })),
71
+ * });
72
+ *
73
+ * registerRestRoutes(app.server, [users], { prefix: '/api' });
74
+ *
75
+ * // Generates:
76
+ * // GET /api/users/:id -> getUser
77
+ * // GET /api/users -> listUsers
78
+ * // POST /api/users -> createUser
79
+ * ```
80
+ */
81
+ export declare function registerRestRoutes(server: FastifyInstance, collections: ProcedureCollection[], options?: RestAdapterOptions): void;
82
+ /**
83
+ * Get a summary of routes that would be generated from collections
84
+ *
85
+ * Useful for debugging and documentation.
86
+ *
87
+ * @param collections - Procedure collections to analyze
88
+ * @param prefix - API prefix (default: '/api')
89
+ * @returns Array of route summaries
90
+ */
91
+ export declare function getRouteSummary(collections: ProcedureCollection[], prefix?: string): Array<{
92
+ method: HttpMethod;
93
+ path: string;
94
+ procedure: string;
95
+ namespace: string;
96
+ }>;
97
+ /**
98
+ * Creates a route registrar function for use with VeloxApp.routes()
99
+ *
100
+ * This is a convenience helper that returns a function suitable for
101
+ * passing to app.routes(), enabling a cleaner API.
102
+ *
103
+ * @param collections - Procedure collections to register
104
+ * @param options - Registration options
105
+ * @returns A function that registers routes on a Fastify instance
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * import { createVeloxApp } from '@veloxts/core';
110
+ * import { createRoutesRegistrar, defineProcedures, procedure } from '@veloxts/router';
111
+ *
112
+ * const users = defineProcedures('users', {
113
+ * listUsers: procedure().query(async () => []),
114
+ * });
115
+ *
116
+ * const app = await createVeloxApp();
117
+ *
118
+ * app.routes(createRoutesRegistrar([users], { prefix: '/api' }));
119
+ *
120
+ * await app.start();
121
+ * ```
122
+ */
123
+ export declare function createRoutesRegistrar(collections: ProcedureCollection[], options?: RestAdapterOptions): (server: FastifyInstance) => void;
124
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/rest/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAOtF;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,kBAAkB;IAClB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,6BAA6B;IAC7B,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAClF;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,mBAAmB,GAAG,SAAS,EAAE,CAW/E;AA4ID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,eAAe,EACvB,WAAW,EAAE,mBAAmB,EAAE,EAClC,OAAO,GAAE,kBAAuB,GAC/B,IAAI,CA0BN;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,mBAAmB,EAAE,EAClC,MAAM,SAAS,GACd,KAAK,CAAC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAsBnF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,mBAAmB,EAAE,EAClC,OAAO,GAAE,kBAAuB,GAC/B,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAInC"}
@@ -0,0 +1,262 @@
1
+ /**
2
+ * REST adapter for procedure collections
3
+ *
4
+ * Generates REST routes from procedure definitions using naming conventions
5
+ * or manual overrides. Handles input validation, middleware execution, and
6
+ * output serialization.
7
+ *
8
+ * @module rest/adapter
9
+ */
10
+ import { ConfigurationError } from '@veloxts/core';
11
+ import { executeProcedure } from '../procedure/builder.js';
12
+ import { buildRestPath, parseNamingConvention } from './naming.js';
13
+ // ============================================================================
14
+ // Route Generation
15
+ // ============================================================================
16
+ /**
17
+ * Generate REST routes from a procedure collection
18
+ *
19
+ * Routes are generated by:
20
+ * 1. Using manual .rest() override if provided
21
+ * 2. Inferring from naming convention (getX, listX, createX)
22
+ * 3. Skipping if neither applies (tRPC-only procedure)
23
+ *
24
+ * @param collection - Procedure collection to generate routes from
25
+ * @returns Array of REST route definitions
26
+ */
27
+ export function generateRestRoutes(collection) {
28
+ const routes = [];
29
+ for (const [name, procedure] of Object.entries(collection.procedures)) {
30
+ const route = generateRouteForProcedure(name, procedure, collection.namespace);
31
+ if (route) {
32
+ routes.push(route);
33
+ }
34
+ }
35
+ return routes;
36
+ }
37
+ /**
38
+ * Generate a REST route for a single procedure
39
+ *
40
+ * @internal
41
+ */
42
+ function generateRouteForProcedure(name, procedure, namespace) {
43
+ // Check for manual REST override first
44
+ if (procedure.restOverride) {
45
+ const override = procedure.restOverride;
46
+ // Must have both method and path for override
47
+ if (override.method && override.path) {
48
+ return {
49
+ method: override.method,
50
+ path: override.path,
51
+ procedureName: name,
52
+ procedure,
53
+ };
54
+ }
55
+ // Partial override - try to fill in missing parts from convention
56
+ const convention = parseNamingConvention(name, procedure.type);
57
+ if (convention) {
58
+ return {
59
+ method: override.method ?? convention.method,
60
+ path: override.path ?? buildRestPath(namespace, convention),
61
+ procedureName: name,
62
+ procedure,
63
+ };
64
+ }
65
+ // Can't generate route without full info
66
+ return undefined;
67
+ }
68
+ // Try to infer from naming convention
69
+ const mapping = parseNamingConvention(name, procedure.type);
70
+ if (mapping) {
71
+ // MVP: Only allow GET and POST
72
+ if (mapping.method !== 'GET' && mapping.method !== 'POST') {
73
+ return undefined;
74
+ }
75
+ return {
76
+ method: mapping.method,
77
+ path: buildRestPath(namespace, mapping),
78
+ procedureName: name,
79
+ procedure,
80
+ };
81
+ }
82
+ // No route for this procedure (tRPC-only)
83
+ return undefined;
84
+ }
85
+ // ============================================================================
86
+ // Route Handler Creation
87
+ // ============================================================================
88
+ /**
89
+ * Create a Fastify route handler from a procedure
90
+ *
91
+ * The handler:
92
+ * 1. Extracts input from request (params, query, body)
93
+ * 2. Gets context from request decorator
94
+ * 3. Executes the procedure (validation, middleware, handler)
95
+ * 4. Returns the result
96
+ */
97
+ function createRouteHandler(route) {
98
+ return async (request, reply) => {
99
+ // Gather input from appropriate sources based on method
100
+ const input = gatherInput(request, route);
101
+ // Get context from request (decorated by @veloxts/core)
102
+ const ctx = getContextFromRequest(request);
103
+ // Execute the procedure
104
+ const result = await executeProcedure(route.procedure, input, ctx);
105
+ // For mutations (POST), set 201 status for creates
106
+ if (route.method === 'POST' && route.procedureName.startsWith('create')) {
107
+ reply.status(201);
108
+ }
109
+ return result;
110
+ };
111
+ }
112
+ /**
113
+ * Gather input data from the request based on HTTP method
114
+ *
115
+ * - GET: Merge params and query
116
+ * - POST: Use body
117
+ */
118
+ function gatherInput(request, route) {
119
+ if (route.method === 'GET') {
120
+ // Merge route params and query params
121
+ return {
122
+ ...request.params,
123
+ ...request.query,
124
+ };
125
+ }
126
+ // POST, PUT, PATCH, DELETE: Use body
127
+ return request.body;
128
+ }
129
+ /**
130
+ * Extract context from Fastify request
131
+ *
132
+ * The context is decorated onto the request by @veloxts/core's onRequest hook.
133
+ * This function expects the context to already be present.
134
+ */
135
+ function getContextFromRequest(request) {
136
+ // Type assertion is safe because @veloxts/core decorates this in onRequest hook
137
+ const requestWithContext = request;
138
+ // The context should always be present if @veloxts/core is properly initialized
139
+ // If it's not, we throw an error to help developers debug
140
+ if (!requestWithContext.context) {
141
+ throw new ConfigurationError('Request context not found. Ensure VeloxApp is started before registering routes.');
142
+ }
143
+ return requestWithContext.context;
144
+ }
145
+ // ============================================================================
146
+ // Route Registration
147
+ // ============================================================================
148
+ /**
149
+ * Register REST routes from procedure collections onto a Fastify instance
150
+ *
151
+ * @param server - Fastify instance to register routes on
152
+ * @param collections - Array of procedure collections
153
+ * @param options - Registration options
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * import { createVeloxApp } from '@veloxts/core';
158
+ * import { registerRestRoutes, defineProcedures, procedure } from '@veloxts/router';
159
+ *
160
+ * const app = await createVeloxApp();
161
+ *
162
+ * const users = defineProcedures('users', {
163
+ * getUser: procedure()
164
+ * .input(z.object({ id: z.string() }))
165
+ * .query(async ({ input }) => ({ id: input.id, name: 'John' })),
166
+ *
167
+ * listUsers: procedure()
168
+ * .query(async () => [{ id: '1', name: 'John' }]),
169
+ *
170
+ * createUser: procedure()
171
+ * .input(z.object({ name: z.string() }))
172
+ * .mutation(async ({ input }) => ({ id: 'new', name: input.name })),
173
+ * });
174
+ *
175
+ * registerRestRoutes(app.server, [users], { prefix: '/api' });
176
+ *
177
+ * // Generates:
178
+ * // GET /api/users/:id -> getUser
179
+ * // GET /api/users -> listUsers
180
+ * // POST /api/users -> createUser
181
+ * ```
182
+ */
183
+ export function registerRestRoutes(server, collections, options = {}) {
184
+ const { prefix = '/api' } = options;
185
+ for (const collection of collections) {
186
+ const routes = generateRestRoutes(collection);
187
+ for (const route of routes) {
188
+ const fullPath = `${prefix}${route.path}`;
189
+ const handler = createRouteHandler(route);
190
+ // Register route based on method
191
+ switch (route.method) {
192
+ case 'GET':
193
+ server.get(fullPath, handler);
194
+ break;
195
+ case 'POST':
196
+ server.post(fullPath, handler);
197
+ break;
198
+ // MVP: Only GET and POST
199
+ // v1.1+ will add PUT, PATCH, DELETE
200
+ default:
201
+ // Skip unsupported methods in MVP
202
+ break;
203
+ }
204
+ }
205
+ }
206
+ }
207
+ /**
208
+ * Get a summary of routes that would be generated from collections
209
+ *
210
+ * Useful for debugging and documentation.
211
+ *
212
+ * @param collections - Procedure collections to analyze
213
+ * @param prefix - API prefix (default: '/api')
214
+ * @returns Array of route summaries
215
+ */
216
+ export function getRouteSummary(collections, prefix = '/api') {
217
+ const summaries = [];
218
+ for (const collection of collections) {
219
+ const routes = generateRestRoutes(collection);
220
+ for (const route of routes) {
221
+ summaries.push({
222
+ method: route.method,
223
+ path: `${prefix}${route.path}`,
224
+ procedure: route.procedureName,
225
+ namespace: collection.namespace,
226
+ });
227
+ }
228
+ }
229
+ return summaries;
230
+ }
231
+ /**
232
+ * Creates a route registrar function for use with VeloxApp.routes()
233
+ *
234
+ * This is a convenience helper that returns a function suitable for
235
+ * passing to app.routes(), enabling a cleaner API.
236
+ *
237
+ * @param collections - Procedure collections to register
238
+ * @param options - Registration options
239
+ * @returns A function that registers routes on a Fastify instance
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * import { createVeloxApp } from '@veloxts/core';
244
+ * import { createRoutesRegistrar, defineProcedures, procedure } from '@veloxts/router';
245
+ *
246
+ * const users = defineProcedures('users', {
247
+ * listUsers: procedure().query(async () => []),
248
+ * });
249
+ *
250
+ * const app = await createVeloxApp();
251
+ *
252
+ * app.routes(createRoutesRegistrar([users], { prefix: '/api' }));
253
+ *
254
+ * await app.start();
255
+ * ```
256
+ */
257
+ export function createRoutesRegistrar(collections, options = {}) {
258
+ return (server) => {
259
+ registerRestRoutes(server, collections, options);
260
+ };
261
+ }
262
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/rest/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * REST adapter exports
3
+ *
4
+ * @module rest
5
+ */
6
+ export type { RestAdapterOptions, RestRoute } from './adapter.js';
7
+ export { createRoutesRegistrar, generateRestRoutes, getRouteSummary, registerRestRoutes, } from './adapter.js';
8
+ export type { RestMapping } from './naming.js';
9
+ export { buildRestPath, followsNamingConvention, inferResourceName, parseNamingConvention, } from './naming.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rest/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAElE,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * REST adapter exports
3
+ *
4
+ * @module rest
5
+ */
6
+ // REST adapter - public API
7
+ export { createRoutesRegistrar, generateRestRoutes, getRouteSummary, registerRestRoutes, } from './adapter.js';
8
+ export { buildRestPath, followsNamingConvention, inferResourceName, parseNamingConvention, } from './naming.js';
9
+ //# sourceMappingURL=index.js.map