@objectstack/rest 4.0.4 → 4.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.
@@ -1,308 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { RouteHandler, IHttpServer } from '@objectstack/core';
4
- import { System, Shared } from '@objectstack/spec';
5
- import { z } from 'zod';
6
-
7
- type RouteHandlerMetadata = System.RouteHandlerMetadata;
8
- type HttpMethod = z.infer<typeof Shared.HttpMethod>;
9
-
10
- /**
11
- * Route Entry
12
- * Internal representation of registered routes
13
- */
14
- export interface RouteEntry {
15
- method: HttpMethod;
16
- path: string;
17
- handler: RouteHandler;
18
- metadata?: RouteHandlerMetadata['metadata'];
19
- security?: RouteHandlerMetadata['security'];
20
- }
21
-
22
- /**
23
- * RouteManager
24
- *
25
- * Manages route registration and organization for HTTP servers.
26
- * Provides:
27
- * - Route registration with metadata
28
- * - Route lookup and querying
29
- * - Bulk route registration
30
- * - Route grouping by prefix
31
- *
32
- * @example
33
- * const manager = new RouteManager(server);
34
- *
35
- * // Register individual route
36
- * manager.register({
37
- * method: 'GET',
38
- * path: '/api/users/:id',
39
- * handler: getUserHandler,
40
- * metadata: {
41
- * summary: 'Get user by ID',
42
- * tags: ['users']
43
- * }
44
- * });
45
- *
46
- * // Register route group
47
- * manager.group('/api/users', (group) => {
48
- * group.get('/', listUsersHandler);
49
- * group.post('/', createUserHandler);
50
- * group.get('/:id', getUserHandler);
51
- * });
52
- */
53
- export class RouteManager {
54
- private server: IHttpServer;
55
- private routes: Map<string, RouteEntry>;
56
-
57
- constructor(server: IHttpServer) {
58
- this.server = server;
59
- this.routes = new Map();
60
- }
61
-
62
- /**
63
- * Register a route
64
- * @param entry - Route entry with method, path, handler, and metadata
65
- */
66
- register(entry: Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }): void {
67
- // Validate handler type - string handlers not yet supported
68
- if (typeof entry.handler === 'string') {
69
- throw new Error(
70
- `String-based route handlers are not supported yet. ` +
71
- `Received handler identifier "${entry.handler}". ` +
72
- `Please provide a RouteHandler function instead.`
73
- );
74
- }
75
-
76
- const handler: RouteHandler = entry.handler;
77
-
78
- const routeEntry: RouteEntry = {
79
- method: entry.method,
80
- path: entry.path,
81
- handler,
82
- metadata: entry.metadata,
83
- security: entry.security,
84
- };
85
-
86
- const key = this.getRouteKey(entry.method, entry.path);
87
- this.routes.set(key, routeEntry);
88
-
89
- // Register with underlying server
90
- this.registerWithServer(routeEntry);
91
- }
92
-
93
- /**
94
- * Register multiple routes
95
- * @param entries - Array of route entries
96
- */
97
- registerMany(entries: Array<Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }>): void {
98
- entries.forEach(entry => this.register(entry));
99
- }
100
-
101
- /**
102
- * Unregister a route
103
- * @param method - HTTP method
104
- * @param path - Route path
105
- */
106
- unregister(method: HttpMethod, path: string): void {
107
- const key = this.getRouteKey(method, path);
108
- this.routes.delete(key);
109
- // Note: Most server frameworks don't support unregistering routes at runtime
110
- // This just removes it from our registry
111
- }
112
-
113
- /**
114
- * Get route by method and path
115
- * @param method - HTTP method
116
- * @param path - Route path
117
- */
118
- get(method: HttpMethod, path: string): RouteEntry | undefined {
119
- const key = this.getRouteKey(method, path);
120
- return this.routes.get(key);
121
- }
122
-
123
- /**
124
- * Get all routes
125
- */
126
- getAll(): RouteEntry[] {
127
- return Array.from(this.routes.values());
128
- }
129
-
130
- /**
131
- * Get routes by method
132
- * @param method - HTTP method
133
- */
134
- getByMethod(method: HttpMethod): RouteEntry[] {
135
- return this.getAll().filter(route => route.method === method);
136
- }
137
-
138
- /**
139
- * Get routes by path prefix
140
- * @param prefix - Path prefix
141
- */
142
- getByPrefix(prefix: string): RouteEntry[] {
143
- return this.getAll().filter(route => route.path.startsWith(prefix));
144
- }
145
-
146
- /**
147
- * Get routes by tag
148
- * @param tag - Tag name
149
- */
150
- getByTag(tag: string): RouteEntry[] {
151
- return this.getAll().filter(route =>
152
- route.metadata?.tags?.includes(tag)
153
- );
154
- }
155
-
156
- /**
157
- * Create a route group with common prefix
158
- * @param prefix - Common path prefix
159
- * @param configure - Function to configure routes in the group
160
- */
161
- group(prefix: string, configure: (group: RouteGroupBuilder) => void): void {
162
- const builder = new RouteGroupBuilder(this, prefix);
163
- configure(builder);
164
- }
165
-
166
- /**
167
- * Get route count
168
- */
169
- count(): number {
170
- return this.routes.size;
171
- }
172
-
173
- /**
174
- * Clear all routes
175
- */
176
- clear(): void {
177
- this.routes.clear();
178
- }
179
-
180
- /**
181
- * Get route key for storage
182
- */
183
- private getRouteKey(method: HttpMethod, path: string): string {
184
- return `${method}:${path}`;
185
- }
186
-
187
- /**
188
- * Register route with underlying server
189
- */
190
- private registerWithServer(entry: RouteEntry): void {
191
- const { method, path, handler } = entry;
192
-
193
- switch (method) {
194
- case 'GET':
195
- this.server.get(path, handler);
196
- break;
197
- case 'POST':
198
- this.server.post(path, handler);
199
- break;
200
- case 'PUT':
201
- this.server.put(path, handler);
202
- break;
203
- case 'DELETE':
204
- this.server.delete(path, handler);
205
- break;
206
- case 'PATCH':
207
- this.server.patch(path, handler);
208
- break;
209
- default:
210
- throw new Error(`Unsupported HTTP method: ${method}`);
211
- }
212
- }
213
- }
214
-
215
- /**
216
- * RouteGroupBuilder
217
- *
218
- * Builder for creating route groups with common prefix
219
- */
220
- export class RouteGroupBuilder {
221
- private manager: RouteManager;
222
- private prefix: string;
223
-
224
- constructor(manager: RouteManager, prefix: string) {
225
- this.manager = manager;
226
- this.prefix = prefix;
227
- }
228
-
229
- /**
230
- * Register GET route in group
231
- */
232
- get(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {
233
- this.manager.register({
234
- method: 'GET',
235
- path: this.resolvePath(path),
236
- handler,
237
- metadata,
238
- });
239
- return this;
240
- }
241
-
242
- /**
243
- * Register POST route in group
244
- */
245
- post(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {
246
- this.manager.register({
247
- method: 'POST',
248
- path: this.resolvePath(path),
249
- handler,
250
- metadata,
251
- });
252
- return this;
253
- }
254
-
255
- /**
256
- * Register PUT route in group
257
- */
258
- put(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {
259
- this.manager.register({
260
- method: 'PUT',
261
- path: this.resolvePath(path),
262
- handler,
263
- metadata,
264
- });
265
- return this;
266
- }
267
-
268
- /**
269
- * Register PATCH route in group
270
- */
271
- patch(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {
272
- this.manager.register({
273
- method: 'PATCH',
274
- path: this.resolvePath(path),
275
- handler,
276
- metadata,
277
- });
278
- return this;
279
- }
280
-
281
- /**
282
- * Register DELETE route in group
283
- */
284
- delete(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {
285
- this.manager.register({
286
- method: 'DELETE',
287
- path: this.resolvePath(path),
288
- handler,
289
- metadata,
290
- });
291
- return this;
292
- }
293
-
294
- /**
295
- * Resolve full path with prefix
296
- */
297
- private resolvePath(path: string): string {
298
- // Normalize slashes
299
- const normalizedPrefix = this.prefix.endsWith('/')
300
- ? this.prefix.slice(0, -1)
301
- : this.prefix;
302
- const normalizedPath = path.startsWith('/')
303
- ? path
304
- : '/' + path;
305
-
306
- return normalizedPrefix + normalizedPath;
307
- }
308
- }
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src"
6
- },
7
- "include": ["src/**/*"],
8
- "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
9
- }
package/vitest.config.ts DELETED
@@ -1,10 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { defineConfig } from 'vitest/config';
4
-
5
- export default defineConfig({
6
- test: {
7
- globals: true,
8
- environment: 'node',
9
- },
10
- });