@edgeone/nuxt-pages 1.0.0

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.
Files changed (47) hide show
  1. package/README.md +275 -0
  2. package/dist/build/content/server.js +18 -0
  3. package/dist/build/content/static.js +17 -0
  4. package/dist/build/functions/server.js +19 -0
  5. package/dist/build/plugin-context.js +18 -0
  6. package/dist/build/routes.js +18 -0
  7. package/dist/build/templates/nuxt-handler-backup.js +305 -0
  8. package/dist/build/templates/nuxt-handler-monorepo.tmpl-ipx_backup.js +511 -0
  9. package/dist/build/templates/nuxt-handler-monorepo.tmpl.js +243 -0
  10. package/dist/build/templates/nuxt-handler.tmpl.js +212 -0
  11. package/dist/esm-chunks/chunk-5YBUNNZ4.js +81 -0
  12. package/dist/esm-chunks/chunk-6BT4RYQJ.js +43 -0
  13. package/dist/esm-chunks/chunk-6YERJDAJ.js +208 -0
  14. package/dist/esm-chunks/chunk-GX4Z7KQX.js +15065 -0
  15. package/dist/esm-chunks/chunk-HBXUWFGE.js +19 -0
  16. package/dist/esm-chunks/chunk-HY3HNABZ.js +87 -0
  17. package/dist/esm-chunks/chunk-KGYBHZC3.js +1467 -0
  18. package/dist/esm-chunks/chunk-MMMRMLH2.js +132 -0
  19. package/dist/esm-chunks/chunk-NJ4SUJNF.js +5635 -0
  20. package/dist/esm-chunks/chunk-QG7JLDXY.js +127 -0
  21. package/dist/esm-chunks/chunk-RPSYO4VM.js +562 -0
  22. package/dist/esm-chunks/chunk-UOPC2N5A.js +69 -0
  23. package/dist/esm-chunks/chunk-V2LFVP3C.js +838 -0
  24. package/dist/index.js +61 -0
  25. package/dist/run/config.js +17 -0
  26. package/dist/run/constants.js +17 -0
  27. package/dist/run/handlers/cache.cjs +1410 -0
  28. package/dist/run/handlers/nuxt-cache.cjs +200 -0
  29. package/dist/run/handlers/nuxt-server.js +156 -0
  30. package/dist/run/handlers/request-context.cjs +148 -0
  31. package/dist/run/handlers/server.js +77 -0
  32. package/dist/run/handlers/tags-handler.cjs +177 -0
  33. package/dist/run/handlers/tracer.cjs +1004 -0
  34. package/dist/run/handlers/use-cache-handler.js +220 -0
  35. package/dist/run/handlers/wait-until.cjs +123 -0
  36. package/dist/run/headers.js +17 -0
  37. package/dist/run/revalidate.js +34 -0
  38. package/dist/run/storage/regional-blob-store.cjs +64 -0
  39. package/dist/run/storage/request-scoped-in-memory-cache.cjs +1582 -0
  40. package/dist/run/storage/storage.cjs +191 -0
  41. package/dist/shared/blob-types.cjs +37 -0
  42. package/dist/shared/blobkey.js +25 -0
  43. package/dist/shared/cache-types.cjs +33 -0
  44. package/dist/shared/nuxt-cache-types.cjs +18 -0
  45. package/dist/types/options.js +6 -0
  46. package/dist/utils.js +25 -0
  47. package/package.json +58 -0
