@openworkers/adapter-sveltekit 0.3.6 → 0.4.1
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/dist/function-worker.js +66 -0
- package/dist/index.js +349 -0
- package/{files → dist}/worker.js +12 -19
- package/package.json +11 -9
- package/files/function-worker.js +0 -90
- package/files/shims/async_hooks.js +0 -41
- package/index.js +0 -242
- /package/{index.d.ts → dist/index.d.ts} +0 -0
|
@@ -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(process.cwd(), "node_modules/@sveltejs/kit/src/runtime/server/cookie.js");
|
|
161
|
+
const svelteKitRouting = path.join(process.cwd(), "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
|
+
};
|
package/{files → dist}/worker.js
RENAMED
|
@@ -1,42 +1,34 @@
|
|
|
1
|
-
// src/worker.
|
|
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
|
-
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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
|
|
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
|
+
"version": "0.4.1",
|
|
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
|
-
"
|
|
29
|
-
"index.js",
|
|
30
|
-
"index.d.ts"
|
|
29
|
+
"dist"
|
|
31
30
|
],
|
|
32
31
|
"scripts": {
|
|
33
|
-
"build": "
|
|
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.
|
|
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": {
|
package/files/function-worker.js
DELETED
|
@@ -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
|