@openworkers/adapter-sveltekit 0.3.4 → 0.3.6

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 CHANGED
@@ -35,10 +35,10 @@ export default {
35
35
 
36
36
  ```
37
37
  dist/
38
- ├── worker.js # Main SSR worker
39
- ├── routes.js # Route manifest for edge routing
40
- ├── assets/ # Static assets and prerendered pages
41
- └── functions/ # Mini-workers for API routes (if functions: true)
38
+ ├── _worker.js # Main SSR worker
39
+ ├── _routes.json # Route manifest for edge routing
40
+ ├── assets/ # Static assets and prerendered pages
41
+ └── functions/ # Mini-workers for API routes (if functions: true)
42
42
  ├── api-hello.js
43
43
  └── api-users.js
44
44
  ```
@@ -50,7 +50,7 @@ When `functions: true`, the adapter generates a separate mini-worker for each `+
50
50
  - `/api/hello/+server.ts` → `functions/api-hello.js`
51
51
  - `/api/users/+server.ts` → `functions/api-users.js`
52
52
 
53
- The route mappings are included in `routes.js`:
53
+ The route mappings are included in `_routes.json`:
54
54
 
55
55
  ```js
56
56
  {
@@ -5,42 +5,86 @@
5
5
 
6
6
  import * as handlers from 'ENDPOINT';
7
7
 
8
+ /**
9
+ * Extract route params from URL pathname based on SvelteKit route pattern
10
+ * @param {string} pathname - URL pathname (e.g., "/stream/42")
11
+ * @param {string} pattern - SvelteKit route pattern (e.g., "/stream/[n]")
12
+ * @returns {Record<string, string>} - Extracted params (e.g., {n: "42"})
13
+ */
14
+ function extractParams(pathname, pattern) {
15
+ const params = {};
16
+ const patternSegments = pattern.split('/').filter(Boolean);
17
+ const pathSegments = pathname.split('/').filter(Boolean);
18
+
19
+ for (let i = 0; i < patternSegments.length; i++) {
20
+ const patternSegment = patternSegments[i];
21
+
22
+ // Rest parameter: [...rest]
23
+ if (patternSegment.startsWith('[...') && patternSegment.endsWith(']')) {
24
+ const paramName = patternSegment.slice(4, -1);
25
+ params[paramName] = pathSegments.slice(i).join('/');
26
+ break;
27
+ }
28
+
29
+ // Optional parameter: [[optional]]
30
+ if (patternSegment.startsWith('[[') && patternSegment.endsWith(']]')) {
31
+ const paramName = patternSegment.slice(2, -2);
32
+ if (i < pathSegments.length) {
33
+ params[paramName] = pathSegments[i];
34
+ }
35
+ continue;
36
+ }
37
+
38
+ // Regular parameter: [param]
39
+ if (patternSegment.startsWith('[') && patternSegment.endsWith(']')) {
40
+ const paramName = patternSegment.slice(1, -1);
41
+ params[paramName] = pathSegments[i];
42
+ continue;
43
+ }
44
+ }
45
+
46
+ return params;
47
+ }
48
+
8
49
  export default {
9
- async fetch(req, env, ctx) {
10
- globalThis.env = env;
11
-
12
- const method = req.method;
13
- const handler = handlers[method];
14
-
15
- if (!handler) {
16
- return new Response('Method Not Allowed', {
17
- status: 405,
18
- headers: { Allow: Object.keys(handlers).join(', ') }
19
- });
20
- }
21
-
22
- const url = new URL(req.url);
23
-
24
- // Build a minimal RequestEvent-like object
25
- const event = {
26
- request: req,
27
- url,
28
- params: ctx.params ?? {},
29
- platform: { env, ctx },
30
- getClientAddress() {
31
- return req.headers.get('x-real-ip') ?? req.headers.get('x-forwarded-for') ?? '';
32
- }
33
- };
34
-
35
- try {
36
- return await handler(event);
37
- } catch (error) {
38
- console.error(`[Function] Error in ${method} handler:`, error);
39
-
40
- return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
41
- status: 500,
42
- headers: { 'Content-Type': 'application/json' }
43
- });
44
- }
45
- }
50
+ async fetch(req, env, ctx) {
51
+ globalThis.env = env;
52
+
53
+ const method = req.method;
54
+ const handler = handlers[method];
55
+
56
+ if (!handler) {
57
+ return new Response('Method Not Allowed', {
58
+ status: 405,
59
+ headers: { Allow: Object.keys(handlers).join(', ') }
60
+ });
61
+ }
62
+
63
+ const url = new URL(req.url);
64
+
65
+ // Extract params from URL based on route pattern
66
+ const params = extractParams(url.pathname, ROUTE_PATTERN);
67
+
68
+ // Build a minimal RequestEvent-like object
69
+ const event = {
70
+ request: req,
71
+ url,
72
+ params,
73
+ platform: { env, ctx },
74
+ getClientAddress() {
75
+ return req.headers.get('x-real-ip') ?? req.headers.get('x-forwarded-for') ?? '';
76
+ }
77
+ };
78
+
79
+ try {
80
+ return await handler(event);
81
+ } catch (error) {
82
+ console.error(`[Function] Error in ${method} handler:`, error);
83
+
84
+ return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
85
+ status: 500,
86
+ headers: { 'Content-Type': 'application/json' }
87
+ });
88
+ }
89
+ }
46
90
  };