package/README.md ADDED
@@ -0,0 +1,275 @@
1
+ # EdgeOne Nuxt Deploy
2
+
3
+ A professional deployment package that seamlessly deploys your Nuxt 3 applications to Tencent Cloud EdgeOne platform with optimized performance and intelligent caching.
4
+
5
+ ## ✨ Features
6
+
7
+ - 🚀 **One-Click Deployment** - Automated build and deployment process for EdgeOne
8
+ - 🏗️ **Nitro Integration** - Full compatibility with Nuxt 3's Nitro engine
9
+ - 📦 **Monorepo Support** - Optimized templates for complex project structures
10
+ - 🎯 **Smart Caching** - Multi-layer caching with memory, regional blobs, and tag invalidation
11
+ - ⚡ **Performance Optimized** - Static asset handling, lazy loading, and OpenTelemetry tracing
12
+ - 🔧 **Auto Configuration** - Intelligent Nuxt config detection and modification
13
+ - 🌐 **SSR Ready** - Full server-side rendering support on EdgeOne
14
+
15
+ ## 📋 Requirements
16
+
17
+ - **Nuxt**: 3.8.0 or higher
18
+ - **Node.js**: 18.x or higher
19
+ - **EdgeOne**: Tencent Cloud EdgeOne account
20
+
21
+ ## 🚀 Quick Start
22
+
23
+ ### Installation
24
+
25
+ ```bash
26
+ npm install @tencent/nuxt-pages
27
+ ```
28
+
29
+ ### Basic Usage
30
+
31
+ 1. **Add to your build process:**
32
+
33
+ ```javascript
34
+ // In your build script or CI/CD pipeline
35
+ import { onPreBuild, onBuild, onPostBuild } from '@tencent/nuxt-pages'
36
+
37
+ const buildOptions = {
38
+ cwd: process.cwd(),
39
+ env: process.env,
40
+ meta: {},
41
+ functions: {},
42
+ constants: {
43
+ PUBLISH_DIR: 'dist'
44
+ }
45
+ }
46
+
47
+ // Execute build phases
48
+ await onPreBuild(buildOptions)
49
+ await onBuild(buildOptions)
50
+ await onPostBuild(buildOptions)
51
+ ```
52
+
53
+ 2. **Your Nuxt project will be automatically configured:**
54
+
55
+ The package will create or modify your `nuxt.config.ts`:
56
+
57
+ ```typescript
58
+ export default defineNuxtConfig({
59
+ srcDir: 'app',
60
+ nitro: {
61
+ preset: 'node-server',
62
+ output: {
63
+ dir: '.edgeone',
64
+ publicDir: '.edgeone/assets',
65
+ serverDir: '.edgeone/server-handler',
66
+ },
67
+ },
68
+ devtools: { enabled: true },
69
+ })
70
+ ```
71
+
72
+ ## 🏗️ Architecture
73
+
74
+ ### Build Process
75
+
76
+ The deployment follows a three-phase approach:
77
+
78
+ 1. **PreBuild Phase** (`onPreBuild`)
79
+ - Validates Nuxt version compatibility
80
+ - Configures Nitro build output
81
+ - Sets up EdgeOne-specific configurations
82
+
83
+ 2. **Build Phase** (`onBuild`)
84
+ - Creates server handlers
85
+ - Generates route metadata for pages and API routes
86
+ - Patches Nitro handlers for EdgeOne compatibility
87
+
88
+ 3. **PostBuild Phase** (`onPostBuild`)
89
+ - Restores original configurations
90
+ - Cleanup and optimization
91
+
92
+ ### Caching Strategy
93
+
94
+ - **Memory Cache**: LRU cache for frequently accessed data
95
+ - **Regional Blobs**: Distributed storage for static assets
96
+ - **Tag Invalidation**: Smart cache invalidation based on content tags
97
+ - **Stale-While-Revalidate**: Background revalidation for optimal performance
98
+
99
+ ## 📁 Project Structure
100
+
101
+ After deployment, your project will have:
102
+
103
+ ```
104
+ your-project/
105
+ ├── .edgeone/
106
+ │ ├── assets/ # Static assets
107
+ │ ├── server-handler/ # Server-side code
108
+ │ │ ├── chunks/ # Nitro chunks
109
+ │ │ ├── handler.js # EdgeOne handler
110
+ │ │ └── index.mjs # Server entry point
111
+ │ └── dist/ # Runtime modules
112
+ ├── app/ # Your Nuxt app (if using srcDir)
113
+ ├── nuxt.config.ts # Auto-generated/modified config
114
+ └── package.json
115
+ ```
116
+
117
+ ## ⚙️ Configuration
118
+
119
+ ### Advanced Options
120
+
121
+ You can customize the deployment behavior:
122
+
123
+ ```typescript
124
+ // Custom build options
125
+ const buildOptions = {
126
+ cwd: process.cwd(),
127
+ env: {
128
+ ...process.env,
129
+ USE_REGIONAL_BLOBS: 'true',
130
+ NITRO_PORT: '9000'
131
+ },
132
+ meta: {
133
+ // Custom metadata
134
+ },
135
+ functions: {
136
+ // Function-specific settings
137
+ },
138
+ constants: {
139
+ PUBLISH_DIR: 'dist'
140
+ }
141
+ }
142
+ ```
143
+
144
+ ### Environment Variables
145
+
146
+ - `USE_REGIONAL_BLOBS`: Enable regional blob storage (default: true)
147
+ - `NITRO_PORT`: Development server port (default: 9000)
148
+ - `NITRO_PUBLIC_DIR`: Static assets directory
149
+
150
+ ## 🎯 Monorepo Support
151
+
152
+ For monorepo projects, the package automatically detects the structure and uses optimized templates:
153
+
154
+ ```javascript
155
+ // Automatic detection of monorepo structure
156
+ // Uses nuxt-handler-monorepo.tmpl.js for complex setups
157
+ // Handles working directory changes and path resolution
158
+ ```
159
+
160
+ ## 🔧 Development
161
+
162
+ ### Local Testing
163
+
164
+ The package includes a development server for local testing:
165
+
166
+ ```bash
167
+ # Start development server
168
+ npm run start
169
+ ```
170
+
171
+ Your Nuxt app will be available at `http://localhost:9000`
172
+
173
+ ### Build Scripts
174
+
175
+ ```json
176
+ {
177
+ "scripts": {
178
+ "build": "node ./tools/build.js",
179
+ "build:watch": "node ./tools/build.js --watch",
180
+ "start": "node dist/index.js",
181
+ "test": "ts-node src/test.ts"
182
+ }
183
+ }
184
+ ```
185
+
186
+ ## 📊 Performance Features
187
+
188
+ - **Static Asset Optimization**: 1-year cache headers for static files
189
+ - **Lazy Loading**: Nitro app initialization on first request
190
+ - **OpenTelemetry Tracing**: Built-in performance monitoring
191
+ - **Error Handling**: Graceful fallbacks and error recovery
192
+ - **Memory Management**: Efficient memory usage with LRU caching
193
+
194
+ ## 🚨 Compatibility
195
+
196
+ ### Supported Features
197
+ - ✅ Nuxt 3.8.0+
198
+ - ✅ Server-Side Rendering (SSR)
199
+ - ✅ Static Site Generation (SSG)
200
+ - ✅ API Routes
201
+ - ✅ Middleware
202
+ - ✅ Plugins
203
+ - ✅ Monorepo structures
204
+
205
+ ### Known Limitations
206
+ - ❌ `@nuxt/image` module (under development)
207
+ - ⚠️ Nuxt versions below 3.8.0 (compatibility in progress)
208
+
209
+ ## 🛠️ Troubleshooting
210
+
211
+ ### Common Issues
212
+
213
+ 1. **Build Fails**: Ensure Nuxt version is 3.8.0 or higher
214
+ 2. **Module Conflicts**: Check for unsupported modules like `@nuxt/image`
215
+ 3. **Path Issues**: Verify your project structure matches expected layout
216
+
217
+ ### Debug Mode
218
+
219
+ Enable detailed logging:
220
+
221
+ ```bash
222
+ DEBUG=edgeone:* npm run build
223
+ ```
224
+
225
+ ## 📝 API Reference
226
+
227
+ ### Core Functions
228
+
229
+ #### `onPreBuild(options: BuildOptions)`
230
+ Prepares the project for EdgeOne deployment.
231
+
232
+ #### `onBuild(options: BuildOptions)`
233
+ Executes the main build process.
234
+
235
+ #### `onPostBuild(options: BuildOptions)`
236
+ Cleanup and finalization.
237
+
238
+ ### Types
239
+
240
+ ```typescript
241
+ interface BuildOptions {
242
+ cwd: string
243
+ env: any
244
+ meta: any
245
+ functions: any
246
+ constants: {
247
+ PUBLISH_DIR: string
248
+ }
249
+ }
250
+ ```
251
+
252
+ ## 🤝 Contributing
253
+
254
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
255
+
256
+ ## 📄 License
257
+
258
+ ISC License - see [LICENSE](LICENSE) file for details.
259
+
260
+ ## 🔗 Links
261
+
262
+ - [GitHub Repository](https://github.com/q153877011/edgeone-nuxt-deploy)
263
+ - [Issues](https://github.com/q153877011/edgeone-nuxt-deploy/issues)
264
+ - [Tencent Cloud EdgeOne](https://cloud.tencent.com/product/eo)
265
+ - [Nuxt 3 Documentation](https://nuxt.com)
266
+
267
+ ## 📞 Support
268
+
269
+ - 📧 Create an issue on GitHub
270
+ - 💬 Community discussions
271
+ - 📖 Check the documentation
272
+
273
+ ---
274
+
275
+ Made with ❤️ by [Venzil](https://github.com/q153877011) for the Nuxt community.
@@ -0,0 +1,18 @@
1
+
2
+ var require = await (async () => {
3
+ var { createRequire } = await import("node:module");
4
+ return createRequire(import.meta.url);
5
+ })();
6
+
7
+ import {
8
+ RUN_CONFIG_FILE,
9
+ copyNuxtServerCode,
10
+ verifyNuxtHandlerDirStructure
11
+ } from "../../esm-chunks/chunk-NJ4SUJNF.js";
12
+ import "../../esm-chunks/chunk-V2LFVP3C.js";
13
+ import "../../esm-chunks/chunk-6BT4RYQJ.js";
14
+ export {
15
+ RUN_CONFIG_FILE,
16
+ copyNuxtServerCode,
17
+ verifyNuxtHandlerDirStructure
18
+ };
@@ -0,0 +1,17 @@
1
+
2
+ var require = await (async () => {
3
+ var { createRequire } = await import("node:module");
4
+ return createRequire(import.meta.url);
5
+ })();
6
+
7
+ import {
8
+ addNitroBuildOutputConfig,
9
+ resetNitroConfig
10
+ } from "../../esm-chunks/chunk-MMMRMLH2.js";
11
+ import "../../esm-chunks/chunk-V2LFVP3C.js";
12
+ import "../../esm-chunks/chunk-GX4Z7KQX.js";
13
+ import "../../esm-chunks/chunk-6BT4RYQJ.js";
14
+ export {
15
+ addNitroBuildOutputConfig,
16
+ resetNitroConfig
17
+ };
@@ -0,0 +1,19 @@
1
+
2
+ var require = await (async () => {
3
+ var { createRequire } = await import("node:module");
4
+ return createRequire(import.meta.url);
5
+ })();
6
+
7
+ import {
8
+ clearStaleServerHandlers,
9
+ createServerHandler,
10
+ patchNitroHandler
11
+ } from "../../esm-chunks/chunk-QG7JLDXY.js";
12
+ import "../../esm-chunks/chunk-NJ4SUJNF.js";
13
+ import "../../esm-chunks/chunk-V2LFVP3C.js";
14
+ import "../../esm-chunks/chunk-6BT4RYQJ.js";
15
+ export {
16
+ clearStaleServerHandlers,
17
+ createServerHandler,
18
+ patchNitroHandler
19
+ };
@@ -0,0 +1,18 @@
1
+
2
+ var require = await (async () => {
3
+ var { createRequire } = await import("node:module");
4
+ return createRequire(import.meta.url);
5
+ })();
6
+
7
+ import {
8
+ EDGE_HANDLER_NAME,
9
+ PluginContext,
10
+ SERVER_HANDLER_NAME
11
+ } from "../esm-chunks/chunk-RPSYO4VM.js";
12
+ import "../esm-chunks/chunk-GX4Z7KQX.js";
13
+ import "../esm-chunks/chunk-6BT4RYQJ.js";
14
+ export {
15
+ EDGE_HANDLER_NAME,
16
+ PluginContext,
17
+ SERVER_HANDLER_NAME
18
+ };
@@ -0,0 +1,18 @@
1
+
2
+ var require = await (async () => {
3
+ var { createRequire } = await import("node:module");
4
+ return createRequire(import.meta.url);
5
+ })();
6
+
7
+ import {
8
+ convertNuxtRoutePattern,
9
+ createNuxtApiRoutesMeta,
10
+ createNuxtPagesRouteMeta
11
+ } from "../esm-chunks/chunk-6YERJDAJ.js";
12
+ import "../esm-chunks/chunk-GX4Z7KQX.js";
13
+ import "../esm-chunks/chunk-6BT4RYQJ.js";
14
+ export {
15
+ convertNuxtRoutePattern,
16
+ createNuxtApiRoutesMeta,
17
+ createNuxtPagesRouteMeta
18
+ };
@@ -0,0 +1,305 @@
1
+ import { resolve, dirname } from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import { readFileSync, existsSync, statSync } from 'fs';
4
+ import { extname } from 'path';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+
9
+ // Static assets directory
10
+ const ASSET_DIR = resolve(__dirname, '../assets');
11
+
12
+ // MIME type mapping
13
+ const MIME_TYPES = {
14
+ '.html': 'text/html; charset=utf-8',
15
+ '.js': 'application/javascript',
16
+ '.css': 'text/css',
17
+ '.json': 'application/json',
18
+ '.png': 'image/png',
19
+ '.jpg': 'image/jpeg',
20
+ '.jpeg': 'image/jpeg',
21
+ '.gif': 'image/gif',
22
+ '.svg': 'image/svg+xml',
23
+ '.ico': 'image/x-icon',
24
+ '.txt': 'text/plain',
25
+ '.xml': 'application/xml'
26
+ };
27
+
28
+ /**
29
+ * Get the MIME type of a file
30
+ */
31
+ function getMimeType(filePath) {
32
+ const ext = extname(filePath).toLowerCase();
33
+ return MIME_TYPES[ext] || 'application/octet-stream';
34
+ }
35
+
36
+ /**
37
+ * Handle HTTP response
38
+ */
39
+ async function handleResponse(response, req, res, context) {
40
+ if (!response) {
41
+ res.statusCode = 500;
42
+ res.setHeader('Content-Type', 'text/plain');
43
+ res.end('Server Error');
44
+ return;
45
+ }
46
+
47
+ // Ensure response.headers is a Headers object
48
+ if (!(response.headers instanceof Headers)) {
49
+ response.headers = new Headers(response.headers || {});
50
+ }
51
+
52
+ // Correctly iterate over Headers object (using entries() method)
53
+ for (const [key, value] of response.headers.entries()) {
54
+ res.setHeader(key, value);
55
+ }
56
+
57
+ // Check if Content-Type already exists (case-insensitive)
58
+ const hasContentType = response.headers.has('content-type');
59
+
60
+ // Only set default value if Content-Type is missing
61
+ if (!hasContentType) {
62
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
63
+ }
64
+ // try {
65
+ // res.setHeader('functions-request-id', context.headers['x-scf-request-id']);
66
+ // } catch (error) {
67
+ // console.error('Handle response error:', error);
68
+ // }
69
+
70
+ res.setHeader('from-server', 'true');
71
+
72
+ // Handle set-cookie header (special handling, as there may be multiple values)
73
+ if (response.headers.has('set-cookie')) {
74
+ const cookieArr = response.headers.getSetCookie();
75
+ res.setHeader('set-cookie', Array.isArray(cookieArr) ? cookieArr : [cookieArr]);
76
+ // headers['set-cookie'] = Array.isArray(cookieArr) ? cookieArr : [cookieArr];
77
+ }
78
+
79
+ res.statusCode = response.status || response.statusCode || 200;
80
+ if(Buffer.isBuffer(response.body)) {
81
+ res.end(response.body);
82
+ } else if(response.body && typeof response.body === 'object' && typeof response.body.getReader === 'function') {
83
+ const reader = response.body.getReader();
84
+ const chunks = [];
85
+ while (true) {
86
+ const { done, value } = await reader.read();
87
+ if (done) break;
88
+ chunks.push(Buffer.from(value));
89
+ }
90
+ res.end(Buffer.concat(chunks));
91
+ } else {
92
+ res.end(response.body || response._data || '');
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Lazy load Nitro application
98
+ */
99
+ let nitroApp = null;
100
+ async function getNitroApp() {
101
+ if (!nitroApp) {
102
+ // Set environment variables to prevent automatic server startup
103
+ process.env.NITRO_PORT = '';
104
+ process.env.PORT = '';
105
+
106
+ // Set correct static assets path
107
+ process.env.NITRO_PUBLIC_DIR = ASSET_DIR;
108
+
109
+ const { {{USE_NITRO_APP_SYMBOL}}: useNitroApp } = await import('./chunks/nitro/nitro.mjs');
110
+ nitroApp = useNitroApp();
111
+ }
112
+ return nitroApp;
113
+ }
114
+
115
+ /**
116
+ * Handle HTTP response
117
+ */
118
+ function handleResponse(response, event) {
119
+ if (!response) {
120
+ return {
121
+ statusCode: 500,
122
+ headers: { 'Content-Type': 'text/plain' },
123
+ body: 'Internal Server Error'
124
+ };
125
+ }
126
+
127
+ const headers = {};
128
+
129
+ // Ensure response.headers is a Headers object
130
+ if (!(response.headers instanceof Headers)) {
131
+ response.headers = new Headers(response.headers || {});
132
+ }
133
+
134
+ // Correctly iterate over Headers object (using entries() method)
135
+ for (const [key, value] of response.headers.entries()) {
136
+ headers[key] = value;
137
+ }
138
+
139
+ // Check if Content-Type already exists (case-insensitive)
140
+ const hasContentType = response.headers.has('content-type');
141
+
142
+ // Only set default value if Content-Type is missing
143
+ if (!hasContentType) {
144
+ headers['Content-Type'] = 'text/html; charset=utf-8';
145
+ }
146
+
147
+ headers['from-server'] = 'true';
148
+ headers['functions-request-id'] = event.headers['x-scf-request-id'];
149
+
150
+ // Handle set-cookie header (special handling, as there may be multiple values)
151
+ if (response.headers.has('set-cookie')) {
152
+ const cookieArr = response.headers.getSetCookie();
153
+ headers['set-cookie'] = Array.isArray(cookieArr) ? cookieArr : [cookieArr];
154
+ }
155
+
156
+ return {
157
+ statusCode: response.status || response.statusCode || 200,
158
+ headers,
159
+ body: response.body || response._data || ''
160
+ };
161
+ }
162
+
163
+ /**
164
+ * EdgeOne function handler
165
+ */
166
+ export async function handler(event) {
167
+ try {
168
+ const url = event.path || '/';
169
+ const method = event.httpMethod || event.method || 'GET';
170
+ const headers = event.headers || {};
171
+ const body = event.body || '';
172
+
173
+ // First try to handle static assets
174
+ if (method === 'GET') {
175
+ const staticResponse = handleStaticFile(url);
176
+ if (staticResponse) {
177
+ return staticResponse;
178
+ }
179
+ }
180
+
181
+ // Handle dynamic requests
182
+ const app = await getNitroApp();
183
+
184
+ try {
185
+ const response = await app.localCall({
186
+ url,
187
+ method,
188
+ headers,
189
+ body
190
+ });
191
+
192
+ return handleResponse(response, event);
193
+ } catch (nitroError) {
194
+ // Handle Nitro static file read errors (prerender files not found)
195
+ // Check error and its cause property (H3Error may wrap actual error in cause)
196
+ const actualError = nitroError?.cause || nitroError;
197
+ const errorPath = actualError?.path || nitroError?.path;
198
+ const errorCode = actualError?.code || nitroError?.code;
199
+
200
+ // If error is due to prerender static file not found, try dynamic rendering
201
+ if (errorCode === 'ENOENT' &&
202
+ errorPath &&
203
+ (errorPath.includes('/assets/') || errorPath.includes('assets/')) &&
204
+ (errorPath.includes('/index.html') || errorPath.includes('index.html'))) {
205
+ console.warn(`Prerender file not found: ${errorPath}, falling back to dynamic rendering for ${url}`);
206
+
207
+ // If static file handling has been tried but file not found, should perform dynamic rendering
208
+ // Nitro should be able to handle dynamic routes, but if it still tries to read static files,
209
+ // it may be due to configuration issues. We throw an error directly to let user know to build or check configuration
210
+ throw new Error(`Prerender route ${url} not found. Make sure to run build first or configure routeRules correctly. Original error: ${actualError?.message || nitroError?.message}`);
211
+ }
212
+
213
+ // Other errors are thrown directly
214
+ throw nitroError;
215
+ }
216
+ } catch (error) {
217
+ console.error('EdgeOne handler error:', error);
218
+ return {
219
+ statusCode: 500,
220
+ headers: { 'Content-Type': 'text/plain' },
221
+ body: `Internal Server Error: ${error.message}`
222
+ };
223
+ }
224
+ }
225
+
226
+ import('http').then(async (http) => {
227
+ const { createServer } = http;
228
+ // Dynamically import stream module to handle ReadableStream
229
+ await import('stream').then(({ Readable, pipeline }) => {
230
+ const server = createServer(async (req, res) => {
231
+ try {
232
+ const event = {
233
+ path: req.url,
234
+ httpMethod: req.method,
235
+ headers: req.headers,
236
+ body: ''
237
+ };
238
+
239
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
240
+ const chunks = [];
241
+ for await (const chunk of req) {
242
+ chunks.push(chunk);
243
+ }
244
+ event.body = Buffer.concat(chunks).toString();
245
+ }
246
+
247
+ const result = await handler(event);
248
+
249
+ res.statusCode = result.statusCode;
250
+ // Object.entries(result.headers).forEach(([key, value]) => {
251
+ // if(key === 'set-cookie') {
252
+ res.setHeader('set-cookie', Array.isArray(value) ? value[0].split(',') : value);
253
+ // } else {
254
+ // res.setHeader(key, value);
255
+ // }
256
+ // });
257
+ // debug message
258
+ // res.setHeader('functions-request-id', req.headers['x-scf-request-id']);
259
+
260
+
261
+ // Handle response body: support Buffer, string, and ReadableStream
262
+ if (Buffer.isBuffer(result.body)) {
263
+ res.end(result.body);
264
+ } else if (result.body && typeof result.body === 'object' && typeof result.body.getReader === 'function') {
265
+ // Detect ReadableStream (Web Streams API)
266
+ try {
267
+ const nodeStream = Readable.fromWeb(result.body);
268
+ nodeStream.pipe(res);
269
+ } catch (streamError) {
270
+ console.error('Stream conversion error:', streamError);
271
+ // If conversion fails, try to read the entire stream
272
+ const reader = result.body.getReader();
273
+ const chunks = [];
274
+ try {
275
+ while (true) {
276
+ const { done, value } = await reader.read();
277
+ if (done) break;
278
+ chunks.push(Buffer.from(value));
279
+ }
280
+ res.end(Buffer.concat(chunks));
281
+ } catch (readError) {
282
+ console.error('Stream read error:', readError);
283
+ res.end();
284
+ }
285
+ }
286
+ } else {
287
+ // Handle string or other types
288
+ res.end(result.body || '');
289
+ }
290
+ } catch (error) {
291
+ console.error('Local server error:', error);
292
+ res.statusCode = 500;
293
+ res.setHeader('Content-Type', 'text/plain');
294
+ res.end(`Server Error: ${error.message}`);
295
+ }
296
+ });
297
+
298
+ const port = process.env.DEV_PORT || 9000;
299
+ server.listen(port, () => {
300
+ console.log(`EdgeOne development server running at http://localhost:${port}`);
301
+ console.log(`Static assets served from: ${ASSET_DIR}`);
302
+ });
303
+ });
304
+ });
305
+