@objectstack/express 4.0.1 → 4.0.3

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,22 +1,22 @@
1
1
 
2
- > @objectstack/express@4.0.1 build /home/runner/work/spec/spec/packages/adapters/express
2
+ > @objectstack/express@4.0.3 build /home/runner/work/framework/framework/packages/adapters/express
3
3
  > tsup --config ../../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.5.1
8
- CLI Using tsup config: /home/runner/work/spec/spec/tsup.config.ts
8
+ CLI Using tsup config: /home/runner/work/framework/framework/tsup.config.ts
9
9
  CLI Target: es2020
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- ESM dist/index.mjs 4.71 KB
14
- ESM dist/index.mjs.map 10.58 KB
15
- ESM ⚡️ Build success in 106ms
16
- CJS dist/index.js 5.79 KB
17
- CJS dist/index.js.map 10.63 KB
18
- CJS ⚡️ Build success in 98ms
13
+ CJS dist/index.js 5.80 KB
14
+ CJS dist/index.js.map 10.64 KB
15
+ CJS ⚡️ Build success in 42ms
16
+ ESM dist/index.mjs 4.72 KB
17
+ ESM dist/index.mjs.map 10.59 KB
18
+ ESM ⚡️ Build success in 43ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 13970ms
20
+ DTS ⚡️ Build success in 10385ms
21
21
  DTS dist/index.d.mts 1.18 KB
22
22
  DTS dist/index.d.ts 1.18 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @objectstack/express
2
2
 
3
+ ## 4.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - @objectstack/runtime@4.0.3
8
+
9
+ ## 4.0.2
10
+
11
+ ### Patch Changes
12
+
13
+ - @objectstack/runtime@4.0.2
14
+
3
15
  ## 4.0.0
4
16
 
5
17
  ### Patch Changes
package/dist/index.js CHANGED
@@ -137,7 +137,7 @@ function createExpressRouter(options) {
137
137
  const subPath = "/" + req.params.path;
138
138
  const method = req.method;
139
139
  const body = method === "POST" || method === "PUT" || method === "PATCH" ? req.body : void 0;
140
- const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res });
140
+ const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res }, prefix);
141
141
  return sendResult(result, res);