@@ -6,36 +6,36 @@
6
6
  */
7
7
 
8
8
  export class AsyncLocalStorage {
9
- #store;
10
-
11
- run(store, fn, ...args) {
12
- this.#store = store;
13
- return fn(...args);
14
- }
15
-
16
- getStore() {
17
- return this.#store;
18
- }
19
-
20
- // Stubs for API completeness
21
- enterWith(store) {
22
- this.#store = store;
23
- }
24
-
25
- exit(fn, ...args) {
26
- this.#store = undefined;
27
- return fn(...args);
28
- }
29
-
30
- disable() {
31
- this.#store = undefined;
32
- }
33
-
34
- static bind(fn) {
35
- return fn;
36
- }
37
-
38
- static snapshot() {
39
- return (fn, ...args) => fn(...args);
40
- }
9
+ #store;
10
+
11
+ run(store, fn, ...args) {
12
+ this.#store = store;
13
+ return fn(...args);
14
+ }
15
+
16
+ getStore() {
17
+ return this.#store;
18
+ }
19
+
20
+ // Stubs for API completeness
21
+ enterWith(store) {
22
+ this.#store = store;
23
+ }
24
+
25
+ exit(fn, ...args) {
26
+ this.#store = undefined;
27
+ return fn(...args);
28
+ }
29
+
30
+ disable() {
31
+ this.#store = undefined;
32
+ }
33
+
34
+ static bind(fn) {
35
+ return fn;
36
+ }
37
+
38
+ static snapshot() {
39
+ return (fn, ...args) => fn(...args);
40
+ }
41
41
  }
