@openworkers/adapter-sveltekit 0.3.6 → 0.4.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.
@@ -0,0 +1,66 @@
1
+ // src/function-worker.ts
2
+ import * as handlers from "ENDPOINT";
3
+ var worker = {
4
+ async fetch(req, env, ctx) {
5
+ globalThis.env = env;
6
+ const method = req.method;
7
+ const handler = handlers[method];
8
+ if (!handler) {
9
+ return new Response("Method Not Allowed", {
10
+ status: 405,
11
+ headers: { Allow: Object.keys(handlers).join(", ") }
12
+ });
13
+ }
14
+ const url = new URL(req.url);
15
+ let params = {};
16
+ if (WITH_PARAMS) {
17
+ const { extractParams } = await import("lib:routing");
18
+ params = extractParams(url);
19
+ }
20
+ const responseHeaders = new Headers();
21
+ let cookiesData;
22
+ if (WITH_COOKIES) {
23
+ const { getCookies } = await import("lib:cookies");
24
+ cookiesData = getCookies(req, url);
25
+ cookiesData.set_trailing_slash("ignore");
26
+ }
27
+ const event = {
28
+ fetch: globalThis.fetch.bind(globalThis),
29
+ request: req,
30
+ url,
31
+ params,
32
+ cookies: WITH_COOKIES && cookiesData?.cookies || void 0,
33
+ locals: {},
34
+ platform: { env, ctx },
35
+ getClientAddress() {
36
+ return req.headers.get("x-real-ip") ?? req.headers.get("x-forwarded-for") ?? "";
37
+ },
38
+ setHeaders(headers) {
39
+ for (const [key, value] of Object.entries(headers)) {
40
+ responseHeaders.set(key, String(value));
41
+ }
42
+ }
43
+ };
44
+ try {
45
+ const response = await handler(event);
46
+ if (WITH_COOKIES && cookiesData?.new_cookies) {
47
+ const { addCookiesToHeaders } = await import("lib:cookies");
48
+ addCookiesToHeaders(response.headers, cookiesData.new_cookies.values());
49
+ }
50
+ for (const [key, value] of responseHeaders.entries()) {
51
+ response.headers.append(key, value);
52
+ }
53
+ return response;
54
+ } catch (error) {
55
+ console.error(`[Function] Error in ${method} handler:`, error);
56
+ return new Response(JSON.stringify({ error: "Internal Server Error" }), {
57
+ status: 500,
58
+ headers: { "Content-Type": "application/json" }
59
+ });
60
+ }
61
+ }
62
+ };
63
+ var function_worker_default = worker;
64
+ export {
65
+ function_worker_default as default
66
+ };
package/dist/index.js ADDED
@@ -0,0 +1,349 @@
1
+ // src/index.ts
2
+ import { existsSync, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "node:fs";
3
+ import path2 from "node:path";
4
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
5
+ import { build as build2 } from "esbuild";
6
+
7
+ // src/lib/build-function.ts
8
+ import { build } from "esbuild";
9
+ import path from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ import { writeFileSync, unlinkSync, mkdtempSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+
14
+ // src/lib/detect-cookies.ts
15
+ import { readFileSync } from "node:fs";
16
+ function detectCookiesUsage(filePath) {
17
+ try {
18
+ let code = readFileSync(filePath, "utf-8");
19
+ code = code.replace(/\/\/.*$/gm, "");
20
+ code = code.replace(/\/\*[\s\S]*?\*\//g, "");
21
+ code = code.replace(/`[^`]*`/g, "");
22
+ code = code.replace(/"[^"]*"/g, "");
23
+ code = code.replace(/'[^']*'/g, "");
24
+ const patterns = [
25
+ /\{\s*[^}]*\bcookies\b[^}]*\}/,
26
+ // Destructuring: { cookies }
27
+ /\bevent\.cookies\b/,
28
+ // Direct access: event.cookies
29
+ /\brequest\.cookies\b/
30
+ // Alternative: request.cookies
31
+ ];
32
+ return patterns.some((pattern) => pattern.test(code));
33
+ } catch (error) {
34
+ console.warn(`Could not analyze ${filePath} for cookie usage:`, error.message);
35
+ return true;
36
+ }
37
+ }
38
+
39
+ // node_modules/esm-env/browser-fallback.js
40
+ var browser_fallback_default = typeof window !== "undefined";
41
+
42
+ // node_modules/esm-env/dev-fallback.js
43
+ var node_env = globalThis.process?.env?.NODE_ENV;
44
+ var dev_fallback_default = node_env && !node_env.toLowerCase().startsWith("prod");
45
+
46
+ // node_modules/@sveltejs/kit/src/utils/routing.js
47
+ var param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
48
+ function parse_route_id(id) {
49
+ const params = [];
50
+ const pattern = id === "/" ? /^\/$/ : new RegExp(
51
+ `^${get_route_segments(id).map((segment) => {
52
+ const rest_match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(segment);
53
+ if (rest_match) {
54
+ params.push({
55
+ name: rest_match[1],
56
+ matcher: rest_match[2],
57
+ optional: false,
58
+ rest: true,
59
+ chained: true
60
+ });
61
+ return "(?:/([^]*))?";
62
+ }
63
+ const optional_match = /^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(segment);
64
+ if (optional_match) {
65
+ params.push({
66
+ name: optional_match[1],
67
+ matcher: optional_match[2],
68
+ optional: true,
69
+ rest: false,
70
+ chained: true
71
+ });
72
+ return "(?:/([^/]+))?";
73
+ }
74
+ if (!segment) {
75
+ return;
76
+ }
77
+ const parts = segment.split(/\[(.+?)\](?!\])/);
78
+ const result = parts.map((content, i) => {
79
+ if (i % 2) {
80
+ if (content.startsWith("x+")) {
81
+ return escape(String.fromCharCode(parseInt(content.slice(2), 16)));
82
+ }
83
+ if (content.startsWith("u+")) {
84
+ return escape(
85
+ String.fromCharCode(
86
+ ...content.slice(2).split("-").map((code) => parseInt(code, 16))
87
+ )
88
+ );
89
+ }
90
+ const match = (
91
+ /** @type {RegExpExecArray} */
92
+ param_pattern.exec(content)
93
+ );
94
+ if (!browser_fallback_default && !match) {
95
+ throw new Error(
96
+ `Invalid param: ${content}. Params and matcher names can only have underscores and alphanumeric characters.`
97
+ );
98
+ }
99
+ const [, is_optional, is_rest, name2, matcher] = match;
100
+ params.push({
101
+ name: name2,
102
+ matcher,
103
+ optional: !!is_optional,
104
+ rest: !!is_rest,
105
+ chained: is_rest ? i === 1 && parts[0] === "" : false
106
+ });
107
+ return is_rest ? "([^]*?)" : is_optional ? "([^/]*)?" : "([^/]+?)";
108
+ }
109
+ return escape(content);
110
+ }).join("");
111
+ return "/" + result;
112
+ }).join("")}/?$`
113
+ );
114
+ return { pattern, params };
115
+ }
116
+ function affects_path(segment) {
117
+ return segment !== "" && !/^\([^)]+\)$/.test(segment);
118
+ }
119
+ function get_route_segments(route) {
120
+ return route.slice(1).split("/").filter(affects_path);
121
+ }
122
+ function escape(str) {
123
+ return str.normalize().replace(/[[\]]/g, "\\$&").replace(/%/g, "%25").replace(/\//g, "%2[Ff]").replace(/\?/g, "%3[Ff]").replace(/#/g, "%23").replace(/[.*+?^${}()|\\]/g, "\\$&");
124
+ }
125
+
126
+ // src/lib/generate-params.ts
127
+ function generateParamsModule(routePattern) {
128
+ const { pattern, params } = parse_route_id(routePattern);
129
+ return `import { exec } from 'sveltekit:routing';
130
+
131
+ const pattern = ${pattern};
132
+ const params = ${JSON.stringify(params)};
133
+
134
+ export function extractParams(url) {
135
+ const match = pattern.exec(url.pathname);
136
+ if (!match) return {};
137
+ return exec(match, params, {}) || {};
138
+ }`;
139
+ }
140
+
141
+ // src/lib/build-function.ts
142
+ async function buildFunctionWorker(options) {
143
+ const {
144
+ endpointFile,
145
+ outfile,
146
+ routePattern,
147
+ minify = false
148
+ } = options;
149
+ const usesCookies = detectCookiesUsage(endpointFile);
150
+ const hasParams = routePattern.includes("[");
151
+ const tempDir = mkdtempSync(path.join(tmpdir(), "adapter-params-"));
152
+ const paramsModulePath = path.join(tempDir, "params.js");
153
+ const paramsModuleCode = generateParamsModule(routePattern);
154
+ writeFileSync(paramsModulePath, paramsModuleCode);
155
+ const urlPath = fileURLToPath(import.meta.url);
156
+ const adapterRoot = urlPath.includes("/dist/") ? fileURLToPath(new URL("..", import.meta.url)) : fileURLToPath(new URL("../..", import.meta.url));
157
+ const files = path.join(adapterRoot, "dist");
158
+ const functionTemplate = path.join(files, "function-worker.js");
159
+ const libCookies = path.join(files, "../src/lib/cookies.js");
160
+ const svelteKitCookie = path.join(adapterRoot, "node_modules/@sveltejs/kit/src/runtime/server/cookie.js");
161
+ const svelteKitRouting = path.join(adapterRoot, "node_modules/@sveltejs/kit/src/utils/routing.js");
162
+ const external = ["node:*"];
163
+ if (!usesCookies) {
164
+ external.push("cookie");
165
+ }
166
+ try {
167
+ await build({
168
+ entryPoints: [functionTemplate],
169
+ bundle: true,
170
+ format: "esm",
171
+ platform: "neutral",
172
+ outfile,
173
+ alias: {
174
+ ENDPOINT: endpointFile,
175
+ "lib:cookies": libCookies,
176
+ "lib:routing": paramsModulePath,
177
+ "sveltekit:cookie": svelteKitCookie,
178
+ "sveltekit:routing": svelteKitRouting
179
+ },
180
+ external,
181
+ minifySyntax: minify,
182
+ treeShaking: true,
183
+ define: {
184
+ ROUTE_PATTERN: JSON.stringify(routePattern),
185
+ WITH_COOKIES: JSON.stringify(usesCookies),
186
+ WITH_PARAMS: JSON.stringify(hasParams)
187
+ }
188
+ });
189
+ } finally {
190
+ try {
191
+ unlinkSync(paramsModulePath);
192
+ unlinkSync(tempDir);
193
+ } catch {
194
+ }
195
+ }
196
+ }
197
+
198
+ // src/index.ts
199
+ var pkg = JSON.parse(readFileSync2(new URL("../package.json", import.meta.url), "utf-8"));
200
+ var name = "@openworkers/adapter-sveltekit";
201
+ var version = pkg.version;
202
+ function index_default(options = {}) {
203
+ return {
204
+ name,
205
+ async adapt(builder) {
206
+ const dest = options.out ?? "dist";
207
+ const assetsDir = `${dest}/assets`;
208
+ const functionsEnabled = options.functions ?? false;
209
+ const files = fileURLToPath2(new URL(".", import.meta.url).href);
210
+ const tmp = builder.getBuildDirectory("openworkers-tmp");
211
+ builder.rimraf(dest);
212
+ builder.mkdirp(dest);
213
+ builder.mkdirp(assetsDir);
214
+ builder.mkdirp(tmp);
215
+ const clientFiles = builder.writeClient(assetsDir);
216
+ builder.writePrerendered(assetsDir);
217
+ const appPath = builder.getAppPath();
218
+ const staticFiles = clientFiles.filter((f) => !f.startsWith(appPath));
219
+ const fallback = path2.join(assetsDir, "404.html");
220
+ if (!existsSync(fallback)) {
221
+ writeFileSync2(fallback, "Not Found");
222
+ }
223
+ writeFileSync2(
224
+ `${tmp}/manifest.js`,
225
+ `export const manifest = ${builder.generateManifest({ relativePath: path2.posix.relative(tmp, builder.getServerDirectory()) })};
226
+
227
+ export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});
228
+
229
+ export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};
230
+ `
231
+ );
232
+ const entryPoint = `${tmp}/entry.js`;
233
+ const serverPath = posixify(path2.resolve(builder.getServerDirectory(), "index.js"));
234
+ const manifestPath = posixify(path2.resolve(tmp, "manifest.js"));
235
+ writeFileSync2(
236
+ entryPoint,
237
+ `import { Server } from '${serverPath}';
238
+ import { manifest, prerendered, base_path } from '${manifestPath}';
239
+ export { Server, manifest, prerendered, base_path };
240
+ `
241
+ );
242
+ const workerDest = `${dest}/_worker.js`;
243
+ const libAsyncHooks = posixify(path2.resolve(files, "../src/lib/async-hooks.ts"));
244
+ await build2({
245
+ entryPoints: [`${files}/worker.js`],
246
+ bundle: true,
247
+ format: "esm",
248
+ platform: "neutral",
249
+ outfile: workerDest,
250
+ alias: {
251
+ SERVER: entryPoint,
252
+ MANIFEST: entryPoint,
253
+ "node:async_hooks": libAsyncHooks
254
+ },
255
+ external: ["node:*"],
256
+ minifySyntax: true,
257
+ minifyIdentifiers: true,
258
+ treeShaking: true,
259
+ banner: {
260
+ js: `// Generated by ${name} v${version} at ${(/* @__PURE__ */ new Date()).toISOString()}`
261
+ }
262
+ });
263
+ const functions = [];
264
+ if (functionsEnabled) {
265
+ builder.mkdirp(`${dest}/functions`);
266
+ const serverDir = builder.getServerDirectory();
267
+ const viteManifestPath = path2.join(serverDir, ".vite/manifest.json");
268
+ if (existsSync(viteManifestPath)) {
269
+ const viteManifest = JSON.parse(readFileSync2(viteManifestPath, "utf-8"));
270
+ const endpoints = extractEndpointsFromManifest(viteManifest, serverDir);
271
+ for (const endpoint of endpoints) {
272
+ const routePattern = endpoint.pattern;
273
+ const workerName = endpoint.route.replace(/\//g, "-").replace(/^-/, "") || "index";
274
+ const workerFile = `functions/${workerName}.js`;
275
+ await buildFunctionWorker({
276
+ endpointFile: posixify(endpoint.file),
277
+ outfile: `${dest}/${workerFile}`,
278
+ routePattern: endpoint.route,
279
+ minify: true
280
+ });
281
+ functions.push({
282
+ pattern: routePattern,
283
+ worker: workerFile
284
+ });
285
+ builder.log.minor(` Generated function: ${routePattern} \u2192 ${workerFile}`);
286
+ }
287
+ }
288
+ }
289
+ const routes = {
290
+ // Immutable assets (hashed filenames, cache forever)
291
+ immutable: [`/${appPath}/immutable/*`],
292
+ // Static files from /static folder (robots.txt, favicon, etc.)
293
+ static: staticFiles.map((f) => `/${f}`),
294
+ // Prerendered pages (serve from assets, no worker needed)
295
+ prerendered: builder.prerendered.paths,
296
+ // Functions (API routes as separate workers)
297
+ functions,
298
+ // Everything else -> SSR
299
+ ssr: ["/*"]
300
+ };
301
+ writeFileSync2(`${dest}/_routes.json`, JSON.stringify(routes, null, 2));
302
+ builder.log.minor(`Wrote ${workerDest}`);
303
+ builder.log.minor(`Wrote ${dest}/_routes.json`);
304
+ if (functions.length > 0) {
305
+ builder.log.minor(`Wrote ${functions.length} function workers to ${dest}/functions/`);
306
+ }
307
+ builder.log.minor(`Wrote ${assetsDir} (${builder.prerendered.paths.length} prerendered pages)`);
308
+ }
309
+ };
310
+ }
311
+ function extractEndpointsFromManifest(manifest, serverDir) {
312
+ const endpoints = [];
313
+ for (const [key, entry] of Object.entries(manifest)) {
314
+ if (!key.includes("+server.ts")) continue;
315
+ const sourcePath = entry.src;
316
+ if (!sourcePath || !sourcePath.startsWith("src/routes/")) continue;
317
+ const routePart = sourcePath.replace(/^src\/routes/, "").replace(/\/\+server\.(ts|js)$/, "");
318
+ const pattern = convertRouteToPattern(routePart);
319
+ const file = path2.join(serverDir, entry.file);
320
+ endpoints.push({
321
+ pattern,
322
+ // For routing: /status/*/*
323
+ route: routePart,
324
+ // For filename: /status/[code]/[[reason]]
325
+ file
326
+ });
327
+ }
328
+ return endpoints;
329
+ }
330
+ function convertRouteToPattern(route) {
331
+ if (!route || route === "/") return "/";
332
+ const segments = route.split("/").filter(Boolean);
333
+ const patternSegments = segments.map((segment) => {
334
+ if (segment.startsWith("[...") && segment.endsWith("]")) {
335
+ return "**";
336
+ }
337
+ if (segment.startsWith("[") && segment.endsWith("]") || segment.startsWith("[[") && segment.endsWith("]]")) {
338
+ return "*";
339
+ }
340
+ return segment;
341
+ });
342
+ return "/" + patternSegments.join("/");
343
+ }
344
+ function posixify(str) {
345
+ return str.replace(/\\/g, "/");
346
+ }
347
+ export {
348
+ index_default as default
349
+ };
@@ -1,42 +1,34 @@
1
- // src/worker.js
1
+ // src/worker.ts
2
2
  import { Server, manifest, prerendered, base_path } from "SERVER";
