@objectstack/rest 2.0.6 → 2.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/rest@2.0.6 build /home/runner/work/spec/spec/packages/rest
2
+ > @objectstack/rest@2.0.7 build /home/runner/work/spec/spec/packages/rest
3
3
  > tsup --config ../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- CJS dist/index.cjs 21.89 KB
14
- CJS dist/index.cjs.map 50.79 KB
15
- CJS ⚡️ Build success in 84ms
16
- ESM dist/index.js 20.70 KB
17
- ESM dist/index.js.map 50.10 KB
18
- ESM ⚡️ Build success in 84ms
13
+ ESM dist/index.js 20.92 KB
14
+ ESM dist/index.js.map 50.71 KB
15
+ ESM ⚡️ Build success in 113ms
16
+ CJS dist/index.cjs 22.11 KB
17
+ CJS dist/index.cjs.map 51.39 KB
18
+ CJS ⚡️ Build success in 113ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 10053ms
20
+ DTS ⚡️ Build success in 10741ms
21
21
  DTS dist/index.d.ts 6.70 KB
22
22
  DTS dist/index.d.cts 6.70 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @objectstack/rest
2
2
 
3
+ ## 2.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @objectstack/spec@2.0.7
9
+ - @objectstack/core@2.0.7
10
+
3
11
  ## 2.0.6
4
12
 
5
13
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -341,32 +341,42 @@ var RestServer = class {
341
341
  * Register discovery endpoints
342
342
  */
343
343
  registerDiscoveryEndpoints(basePath) {
344
+ const discoveryHandler = async (_req, res) => {
345
+ try {
346
+ const discovery = await this.protocol.getDiscovery();
347
+ discovery.version = this.config.api.version;
348
+ if (discovery.routes) {
349
+ if (this.config.api.enableCrud) {
350
+ discovery.routes.data = `${basePath}${this.config.crud.dataPrefix}`;
351
+ }
352
+ if (this.config.api.enableMetadata) {
353
+ discovery.routes.metadata = `${basePath}${this.config.metadata.prefix}`;
354
+ }
355
+ if (this.config.api.enableUi) {
356
+ discovery.routes.ui = `${basePath}/ui`;
357
+ }
358
+ if (discovery.routes.auth) {
359
+ discovery.routes.auth = `${basePath}/auth`;
360
+ }
361
+ }
362
+ res.json(discovery);
363
+ } catch (error) {
364
+ res.status(500).json({ error: error.message });
365
+ }
366
+ };
344
367
  this.routeManager.register({
345
368
  method: "GET",
346
369
  path: basePath,
347
- handler: async (_req, res) => {
348
- try {
349
- const discovery = await this.protocol.getDiscovery();
350
- discovery.version = this.config.api.version;
351
- if (discovery.endpoints) {
352
- if (this.config.api.enableCrud) {
353
- discovery.endpoints.data = `${basePath}${this.config.crud.dataPrefix}`;
354
- }
355
- if (this.config.api.enableMetadata) {
356
- discovery.endpoints.metadata = `${basePath}${this.config.metadata.prefix}`;
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
- },
370
+ handler: discoveryHandler,
371
+ metadata: {
372
+ summary: "Get API discovery information",
373
+ tags: ["discovery"]
374
+ }
375
+ });
376
+ this.routeManager.register({
377
+ method: "GET",
378
+ path: `${basePath}/discovery`,
379
+ handler: discoveryHandler,
370
380
  metadata: {
371
381
  summary: "Get API discovery information",
372
382
  tags: ["discovery"]
@@ -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\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 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 * @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":";;;;;;;;;;;;;;;;;;;;AAAA;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;;;ACnpBO,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":[]}
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: async (_req, res) => {
318
- try {
319
- const discovery = await this.protocol.getDiscovery();
320
- discovery.version = this.config.api.version;
321
- if (discovery.endpoints) {
322
- if (this.config.api.enableCrud) {
323
- discovery.endpoints.data = `${basePath}${this.config.crud.dataPrefix}`;
324
- }
325
- if (this.config.api.enableMetadata) {
326
- discovery.endpoints.metadata = `${basePath}${this.config.metadata.prefix}`;
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"]
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":"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","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 * @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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/rest",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
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": "2.0.6",
19
- "@objectstack/spec": "2.0.6"
18
+ "@objectstack/core": "2.0.7",
19
+ "@objectstack/spec": "2.0.7"
20
20
  },
21
21
  "devDependencies": {
22
22
  "typescript": "^5.0.0",
@@ -216,33 +216,30 @@ export class RestServer {
216
216
  * Register discovery endpoints
217
217
  */
218
218
  private registerDiscoveryEndpoints(basePath: string): void {
219
- this.routeManager.register({
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.endpoints) {
230
- // Ensure endpoints match the actual mounted paths
226
+ if (discovery.routes) {
227
+ // Ensure routes match the actual mounted paths
231
228
  if (this.config.api.enableCrud) {
232
- discovery.endpoints.data = `${basePath}${this.config.crud.dataPrefix}`;
229
+ discovery.routes.data = `${basePath}${this.config.crud.dataPrefix}`;
233
230
  }
234
231
 
235
232
  if (this.config.api.enableMetadata) {
236
- discovery.endpoints.metadata = `${basePath}${this.config.metadata.prefix}`;
233
+ discovery.routes.metadata = `${basePath}${this.config.metadata.prefix}`;
237
234
  }
238
235
 
239
236
  if (this.config.api.enableUi) {
240
- discovery.endpoints.ui = `${basePath}/ui`;
237
+ discovery.routes.ui = `${basePath}/ui`;
241
238
  }
242
239
 
243
- // Align auth endpoint with the versioned base path if present
244
- if (discovery.endpoints.auth) {
245
- discovery.endpoints.auth = `${basePath}/auth`;
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
@@ -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
- // Discovery route is the basePath itself (e.g. /api/v1)
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
  );