@openworkers/adapter-sveltekit 0.2.3 → 0.3.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.
package/README.md CHANGED
@@ -17,19 +17,77 @@ import adapter from '@openworkers/adapter-sveltekit';
17
17
  export default {
18
18
  kit: {
19
19
  adapter: adapter({
20
- out: 'dist' // default
20
+ out: 'dist', // Output directory (default: 'dist')
21
+ functions: false // Generate mini-workers for API routes (default: false)
21
22
  })
22
23
  }
23
24
  };
24
25
  ```
25
26
 
27
+ ## Options
28
+
29
+ | Option | Type | Default | Description |
30
+ |--------|------|---------|-------------|
31
+ | `out` | `string` | `'dist'` | Output directory for the build |
32
+ | `functions` | `boolean` | `false` | Generate separate mini-workers for each API route |
33
+
26
34
  ## Output
27
35
 
28
36
  ```
29
37
  dist/
30
- worker.js # Worker entry point
31
- routes.js # Route manifest for edge routing
32
- assets/ # Static assets and prerendered pages
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)
42
+ ├── api-hello.js
43
+ └── api-users.js
44
+ ```
45
+
46
+ ## Functions Mode
47
+
48
+ When `functions: true`, the adapter generates a separate mini-worker for each `+server.ts` endpoint:
49
+
50
+ - `/api/hello/+server.ts` → `functions/api-hello.js`
51
+ - `/api/users/+server.ts` → `functions/api-users.js`
52
+
53
+ The route mappings are included in `routes.js`:
54
+
55
+ ```js
56
+ {
57
+ "functions": [
58
+ { "pattern": "/api/hello", "worker": "functions/api-hello.js" },
59
+ { "pattern": "/api/users", "worker": "functions/api-users.js" }
60
+ ]
61
+ }
62
+ ```
63
+
64
+ This prepares for native project routing in the OpenWorkers runner, where each function can be deployed as a separate worker for better isolation and scaling.
65
+
66
+ ## TypeScript
67
+
68
+ For proper types on `platform.env`, add `@openworkers/workers-types`:
69
+
70
+ ```bash
71
+ bun add -d @openworkers/workers-types
72
+ ```
73
+
74
+ Then in `src/app.d.ts`:
75
+
76
+ ```ts
77
+ /// <reference types="@openworkers/workers-types" />
78
+
79
+ declare global {
80
+ namespace App {
81
+ interface Platform {
82
+ env: {
83
+ KV: BindingKV;
84
+ ASSETS: BindingAssets;
85
+ };
86
+ }
87
+ }
88
+ }
89
+
90
+ export {};
33
91
  ```
34
92
 
35
93
  ## License
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Mini-worker template for OpenWorkers Functions.
3
+ * Wraps a SvelteKit API endpoint as a standalone worker.
4
+ */
5
+
6
+ import * as handlers from 'ENDPOINT';
7
+
8
+ 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
+ }
46
+ };
package/index.d.ts CHANGED
@@ -6,6 +6,13 @@ export interface AdapterOptions {
6
6
  * @default 'dist'
7
7
  */
8
8
  out?: string;
9
+
10
+ /**
11
+ * Generate separate mini-workers for each API route.
12
+ * Outputs to `dist/functions/` with routing info in `routes.js`.
13
+ * @default false
14
+ */
15
+ functions?: boolean;
9
16
  }
10
17
 
11
18
  export default function plugin(options?: AdapterOptions): Adapter;
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, writeFileSync } from 'node:fs';
1
+ import { existsSync, writeFileSync, readdirSync, statSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { build } from 'esbuild';
@@ -13,6 +13,7 @@ export default function (options = {}) {
13
13
  async adapt(builder) {
14
14
  const dest = options.out ?? 'dist';
15
15
  const assetsDir = `${dest}/assets`;
16
+ const functionsEnabled = options.functions ?? false;
16
17
 
17
18
  const files = fileURLToPath(new URL('./files', import.meta.url).href);
18
19
  const tmp = builder.getBuildDirectory('openworkers-tmp');
@@ -34,6 +35,7 @@ export default function (options = {}) {
34
35
 
35
36
  // Generate 404.html fallback
36
37
  const fallback = path.join(assetsDir, '404.html');
38
+
37
39
  if (!existsSync(fallback)) {
38
40
  writeFileSync(fallback, 'Not Found');
39
41
  }
@@ -58,7 +60,7 @@ export default function (options = {}) {
58
60
  `export { Server, manifest, prerendered, base_path };\n`
59
61
  );
60
62
 
61
- // Bundle worker with esbuild
63
+ // Bundle main worker with esbuild
62
64
  const workerDest = `${dest}/worker.js`;
63
65
  const shimAsyncHooks = posixify(path.resolve(files, 'shims/async_hooks.js'));
64
66
 
@@ -80,6 +82,91 @@ export default function (options = {}) {
80
82
  }
81
83
  });
82
84
 
85
+ // Generate mini-workers for API routes if enabled
86
+ /** @type {Array<{pattern: string, worker: string}>} */
87
+ const functions = [];
88
+
89
+ if (functionsEnabled) {
90
+ builder.mkdirp(`${dest}/functions`);
91
+
92
+ const endpointsDir = path.join(builder.getServerDirectory(), 'entries/endpoints');
93
+
94
+ if (existsSync(endpointsDir)) {
95
+ const endpoints = findEndpoints(endpointsDir);
96
+
97
+ for (const endpoint of endpoints) {
98
+ const routePattern = endpoint.route;
99
+ const workerName = routePattern.replace(/\//g, '-').replace(/^-/, '') || 'index';
100
+ const workerFile = `functions/${workerName}.js`;
101
+
102
+ // Create entry point for this function
103
+ const functionEntry = `${tmp}/function-${workerName}.js`;
104
+ const endpointPath = posixify(endpoint.file);
105
+
106
+ writeFileSync(
107
+ functionEntry,
108
+ `import * as handlers from '${endpointPath}';\n` +
109
+ `export default {\n` +
110
+ ` async fetch(req, env, ctx) {\n` +
111
+ ` globalThis.env = env;\n` +
112
+ ` const method = req.method;\n` +
113
+ ` const handler = handlers[method];\n` +
114
+ ` if (!handler) {\n` +
115
+ ` return new Response('Method Not Allowed', {\n` +
116
+ ` status: 405,\n` +
117
+ ` headers: { Allow: Object.keys(handlers).join(', ') }\n` +
118
+ ` });\n` +
119
+ ` }\n` +
120
+ ` const url = new URL(req.url);\n` +
121
+ ` const event = {\n` +
122
+ ` request: req,\n` +
123
+ ` url,\n` +
124
+ ` params: ctx.params ?? {},\n` +
125
+ ` platform: { env, ctx },\n` +
126
+ ` getClientAddress() {\n` +
127
+ ` return req.headers.get('x-real-ip') ?? req.headers.get('x-forwarded-for') ?? '';\n` +
128
+ ` }\n` +
129
+ ` };\n` +
130
+ ` try {\n` +
131
+ ` return await handler(event);\n` +
132
+ ` } catch (error) {\n` +
133
+ ` console.error('[Function] Error:', error);\n` +
134
+ ` return new Response(JSON.stringify({ error: 'Internal Server Error' }), {\n` +
135
+ ` status: 500,\n` +
136
+ ` headers: { 'Content-Type': 'application/json' }\n` +
137
+ ` });\n` +
138
+ ` }\n` +
139
+ ` }\n` +
140
+ `};\n`
141
+ );
142
+
143
+ // Bundle the function
144
+ await build({
145
+ entryPoints: [functionEntry],
146
+ bundle: true,
147
+ format: 'esm',
148
+ platform: 'browser',
149
+ outfile: `${dest}/${workerFile}`,
150
+ alias: {
151
+ 'node:async_hooks': shimAsyncHooks
152
+ },
153
+ external: ['node:*'],
154
+ minify: false,
155
+ banner: {
156
+ js: `// Generated by ${name} - Function: ${routePattern}\n`
157
+ }
158
+ });
159
+
160
+ functions.push({
161
+ pattern: routePattern,
162
+ worker: workerFile
163
+ });
164
+
165
+ builder.log.minor(` Generated function: ${routePattern} → ${workerFile}`);
166
+ }
167
+ }
168
+ }
169
+
83
170
  // Generate routes.js for edge routing