3
3
  var server = new Server(manifest);
4
4
  var app_path = `/${manifest.appPath}`;
5
5
  var immutable = `${app_path}/immutable/`;
6
6
  var version_file = `${app_path}/version.json`;
7
7
  if (typeof caches === "undefined") {
8
- const noopCache = (
9
- /** @type {any} */
10
- {
11
- match: async () => void 0,
12
- put: async () => {
13
- },
14
- delete: async () => false
15
- }
16
- );
17
- globalThis.caches = /** @type {any} */
18
- {
8
+ const noopCache = {
9
+ match: async () => void 0,
10
+ put: async () => {
11
+ },
12
+ delete: async () => false
13
+ };
14
+ globalThis.caches = {
19
15
  default: noopCache,
20
16
  open: async () => noopCache
21
17
  };
22
18
  }
23
19
  var origin;
24
20
  var initialized = server.init({
25
- env: (
26
- /** @type {Record<string, string>} */
27
- globalThis.env ?? {}
28
- ),
21
+ env: globalThis.env ?? {},
29
22
  read: async (file) => {
30
23
  const url = `${origin}/${file}`;
31
- const response = await /** @type {Env} */
32
- globalThis.env.ASSETS.fetch(url);
24
+ const response = await globalThis.env.ASSETS.fetch(url);
33
25
  if (!response.ok) {
34
26
  throw new Error(`read(...) failed: could not fetch ${url} (${response.status} ${response.statusText})`);
35
27
  }
36
28
  return response.body;
37
29
  }
38
30
  });
39
- var worker_default = {
31
+ var worker = {
40
32
  async fetch(req, env, ctx) {
41
33
  globalThis.env = env;
42
34
  if (!origin) {
@@ -78,6 +70,7 @@ var worker_default = {
78
70
  });
79
71
  }
80
72
  };
73
+ var worker_default = worker;
81
74
  export {
82
75
  worker_default as default
83
76
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openworkers/adapter-sveltekit",
3
- "version": "0.3.6",
3
+ "version": "0.4.0",
4
4
  "description": "SvelteKit adapter for OpenWorkers",
5
5
  "keywords": [
6
6
  "adapter",
@@ -18,28 +18,30 @@
18
18
  "type": "module",
19
19
  "exports": {
20
20
  ".": {
21
- "types": "./index.d.ts",
22
- "import": "./index.js"
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
23
  },
24
+ "./dist/*": "./dist/*",
24
25
  "./package.json": "./package.json"
25
26
  },
26
- "types": "index.d.ts",
27
+ "types": "dist/index.d.ts",
27
28
  "files": [
28
- "files",
29
- "index.js",
30
- "index.d.ts"
29
+ "dist"
31
30
  ],
32
31
  "scripts": {
33
- "build": "esbuild src/worker.js --bundle --outfile=files/worker.js --external:SERVER --external:MANIFEST --format=esm",
32
+ "build": "bun build.ts",
33
+ "test": "bun test",
34
34
  "prepublishOnly": "npm run build",
35
+ "check": "tsc --noEmit",
35
36
  "format": "prettier --write ."
36
37
  },
37
38
  "dependencies": {
38
39
  "esbuild": "^0.27.2"
39
40
  },
40
41
  "devDependencies": {
41
- "@openworkers/workers-types": "^0.1.7",
42
+ "@openworkers/workers-types": "^0.1.9",
42
43
  "@sveltejs/kit": "^2.0.0",
44
+ "bun-types": "^1.3.8",
43
45
  "prettier": "^3.8.1"
44
46
  },
45
47
  "peerDependencies": {
@@ -1,90 +0,0 @@
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
- /**
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
-
49
- export default {
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
- }
90
- };
@@ -1,41 +0,0 @@
1
- /**
2
- * Minimal AsyncLocalStorage shim for OpenWorkers.
3
- *
4
- * OpenWorkers creates a fresh V8 context per request.
5
- * Module-level state is already isolated, so no real async tracking is needed.
6
- */
7
-
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
- }
41
- }
package/index.js DELETED
@@ -1,242 +0,0 @@
1
- import { existsSync, writeFileSync, readdirSync, statSync, readFileSync } from 'node:fs';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { build } from 'esbuild';
5
-
6
- const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf-8'));
7
- const name = '@openworkers/adapter-sveltekit';
8
- const version = pkg.version;
9
-
10
- /** @type {import('./index.js').default} */
11
- export default function (options = {}) {
12
- return {
13
- name,
14
- /** @param {import('@sveltejs/kit').Builder} builder */
15
- async adapt(builder) {
16
- const dest = options.out ?? 'dist';
17
- const assetsDir = `${dest}/assets`;
18
- const functionsEnabled = options.functions ?? false;
19
-
20
- const files = fileURLToPath(new URL('./files', import.meta.url).href);
21
- const tmp = builder.getBuildDirectory('openworkers-tmp');
22
-
23
- builder.rimraf(dest);
24
- builder.mkdirp(dest);
25
- builder.mkdirp(assetsDir);
26
- builder.mkdirp(tmp);
27
-
28
- // Write client assets (returns list of files)
29
- const clientFiles = builder.writeClient(assetsDir);
30
-
31
- // Write prerendered pages
32
- builder.writePrerendered(assetsDir);
33
-
34
- // Separate static files from app files
35
- const appPath = builder.getAppPath();
36
- const staticFiles = clientFiles.filter((f) => !f.startsWith(appPath));
37
-
38
- // Generate 404.html fallback
39
- const fallback = path.join(assetsDir, '404.html');
40
-
41
- if (!existsSync(fallback)) {
42
- writeFileSync(fallback, 'Not Found');
43
- }
44
-
45
- // Generate manifest
46
- writeFileSync(
47
- `${tmp}/manifest.js`,
48
- `export const manifest = ${builder.generateManifest({ relativePath: path.posix.relative(tmp, builder.getServerDirectory()) })};\n\n` +
49
- `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n\n` +
50
- `export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n`
51
- );
52
-
53
- // Create entry point that imports server and manifest
54
- const entryPoint = `${tmp}/entry.js`;
55
- const serverPath = posixify(path.resolve(builder.getServerDirectory(), 'index.js'));
56
- const manifestPath = posixify(path.resolve(tmp, 'manifest.js'));
57
-
58
- writeFileSync(
59
- entryPoint,
60
- `import { Server } from '${serverPath}';\n` +
61
- `import { manifest, prerendered, base_path } from '${manifestPath}';\n` +
62
- `export { Server, manifest, prerendered, base_path };\n`
63
- );
64
-
65
- // Bundle main worker with esbuild
66
- const workerDest = `${dest}/_worker.js`;
67
- const shimAsyncHooks = posixify(path.resolve(files, 'shims/async_hooks.js'));
68
-
69
- await build({
70
- entryPoints: [`${files}/worker.js`],
71
- bundle: true,
72
- format: 'esm',
73
- platform: 'browser',
74
- outfile: workerDest,
75
- alias: {
76
- SERVER: entryPoint,
77
- MANIFEST: entryPoint,
78
- 'node:async_hooks': shimAsyncHooks
79
- },
80
- external: ['node:*'],
81
- minify: false,
82
- banner: {
83
- js: `// Generated by ${name} v${version}\n`
84
- }
85
- });
86
-
87
- // Generate mini-workers for API routes if enabled
88
- /** @type {Array<{pattern: string, worker: string}>} */
89
- const functions = [];
90
-
91
- if (functionsEnabled) {
92
- builder.mkdirp(`${dest}/functions`);
93
-
94
- const serverDir = builder.getServerDirectory();
95
- const viteManifestPath = path.join(serverDir, '.vite/manifest.json');
96
- const functionTemplate = posixify(path.resolve(files, 'function-worker.js'));
97
-
98
- if (existsSync(viteManifestPath)) {
99
- const viteManifest = JSON.parse(readFileSync(viteManifestPath, 'utf-8'));
100
- const endpoints = extractEndpointsFromManifest(viteManifest, serverDir);
101
-
102
- for (const endpoint of endpoints) {
103
- const routePattern = endpoint.pattern;
104
- // Use SvelteKit route syntax for worker filename
105
- const workerName = endpoint.route.replace(/\//g, '-').replace(/^-/, '') || 'index';
106
- const workerFile = `functions/${workerName}.js`;
107
-
108
- // Bundle using the template with ENDPOINT alias
109
- await build({
110
- entryPoints: [functionTemplate],
111
- bundle: true,
112
- format: 'esm',
113
- platform: 'browser',
114
- outfile: `${dest}/${workerFile}`,
115
- alias: {
116
- ENDPOINT: posixify(endpoint.file),
117
- 'node:async_hooks': shimAsyncHooks
118
- },
119
- external: ['node:*'],
120
- minify: false,
121
- define: {
122
- ROUTE_PATTERN: JSON.stringify(endpoint.route)
123
- },
124
- banner: {
125
- js: `// Generated by ${name} v${version} - Function: ${routePattern}\n`
126
- }
127
- });
128
-
129
- functions.push({
130
- pattern: routePattern,
131
- worker: workerFile
132
- });
133
-
134
- builder.log.minor(` Generated function: ${routePattern} → ${workerFile}`);
135
- }
136
- }
137
- }
138
-
139
- // Generate _routes.json for edge routing
140
- const routes = {
141
- // Immutable assets (hashed filenames, cache forever)
142
- immutable: [`/${appPath}/immutable/*`],
143
- // Static files from /static folder (robots.txt, favicon, etc.)
144
- static: staticFiles.map((f) => `/${f}`),
145
- // Prerendered pages (serve from assets, no worker needed)
146
- prerendered: builder.prerendered.paths,
147
- // Functions (API routes as separate workers)
148
- functions: functions,
149
- // Everything else -> SSR
150
- ssr: ['/*']
151
- };
152
-
153
- writeFileSync(
154
- `${dest}/_routes.json`,
155
- JSON.stringify(routes, null, 2)
156
- );
157
-
158
- builder.log.minor(`Wrote ${workerDest}`);
159
- builder.log.minor(`Wrote ${dest}/_routes.json`);
160
-
161
- if (functions.length > 0) {
162
- builder.log.minor(`Wrote ${functions.length} function workers to ${dest}/functions/`);
163
- }
164
-
165
- builder.log.minor(`Wrote ${assetsDir} (${builder.prerendered.paths.length} prerendered pages)`);
166
- }
167
- };
168
- }
169
-
170
- /**
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}>}
175
- */
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
- });
202
- }
203
-
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('/');
237
- }
238
-
239
- /** @param {string} str */
240
- function posixify(str) {
241
- return str.replace(/\\/g, '/');
242
- }
File without changes