142
142
  } catch (err) {
143
143
  return errorResponse(err, res);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { type Router, type Request, type Response, type NextFunction, Router as createRouter } from 'express';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface ExpressAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: globalThis.Request): Promise<globalThis.Response>;\n}\n\n/**\n * Creates an Express Router with all ObjectStack route dispatchers.\n *\n * Only routes that need framework-specific handling (auth service, storage\n * file upload, GraphQL raw result, discovery wrapper) are registered explicitly.\n * All other routes are handled by a catch-all that delegates to\n * `HttpDispatcher.dispatch()`, making the adapter automatically support\n * new routes added to the dispatcher.\n *\n * @example\n * ```ts\n * import express from 'express';\n * import { createExpressRouter } from '@objectstack/express';\n *\n * const app = express();\n * app.use('/api', createExpressRouter({ kernel }));\n * app.listen(3000);\n * ```\n */\nexport function createExpressRouter(options: ExpressAdapterOptions): Router {\n const router = createRouter();\n const dispatcher = new HttpDispatcher(options.kernel);\n const prefix = options.prefix || '/api';\n\n const sendResult = (result: HttpDispatcherResult, res: Response) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) => res.set(k, v as string));\n }\n return res.status(result.response.status).json(result.response.body);\n }\n if (result.result) {\n const response = result.result;\n if (response.type === 'redirect' && response.url) {\n return res.redirect(response.url);\n }\n if (response.type === 'stream' && response.stream) {\n if (response.headers) {\n Object.entries(response.headers).forEach(([k, v]) => res.set(k, v as string));\n }\n response.stream.pipe(res);\n return;\n }\n return res.status(200).json(response);\n }\n }\n return res.status(404).json({ success: false, error: { message: 'Not Found', code: 404 } });\n };\n\n const errorResponse = (err: any, res: Response) => {\n return res.status(err.statusCode || 500).json({\n success: false,\n error: {\n message: err.message || 'Internal Server Error',\n code: err.statusCode || 500,\n },\n });\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n router.get('/discovery', async (_req: Request, res: Response) => {\n res.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- Auth (needs auth service integration) ---\n router.all('/auth/{*path}', async (req: Request, res: Response) => {\n try {\n const path = (req.params as any).path;\n const method = req.method;\n\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\n\n if (authService && typeof authService.handleRequest === 'function') {\n const protocol = req.protocol || 'http';\n const host = req.get?.('host') || req.headers?.host || 'localhost';\n const url = `${protocol}://${host}${req.originalUrl || req.url}`;\n const headers = new Headers();\n if (req.headers) {\n Object.entries(req.headers).forEach(([k, v]) => {\n if (typeof v === 'string') headers.set(k, v);\n else if (Array.isArray(v)) headers.set(k, v.join(', '));\n });\n }\n const init: RequestInit = { method, headers };\n if (method !== 'GET' && method !== 'HEAD' && req.body) {\n init.body = JSON.stringify(req.body);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n }\n const webRequest = new Request(url, init);\n const response = await authService.handleRequest(webRequest);\n res.status(response.status);\n response.headers.forEach((v: string, k: string) => res.set(k, v));\n const text = await response.text();\n return res.send(text);\n }\n\n // Fallback to dispatcher\n const body = method === 'GET' || method === 'HEAD' ? {} : req.body || {};\n const result = await dispatcher.handleAuth(path, method, body, { request: req, response: res });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\n router.post('/graphql', async (req: Request, res: Response) => {\n try {\n const result = await dispatcher.handleGraphQL(req.body, { request: req });\n return res.json(result);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- Storage (needs file upload handling) ---\n router.all('/storage/{*path}', async (req: Request, res: Response) => {\n try {\n const subPath = '/' + (req.params as any).path;\n const method = req.method;\n const file = (req as any).file || (req as any).files?.file;\n const result = await dispatcher.handleStorage(subPath, method, file, { request: req });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────\n // Handles meta, data, packages, analytics, automation, i18n, ui, openapi,\n // custom API endpoints, and any future routes added to HttpDispatcher.\n router.all('/{*path}', async (req: Request, res: Response) => {\n try {\n const subPath = '/' + (req.params as any).path;\n const method = req.method;\n const body = (method === 'POST' || method === 'PUT' || method === 'PATCH') ? req.body : undefined;\n const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n return router;\n}\n\n/**\n * Middleware that attaches the ObjectStack kernel to the request.\n */\nexport function objectStackMiddleware(kernel: ObjectKernel) {\n return (req: Request, _res: Response, next: NextFunction) => {\n (req as any).objectStack = kernel;\n next();\n };\n}\n"],"mappings":"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","names":["createRouter"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { type Router, type Request, type Response, type NextFunction, Router as createRouter } from 'express';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface ExpressAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: globalThis.Request): Promise<globalThis.Response>;\n}\n\n/**\n * Creates an Express Router with all ObjectStack route dispatchers.\n *\n * Only routes that need framework-specific handling (auth service, storage\n * file upload, GraphQL raw result, discovery wrapper) are registered explicitly.\n * All other routes are handled by a catch-all that delegates to\n * `HttpDispatcher.dispatch()`, making the adapter automatically support\n * new routes added to the dispatcher.\n *\n * @example\n * ```ts\n * import express from 'express';\n * import { createExpressRouter } from '@objectstack/express';\n *\n * const app = express();\n * app.use('/api', createExpressRouter({ kernel }));\n * app.listen(3000);\n * ```\n */\nexport function createExpressRouter(options: ExpressAdapterOptions): Router {\n const router = createRouter();\n const dispatcher = new HttpDispatcher(options.kernel);\n const prefix = options.prefix || '/api';\n\n const sendResult = (result: HttpDispatcherResult, res: Response) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) => res.set(k, v as string));\n }\n return res.status(result.response.status).json(result.response.body);\n }\n if (result.result) {\n const response = result.result;\n if (response.type === 'redirect' && response.url) {\n return res.redirect(response.url);\n }\n if (response.type === 'stream' && response.stream) {\n if (response.headers) {\n Object.entries(response.headers).forEach(([k, v]) => res.set(k, v as string));\n }\n response.stream.pipe(res);\n return;\n }\n return res.status(200).json(response);\n }\n }\n return res.status(404).json({ success: false, error: { message: 'Not Found', code: 404 } });\n };\n\n const errorResponse = (err: any, res: Response) => {\n return res.status(err.statusCode || 500).json({\n success: false,\n error: {\n message: err.message || 'Internal Server Error',\n code: err.statusCode || 500,\n },\n });\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n router.get('/discovery', async (_req: Request, res: Response) => {\n res.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- Auth (needs auth service integration) ---\n router.all('/auth/{*path}', async (req: Request, res: Response) => {\n try {\n const path = (req.params as any).path;\n const method = req.method;\n\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\n\n if (authService && typeof authService.handleRequest === 'function') {\n const protocol = req.protocol || 'http';\n const host = req.get?.('host') || req.headers?.host || 'localhost';\n const url = `${protocol}://${host}${req.originalUrl || req.url}`;\n const headers = new Headers();\n if (req.headers) {\n Object.entries(req.headers).forEach(([k, v]) => {\n if (typeof v === 'string') headers.set(k, v);\n else if (Array.isArray(v)) headers.set(k, v.join(', '));\n });\n }\n const init: RequestInit = { method, headers };\n if (method !== 'GET' && method !== 'HEAD' && req.body) {\n init.body = JSON.stringify(req.body);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n }\n const webRequest = new Request(url, init);\n const response = await authService.handleRequest(webRequest);\n res.status(response.status);\n response.headers.forEach((v: string, k: string) => res.set(k, v));\n const text = await response.text();\n return res.send(text);\n }\n\n // Fallback to dispatcher\n const body = method === 'GET' || method === 'HEAD' ? {} : req.body || {};\n const result = await dispatcher.handleAuth(path, method, body, { request: req, response: res });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\n router.post('/graphql', async (req: Request, res: Response) => {\n try {\n const result = await dispatcher.handleGraphQL(req.body, { request: req });\n return res.json(result);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- Storage (needs file upload handling) ---\n router.all('/storage/{*path}', async (req: Request, res: Response) => {\n try {\n const subPath = '/' + (req.params as any).path;\n const method = req.method;\n const file = (req as any).file || (req as any).files?.file;\n const result = await dispatcher.handleStorage(subPath, method, file, { request: req });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────\n // Handles meta, data, packages, analytics, automation, i18n, ui, openapi,\n // custom API endpoints, and any future routes added to HttpDispatcher.\n router.all('/{*path}', async (req: Request, res: Response) => {\n try {\n const subPath = '/' + (req.params as any).path;\n const method = req.method;\n const body = (method === 'POST' || method === 'PUT' || method === 'PATCH') ? req.body : undefined;\n const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res }, prefix);\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n return router;\n}\n\n/**\n * Middleware that attaches the ObjectStack kernel to the request.\n */\nexport function objectStackMiddleware(kernel: ObjectKernel) {\n return (req: Request, _res: Response, next: NextFunction) => {\n (req as any).objectStack = kernel;\n next();\n };\n}\n"],"mappings":"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","names":["createRouter"]}
package/dist/index.mjs CHANGED
@@ -112,7 +112,7 @@ function createExpressRouter(options) {
112
112
  const subPath = "/" + req.params.path;
113
113
  const method = req.method;
114
114
  const body = method === "POST" || method === "PUT" || method === "PATCH" ? req.body : void 0;
115
- const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res });
115
+ const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res }, prefix);
116
116
  return sendResult(result, res);
117
117
  } catch (err) {
118
118
  return errorResponse(err, res);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { type Router, type Request, type Response, type NextFunction, Router as createRouter } from 'express';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface ExpressAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: globalThis.Request): Promise<globalThis.Response>;\n}\n\n/**\n * Creates an Express Router with all ObjectStack route dispatchers.\n *\n * Only routes that need framework-specific handling (auth service, storage\n * file upload, GraphQL raw result, discovery wrapper) are registered explicitly.\n * All other routes are handled by a catch-all that delegates to\n * `HttpDispatcher.dispatch()`, making the adapter automatically support\n * new routes added to the dispatcher.\n *\n * @example\n * ```ts\n * import express from 'express';\n * import { createExpressRouter } from '@objectstack/express';\n *\n * const app = express();\n * app.use('/api', createExpressRouter({ kernel }));\n * app.listen(3000);\n * ```\n */\nexport function createExpressRouter(options: ExpressAdapterOptions): Router {\n const router = createRouter();\n const dispatcher = new HttpDispatcher(options.kernel);\n const prefix = options.prefix || '/api';\n\n const sendResult = (result: HttpDispatcherResult, res: Response) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) => res.set(k, v as string));\n }\n return res.status(result.response.status).json(result.response.body);\n }\n if (result.result) {\n const response = result.result;\n if (response.type === 'redirect' && response.url) {\n return res.redirect(response.url);\n }\n if (response.type === 'stream' && response.stream) {\n if (response.headers) {\n Object.entries(response.headers).forEach(([k, v]) => res.set(k, v as string));\n }\n response.stream.pipe(res);\n return;\n }\n return res.status(200).json(response);\n }\n }\n return res.status(404).json({ success: false, error: { message: 'Not Found', code: 404 } });\n };\n\n const errorResponse = (err: any, res: Response) => {\n return res.status(err.statusCode || 500).json({\n success: false,\n error: {\n message: err.message || 'Internal Server Error',\n code: err.statusCode || 500,\n },\n });\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n router.get('/discovery', async (_req: Request, res: Response) => {\n res.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- Auth (needs auth service integration) ---\n router.all('/auth/{*path}', async (req: Request, res: Response) => {\n try {\n const path = (req.params as any).path;\n const method = req.method;\n\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\n\n if (authService && typeof authService.handleRequest === 'function') {\n const protocol = req.protocol || 'http';\n const host = req.get?.('host') || req.headers?.host || 'localhost';\n const url = `${protocol}://${host}${req.originalUrl || req.url}`;\n const headers = new Headers();\n if (req.headers) {\n Object.entries(req.headers).forEach(([k, v]) => {\n if (typeof v === 'string') headers.set(k, v);\n else if (Array.isArray(v)) headers.set(k, v.join(', '));\n });\n }\n const init: RequestInit = { method, headers };\n if (method !== 'GET' && method !== 'HEAD' && req.body) {\n init.body = JSON.stringify(req.body);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n }\n const webRequest = new Request(url, init);\n const response = await authService.handleRequest(webRequest);\n res.status(response.status);\n response.headers.forEach((v: string, k: string) => res.set(k, v));\n const text = await response.text();\n return res.send(text);\n }\n\n // Fallback to dispatcher\n const body = method === 'GET' || method === 'HEAD' ? {} : req.body || {};\n const result = await dispatcher.handleAuth(path, method, body, { request: req, response: res });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\n router.post('/graphql', async (req: Request, res: Response) => {\n try {\n const result = await dispatcher.handleGraphQL(req.body, { request: req });\n return res.json(result);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- Storage (needs file upload handling) ---\n router.all('/storage/{*path}', async (req: Request, res: Response) => {\n try {\n const subPath = '/' + (req.params as any).path;\n const method = req.method;\n const file = (req as any).file || (req as any).files?.file;\n const result = await dispatcher.handleStorage(subPath, method, file, { request: req });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────\n // Handles meta, data, packages, analytics, automation, i18n, ui, openapi,\n // custom API endpoints, and any future routes added to HttpDispatcher.\n router.all('/{*path}', async (req: Request, res: Response) => {\n try {\n const subPath = '/' + (req.params as any).path;\n const method = req.method;\n const body = (method === 'POST' || method === 'PUT' || method === 'PATCH') ? req.body : undefined;\n const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n return router;\n}\n\n/**\n * Middleware that attaches the ObjectStack kernel to the request.\n */\nexport function objectStackMiddleware(kernel: ObjectKernel) {\n return (req: Request, _res: Response, next: NextFunction) => {\n (req as any).objectStack = kernel;\n next();\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { type Router, type Request, type Response, type NextFunction, Router as createRouter } from 'express';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface ExpressAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: globalThis.Request): Promise<globalThis.Response>;\n}\n\n/**\n * Creates an Express Router with all ObjectStack route dispatchers.\n *\n * Only routes that need framework-specific handling (auth service, storage\n * file upload, GraphQL raw result, discovery wrapper) are registered explicitly.\n * All other routes are handled by a catch-all that delegates to\n * `HttpDispatcher.dispatch()`, making the adapter automatically support\n * new routes added to the dispatcher.\n *\n * @example\n * ```ts\n * import express from 'express';\n * import { createExpressRouter } from '@objectstack/express';\n *\n * const app = express();\n * app.use('/api', createExpressRouter({ kernel }));\n * app.listen(3000);\n * ```\n */\nexport function createExpressRouter(options: ExpressAdapterOptions): Router {\n const router = createRouter();\n const dispatcher = new HttpDispatcher(options.kernel);\n const prefix = options.prefix || '/api';\n\n const sendResult = (result: HttpDispatcherResult, res: Response) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) => res.set(k, v as string));\n }\n return res.status(result.response.status).json(result.response.body);\n }\n if (result.result) {\n const response = result.result;\n if (response.type === 'redirect' && response.url) {\n return res.redirect(response.url);\n }\n if (response.type === 'stream' && response.stream) {\n if (response.headers) {\n Object.entries(response.headers).forEach(([k, v]) => res.set(k, v as string));\n }\n response.stream.pipe(res);\n return;\n }\n return res.status(200).json(response);\n }\n }\n return res.status(404).json({ success: false, error: { message: 'Not Found', code: 404 } });\n };\n\n const errorResponse = (err: any, res: Response) => {\n return res.status(err.statusCode || 500).json({\n success: false,\n error: {\n message: err.message || 'Internal Server Error',\n code: err.statusCode || 500,\n },\n });\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n router.get('/discovery', async (_req: Request, res: Response) => {\n res.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- Auth (needs auth service integration) ---\n router.all('/auth/{*path}', async (req: Request, res: Response) => {\n try {\n const path = (req.params as any).path;\n const method = req.method;\n\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\n\n if (authService && typeof authService.handleRequest === 'function') {\n const protocol = req.protocol || 'http';\n const host = req.get?.('host') || req.headers?.host || 'localhost';\n const url = `${protocol}://${host}${req.originalUrl || req.url}`;\n const headers = new Headers();\n if (req.headers) {\n Object.entries(req.headers).forEach(([k, v]) => {\n if (typeof v === 'string') headers.set(k, v);\n else if (Array.isArray(v)) headers.set(k, v.join(', '));\n });\n }\n const init: RequestInit = { method, headers };\n if (method !== 'GET' && method !== 'HEAD' && req.body) {\n init.body = JSON.stringify(req.body);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n }\n const webRequest = new Request(url, init);\n const response = await authService.handleRequest(webRequest);\n res.status(response.status);\n response.headers.forEach((v: string, k: string) => res.set(k, v));\n const text = await response.text();\n return res.send(text);\n }\n\n // Fallback to dispatcher\n const body = method === 'GET' || method === 'HEAD' ? {} : req.body || {};\n const result = await dispatcher.handleAuth(path, method, body, { request: req, response: res });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\n router.post('/graphql', async (req: Request, res: Response) => {\n try {\n const result = await dispatcher.handleGraphQL(req.body, { request: req });\n return res.json(result);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- Storage (needs file upload handling) ---\n router.all('/storage/{*path}', async (req: Request, res: Response) => {\n try {\n const subPath = '/' + (req.params as any).path;\n const method = req.method;\n const file = (req as any).file || (req as any).files?.file;\n const result = await dispatcher.handleStorage(subPath, method, file, { request: req });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────\n // Handles meta, data, packages, analytics, automation, i18n, ui, openapi,\n // custom API endpoints, and any future routes added to HttpDispatcher.\n router.all('/{*path}', async (req: Request, res: Response) => {\n try {\n const subPath = '/' + (req.params as any).path;\n const method = req.method;\n const body = (method === 'POST' || method === 'PUT' || method === 'PATCH') ? req.body : undefined;\n const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res }, prefix);\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n return router;\n}\n\n/**\n * Middleware that attaches the ObjectStack kernel to the request.\n */\nexport function objectStackMiddleware(kernel: ObjectKernel) {\n return (req: Request, _res: Response, next: NextFunction) => {\n (req as any).objectStack = kernel;\n next();\n };\n}\n"],"mappings":";AAEA,SAAsE,UAAU,oBAAoB;AACpG,SAA4B,sBAA4C;AAiCjE,SAAS,oBAAoB,SAAwC;AAC1E,QAAM,SAAS,aAAa;AAC5B,QAAM,aAAa,IAAI,eAAe,QAAQ,MAAM;AACpD,QAAM,SAAS,QAAQ,UAAU;AAEjC,QAAM,aAAa,CAAC,QAA8B,QAAkB;AAClE,QAAI,OAAO,SAAS;AAClB,UAAI,OAAO,UAAU;AACnB,YAAI,OAAO,SAAS,SAAS;AAC3B,iBAAO,QAAQ,OAAO,SAAS,OAAO,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,GAAG,CAAW,CAAC;AAAA,QACrF;AACA,eAAO,IAAI,OAAO,OAAO,SAAS,MAAM,EAAE,KAAK,OAAO,SAAS,IAAI;AAAA,MACrE;AACA,UAAI,OAAO,QAAQ;AACjB,cAAM,WAAW,OAAO;AACxB,YAAI,SAAS,SAAS,cAAc,SAAS,KAAK;AAChD,iBAAO,IAAI,SAAS,SAAS,GAAG;AAAA,QAClC;AACA,YAAI,SAAS,SAAS,YAAY,SAAS,QAAQ;AACjD,cAAI,SAAS,SAAS;AACpB,mBAAO,QAAQ,SAAS,OAAO,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,GAAG,CAAW,CAAC;AAAA,UAC9E;AACA,mBAAS,OAAO,KAAK,GAAG;AACxB;AAAA,QACF;AACA,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,QAAQ;AAAA,MACtC;AAAA,IACF;AACA,WAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,SAAS,aAAa,MAAM,IAAI,EAAE,CAAC;AAAA,EAC5F;AAEA,QAAM,gBAAgB,CAAC,KAAU,QAAkB;AACjD,WAAO,IAAI,OAAO,IAAI,cAAc,GAAG,EAAE,KAAK;AAAA,MAC5C,SAAS;AAAA,MACT,OAAO;AAAA,QACL,SAAS,IAAI,WAAW;AAAA,QACxB,MAAM,IAAI,cAAc;AAAA,MAC1B;AAAA,IACF,CAAC;AAAA,EACH;AAKA,SAAO,IAAI,cAAc,OAAO,MAAe,QAAkB;AAC/D,QAAI,KAAK,EAAE,MAAM,MAAM,WAAW,iBAAiB,MAAM,EAAE,CAAC;AAAA,EAC9D,CAAC;AAGD,SAAO,IAAI,iBAAiB,OAAO,KAAc,QAAkB;AACjE,QAAI;AACF,YAAM,OAAQ,IAAI,OAAe;AACjC,YAAM,SAAS,IAAI;AAGnB,UAAI,cAAkC;AACtC,UAAI;AACF,YAAI,OAAO,QAAQ,OAAO,oBAAoB,YAAY;AACxD,wBAAc,MAAM,QAAQ,OAAO,gBAA6B,MAAM;AAAA,QACxE,WAAW,OAAO,QAAQ,OAAO,eAAe,YAAY;AAC1D,wBAAc,QAAQ,OAAO,WAAwB,MAAM;AAAA,QAC7D;AAAA,MACF,QAAQ;AAEN,sBAAc;AAAA,MAChB;AAEA,UAAI,eAAe,OAAO,YAAY,kBAAkB,YAAY;AAClE,cAAM,WAAW,IAAI,YAAY;AACjC,cAAM,OAAO,IAAI,MAAM,MAAM,KAAK,IAAI,SAAS,QAAQ;AACvD,cAAM,MAAM,GAAG,QAAQ,MAAM,IAAI,GAAG,IAAI,eAAe,IAAI,GAAG;AAC9D,cAAM,UAAU,IAAI,QAAQ;AAC5B,YAAI,IAAI,SAAS;AACf,iBAAO,QAAQ,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM;AAC9C,gBAAI,OAAO,MAAM,SAAU,SAAQ,IAAI,GAAG,CAAC;AAAA,qBAClC,MAAM,QAAQ,CAAC,EAAG,SAAQ,IAAI,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA,UACxD,CAAC;AAAA,QACH;AACA,cAAM,OAAoB,EAAE,QAAQ,QAAQ;AAC5C,YAAI,WAAW,SAAS,WAAW,UAAU,IAAI,MAAM;AACrD,eAAK,OAAO,KAAK,UAAU,IAAI,IAAI;AACnC,cAAI,CAAC,QAAQ,IAAI,cAAc,GAAG;AAChC,oBAAQ,IAAI,gBAAgB,kBAAkB;AAAA,UAChD;AAAA,QACF;AACA,cAAM,aAAa,IAAI,QAAQ,KAAK,IAAI;AACxC,cAAM,WAAW,MAAM,YAAY,cAAc,UAAU;AAC3D,YAAI,OAAO,SAAS,MAAM;AAC1B,iBAAS,QAAQ,QAAQ,CAAC,GAAW,MAAc,IAAI,IAAI,GAAG,CAAC,CAAC;AAChE,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,eAAO,IAAI,KAAK,IAAI;AAAA,MACtB;AAGA,YAAM,OAAO,WAAW,SAAS,WAAW,SAAS,CAAC,IAAI,IAAI,QAAQ,CAAC;AACvE,YAAM,SAAS,MAAM,WAAW,WAAW,MAAM,QAAQ,MAAM,EAAE,SAAS,KAAK,UAAU,IAAI,CAAC;AAC9F,aAAO,WAAW,QAAQ,GAAG;AAAA,IAC/B,SAAS,KAAU;AACjB,aAAO,cAAc,KAAK,GAAG;AAAA,IAC/B;AAAA,EACF,CAAC;AAGD,SAAO,KAAK,YAAY,OAAO,KAAc,QAAkB;AAC7D,QAAI;AACF,YAAM,SAAS,MAAM,WAAW,cAAc,IAAI,MAAM,EAAE,SAAS,IAAI,CAAC;AACxE,aAAO,IAAI,KAAK,MAAM;AAAA,IACxB,SAAS,KAAU;AACjB,aAAO,cAAc,KAAK,GAAG;AAAA,IAC/B;AAAA,EACF,CAAC;AAGD,SAAO,IAAI,oBAAoB,OAAO,KAAc,QAAkB;AACpE,QAAI;AACF,YAAM,UAAU,MAAO,IAAI,OAAe;AAC1C,YAAM,SAAS,IAAI;AACnB,YAAM,OAAQ,IAAY,QAAS,IAAY,OAAO;AACtD,YAAM,SAAS,MAAM,WAAW,cAAc,SAAS,QAAQ,MAAM,EAAE,SAAS,IAAI,CAAC;AACrF,aAAO,WAAW,QAAQ,GAAG;AAAA,IAC/B,SAAS,KAAU;AACjB,aAAO,cAAc,KAAK,GAAG;AAAA,IAC/B;AAAA,EACF,CAAC;AAKD,SAAO,IAAI,YAAY,OAAO,KAAc,QAAkB;AAC5D,QAAI;AACF,YAAM,UAAU,MAAO,IAAI,OAAe;AAC1C,YAAM,SAAS,IAAI;AACnB,YAAM,OAAQ,WAAW,UAAU,WAAW,SAAS,WAAW,UAAW,IAAI,OAAO;AACxF,YAAM,SAAS,MAAM,WAAW,SAAS,QAAQ,SAAS,MAAM,IAAI,OAAO,EAAE,SAAS,KAAK,UAAU,IAAI,GAAG,MAAM;AAClH,aAAO,WAAW,QAAQ,GAAG;AAAA,IAC/B,SAAS,KAAU;AACjB,aAAO,cAAc,KAAK,GAAG;AAAA,IAC/B;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAKO,SAAS,sBAAsB,QAAsB;AAC1D,SAAO,CAAC,KAAc,MAAgB,SAAuB;AAC3D,IAAC,IAAY,cAAc;AAC3B,SAAK;AAAA,EACP;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/express",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,14 +13,14 @@
13
13
  },
14
14
  "peerDependencies": {
15
15
  "express": "^5.1.0",
16
- "@objectstack/runtime": "^4.0.1"
16
+ "@objectstack/runtime": "^4.0.3"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/express": "^5.0.6",
20
20
  "express": "^5.2.1",
21
21
  "typescript": "^6.0.2",
22
- "vitest": "^4.1.2",
23
- "@objectstack/runtime": "4.0.1"
22
+ "vitest": "^4.1.4",
23
+ "@objectstack/runtime": "4.0.3"
24
24
  },
25
25
  "scripts": {
26
26
  "build": "tsup --config ../../../tsup.config.ts",
package/src/index.ts CHANGED
@@ -167,7 +167,7 @@ export function createExpressRouter(options: ExpressAdapterOptions): Router {
167
167
  const subPath = '/' + (req.params as any).path;
168
168
  const method = req.method;
169
169
  const body = (method === 'POST' || method === 'PUT' || method === 'PATCH') ? req.body : undefined;
170
- const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res });
170
+ const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res }, prefix);
171
171
  return sendResult(result, res);
172
172
  } catch (err: any) {
173
173
  return errorResponse(err, res);