@objectstack/nuxt 4.0.3 → 4.0.5
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/README.md +50 -11
- package/package.json +31 -5
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -188
- package/src/__mocks__/runtime.ts +0 -4
- package/src/index.ts +0 -229
- package/src/nuxt.test.ts +0 -144
- package/tsconfig.json +0 -26
- package/vitest.config.ts +0 -16
package/README.md
CHANGED
|
@@ -1,20 +1,59 @@
|
|
|
1
1
|
# @objectstack/nuxt
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Nuxt adapter for ObjectStack — Nitro/h3 server routes for the auto-generated REST API.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
-
|
|
7
|
-
- Full Auth/GraphQL/Metadata/Data/Storage routes
|
|
8
|
-
- AuthPlugin service support
|
|
9
|
-
- Works with standalone h3 apps too
|
|
5
|
+
[](https://www.npmjs.com/package/@objectstack/nuxt)
|
|
6
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
10
7
|
|
|
11
|
-
##
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Provides an h3 router factory that exposes all ObjectStack dispatcher routes, mountable inside `server/api/[...].ts` of any Nuxt 3 application (or directly in a standalone Nitro app).
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @objectstack/nuxt
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
12
19
|
|
|
13
20
|
```typescript
|
|
14
|
-
// server/api/[...].ts
|
|
21
|
+
// server/api/[...objectstack].ts
|
|
15
22
|
import { createH3Router } from '@objectstack/nuxt';
|
|
16
|
-
import { kernel } from '
|
|
23
|
+
import { kernel } from '~/server/kernel';
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
export default defineEventHandler(router.handler);
|
|
25
|
+
export default createH3Router({ kernel, prefix: '/api' }).handler;
|
|
20
26
|
```
|
|
27
|
+
|
|
28
|
+
## Key Exports
|
|
29
|
+
|
|
30
|
+
| Export | Kind | Description |
|
|
31
|
+
|:---|:---|:---|
|
|
32
|
+
| `createH3Router(options)` | function | h3 `Router` with dispatcher routes installed. |
|
|
33
|
+
| `NuxtAdapterOptions` | interface | `{ kernel, prefix? }`. |
|
|
34
|
+
|
|
35
|
+
## Middleware order
|
|
36
|
+
|
|
37
|
+
Register Nuxt server middleware that rewrites or compresses responses **before** this router. Do not wrap ObjectStack routes with `defineEventHandler` validators.
|
|
38
|
+
|
|
39
|
+
## When to use
|
|
40
|
+
|
|
41
|
+
- ✅ Nuxt 3 / Nitro applications.
|
|
42
|
+
- ✅ Any h3-based server (used directly via `createH3Router`).
|
|
43
|
+
|
|
44
|
+
## When not to use
|
|
45
|
+
|
|
46
|
+
- ❌ Nuxt 2 is not supported.
|
|
47
|
+
|
|
48
|
+
## Related Packages
|
|
49
|
+
|
|
50
|
+
- [`@objectstack/runtime`](../../runtime), [`@objectstack/rest`](../../rest).
|
|
51
|
+
|
|
52
|
+
## Links
|
|
53
|
+
|
|
54
|
+
- 📖 Docs: <https://objectstack.ai/docs>
|
|
55
|
+
- 📚 API Reference: <https://objectstack.ai/docs/references>
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
Apache-2.0 © ObjectStack
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/nuxt",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.5",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -13,13 +13,39 @@
|
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"h3": "^1.15.11",
|
|
16
|
-
"@objectstack/runtime": "^4.0.
|
|
16
|
+
"@objectstack/runtime": "^4.0.5"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"h3": "^1.15.11",
|
|
20
|
-
"typescript": "^6.0.
|
|
21
|
-
"vitest": "^4.1.
|
|
22
|
-
"@objectstack/runtime": "4.0.
|
|
20
|
+
"typescript": "^6.0.3",
|
|
21
|
+
"vitest": "^4.1.5",
|
|
22
|
+
"@objectstack/runtime": "4.0.5"
|
|
23
|
+
},
|
|
24
|
+
"description": "Nuxt adapter for ObjectStack — Nitro server routes for the ObjectStack REST API.",
|
|
25
|
+
"keywords": [
|
|
26
|
+
"objectstack",
|
|
27
|
+
"nuxt",
|
|
28
|
+
"adapter",
|
|
29
|
+
"nitro",
|
|
30
|
+
"rest"
|
|
31
|
+
],
|
|
32
|
+
"author": "ObjectStack",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/objectstack-ai/framework.git",
|
|
36
|
+
"directory": "packages/adapters/nuxt"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://objectstack.ai/docs",
|
|
39
|
+
"bugs": "https://github.com/objectstack-ai/framework/issues",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"README.md"
|
|
46
|
+
],
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
23
49
|
},
|
|
24
50
|
"scripts": {
|
|
25
51
|
"build": "tsup --config ../../../tsup.config.ts",
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @objectstack/nuxt@4.0.3 build /home/runner/work/framework/framework/packages/adapters/nuxt
|
|
3
|
-
> tsup --config ../../../tsup.config.ts
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: src/index.ts
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.1
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/framework/framework/tsup.config.ts
|
|
9
|
-
[34mCLI[39m Target: es2020
|
|
10
|
-
[34mCLI[39m Cleaning output folder
|
|
11
|
-
[34mESM[39m Build start
|
|
12
|
-
[34mCJS[39m Build start
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[32m7.07 KB[39m
|
|
14
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m12.31 KB[39m
|
|
15
|
-
[32mCJS[39m ⚡️ Build success in 65ms
|
|
16
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m5.86 KB[39m
|
|
17
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[32m12.34 KB[39m
|
|
18
|
-
[32mESM[39m ⚡️ Build success in 66ms
|
|
19
|
-
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in 10439ms
|
|
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
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
# @objectstack/nuxt
|
|
2
|
-
|
|
3
|
-
## 4.0.3
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- @objectstack/runtime@4.0.3
|
|
8
|
-
|
|
9
|
-
## 4.0.2
|
|
10
|
-
|
|
11
|
-
### Patch Changes
|
|
12
|
-
|
|
13
|
-
- @objectstack/runtime@4.0.2
|
|
14
|
-
|
|
15
|
-
## 4.0.0
|
|
16
|
-
|
|
17
|
-
### Patch Changes
|
|
18
|
-
|
|
19
|
-
- f08ffc3: Fix discovery API endpoint routing and protocol consistency.
|
|
20
|
-
|
|
21
|
-
**Discovery route standardization:**
|
|
22
|
-
|
|
23
|
-
- All adapters (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) now mount the discovery endpoint at `{prefix}/discovery` instead of `{prefix}` root.
|
|
24
|
-
- `.well-known/objectstack` redirects now point to `{prefix}/discovery`.
|
|
25
|
-
- Client `connect()` fallback URL changed from `/api/v1` to `/api/v1/discovery`.
|
|
26
|
-
- Runtime dispatcher handles both `/discovery` (standard) and `/` (legacy) for backward compatibility.
|
|
27
|
-
|
|
28
|
-
**Schema & route alignment:**
|
|
29
|
-
|
|
30
|
-
- Added `storage` (service: `file-storage`) and `feed` (service: `data`) routes to `DEFAULT_DISPATCHER_ROUTES`.
|
|
31
|
-
- Added `feed` and `discovery` fields to `ApiRoutesSchema`.
|
|
32
|
-
- Unified `GetDiscoveryResponseSchema` with `DiscoverySchema` as single source of truth.
|
|
33
|
-
- Client `getRoute('feed')` fallback updated from `/api/v1/data` to `/api/v1/feed`.
|
|
34
|
-
|
|
35
|
-
**Type safety:**
|
|
36
|
-
|
|
37
|
-
- Extracted `ApiRouteType` from `ApiRoutes` keys for type-safe client route resolution.
|
|
38
|
-
- Removed `as any` type casting in client route access.
|
|
39
|
-
|
|
40
|
-
- Updated dependencies [f08ffc3]
|
|
41
|
-
- Updated dependencies [e0b0a78]
|
|
42
|
-
- @objectstack/runtime@4.0.0
|
|
43
|
-
|
|
44
|
-
## 3.3.1
|
|
45
|
-
|
|
46
|
-
### Patch Changes
|
|
47
|
-
|
|
48
|
-
- @objectstack/runtime@3.3.1
|
|
49
|
-
|
|
50
|
-
## 3.3.0
|
|
51
|
-
|
|
52
|
-
### Patch Changes
|
|
53
|
-
|
|
54
|
-
- @objectstack/runtime@3.3.0
|
|
55
|
-
|
|
56
|
-
## 3.2.9
|
|
57
|
-
|
|
58
|
-
### Patch Changes
|
|
59
|
-
|
|
60
|
-
- @objectstack/runtime@3.2.9
|
|
61
|
-
|
|
62
|
-
## 3.2.8
|
|
63
|
-
|
|
64
|
-
### Patch Changes
|
|
65
|
-
|
|
66
|
-
- @objectstack/runtime@3.2.8
|
|
67
|
-
|
|
68
|
-
## 3.2.8
|
|
69
|
-
|
|
70
|
-
### Patch Changes
|
|
71
|
-
|
|
72
|
-
- 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
|
|
73
|
-
- Only auth (service check), storage (multipart), GraphQL (raw result), and discovery (response wrapper) remain as explicit routes
|
|
74
|
-
|
|
75
|
-
## 3.2.7
|
|
76
|
-
|
|
77
|
-
### Patch Changes
|
|
78
|
-
|
|
79
|
-
- @objectstack/runtime@3.2.7
|
|
80
|
-
|
|
81
|
-
## 3.2.6
|
|
82
|
-
|
|
83
|
-
### Patch Changes
|
|
84
|
-
|
|
85
|
-
- @objectstack/runtime@3.2.6
|
|
86
|
-
|
|
87
|
-
## 3.2.5
|
|
88
|
-
|
|
89
|
-
### Patch Changes
|
|
90
|
-
|
|
91
|
-
- @objectstack/runtime@3.2.5
|
|
92
|
-
|
|
93
|
-
## 3.2.4
|
|
94
|
-
|
|
95
|
-
### Patch Changes
|
|
96
|
-
|
|
97
|
-
- @objectstack/runtime@3.2.4
|
|
98
|
-
|
|
99
|
-
## 3.2.3
|
|
100
|
-
|
|
101
|
-
### Patch Changes
|
|
102
|
-
|
|
103
|
-
- @objectstack/runtime@3.2.3
|
|
104
|
-
|
|
105
|
-
## 3.2.2
|
|
106
|
-
|
|
107
|
-
### Patch Changes
|
|
108
|
-
|
|
109
|
-
- @objectstack/runtime@3.2.2
|
|
110
|
-
|
|
111
|
-
## 3.2.1
|
|
112
|
-
|
|
113
|
-
### Patch Changes
|
|
114
|
-
|
|
115
|
-
- @objectstack/runtime@3.2.1
|
|
116
|
-
|
|
117
|
-
## 3.2.0
|
|
118
|
-
|
|
119
|
-
### Patch Changes
|
|
120
|
-
|
|
121
|
-
- @objectstack/runtime@3.2.0
|
|
122
|
-
|
|
123
|
-
## 3.1.1
|
|
124
|
-
|
|
125
|
-
### Patch Changes
|
|
126
|
-
|
|
127
|
-
- @objectstack/runtime@3.1.1
|
|
128
|
-
|
|
129
|
-
## 3.1.0
|
|
130
|
-
|
|
131
|
-
### Patch Changes
|
|
132
|
-
|
|
133
|
-
- @objectstack/runtime@3.1.0
|
|
134
|
-
|
|
135
|
-
## 3.0.11
|
|
136
|
-
|
|
137
|
-
### Patch Changes
|
|
138
|
-
|
|
139
|
-
- @objectstack/runtime@3.0.11
|
|
140
|
-
|
|
141
|
-
## 3.0.10
|
|
142
|
-
|
|
143
|
-
### Patch Changes
|
|
144
|
-
|
|
145
|
-
- @objectstack/runtime@3.0.10
|
|
146
|
-
|
|
147
|
-
## 3.0.9
|
|
148
|
-
|
|
149
|
-
### Patch Changes
|
|
150
|
-
|
|
151
|
-
- @objectstack/runtime@3.0.9
|
|
152
|
-
|
|
153
|
-
## 3.0.8
|
|
154
|
-
|
|
155
|
-
### Patch Changes
|
|
156
|
-
|
|
157
|
-
- @objectstack/runtime@3.0.8
|
|
158
|
-
|
|
159
|
-
## 3.0.7
|
|
160
|
-
|
|
161
|
-
### Patch Changes
|
|
162
|
-
|
|
163
|
-
- @objectstack/runtime@3.0.7
|
|
164
|
-
|
|
165
|
-
## 3.0.6
|
|
166
|
-
|
|
167
|
-
### Patch Changes
|
|
168
|
-
|
|
169
|
-
- @objectstack/runtime@3.0.6
|
|
170
|
-
|
|
171
|
-
## 3.0.5
|
|
172
|
-
|
|
173
|
-
### Patch Changes
|
|
174
|
-
|
|
175
|
-
- @objectstack/runtime@3.0.5
|
|
176
|
-
|
|
177
|
-
## 3.0.4
|
|
178
|
-
|
|
179
|
-
### Patch Changes
|
|
180
|
-
|
|
181
|
-
- @objectstack/runtime@3.0.4
|
|
182
|
-
|
|
183
|
-
## 3.0.3
|
|
184
|
-
|
|
185
|
-
### Patch Changes
|
|
186
|
-
|
|
187
|
-
- Updated dependencies [c7267f6]
|
|
188
|
-
- @objectstack/runtime@3.0.3
|
package/src/__mocks__/runtime.ts
DELETED
package/src/index.ts
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
createRouter,
|
|
5
|
-
defineEventHandler,
|
|
6
|
-
getQuery,
|
|
7
|
-
readBody,
|
|
8
|
-
sendRedirect,
|
|
9
|
-
setResponseHeader,
|
|
10
|
-
setResponseStatus,
|
|
11
|
-
type H3Event,
|
|
12
|
-
type Router,
|
|
13
|
-
} from 'h3';
|
|
14
|
-
import { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';
|
|
15
|
-
|
|
16
|
-
export interface NuxtAdapterOptions {
|
|
17
|
-
kernel: ObjectKernel;
|
|
18
|
-
prefix?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Auth service interface with handleRequest method
|
|
23
|
-
*/
|
|
24
|
-
interface AuthService {
|
|
25
|
-
handleRequest(request: Request): Promise<Response>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Creates an h3 router with all ObjectStack route dispatchers.
|
|
30
|
-
* Designed for use in Nuxt server routes or standalone h3 apps.
|
|
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
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* // server/api/[...].ts
|
|
41
|
-
* import { createH3Router } from '@objectstack/nuxt';
|
|
42
|
-
* import { kernel } from '../kernel';
|
|
43
|
-
*
|
|
44
|
-
* const router = createH3Router({ kernel });
|
|
45
|
-
* export default defineEventHandler(router.handler);
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
export function createH3Router(options: NuxtAdapterOptions): Router {
|
|
49
|
-
const router = createRouter();
|
|
50
|
-
const dispatcher = new HttpDispatcher(options.kernel);
|
|
51
|
-
const prefix = options.prefix || '/api';
|
|
52
|
-
|
|
53
|
-
const errorJson = (event: H3Event, message: string, code: number = 500) => {
|
|
54
|
-
setResponseStatus(event, code);
|
|
55
|
-
return { success: false, error: { message, code } };
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const toResponse = (event: H3Event, result: HttpDispatcherResult) => {
|
|
59
|
-
if (result.handled) {
|
|
60
|
-
if (result.response) {
|
|
61
|
-
if (result.response.headers) {
|
|
62
|
-
Object.entries(result.response.headers).forEach(([k, v]) =>
|
|
63
|
-
setResponseHeader(event, k, v as string),
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
setResponseStatus(event, result.response.status);
|
|
67
|
-
return result.response.body;
|
|
68
|
-
}
|
|
69
|
-
if (result.result) {
|
|
70
|
-
const res = result.result;
|
|
71
|
-
if (res.type === 'redirect' && res.url) {
|
|
72
|
-
return sendRedirect(event, res.url);
|
|
73
|
-
}
|
|
74
|
-
if (res.type === 'stream' && res.stream) {
|
|
75
|
-
if (res.headers) {
|
|
76
|
-
Object.entries(res.headers).forEach(([k, v]) =>
|
|
77
|
-
setResponseHeader(event, k, v as string),
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
return res.stream;
|
|
81
|
-
}
|
|
82
|
-
return res;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return errorJson(event, 'Not Found', 404);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// ─── Explicit routes (framework-specific handling required) ────────────────
|
|
89
|
-
|
|
90
|
-
// --- Discovery ---
|
|
91
|
-
router.get(
|
|
92
|
-
prefix,
|
|
93
|
-
defineEventHandler(async () => {
|
|
94
|
-
return { data: await dispatcher.getDiscoveryInfo(prefix) };
|
|
95
|
-
}),
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
router.get(
|
|
99
|
-
`${prefix}/discovery`,
|
|
100
|
-
defineEventHandler(async () => {
|
|
101
|
-
return { data: await dispatcher.getDiscoveryInfo(prefix) };
|
|
102
|
-
}),
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
// --- .well-known ---
|
|
106
|
-
router.get(
|
|
107
|
-
'/.well-known/objectstack',
|
|
108
|
-
defineEventHandler((event) => {
|
|
109
|
-
return sendRedirect(event, prefix);
|
|
110
|
-
}),
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
// --- Auth (needs auth service integration) ---
|
|
114
|
-
router.use(
|
|
115
|
-
`${prefix}/auth/**`,
|
|
116
|
-
defineEventHandler(async (event) => {
|
|
117
|
-
try {
|
|
118
|
-
const urlPath = event.path || event.node.req.url || '';
|
|
119
|
-
const path = urlPath.substring(`${prefix}/auth/`.length).split('?')[0];
|
|
120
|
-
const method = event.method;
|
|
121
|
-
|
|
122
|
-
// Try AuthPlugin service first (prefer async to support factory-based services)
|
|
123
|
-
let authService: AuthService | null = null;
|
|
124
|
-
try {
|
|
125
|
-
if (typeof options.kernel.getServiceAsync === 'function') {
|
|
126
|
-
authService = await options.kernel.getServiceAsync<AuthService>('auth');
|
|
127
|
-
} else if (typeof options.kernel.getService === 'function') {
|
|
128
|
-
authService = options.kernel.getService<AuthService>('auth');
|
|
129
|
-
}
|
|
130
|
-
} catch {
|
|
131
|
-
// Service not registered — fall through to dispatcher
|
|
132
|
-
authService = null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (authService && typeof authService.handleRequest === 'function') {
|
|
136
|
-
const host = event.node.req.headers.host || 'localhost';
|
|
137
|
-
const protocol = (event.node.req.socket as any)?.encrypted ? 'https' : 'http';
|
|
138
|
-
const url = `${protocol}://${host}${urlPath}`;
|
|
139
|
-
const headers = new Headers();
|
|
140
|
-
if (event.node.req.headers) {
|
|
141
|
-
Object.entries(event.node.req.headers).forEach(([k, v]) => {
|
|
142
|
-
if (typeof v === 'string') headers.set(k, v);
|
|
143
|
-
else if (Array.isArray(v)) headers.set(k, v.join(', '));
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
const init: RequestInit = { method, headers };
|
|
147
|
-
if (method !== 'GET' && method !== 'HEAD') {
|
|
148
|
-
const body = await readBody(event);
|
|
149
|
-
if (body) {
|
|
150
|
-
init.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
151
|
-
if (!headers.has('content-type')) {
|
|
152
|
-
headers.set('content-type', 'application/json');
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
const webRequest = new Request(url, init);
|
|
157
|
-
const response = await authService.handleRequest(webRequest);
|
|
158
|
-
setResponseStatus(event, response.status);
|
|
159
|
-
response.headers.forEach((v: string, k: string) => setResponseHeader(event, k, v));
|
|
160
|
-
return await response.text();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Fallback to dispatcher
|
|
164
|
-
const body = method === 'GET' || method === 'HEAD'
|
|
165
|
-
? {}
|
|
166
|
-
: await readBody(event).catch(() => ({}));
|
|
167
|
-
const result = await dispatcher.handleAuth(path, method, body, { request: event.node.req });
|
|
168
|
-
return toResponse(event, result);
|
|
169
|
-
} catch (err: any) {
|
|
170
|
-
return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);
|
|
171
|
-
}
|
|
172
|
-
}),
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
// --- GraphQL (returns raw result, not HttpDispatcherResult) ---
|
|
176
|
-
router.post(
|
|
177
|
-
`${prefix}/graphql`,
|
|
178
|
-
defineEventHandler(async (event) => {
|
|
179
|
-
try {
|
|
180
|
-
const body = await readBody(event);
|
|
181
|
-
const result = await dispatcher.handleGraphQL(body, { request: event.node.req });
|
|
182
|
-
return result;
|
|
183
|
-
} catch (err: any) {
|
|
184
|
-
return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);
|
|
185
|
-
}
|
|
186
|
-
}),
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
// --- Storage (needs multipart form parsing) ---
|
|
190
|
-
router.use(
|
|
191
|
-
`${prefix}/storage/**`,
|
|
192
|
-
defineEventHandler(async (event) => {
|
|
193
|
-
try {
|
|
194
|
-
const urlPath = (event.path || event.node.req.url || '').split('?')[0];
|
|
195
|
-
const subPath = urlPath.substring(`${prefix}/storage`.length);
|
|
196
|
-
const method = event.method;
|
|
197
|
-
const file = undefined; // File upload requires multipart parsing (e.g., formidable)
|
|
198
|
-
const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req });
|
|
199
|
-
return toResponse(event, result);
|
|
200
|
-
} catch (err: any) {
|
|
201
|
-
return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);
|
|
202
|
-
}
|
|
203
|
-
}),
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
// ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────
|
|
207
|
-
// Handles meta, data, packages, analytics, automation, i18n, ui, openapi,
|
|
208
|
-
// custom API endpoints, and any future routes added to HttpDispatcher.
|
|
209
|
-
router.use(
|
|
210
|
-
`${prefix}/**`,
|
|
211
|
-
defineEventHandler(async (event) => {
|
|
212
|
-
try {
|
|
213
|
-
const urlPath = (event.path || event.node.req.url || '').split('?')[0];
|
|
214
|
-
const subPath = urlPath.substring(prefix.length);
|
|
215
|
-
const method = event.method;
|
|
216
|
-
const body = (method === 'POST' || method === 'PUT' || method === 'PATCH')
|
|
217
|
-
? await readBody(event)
|
|
218
|
-
: undefined;
|
|
219
|
-
const query = getQuery(event);
|
|
220
|
-
const result = await dispatcher.dispatch(method, subPath, body, query, { request: event.node.req }, prefix);
|
|
221
|
-
return toResponse(event, result);
|
|
222
|
-
} catch (err: any) {
|
|
223
|
-
return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500);
|
|
224
|
-
}
|
|
225
|
-
}),
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
return router;
|
|
229
|
-
}
|
package/src/nuxt.test.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
4
|
-
|
|
5
|
-
// Mock dispatcher instance
|
|
6
|
-
const mockDispatcher = {
|
|
7
|
-
getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }),
|
|
8
|
-
handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }),
|
|
9
|
-
handleGraphQL: vi.fn().mockResolvedValue({ data: {} }),
|
|
10
|
-
handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }),
|
|
11
|
-
dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }),
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
vi.mock('@objectstack/runtime', () => {
|
|
15
|
-
return {
|
|
16
|
-
HttpDispatcher: function HttpDispatcher() {
|
|
17
|
-
return mockDispatcher;
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
import { createApp, toNodeListener, createEvent } from 'h3';
|
|
23
|
-
import { createH3Router } from './index';
|
|
24
|
-
import http from 'node:http';
|
|
25
|
-
|
|
26
|
-
const mockKernel = { name: 'test-kernel' } as any;
|
|
27
|
-
|
|
28
|
-
function createTestApp() {
|
|
29
|
-
const app = createApp();
|
|
30
|
-
const router = createH3Router({ kernel: mockKernel });
|
|
31
|
-
app.use(router);
|
|
32
|
-
return app;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function makeRequest(app: any, url: string, method = 'GET', body?: any) {
|
|
36
|
-
const listener = toNodeListener(app);
|
|
37
|
-
|
|
38
|
-
return new Promise<{ status: number; body: any }>((resolve, reject) => {
|
|
39
|
-
const server = http.createServer(listener);
|
|
40
|
-
server.listen(0, () => {
|
|
41
|
-
const addr = server.address() as any;
|
|
42
|
-
const reqOpts: http.RequestOptions = {
|
|
43
|
-
hostname: 'localhost',
|
|
44
|
-
port: addr.port,
|
|
45
|
-
path: url,
|
|
46
|
-
method,
|
|
47
|
-
headers: body ? { 'Content-Type': 'application/json' } : {},
|
|
48
|
-
};
|
|
49
|
-
const req = http.request(reqOpts, (res) => {
|
|
50
|
-
let data = '';
|
|
51
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
52
|
-
res.on('end', () => {
|
|
53
|
-
server.close();
|
|
54
|
-
try {
|
|
55
|
-
resolve({ status: res.statusCode || 500, body: JSON.parse(data) });
|
|
56
|
-
} catch {
|
|
57
|
-
resolve({ status: res.statusCode || 500, body: data });
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
req.on('error', (err) => { server.close(); reject(err); });
|
|
62
|
-
if (body) req.write(JSON.stringify(body));
|
|
63
|
-
req.end();
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
describe('createH3Router', () => {
|
|
69
|
-
beforeEach(() => {
|
|
70
|
-
vi.clearAllMocks();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('handles discovery endpoint', async () => {
|
|
74
|
-
const app = createTestApp();
|
|
75
|
-
const res = await makeRequest(app, '/api');
|
|
76
|
-
expect(res.status).toBe(200);
|
|
77
|
-
expect(res.body.data).toBeDefined();
|
|
78
|
-
expect(res.body.data.version).toBe('1.0');
|
|
79
|
-
expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/api');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('handles auth route', async () => {
|
|
83
|
-
const app = createTestApp();
|
|
84
|
-
const res = await makeRequest(app, '/api/auth/login', 'POST', { email: 'a@b.com' });
|
|
85
|
-
expect(res.status).toBe(200);
|
|
86
|
-
expect(res.body.ok).toBe(true);
|
|
87
|
-
expect(mockDispatcher.handleAuth).toHaveBeenCalled();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('handles graphql route', async () => {
|
|
91
|
-
const app = createTestApp();
|
|
92
|
-
const body = { query: '{ objects { name } }' };
|
|
93
|
-
const res = await makeRequest(app, '/api/graphql', 'POST', body);
|
|
94
|
-
expect(res.status).toBe(200);
|
|
95
|
-
expect(res.body.data).toBeDefined();
|
|
96
|
-
expect(mockDispatcher.handleGraphQL).toHaveBeenCalled();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('handles metadata route', async () => {
|
|
100
|
-
const app = createTestApp();
|
|
101
|
-
const res = await makeRequest(app, '/api/meta/objects');
|
|
102
|
-
expect(res.status).toBe(200);
|
|
103
|
-
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
104
|
-
'GET',
|
|
105
|
-
'/meta/objects',
|
|
106
|
-
undefined,
|
|
107
|
-
expect.any(Object),
|
|
108
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
109
|
-
'/api',
|
|
110
|
-
);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('handles data route', async () => {
|
|
114
|
-
const app = createTestApp();
|
|
115
|
-
const res = await makeRequest(app, '/api/data/account');
|
|
116
|
-
expect(res.status).toBe(200);
|
|
117
|
-
expect(mockDispatcher.dispatch).toHaveBeenCalledWith(
|
|
118
|
-
'GET',
|
|
119
|
-
'/data/account',
|
|
120
|
-
undefined,
|
|
121
|
-
expect.any(Object),
|
|
122
|
-
expect.objectContaining({ request: expect.anything() }),
|
|
123
|
-
'/api',
|
|
124
|
-
);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('handles storage route', async () => {
|
|
128
|
-
const app = createTestApp();
|
|
129
|
-
const res = await makeRequest(app, '/api/storage/files');
|
|
130
|
-
expect(res.status).toBe(200);
|
|
131
|
-
expect(mockDispatcher.handleStorage).toHaveBeenCalled();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('handles errors', async () => {
|
|
135
|
-
mockDispatcher.dispatch.mockRejectedValueOnce(
|
|
136
|
-
Object.assign(new Error('Forbidden'), { statusCode: 403 }),
|
|
137
|
-
);
|
|
138
|
-
const app = createTestApp();
|
|
139
|
-
const res = await makeRequest(app, '/api/data/account');
|
|
140
|
-
expect(res.status).toBe(403);
|
|
141
|
-
expect(res.body.success).toBe(false);
|
|
142
|
-
expect(res.body.error.message).toBe('Forbidden');
|
|
143
|
-
});
|
|
144
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "./dist",
|
|
5
|
-
"rootDir": "./src",
|
|
6
|
-
"module": "NodeNext",
|
|
7
|
-
"moduleResolution": "NodeNext",
|
|
8
|
-
"esModuleInterop": true,
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"lib": [
|
|
11
|
-
"ES2020",
|
|
12
|
-
"DOM",
|
|
13
|
-
"DOM.Iterable"
|
|
14
|
-
],
|
|
15
|
-
"types": [
|
|
16
|
-
"node"
|
|
17
|
-
]
|
|
18
|
-
},
|
|
19
|
-
"include": [
|
|
20
|
-
"src/**/*"
|
|
21
|
-
],
|
|
22
|
-
"exclude": [
|
|
23
|
-
"node_modules",
|
|
24
|
-
"dist"
|
|
25
|
-
]
|
|
26
|
-
}
|
package/vitest.config.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { defineConfig } from 'vitest/config';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
|
|
6
|
-
export default defineConfig({
|
|
7
|
-
test: {
|
|
8
|
-
globals: true,
|
|
9
|
-
environment: 'node',
|
|
10
|
-
},
|
|
11
|
-
resolve: {
|
|
12
|
-
alias: {
|
|
13
|
-
'@objectstack/runtime': path.resolve(__dirname, 'src/__mocks__/runtime.ts'),
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
});
|