package/index.js CHANGED
@@ -91,15 +91,18 @@ export default function (options = {}) {
91
91
  if (functionsEnabled) {
92
92
  builder.mkdirp(`${dest}/functions`);
93
93
 
94
- const endpointsDir = path.join(builder.getServerDirectory(), 'entries/endpoints');
94
+ const serverDir = builder.getServerDirectory();
95
+ const viteManifestPath = path.join(serverDir, '.vite/manifest.json');
95
96
  const functionTemplate = posixify(path.resolve(files, 'function-worker.js'));
96
97
 
97
- if (existsSync(endpointsDir)) {
98
- const endpoints = findEndpoints(endpointsDir);
98
+ if (existsSync(viteManifestPath)) {
99
+ const viteManifest = JSON.parse(readFileSync(viteManifestPath, 'utf-8'));
100
+ const endpoints = extractEndpointsFromManifest(viteManifest, serverDir);
99
101
 
100
102
  for (const endpoint of endpoints) {
101
- const routePattern = endpoint.route;
102
- const workerName = routePattern.replace(/\//g, '-').replace(/^-/, '') || 'index';
103
+ const routePattern = endpoint.pattern;
104
+ // Use SvelteKit route syntax for worker filename
105
+ const workerName = endpoint.route.replace(/\//g, '-').replace(/^-/, '') || 'index';
103
106
  const workerFile = `functions/${workerName}.js`;
104
107
 
105
108
  // Bundle using the template with ENDPOINT alias
@@ -115,6 +118,9 @@ export default function (options = {}) {
115
118
  },
116
119
  external: ['node:*'],
117
120
  minify: false,
121
+ define: {
122
+ ROUTE_PATTERN: JSON.stringify(endpoint.route)
123
+ },
118
124
  banner: {
119
125
  js: `// Generated by ${name} v${version} - Function: ${routePattern}\n`
120
126
  }
@@ -162,30 +168,72 @@ export default function (options = {}) {
162
168
  }
163
169
 
164
170
  /**
165
- * Find all endpoint files in the server output
166
- * @param {string} dir
167
- * @param {string} [base='']
168
- * @returns {Array<{route: string, file: string}>}
171
+ * Extract endpoints from Vite manifest and convert SvelteKit routes to simple patterns
172
+ * @param {object} manifest - Vite manifest object
173
+ * @param {string} serverDir - Server build directory
174
+ * @returns {Array<{pattern: string, route: string, file: string}>}
169
175
  */
170
- function findEndpoints(dir, base = '') {
171
- const results = [];
172
- const entries = readdirSync(dir);
173
-
174
- for (const entry of entries) {
175
- const fullPath = path.join(dir, entry);
176
- const stat = statSync(fullPath);
177
-
178
- if (stat.isDirectory()) {
179
- results.push(...findEndpoints(fullPath, `${base}/${entry}`));
180
- } else if (entry === '_server.ts.js' || entry === '_server.js.js') {
181
- results.push({
182
- route: base || '/',
183
- file: fullPath
184
- });
185
- }
176
+ function extractEndpointsFromManifest(manifest, serverDir) {
177
+ const endpoints = [];
178
+
179
+ for (const [key, entry] of Object.entries(manifest)) {
180
+ // Only process endpoint files (+server.ts)
181
+ if (!key.includes('+server.ts')) continue;
182
+
183
+ const sourcePath = entry.src;
184
+ if (!sourcePath || !sourcePath.startsWith('src/routes/')) continue;
185
+
186
+ // Extract route from source path: src/routes/status/[code]/[[reason]]/+server.ts
187
+ const routePart = sourcePath
188
+ .replace(/^src\/routes/, '')
189
+ .replace(/\/\+server\.(ts|js)$/, '');
190
+
191
+ // Convert SvelteKit route syntax to simple wildcard patterns
192
+ const pattern = convertRouteToPattern(routePart);
193
+
194
+ // Get the built file path
195
+ const file = path.join(serverDir, entry.file);
196
+
197
+ endpoints.push({
198
+ pattern, // For routing: /status/*/*
199
+ route: routePart, // For filename: /status/[code]/[[reason]]
200
+ file
201
+ });
186
202
  }
187
203
 
188
- return results;
204
+ return endpoints;
205
+ }
206
+
207
+ /**
208
+ * Convert SvelteKit route syntax to simple wildcard patterns
209
+ * Examples:
210
+ * /status/[code]/[[reason]] becomes /status/star/star
211
+ * /range/[n] becomes /range/star
212
+ * /drip/[...params] becomes /drip/doublestar
213
+ * @param {string} route - SvelteKit route (e.g. "/status/[code]/[[reason]]")
214
+ * @returns {string} - Simple pattern with wildcards
215
+ */
216
+ function convertRouteToPattern(route) {
217
+ if (!route || route === '/') return '/';
218
+
219
+ const segments = route.split('/').filter(Boolean);
220
+ const patternSegments = segments.map(segment => {
221
+ // Rest parameter: [...params] -> **
222
+ if (segment.startsWith('[...') && segment.endsWith(']')) {
223
+ return '**';
224
+ }
225
+
226
+ // Required or optional parameter: [param] or [[param]] -> *
227
+ if ((segment.startsWith('[') && segment.endsWith(']')) ||
228
+ (segment.startsWith('[[') && segment.endsWith(']]'))) {
229
+ return '*';
230
+ }
231
+
232
+ // Static segment: keep as-is
233
+ return segment;
234
+ });
235
+
236
+ return '/' + patternSegments.join('/');
189
237
  }
190
238
 
191
239
  /** @param {string} str */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openworkers/adapter-sveltekit",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "SvelteKit adapter for OpenWorkers",
5
5
  "keywords": [
6
6
  "adapter",