@objectstack/express 3.2.7 → 3.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +19 -0
- package/dist/index.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/express.test.ts +1 -2
- package/src/index.ts +20 -35
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/express@3.2.
|
|
2
|
+
> @objectstack/express@3.2.9 build /home/runner/work/spec/spec/packages/adapters/express
|
|
3
3
|
> tsup --config ../../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m5.78 KB[39m
|
|
14
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m10.62 KB[39m
|
|
15
|
+
[32mCJS[39m ⚡️ Build success in 57ms
|
|
16
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m4.71 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m10.57 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 58ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 12433ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.18 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.18 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @objectstack/express
|
|
2
2
|
|
|
3
|
+
## 3.2.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @objectstack/runtime@3.2.9
|
|
8
|
+
|
|
9
|
+
## 3.2.8
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- @objectstack/runtime@3.2.8
|
|
14
|
+
|
|
15
|
+
## 3.2.8
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- fix: unified catch-all dispatch pattern — `createExpressRouter()` now delegates all non-framework-specific routes to `HttpDispatcher.dispatch()`, automatically supporting packages, analytics, automation, i18n, ui, openapi, custom endpoints, and any future routes
|
|
20
|
+
- Only auth (service check), storage (file upload), GraphQL (raw result), and discovery (response wrapper) remain as explicit routes
|
|
21
|
+
|
|
3
22
|
## 3.2.7
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -7,7 +7,12 @@ interface ExpressAdapterOptions {
|
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Creates an Express Router with all ObjectStack route dispatchers.
|
|
10
|
-
*
|
|
10
|
+
*
|
|
11
|
+
* Only routes that need framework-specific handling (auth service, storage
|
|
12
|
+
* file upload, GraphQL raw result, discovery wrapper) are registered explicitly.
|
|
13
|
+
* All other routes are handled by a catch-all that delegates to
|
|
14
|
+
* `HttpDispatcher.dispatch()`, making the adapter automatically support
|
|
15
|
+
* new routes added to the dispatcher.
|
|
11
16
|
*
|
|
12
17
|
* @example
|
|
13
18
|
* ```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,12 @@ interface ExpressAdapterOptions {
|
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Creates an Express Router with all ObjectStack route dispatchers.
|
|
10
|
-
*
|
|
10
|
+
*
|
|
11
|
+
* Only routes that need framework-specific handling (auth service, storage
|
|
12
|
+
* file upload, GraphQL raw result, discovery wrapper) are registered explicitly.
|
|
13
|
+
* All other routes are handled by a catch-all that delegates to
|
|
14
|
+
* `HttpDispatcher.dispatch()`, making the adapter automatically support
|
|
15
|
+
* new routes added to the dispatcher.
|
|
11
16
|
*
|
|
12
17
|
* @example
|
|
13
18
|
* ```ts
|
package/dist/index.js
CHANGED
|
@@ -121,44 +121,23 @@ function createExpressRouter(options) {
|
|
|
121
121
|
return errorResponse(err, res);
|
|
122
122
|
}
|
|
123
123
|
});
|
|
124
|
-
router.all("/
|
|
125
|
-
try {
|
|
126
|
-
const subPath = "/" + req.params.path;
|
|
127
|
-
const method = req.method;
|
|
128
|
-
const body = method === "PUT" || method === "POST" ? req.body : void 0;
|
|
129
|
-
const result = await dispatcher.handleMetadata(subPath, { request: req }, method, body);
|
|
130
|
-
return sendResult(result, res);
|
|
131
|
-
} catch (err) {
|
|
132
|
-
return errorResponse(err, res);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
router.all("/meta", async (req, res) => {
|
|
136
|
-
try {
|
|
137
|
-
const method = req.method;
|
|
138
|
-
const body = method === "PUT" || method === "POST" ? req.body : void 0;
|
|
139
|
-
const result = await dispatcher.handleMetadata("", { request: req }, method, body);
|
|
140
|
-
return sendResult(result, res);
|
|
141
|
-
} catch (err) {
|
|
142
|
-
return errorResponse(err, res);
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
router.all("/data/{*path}", async (req, res) => {
|
|
124
|
+
router.all("/storage/{*path}", async (req, res) => {
|
|
146
125
|
try {
|
|
147
126
|
const subPath = "/" + req.params.path;
|
|
148
127
|
const method = req.method;
|
|
149
|
-
const
|
|
150
|
-
const result = await dispatcher.
|
|
128
|
+
const file = req.file || req.files?.file;
|
|
129
|
+
const result = await dispatcher.handleStorage(subPath, method, file, { request: req });
|
|
151
130
|
return sendResult(result, res);
|
|
152
131
|
} catch (err) {
|
|
153
132
|
return errorResponse(err, res);
|
|
154
133
|
}
|
|
155
134
|
});
|
|
156
|
-
router.all("/
|
|
135
|
+
router.all("/{*path}", async (req, res) => {
|
|
157
136
|
try {
|
|
158
137
|
const subPath = "/" + req.params.path;
|
|
159
138
|
const method = req.method;
|
|
160
|
-
const
|
|
161
|
-
const result = await dispatcher.
|
|
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 });
|
|
162
141
|
return sendResult(result, res);
|
|
163
142
|
} catch (err) {
|
|
164
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 * Provides Auth, GraphQL, Metadata, Data, and Storage routes.\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 // --- Discovery ---\n router.get('/', async (_req: Request, res: Response) => {\n res.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- Auth ---\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 ---\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 // --- Metadata ---\n router.all('/meta/{*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 === 'PUT' || method === 'POST') ? req.body : undefined;\n const result = await dispatcher.handleMetadata(subPath, { request: req }, method, body);\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n router.all('/meta', async (req: Request, res: Response) => {\n try {\n const method = req.method;\n const body = (method === 'PUT' || method === 'POST') ? req.body : undefined;\n const result = await dispatcher.handleMetadata('', { request: req }, method, body);\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- Data ---\n router.all('/data/{*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 === 'PATCH') ? req.body : {};\n const result = await dispatcher.handleData(subPath, method, body, req.query, { request: req });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- Storage ---\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 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('/', 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"]}
|
package/dist/index.mjs
CHANGED
|
@@ -96,44 +96,23 @@ function createExpressRouter(options) {
|
|
|
96
96
|
return errorResponse(err, res);
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
|
-
router.all("/
|
|
100
|
-
try {
|
|
101
|
-
const subPath = "/" + req.params.path;
|
|
102
|
-
const method = req.method;
|
|
103
|
-
const body = method === "PUT" || method === "POST" ? req.body : void 0;
|
|
104
|
-
const result = await dispatcher.handleMetadata(subPath, { request: req }, method, body);
|
|
105
|
-
return sendResult(result, res);
|
|
106
|
-
} catch (err) {
|
|
107
|
-
return errorResponse(err, res);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
router.all("/meta", async (req, res) => {
|
|
111
|
-
try {
|
|
112
|
-
const method = req.method;
|
|
113
|
-
const body = method === "PUT" || method === "POST" ? req.body : void 0;
|
|
114
|
-
const result = await dispatcher.handleMetadata("", { request: req }, method, body);
|
|
115
|
-
return sendResult(result, res);
|
|
116
|
-
} catch (err) {
|
|
117
|
-
return errorResponse(err, res);
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
router.all("/data/{*path}", async (req, res) => {
|
|
99
|
+
router.all("/storage/{*path}", async (req, res) => {
|
|
121
100
|
try {
|
|
122
101
|
const subPath = "/" + req.params.path;
|
|
123
102
|
const method = req.method;
|
|
124
|
-
const
|
|
125
|
-
const result = await dispatcher.
|
|
103
|
+
const file = req.file || req.files?.file;
|
|
104
|
+
const result = await dispatcher.handleStorage(subPath, method, file, { request: req });
|
|
126
105
|
return sendResult(result, res);
|
|
127
106
|
} catch (err) {
|
|
128
107
|
return errorResponse(err, res);
|
|
129
108
|
}
|
|
130
109
|
});
|
|
131
|
-
router.all("/
|
|
110
|
+
router.all("/{*path}", async (req, res) => {
|
|
132
111
|
try {
|
|
133
112
|
const subPath = "/" + req.params.path;
|
|
134
113
|
const method = req.method;
|
|
135
|
-
const
|
|
136
|
-
const result = await dispatcher.
|
|
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 });
|
|
137
116
|
return sendResult(result, res);
|
|
138
117
|
} catch (err) {
|
|
139
118
|
return errorResponse(err, res);
|
package/dist/index.mjs.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 * Provides Auth, GraphQL, Metadata, Data, and Storage routes.\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 // --- Discovery ---\n router.get('/', async (_req: Request, res: Response) => {\n res.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- Auth ---\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 ---\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 // --- Metadata ---\n router.all('/meta/{*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 === 'PUT' || method === 'POST') ? req.body : undefined;\n const result = await dispatcher.handleMetadata(subPath, { request: req }, method, body);\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n router.all('/meta', async (req: Request, res: Response) => {\n try {\n const method = req.method;\n const body = (method === 'PUT' || method === 'POST') ? req.body : undefined;\n const result = await dispatcher.handleMetadata('', { request: req }, method, body);\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- Data ---\n router.all('/data/{*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 === 'PATCH') ? req.body : {};\n const result = await dispatcher.handleData(subPath, method, body, req.query, { request: req });\n return sendResult(result, res);\n } catch (err: any) {\n return errorResponse(err, res);\n }\n });\n\n // --- Storage ---\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 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('/', 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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/express",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.9",
|
|
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": "^3.2.
|
|
16
|
+
"@objectstack/runtime": "^3.2.9"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/express": "^5.0.3",
|
|
20
20
|
"express": "^5.1.0",
|
|
21
21
|
"typescript": "^5.0.0",
|
|
22
|
-
"vitest": "^4.0
|
|
23
|
-
"@objectstack/runtime": "3.2.
|
|
22
|
+
"vitest": "^4.1.0",
|
|
23
|
+
"@objectstack/runtime": "3.2.9"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsup --config ../../../tsup.config.ts",
|
package/src/express.test.ts
CHANGED
|
@@ -7,9 +7,8 @@ const mockDispatcher = {
|
|
|
7
7
|
getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }),
|
|
8
8
|
handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }),
|
|
9
9
|
handleGraphQL: vi.fn().mockResolvedValue({ data: {} }),
|
|
10
|
-
handleMetadata: vi.fn().mockResolvedValue({ handled: true, response: { body: { objects: [] }, status: 200 } }),
|
|
11
|
-
handleData: vi.fn().mockResolvedValue({ handled: true, response: { body: { records: [] }, status: 200 } }),
|
|
12
10
|
handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }),
|
|
11
|
+
dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }),
|
|
13
12
|
};
|
|
14
13
|
|
|
15
14
|
vi.mock('@objectstack/runtime', () => {
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,12 @@ interface AuthService {
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Creates an Express Router with all ObjectStack route dispatchers.
|
|
20
|
-
*
|
|
20
|
+
*
|
|
21
|
+
* Only routes that need framework-specific handling (auth service, storage
|
|
22
|
+
* file upload, GraphQL raw result, discovery wrapper) are registered explicitly.
|
|
23
|
+
* All other routes are handled by a catch-all that delegates to
|
|
24
|
+
* `HttpDispatcher.dispatch()`, making the adapter automatically support
|
|
25
|
+
* new routes added to the dispatcher.
|
|
21
26
|
*
|
|
22
27
|
* @example
|
|
23
28
|
* ```ts
|
|
@@ -70,12 +75,14 @@ export function createExpressRouter(options: ExpressAdapterOptions): Router {
|
|
|
70
75
|
});
|
|
71
76
|
};
|
|
72
77
|
|
|
78
|
+
// ─── Explicit routes (framework-specific handling required) ────────────────
|
|
79
|
+
|
|
73
80
|
// --- Discovery ---
|
|
74
81
|
router.get('/', async (_req: Request, res: Response) => {
|
|
75
82
|
res.json({ data: await dispatcher.getDiscoveryInfo(prefix) });
|
|
76
83
|
});
|
|
77
84
|
|
|
78
|
-
// --- Auth ---
|
|
85
|
+
// --- Auth (needs auth service integration) ---
|
|
79
86
|
router.all('/auth/{*path}', async (req: Request, res: Response) => {
|
|
80
87
|
try {
|
|
81
88
|
const path = (req.params as any).path;
|
|
@@ -129,7 +136,7 @@ export function createExpressRouter(options: ExpressAdapterOptions): Router {
|
|
|
129
136
|
}
|
|
130
137
|
});
|
|
131
138
|
|
|
132
|
-
// --- GraphQL ---
|
|
139
|
+
// --- GraphQL (returns raw result, not HttpDispatcherResult) ---
|
|
133
140
|
router.post('/graphql', async (req: Request, res: Response) => {
|
|
134
141
|
try {
|
|
135
142
|
const result = await dispatcher.handleGraphQL(req.body, { request: req });
|
|
@@ -139,50 +146,28 @@ export function createExpressRouter(options: ExpressAdapterOptions): Router {
|
|
|
139
146
|
}
|
|
140
147
|
});
|
|
141
148
|
|
|
142
|
-
// ---
|
|
143
|
-
router.all('/
|
|
144
|
-
try {
|
|
145
|
-
const subPath = '/' + (req.params as any).path;
|
|
146
|
-
const method = req.method;
|
|
147
|
-
const body = (method === 'PUT' || method === 'POST') ? req.body : undefined;
|
|
148
|
-
const result = await dispatcher.handleMetadata(subPath, { request: req }, method, body);
|
|
149
|
-
return sendResult(result, res);
|
|
150
|
-
} catch (err: any) {
|
|
151
|
-
return errorResponse(err, res);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
router.all('/meta', async (req: Request, res: Response) => {
|
|
156
|
-
try {
|
|
157
|
-
const method = req.method;
|
|
158
|
-
const body = (method === 'PUT' || method === 'POST') ? req.body : undefined;
|
|
159
|
-
const result = await dispatcher.handleMetadata('', { request: req }, method, body);
|
|
160
|
-
return sendResult(result, res);
|
|
161
|
-
} catch (err: any) {
|
|
162
|
-
return errorResponse(err, res);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// --- Data ---
|
|
167
|
-
router.all('/data/{*path}', async (req: Request, res: Response) => {
|
|
149
|
+
// --- Storage (needs file upload handling) ---
|
|
150
|
+
router.all('/storage/{*path}', async (req: Request, res: Response) => {
|
|
168
151
|
try {
|
|
169
152
|
const subPath = '/' + (req.params as any).path;
|
|
170
153
|
const method = req.method;
|
|
171
|
-
const
|
|
172
|
-
const result = await dispatcher.
|
|
154
|
+
const file = (req as any).file || (req as any).files?.file;
|
|
155
|
+
const result = await dispatcher.handleStorage(subPath, method, file, { request: req });
|
|
173
156
|
return sendResult(result, res);
|
|
174
157
|
} catch (err: any) {
|
|
175
158
|
return errorResponse(err, res);
|
|
176
159
|
}
|
|
177
160
|
});
|
|
178
161
|
|
|
179
|
-
//
|
|
180
|
-
|
|
162
|
+
// ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────
|
|
163
|
+
// Handles meta, data, packages, analytics, automation, i18n, ui, openapi,
|
|
164
|
+
// custom API endpoints, and any future routes added to HttpDispatcher.
|
|
165
|
+
router.all('/{*path}', async (req: Request, res: Response) => {
|
|
181
166
|
try {
|
|
182
167
|
const subPath = '/' + (req.params as any).path;
|
|
183
168
|
const method = req.method;
|
|
184
|
-
const
|
|
185
|
-
const result = await dispatcher.
|
|
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 });
|
|
186
171
|
return sendResult(result, res);
|
|
187
172
|
} catch (err: any) {
|
|
188
173
|
return errorResponse(err, res);
|