@objectstack/rest 2.0.6 → 3.0.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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +20 -0
- package/dist/index.cjs +33 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -9
- package/dist/index.d.ts +1 -9
- package/dist/index.js +33 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +0 -4
- package/src/rest-api-plugin.ts +0 -10
- package/src/rest-server.ts +26 -12
- package/src/rest.test.ts +4 -13
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/rest@
|
|
2
|
+
> @objectstack/rest@3.0.0 build /home/runner/work/spec/spec/packages/rest
|
|
3
3
|
> tsup --config ../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mCJS[39m [1mdist/index.cjs [22m[32m21.
|
|
14
|
-
[32mCJS[39m [1mdist/index.cjs.map [22m[32m50.
|
|
15
|
-
[32mCJS[39m ⚡️ Build success in
|
|
16
|
-
[32mESM[39m [1mdist/index.js [22m[32m20.
|
|
17
|
-
[32mESM[39m [1mdist/index.js.map [22m[32m50.
|
|
18
|
-
[32mESM[39m ⚡️ Build success in
|
|
13
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m21.98 KB[39m
|
|
14
|
+
[32mCJS[39m [1mdist/index.cjs.map [22m[32m50.95 KB[39m
|
|
15
|
+
[32mCJS[39m ⚡️ Build success in 60ms
|
|
16
|
+
[32mESM[39m [1mdist/index.js [22m[32m20.84 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m50.45 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 64ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m6.
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[32m6.
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 10449ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m6.42 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m6.42 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @objectstack/rest
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- Release v3.0.0 — unified version bump for all ObjectStack packages.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @objectstack/spec@3.0.0
|
|
13
|
+
- @objectstack/core@3.0.0
|
|
14
|
+
|
|
15
|
+
## 2.0.7
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
- @objectstack/spec@2.0.7
|
|
21
|
+
- @objectstack/core@2.0.7
|
|
22
|
+
|
|
3
23
|
## 2.0.6
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -23,7 +23,6 @@ __export(index_exports, {
|
|
|
23
23
|
RestServer: () => RestServer,
|
|
24
24
|
RouteGroupBuilder: () => RouteGroupBuilder,
|
|
25
25
|
RouteManager: () => RouteManager,
|
|
26
|
-
createApiRegistryPlugin: () => createApiRegistryPlugin,
|
|
27
26
|
createRestApiPlugin: () => createRestApiPlugin
|
|
28
27
|
});
|
|
29
28
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -341,32 +340,42 @@ var RestServer = class {
|
|
|
341
340
|
* Register discovery endpoints
|
|
342
341
|
*/
|
|
343
342
|
registerDiscoveryEndpoints(basePath) {
|
|
343
|
+
const discoveryHandler = async (_req, res) => {
|
|
344
|
+
try {
|
|
345
|
+
const discovery = await this.protocol.getDiscovery();
|
|
346
|
+
discovery.version = this.config.api.version;
|
|
347
|
+
if (discovery.routes) {
|
|
348
|
+
if (this.config.api.enableCrud) {
|
|
349
|
+
discovery.routes.data = `${basePath}${this.config.crud.dataPrefix}`;
|
|
350
|
+
}
|
|
351
|
+
if (this.config.api.enableMetadata) {
|
|
352
|
+
discovery.routes.metadata = `${basePath}${this.config.metadata.prefix}`;
|
|
353
|
+
}
|
|
354
|
+
if (this.config.api.enableUi) {
|
|
355
|
+
discovery.routes.ui = `${basePath}/ui`;
|
|
356
|
+
}
|
|
357
|
+
if (discovery.routes.auth) {
|
|
358
|
+
discovery.routes.auth = `${basePath}/auth`;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
res.json(discovery);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
res.status(500).json({ error: error.message });
|
|
364
|
+
}
|
|
365
|
+
};
|
|
344
366
|
this.routeManager.register({
|
|
345
367
|
method: "GET",
|
|
346
368
|
path: basePath,
|
|
347
|
-
handler:
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
if (this.config.api.enableUi) {
|
|
359
|
-
discovery.endpoints.ui = `${basePath}/ui`;
|
|
360
|
-
}
|
|
361
|
-
if (discovery.endpoints.auth) {
|
|
362
|
-
discovery.endpoints.auth = `${basePath}/auth`;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
res.json(discovery);
|
|
366
|
-
} catch (error) {
|
|
367
|
-
res.status(500).json({ error: error.message });
|
|
368
|
-
}
|
|
369
|
-
},
|
|
369
|
+
handler: discoveryHandler,
|
|
370
|
+
metadata: {
|
|
371
|
+
summary: "Get API discovery information",
|
|
372
|
+
tags: ["discovery"]
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
this.routeManager.register({
|
|
376
|
+
method: "GET",
|
|
377
|
+
path: `${basePath}/discovery`,
|
|
378
|
+
handler: discoveryHandler,
|
|
370
379
|
metadata: {
|
|
371
380
|
summary: "Get API discovery information",
|
|
372
381
|
tags: ["discovery"]
|
|
@@ -776,13 +785,11 @@ function createRestApiPlugin(config = {}) {
|
|
|
776
785
|
}
|
|
777
786
|
};
|
|
778
787
|
}
|
|
779
|
-
var createApiRegistryPlugin = createRestApiPlugin;
|
|
780
788
|
// Annotate the CommonJS export names for ESM import in node:
|
|
781
789
|
0 && (module.exports = {
|
|
782
790
|
RestServer,
|
|
783
791
|
RouteGroupBuilder,
|
|
784
792
|
RouteManager,
|
|
785
|
-
createApiRegistryPlugin,
|
|
786
793
|
createRestApiPlugin
|
|
787
794
|
});
|
|
788
795
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/route-manager.ts","../src/rest-server.ts","../src/rest-api-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// REST Server\nexport { RestServer } from './rest-server.js';\n\n// Route Management\nexport { RouteManager, RouteGroupBuilder } from './route-manager.js';\nexport type { RouteEntry } from './route-manager.js';\n\n// REST API Plugin\nexport { createRestApiPlugin } from './rest-api-plugin.js';\nexport type { RestApiPluginConfig } from './rest-api-plugin.js';\n\n// Backward-compatible aliases (deprecated)\nexport { createApiRegistryPlugin } from './rest-api-plugin.js';\nexport type { ApiRegistryConfig } from './rest-api-plugin.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { RouteHandler, IHttpServer } from '@objectstack/core';\nimport { System, Shared } from '@objectstack/spec';\nimport { z } from 'zod';\n\ntype RouteHandlerMetadata = System.RouteHandlerMetadata;\ntype HttpMethod = z.infer<typeof Shared.HttpMethod>;\n\n/**\n * Route Entry\n * Internal representation of registered routes\n */\nexport interface RouteEntry {\n method: HttpMethod;\n path: string;\n handler: RouteHandler;\n metadata?: RouteHandlerMetadata['metadata'];\n security?: RouteHandlerMetadata['security'];\n}\n\n/**\n * RouteManager\n * \n * Manages route registration and organization for HTTP servers.\n * Provides:\n * - Route registration with metadata\n * - Route lookup and querying\n * - Bulk route registration\n * - Route grouping by prefix\n * \n * @example\n * const manager = new RouteManager(server);\n * \n * // Register individual route\n * manager.register({\n * method: 'GET',\n * path: '/api/users/:id',\n * handler: getUserHandler,\n * metadata: {\n * summary: 'Get user by ID',\n * tags: ['users']\n * }\n * });\n * \n * // Register route group\n * manager.group('/api/users', (group) => {\n * group.get('/', listUsersHandler);\n * group.post('/', createUserHandler);\n * group.get('/:id', getUserHandler);\n * });\n */\nexport class RouteManager {\n private server: IHttpServer;\n private routes: Map<string, RouteEntry>;\n \n constructor(server: IHttpServer) {\n this.server = server;\n this.routes = new Map();\n }\n \n /**\n * Register a route\n * @param entry - Route entry with method, path, handler, and metadata\n */\n register(entry: Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }): void {\n // Validate handler type - string handlers not yet supported\n if (typeof entry.handler === 'string') {\n throw new Error(\n `String-based route handlers are not supported yet. ` +\n `Received handler identifier \"${entry.handler}\". ` +\n `Please provide a RouteHandler function instead.`\n );\n }\n \n const handler: RouteHandler = entry.handler;\n \n const routeEntry: RouteEntry = {\n method: entry.method,\n path: entry.path,\n handler,\n metadata: entry.metadata,\n security: entry.security,\n };\n \n const key = this.getRouteKey(entry.method, entry.path);\n this.routes.set(key, routeEntry);\n \n // Register with underlying server\n this.registerWithServer(routeEntry);\n }\n \n /**\n * Register multiple routes\n * @param entries - Array of route entries\n */\n registerMany(entries: Array<Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }>): void {\n entries.forEach(entry => this.register(entry));\n }\n \n /**\n * Unregister a route\n * @param method - HTTP method\n * @param path - Route path\n */\n unregister(method: HttpMethod, path: string): void {\n const key = this.getRouteKey(method, path);\n this.routes.delete(key);\n // Note: Most server frameworks don't support unregistering routes at runtime\n // This just removes it from our registry\n }\n \n /**\n * Get route by method and path\n * @param method - HTTP method\n * @param path - Route path\n */\n get(method: HttpMethod, path: string): RouteEntry | undefined {\n const key = this.getRouteKey(method, path);\n return this.routes.get(key);\n }\n \n /**\n * Get all routes\n */\n getAll(): RouteEntry[] {\n return Array.from(this.routes.values());\n }\n \n /**\n * Get routes by method\n * @param method - HTTP method\n */\n getByMethod(method: HttpMethod): RouteEntry[] {\n return this.getAll().filter(route => route.method === method);\n }\n \n /**\n * Get routes by path prefix\n * @param prefix - Path prefix\n */\n getByPrefix(prefix: string): RouteEntry[] {\n return this.getAll().filter(route => route.path.startsWith(prefix));\n }\n \n /**\n * Get routes by tag\n * @param tag - Tag name\n */\n getByTag(tag: string): RouteEntry[] {\n return this.getAll().filter(route => \n route.metadata?.tags?.includes(tag)\n );\n }\n \n /**\n * Create a route group with common prefix\n * @param prefix - Common path prefix\n * @param configure - Function to configure routes in the group\n */\n group(prefix: string, configure: (group: RouteGroupBuilder) => void): void {\n const builder = new RouteGroupBuilder(this, prefix);\n configure(builder);\n }\n \n /**\n * Get route count\n */\n count(): number {\n return this.routes.size;\n }\n \n /**\n * Clear all routes\n */\n clear(): void {\n this.routes.clear();\n }\n \n /**\n * Get route key for storage\n */\n private getRouteKey(method: HttpMethod, path: string): string {\n return `${method}:${path}`;\n }\n \n /**\n * Register route with underlying server\n */\n private registerWithServer(entry: RouteEntry): void {\n const { method, path, handler } = entry;\n \n switch (method) {\n case 'GET':\n this.server.get(path, handler);\n break;\n case 'POST':\n this.server.post(path, handler);\n break;\n case 'PUT':\n this.server.put(path, handler);\n break;\n case 'DELETE':\n this.server.delete(path, handler);\n break;\n case 'PATCH':\n this.server.patch(path, handler);\n break;\n default:\n throw new Error(`Unsupported HTTP method: ${method}`);\n }\n }\n}\n\n/**\n * RouteGroupBuilder\n * \n * Builder for creating route groups with common prefix\n */\nexport class RouteGroupBuilder {\n private manager: RouteManager;\n private prefix: string;\n \n constructor(manager: RouteManager, prefix: string) {\n this.manager = manager;\n this.prefix = prefix;\n }\n \n /**\n * Register GET route in group\n */\n get(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'GET',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register POST route in group\n */\n post(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'POST',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register PUT route in group\n */\n put(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'PUT',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register PATCH route in group\n */\n patch(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'PATCH',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register DELETE route in group\n */\n delete(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'DELETE',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Resolve full path with prefix\n */\n private resolvePath(path: string): string {\n // Normalize slashes\n const normalizedPrefix = this.prefix.endsWith('/') \n ? this.prefix.slice(0, -1) \n : this.prefix;\n const normalizedPath = path.startsWith('/') \n ? path \n : '/' + path;\n \n return normalizedPrefix + normalizedPath;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { IHttpServer } from '@objectstack/core';\nimport { RouteManager } from './route-manager.js';\nimport { RestServerConfig, RestApiConfig, CrudEndpointsConfig, MetadataEndpointsConfig, BatchEndpointsConfig, RouteGenerationConfig } from '@objectstack/spec/api';\nimport { ObjectStackProtocol } from '@objectstack/spec/api';\n\n/**\n * Normalized REST Server Configuration\n * All nested properties are required after normalization\n */\ntype NormalizedRestServerConfig = {\n api: {\n version: string;\n basePath: string;\n apiPath: string | undefined;\n enableCrud: boolean;\n enableMetadata: boolean;\n enableUi: boolean;\n enableBatch: boolean;\n enableDiscovery: boolean;\n documentation: RestApiConfig['documentation'];\n responseFormat: RestApiConfig['responseFormat'];\n };\n crud: {\n operations: {\n create: boolean;\n read: boolean;\n update: boolean;\n delete: boolean;\n list: boolean;\n };\n patterns: CrudEndpointsConfig['patterns'];\n dataPrefix: string;\n objectParamStyle: 'path' | 'query';\n };\n metadata: {\n prefix: string;\n enableCache: boolean;\n cacheTtl: number;\n endpoints: {\n types: boolean;\n items: boolean;\n item: boolean;\n schema: boolean;\n };\n };\n batch: {\n maxBatchSize: number;\n enableBatchEndpoint: boolean;\n operations: {\n createMany: boolean;\n updateMany: boolean;\n deleteMany: boolean;\n upsertMany: boolean;\n };\n defaultAtomic: boolean;\n };\n routes: {\n includeObjects: string[] | undefined;\n excludeObjects: string[] | undefined;\n nameTransform: 'none' | 'plural' | 'kebab-case' | 'camelCase';\n overrides: RouteGenerationConfig['overrides'];\n };\n};\n\n/**\n * RestServer\n * \n * Provides automatic REST API endpoint generation for ObjectStack.\n * Generates standard RESTful CRUD endpoints, metadata endpoints, and batch operations\n * based on the configured protocol provider.\n * \n * Features:\n * - Automatic CRUD endpoint generation (GET, POST, PUT, PATCH, DELETE)\n * - Metadata API endpoints (/meta)\n * - Batch operation endpoints (/batch, /createMany, /updateMany, /deleteMany)\n * - Discovery endpoint\n * - Configurable path prefixes and patterns\n * \n * @example\n * const restServer = new RestServer(httpServer, protocolProvider, {\n * api: {\n * version: 'v1',\n * basePath: '/api'\n * },\n * crud: {\n * dataPrefix: '/data'\n * }\n * });\n * \n * restServer.registerRoutes();\n */\nexport class RestServer {\n private protocol: ObjectStackProtocol;\n private config: NormalizedRestServerConfig;\n private routeManager: RouteManager;\n \n constructor(\n server: IHttpServer, \n protocol: ObjectStackProtocol, \n config: RestServerConfig = {}\n ) {\n this.protocol = protocol;\n this.config = this.normalizeConfig(config);\n this.routeManager = new RouteManager(server);\n }\n \n /**\n * Normalize configuration with defaults\n */\n private normalizeConfig(config: RestServerConfig): NormalizedRestServerConfig {\n const api = (config.api ?? {}) as Partial<RestApiConfig>;\n const crud = (config.crud ?? {}) as Partial<CrudEndpointsConfig>;\n const metadata = (config.metadata ?? {}) as Partial<MetadataEndpointsConfig>;\n const batch = (config.batch ?? {}) as Partial<BatchEndpointsConfig>;\n const routes = (config.routes ?? {}) as Partial<RouteGenerationConfig>;\n \n return {\n api: {\n version: api.version ?? 'v1',\n basePath: api.basePath ?? '/api',\n apiPath: api.apiPath,\n enableCrud: api.enableCrud ?? true,\n enableMetadata: api.enableMetadata ?? true,\n enableUi: api.enableUi ?? true,\n enableBatch: api.enableBatch ?? true,\n enableDiscovery: api.enableDiscovery ?? true,\n documentation: api.documentation,\n responseFormat: api.responseFormat,\n },\n crud: {\n operations: crud.operations ?? {\n create: true,\n read: true,\n update: true,\n delete: true,\n list: true,\n },\n patterns: crud.patterns,\n dataPrefix: crud.dataPrefix ?? '/data',\n objectParamStyle: crud.objectParamStyle ?? 'path',\n },\n metadata: {\n prefix: metadata.prefix ?? '/meta',\n enableCache: metadata.enableCache ?? true,\n cacheTtl: metadata.cacheTtl ?? 3600,\n endpoints: metadata.endpoints ?? {\n types: true,\n items: true,\n item: true,\n schema: true,\n },\n },\n batch: {\n maxBatchSize: batch.maxBatchSize ?? 200,\n enableBatchEndpoint: batch.enableBatchEndpoint ?? true,\n operations: batch.operations ?? {\n createMany: true,\n updateMany: true,\n deleteMany: true,\n upsertMany: true,\n },\n defaultAtomic: batch.defaultAtomic ?? true,\n },\n routes: {\n includeObjects: routes.includeObjects,\n excludeObjects: routes.excludeObjects,\n nameTransform: routes.nameTransform ?? 'none',\n overrides: routes.overrides,\n },\n };\n }\n \n /**\n * Get the full API base path\n */\n private getApiBasePath(): string {\n const { api } = this.config;\n return api.apiPath ?? `${api.basePath}/${api.version}`;\n }\n \n /**\n * Register all REST API routes\n */\n registerRoutes(): void {\n const basePath = this.getApiBasePath();\n \n // Discovery endpoint\n if (this.config.api.enableDiscovery) {\n this.registerDiscoveryEndpoints(basePath);\n }\n \n // Metadata endpoints\n if (this.config.api.enableMetadata) {\n this.registerMetadataEndpoints(basePath);\n }\n\n // UI endpoints\n if (this.config.api.enableUi) {\n this.registerUiEndpoints(basePath);\n }\n \n // CRUD endpoints\n if (this.config.api.enableCrud) {\n this.registerCrudEndpoints(basePath);\n }\n \n // Batch endpoints\n if (this.config.api.enableBatch) {\n this.registerBatchEndpoints(basePath);\n }\n }\n \n /**\n * Register discovery endpoints\n */\n private registerDiscoveryEndpoints(basePath: string): void {\n this.routeManager.register({\n method: 'GET',\n path: basePath,\n handler: async (_req: any, res: any) => {\n try {\n const discovery = await this.protocol.getDiscovery();\n \n // Override discovery information with actual server configuration\n discovery.version = this.config.api.version;\n \n if (discovery.endpoints) {\n // Ensure endpoints match the actual mounted paths\n if (this.config.api.enableCrud) {\n discovery.endpoints.data = `${basePath}${this.config.crud.dataPrefix}`;\n }\n \n if (this.config.api.enableMetadata) {\n discovery.endpoints.metadata = `${basePath}${this.config.metadata.prefix}`;\n }\n\n if (this.config.api.enableUi) {\n discovery.endpoints.ui = `${basePath}/ui`;\n }\n\n // Align auth endpoint with the versioned base path if present\n if (discovery.endpoints.auth) {\n discovery.endpoints.auth = `${basePath}/auth`;\n }\n }\n\n res.json(discovery);\n } catch (error: any) {\n res.status(500).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get API discovery information',\n tags: ['discovery'],\n },\n });\n }\n \n /**\n * Register metadata endpoints\n */\n private registerMetadataEndpoints(basePath: string): void {\n const { metadata } = this.config;\n const metaPath = `${basePath}${metadata.prefix}`;\n \n // GET /meta - List all metadata types\n if (metadata.endpoints.types !== false) {\n this.routeManager.register({\n method: 'GET',\n path: metaPath,\n handler: async (_req: any, res: any) => {\n try {\n const types = await this.protocol.getMetaTypes();\n res.json(types);\n } catch (error: any) {\n res.status(500).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'List all metadata types',\n tags: ['metadata'],\n },\n });\n }\n \n // GET /meta/:type - List items of a type\n if (metadata.endpoints.items !== false) {\n this.routeManager.register({\n method: 'GET',\n path: `${metaPath}/:type`,\n handler: async (req: any, res: any) => {\n try {\n const items = await this.protocol.getMetaItems({ type: req.params.type });\n res.json(items);\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'List metadata items of a type',\n tags: ['metadata'],\n },\n });\n }\n \n // GET /meta/:type/:name - Get specific item\n if (metadata.endpoints.item !== false) {\n this.routeManager.register({\n method: 'GET',\n path: `${metaPath}/:type/:name`,\n handler: async (req: any, res: any) => {\n try {\n // Check if cached version is available\n if (metadata.enableCache && this.protocol.getMetaItemCached) {\n const cacheRequest = {\n ifNoneMatch: req.headers['if-none-match'] as string,\n ifModifiedSince: req.headers['if-modified-since'] as string,\n };\n \n const result = await this.protocol.getMetaItemCached({\n type: req.params.type,\n name: req.params.name,\n cacheRequest\n });\n \n if (result.notModified) {\n res.status(304).send();\n return;\n }\n \n // Set cache headers\n if (result.etag) {\n const etagValue = result.etag.weak \n ? `W/\"${result.etag.value}\"` \n : `\"${result.etag.value}\"`;\n res.header('ETag', etagValue);\n }\n if (result.lastModified) {\n res.header('Last-Modified', new Date(result.lastModified).toUTCString());\n }\n if (result.cacheControl) {\n const directives = result.cacheControl.directives.join(', ');\n const maxAge = result.cacheControl.maxAge \n ? `, max-age=${result.cacheControl.maxAge}` \n : '';\n res.header('Cache-Control', directives + maxAge);\n }\n \n res.json(result.data);\n } else {\n // Non-cached version\n const item = await this.protocol.getMetaItem({ type: req.params.type, name: req.params.name });\n res.json(item);\n }\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get specific metadata item',\n tags: ['metadata'],\n },\n });\n }\n\n // PUT /meta/:type/:name - Save metadata item\n // We always register this route, but return 501 if protocol doesn't support it\n // This makes it discoverable even if not implemented\n this.routeManager.register({\n method: 'PUT',\n path: `${metaPath}/:type/:name`,\n handler: async (req: any, res: any) => {\n try {\n if (!this.protocol.saveMetaItem) {\n res.status(501).json({ error: 'Save operation not supported by protocol implementation' });\n return;\n }\n\n const result = await this.protocol.saveMetaItem({\n type: req.params.type,\n name: req.params.name,\n item: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Save specific metadata item',\n tags: ['metadata'],\n },\n });\n }\n\n /**\n * Register UI endpoints\n */\n private registerUiEndpoints(basePath: string): void {\n const uiPath = `${basePath}/ui`;\n \n // GET /ui/view/:object/:type - Resolve view for object\n this.routeManager.register({\n method: 'GET',\n path: `${uiPath}/view/:object/:type`,\n handler: async (req: any, res: any) => {\n try {\n if (this.protocol.getUiView) {\n const view = await this.protocol.getUiView({ \n object: req.params.object, \n type: req.params.type as any \n });\n res.json(view);\n } else {\n res.status(501).json({ error: 'UI View resolution not supported by protocol implementation' });\n }\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Resolve UI View for object',\n tags: ['ui'],\n },\n });\n }\n \n /**\n * Register CRUD endpoints for data operations\n */\n private registerCrudEndpoints(basePath: string): void {\n const { crud } = this.config;\n const dataPath = `${basePath}${crud.dataPrefix}`;\n \n const operations = crud.operations;\n \n // GET /data/:object - List/query records\n if (operations.list) {\n this.routeManager.register({\n method: 'GET',\n path: `${dataPath}/:object`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.findData({\n object: req.params.object, \n query: req.query\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Query records',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // GET /data/:object/:id - Get single record\n if (operations.read) {\n this.routeManager.register({\n method: 'GET',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.getData({\n object: req.params.object, \n id: req.params.id\n });\n res.json(result);\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get record by ID',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // POST /data/:object - Create record\n if (operations.create) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.createData({\n object: req.params.object, \n data: req.body\n });\n res.status(201).json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Create record',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // PATCH /data/:object/:id - Update record\n if (operations.update) {\n this.routeManager.register({\n method: 'PATCH',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.updateData({\n object: req.params.object,\n id: req.params.id,\n data: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Update record',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // DELETE /data/:object/:id - Delete record\n if (operations.delete) {\n this.routeManager.register({\n method: 'DELETE',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.deleteData({\n object: req.params.object, \n id: req.params.id\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Delete record',\n tags: ['data', 'crud'],\n },\n });\n }\n }\n \n /**\n * Register batch operation endpoints\n */\n private registerBatchEndpoints(basePath: string): void {\n const { crud, batch } = this.config;\n const dataPath = `${basePath}${crud.dataPrefix}`;\n \n const operations = batch.operations;\n \n // POST /data/:object/batch - Generic batch endpoint\n if (batch.enableBatchEndpoint && this.protocol.batchData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/batch`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.batchData!({\n object: req.params.object, \n request: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Batch operations',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/createMany - Bulk create\n if (operations.createMany && this.protocol.createManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/createMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.createManyData!({\n object: req.params.object,\n records: req.body || []\n });\n res.status(201).json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Create multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/updateMany - Bulk update\n if (operations.updateMany && this.protocol.updateManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/updateMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.updateManyData!({\n object: req.params.object,\n ...req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Update multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/deleteMany - Bulk delete\n if (operations.deleteMany && this.protocol.deleteManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/deleteMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.deleteManyData!({\n object: req.params.object, \n ...req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Delete multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n }\n \n /**\n * Get the route manager\n */\n getRouteManager(): RouteManager {\n return this.routeManager;\n }\n \n /**\n * Get all registered routes\n */\n getRoutes() {\n return this.routeManager.getAll();\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport { RestServer } from './rest-server.js';\nimport { ObjectStackProtocol, RestServerConfig } from '@objectstack/spec/api';\n\nexport interface RestApiPluginConfig {\n serverServiceName?: string;\n protocolServiceName?: string;\n api?: RestServerConfig;\n}\n\n/**\n * @deprecated Use {@link RestApiPluginConfig} instead\n */\nexport type ApiRegistryConfig = RestApiPluginConfig;\n\n/**\n * REST API Plugin\n * \n * Responsibilities:\n * 1. Consumes 'http.server' (or configured service)\n * 2. Consumes 'protocol' (ObjectStackProtocol)\n * 3. Instantiates RestServer to auto-generate routes\n */\nexport function createRestApiPlugin(config: RestApiPluginConfig = {}): Plugin {\n return {\n name: 'com.objectstack.rest.api',\n version: '1.0.0',\n \n init: async (_ctx: PluginContext) => {\n // No service registration, this is a consumer plugin\n },\n \n start: async (ctx: PluginContext) => {\n const serverService = config.serverServiceName || 'http.server';\n const protocolService = config.protocolServiceName || 'protocol';\n \n let server: IHttpServer | undefined;\n let protocol: ObjectStackProtocol | undefined;\n\n try {\n server = ctx.getService<IHttpServer>(serverService);\n } catch (e) {\n // Ignore missing service\n }\n\n try {\n protocol = ctx.getService<ObjectStackProtocol>(protocolService);\n } catch (e) {\n // Ignore missing service\n }\n \n if (!server) {\n ctx.logger.warn(`RestApiPlugin: HTTP Server service '${serverService}' not found. REST routes skipped.`);\n return;\n }\n \n if (!protocol) {\n ctx.logger.warn(`RestApiPlugin: Protocol service '${protocolService}' not found. REST routes skipped.`);\n return;\n }\n \n ctx.logger.info('Hydrating REST API from Protocol...');\n \n try {\n const restServer = new RestServer(server, protocol, config.api as any);\n restServer.registerRoutes();\n \n ctx.logger.info('REST API successfully registered');\n } catch (err: any) {\n ctx.logger.error('Failed to register REST API routes', { error: err.message } as any);\n throw err;\n }\n }\n };\n}\n\n/**\n * @deprecated Use {@link createRestApiPlugin} instead\n */\nexport const createApiRegistryPlugin = createRestApiPlugin;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/route-manager.ts","../src/rest-server.ts","../src/rest-api-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// REST Server\nexport { RestServer } from './rest-server.js';\n\n// Route Management\nexport { RouteManager, RouteGroupBuilder } from './route-manager.js';\nexport type { RouteEntry } from './route-manager.js';\n\n// REST API Plugin\nexport { createRestApiPlugin } from './rest-api-plugin.js';\nexport type { RestApiPluginConfig } from './rest-api-plugin.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { RouteHandler, IHttpServer } from '@objectstack/core';\nimport { System, Shared } from '@objectstack/spec';\nimport { z } from 'zod';\n\ntype RouteHandlerMetadata = System.RouteHandlerMetadata;\ntype HttpMethod = z.infer<typeof Shared.HttpMethod>;\n\n/**\n * Route Entry\n * Internal representation of registered routes\n */\nexport interface RouteEntry {\n method: HttpMethod;\n path: string;\n handler: RouteHandler;\n metadata?: RouteHandlerMetadata['metadata'];\n security?: RouteHandlerMetadata['security'];\n}\n\n/**\n * RouteManager\n * \n * Manages route registration and organization for HTTP servers.\n * Provides:\n * - Route registration with metadata\n * - Route lookup and querying\n * - Bulk route registration\n * - Route grouping by prefix\n * \n * @example\n * const manager = new RouteManager(server);\n * \n * // Register individual route\n * manager.register({\n * method: 'GET',\n * path: '/api/users/:id',\n * handler: getUserHandler,\n * metadata: {\n * summary: 'Get user by ID',\n * tags: ['users']\n * }\n * });\n * \n * // Register route group\n * manager.group('/api/users', (group) => {\n * group.get('/', listUsersHandler);\n * group.post('/', createUserHandler);\n * group.get('/:id', getUserHandler);\n * });\n */\nexport class RouteManager {\n private server: IHttpServer;\n private routes: Map<string, RouteEntry>;\n \n constructor(server: IHttpServer) {\n this.server = server;\n this.routes = new Map();\n }\n \n /**\n * Register a route\n * @param entry - Route entry with method, path, handler, and metadata\n */\n register(entry: Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }): void {\n // Validate handler type - string handlers not yet supported\n if (typeof entry.handler === 'string') {\n throw new Error(\n `String-based route handlers are not supported yet. ` +\n `Received handler identifier \"${entry.handler}\". ` +\n `Please provide a RouteHandler function instead.`\n );\n }\n \n const handler: RouteHandler = entry.handler;\n \n const routeEntry: RouteEntry = {\n method: entry.method,\n path: entry.path,\n handler,\n metadata: entry.metadata,\n security: entry.security,\n };\n \n const key = this.getRouteKey(entry.method, entry.path);\n this.routes.set(key, routeEntry);\n \n // Register with underlying server\n this.registerWithServer(routeEntry);\n }\n \n /**\n * Register multiple routes\n * @param entries - Array of route entries\n */\n registerMany(entries: Array<Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }>): void {\n entries.forEach(entry => this.register(entry));\n }\n \n /**\n * Unregister a route\n * @param method - HTTP method\n * @param path - Route path\n */\n unregister(method: HttpMethod, path: string): void {\n const key = this.getRouteKey(method, path);\n this.routes.delete(key);\n // Note: Most server frameworks don't support unregistering routes at runtime\n // This just removes it from our registry\n }\n \n /**\n * Get route by method and path\n * @param method - HTTP method\n * @param path - Route path\n */\n get(method: HttpMethod, path: string): RouteEntry | undefined {\n const key = this.getRouteKey(method, path);\n return this.routes.get(key);\n }\n \n /**\n * Get all routes\n */\n getAll(): RouteEntry[] {\n return Array.from(this.routes.values());\n }\n \n /**\n * Get routes by method\n * @param method - HTTP method\n */\n getByMethod(method: HttpMethod): RouteEntry[] {\n return this.getAll().filter(route => route.method === method);\n }\n \n /**\n * Get routes by path prefix\n * @param prefix - Path prefix\n */\n getByPrefix(prefix: string): RouteEntry[] {\n return this.getAll().filter(route => route.path.startsWith(prefix));\n }\n \n /**\n * Get routes by tag\n * @param tag - Tag name\n */\n getByTag(tag: string): RouteEntry[] {\n return this.getAll().filter(route => \n route.metadata?.tags?.includes(tag)\n );\n }\n \n /**\n * Create a route group with common prefix\n * @param prefix - Common path prefix\n * @param configure - Function to configure routes in the group\n */\n group(prefix: string, configure: (group: RouteGroupBuilder) => void): void {\n const builder = new RouteGroupBuilder(this, prefix);\n configure(builder);\n }\n \n /**\n * Get route count\n */\n count(): number {\n return this.routes.size;\n }\n \n /**\n * Clear all routes\n */\n clear(): void {\n this.routes.clear();\n }\n \n /**\n * Get route key for storage\n */\n private getRouteKey(method: HttpMethod, path: string): string {\n return `${method}:${path}`;\n }\n \n /**\n * Register route with underlying server\n */\n private registerWithServer(entry: RouteEntry): void {\n const { method, path, handler } = entry;\n \n switch (method) {\n case 'GET':\n this.server.get(path, handler);\n break;\n case 'POST':\n this.server.post(path, handler);\n break;\n case 'PUT':\n this.server.put(path, handler);\n break;\n case 'DELETE':\n this.server.delete(path, handler);\n break;\n case 'PATCH':\n this.server.patch(path, handler);\n break;\n default:\n throw new Error(`Unsupported HTTP method: ${method}`);\n }\n }\n}\n\n/**\n * RouteGroupBuilder\n * \n * Builder for creating route groups with common prefix\n */\nexport class RouteGroupBuilder {\n private manager: RouteManager;\n private prefix: string;\n \n constructor(manager: RouteManager, prefix: string) {\n this.manager = manager;\n this.prefix = prefix;\n }\n \n /**\n * Register GET route in group\n */\n get(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'GET',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register POST route in group\n */\n post(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'POST',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register PUT route in group\n */\n put(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'PUT',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register PATCH route in group\n */\n patch(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'PATCH',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register DELETE route in group\n */\n delete(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'DELETE',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Resolve full path with prefix\n */\n private resolvePath(path: string): string {\n // Normalize slashes\n const normalizedPrefix = this.prefix.endsWith('/') \n ? this.prefix.slice(0, -1) \n : this.prefix;\n const normalizedPath = path.startsWith('/') \n ? path \n : '/' + path;\n \n return normalizedPrefix + normalizedPath;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { IHttpServer } from '@objectstack/core';\nimport { RouteManager } from './route-manager.js';\nimport { RestServerConfig, RestApiConfig, CrudEndpointsConfig, MetadataEndpointsConfig, BatchEndpointsConfig, RouteGenerationConfig } from '@objectstack/spec/api';\nimport { ObjectStackProtocol } from '@objectstack/spec/api';\n\n/**\n * Normalized REST Server Configuration\n * All nested properties are required after normalization\n */\ntype NormalizedRestServerConfig = {\n api: {\n version: string;\n basePath: string;\n apiPath: string | undefined;\n enableCrud: boolean;\n enableMetadata: boolean;\n enableUi: boolean;\n enableBatch: boolean;\n enableDiscovery: boolean;\n documentation: RestApiConfig['documentation'];\n responseFormat: RestApiConfig['responseFormat'];\n };\n crud: {\n operations: {\n create: boolean;\n read: boolean;\n update: boolean;\n delete: boolean;\n list: boolean;\n };\n patterns: CrudEndpointsConfig['patterns'];\n dataPrefix: string;\n objectParamStyle: 'path' | 'query';\n };\n metadata: {\n prefix: string;\n enableCache: boolean;\n cacheTtl: number;\n endpoints: {\n types: boolean;\n items: boolean;\n item: boolean;\n schema: boolean;\n };\n };\n batch: {\n maxBatchSize: number;\n enableBatchEndpoint: boolean;\n operations: {\n createMany: boolean;\n updateMany: boolean;\n deleteMany: boolean;\n upsertMany: boolean;\n };\n defaultAtomic: boolean;\n };\n routes: {\n includeObjects: string[] | undefined;\n excludeObjects: string[] | undefined;\n nameTransform: 'none' | 'plural' | 'kebab-case' | 'camelCase';\n overrides: RouteGenerationConfig['overrides'];\n };\n};\n\n/**\n * RestServer\n * \n * Provides automatic REST API endpoint generation for ObjectStack.\n * Generates standard RESTful CRUD endpoints, metadata endpoints, and batch operations\n * based on the configured protocol provider.\n * \n * Features:\n * - Automatic CRUD endpoint generation (GET, POST, PUT, PATCH, DELETE)\n * - Metadata API endpoints (/meta)\n * - Batch operation endpoints (/batch, /createMany, /updateMany, /deleteMany)\n * - Discovery endpoint\n * - Configurable path prefixes and patterns\n * \n * @example\n * const restServer = new RestServer(httpServer, protocolProvider, {\n * api: {\n * version: 'v1',\n * basePath: '/api'\n * },\n * crud: {\n * dataPrefix: '/data'\n * }\n * });\n * \n * restServer.registerRoutes();\n */\nexport class RestServer {\n private protocol: ObjectStackProtocol;\n private config: NormalizedRestServerConfig;\n private routeManager: RouteManager;\n \n constructor(\n server: IHttpServer, \n protocol: ObjectStackProtocol, \n config: RestServerConfig = {}\n ) {\n this.protocol = protocol;\n this.config = this.normalizeConfig(config);\n this.routeManager = new RouteManager(server);\n }\n \n /**\n * Normalize configuration with defaults\n */\n private normalizeConfig(config: RestServerConfig): NormalizedRestServerConfig {\n const api = (config.api ?? {}) as Partial<RestApiConfig>;\n const crud = (config.crud ?? {}) as Partial<CrudEndpointsConfig>;\n const metadata = (config.metadata ?? {}) as Partial<MetadataEndpointsConfig>;\n const batch = (config.batch ?? {}) as Partial<BatchEndpointsConfig>;\n const routes = (config.routes ?? {}) as Partial<RouteGenerationConfig>;\n \n return {\n api: {\n version: api.version ?? 'v1',\n basePath: api.basePath ?? '/api',\n apiPath: api.apiPath,\n enableCrud: api.enableCrud ?? true,\n enableMetadata: api.enableMetadata ?? true,\n enableUi: api.enableUi ?? true,\n enableBatch: api.enableBatch ?? true,\n enableDiscovery: api.enableDiscovery ?? true,\n documentation: api.documentation,\n responseFormat: api.responseFormat,\n },\n crud: {\n operations: crud.operations ?? {\n create: true,\n read: true,\n update: true,\n delete: true,\n list: true,\n },\n patterns: crud.patterns,\n dataPrefix: crud.dataPrefix ?? '/data',\n objectParamStyle: crud.objectParamStyle ?? 'path',\n },\n metadata: {\n prefix: metadata.prefix ?? '/meta',\n enableCache: metadata.enableCache ?? true,\n cacheTtl: metadata.cacheTtl ?? 3600,\n endpoints: metadata.endpoints ?? {\n types: true,\n items: true,\n item: true,\n schema: true,\n },\n },\n batch: {\n maxBatchSize: batch.maxBatchSize ?? 200,\n enableBatchEndpoint: batch.enableBatchEndpoint ?? true,\n operations: batch.operations ?? {\n createMany: true,\n updateMany: true,\n deleteMany: true,\n upsertMany: true,\n },\n defaultAtomic: batch.defaultAtomic ?? true,\n },\n routes: {\n includeObjects: routes.includeObjects,\n excludeObjects: routes.excludeObjects,\n nameTransform: routes.nameTransform ?? 'none',\n overrides: routes.overrides,\n },\n };\n }\n \n /**\n * Get the full API base path\n */\n private getApiBasePath(): string {\n const { api } = this.config;\n return api.apiPath ?? `${api.basePath}/${api.version}`;\n }\n \n /**\n * Register all REST API routes\n */\n registerRoutes(): void {\n const basePath = this.getApiBasePath();\n \n // Discovery endpoint\n if (this.config.api.enableDiscovery) {\n this.registerDiscoveryEndpoints(basePath);\n }\n \n // Metadata endpoints\n if (this.config.api.enableMetadata) {\n this.registerMetadataEndpoints(basePath);\n }\n\n // UI endpoints\n if (this.config.api.enableUi) {\n this.registerUiEndpoints(basePath);\n }\n \n // CRUD endpoints\n if (this.config.api.enableCrud) {\n this.registerCrudEndpoints(basePath);\n }\n \n // Batch endpoints\n if (this.config.api.enableBatch) {\n this.registerBatchEndpoints(basePath);\n }\n }\n \n /**\n * Register discovery endpoints\n */\n private registerDiscoveryEndpoints(basePath: string): void {\n const discoveryHandler = async (_req: any, res: any) => {\n try {\n const discovery = await this.protocol.getDiscovery();\n \n // Override discovery information with actual server configuration\n discovery.version = this.config.api.version;\n \n if (discovery.routes) {\n // Ensure routes match the actual mounted paths\n if (this.config.api.enableCrud) {\n discovery.routes.data = `${basePath}${this.config.crud.dataPrefix}`;\n }\n \n if (this.config.api.enableMetadata) {\n discovery.routes.metadata = `${basePath}${this.config.metadata.prefix}`;\n }\n\n if (this.config.api.enableUi) {\n discovery.routes.ui = `${basePath}/ui`;\n }\n\n // Align auth route with the versioned base path if present\n if (discovery.routes.auth) {\n discovery.routes.auth = `${basePath}/auth`;\n }\n }\n\n res.json(discovery);\n } catch (error: any) {\n res.status(500).json({ error: error.message });\n }\n };\n\n // Register at basePath (e.g. /api/v1)\n this.routeManager.register({\n method: 'GET',\n path: basePath,\n handler: discoveryHandler,\n metadata: {\n summary: 'Get API discovery information',\n tags: ['discovery'],\n },\n });\n\n // Register at basePath/discovery (e.g. /api/v1/discovery)\n this.routeManager.register({\n method: 'GET',\n path: `${basePath}/discovery`,\n handler: discoveryHandler,\n metadata: {\n summary: 'Get API discovery information',\n tags: ['discovery'],\n },\n });\n }\n \n /**\n * Register metadata endpoints\n */\n private registerMetadataEndpoints(basePath: string): void {\n const { metadata } = this.config;\n const metaPath = `${basePath}${metadata.prefix}`;\n \n // GET /meta - List all metadata types\n if (metadata.endpoints.types !== false) {\n this.routeManager.register({\n method: 'GET',\n path: metaPath,\n handler: async (_req: any, res: any) => {\n try {\n const types = await this.protocol.getMetaTypes();\n res.json(types);\n } catch (error: any) {\n res.status(500).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'List all metadata types',\n tags: ['metadata'],\n },\n });\n }\n \n // GET /meta/:type - List items of a type\n if (metadata.endpoints.items !== false) {\n this.routeManager.register({\n method: 'GET',\n path: `${metaPath}/:type`,\n handler: async (req: any, res: any) => {\n try {\n const items = await this.protocol.getMetaItems({ type: req.params.type });\n res.json(items);\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'List metadata items of a type',\n tags: ['metadata'],\n },\n });\n }\n \n // GET /meta/:type/:name - Get specific item\n if (metadata.endpoints.item !== false) {\n this.routeManager.register({\n method: 'GET',\n path: `${metaPath}/:type/:name`,\n handler: async (req: any, res: any) => {\n try {\n // Check if cached version is available\n if (metadata.enableCache && this.protocol.getMetaItemCached) {\n const cacheRequest = {\n ifNoneMatch: req.headers['if-none-match'] as string,\n ifModifiedSince: req.headers['if-modified-since'] as string,\n };\n \n const result = await this.protocol.getMetaItemCached({\n type: req.params.type,\n name: req.params.name,\n cacheRequest\n });\n \n if (result.notModified) {\n res.status(304).send();\n return;\n }\n \n // Set cache headers\n if (result.etag) {\n const etagValue = result.etag.weak \n ? `W/\"${result.etag.value}\"` \n : `\"${result.etag.value}\"`;\n res.header('ETag', etagValue);\n }\n if (result.lastModified) {\n res.header('Last-Modified', new Date(result.lastModified).toUTCString());\n }\n if (result.cacheControl) {\n const directives = result.cacheControl.directives.join(', ');\n const maxAge = result.cacheControl.maxAge \n ? `, max-age=${result.cacheControl.maxAge}` \n : '';\n res.header('Cache-Control', directives + maxAge);\n }\n \n res.json(result.data);\n } else {\n // Non-cached version\n const item = await this.protocol.getMetaItem({ type: req.params.type, name: req.params.name });\n res.json(item);\n }\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get specific metadata item',\n tags: ['metadata'],\n },\n });\n }\n\n // PUT /meta/:type/:name - Save metadata item\n // We always register this route, but return 501 if protocol doesn't support it\n // This makes it discoverable even if not implemented\n this.routeManager.register({\n method: 'PUT',\n path: `${metaPath}/:type/:name`,\n handler: async (req: any, res: any) => {\n try {\n if (!this.protocol.saveMetaItem) {\n res.status(501).json({ error: 'Save operation not supported by protocol implementation' });\n return;\n }\n\n const result = await this.protocol.saveMetaItem({\n type: req.params.type,\n name: req.params.name,\n item: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Save specific metadata item',\n tags: ['metadata'],\n },\n });\n }\n\n /**\n * Register UI endpoints\n */\n private registerUiEndpoints(basePath: string): void {\n const uiPath = `${basePath}/ui`;\n \n // GET /ui/view/:object/:type - Resolve view for object\n this.routeManager.register({\n method: 'GET',\n path: `${uiPath}/view/:object/:type`,\n handler: async (req: any, res: any) => {\n try {\n if (this.protocol.getUiView) {\n const view = await this.protocol.getUiView({ \n object: req.params.object, \n type: req.params.type as any \n });\n res.json(view);\n } else {\n res.status(501).json({ error: 'UI View resolution not supported by protocol implementation' });\n }\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Resolve UI View for object',\n tags: ['ui'],\n },\n });\n }\n \n /**\n * Register CRUD endpoints for data operations\n */\n private registerCrudEndpoints(basePath: string): void {\n const { crud } = this.config;\n const dataPath = `${basePath}${crud.dataPrefix}`;\n \n const operations = crud.operations;\n \n // GET /data/:object - List/query records\n if (operations.list) {\n this.routeManager.register({\n method: 'GET',\n path: `${dataPath}/:object`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.findData({\n object: req.params.object, \n query: req.query\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Query records',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // GET /data/:object/:id - Get single record\n if (operations.read) {\n this.routeManager.register({\n method: 'GET',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.getData({\n object: req.params.object, \n id: req.params.id\n });\n res.json(result);\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get record by ID',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // POST /data/:object - Create record\n if (operations.create) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.createData({\n object: req.params.object, \n data: req.body\n });\n res.status(201).json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Create record',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // PATCH /data/:object/:id - Update record\n if (operations.update) {\n this.routeManager.register({\n method: 'PATCH',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.updateData({\n object: req.params.object,\n id: req.params.id,\n data: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Update record',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // DELETE /data/:object/:id - Delete record\n if (operations.delete) {\n this.routeManager.register({\n method: 'DELETE',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.deleteData({\n object: req.params.object, \n id: req.params.id\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Delete record',\n tags: ['data', 'crud'],\n },\n });\n }\n }\n \n /**\n * Register batch operation endpoints\n */\n private registerBatchEndpoints(basePath: string): void {\n const { crud, batch } = this.config;\n const dataPath = `${basePath}${crud.dataPrefix}`;\n \n const operations = batch.operations;\n \n // POST /data/:object/batch - Generic batch endpoint\n if (batch.enableBatchEndpoint && this.protocol.batchData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/batch`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.batchData!({\n object: req.params.object, \n request: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Batch operations',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/createMany - Bulk create\n if (operations.createMany && this.protocol.createManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/createMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.createManyData!({\n object: req.params.object,\n records: req.body || []\n });\n res.status(201).json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Create multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/updateMany - Bulk update\n if (operations.updateMany && this.protocol.updateManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/updateMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.updateManyData!({\n object: req.params.object,\n ...req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Update multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/deleteMany - Bulk delete\n if (operations.deleteMany && this.protocol.deleteManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/deleteMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.deleteManyData!({\n object: req.params.object, \n ...req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Delete multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n }\n \n /**\n * Get the route manager\n */\n getRouteManager(): RouteManager {\n return this.routeManager;\n }\n \n /**\n * Get all registered routes\n */\n getRoutes() {\n return this.routeManager.getAll();\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport { RestServer } from './rest-server.js';\nimport { ObjectStackProtocol, RestServerConfig } from '@objectstack/spec/api';\n\nexport interface RestApiPluginConfig {\n serverServiceName?: string;\n protocolServiceName?: string;\n api?: RestServerConfig;\n}\n\n/**\n * REST API Plugin\n * \n * Responsibilities:\n * 1. Consumes 'http.server' (or configured service)\n * 2. Consumes 'protocol' (ObjectStackProtocol)\n * 3. Instantiates RestServer to auto-generate routes\n */\nexport function createRestApiPlugin(config: RestApiPluginConfig = {}): Plugin {\n return {\n name: 'com.objectstack.rest.api',\n version: '1.0.0',\n \n init: async (_ctx: PluginContext) => {\n // No service registration, this is a consumer plugin\n },\n \n start: async (ctx: PluginContext) => {\n const serverService = config.serverServiceName || 'http.server';\n const protocolService = config.protocolServiceName || 'protocol';\n \n let server: IHttpServer | undefined;\n let protocol: ObjectStackProtocol | undefined;\n\n try {\n server = ctx.getService<IHttpServer>(serverService);\n } catch (e) {\n // Ignore missing service\n }\n\n try {\n protocol = ctx.getService<ObjectStackProtocol>(protocolService);\n } catch (e) {\n // Ignore missing service\n }\n \n if (!server) {\n ctx.logger.warn(`RestApiPlugin: HTTP Server service '${serverService}' not found. REST routes skipped.`);\n return;\n }\n \n if (!protocol) {\n ctx.logger.warn(`RestApiPlugin: Protocol service '${protocolService}' not found. REST routes skipped.`);\n return;\n }\n \n ctx.logger.info('Hydrating REST API from Protocol...');\n \n try {\n const restServer = new RestServer(server, protocol, config.api as any);\n restServer.registerRoutes();\n \n ctx.logger.info('REST API successfully registered');\n } catch (err: any) {\n ctx.logger.error('Failed to register REST API routes', { error: err.message } as any);\n throw err;\n }\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACoDO,IAAM,eAAN,MAAmB;AAAA,EAItB,YAAY,QAAqB;AAC7B,SAAK,SAAS;AACd,SAAK,SAAS,oBAAI,IAAI;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,OAA+E;AAEpF,QAAI,OAAO,MAAM,YAAY,UAAU;AACnC,YAAM,IAAI;AAAA,QACN,mFACgC,MAAM,OAAO;AAAA,MAEjD;AAAA,IACJ;AAEA,UAAM,UAAwB,MAAM;AAEpC,UAAM,aAAyB;AAAA,MAC3B,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,UAAU,MAAM;AAAA,IACpB;AAEA,UAAM,MAAM,KAAK,YAAY,MAAM,QAAQ,MAAM,IAAI;AACrD,SAAK,OAAO,IAAI,KAAK,UAAU;AAG/B,SAAK,mBAAmB,UAAU;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,SAAwF;AACjG,YAAQ,QAAQ,WAAS,KAAK,SAAS,KAAK,CAAC;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,QAAoB,MAAoB;AAC/C,UAAM,MAAM,KAAK,YAAY,QAAQ,IAAI;AACzC,SAAK,OAAO,OAAO,GAAG;AAAA,EAG1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,QAAoB,MAAsC;AAC1D,UAAM,MAAM,KAAK,YAAY,QAAQ,IAAI;AACzC,WAAO,KAAK,OAAO,IAAI,GAAG;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,SAAuB;AACnB,WAAO,MAAM,KAAK,KAAK,OAAO,OAAO,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,QAAkC;AAC1C,WAAO,KAAK,OAAO,EAAE,OAAO,WAAS,MAAM,WAAW,MAAM;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,QAA8B;AACtC,WAAO,KAAK,OAAO,EAAE,OAAO,WAAS,MAAM,KAAK,WAAW,MAAM,CAAC;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,KAA2B;AAChC,WAAO,KAAK,OAAO,EAAE;AAAA,MAAO,WACxB,MAAM,UAAU,MAAM,SAAS,GAAG;AAAA,IACtC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAgB,WAAqD;AACvE,UAAM,UAAU,IAAI,kBAAkB,MAAM,MAAM;AAClD,cAAU,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAgB;AACZ,WAAO,KAAK,OAAO;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACV,SAAK,OAAO,MAAM;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,QAAoB,MAAsB;AAC1D,WAAO,GAAG,MAAM,IAAI,IAAI;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,OAAyB;AAChD,UAAM,EAAE,QAAQ,MAAM,QAAQ,IAAI;AAElC,YAAQ,QAAQ;AAAA,MACZ,KAAK;AACD,aAAK,OAAO,IAAI,MAAM,OAAO;AAC7B;AAAA,MACJ,KAAK;AACD,aAAK,OAAO,KAAK,MAAM,OAAO;AAC9B;AAAA,MACJ,KAAK;AACD,aAAK,OAAO,IAAI,MAAM,OAAO;AAC7B;AAAA,MACJ,KAAK;AACD,aAAK,OAAO,OAAO,MAAM,OAAO;AAChC;AAAA,MACJ,KAAK;AACD,aAAK,OAAO,MAAM,MAAM,OAAO;AAC/B;AAAA,MACJ;AACI,cAAM,IAAI,MAAM,4BAA4B,MAAM,EAAE;AAAA,IAC5D;AAAA,EACJ;AACJ;AAOO,IAAM,oBAAN,MAAwB;AAAA,EAI3B,YAAY,SAAuB,QAAgB;AAC/C,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,MAAc,SAAuB,UAAmD;AACxF,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,MAAc,SAAuB,UAAmD;AACzF,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,MAAc,SAAuB,UAAmD;AACxF,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAc,SAAuB,UAAmD;AAC1F,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,MAAc,SAAuB,UAAmD;AAC3F,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,MAAsB;AAEtC,UAAM,mBAAmB,KAAK,OAAO,SAAS,GAAG,IAC3C,KAAK,OAAO,MAAM,GAAG,EAAE,IACvB,KAAK;AACX,UAAM,iBAAiB,KAAK,WAAW,GAAG,IACpC,OACA,MAAM;AAEZ,WAAO,mBAAmB;AAAA,EAC9B;AACJ;;;ACtNO,IAAM,aAAN,MAAiB;AAAA,EAKpB,YACI,QACA,UACA,SAA2B,CAAC,GAC9B;AACE,SAAK,WAAW;AAChB,SAAK,SAAS,KAAK,gBAAgB,MAAM;AACzC,SAAK,eAAe,IAAI,aAAa,MAAM;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,QAAsD;AAC1E,UAAM,MAAO,OAAO,OAAO,CAAC;AAC5B,UAAM,OAAQ,OAAO,QAAQ,CAAC;AAC9B,UAAM,WAAY,OAAO,YAAY,CAAC;AACtC,UAAM,QAAS,OAAO,SAAS,CAAC;AAChC,UAAM,SAAU,OAAO,UAAU,CAAC;AAElC,WAAO;AAAA,MACH,KAAK;AAAA,QACD,SAAS,IAAI,WAAW;AAAA,QACxB,UAAU,IAAI,YAAY;AAAA,QAC1B,SAAS,IAAI;AAAA,QACb,YAAY,IAAI,cAAc;AAAA,QAC9B,gBAAgB,IAAI,kBAAkB;AAAA,QACtC,UAAU,IAAI,YAAY;AAAA,QAC1B,aAAa,IAAI,eAAe;AAAA,QAChC,iBAAiB,IAAI,mBAAmB;AAAA,QACxC,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,MACxB;AAAA,MACA,MAAM;AAAA,QACF,YAAY,KAAK,cAAc;AAAA,UAC3B,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,MAAM;AAAA,QACV;AAAA,QACA,UAAU,KAAK;AAAA,QACf,YAAY,KAAK,cAAc;AAAA,QAC/B,kBAAkB,KAAK,oBAAoB;AAAA,MAC/C;AAAA,MACA,UAAU;AAAA,QACN,QAAQ,SAAS,UAAU;AAAA,QAC3B,aAAa,SAAS,eAAe;AAAA,QACrC,UAAU,SAAS,YAAY;AAAA,QAC/B,WAAW,SAAS,aAAa;AAAA,UAC7B,OAAO;AAAA,UACP,OAAO;AAAA,UACP,MAAM;AAAA,UACN,QAAQ;AAAA,QACZ;AAAA,MACJ;AAAA,MACA,OAAO;AAAA,QACH,cAAc,MAAM,gBAAgB;AAAA,QACpC,qBAAqB,MAAM,uBAAuB;AAAA,QAClD,YAAY,MAAM,cAAc;AAAA,UAC5B,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,YAAY;AAAA,QAChB;AAAA,QACA,eAAe,MAAM,iBAAiB;AAAA,MAC1C;AAAA,MACA,QAAQ;AAAA,QACJ,gBAAgB,OAAO;AAAA,QACvB,gBAAgB,OAAO;AAAA,QACvB,eAAe,OAAO,iBAAiB;AAAA,QACvC,WAAW,OAAO;AAAA,MACtB;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAyB;AAC7B,UAAM,EAAE,IAAI,IAAI,KAAK;AACrB,WAAO,IAAI,WAAW,GAAG,IAAI,QAAQ,IAAI,IAAI,OAAO;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAuB;AACnB,UAAM,WAAW,KAAK,eAAe;AAGrC,QAAI,KAAK,OAAO,IAAI,iBAAiB;AACjC,WAAK,2BAA2B,QAAQ;AAAA,IAC5C;AAGA,QAAI,KAAK,OAAO,IAAI,gBAAgB;AAChC,WAAK,0BAA0B,QAAQ;AAAA,IAC3C;AAGA,QAAI,KAAK,OAAO,IAAI,UAAU;AAC1B,WAAK,oBAAoB,QAAQ;AAAA,IACrC;AAGA,QAAI,KAAK,OAAO,IAAI,YAAY;AAC5B,WAAK,sBAAsB,QAAQ;AAAA,IACvC;AAGA,QAAI,KAAK,OAAO,IAAI,aAAa;AAC7B,WAAK,uBAAuB,QAAQ;AAAA,IACxC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,2BAA2B,UAAwB;AACvD,UAAM,mBAAmB,OAAO,MAAW,QAAa;AAChD,UAAI;AACA,cAAM,YAAY,MAAM,KAAK,SAAS,aAAa;AAGnD,kBAAU,UAAU,KAAK,OAAO,IAAI;AAEpC,YAAI,UAAU,QAAQ;AAElB,cAAI,KAAK,OAAO,IAAI,YAAY;AAC5B,sBAAU,OAAO,OAAO,GAAG,QAAQ,GAAG,KAAK,OAAO,KAAK,UAAU;AAAA,UACrE;AAEA,cAAI,KAAK,OAAO,IAAI,gBAAgB;AAChC,sBAAU,OAAO,WAAW,GAAG,QAAQ,GAAG,KAAK,OAAO,SAAS,MAAM;AAAA,UACzE;AAEA,cAAI,KAAK,OAAO,IAAI,UAAU;AAC1B,sBAAU,OAAO,KAAK,GAAG,QAAQ;AAAA,UACrC;AAGA,cAAI,UAAU,OAAO,MAAM;AACvB,sBAAU,OAAO,OAAO,GAAG,QAAQ;AAAA,UACvC;AAAA,QACJ;AAEA,YAAI,KAAK,SAAS;AAAA,MACtB,SAAS,OAAY;AACjB,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,MACjD;AAAA,IACJ;AAGJ,SAAK,aAAa,SAAS;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,MACT,UAAU;AAAA,QACN,SAAS;AAAA,QACT,MAAM,CAAC,WAAW;AAAA,MACtB;AAAA,IACJ,CAAC;AAGD,SAAK,aAAa,SAAS;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM,GAAG,QAAQ;AAAA,MACjB,SAAS;AAAA,MACT,UAAU;AAAA,QACN,SAAS;AAAA,QACT,MAAM,CAAC,WAAW;AAAA,MACtB;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKQ,0BAA0B,UAAwB;AACtD,UAAM,EAAE,SAAS,IAAI,KAAK;AAC1B,UAAM,WAAW,GAAG,QAAQ,GAAG,SAAS,MAAM;AAG9C,QAAI,SAAS,UAAU,UAAU,OAAO;AACpC,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS,OAAO,MAAW,QAAa;AACpC,cAAI;AACA,kBAAM,QAAQ,MAAM,KAAK,SAAS,aAAa;AAC/C,gBAAI,KAAK,KAAK;AAAA,UAClB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,UAAU;AAAA,QACrB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,SAAS,UAAU,UAAU,OAAO;AACpC,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,QAAQ,MAAM,KAAK,SAAS,aAAa,EAAE,MAAM,IAAI,OAAO,KAAK,CAAC;AACxE,gBAAI,KAAK,KAAK;AAAA,UAClB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,UAAU;AAAA,QACrB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,SAAS,UAAU,SAAS,OAAO;AACnC,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AAEA,gBAAI,SAAS,eAAe,KAAK,SAAS,mBAAmB;AACzD,oBAAM,eAAe;AAAA,gBACjB,aAAa,IAAI,QAAQ,eAAe;AAAA,gBACxC,iBAAiB,IAAI,QAAQ,mBAAmB;AAAA,cACpD;AAEA,oBAAM,SAAS,MAAM,KAAK,SAAS,kBAAkB;AAAA,gBACjD,MAAM,IAAI,OAAO;AAAA,gBACjB,MAAM,IAAI,OAAO;AAAA,gBACjB;AAAA,cACJ,CAAC;AAED,kBAAI,OAAO,aAAa;AACpB,oBAAI,OAAO,GAAG,EAAE,KAAK;AACrB;AAAA,cACJ;AAGA,kBAAI,OAAO,MAAM;AACb,sBAAM,YAAY,OAAO,KAAK,OACxB,MAAM,OAAO,KAAK,KAAK,MACvB,IAAI,OAAO,KAAK,KAAK;AAC3B,oBAAI,OAAO,QAAQ,SAAS;AAAA,cAChC;AACA,kBAAI,OAAO,cAAc;AACrB,oBAAI,OAAO,iBAAiB,IAAI,KAAK,OAAO,YAAY,EAAE,YAAY,CAAC;AAAA,cAC3E;AACA,kBAAI,OAAO,cAAc;AACrB,sBAAM,aAAa,OAAO,aAAa,WAAW,KAAK,IAAI;AAC3D,sBAAM,SAAS,OAAO,aAAa,SAC7B,aAAa,OAAO,aAAa,MAAM,KACvC;AACN,oBAAI,OAAO,iBAAiB,aAAa,MAAM;AAAA,cACnD;AAEA,kBAAI,KAAK,OAAO,IAAI;AAAA,YACxB,OAAO;AAEH,oBAAM,OAAO,MAAM,KAAK,SAAS,YAAY,EAAE,MAAM,IAAI,OAAO,MAAM,MAAM,IAAI,OAAO,KAAK,CAAC;AAC7F,kBAAI,KAAK,IAAI;AAAA,YACjB;AAAA,UACJ,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,UAAU;AAAA,QACrB;AAAA,MACJ,CAAC;AAAA,IACL;AAKA,SAAK,aAAa,SAAS;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM,GAAG,QAAQ;AAAA,MACjB,SAAS,OAAO,KAAU,QAAa;AACnC,YAAI;AACA,cAAI,CAAC,KAAK,SAAS,cAAc;AAC7B,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,0DAA0D,CAAC;AACzF;AAAA,UACJ;AAEA,gBAAM,SAAS,MAAM,KAAK,SAAS,aAAa;AAAA,YAC5C,MAAM,IAAI,OAAO;AAAA,YACjB,MAAM,IAAI,OAAO;AAAA,YACjB,MAAM,IAAI;AAAA,UACd,CAAC;AACD,cAAI,KAAK,MAAM;AAAA,QACnB,SAAS,OAAY;AACjB,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,QACjD;AAAA,MACJ;AAAA,MACA,UAAU;AAAA,QACN,SAAS;AAAA,QACT,MAAM,CAAC,UAAU;AAAA,MACrB;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,UAAwB;AAChD,UAAM,SAAS,GAAG,QAAQ;AAG1B,SAAK,aAAa,SAAS;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM,GAAG,MAAM;AAAA,MACf,SAAS,OAAO,KAAU,QAAa;AACnC,YAAI;AACA,cAAI,KAAK,SAAS,WAAW;AACzB,kBAAM,OAAO,MAAM,KAAK,SAAS,UAAU;AAAA,cACvC,QAAQ,IAAI,OAAO;AAAA,cACnB,MAAM,IAAI,OAAO;AAAA,YACrB,CAAC;AACD,gBAAI,KAAK,IAAI;AAAA,UACjB,OAAO;AACH,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,8DAA8D,CAAC;AAAA,UACjG;AAAA,QACJ,SAAS,OAAY;AACjB,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,QACjD;AAAA,MACJ;AAAA,MACA,UAAU;AAAA,QACN,SAAS;AAAA,QACT,MAAM,CAAC,IAAI;AAAA,MACf;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAsB,UAAwB;AAClD,UAAM,EAAE,KAAK,IAAI,KAAK;AACtB,UAAM,WAAW,GAAG,QAAQ,GAAG,KAAK,UAAU;AAE9C,UAAM,aAAa,KAAK;AAGxB,QAAI,WAAW,MAAM;AACjB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,SAAS;AAAA,cACxC,QAAQ,IAAI,OAAO;AAAA,cACnB,OAAO,IAAI;AAAA,YACf,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,MAAM;AACjB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,QAAQ;AAAA,cACvC,QAAQ,IAAI,OAAO;AAAA,cACnB,IAAI,IAAI,OAAO;AAAA,YACnB,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,QAAQ;AACnB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,WAAW;AAAA,cAC1C,QAAQ,IAAI,OAAO;AAAA,cACnB,MAAM,IAAI;AAAA,YACd,CAAC;AACD,gBAAI,OAAO,GAAG,EAAE,KAAK,MAAM;AAAA,UAC/B,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,QAAQ;AACnB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,WAAW;AAAA,cAC1C,QAAQ,IAAI,OAAO;AAAA,cACnB,IAAI,IAAI,OAAO;AAAA,cACf,MAAM,IAAI;AAAA,YACd,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,QAAQ;AACnB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,WAAW;AAAA,cAC1C,QAAQ,IAAI,OAAO;AAAA,cACnB,IAAI,IAAI,OAAO;AAAA,YACnB,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAAuB,UAAwB;AACnD,UAAM,EAAE,MAAM,MAAM,IAAI,KAAK;AAC7B,UAAM,WAAW,GAAG,QAAQ,GAAG,KAAK,UAAU;AAE9C,UAAM,aAAa,MAAM;AAGzB,QAAI,MAAM,uBAAuB,KAAK,SAAS,WAAW;AACtD,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,UAAW;AAAA,cAC1C,QAAQ,IAAI,OAAO;AAAA,cACnB,SAAS,IAAI;AAAA,YACjB,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,OAAO;AAAA,QAC1B;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,cAAc,KAAK,SAAS,gBAAgB;AACvD,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,eAAgB;AAAA,cAC/C,QAAQ,IAAI,OAAO;AAAA,cACnB,SAAS,IAAI,QAAQ,CAAC;AAAA,YAC1B,CAAC;AACD,gBAAI,OAAO,GAAG,EAAE,KAAK,MAAM;AAAA,UAC/B,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,OAAO;AAAA,QAC1B;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,cAAc,KAAK,SAAS,gBAAgB;AACvD,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,eAAgB;AAAA,cAC/C,QAAQ,IAAI,OAAO;AAAA,cACnB,GAAG,IAAI;AAAA,YACX,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,OAAO;AAAA,QAC1B;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,cAAc,KAAK,SAAS,gBAAgB;AACvD,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,eAAgB;AAAA,cAC/C,QAAQ,IAAI,OAAO;AAAA,cACnB,GAAG,IAAI;AAAA,YACX,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,OAAO;AAAA,QAC1B;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAgC;AAC5B,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY;AACR,WAAO,KAAK,aAAa,OAAO;AAAA,EACpC;AACJ;;;ACxpBO,SAAS,oBAAoB,SAA8B,CAAC,GAAW;AAC1E,SAAO;AAAA,IACH,MAAM;AAAA,IACN,SAAS;AAAA,IAET,MAAM,OAAO,SAAwB;AAAA,IAErC;AAAA,IAEA,OAAO,OAAO,QAAuB;AACjC,YAAM,gBAAgB,OAAO,qBAAqB;AAClD,YAAM,kBAAkB,OAAO,uBAAuB;AAEtD,UAAI;AACJ,UAAI;AAEJ,UAAI;AACA,iBAAS,IAAI,WAAwB,aAAa;AAAA,MACtD,SAAS,GAAG;AAAA,MAEZ;AAEA,UAAI;AACA,mBAAW,IAAI,WAAgC,eAAe;AAAA,MAClE,SAAS,GAAG;AAAA,MAEZ;AAEA,UAAI,CAAC,QAAQ;AACT,YAAI,OAAO,KAAK,uCAAuC,aAAa,mCAAmC;AACvG;AAAA,MACJ;AAEA,UAAI,CAAC,UAAU;AACX,YAAI,OAAO,KAAK,oCAAoC,eAAe,mCAAmC;AACtG;AAAA,MACJ;AAEA,UAAI,OAAO,KAAK,qCAAqC;AAErD,UAAI;AACA,cAAM,aAAa,IAAI,WAAW,QAAQ,UAAU,OAAO,GAAU;AACrE,mBAAW,eAAe;AAE1B,YAAI,OAAO,KAAK,kCAAkC;AAAA,MACtD,SAAS,KAAU;AACf,YAAI,OAAO,MAAM,sCAAsC,EAAE,OAAO,IAAI,QAAQ,CAAQ;AACpF,cAAM;AAAA,MACV;AAAA,IACJ;AAAA,EACJ;AACJ;","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -233,10 +233,6 @@ interface RestApiPluginConfig {
|
|
|
233
233
|
protocolServiceName?: string;
|
|
234
234
|
api?: RestServerConfig;
|
|
235
235
|
}
|
|
236
|
-
/**
|
|
237
|
-
* @deprecated Use {@link RestApiPluginConfig} instead
|
|
238
|
-
*/
|
|
239
|
-
type ApiRegistryConfig = RestApiPluginConfig;
|
|
240
236
|
/**
|
|
241
237
|
* REST API Plugin
|
|
242
238
|
*
|
|
@@ -246,9 +242,5 @@ type ApiRegistryConfig = RestApiPluginConfig;
|
|
|
246
242
|
* 3. Instantiates RestServer to auto-generate routes
|
|
247
243
|
*/
|
|
248
244
|
declare function createRestApiPlugin(config?: RestApiPluginConfig): Plugin;
|
|
249
|
-
/**
|
|
250
|
-
* @deprecated Use {@link createRestApiPlugin} instead
|
|
251
|
-
*/
|
|
252
|
-
declare const createApiRegistryPlugin: typeof createRestApiPlugin;
|
|
253
245
|
|
|
254
|
-
export { type
|
|
246
|
+
export { type RestApiPluginConfig, RestServer, type RouteEntry, RouteGroupBuilder, RouteManager, createRestApiPlugin };
|
package/dist/index.d.ts
CHANGED
|
@@ -233,10 +233,6 @@ interface RestApiPluginConfig {
|
|
|
233
233
|
protocolServiceName?: string;
|
|
234
234
|
api?: RestServerConfig;
|
|
235
235
|
}
|
|
236
|
-
/**
|
|
237
|
-
* @deprecated Use {@link RestApiPluginConfig} instead
|
|
238
|
-
*/
|
|
239
|
-
type ApiRegistryConfig = RestApiPluginConfig;
|
|
240
236
|
/**
|
|
241
237
|
* REST API Plugin
|
|
242
238
|
*
|
|
@@ -246,9 +242,5 @@ type ApiRegistryConfig = RestApiPluginConfig;
|
|
|
246
242
|
* 3. Instantiates RestServer to auto-generate routes
|
|
247
243
|
*/
|
|
248
244
|
declare function createRestApiPlugin(config?: RestApiPluginConfig): Plugin;
|
|
249
|
-
/**
|
|
250
|
-
* @deprecated Use {@link createRestApiPlugin} instead
|
|
251
|
-
*/
|
|
252
|
-
declare const createApiRegistryPlugin: typeof createRestApiPlugin;
|
|
253
245
|
|
|
254
|
-
export { type
|
|
246
|
+
export { type RestApiPluginConfig, RestServer, type RouteEntry, RouteGroupBuilder, RouteManager, createRestApiPlugin };
|
package/dist/index.js
CHANGED
|
@@ -311,32 +311,42 @@ var RestServer = class {
|
|
|
311
311
|
* Register discovery endpoints
|
|
312
312
|
*/
|
|
313
313
|
registerDiscoveryEndpoints(basePath) {
|
|
314
|
+
const discoveryHandler = async (_req, res) => {
|
|
315
|
+
try {
|
|
316
|
+
const discovery = await this.protocol.getDiscovery();
|
|
317
|
+
discovery.version = this.config.api.version;
|
|
318
|
+
if (discovery.routes) {
|
|
319
|
+
if (this.config.api.enableCrud) {
|
|
320
|
+
discovery.routes.data = `${basePath}${this.config.crud.dataPrefix}`;
|
|
321
|
+
}
|
|
322
|
+
if (this.config.api.enableMetadata) {
|
|
323
|
+
discovery.routes.metadata = `${basePath}${this.config.metadata.prefix}`;
|
|
324
|
+
}
|
|
325
|
+
if (this.config.api.enableUi) {
|
|
326
|
+
discovery.routes.ui = `${basePath}/ui`;
|
|
327
|
+
}
|
|
328
|
+
if (discovery.routes.auth) {
|
|
329
|
+
discovery.routes.auth = `${basePath}/auth`;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
res.json(discovery);
|
|
333
|
+
} catch (error) {
|
|
334
|
+
res.status(500).json({ error: error.message });
|
|
335
|
+
}
|
|
336
|
+
};
|
|
314
337
|
this.routeManager.register({
|
|
315
338
|
method: "GET",
|
|
316
339
|
path: basePath,
|
|
317
|
-
handler:
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
if (this.config.api.enableUi) {
|
|
329
|
-
discovery.endpoints.ui = `${basePath}/ui`;
|
|
330
|
-
}
|
|
331
|
-
if (discovery.endpoints.auth) {
|
|
332
|
-
discovery.endpoints.auth = `${basePath}/auth`;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
res.json(discovery);
|
|
336
|
-
} catch (error) {
|
|
337
|
-
res.status(500).json({ error: error.message });
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
|
+
handler: discoveryHandler,
|
|
341
|
+
metadata: {
|
|
342
|
+
summary: "Get API discovery information",
|
|
343
|
+
tags: ["discovery"]
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
this.routeManager.register({
|
|
347
|
+
method: "GET",
|
|
348
|
+
path: `${basePath}/discovery`,
|
|
349
|
+
handler: discoveryHandler,
|
|
340
350
|
metadata: {
|
|
341
351
|
summary: "Get API discovery information",
|
|
342
352
|
tags: ["discovery"]
|
|
@@ -746,12 +756,10 @@ function createRestApiPlugin(config = {}) {
|
|
|
746
756
|
}
|
|
747
757
|
};
|
|
748
758
|
}
|
|
749
|
-
var createApiRegistryPlugin = createRestApiPlugin;
|
|
750
759
|
export {
|
|
751
760
|
RestServer,
|
|
752
761
|
RouteGroupBuilder,
|
|
753
762
|
RouteManager,
|
|
754
|
-
createApiRegistryPlugin,
|
|
755
763
|
createRestApiPlugin
|
|
756
764
|
};
|
|
757
765
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/route-manager.ts","../src/rest-server.ts","../src/rest-api-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { RouteHandler, IHttpServer } from '@objectstack/core';\nimport { System, Shared } from '@objectstack/spec';\nimport { z } from 'zod';\n\ntype RouteHandlerMetadata = System.RouteHandlerMetadata;\ntype HttpMethod = z.infer<typeof Shared.HttpMethod>;\n\n/**\n * Route Entry\n * Internal representation of registered routes\n */\nexport interface RouteEntry {\n method: HttpMethod;\n path: string;\n handler: RouteHandler;\n metadata?: RouteHandlerMetadata['metadata'];\n security?: RouteHandlerMetadata['security'];\n}\n\n/**\n * RouteManager\n * \n * Manages route registration and organization for HTTP servers.\n * Provides:\n * - Route registration with metadata\n * - Route lookup and querying\n * - Bulk route registration\n * - Route grouping by prefix\n * \n * @example\n * const manager = new RouteManager(server);\n * \n * // Register individual route\n * manager.register({\n * method: 'GET',\n * path: '/api/users/:id',\n * handler: getUserHandler,\n * metadata: {\n * summary: 'Get user by ID',\n * tags: ['users']\n * }\n * });\n * \n * // Register route group\n * manager.group('/api/users', (group) => {\n * group.get('/', listUsersHandler);\n * group.post('/', createUserHandler);\n * group.get('/:id', getUserHandler);\n * });\n */\nexport class RouteManager {\n private server: IHttpServer;\n private routes: Map<string, RouteEntry>;\n \n constructor(server: IHttpServer) {\n this.server = server;\n this.routes = new Map();\n }\n \n /**\n * Register a route\n * @param entry - Route entry with method, path, handler, and metadata\n */\n register(entry: Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }): void {\n // Validate handler type - string handlers not yet supported\n if (typeof entry.handler === 'string') {\n throw new Error(\n `String-based route handlers are not supported yet. ` +\n `Received handler identifier \"${entry.handler}\". ` +\n `Please provide a RouteHandler function instead.`\n );\n }\n \n const handler: RouteHandler = entry.handler;\n \n const routeEntry: RouteEntry = {\n method: entry.method,\n path: entry.path,\n handler,\n metadata: entry.metadata,\n security: entry.security,\n };\n \n const key = this.getRouteKey(entry.method, entry.path);\n this.routes.set(key, routeEntry);\n \n // Register with underlying server\n this.registerWithServer(routeEntry);\n }\n \n /**\n * Register multiple routes\n * @param entries - Array of route entries\n */\n registerMany(entries: Array<Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }>): void {\n entries.forEach(entry => this.register(entry));\n }\n \n /**\n * Unregister a route\n * @param method - HTTP method\n * @param path - Route path\n */\n unregister(method: HttpMethod, path: string): void {\n const key = this.getRouteKey(method, path);\n this.routes.delete(key);\n // Note: Most server frameworks don't support unregistering routes at runtime\n // This just removes it from our registry\n }\n \n /**\n * Get route by method and path\n * @param method - HTTP method\n * @param path - Route path\n */\n get(method: HttpMethod, path: string): RouteEntry | undefined {\n const key = this.getRouteKey(method, path);\n return this.routes.get(key);\n }\n \n /**\n * Get all routes\n */\n getAll(): RouteEntry[] {\n return Array.from(this.routes.values());\n }\n \n /**\n * Get routes by method\n * @param method - HTTP method\n */\n getByMethod(method: HttpMethod): RouteEntry[] {\n return this.getAll().filter(route => route.method === method);\n }\n \n /**\n * Get routes by path prefix\n * @param prefix - Path prefix\n */\n getByPrefix(prefix: string): RouteEntry[] {\n return this.getAll().filter(route => route.path.startsWith(prefix));\n }\n \n /**\n * Get routes by tag\n * @param tag - Tag name\n */\n getByTag(tag: string): RouteEntry[] {\n return this.getAll().filter(route => \n route.metadata?.tags?.includes(tag)\n );\n }\n \n /**\n * Create a route group with common prefix\n * @param prefix - Common path prefix\n * @param configure - Function to configure routes in the group\n */\n group(prefix: string, configure: (group: RouteGroupBuilder) => void): void {\n const builder = new RouteGroupBuilder(this, prefix);\n configure(builder);\n }\n \n /**\n * Get route count\n */\n count(): number {\n return this.routes.size;\n }\n \n /**\n * Clear all routes\n */\n clear(): void {\n this.routes.clear();\n }\n \n /**\n * Get route key for storage\n */\n private getRouteKey(method: HttpMethod, path: string): string {\n return `${method}:${path}`;\n }\n \n /**\n * Register route with underlying server\n */\n private registerWithServer(entry: RouteEntry): void {\n const { method, path, handler } = entry;\n \n switch (method) {\n case 'GET':\n this.server.get(path, handler);\n break;\n case 'POST':\n this.server.post(path, handler);\n break;\n case 'PUT':\n this.server.put(path, handler);\n break;\n case 'DELETE':\n this.server.delete(path, handler);\n break;\n case 'PATCH':\n this.server.patch(path, handler);\n break;\n default:\n throw new Error(`Unsupported HTTP method: ${method}`);\n }\n }\n}\n\n/**\n * RouteGroupBuilder\n * \n * Builder for creating route groups with common prefix\n */\nexport class RouteGroupBuilder {\n private manager: RouteManager;\n private prefix: string;\n \n constructor(manager: RouteManager, prefix: string) {\n this.manager = manager;\n this.prefix = prefix;\n }\n \n /**\n * Register GET route in group\n */\n get(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'GET',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register POST route in group\n */\n post(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'POST',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register PUT route in group\n */\n put(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'PUT',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register PATCH route in group\n */\n patch(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'PATCH',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register DELETE route in group\n */\n delete(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'DELETE',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Resolve full path with prefix\n */\n private resolvePath(path: string): string {\n // Normalize slashes\n const normalizedPrefix = this.prefix.endsWith('/') \n ? this.prefix.slice(0, -1) \n : this.prefix;\n const normalizedPath = path.startsWith('/') \n ? path \n : '/' + path;\n \n return normalizedPrefix + normalizedPath;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { IHttpServer } from '@objectstack/core';\nimport { RouteManager } from './route-manager.js';\nimport { RestServerConfig, RestApiConfig, CrudEndpointsConfig, MetadataEndpointsConfig, BatchEndpointsConfig, RouteGenerationConfig } from '@objectstack/spec/api';\nimport { ObjectStackProtocol } from '@objectstack/spec/api';\n\n/**\n * Normalized REST Server Configuration\n * All nested properties are required after normalization\n */\ntype NormalizedRestServerConfig = {\n api: {\n version: string;\n basePath: string;\n apiPath: string | undefined;\n enableCrud: boolean;\n enableMetadata: boolean;\n enableUi: boolean;\n enableBatch: boolean;\n enableDiscovery: boolean;\n documentation: RestApiConfig['documentation'];\n responseFormat: RestApiConfig['responseFormat'];\n };\n crud: {\n operations: {\n create: boolean;\n read: boolean;\n update: boolean;\n delete: boolean;\n list: boolean;\n };\n patterns: CrudEndpointsConfig['patterns'];\n dataPrefix: string;\n objectParamStyle: 'path' | 'query';\n };\n metadata: {\n prefix: string;\n enableCache: boolean;\n cacheTtl: number;\n endpoints: {\n types: boolean;\n items: boolean;\n item: boolean;\n schema: boolean;\n };\n };\n batch: {\n maxBatchSize: number;\n enableBatchEndpoint: boolean;\n operations: {\n createMany: boolean;\n updateMany: boolean;\n deleteMany: boolean;\n upsertMany: boolean;\n };\n defaultAtomic: boolean;\n };\n routes: {\n includeObjects: string[] | undefined;\n excludeObjects: string[] | undefined;\n nameTransform: 'none' | 'plural' | 'kebab-case' | 'camelCase';\n overrides: RouteGenerationConfig['overrides'];\n };\n};\n\n/**\n * RestServer\n * \n * Provides automatic REST API endpoint generation for ObjectStack.\n * Generates standard RESTful CRUD endpoints, metadata endpoints, and batch operations\n * based on the configured protocol provider.\n * \n * Features:\n * - Automatic CRUD endpoint generation (GET, POST, PUT, PATCH, DELETE)\n * - Metadata API endpoints (/meta)\n * - Batch operation endpoints (/batch, /createMany, /updateMany, /deleteMany)\n * - Discovery endpoint\n * - Configurable path prefixes and patterns\n * \n * @example\n * const restServer = new RestServer(httpServer, protocolProvider, {\n * api: {\n * version: 'v1',\n * basePath: '/api'\n * },\n * crud: {\n * dataPrefix: '/data'\n * }\n * });\n * \n * restServer.registerRoutes();\n */\nexport class RestServer {\n private protocol: ObjectStackProtocol;\n private config: NormalizedRestServerConfig;\n private routeManager: RouteManager;\n \n constructor(\n server: IHttpServer, \n protocol: ObjectStackProtocol, \n config: RestServerConfig = {}\n ) {\n this.protocol = protocol;\n this.config = this.normalizeConfig(config);\n this.routeManager = new RouteManager(server);\n }\n \n /**\n * Normalize configuration with defaults\n */\n private normalizeConfig(config: RestServerConfig): NormalizedRestServerConfig {\n const api = (config.api ?? {}) as Partial<RestApiConfig>;\n const crud = (config.crud ?? {}) as Partial<CrudEndpointsConfig>;\n const metadata = (config.metadata ?? {}) as Partial<MetadataEndpointsConfig>;\n const batch = (config.batch ?? {}) as Partial<BatchEndpointsConfig>;\n const routes = (config.routes ?? {}) as Partial<RouteGenerationConfig>;\n \n return {\n api: {\n version: api.version ?? 'v1',\n basePath: api.basePath ?? '/api',\n apiPath: api.apiPath,\n enableCrud: api.enableCrud ?? true,\n enableMetadata: api.enableMetadata ?? true,\n enableUi: api.enableUi ?? true,\n enableBatch: api.enableBatch ?? true,\n enableDiscovery: api.enableDiscovery ?? true,\n documentation: api.documentation,\n responseFormat: api.responseFormat,\n },\n crud: {\n operations: crud.operations ?? {\n create: true,\n read: true,\n update: true,\n delete: true,\n list: true,\n },\n patterns: crud.patterns,\n dataPrefix: crud.dataPrefix ?? '/data',\n objectParamStyle: crud.objectParamStyle ?? 'path',\n },\n metadata: {\n prefix: metadata.prefix ?? '/meta',\n enableCache: metadata.enableCache ?? true,\n cacheTtl: metadata.cacheTtl ?? 3600,\n endpoints: metadata.endpoints ?? {\n types: true,\n items: true,\n item: true,\n schema: true,\n },\n },\n batch: {\n maxBatchSize: batch.maxBatchSize ?? 200,\n enableBatchEndpoint: batch.enableBatchEndpoint ?? true,\n operations: batch.operations ?? {\n createMany: true,\n updateMany: true,\n deleteMany: true,\n upsertMany: true,\n },\n defaultAtomic: batch.defaultAtomic ?? true,\n },\n routes: {\n includeObjects: routes.includeObjects,\n excludeObjects: routes.excludeObjects,\n nameTransform: routes.nameTransform ?? 'none',\n overrides: routes.overrides,\n },\n };\n }\n \n /**\n * Get the full API base path\n */\n private getApiBasePath(): string {\n const { api } = this.config;\n return api.apiPath ?? `${api.basePath}/${api.version}`;\n }\n \n /**\n * Register all REST API routes\n */\n registerRoutes(): void {\n const basePath = this.getApiBasePath();\n \n // Discovery endpoint\n if (this.config.api.enableDiscovery) {\n this.registerDiscoveryEndpoints(basePath);\n }\n \n // Metadata endpoints\n if (this.config.api.enableMetadata) {\n this.registerMetadataEndpoints(basePath);\n }\n\n // UI endpoints\n if (this.config.api.enableUi) {\n this.registerUiEndpoints(basePath);\n }\n \n // CRUD endpoints\n if (this.config.api.enableCrud) {\n this.registerCrudEndpoints(basePath);\n }\n \n // Batch endpoints\n if (this.config.api.enableBatch) {\n this.registerBatchEndpoints(basePath);\n }\n }\n \n /**\n * Register discovery endpoints\n */\n private registerDiscoveryEndpoints(basePath: string): void {\n this.routeManager.register({\n method: 'GET',\n path: basePath,\n handler: async (_req: any, res: any) => {\n try {\n const discovery = await this.protocol.getDiscovery();\n \n // Override discovery information with actual server configuration\n discovery.version = this.config.api.version;\n \n if (discovery.endpoints) {\n // Ensure endpoints match the actual mounted paths\n if (this.config.api.enableCrud) {\n discovery.endpoints.data = `${basePath}${this.config.crud.dataPrefix}`;\n }\n \n if (this.config.api.enableMetadata) {\n discovery.endpoints.metadata = `${basePath}${this.config.metadata.prefix}`;\n }\n\n if (this.config.api.enableUi) {\n discovery.endpoints.ui = `${basePath}/ui`;\n }\n\n // Align auth endpoint with the versioned base path if present\n if (discovery.endpoints.auth) {\n discovery.endpoints.auth = `${basePath}/auth`;\n }\n }\n\n res.json(discovery);\n } catch (error: any) {\n res.status(500).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get API discovery information',\n tags: ['discovery'],\n },\n });\n }\n \n /**\n * Register metadata endpoints\n */\n private registerMetadataEndpoints(basePath: string): void {\n const { metadata } = this.config;\n const metaPath = `${basePath}${metadata.prefix}`;\n \n // GET /meta - List all metadata types\n if (metadata.endpoints.types !== false) {\n this.routeManager.register({\n method: 'GET',\n path: metaPath,\n handler: async (_req: any, res: any) => {\n try {\n const types = await this.protocol.getMetaTypes();\n res.json(types);\n } catch (error: any) {\n res.status(500).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'List all metadata types',\n tags: ['metadata'],\n },\n });\n }\n \n // GET /meta/:type - List items of a type\n if (metadata.endpoints.items !== false) {\n this.routeManager.register({\n method: 'GET',\n path: `${metaPath}/:type`,\n handler: async (req: any, res: any) => {\n try {\n const items = await this.protocol.getMetaItems({ type: req.params.type });\n res.json(items);\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'List metadata items of a type',\n tags: ['metadata'],\n },\n });\n }\n \n // GET /meta/:type/:name - Get specific item\n if (metadata.endpoints.item !== false) {\n this.routeManager.register({\n method: 'GET',\n path: `${metaPath}/:type/:name`,\n handler: async (req: any, res: any) => {\n try {\n // Check if cached version is available\n if (metadata.enableCache && this.protocol.getMetaItemCached) {\n const cacheRequest = {\n ifNoneMatch: req.headers['if-none-match'] as string,\n ifModifiedSince: req.headers['if-modified-since'] as string,\n };\n \n const result = await this.protocol.getMetaItemCached({\n type: req.params.type,\n name: req.params.name,\n cacheRequest\n });\n \n if (result.notModified) {\n res.status(304).send();\n return;\n }\n \n // Set cache headers\n if (result.etag) {\n const etagValue = result.etag.weak \n ? `W/\"${result.etag.value}\"` \n : `\"${result.etag.value}\"`;\n res.header('ETag', etagValue);\n }\n if (result.lastModified) {\n res.header('Last-Modified', new Date(result.lastModified).toUTCString());\n }\n if (result.cacheControl) {\n const directives = result.cacheControl.directives.join(', ');\n const maxAge = result.cacheControl.maxAge \n ? `, max-age=${result.cacheControl.maxAge}` \n : '';\n res.header('Cache-Control', directives + maxAge);\n }\n \n res.json(result.data);\n } else {\n // Non-cached version\n const item = await this.protocol.getMetaItem({ type: req.params.type, name: req.params.name });\n res.json(item);\n }\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get specific metadata item',\n tags: ['metadata'],\n },\n });\n }\n\n // PUT /meta/:type/:name - Save metadata item\n // We always register this route, but return 501 if protocol doesn't support it\n // This makes it discoverable even if not implemented\n this.routeManager.register({\n method: 'PUT',\n path: `${metaPath}/:type/:name`,\n handler: async (req: any, res: any) => {\n try {\n if (!this.protocol.saveMetaItem) {\n res.status(501).json({ error: 'Save operation not supported by protocol implementation' });\n return;\n }\n\n const result = await this.protocol.saveMetaItem({\n type: req.params.type,\n name: req.params.name,\n item: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Save specific metadata item',\n tags: ['metadata'],\n },\n });\n }\n\n /**\n * Register UI endpoints\n */\n private registerUiEndpoints(basePath: string): void {\n const uiPath = `${basePath}/ui`;\n \n // GET /ui/view/:object/:type - Resolve view for object\n this.routeManager.register({\n method: 'GET',\n path: `${uiPath}/view/:object/:type`,\n handler: async (req: any, res: any) => {\n try {\n if (this.protocol.getUiView) {\n const view = await this.protocol.getUiView({ \n object: req.params.object, \n type: req.params.type as any \n });\n res.json(view);\n } else {\n res.status(501).json({ error: 'UI View resolution not supported by protocol implementation' });\n }\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Resolve UI View for object',\n tags: ['ui'],\n },\n });\n }\n \n /**\n * Register CRUD endpoints for data operations\n */\n private registerCrudEndpoints(basePath: string): void {\n const { crud } = this.config;\n const dataPath = `${basePath}${crud.dataPrefix}`;\n \n const operations = crud.operations;\n \n // GET /data/:object - List/query records\n if (operations.list) {\n this.routeManager.register({\n method: 'GET',\n path: `${dataPath}/:object`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.findData({\n object: req.params.object, \n query: req.query\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Query records',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // GET /data/:object/:id - Get single record\n if (operations.read) {\n this.routeManager.register({\n method: 'GET',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.getData({\n object: req.params.object, \n id: req.params.id\n });\n res.json(result);\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get record by ID',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // POST /data/:object - Create record\n if (operations.create) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.createData({\n object: req.params.object, \n data: req.body\n });\n res.status(201).json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Create record',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // PATCH /data/:object/:id - Update record\n if (operations.update) {\n this.routeManager.register({\n method: 'PATCH',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.updateData({\n object: req.params.object,\n id: req.params.id,\n data: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Update record',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // DELETE /data/:object/:id - Delete record\n if (operations.delete) {\n this.routeManager.register({\n method: 'DELETE',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.deleteData({\n object: req.params.object, \n id: req.params.id\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Delete record',\n tags: ['data', 'crud'],\n },\n });\n }\n }\n \n /**\n * Register batch operation endpoints\n */\n private registerBatchEndpoints(basePath: string): void {\n const { crud, batch } = this.config;\n const dataPath = `${basePath}${crud.dataPrefix}`;\n \n const operations = batch.operations;\n \n // POST /data/:object/batch - Generic batch endpoint\n if (batch.enableBatchEndpoint && this.protocol.batchData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/batch`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.batchData!({\n object: req.params.object, \n request: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Batch operations',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/createMany - Bulk create\n if (operations.createMany && this.protocol.createManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/createMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.createManyData!({\n object: req.params.object,\n records: req.body || []\n });\n res.status(201).json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Create multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/updateMany - Bulk update\n if (operations.updateMany && this.protocol.updateManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/updateMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.updateManyData!({\n object: req.params.object,\n ...req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Update multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/deleteMany - Bulk delete\n if (operations.deleteMany && this.protocol.deleteManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/deleteMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.deleteManyData!({\n object: req.params.object, \n ...req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Delete multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n }\n \n /**\n * Get the route manager\n */\n getRouteManager(): RouteManager {\n return this.routeManager;\n }\n \n /**\n * Get all registered routes\n */\n getRoutes() {\n return this.routeManager.getAll();\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport { RestServer } from './rest-server.js';\nimport { ObjectStackProtocol, RestServerConfig } from '@objectstack/spec/api';\n\nexport interface RestApiPluginConfig {\n serverServiceName?: string;\n protocolServiceName?: string;\n api?: RestServerConfig;\n}\n\n/**\n * @deprecated Use {@link RestApiPluginConfig} instead\n */\nexport type ApiRegistryConfig = RestApiPluginConfig;\n\n/**\n * REST API Plugin\n * \n * Responsibilities:\n * 1. Consumes 'http.server' (or configured service)\n * 2. Consumes 'protocol' (ObjectStackProtocol)\n * 3. Instantiates RestServer to auto-generate routes\n */\nexport function createRestApiPlugin(config: RestApiPluginConfig = {}): Plugin {\n return {\n name: 'com.objectstack.rest.api',\n version: '1.0.0',\n \n init: async (_ctx: PluginContext) => {\n // No service registration, this is a consumer plugin\n },\n \n start: async (ctx: PluginContext) => {\n const serverService = config.serverServiceName || 'http.server';\n const protocolService = config.protocolServiceName || 'protocol';\n \n let server: IHttpServer | undefined;\n let protocol: ObjectStackProtocol | undefined;\n\n try {\n server = ctx.getService<IHttpServer>(serverService);\n } catch (e) {\n // Ignore missing service\n }\n\n try {\n protocol = ctx.getService<ObjectStackProtocol>(protocolService);\n } catch (e) {\n // Ignore missing service\n }\n \n if (!server) {\n ctx.logger.warn(`RestApiPlugin: HTTP Server service '${serverService}' not found. REST routes skipped.`);\n return;\n }\n \n if (!protocol) {\n ctx.logger.warn(`RestApiPlugin: Protocol service '${protocolService}' not found. REST routes skipped.`);\n return;\n }\n \n ctx.logger.info('Hydrating REST API from Protocol...');\n \n try {\n const restServer = new RestServer(server, protocol, config.api as any);\n restServer.registerRoutes();\n \n ctx.logger.info('REST API successfully registered');\n } catch (err: any) {\n ctx.logger.error('Failed to register REST API routes', { error: err.message } as any);\n throw err;\n }\n }\n };\n}\n\n/**\n * @deprecated Use {@link createRestApiPlugin} instead\n */\nexport const createApiRegistryPlugin = createRestApiPlugin;\n"],"mappings":";AAoDO,IAAM,eAAN,MAAmB;AAAA,EAItB,YAAY,QAAqB;AAC7B,SAAK,SAAS;AACd,SAAK,SAAS,oBAAI,IAAI;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,OAA+E;AAEpF,QAAI,OAAO,MAAM,YAAY,UAAU;AACnC,YAAM,IAAI;AAAA,QACN,mFACgC,MAAM,OAAO;AAAA,MAEjD;AAAA,IACJ;AAEA,UAAM,UAAwB,MAAM;AAEpC,UAAM,aAAyB;AAAA,MAC3B,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,UAAU,MAAM;AAAA,IACpB;AAEA,UAAM,MAAM,KAAK,YAAY,MAAM,QAAQ,MAAM,IAAI;AACrD,SAAK,OAAO,IAAI,KAAK,UAAU;AAG/B,SAAK,mBAAmB,UAAU;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,SAAwF;AACjG,YAAQ,QAAQ,WAAS,KAAK,SAAS,KAAK,CAAC;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,QAAoB,MAAoB;AAC/C,UAAM,MAAM,KAAK,YAAY,QAAQ,IAAI;AACzC,SAAK,OAAO,OAAO,GAAG;AAAA,EAG1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,QAAoB,MAAsC;AAC1D,UAAM,MAAM,KAAK,YAAY,QAAQ,IAAI;AACzC,WAAO,KAAK,OAAO,IAAI,GAAG;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,SAAuB;AACnB,WAAO,MAAM,KAAK,KAAK,OAAO,OAAO,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,QAAkC;AAC1C,WAAO,KAAK,OAAO,EAAE,OAAO,WAAS,MAAM,WAAW,MAAM;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,QAA8B;AACtC,WAAO,KAAK,OAAO,EAAE,OAAO,WAAS,MAAM,KAAK,WAAW,MAAM,CAAC;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,KAA2B;AAChC,WAAO,KAAK,OAAO,EAAE;AAAA,MAAO,WACxB,MAAM,UAAU,MAAM,SAAS,GAAG;AAAA,IACtC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAgB,WAAqD;AACvE,UAAM,UAAU,IAAI,kBAAkB,MAAM,MAAM;AAClD,cAAU,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAgB;AACZ,WAAO,KAAK,OAAO;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACV,SAAK,OAAO,MAAM;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,QAAoB,MAAsB;AAC1D,WAAO,GAAG,MAAM,IAAI,IAAI;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,OAAyB;AAChD,UAAM,EAAE,QAAQ,MAAM,QAAQ,IAAI;AAElC,YAAQ,QAAQ;AAAA,MACZ,KAAK;AACD,aAAK,OAAO,IAAI,MAAM,OAAO;AAC7B;AAAA,MACJ,KAAK;AACD,aAAK,OAAO,KAAK,MAAM,OAAO;AAC9B;AAAA,MACJ,KAAK;AACD,aAAK,OAAO,IAAI,MAAM,OAAO;AAC7B;AAAA,MACJ,KAAK;AACD,aAAK,OAAO,OAAO,MAAM,OAAO;AAChC;AAAA,MACJ,KAAK;AACD,aAAK,OAAO,MAAM,MAAM,OAAO;AAC/B;AAAA,MACJ;AACI,cAAM,IAAI,MAAM,4BAA4B,MAAM,EAAE;AAAA,IAC5D;AAAA,EACJ;AACJ;AAOO,IAAM,oBAAN,MAAwB;AAAA,EAI3B,YAAY,SAAuB,QAAgB;AAC/C,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,MAAc,SAAuB,UAAmD;AACxF,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,MAAc,SAAuB,UAAmD;AACzF,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,MAAc,SAAuB,UAAmD;AACxF,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAc,SAAuB,UAAmD;AAC1F,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,MAAc,SAAuB,UAAmD;AAC3F,SAAK,QAAQ,SAAS;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM,KAAK,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,MAAsB;AAEtC,UAAM,mBAAmB,KAAK,OAAO,SAAS,GAAG,IAC3C,KAAK,OAAO,MAAM,GAAG,EAAE,IACvB,KAAK;AACX,UAAM,iBAAiB,KAAK,WAAW,GAAG,IACpC,OACA,MAAM;AAEZ,WAAO,mBAAmB;AAAA,EAC9B;AACJ;;;ACtNO,IAAM,aAAN,MAAiB;AAAA,EAKpB,YACI,QACA,UACA,SAA2B,CAAC,GAC9B;AACE,SAAK,WAAW;AAChB,SAAK,SAAS,KAAK,gBAAgB,MAAM;AACzC,SAAK,eAAe,IAAI,aAAa,MAAM;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,QAAsD;AAC1E,UAAM,MAAO,OAAO,OAAO,CAAC;AAC5B,UAAM,OAAQ,OAAO,QAAQ,CAAC;AAC9B,UAAM,WAAY,OAAO,YAAY,CAAC;AACtC,UAAM,QAAS,OAAO,SAAS,CAAC;AAChC,UAAM,SAAU,OAAO,UAAU,CAAC;AAElC,WAAO;AAAA,MACH,KAAK;AAAA,QACD,SAAS,IAAI,WAAW;AAAA,QACxB,UAAU,IAAI,YAAY;AAAA,QAC1B,SAAS,IAAI;AAAA,QACb,YAAY,IAAI,cAAc;AAAA,QAC9B,gBAAgB,IAAI,kBAAkB;AAAA,QACtC,UAAU,IAAI,YAAY;AAAA,QAC1B,aAAa,IAAI,eAAe;AAAA,QAChC,iBAAiB,IAAI,mBAAmB;AAAA,QACxC,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,MACxB;AAAA,MACA,MAAM;AAAA,QACF,YAAY,KAAK,cAAc;AAAA,UAC3B,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,MAAM;AAAA,QACV;AAAA,QACA,UAAU,KAAK;AAAA,QACf,YAAY,KAAK,cAAc;AAAA,QAC/B,kBAAkB,KAAK,oBAAoB;AAAA,MAC/C;AAAA,MACA,UAAU;AAAA,QACN,QAAQ,SAAS,UAAU;AAAA,QAC3B,aAAa,SAAS,eAAe;AAAA,QACrC,UAAU,SAAS,YAAY;AAAA,QAC/B,WAAW,SAAS,aAAa;AAAA,UAC7B,OAAO;AAAA,UACP,OAAO;AAAA,UACP,MAAM;AAAA,UACN,QAAQ;AAAA,QACZ;AAAA,MACJ;AAAA,MACA,OAAO;AAAA,QACH,cAAc,MAAM,gBAAgB;AAAA,QACpC,qBAAqB,MAAM,uBAAuB;AAAA,QAClD,YAAY,MAAM,cAAc;AAAA,UAC5B,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,YAAY;AAAA,QAChB;AAAA,QACA,eAAe,MAAM,iBAAiB;AAAA,MAC1C;AAAA,MACA,QAAQ;AAAA,QACJ,gBAAgB,OAAO;AAAA,QACvB,gBAAgB,OAAO;AAAA,QACvB,eAAe,OAAO,iBAAiB;AAAA,QACvC,WAAW,OAAO;AAAA,MACtB;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAyB;AAC7B,UAAM,EAAE,IAAI,IAAI,KAAK;AACrB,WAAO,IAAI,WAAW,GAAG,IAAI,QAAQ,IAAI,IAAI,OAAO;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAuB;AACnB,UAAM,WAAW,KAAK,eAAe;AAGrC,QAAI,KAAK,OAAO,IAAI,iBAAiB;AACjC,WAAK,2BAA2B,QAAQ;AAAA,IAC5C;AAGA,QAAI,KAAK,OAAO,IAAI,gBAAgB;AAChC,WAAK,0BAA0B,QAAQ;AAAA,IAC3C;AAGA,QAAI,KAAK,OAAO,IAAI,UAAU;AAC1B,WAAK,oBAAoB,QAAQ;AAAA,IACrC;AAGA,QAAI,KAAK,OAAO,IAAI,YAAY;AAC5B,WAAK,sBAAsB,QAAQ;AAAA,IACvC;AAGA,QAAI,KAAK,OAAO,IAAI,aAAa;AAC7B,WAAK,uBAAuB,QAAQ;AAAA,IACxC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,2BAA2B,UAAwB;AACvD,SAAK,aAAa,SAAS;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS,OAAO,MAAW,QAAa;AACpC,YAAI;AACA,gBAAM,YAAY,MAAM,KAAK,SAAS,aAAa;AAGnD,oBAAU,UAAU,KAAK,OAAO,IAAI;AAEpC,cAAI,UAAU,WAAW;AAErB,gBAAI,KAAK,OAAO,IAAI,YAAY;AAC5B,wBAAU,UAAU,OAAO,GAAG,QAAQ,GAAG,KAAK,OAAO,KAAK,UAAU;AAAA,YACxE;AAEA,gBAAI,KAAK,OAAO,IAAI,gBAAgB;AAChC,wBAAU,UAAU,WAAW,GAAG,QAAQ,GAAG,KAAK,OAAO,SAAS,MAAM;AAAA,YAC5E;AAEA,gBAAI,KAAK,OAAO,IAAI,UAAU;AAC1B,wBAAU,UAAU,KAAK,GAAG,QAAQ;AAAA,YACxC;AAGA,gBAAI,UAAU,UAAU,MAAM;AAC1B,wBAAU,UAAU,OAAO,GAAG,QAAQ;AAAA,YAC1C;AAAA,UACJ;AAEA,cAAI,KAAK,SAAS;AAAA,QACtB,SAAS,OAAY;AACjB,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,QACjD;AAAA,MACJ;AAAA,MACA,UAAU;AAAA,QACN,SAAS;AAAA,QACT,MAAM,CAAC,WAAW;AAAA,MACtB;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKQ,0BAA0B,UAAwB;AACtD,UAAM,EAAE,SAAS,IAAI,KAAK;AAC1B,UAAM,WAAW,GAAG,QAAQ,GAAG,SAAS,MAAM;AAG9C,QAAI,SAAS,UAAU,UAAU,OAAO;AACpC,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS,OAAO,MAAW,QAAa;AACpC,cAAI;AACA,kBAAM,QAAQ,MAAM,KAAK,SAAS,aAAa;AAC/C,gBAAI,KAAK,KAAK;AAAA,UAClB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,UAAU;AAAA,QACrB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,SAAS,UAAU,UAAU,OAAO;AACpC,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,QAAQ,MAAM,KAAK,SAAS,aAAa,EAAE,MAAM,IAAI,OAAO,KAAK,CAAC;AACxE,gBAAI,KAAK,KAAK;AAAA,UAClB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,UAAU;AAAA,QACrB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,SAAS,UAAU,SAAS,OAAO;AACnC,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AAEA,gBAAI,SAAS,eAAe,KAAK,SAAS,mBAAmB;AACzD,oBAAM,eAAe;AAAA,gBACjB,aAAa,IAAI,QAAQ,eAAe;AAAA,gBACxC,iBAAiB,IAAI,QAAQ,mBAAmB;AAAA,cACpD;AAEA,oBAAM,SAAS,MAAM,KAAK,SAAS,kBAAkB;AAAA,gBACjD,MAAM,IAAI,OAAO;AAAA,gBACjB,MAAM,IAAI,OAAO;AAAA,gBACjB;AAAA,cACJ,CAAC;AAED,kBAAI,OAAO,aAAa;AACpB,oBAAI,OAAO,GAAG,EAAE,KAAK;AACrB;AAAA,cACJ;AAGA,kBAAI,OAAO,MAAM;AACb,sBAAM,YAAY,OAAO,KAAK,OACxB,MAAM,OAAO,KAAK,KAAK,MACvB,IAAI,OAAO,KAAK,KAAK;AAC3B,oBAAI,OAAO,QAAQ,SAAS;AAAA,cAChC;AACA,kBAAI,OAAO,cAAc;AACrB,oBAAI,OAAO,iBAAiB,IAAI,KAAK,OAAO,YAAY,EAAE,YAAY,CAAC;AAAA,cAC3E;AACA,kBAAI,OAAO,cAAc;AACrB,sBAAM,aAAa,OAAO,aAAa,WAAW,KAAK,IAAI;AAC3D,sBAAM,SAAS,OAAO,aAAa,SAC7B,aAAa,OAAO,aAAa,MAAM,KACvC;AACN,oBAAI,OAAO,iBAAiB,aAAa,MAAM;AAAA,cACnD;AAEA,kBAAI,KAAK,OAAO,IAAI;AAAA,YACxB,OAAO;AAEH,oBAAM,OAAO,MAAM,KAAK,SAAS,YAAY,EAAE,MAAM,IAAI,OAAO,MAAM,MAAM,IAAI,OAAO,KAAK,CAAC;AAC7F,kBAAI,KAAK,IAAI;AAAA,YACjB;AAAA,UACJ,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,UAAU;AAAA,QACrB;AAAA,MACJ,CAAC;AAAA,IACL;AAKA,SAAK,aAAa,SAAS;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM,GAAG,QAAQ;AAAA,MACjB,SAAS,OAAO,KAAU,QAAa;AACnC,YAAI;AACA,cAAI,CAAC,KAAK,SAAS,cAAc;AAC7B,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,0DAA0D,CAAC;AACzF;AAAA,UACJ;AAEA,gBAAM,SAAS,MAAM,KAAK,SAAS,aAAa;AAAA,YAC5C,MAAM,IAAI,OAAO;AAAA,YACjB,MAAM,IAAI,OAAO;AAAA,YACjB,MAAM,IAAI;AAAA,UACd,CAAC;AACD,cAAI,KAAK,MAAM;AAAA,QACnB,SAAS,OAAY;AACjB,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,QACjD;AAAA,MACJ;AAAA,MACA,UAAU;AAAA,QACN,SAAS;AAAA,QACT,MAAM,CAAC,UAAU;AAAA,MACrB;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,UAAwB;AAChD,UAAM,SAAS,GAAG,QAAQ;AAG1B,SAAK,aAAa,SAAS;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM,GAAG,MAAM;AAAA,MACf,SAAS,OAAO,KAAU,QAAa;AACnC,YAAI;AACA,cAAI,KAAK,SAAS,WAAW;AACzB,kBAAM,OAAO,MAAM,KAAK,SAAS,UAAU;AAAA,cACvC,QAAQ,IAAI,OAAO;AAAA,cACnB,MAAM,IAAI,OAAO;AAAA,YACrB,CAAC;AACD,gBAAI,KAAK,IAAI;AAAA,UACjB,OAAO;AACH,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,8DAA8D,CAAC;AAAA,UACjG;AAAA,QACJ,SAAS,OAAY;AACjB,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,QACjD;AAAA,MACJ;AAAA,MACA,UAAU;AAAA,QACN,SAAS;AAAA,QACT,MAAM,CAAC,IAAI;AAAA,MACf;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAsB,UAAwB;AAClD,UAAM,EAAE,KAAK,IAAI,KAAK;AACtB,UAAM,WAAW,GAAG,QAAQ,GAAG,KAAK,UAAU;AAE9C,UAAM,aAAa,KAAK;AAGxB,QAAI,WAAW,MAAM;AACjB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,SAAS;AAAA,cACxC,QAAQ,IAAI,OAAO;AAAA,cACnB,OAAO,IAAI;AAAA,YACf,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,MAAM;AACjB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,QAAQ;AAAA,cACvC,QAAQ,IAAI,OAAO;AAAA,cACnB,IAAI,IAAI,OAAO;AAAA,YACnB,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,QAAQ;AACnB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,WAAW;AAAA,cAC1C,QAAQ,IAAI,OAAO;AAAA,cACnB,MAAM,IAAI;AAAA,YACd,CAAC;AACD,gBAAI,OAAO,GAAG,EAAE,KAAK,MAAM;AAAA,UAC/B,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,QAAQ;AACnB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,WAAW;AAAA,cAC1C,QAAQ,IAAI,OAAO;AAAA,cACnB,IAAI,IAAI,OAAO;AAAA,cACf,MAAM,IAAI;AAAA,YACd,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,QAAQ;AACnB,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,WAAW;AAAA,cAC1C,QAAQ,IAAI,OAAO;AAAA,cACnB,IAAI,IAAI,OAAO;AAAA,YACnB,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,MAAM;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAAuB,UAAwB;AACnD,UAAM,EAAE,MAAM,MAAM,IAAI,KAAK;AAC7B,UAAM,WAAW,GAAG,QAAQ,GAAG,KAAK,UAAU;AAE9C,UAAM,aAAa,MAAM;AAGzB,QAAI,MAAM,uBAAuB,KAAK,SAAS,WAAW;AACtD,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,UAAW;AAAA,cAC1C,QAAQ,IAAI,OAAO;AAAA,cACnB,SAAS,IAAI;AAAA,YACjB,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,OAAO;AAAA,QAC1B;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,cAAc,KAAK,SAAS,gBAAgB;AACvD,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,eAAgB;AAAA,cAC/C,QAAQ,IAAI,OAAO;AAAA,cACnB,SAAS,IAAI,QAAQ,CAAC;AAAA,YAC1B,CAAC;AACD,gBAAI,OAAO,GAAG,EAAE,KAAK,MAAM;AAAA,UAC/B,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,OAAO;AAAA,QAC1B;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,cAAc,KAAK,SAAS,gBAAgB;AACvD,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,eAAgB;AAAA,cAC/C,QAAQ,IAAI,OAAO;AAAA,cACnB,GAAG,IAAI;AAAA,YACX,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,OAAO;AAAA,QAC1B;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,WAAW,cAAc,KAAK,SAAS,gBAAgB;AACvD,WAAK,aAAa,SAAS;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM,GAAG,QAAQ;AAAA,QACjB,SAAS,OAAO,KAAU,QAAa;AACnC,cAAI;AACA,kBAAM,SAAS,MAAM,KAAK,SAAS,eAAgB;AAAA,cAC/C,QAAQ,IAAI,OAAO;AAAA,cACnB,GAAG,IAAI;AAAA,YACX,CAAC;AACD,gBAAI,KAAK,MAAM;AAAA,UACnB,SAAS,OAAY;AACjB,gBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,UACjD;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,QAAQ,OAAO;AAAA,QAC1B;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAgC;AAC5B,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY;AACR,WAAO,KAAK,aAAa,OAAO;AAAA,EACpC;AACJ;;;ACroBO,SAAS,oBAAoB,SAA8B,CAAC,GAAW;AAC1E,SAAO;AAAA,IACH,MAAM;AAAA,IACN,SAAS;AAAA,IAET,MAAM,OAAO,SAAwB;AAAA,IAErC;AAAA,IAEA,OAAO,OAAO,QAAuB;AACjC,YAAM,gBAAgB,OAAO,qBAAqB;AAClD,YAAM,kBAAkB,OAAO,uBAAuB;AAEtD,UAAI;AACJ,UAAI;AAEJ,UAAI;AACA,iBAAS,IAAI,WAAwB,aAAa;AAAA,MACtD,SAAS,GAAG;AAAA,MAEZ;AAEA,UAAI;AACA,mBAAW,IAAI,WAAgC,eAAe;AAAA,MAClE,SAAS,GAAG;AAAA,MAEZ;AAEA,UAAI,CAAC,QAAQ;AACT,YAAI,OAAO,KAAK,uCAAuC,aAAa,mCAAmC;AACvG;AAAA,MACJ;AAEA,UAAI,CAAC,UAAU;AACX,YAAI,OAAO,KAAK,oCAAoC,eAAe,mCAAmC;AACtG;AAAA,MACJ;AAEA,UAAI,OAAO,KAAK,qCAAqC;AAErD,UAAI;AACA,cAAM,aAAa,IAAI,WAAW,QAAQ,UAAU,OAAO,GAAU;AACrE,mBAAW,eAAe;AAE1B,YAAI,OAAO,KAAK,kCAAkC;AAAA,MACtD,SAAS,KAAU;AACf,YAAI,OAAO,MAAM,sCAAsC,EAAE,OAAO,IAAI,QAAQ,CAAQ;AACpF,cAAM;AAAA,MACV;AAAA,IACJ;AAAA,EACJ;AACJ;AAKO,IAAM,0BAA0B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/route-manager.ts","../src/rest-server.ts","../src/rest-api-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { RouteHandler, IHttpServer } from '@objectstack/core';\nimport { System, Shared } from '@objectstack/spec';\nimport { z } from 'zod';\n\ntype RouteHandlerMetadata = System.RouteHandlerMetadata;\ntype HttpMethod = z.infer<typeof Shared.HttpMethod>;\n\n/**\n * Route Entry\n * Internal representation of registered routes\n */\nexport interface RouteEntry {\n method: HttpMethod;\n path: string;\n handler: RouteHandler;\n metadata?: RouteHandlerMetadata['metadata'];\n security?: RouteHandlerMetadata['security'];\n}\n\n/**\n * RouteManager\n * \n * Manages route registration and organization for HTTP servers.\n * Provides:\n * - Route registration with metadata\n * - Route lookup and querying\n * - Bulk route registration\n * - Route grouping by prefix\n * \n * @example\n * const manager = new RouteManager(server);\n * \n * // Register individual route\n * manager.register({\n * method: 'GET',\n * path: '/api/users/:id',\n * handler: getUserHandler,\n * metadata: {\n * summary: 'Get user by ID',\n * tags: ['users']\n * }\n * });\n * \n * // Register route group\n * manager.group('/api/users', (group) => {\n * group.get('/', listUsersHandler);\n * group.post('/', createUserHandler);\n * group.get('/:id', getUserHandler);\n * });\n */\nexport class RouteManager {\n private server: IHttpServer;\n private routes: Map<string, RouteEntry>;\n \n constructor(server: IHttpServer) {\n this.server = server;\n this.routes = new Map();\n }\n \n /**\n * Register a route\n * @param entry - Route entry with method, path, handler, and metadata\n */\n register(entry: Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }): void {\n // Validate handler type - string handlers not yet supported\n if (typeof entry.handler === 'string') {\n throw new Error(\n `String-based route handlers are not supported yet. ` +\n `Received handler identifier \"${entry.handler}\". ` +\n `Please provide a RouteHandler function instead.`\n );\n }\n \n const handler: RouteHandler = entry.handler;\n \n const routeEntry: RouteEntry = {\n method: entry.method,\n path: entry.path,\n handler,\n metadata: entry.metadata,\n security: entry.security,\n };\n \n const key = this.getRouteKey(entry.method, entry.path);\n this.routes.set(key, routeEntry);\n \n // Register with underlying server\n this.registerWithServer(routeEntry);\n }\n \n /**\n * Register multiple routes\n * @param entries - Array of route entries\n */\n registerMany(entries: Array<Omit<RouteEntry, 'handler'> & { handler: RouteHandler | string }>): void {\n entries.forEach(entry => this.register(entry));\n }\n \n /**\n * Unregister a route\n * @param method - HTTP method\n * @param path - Route path\n */\n unregister(method: HttpMethod, path: string): void {\n const key = this.getRouteKey(method, path);\n this.routes.delete(key);\n // Note: Most server frameworks don't support unregistering routes at runtime\n // This just removes it from our registry\n }\n \n /**\n * Get route by method and path\n * @param method - HTTP method\n * @param path - Route path\n */\n get(method: HttpMethod, path: string): RouteEntry | undefined {\n const key = this.getRouteKey(method, path);\n return this.routes.get(key);\n }\n \n /**\n * Get all routes\n */\n getAll(): RouteEntry[] {\n return Array.from(this.routes.values());\n }\n \n /**\n * Get routes by method\n * @param method - HTTP method\n */\n getByMethod(method: HttpMethod): RouteEntry[] {\n return this.getAll().filter(route => route.method === method);\n }\n \n /**\n * Get routes by path prefix\n * @param prefix - Path prefix\n */\n getByPrefix(prefix: string): RouteEntry[] {\n return this.getAll().filter(route => route.path.startsWith(prefix));\n }\n \n /**\n * Get routes by tag\n * @param tag - Tag name\n */\n getByTag(tag: string): RouteEntry[] {\n return this.getAll().filter(route => \n route.metadata?.tags?.includes(tag)\n );\n }\n \n /**\n * Create a route group with common prefix\n * @param prefix - Common path prefix\n * @param configure - Function to configure routes in the group\n */\n group(prefix: string, configure: (group: RouteGroupBuilder) => void): void {\n const builder = new RouteGroupBuilder(this, prefix);\n configure(builder);\n }\n \n /**\n * Get route count\n */\n count(): number {\n return this.routes.size;\n }\n \n /**\n * Clear all routes\n */\n clear(): void {\n this.routes.clear();\n }\n \n /**\n * Get route key for storage\n */\n private getRouteKey(method: HttpMethod, path: string): string {\n return `${method}:${path}`;\n }\n \n /**\n * Register route with underlying server\n */\n private registerWithServer(entry: RouteEntry): void {\n const { method, path, handler } = entry;\n \n switch (method) {\n case 'GET':\n this.server.get(path, handler);\n break;\n case 'POST':\n this.server.post(path, handler);\n break;\n case 'PUT':\n this.server.put(path, handler);\n break;\n case 'DELETE':\n this.server.delete(path, handler);\n break;\n case 'PATCH':\n this.server.patch(path, handler);\n break;\n default:\n throw new Error(`Unsupported HTTP method: ${method}`);\n }\n }\n}\n\n/**\n * RouteGroupBuilder\n * \n * Builder for creating route groups with common prefix\n */\nexport class RouteGroupBuilder {\n private manager: RouteManager;\n private prefix: string;\n \n constructor(manager: RouteManager, prefix: string) {\n this.manager = manager;\n this.prefix = prefix;\n }\n \n /**\n * Register GET route in group\n */\n get(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'GET',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register POST route in group\n */\n post(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'POST',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register PUT route in group\n */\n put(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'PUT',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register PATCH route in group\n */\n patch(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'PATCH',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Register DELETE route in group\n */\n delete(path: string, handler: RouteHandler, metadata?: RouteHandlerMetadata['metadata']): this {\n this.manager.register({\n method: 'DELETE',\n path: this.resolvePath(path),\n handler,\n metadata,\n });\n return this;\n }\n \n /**\n * Resolve full path with prefix\n */\n private resolvePath(path: string): string {\n // Normalize slashes\n const normalizedPrefix = this.prefix.endsWith('/') \n ? this.prefix.slice(0, -1) \n : this.prefix;\n const normalizedPath = path.startsWith('/') \n ? path \n : '/' + path;\n \n return normalizedPrefix + normalizedPath;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { IHttpServer } from '@objectstack/core';\nimport { RouteManager } from './route-manager.js';\nimport { RestServerConfig, RestApiConfig, CrudEndpointsConfig, MetadataEndpointsConfig, BatchEndpointsConfig, RouteGenerationConfig } from '@objectstack/spec/api';\nimport { ObjectStackProtocol } from '@objectstack/spec/api';\n\n/**\n * Normalized REST Server Configuration\n * All nested properties are required after normalization\n */\ntype NormalizedRestServerConfig = {\n api: {\n version: string;\n basePath: string;\n apiPath: string | undefined;\n enableCrud: boolean;\n enableMetadata: boolean;\n enableUi: boolean;\n enableBatch: boolean;\n enableDiscovery: boolean;\n documentation: RestApiConfig['documentation'];\n responseFormat: RestApiConfig['responseFormat'];\n };\n crud: {\n operations: {\n create: boolean;\n read: boolean;\n update: boolean;\n delete: boolean;\n list: boolean;\n };\n patterns: CrudEndpointsConfig['patterns'];\n dataPrefix: string;\n objectParamStyle: 'path' | 'query';\n };\n metadata: {\n prefix: string;\n enableCache: boolean;\n cacheTtl: number;\n endpoints: {\n types: boolean;\n items: boolean;\n item: boolean;\n schema: boolean;\n };\n };\n batch: {\n maxBatchSize: number;\n enableBatchEndpoint: boolean;\n operations: {\n createMany: boolean;\n updateMany: boolean;\n deleteMany: boolean;\n upsertMany: boolean;\n };\n defaultAtomic: boolean;\n };\n routes: {\n includeObjects: string[] | undefined;\n excludeObjects: string[] | undefined;\n nameTransform: 'none' | 'plural' | 'kebab-case' | 'camelCase';\n overrides: RouteGenerationConfig['overrides'];\n };\n};\n\n/**\n * RestServer\n * \n * Provides automatic REST API endpoint generation for ObjectStack.\n * Generates standard RESTful CRUD endpoints, metadata endpoints, and batch operations\n * based on the configured protocol provider.\n * \n * Features:\n * - Automatic CRUD endpoint generation (GET, POST, PUT, PATCH, DELETE)\n * - Metadata API endpoints (/meta)\n * - Batch operation endpoints (/batch, /createMany, /updateMany, /deleteMany)\n * - Discovery endpoint\n * - Configurable path prefixes and patterns\n * \n * @example\n * const restServer = new RestServer(httpServer, protocolProvider, {\n * api: {\n * version: 'v1',\n * basePath: '/api'\n * },\n * crud: {\n * dataPrefix: '/data'\n * }\n * });\n * \n * restServer.registerRoutes();\n */\nexport class RestServer {\n private protocol: ObjectStackProtocol;\n private config: NormalizedRestServerConfig;\n private routeManager: RouteManager;\n \n constructor(\n server: IHttpServer, \n protocol: ObjectStackProtocol, \n config: RestServerConfig = {}\n ) {\n this.protocol = protocol;\n this.config = this.normalizeConfig(config);\n this.routeManager = new RouteManager(server);\n }\n \n /**\n * Normalize configuration with defaults\n */\n private normalizeConfig(config: RestServerConfig): NormalizedRestServerConfig {\n const api = (config.api ?? {}) as Partial<RestApiConfig>;\n const crud = (config.crud ?? {}) as Partial<CrudEndpointsConfig>;\n const metadata = (config.metadata ?? {}) as Partial<MetadataEndpointsConfig>;\n const batch = (config.batch ?? {}) as Partial<BatchEndpointsConfig>;\n const routes = (config.routes ?? {}) as Partial<RouteGenerationConfig>;\n \n return {\n api: {\n version: api.version ?? 'v1',\n basePath: api.basePath ?? '/api',\n apiPath: api.apiPath,\n enableCrud: api.enableCrud ?? true,\n enableMetadata: api.enableMetadata ?? true,\n enableUi: api.enableUi ?? true,\n enableBatch: api.enableBatch ?? true,\n enableDiscovery: api.enableDiscovery ?? true,\n documentation: api.documentation,\n responseFormat: api.responseFormat,\n },\n crud: {\n operations: crud.operations ?? {\n create: true,\n read: true,\n update: true,\n delete: true,\n list: true,\n },\n patterns: crud.patterns,\n dataPrefix: crud.dataPrefix ?? '/data',\n objectParamStyle: crud.objectParamStyle ?? 'path',\n },\n metadata: {\n prefix: metadata.prefix ?? '/meta',\n enableCache: metadata.enableCache ?? true,\n cacheTtl: metadata.cacheTtl ?? 3600,\n endpoints: metadata.endpoints ?? {\n types: true,\n items: true,\n item: true,\n schema: true,\n },\n },\n batch: {\n maxBatchSize: batch.maxBatchSize ?? 200,\n enableBatchEndpoint: batch.enableBatchEndpoint ?? true,\n operations: batch.operations ?? {\n createMany: true,\n updateMany: true,\n deleteMany: true,\n upsertMany: true,\n },\n defaultAtomic: batch.defaultAtomic ?? true,\n },\n routes: {\n includeObjects: routes.includeObjects,\n excludeObjects: routes.excludeObjects,\n nameTransform: routes.nameTransform ?? 'none',\n overrides: routes.overrides,\n },\n };\n }\n \n /**\n * Get the full API base path\n */\n private getApiBasePath(): string {\n const { api } = this.config;\n return api.apiPath ?? `${api.basePath}/${api.version}`;\n }\n \n /**\n * Register all REST API routes\n */\n registerRoutes(): void {\n const basePath = this.getApiBasePath();\n \n // Discovery endpoint\n if (this.config.api.enableDiscovery) {\n this.registerDiscoveryEndpoints(basePath);\n }\n \n // Metadata endpoints\n if (this.config.api.enableMetadata) {\n this.registerMetadataEndpoints(basePath);\n }\n\n // UI endpoints\n if (this.config.api.enableUi) {\n this.registerUiEndpoints(basePath);\n }\n \n // CRUD endpoints\n if (this.config.api.enableCrud) {\n this.registerCrudEndpoints(basePath);\n }\n \n // Batch endpoints\n if (this.config.api.enableBatch) {\n this.registerBatchEndpoints(basePath);\n }\n }\n \n /**\n * Register discovery endpoints\n */\n private registerDiscoveryEndpoints(basePath: string): void {\n const discoveryHandler = async (_req: any, res: any) => {\n try {\n const discovery = await this.protocol.getDiscovery();\n \n // Override discovery information with actual server configuration\n discovery.version = this.config.api.version;\n \n if (discovery.routes) {\n // Ensure routes match the actual mounted paths\n if (this.config.api.enableCrud) {\n discovery.routes.data = `${basePath}${this.config.crud.dataPrefix}`;\n }\n \n if (this.config.api.enableMetadata) {\n discovery.routes.metadata = `${basePath}${this.config.metadata.prefix}`;\n }\n\n if (this.config.api.enableUi) {\n discovery.routes.ui = `${basePath}/ui`;\n }\n\n // Align auth route with the versioned base path if present\n if (discovery.routes.auth) {\n discovery.routes.auth = `${basePath}/auth`;\n }\n }\n\n res.json(discovery);\n } catch (error: any) {\n res.status(500).json({ error: error.message });\n }\n };\n\n // Register at basePath (e.g. /api/v1)\n this.routeManager.register({\n method: 'GET',\n path: basePath,\n handler: discoveryHandler,\n metadata: {\n summary: 'Get API discovery information',\n tags: ['discovery'],\n },\n });\n\n // Register at basePath/discovery (e.g. /api/v1/discovery)\n this.routeManager.register({\n method: 'GET',\n path: `${basePath}/discovery`,\n handler: discoveryHandler,\n metadata: {\n summary: 'Get API discovery information',\n tags: ['discovery'],\n },\n });\n }\n \n /**\n * Register metadata endpoints\n */\n private registerMetadataEndpoints(basePath: string): void {\n const { metadata } = this.config;\n const metaPath = `${basePath}${metadata.prefix}`;\n \n // GET /meta - List all metadata types\n if (metadata.endpoints.types !== false) {\n this.routeManager.register({\n method: 'GET',\n path: metaPath,\n handler: async (_req: any, res: any) => {\n try {\n const types = await this.protocol.getMetaTypes();\n res.json(types);\n } catch (error: any) {\n res.status(500).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'List all metadata types',\n tags: ['metadata'],\n },\n });\n }\n \n // GET /meta/:type - List items of a type\n if (metadata.endpoints.items !== false) {\n this.routeManager.register({\n method: 'GET',\n path: `${metaPath}/:type`,\n handler: async (req: any, res: any) => {\n try {\n const items = await this.protocol.getMetaItems({ type: req.params.type });\n res.json(items);\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'List metadata items of a type',\n tags: ['metadata'],\n },\n });\n }\n \n // GET /meta/:type/:name - Get specific item\n if (metadata.endpoints.item !== false) {\n this.routeManager.register({\n method: 'GET',\n path: `${metaPath}/:type/:name`,\n handler: async (req: any, res: any) => {\n try {\n // Check if cached version is available\n if (metadata.enableCache && this.protocol.getMetaItemCached) {\n const cacheRequest = {\n ifNoneMatch: req.headers['if-none-match'] as string,\n ifModifiedSince: req.headers['if-modified-since'] as string,\n };\n \n const result = await this.protocol.getMetaItemCached({\n type: req.params.type,\n name: req.params.name,\n cacheRequest\n });\n \n if (result.notModified) {\n res.status(304).send();\n return;\n }\n \n // Set cache headers\n if (result.etag) {\n const etagValue = result.etag.weak \n ? `W/\"${result.etag.value}\"` \n : `\"${result.etag.value}\"`;\n res.header('ETag', etagValue);\n }\n if (result.lastModified) {\n res.header('Last-Modified', new Date(result.lastModified).toUTCString());\n }\n if (result.cacheControl) {\n const directives = result.cacheControl.directives.join(', ');\n const maxAge = result.cacheControl.maxAge \n ? `, max-age=${result.cacheControl.maxAge}` \n : '';\n res.header('Cache-Control', directives + maxAge);\n }\n \n res.json(result.data);\n } else {\n // Non-cached version\n const item = await this.protocol.getMetaItem({ type: req.params.type, name: req.params.name });\n res.json(item);\n }\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get specific metadata item',\n tags: ['metadata'],\n },\n });\n }\n\n // PUT /meta/:type/:name - Save metadata item\n // We always register this route, but return 501 if protocol doesn't support it\n // This makes it discoverable even if not implemented\n this.routeManager.register({\n method: 'PUT',\n path: `${metaPath}/:type/:name`,\n handler: async (req: any, res: any) => {\n try {\n if (!this.protocol.saveMetaItem) {\n res.status(501).json({ error: 'Save operation not supported by protocol implementation' });\n return;\n }\n\n const result = await this.protocol.saveMetaItem({\n type: req.params.type,\n name: req.params.name,\n item: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Save specific metadata item',\n tags: ['metadata'],\n },\n });\n }\n\n /**\n * Register UI endpoints\n */\n private registerUiEndpoints(basePath: string): void {\n const uiPath = `${basePath}/ui`;\n \n // GET /ui/view/:object/:type - Resolve view for object\n this.routeManager.register({\n method: 'GET',\n path: `${uiPath}/view/:object/:type`,\n handler: async (req: any, res: any) => {\n try {\n if (this.protocol.getUiView) {\n const view = await this.protocol.getUiView({ \n object: req.params.object, \n type: req.params.type as any \n });\n res.json(view);\n } else {\n res.status(501).json({ error: 'UI View resolution not supported by protocol implementation' });\n }\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Resolve UI View for object',\n tags: ['ui'],\n },\n });\n }\n \n /**\n * Register CRUD endpoints for data operations\n */\n private registerCrudEndpoints(basePath: string): void {\n const { crud } = this.config;\n const dataPath = `${basePath}${crud.dataPrefix}`;\n \n const operations = crud.operations;\n \n // GET /data/:object - List/query records\n if (operations.list) {\n this.routeManager.register({\n method: 'GET',\n path: `${dataPath}/:object`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.findData({\n object: req.params.object, \n query: req.query\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Query records',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // GET /data/:object/:id - Get single record\n if (operations.read) {\n this.routeManager.register({\n method: 'GET',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.getData({\n object: req.params.object, \n id: req.params.id\n });\n res.json(result);\n } catch (error: any) {\n res.status(404).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Get record by ID',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // POST /data/:object - Create record\n if (operations.create) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.createData({\n object: req.params.object, \n data: req.body\n });\n res.status(201).json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Create record',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // PATCH /data/:object/:id - Update record\n if (operations.update) {\n this.routeManager.register({\n method: 'PATCH',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.updateData({\n object: req.params.object,\n id: req.params.id,\n data: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Update record',\n tags: ['data', 'crud'],\n },\n });\n }\n \n // DELETE /data/:object/:id - Delete record\n if (operations.delete) {\n this.routeManager.register({\n method: 'DELETE',\n path: `${dataPath}/:object/:id`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.deleteData({\n object: req.params.object, \n id: req.params.id\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Delete record',\n tags: ['data', 'crud'],\n },\n });\n }\n }\n \n /**\n * Register batch operation endpoints\n */\n private registerBatchEndpoints(basePath: string): void {\n const { crud, batch } = this.config;\n const dataPath = `${basePath}${crud.dataPrefix}`;\n \n const operations = batch.operations;\n \n // POST /data/:object/batch - Generic batch endpoint\n if (batch.enableBatchEndpoint && this.protocol.batchData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/batch`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.batchData!({\n object: req.params.object, \n request: req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Batch operations',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/createMany - Bulk create\n if (operations.createMany && this.protocol.createManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/createMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.createManyData!({\n object: req.params.object,\n records: req.body || []\n });\n res.status(201).json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Create multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/updateMany - Bulk update\n if (operations.updateMany && this.protocol.updateManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/updateMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.updateManyData!({\n object: req.params.object,\n ...req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Update multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n \n // POST /data/:object/deleteMany - Bulk delete\n if (operations.deleteMany && this.protocol.deleteManyData) {\n this.routeManager.register({\n method: 'POST',\n path: `${dataPath}/:object/deleteMany`,\n handler: async (req: any, res: any) => {\n try {\n const result = await this.protocol.deleteManyData!({\n object: req.params.object, \n ...req.body\n });\n res.json(result);\n } catch (error: any) {\n res.status(400).json({ error: error.message });\n }\n },\n metadata: {\n summary: 'Delete multiple records',\n tags: ['data', 'batch'],\n },\n });\n }\n }\n \n /**\n * Get the route manager\n */\n getRouteManager(): RouteManager {\n return this.routeManager;\n }\n \n /**\n * Get all registered routes\n */\n getRoutes() {\n return this.routeManager.getAll();\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport { RestServer } from './rest-server.js';\nimport { ObjectStackProtocol, RestServerConfig } from '@objectstack/spec/api';\n\nexport interface RestApiPluginConfig {\n serverServiceName?: string;\n protocolServiceName?: string;\n api?: RestServerConfig;\n}\n\n/**\n * REST API Plugin\n * \n * Responsibilities:\n * 1. Consumes 'http.server' (or configured service)\n * 2. Consumes 'protocol' (ObjectStackProtocol)\n * 3. Instantiates RestServer to auto-generate routes\n */\nexport function createRestApiPlugin(config: RestApiPluginConfig = {}): Plugin {\n return {\n name: 'com.objectstack.rest.api',\n version: '1.0.0',\n \n init: async (_ctx: PluginContext) => {\n // No service registration, this is a consumer plugin\n },\n \n start: async (ctx: PluginContext) => {\n const serverService = config.serverServiceName || 'http.server';\n const protocolService = config.protocolServiceName || 'protocol';\n \n let server: IHttpServer | undefined;\n let protocol: ObjectStackProtocol | undefined;\n\n try {\n server = ctx.getService<IHttpServer>(serverService);\n } catch (e) {\n // Ignore missing service\n }\n\n try {\n protocol = ctx.getService<ObjectStackProtocol>(protocolService);\n } catch (e) {\n // Ignore missing service\n }\n \n if (!server) {\n ctx.logger.warn(`RestApiPlugin: HTTP Server service '${serverService}' not found. REST routes skipped.`);\n return;\n }\n \n if (!protocol) {\n ctx.logger.warn(`RestApiPlugin: Protocol service '${protocolService}' not found. REST routes skipped.`);\n return;\n }\n \n ctx.logger.info('Hydrating REST API from Protocol...');\n \n try {\n const restServer = new RestServer(server, protocol, config.api as any);\n restServer.registerRoutes();\n \n ctx.logger.info('REST API successfully registered');\n } catch (err: any) {\n ctx.logger.error('Failed to register REST API routes', { error: err.message } as any);\n throw err;\n }\n }\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/rest",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "ObjectStack REST API Server - automatic REST endpoint generation from protocol",
|
|
6
6
|
"type": "module",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"zod": "^4.3.6",
|
|
18
|
-
"@objectstack/core": "
|
|
19
|
-
"@objectstack/spec": "
|
|
18
|
+
"@objectstack/core": "3.0.0",
|
|
19
|
+
"@objectstack/spec": "3.0.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"typescript": "^5.0.0",
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,3 @@ export type { RouteEntry } from './route-manager.js';
|
|
|
10
10
|
// REST API Plugin
|
|
11
11
|
export { createRestApiPlugin } from './rest-api-plugin.js';
|
|
12
12
|
export type { RestApiPluginConfig } from './rest-api-plugin.js';
|
|
13
|
-
|
|
14
|
-
// Backward-compatible aliases (deprecated)
|
|
15
|
-
export { createApiRegistryPlugin } from './rest-api-plugin.js';
|
|
16
|
-
export type { ApiRegistryConfig } from './rest-api-plugin.js';
|
package/src/rest-api-plugin.ts
CHANGED
|
@@ -10,11 +10,6 @@ export interface RestApiPluginConfig {
|
|
|
10
10
|
api?: RestServerConfig;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* @deprecated Use {@link RestApiPluginConfig} instead
|
|
15
|
-
*/
|
|
16
|
-
export type ApiRegistryConfig = RestApiPluginConfig;
|
|
17
|
-
|
|
18
13
|
/**
|
|
19
14
|
* REST API Plugin
|
|
20
15
|
*
|
|
@@ -75,8 +70,3 @@ export function createRestApiPlugin(config: RestApiPluginConfig = {}): Plugin {
|
|
|
75
70
|
}
|
|
76
71
|
};
|
|
77
72
|
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @deprecated Use {@link createRestApiPlugin} instead
|
|
81
|
-
*/
|
|
82
|
-
export const createApiRegistryPlugin = createRestApiPlugin;
|
package/src/rest-server.ts
CHANGED
|
@@ -216,33 +216,30 @@ export class RestServer {
|
|
|
216
216
|
* Register discovery endpoints
|
|
217
217
|
*/
|
|
218
218
|
private registerDiscoveryEndpoints(basePath: string): void {
|
|
219
|
-
|
|
220
|
-
method: 'GET',
|
|
221
|
-
path: basePath,
|
|
222
|
-
handler: async (_req: any, res: any) => {
|
|
219
|
+
const discoveryHandler = async (_req: any, res: any) => {
|
|
223
220
|
try {
|
|
224
221
|
const discovery = await this.protocol.getDiscovery();
|
|
225
222
|
|
|
226
223
|
// Override discovery information with actual server configuration
|
|
227
224
|
discovery.version = this.config.api.version;
|
|
228
225
|
|
|
229
|
-
if (discovery.
|
|
230
|
-
// Ensure
|
|
226
|
+
if (discovery.routes) {
|
|
227
|
+
// Ensure routes match the actual mounted paths
|
|
231
228
|
if (this.config.api.enableCrud) {
|
|
232
|
-
discovery.
|
|
229
|
+
discovery.routes.data = `${basePath}${this.config.crud.dataPrefix}`;
|
|
233
230
|
}
|
|
234
231
|
|
|
235
232
|
if (this.config.api.enableMetadata) {
|
|
236
|
-
discovery.
|
|
233
|
+
discovery.routes.metadata = `${basePath}${this.config.metadata.prefix}`;
|
|
237
234
|
}
|
|
238
235
|
|
|
239
236
|
if (this.config.api.enableUi) {
|
|
240
|
-
discovery.
|
|
237
|
+
discovery.routes.ui = `${basePath}/ui`;
|
|
241
238
|
}
|
|
242
239
|
|
|
243
|
-
// Align auth
|
|
244
|
-
if (discovery.
|
|
245
|
-
discovery.
|
|
240
|
+
// Align auth route with the versioned base path if present
|
|
241
|
+
if (discovery.routes.auth) {
|
|
242
|
+
discovery.routes.auth = `${basePath}/auth`;
|
|
246
243
|
}
|
|
247
244
|
}
|
|
248
245
|
|
|
@@ -250,7 +247,24 @@ export class RestServer {
|
|
|
250
247
|
} catch (error: any) {
|
|
251
248
|
res.status(500).json({ error: error.message });
|
|
252
249
|
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// Register at basePath (e.g. /api/v1)
|
|
253
|
+
this.routeManager.register({
|
|
254
|
+
method: 'GET',
|
|
255
|
+
path: basePath,
|
|
256
|
+
handler: discoveryHandler,
|
|
257
|
+
metadata: {
|
|
258
|
+
summary: 'Get API discovery information',
|
|
259
|
+
tags: ['discovery'],
|
|
253
260
|
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Register at basePath/discovery (e.g. /api/v1/discovery)
|
|
264
|
+
this.routeManager.register({
|
|
265
|
+
method: 'GET',
|
|
266
|
+
path: `${basePath}/discovery`,
|
|
267
|
+
handler: discoveryHandler,
|
|
254
268
|
metadata: {
|
|
255
269
|
summary: 'Get API discovery information',
|
|
256
270
|
tags: ['discovery'],
|
package/src/rest.test.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
4
4
|
import { RouteManager, RouteGroupBuilder } from './route-manager';
|
|
5
5
|
import { RestServer } from './rest-server';
|
|
6
|
-
import { createRestApiPlugin
|
|
6
|
+
import { createRestApiPlugin } from './rest-api-plugin';
|
|
7
7
|
import type { RestApiPluginConfig } from './rest-api-plugin';
|
|
8
8
|
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
@@ -309,8 +309,9 @@ describe('RestServer', () => {
|
|
|
309
309
|
|
|
310
310
|
// Expect at least discovery + metadata + CRUD routes
|
|
311
311
|
const paths = routes.map((r) => r.path);
|
|
312
|
-
// Discovery
|
|
312
|
+
// Discovery (both basePath and basePath/discovery)
|
|
313
313
|
expect(paths).toContain('/api/v1');
|
|
314
|
+
expect(paths).toContain('/api/v1/discovery');
|
|
314
315
|
// Metadata
|
|
315
316
|
expect(paths.some((p) => p.includes('/meta'))).toBe(true);
|
|
316
317
|
// CRUD
|
|
@@ -356,7 +357,7 @@ describe('RestServer', () => {
|
|
|
356
357
|
rest.registerRoutes();
|
|
357
358
|
|
|
358
359
|
const routes = rest.getRoutes();
|
|
359
|
-
//
|
|
360
|
+
// Neither basePath nor basePath/discovery should be registered
|
|
360
361
|
const discoveryRoutes = routes.filter((r) =>
|
|
361
362
|
r.metadata?.tags?.includes('discovery'),
|
|
362
363
|
);
|
|
@@ -519,13 +520,3 @@ describe('createRestApiPlugin', () => {
|
|
|
519
520
|
});
|
|
520
521
|
});
|
|
521
522
|
});
|
|
522
|
-
|
|
523
|
-
// ---------------------------------------------------------------------------
|
|
524
|
-
// Backward-compatible aliases
|
|
525
|
-
// ---------------------------------------------------------------------------
|
|
526
|
-
|
|
527
|
-
describe('backward-compatible aliases', () => {
|
|
528
|
-
it('createApiRegistryPlugin should be the same function as createRestApiPlugin', () => {
|
|
529
|
-
expect(createApiRegistryPlugin).toBe(createRestApiPlugin);
|
|
530
|
-
});
|
|
531
|
-
});
|