84
171
  const routes = {
85
172
  // Immutable assets (hashed filenames, cache forever)
@@ -88,6 +175,8 @@ export default function (options = {}) {
88
175
  static: staticFiles.map((f) => `/${f}`),
89
176
  // Prerendered pages (serve from assets, no worker needed)
90
177
  prerendered: builder.prerendered.paths,
178
+ // Functions (API routes as separate workers)
179
+ functions: functions,
91
180
  // Everything else -> SSR
92
181
  ssr: ['/*']
93
182
  };
@@ -101,11 +190,43 @@ export default function (options = {}) {
101
190
 
102
191
  builder.log.minor(`Wrote ${workerDest}`);
103
192
  builder.log.minor(`Wrote ${dest}/routes.js`);
193
+
194
+ if (functions.length > 0) {
195
+ builder.log.minor(`Wrote ${functions.length} function workers to ${dest}/functions/`);
196
+ }
197
+
104
198
  builder.log.minor(`Wrote ${assetsDir} (${builder.prerendered.paths.length} prerendered pages)`);
105
199
  }
106
200
  };
107
201
  }
108
202
 
203
+ /**
204
+ * Find all endpoint files in the server output
205
+ * @param {string} dir
206
+ * @param {string} [base='']
207
+ * @returns {Array<{route: string, file: string}>}
208
+ */
209
+ function findEndpoints(dir, base = '') {
210
+ const results = [];
211
+ const entries = readdirSync(dir);
212
+
213
+ for (const entry of entries) {
214
+ const fullPath = path.join(dir, entry);
215
+ const stat = statSync(fullPath);
216
+
217
+ if (stat.isDirectory()) {
218
+ results.push(...findEndpoints(fullPath, `${base}/${entry}`));
219
+ } else if (entry === '_server.ts.js' || entry === '_server.js.js') {
220
+ results.push({
221
+ route: base || '/',
222
+ file: fullPath
223
+ });
224
+ }
225
+ }
226
+
227
+ return results;
228
+ }
229
+
109
230
  /** @param {string} str */
110
231
  function posixify(str) {
111
232
  return str.replace(/\\/g, '/');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openworkers/adapter-sveltekit",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "SvelteKit adapter for OpenWorkers",
5
5
  "keywords": [
6
6
  "adapter",