@objectstack/nuxt 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 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +8 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +23 -48
- package/src/nuxt.test.ts +16 -7
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/nuxt@3.2.
|
|
2
|
+
> @objectstack/nuxt@3.2.9 build /home/runner/work/spec/spec/packages/adapters/nuxt
|
|
3
3
|
> tsup --config ../../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
14
|
-
[32mCJS[39m [1mdist/index.js.map [22m[
|
|
15
|
-
[32mCJS[39m ⚡️ Build success in
|
|
16
|
-
[32mESM[39m [1mdist/index.mjs [22m[
|
|
17
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[
|
|
18
|
-
[32mESM[39m ⚡️ Build success in
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m6.90 KB[39m
|
|
14
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m12.03 KB[39m
|
|
15
|
+
[32mCJS[39m ⚡️ Build success in 52ms
|
|
16
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m5.71 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m12.06 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 61ms
|
|
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 12001ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m1015.00 B[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m1015.00 B[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @objectstack/nuxt
|
|
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 — `createH3Router()` 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 (multipart), 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
|
@@ -9,6 +9,12 @@ interface NuxtAdapterOptions {
|
|
|
9
9
|
* Creates an h3 router with all ObjectStack route dispatchers.
|
|
10
10
|
* Designed for use in Nuxt server routes or standalone h3 apps.
|
|
11
11
|
*
|
|
12
|
+
* Only routes that need framework-specific handling (auth service, storage
|
|
13
|
+
* file upload, GraphQL raw result, discovery wrapper) are registered explicitly.
|
|
14
|
+
* All other routes are handled by a catch-all that delegates to
|
|
15
|
+
* `HttpDispatcher.dispatch()`, making the adapter automatically support
|
|
16
|
+
* new routes added to the dispatcher.
|
|
17
|
+
*
|
|
12
18
|
* @example
|
|
13
19
|
* ```ts
|
|
14
20
|
* // server/api/[...].ts
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ interface NuxtAdapterOptions {
|
|
|
9
9
|
* Creates an h3 router with all ObjectStack route dispatchers.
|
|
10
10
|
* Designed for use in Nuxt server routes or standalone h3 apps.
|
|
11
11
|
*
|
|
12
|
+
* Only routes that need framework-specific handling (auth service, storage
|
|
13
|
+
* file upload, GraphQL raw result, discovery wrapper) are registered explicitly.
|
|
14
|
+
* All other routes are handled by a catch-all that delegates to
|
|
15
|
+
* `HttpDispatcher.dispatch()`, making the adapter automatically support
|
|
16
|
+
* new routes added to the dispatcher.
|
|
17
|
+
*
|
|
12
18
|
* @example
|
|
13
19
|
* ```ts
|
|
14
20
|
* // server/api/[...].ts
|
package/dist/index.js
CHANGED
|
@@ -139,27 +139,14 @@ function createH3Router(options) {
|
|
|
139
139
|
})
|
|
140
140
|
);
|
|
141
141
|
router.use(
|
|
142
|
-
`${prefix}/
|
|
142
|
+
`${prefix}/storage/**`,
|
|
143
143
|
(0, import_h3.defineEventHandler)(async (event) => {
|
|
144
144
|
try {
|
|
145
145
|
const urlPath = (event.path || event.node.req.url || "").split("?")[0];
|
|
146
|
-
const subPath = urlPath.substring(`${prefix}/
|
|
147
|
-
const method = event.method;
|
|
148
|
-
const body = method === "PUT" || method === "POST" ? await (0, import_h3.readBody)(event) : void 0;
|
|
149
|
-
const result = await dispatcher.handleMetadata(subPath, { request: event.node.req }, method, body);
|
|
150
|
-
return toResponse(event, result);
|
|
151
|
-
} catch (err) {
|
|
152
|
-
return errorJson(event, err.message || "Internal Server Error", err.statusCode || 500);
|
|
153
|
-
}
|
|
154
|
-
})
|
|
155
|
-
);
|
|
156
|
-
router.use(
|
|
157
|
-
`${prefix}/meta`,
|
|
158
|
-
(0, import_h3.defineEventHandler)(async (event) => {
|
|
159
|
-
try {
|
|
146
|
+
const subPath = urlPath.substring(`${prefix}/storage`.length);
|
|
160
147
|
const method = event.method;
|
|
161
|
-
const
|
|
162
|
-
const result = await dispatcher.
|
|
148
|
+
const file = void 0;
|
|
149
|
+
const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });
|
|
163
150
|
return toResponse(event, result);
|
|
164
151
|
} catch (err) {
|
|
165
152
|
return errorJson(event, err.message || "Internal Server Error", err.statusCode || 500);
|
|
@@ -167,30 +154,15 @@ function createH3Router(options) {
|
|
|
167
154
|
})
|
|
168
155
|
);
|
|
169
156
|
router.use(
|
|
170
|
-
`${prefix}
|
|
157
|
+
`${prefix}/**`,
|
|
171
158
|
(0, import_h3.defineEventHandler)(async (event) => {
|
|
172
159
|
try {
|
|
173
160
|
const urlPath = (event.path || event.node.req.url || "").split("?")[0];
|
|
174
|
-
const subPath = urlPath.substring(
|
|
161
|
+
const subPath = urlPath.substring(prefix.length);
|
|
175
162
|
const method = event.method;
|
|
176
|
-
const body = method === "POST" || method === "PATCH" ? await (0, import_h3.readBody)(event) :
|
|
163
|
+
const body = method === "POST" || method === "PUT" || method === "PATCH" ? await (0, import_h3.readBody)(event) : void 0;
|
|
177
164
|
const query = (0, import_h3.getQuery)(event);
|
|
178
|
-
const result = await dispatcher.
|
|
179
|
-
return toResponse(event, result);
|
|
180
|
-
} catch (err) {
|
|
181
|
-
return errorJson(event, err.message || "Internal Server Error", err.statusCode || 500);
|
|
182
|
-
}
|
|
183
|
-
})
|
|
184
|
-
);
|
|
185
|
-
router.use(
|
|
186
|
-
`${prefix}/storage/**`,
|
|
187
|
-
(0, import_h3.defineEventHandler)(async (event) => {
|
|
188
|
-
try {
|
|
189
|
-
const urlPath = (event.path || event.node.req.url || "").split("?")[0];
|
|
190
|
-
const subPath = urlPath.substring(`${prefix}/storage`.length);
|
|
191
|
-
const method = event.method;
|
|
192
|
-
const file = void 0;
|
|
193
|
-
const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });
|
|
165
|
+
const result = await dispatcher.dispatch(method, subPath, body, query, { request: event.node.req });
|
|
194
166
|
return toResponse(event, result);
|
|
195
167
|
} catch (err) {
|
|
196
168
|
return errorJson(event, err.message || "Internal Server Error", err.statusCode || 500);
|
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 {\n createRouter,\n defineEventHandler,\n getQuery,\n readBody,\n sendRedirect,\n setResponseHeader,\n setResponseStatus,\n type H3Event,\n type Router,\n} from 'h3';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface NuxtAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\n}\n\n/**\n * Creates an h3 router with all ObjectStack route dispatchers.\n * Designed for use in Nuxt server routes or standalone h3 apps.\n *\n * @example\n * ```ts\n * // server/api/[...].ts\n * import { createH3Router } from '@objectstack/nuxt';\n * import { kernel } from '../kernel';\n *\n * const router = createH3Router({ kernel });\n * export default defineEventHandler(router.handler);\n * ```\n */\nexport function createH3Router(options: NuxtAdapterOptions): Router {\n const router = createRouter();\n const dispatcher = new HttpDispatcher(options.kernel);\n const prefix = options.prefix || '/api';\n\n const errorJson = (event: H3Event, message: string, code: number = 500) => {\n setResponseStatus(event, code);\n return { success: false, error: { message, code } };\n };\n\n const toResponse = (event: H3Event, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) =>\n setResponseHeader(event, k, v as string),\n );\n }\n setResponseStatus(event, result.response.status);\n return result.response.body;\n }\n if (result.result) {\n const res = result.result;\n if (res.type === 'redirect' && res.url) {\n return sendRedirect(event, res.url);\n }\n if (res.type === 'stream' && res.stream) {\n if (res.headers) {\n Object.entries(res.headers).forEach(([k, v]) =>\n setResponseHeader(event, k, v as string),\n );\n }\n return res.stream;\n }\n return res;\n }\n }\n return errorJson(event, 'Not Found', 404);\n };\n\n // --- Discovery ---\n router.get(\n `${prefix}`,\n defineEventHandler(async () => {\n return { data: await dispatcher.getDiscoveryInfo(prefix) };\n }),\n );\n\n // --- .well-known ---\n router.get(\n '/.well-known/objectstack',\n defineEventHandler((event) => {\n return sendRedirect(event, prefix);\n }),\n );\n\n // --- Auth ---\n router.use(\n `${prefix}/auth/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = event.path || event.node.req.url || '';\n const path = urlPath.substring(`${prefix}/auth/`.length).split('?')[0];\n const method = event.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 host = event.node.req.headers.host || 'localhost';\n const protocol = (event.node.req.socket as any)?.encrypted ? 'https' : 'http';\n const url = `${protocol}://${host}${urlPath}`;\n const headers = new Headers();\n if (event.node.req.headers) {\n Object.entries(event.node.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') {\n const body = await readBody(event);\n if (body) {\n init.body = typeof body === 'string' ? body : JSON.stringify(body);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n }\n }\n const webRequest = new Request(url, init);\n const response = await authService.handleRequest(webRequest);\n setResponseStatus(event, response.status);\n response.headers.forEach((v: string, k: string) => setResponseHeader(event, k, v));\n return await response.text();\n }\n\n // Fallback to dispatcher\n const body = method === 'GET' || method === 'HEAD'\n ? {}\n : await readBody(event).catch(() => ({}));\n const result = await dispatcher.handleAuth(path, method, body, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- GraphQL ---\n router.post(\n `${prefix}/graphql`,\n defineEventHandler(async (event) => {\n try {\n const body = await readBody(event);\n const result = await dispatcher.handleGraphQL(body, { request: event.node.req });\n return result;\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- Metadata ---\n router.use(\n `${prefix}/meta/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(`${prefix}/meta`.length);\n const method = event.method;\n const body = (method === 'PUT' || method === 'POST')\n ? await readBody(event)\n : undefined;\n const result = await dispatcher.handleMetadata(subPath, { request: event.node.req }, method, body);\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n router.use(\n `${prefix}/meta`,\n defineEventHandler(async (event) => {\n try {\n const method = event.method;\n const body = (method === 'PUT' || method === 'POST')\n ? await readBody(event)\n : undefined;\n const result = await dispatcher.handleMetadata('', { request: event.node.req }, method, body);\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- Data ---\n router.use(\n `${prefix}/data/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(`${prefix}/data`.length);\n const method = event.method;\n const body = (method === 'POST' || method === 'PATCH')\n ? await readBody(event)\n : {};\n const query = getQuery(event);\n const result = await dispatcher.handleData(subPath, method, body, query, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- Storage ---\n router.use(\n `${prefix}/storage/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(`${prefix}/storage`.length);\n const method = event.method;\n const file = undefined; // File upload requires multipart parsing (e.g., formidable)\n const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n return router;\n}\n"],"mappings":"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","names":["body"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport {\n createRouter,\n defineEventHandler,\n getQuery,\n readBody,\n sendRedirect,\n setResponseHeader,\n setResponseStatus,\n type H3Event,\n type Router,\n} from 'h3';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface NuxtAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\n}\n\n/**\n * Creates an h3 router with all ObjectStack route dispatchers.\n * Designed for use in Nuxt server routes or standalone h3 apps.\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 * // server/api/[...].ts\n * import { createH3Router } from '@objectstack/nuxt';\n * import { kernel } from '../kernel';\n *\n * const router = createH3Router({ kernel });\n * export default defineEventHandler(router.handler);\n * ```\n */\nexport function createH3Router(options: NuxtAdapterOptions): Router {\n const router = createRouter();\n const dispatcher = new HttpDispatcher(options.kernel);\n const prefix = options.prefix || '/api';\n\n const errorJson = (event: H3Event, message: string, code: number = 500) => {\n setResponseStatus(event, code);\n return { success: false, error: { message, code } };\n };\n\n const toResponse = (event: H3Event, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) =>\n setResponseHeader(event, k, v as string),\n );\n }\n setResponseStatus(event, result.response.status);\n return result.response.body;\n }\n if (result.result) {\n const res = result.result;\n if (res.type === 'redirect' && res.url) {\n return sendRedirect(event, res.url);\n }\n if (res.type === 'stream' && res.stream) {\n if (res.headers) {\n Object.entries(res.headers).forEach(([k, v]) =>\n setResponseHeader(event, k, v as string),\n );\n }\n return res.stream;\n }\n return res;\n }\n }\n return errorJson(event, 'Not Found', 404);\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n router.get(\n `${prefix}`,\n defineEventHandler(async () => {\n return { data: await dispatcher.getDiscoveryInfo(prefix) };\n }),\n );\n\n // --- .well-known ---\n router.get(\n '/.well-known/objectstack',\n defineEventHandler((event) => {\n return sendRedirect(event, prefix);\n }),\n );\n\n // --- Auth (needs auth service integration) ---\n router.use(\n `${prefix}/auth/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = event.path || event.node.req.url || '';\n const path = urlPath.substring(`${prefix}/auth/`.length).split('?')[0];\n const method = event.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 host = event.node.req.headers.host || 'localhost';\n const protocol = (event.node.req.socket as any)?.encrypted ? 'https' : 'http';\n const url = `${protocol}://${host}${urlPath}`;\n const headers = new Headers();\n if (event.node.req.headers) {\n Object.entries(event.node.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') {\n const body = await readBody(event);\n if (body) {\n init.body = typeof body === 'string' ? body : JSON.stringify(body);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n }\n }\n const webRequest = new Request(url, init);\n const response = await authService.handleRequest(webRequest);\n setResponseStatus(event, response.status);\n response.headers.forEach((v: string, k: string) => setResponseHeader(event, k, v));\n return await response.text();\n }\n\n // Fallback to dispatcher\n const body = method === 'GET' || method === 'HEAD'\n ? {}\n : await readBody(event).catch(() => ({}));\n const result = await dispatcher.handleAuth(path, method, body, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\n router.post(\n `${prefix}/graphql`,\n defineEventHandler(async (event) => {\n try {\n const body = await readBody(event);\n const result = await dispatcher.handleGraphQL(body, { request: event.node.req });\n return result;\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- Storage (needs multipart form parsing) ---\n router.use(\n `${prefix}/storage/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(`${prefix}/storage`.length);\n const method = event.method;\n const file = undefined; // File upload requires multipart parsing (e.g., formidable)\n const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\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.use(\n `${prefix}/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(prefix.length);\n const method = event.method;\n const body = (method === 'POST' || method === 'PUT' || method === 'PATCH')\n ? await readBody(event)\n : undefined;\n const query = getQuery(event);\n const result = await dispatcher.dispatch(method, subPath, body, query, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n return router;\n}\n"],"mappings":"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","names":["body"]}
|
package/dist/index.mjs
CHANGED
|
@@ -123,27 +123,14 @@ function createH3Router(options) {
|
|
|
123
123
|
})
|
|
124
124
|
);
|
|
125
125
|
router.use(
|
|
126
|
-
`${prefix}/
|
|
126
|
+
`${prefix}/storage/**`,
|
|
127
127
|
defineEventHandler(async (event) => {
|
|
128
128
|
try {
|
|
129
129
|
const urlPath = (event.path || event.node.req.url || "").split("?")[0];
|
|
130
|
-
const subPath = urlPath.substring(`${prefix}/
|
|
131
|
-
const method = event.method;
|
|
132
|
-
const body = method === "PUT" || method === "POST" ? await readBody(event) : void 0;
|
|
133
|
-
const result = await dispatcher.handleMetadata(subPath, { request: event.node.req }, method, body);
|
|
134
|
-
return toResponse(event, result);
|
|
135
|
-
} catch (err) {
|
|
136
|
-
return errorJson(event, err.message || "Internal Server Error", err.statusCode || 500);
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
);
|
|
140
|
-
router.use(
|
|
141
|
-
`${prefix}/meta`,
|
|
142
|
-
defineEventHandler(async (event) => {
|
|
143
|
-
try {
|
|
130
|
+
const subPath = urlPath.substring(`${prefix}/storage`.length);
|
|
144
131
|
const method = event.method;
|
|
145
|
-
const
|
|
146
|
-
const result = await dispatcher.
|
|
132
|
+
const file = void 0;
|
|
133
|
+
const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });
|
|
147
134
|
return toResponse(event, result);
|
|
148
135
|
} catch (err) {
|
|
149
136
|
return errorJson(event, err.message || "Internal Server Error", err.statusCode || 500);
|
|
@@ -151,30 +138,15 @@ function createH3Router(options) {
|
|
|
151
138
|
})
|
|
152
139
|
);
|
|
153
140
|
router.use(
|
|
154
|
-
`${prefix}
|
|
141
|
+
`${prefix}/**`,
|
|
155
142
|
defineEventHandler(async (event) => {
|
|
156
143
|
try {
|
|
157
144
|
const urlPath = (event.path || event.node.req.url || "").split("?")[0];
|
|
158
|
-
const subPath = urlPath.substring(
|
|
145
|
+
const subPath = urlPath.substring(prefix.length);
|
|
159
146
|
const method = event.method;
|
|
160
|
-
const body = method === "POST" || method === "PATCH" ? await readBody(event) :
|
|
147
|
+
const body = method === "POST" || method === "PUT" || method === "PATCH" ? await readBody(event) : void 0;
|
|
161
148
|
const query = getQuery(event);
|
|
162
|
-
const result = await dispatcher.
|
|
163
|
-
return toResponse(event, result);
|
|
164
|
-
} catch (err) {
|
|
165
|
-
return errorJson(event, err.message || "Internal Server Error", err.statusCode || 500);
|
|
166
|
-
}
|
|
167
|
-
})
|
|
168
|
-
);
|
|
169
|
-
router.use(
|
|
170
|
-
`${prefix}/storage/**`,
|
|
171
|
-
defineEventHandler(async (event) => {
|
|
172
|
-
try {
|
|
173
|
-
const urlPath = (event.path || event.node.req.url || "").split("?")[0];
|
|
174
|
-
const subPath = urlPath.substring(`${prefix}/storage`.length);
|
|
175
|
-
const method = event.method;
|
|
176
|
-
const file = void 0;
|
|
177
|
-
const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });
|
|
149
|
+
const result = await dispatcher.dispatch(method, subPath, body, query, { request: event.node.req });
|
|
178
150
|
return toResponse(event, result);
|
|
179
151
|
} catch (err) {
|
|
180
152
|
return errorJson(event, err.message || "Internal Server Error", err.statusCode || 500);
|
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 {\n createRouter,\n defineEventHandler,\n getQuery,\n readBody,\n sendRedirect,\n setResponseHeader,\n setResponseStatus,\n type H3Event,\n type Router,\n} from 'h3';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface NuxtAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\n}\n\n/**\n * Creates an h3 router with all ObjectStack route dispatchers.\n * Designed for use in Nuxt server routes or standalone h3 apps.\n *\n * @example\n * ```ts\n * // server/api/[...].ts\n * import { createH3Router } from '@objectstack/nuxt';\n * import { kernel } from '../kernel';\n *\n * const router = createH3Router({ kernel });\n * export default defineEventHandler(router.handler);\n * ```\n */\nexport function createH3Router(options: NuxtAdapterOptions): Router {\n const router = createRouter();\n const dispatcher = new HttpDispatcher(options.kernel);\n const prefix = options.prefix || '/api';\n\n const errorJson = (event: H3Event, message: string, code: number = 500) => {\n setResponseStatus(event, code);\n return { success: false, error: { message, code } };\n };\n\n const toResponse = (event: H3Event, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) =>\n setResponseHeader(event, k, v as string),\n );\n }\n setResponseStatus(event, result.response.status);\n return result.response.body;\n }\n if (result.result) {\n const res = result.result;\n if (res.type === 'redirect' && res.url) {\n return sendRedirect(event, res.url);\n }\n if (res.type === 'stream' && res.stream) {\n if (res.headers) {\n Object.entries(res.headers).forEach(([k, v]) =>\n setResponseHeader(event, k, v as string),\n );\n }\n return res.stream;\n }\n return res;\n }\n }\n return errorJson(event, 'Not Found', 404);\n };\n\n // --- Discovery ---\n router.get(\n `${prefix}`,\n defineEventHandler(async () => {\n return { data: await dispatcher.getDiscoveryInfo(prefix) };\n }),\n );\n\n // --- .well-known ---\n router.get(\n '/.well-known/objectstack',\n defineEventHandler((event) => {\n return sendRedirect(event, prefix);\n }),\n );\n\n // --- Auth ---\n router.use(\n `${prefix}/auth/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = event.path || event.node.req.url || '';\n const path = urlPath.substring(`${prefix}/auth/`.length).split('?')[0];\n const method = event.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 host = event.node.req.headers.host || 'localhost';\n const protocol = (event.node.req.socket as any)?.encrypted ? 'https' : 'http';\n const url = `${protocol}://${host}${urlPath}`;\n const headers = new Headers();\n if (event.node.req.headers) {\n Object.entries(event.node.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') {\n const body = await readBody(event);\n if (body) {\n init.body = typeof body === 'string' ? body : JSON.stringify(body);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n }\n }\n const webRequest = new Request(url, init);\n const response = await authService.handleRequest(webRequest);\n setResponseStatus(event, response.status);\n response.headers.forEach((v: string, k: string) => setResponseHeader(event, k, v));\n return await response.text();\n }\n\n // Fallback to dispatcher\n const body = method === 'GET' || method === 'HEAD'\n ? {}\n : await readBody(event).catch(() => ({}));\n const result = await dispatcher.handleAuth(path, method, body, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- GraphQL ---\n router.post(\n `${prefix}/graphql`,\n defineEventHandler(async (event) => {\n try {\n const body = await readBody(event);\n const result = await dispatcher.handleGraphQL(body, { request: event.node.req });\n return result;\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- Metadata ---\n router.use(\n `${prefix}/meta/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(`${prefix}/meta`.length);\n const method = event.method;\n const body = (method === 'PUT' || method === 'POST')\n ? await readBody(event)\n : undefined;\n const result = await dispatcher.handleMetadata(subPath, { request: event.node.req }, method, body);\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n router.use(\n `${prefix}/meta`,\n defineEventHandler(async (event) => {\n try {\n const method = event.method;\n const body = (method === 'PUT' || method === 'POST')\n ? await readBody(event)\n : undefined;\n const result = await dispatcher.handleMetadata('', { request: event.node.req }, method, body);\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- Data ---\n router.use(\n `${prefix}/data/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(`${prefix}/data`.length);\n const method = event.method;\n const body = (method === 'POST' || method === 'PATCH')\n ? await readBody(event)\n : {};\n const query = getQuery(event);\n const result = await dispatcher.handleData(subPath, method, body, query, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- Storage ---\n router.use(\n `${prefix}/storage/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(`${prefix}/storage`.length);\n const method = event.method;\n const file = undefined; // File upload requires multipart parsing (e.g., formidable)\n const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n return router;\n}\n"],"mappings":"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","names":["body"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport {\n createRouter,\n defineEventHandler,\n getQuery,\n readBody,\n sendRedirect,\n setResponseHeader,\n setResponseStatus,\n type H3Event,\n type Router,\n} from 'h3';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface NuxtAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\n}\n\n/**\n * Creates an h3 router with all ObjectStack route dispatchers.\n * Designed for use in Nuxt server routes or standalone h3 apps.\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 * // server/api/[...].ts\n * import { createH3Router } from '@objectstack/nuxt';\n * import { kernel } from '../kernel';\n *\n * const router = createH3Router({ kernel });\n * export default defineEventHandler(router.handler);\n * ```\n */\nexport function createH3Router(options: NuxtAdapterOptions): Router {\n const router = createRouter();\n const dispatcher = new HttpDispatcher(options.kernel);\n const prefix = options.prefix || '/api';\n\n const errorJson = (event: H3Event, message: string, code: number = 500) => {\n setResponseStatus(event, code);\n return { success: false, error: { message, code } };\n };\n\n const toResponse = (event: H3Event, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) =>\n setResponseHeader(event, k, v as string),\n );\n }\n setResponseStatus(event, result.response.status);\n return result.response.body;\n }\n if (result.result) {\n const res = result.result;\n if (res.type === 'redirect' && res.url) {\n return sendRedirect(event, res.url);\n }\n if (res.type === 'stream' && res.stream) {\n if (res.headers) {\n Object.entries(res.headers).forEach(([k, v]) =>\n setResponseHeader(event, k, v as string),\n );\n }\n return res.stream;\n }\n return res;\n }\n }\n return errorJson(event, 'Not Found', 404);\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n router.get(\n `${prefix}`,\n defineEventHandler(async () => {\n return { data: await dispatcher.getDiscoveryInfo(prefix) };\n }),\n );\n\n // --- .well-known ---\n router.get(\n '/.well-known/objectstack',\n defineEventHandler((event) => {\n return sendRedirect(event, prefix);\n }),\n );\n\n // --- Auth (needs auth service integration) ---\n router.use(\n `${prefix}/auth/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = event.path || event.node.req.url || '';\n const path = urlPath.substring(`${prefix}/auth/`.length).split('?')[0];\n const method = event.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 host = event.node.req.headers.host || 'localhost';\n const protocol = (event.node.req.socket as any)?.encrypted ? 'https' : 'http';\n const url = `${protocol}://${host}${urlPath}`;\n const headers = new Headers();\n if (event.node.req.headers) {\n Object.entries(event.node.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') {\n const body = await readBody(event);\n if (body) {\n init.body = typeof body === 'string' ? body : JSON.stringify(body);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n }\n }\n const webRequest = new Request(url, init);\n const response = await authService.handleRequest(webRequest);\n setResponseStatus(event, response.status);\n response.headers.forEach((v: string, k: string) => setResponseHeader(event, k, v));\n return await response.text();\n }\n\n // Fallback to dispatcher\n const body = method === 'GET' || method === 'HEAD'\n ? {}\n : await readBody(event).catch(() => ({}));\n const result = await dispatcher.handleAuth(path, method, body, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\n router.post(\n `${prefix}/graphql`,\n defineEventHandler(async (event) => {\n try {\n const body = await readBody(event);\n const result = await dispatcher.handleGraphQL(body, { request: event.node.req });\n return result;\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n // --- Storage (needs multipart form parsing) ---\n router.use(\n `${prefix}/storage/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(`${prefix}/storage`.length);\n const method = event.method;\n const file = undefined; // File upload requires multipart parsing (e.g., formidable)\n const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\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.use(\n `${prefix}/**`,\n defineEventHandler(async (event) => {\n try {\n const urlPath = (event.path || event.node.req.url || '').split('?')[0];\n const subPath = urlPath.substring(prefix.length);\n const method = event.method;\n const body = (method === 'POST' || method === 'PUT' || method === 'PATCH')\n ? await readBody(event)\n : undefined;\n const query = getQuery(event);\n const result = await dispatcher.dispatch(method, subPath, body, query, { request: event.node.req });\n return toResponse(event, result);\n } catch (err: any) {\n return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }),\n );\n\n return router;\n}\n"],"mappings":";AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAA4B,sBAA4C;AAkCjE,SAAS,eAAe,SAAqC;AAClE,QAAM,SAAS,aAAa;AAC5B,QAAM,aAAa,IAAI,eAAe,QAAQ,MAAM;AACpD,QAAM,SAAS,QAAQ,UAAU;AAEjC,QAAM,YAAY,CAAC,OAAgB,SAAiB,OAAe,QAAQ;AACzE,sBAAkB,OAAO,IAAI;AAC7B,WAAO,EAAE,SAAS,OAAO,OAAO,EAAE,SAAS,KAAK,EAAE;AAAA,EACpD;AAEA,QAAM,aAAa,CAAC,OAAgB,WAAiC;AACnE,QAAI,OAAO,SAAS;AAClB,UAAI,OAAO,UAAU;AACnB,YAAI,OAAO,SAAS,SAAS;AAC3B,iBAAO,QAAQ,OAAO,SAAS,OAAO,EAAE;AAAA,YAAQ,CAAC,CAAC,GAAG,CAAC,MACpD,kBAAkB,OAAO,GAAG,CAAW;AAAA,UACzC;AAAA,QACF;AACA,0BAAkB,OAAO,OAAO,SAAS,MAAM;AAC/C,eAAO,OAAO,SAAS;AAAA,MACzB;AACA,UAAI,OAAO,QAAQ;AACjB,cAAM,MAAM,OAAO;AACnB,YAAI,IAAI,SAAS,cAAc,IAAI,KAAK;AACtC,iBAAO,aAAa,OAAO,IAAI,GAAG;AAAA,QACpC;AACA,YAAI,IAAI,SAAS,YAAY,IAAI,QAAQ;AACvC,cAAI,IAAI,SAAS;AACf,mBAAO,QAAQ,IAAI,OAAO,EAAE;AAAA,cAAQ,CAAC,CAAC,GAAG,CAAC,MACxC,kBAAkB,OAAO,GAAG,CAAW;AAAA,YACzC;AAAA,UACF;AACA,iBAAO,IAAI;AAAA,QACb;AACA,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO,UAAU,OAAO,aAAa,GAAG;AAAA,EAC1C;AAKA,SAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,mBAAmB,YAAY;AAC7B,aAAO,EAAE,MAAM,MAAM,WAAW,iBAAiB,MAAM,EAAE;AAAA,IAC3D,CAAC;AAAA,EACH;AAGA,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB,CAAC,UAAU;AAC5B,aAAO,aAAa,OAAO,MAAM;AAAA,IACnC,CAAC;AAAA,EACH;AAGA,SAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,mBAAmB,OAAO,UAAU;AAClC,UAAI;AACF,cAAM,UAAU,MAAM,QAAQ,MAAM,KAAK,IAAI,OAAO;AACpD,cAAM,OAAO,QAAQ,UAAU,GAAG,MAAM,SAAS,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC;AACrE,cAAM,SAAS,MAAM;AAGrB,YAAI,cAAkC;AACtC,YAAI;AACF,cAAI,OAAO,QAAQ,OAAO,oBAAoB,YAAY;AACxD,0BAAc,MAAM,QAAQ,OAAO,gBAA6B,MAAM;AAAA,UACxE,WAAW,OAAO,QAAQ,OAAO,eAAe,YAAY;AAC1D,0BAAc,QAAQ,OAAO,WAAwB,MAAM;AAAA,UAC7D;AAAA,QACF,QAAQ;AAEN,wBAAc;AAAA,QAChB;AAEA,YAAI,eAAe,OAAO,YAAY,kBAAkB,YAAY;AAClE,gBAAM,OAAO,MAAM,KAAK,IAAI,QAAQ,QAAQ;AAC5C,gBAAM,WAAY,MAAM,KAAK,IAAI,QAAgB,YAAY,UAAU;AACvE,gBAAM,MAAM,GAAG,QAAQ,MAAM,IAAI,GAAG,OAAO;AAC3C,gBAAM,UAAU,IAAI,QAAQ;AAC5B,cAAI,MAAM,KAAK,IAAI,SAAS;AAC1B,mBAAO,QAAQ,MAAM,KAAK,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM;AACzD,kBAAI,OAAO,MAAM,SAAU,SAAQ,IAAI,GAAG,CAAC;AAAA,uBAClC,MAAM,QAAQ,CAAC,EAAG,SAAQ,IAAI,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA,YACxD,CAAC;AAAA,UACH;AACA,gBAAM,OAAoB,EAAE,QAAQ,QAAQ;AAC5C,cAAI,WAAW,SAAS,WAAW,QAAQ;AACzC,kBAAMA,QAAO,MAAM,SAAS,KAAK;AACjC,gBAAIA,OAAM;AACR,mBAAK,OAAO,OAAOA,UAAS,WAAWA,QAAO,KAAK,UAAUA,KAAI;AACjE,kBAAI,CAAC,QAAQ,IAAI,cAAc,GAAG;AAChC,wBAAQ,IAAI,gBAAgB,kBAAkB;AAAA,cAChD;AAAA,YACF;AAAA,UACF;AACA,gBAAM,aAAa,IAAI,QAAQ,KAAK,IAAI;AACxC,gBAAM,WAAW,MAAM,YAAY,cAAc,UAAU;AAC3D,4BAAkB,OAAO,SAAS,MAAM;AACxC,mBAAS,QAAQ,QAAQ,CAAC,GAAW,MAAc,kBAAkB,OAAO,GAAG,CAAC,CAAC;AACjF,iBAAO,MAAM,SAAS,KAAK;AAAA,QAC7B;AAGA,cAAM,OAAO,WAAW,SAAS,WAAW,SACxC,CAAC,IACD,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC1C,cAAM,SAAS,MAAM,WAAW,WAAW,MAAM,QAAQ,MAAM,EAAE,SAAS,MAAM,KAAK,IAAI,CAAC;AAC1F,eAAO,WAAW,OAAO,MAAM;AAAA,MACjC,SAAS,KAAU;AACjB,eAAO,UAAU,OAAO,IAAI,WAAW,yBAAyB,IAAI,cAAc,GAAG;AAAA,MACvF;AAAA,IACF,CAAC;AAAA,EACH;AAGA,SAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,mBAAmB,OAAO,UAAU;AAClC,UAAI;AACF,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,cAAM,SAAS,MAAM,WAAW,cAAc,MAAM,EAAE,SAAS,MAAM,KAAK,IAAI,CAAC;AAC/E,eAAO;AAAA,MACT,SAAS,KAAU;AACjB,eAAO,UAAU,OAAO,IAAI,WAAW,yBAAyB,IAAI,cAAc,GAAG;AAAA,MACvF;AAAA,IACF,CAAC;AAAA,EACH;AAGA,SAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,mBAAmB,OAAO,UAAU;AAClC,UAAI;AACF,cAAM,WAAW,MAAM,QAAQ,MAAM,KAAK,IAAI,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AACrE,cAAM,UAAU,QAAQ,UAAU,GAAG,MAAM,WAAW,MAAM;AAC5D,cAAM,SAAS,MAAM;AACrB,cAAM,OAAO;AACb,cAAM,SAAS,MAAM,WAAW,cAAc,SAAS,QAAQ,MAAM,EAAE,SAAS,MAAM,KAAK,IAAI,CAAC;AAChG,eAAO,WAAW,OAAO,MAAM;AAAA,MACjC,SAAS,KAAU;AACjB,eAAO,UAAU,OAAO,IAAI,WAAW,yBAAyB,IAAI,cAAc,GAAG;AAAA,MACvF;AAAA,IACF,CAAC;AAAA,EACH;AAKA,SAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,mBAAmB,OAAO,UAAU;AAClC,UAAI;AACF,cAAM,WAAW,MAAM,QAAQ,MAAM,KAAK,IAAI,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AACrE,cAAM,UAAU,QAAQ,UAAU,OAAO,MAAM;AAC/C,cAAM,SAAS,MAAM;AACrB,cAAM,OAAQ,WAAW,UAAU,WAAW,SAAS,WAAW,UAC9D,MAAM,SAAS,KAAK,IACpB;AACJ,cAAM,QAAQ,SAAS,KAAK;AAC5B,cAAM,SAAS,MAAM,WAAW,SAAS,QAAQ,SAAS,MAAM,OAAO,EAAE,SAAS,MAAM,KAAK,IAAI,CAAC;AAClG,eAAO,WAAW,OAAO,MAAM;AAAA,MACjC,SAAS,KAAU;AACjB,eAAO,UAAU,OAAO,IAAI,WAAW,yBAAyB,IAAI,cAAc,GAAG;AAAA,MACvF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;","names":["body"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/nuxt",
|
|
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",
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"h3": "^1.15.
|
|
16
|
-
"@objectstack/runtime": "^3.2.
|
|
15
|
+
"h3": "^1.15.8",
|
|
16
|
+
"@objectstack/runtime": "^3.2.9"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"h3": "^1.15.
|
|
19
|
+
"h3": "^1.15.8",
|
|
20
20
|
"typescript": "^5.0.0",
|
|
21
|
-
"vitest": "^4.0
|
|
22
|
-
"@objectstack/runtime": "3.2.
|
|
21
|
+
"vitest": "^4.1.0",
|
|
22
|
+
"@objectstack/runtime": "3.2.9"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsup --config ../../../tsup.config.ts",
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,12 @@ interface AuthService {
|
|
|
29
29
|
* Creates an h3 router with all ObjectStack route dispatchers.
|
|
30
30
|
* Designed for use in Nuxt server routes or standalone h3 apps.
|
|
31
31
|
*
|
|
32
|
+
* Only routes that need framework-specific handling (auth service, storage
|
|
33
|
+
* file upload, GraphQL raw result, discovery wrapper) are registered explicitly.
|
|
34
|
+
* All other routes are handled by a catch-all that delegates to
|
|
35
|
+
* `HttpDispatcher.dispatch()`, making the adapter automatically support
|
|
36
|
+
* new routes added to the dispatcher.
|
|
37
|
+
*
|
|
32
38
|
* @example
|
|
33
39
|
* ```ts
|
|
34
40
|
* // server/api/[...].ts
|
|
@@ -79,6 +85,8 @@ export function createH3Router(options: NuxtAdapterOptions): Router {
|
|
|
79
85
|
return errorJson(event, 'Not Found', 404);
|
|
80
86
|
};
|
|
81
87
|
|
|
88
|
+
// ─── Explicit routes (framework-specific handling required) ────────────────
|
|
89
|
+
|
|
82
90
|
// --- Discovery ---
|
|
83
91
|
router.get(
|
|
84
92
|
`${prefix}`,
|
|
@@ -95,7 +103,7 @@ export function createH3Router(options: NuxtAdapterOptions): Router {
|
|
|
95
103
|
}),
|
|
96
104
|
);
|
|
97
105
|
|
|
98
|
-
// --- Auth ---
|
|
106
|
+
// --- Auth (needs auth service integration) ---
|
|
99
107
|
router.use(
|
|
100
108
|
`${prefix}/auth/**`,
|
|
101
109
|
defineEventHandler(async (event) => {
|
|
@@ -157,7 +165,7 @@ export function createH3Router(options: NuxtAdapterOptions): Router {
|
|
|
157
165
|
}),
|
|
158
166
|
);
|
|
159
167
|
|
|
160
|
-
// --- GraphQL ---
|
|
168
|
+
// --- GraphQL (returns raw result, not HttpDispatcherResult) ---
|
|
161
169
|
router.post(
|
|
162
170
|
`${prefix}/graphql`,
|
|
163
171
|
defineEventHandler(async (event) => {
|
|
@@ -171,34 +179,16 @@ export function createH3Router(options: NuxtAdapterOptions): Router {
|
|
|
171
179
|
}),
|
|
172
180
|
);
|
|
173
181
|
|
|
174
|
-
// ---
|
|
182
|
+
// --- Storage (needs multipart form parsing) ---
|
|
175
183
|
router.use(
|
|
176
|
-
`${prefix}/
|
|
184
|
+
`${prefix}/storage/**`,
|
|
177
185
|
defineEventHandler(async (event) => {
|
|
178
186
|
try {
|
|
179
187
|
const urlPath = (event.path || event.node.req.url || '').split('?')[0];
|
|
180
|
-
const subPath = urlPath.substring(`${prefix}/
|
|
181
|
-
const method = event.method;
|
|
182
|
-
const body = (method === 'PUT' || method === 'POST')
|
|
183
|
-
? await readBody(event)
|
|
184
|
-
: undefined;
|
|
185
|
-
const result = await dispatcher.handleMetadata(subPath, { request: event.node.req }, method, body);
|
|
186
|
-
return toResponse(event, result);
|
|
187
|
-
} catch (err: any) {
|
|
188
|
-
return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);
|
|
189
|
-
}
|
|
190
|
-
}),
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
router.use(
|
|
194
|
-
`${prefix}/meta`,
|
|
195
|
-
defineEventHandler(async (event) => {
|
|
196
|
-
try {
|
|
188
|
+
const subPath = urlPath.substring(`${prefix}/storage`.length);
|
|
197
189
|
const method = event.method;
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
: undefined;
|
|
201
|
-
const result = await dispatcher.handleMetadata('', { request: event.node.req }, method, body);
|
|
190
|
+
const file = undefined; // File upload requires multipart parsing (e.g., formidable)
|
|
191
|
+
const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });
|
|
202
192
|
return toResponse(event, result);
|
|
203
193
|
} catch (err: any) {
|
|
204
194
|
return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);
|
|
@@ -206,36 +196,21 @@ export function createH3Router(options: NuxtAdapterOptions): Router {
|
|
|
206
196
|
}),
|
|
207
197
|
);
|
|
208
198
|
|
|
209
|
-
//
|
|
199
|
+
// ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────
|
|
200
|
+
// Handles meta, data, packages, analytics, automation, i18n, ui, openapi,
|
|
201
|
+
// custom API endpoints, and any future routes added to HttpDispatcher.
|
|
210
202
|
router.use(
|
|
211
|
-
`${prefix}
|
|
203
|
+
`${prefix}/**`,
|
|
212
204
|
defineEventHandler(async (event) => {
|
|
213
205
|
try {
|
|
214
206
|
const urlPath = (event.path || event.node.req.url || '').split('?')[0];
|
|
215
|
-
const subPath = urlPath.substring(
|
|
207
|
+
const subPath = urlPath.substring(prefix.length);
|
|
216
208
|
const method = event.method;
|
|
217
|
-
const body = (method === 'POST' || method === 'PATCH')
|
|
209
|
+
const body = (method === 'POST' || method === 'PUT' || method === 'PATCH')
|
|
218
210
|
? await readBody(event)
|
|
219
|
-
:
|
|
211
|
+
: undefined;
|
|
220
212
|
const query = getQuery(event);
|
|
221
|
-
const result = await dispatcher.
|
|
222
|
-
return toResponse(event, result);
|
|
223
|
-
} catch (err: any) {
|
|
224
|
-
return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);
|
|
225
|
-
}
|
|
226
|
-
}),
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
// --- Storage ---
|
|
230
|
-
router.use(
|
|
231
|
-
`${prefix}/storage/**`,
|
|
232
|
-
defineEventHandler(async (event) => {
|
|
233
|
-
try {
|
|
234
|
-
const urlPath = (event.path || event.node.req.url || '').split('?')[0];
|
|
235
|
-
const subPath = urlPath.substring(`${prefix}/storage`.length);
|
|
236
|
-
const method = event.method;
|
|
237
|
-
const file = undefined; // File upload requires multipart parsing (e.g., formidable)
|
|
238
|
-
const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });
|
|
213
|
+
const result = await dispatcher.dispatch(method, subPath, body, query, { request: event.node.req });
|
|
239
214
|
return toResponse(event, result);
|
|
240
215
|
} catch (err: any) {
|
|
241
216
|
return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);
|
package/src/nuxt.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', () => {
|
|
@@ -101,16 +100,26 @@ describe('createH3Router', () => {
|
|
|
101
100
|
const app = createTestApp();
|
|
102
101
|
const res = await makeRequest(app, '/api/meta/objects');
|
|
103
102
|
expect(res.status).toBe(200);
|
|
104
|
-
expect(
|
|
105
|
-
|
|
103
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
104
|
+
'GET',
|
|
105
|
+
'/meta/objects',
|
|
106
|
+
undefined,
|
|
107
|
+
expect.any(Object),
|
|
108
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
109
|
+
);
|
|
106
110
|
});
|
|
107
111
|
|
|
108
112
|
it('handles data route', async () => {
|
|
109
113
|
const app = createTestApp();
|
|
110
114
|
const res = await makeRequest(app, '/api/data/account');
|
|
111
115
|
expect(res.status).toBe(200);
|
|
112
|
-
expect(
|
|
113
|
-
|
|
116
|
+
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
117
|
+
'GET',
|
|
118
|
+
'/data/account',
|
|
119
|
+
undefined,
|
|
120
|
+
expect.any(Object),
|
|
121
|
+
expect.objectContaining({ request: expect.anything() }),
|
|
122
|
+
);
|
|
114
123
|
});
|
|
115
124
|
|
|
116
125
|
it('handles storage route', async () => {
|
|
@@ -121,7 +130,7 @@ describe('createH3Router', () => {
|
|
|
121
130
|
});
|
|
122
131
|
|
|
123
132
|
it('handles errors', async () => {
|
|
124
|
-
mockDispatcher.
|
|
133
|
+
mockDispatcher.dispatch.mockRejectedValueOnce(
|
|
125
134
|
Object.assign(new Error('Forbidden'), { statusCode: 403 }),
|
|
126
135
|
);
|
|
127
136
|
const app = createTestApp();
|