export-runtime 0.0.14 → 0.0.16
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/bin/generate-types.mjs +21 -10
- package/handler.js +51 -11
- package/package.json +1 -1
package/bin/generate-types.mjs
CHANGED
|
@@ -42,6 +42,11 @@ const r2Bindings = cloudflareConfig.r2 || [];
|
|
|
42
42
|
const kvBindings = cloudflareConfig.kv || [];
|
|
43
43
|
const authConfig = cloudflareConfig.auth || null;
|
|
44
44
|
|
|
45
|
+
// Optional: Security configuration
|
|
46
|
+
const securityConfig = pkg.security || {};
|
|
47
|
+
const accessConfig = securityConfig.access || {};
|
|
48
|
+
const allowedOrigins = accessConfig.origin || []; // empty = allow all (default Workers behavior)
|
|
49
|
+
|
|
45
50
|
// Auth requires a D1 database for better-auth
|
|
46
51
|
const allD1Bindings = [...d1Bindings];
|
|
47
52
|
if (authConfig && !allD1Bindings.includes("AUTH_DB")) {
|
|
@@ -81,11 +86,11 @@ function discoverModules(dir, base = "") {
|
|
|
81
86
|
const modules = [];
|
|
82
87
|
if (!fs.existsSync(dir)) return modules;
|
|
83
88
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
84
|
-
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
89
|
+
if (entry.name.startsWith("_") || entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
85
90
|
const fullPath = path.join(dir, entry.name);
|
|
86
91
|
if (entry.isDirectory()) {
|
|
87
92
|
modules.push(...discoverModules(fullPath, base ? `${base}/${entry.name}` : entry.name));
|
|
88
|
-
} else if (EXTENSIONS.includes(path.extname(entry.name))) {
|
|
93
|
+
} else if (EXTENSIONS.includes(path.extname(entry.name)) && !entry.name.endsWith(".d.ts")) {
|
|
89
94
|
const nameWithoutExt = entry.name.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
90
95
|
const routePath = nameWithoutExt === "index"
|
|
91
96
|
? base // index.ts → directory path ("" for root)
|
|
@@ -391,16 +396,21 @@ const wranglerLines = [
|
|
|
391
396
|
``,
|
|
392
397
|
];
|
|
393
398
|
|
|
394
|
-
// Add static assets configuration if main is specified
|
|
399
|
+
// Add static assets configuration if main is specified and directory exists
|
|
395
400
|
if (assetsDir) {
|
|
396
401
|
const normalizedAssetsDir = assetsDir.startsWith("./") ? assetsDir : `./${assetsDir}`;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
402
|
+
const absoluteAssetsDir = path.resolve(cwd, normalizedAssetsDir);
|
|
403
|
+
if (fs.existsSync(absoluteAssetsDir)) {
|
|
404
|
+
wranglerLines.push(
|
|
405
|
+
`[assets]`,
|
|
406
|
+
`directory = "${normalizedAssetsDir}"`,
|
|
407
|
+
`binding = "ASSETS"`,
|
|
408
|
+
`run_worker_first = true`,
|
|
409
|
+
``,
|
|
410
|
+
);
|
|
411
|
+
} else {
|
|
412
|
+
console.log(`Note: Assets directory "${normalizedAssetsDir}" not found. Run "vite build" first to enable static assets.`);
|
|
413
|
+
}
|
|
404
414
|
}
|
|
405
415
|
|
|
406
416
|
// Add Durable Objects for shared state
|
|
@@ -465,6 +475,7 @@ export const d1Bindings = ${JSON.stringify(allD1Bindings)};
|
|
|
465
475
|
export const r2Bindings = ${JSON.stringify(r2Bindings)};
|
|
466
476
|
export const kvBindings = ${JSON.stringify(kvBindings)};
|
|
467
477
|
export const authConfig = ${JSON.stringify(authConfig)};
|
|
478
|
+
export const securityConfig = ${JSON.stringify({ access: { origin: allowedOrigins } })};
|
|
468
479
|
`;
|
|
469
480
|
fs.writeFileSync(configPath, configContent);
|
|
470
481
|
|
package/handler.js
CHANGED
|
@@ -5,14 +5,14 @@ import { handleAuthRoute, getSessionFromRequest, verifySession } from "./auth.js
|
|
|
5
5
|
|
|
6
6
|
const JS = "application/javascript; charset=utf-8";
|
|
7
7
|
const TS = "application/typescript; charset=utf-8";
|
|
8
|
-
const CORS = { "Access-Control-Allow-Origin": "*" };
|
|
9
8
|
const IMMUTABLE = "public, max-age=31536000, immutable";
|
|
10
9
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
const createResponseHelpers = (corsHeaders) => ({
|
|
11
|
+
jsResponse: (body, extra = {}) =>
|
|
12
|
+
new Response(body, { headers: { "Content-Type": JS, ...corsHeaders, ...extra } }),
|
|
13
|
+
tsResponse: (body, status = 200) =>
|
|
14
|
+
new Response(body, { status, headers: { "Content-Type": TS, ...corsHeaders, "Cache-Control": "no-cache" } }),
|
|
15
|
+
});
|
|
16
16
|
|
|
17
17
|
export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, minifiedSharedCore, exportConfig = {}) => {
|
|
18
18
|
// moduleMap: { routePath: moduleNamespace, ... }
|
|
@@ -28,7 +28,33 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// Export configuration
|
|
31
|
-
const { d1Bindings = [], r2Bindings = [], kvBindings = [], authConfig = null } = exportConfig;
|
|
31
|
+
const { d1Bindings = [], r2Bindings = [], kvBindings = [], authConfig = null, securityConfig = {} } = exportConfig;
|
|
32
|
+
|
|
33
|
+
// Security: allowed origins (empty array = allow all)
|
|
34
|
+
const allowedOrigins = securityConfig?.access?.origin || [];
|
|
35
|
+
const hasOriginRestriction = allowedOrigins.length > 0;
|
|
36
|
+
|
|
37
|
+
// Check if origin is allowed
|
|
38
|
+
const isOriginAllowed = (origin) => {
|
|
39
|
+
if (!hasOriginRestriction) return true;
|
|
40
|
+
if (!origin) return false;
|
|
41
|
+
return allowedOrigins.includes(origin);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Get CORS headers for a request
|
|
45
|
+
const getCorsHeaders = (request) => {
|
|
46
|
+
const origin = request.headers.get("Origin");
|
|
47
|
+
if (!hasOriginRestriction) {
|
|
48
|
+
return { "Access-Control-Allow-Origin": "*" };
|
|
49
|
+
}
|
|
50
|
+
if (origin && isOriginAllowed(origin)) {
|
|
51
|
+
return {
|
|
52
|
+
"Access-Control-Allow-Origin": origin,
|
|
53
|
+
"Vary": "Origin",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {};
|
|
57
|
+
};
|
|
32
58
|
const hasClient = d1Bindings.length > 0 || r2Bindings.length > 0 || kvBindings.length > 0 || authConfig;
|
|
33
59
|
|
|
34
60
|
// Generate core code with config
|
|
@@ -70,9 +96,8 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
|
|
|
70
96
|
const namedExports = keys
|
|
71
97
|
.map((key) => `export const ${key} = createProxy([${JSON.stringify(route)}, ${JSON.stringify(key)}]);`)
|
|
72
98
|
.join("\n");
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
return `import { createProxy } from ".${cpath}";\n${namedExports}${defaultExport}`;
|
|
99
|
+
// Always include default export (client) - it will be null if no bindings configured
|
|
100
|
+
return `import { createProxy } from ".${cpath}";\nexport { default } from ".${cpath}";\n${namedExports}`;
|
|
76
101
|
};
|
|
77
102
|
|
|
78
103
|
const buildExportModule = (cpath, route, name) =>
|
|
@@ -331,9 +356,24 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
|
|
|
331
356
|
async fetch(request, env) {
|
|
332
357
|
const url = new URL(request.url);
|
|
333
358
|
const isShared = url.searchParams.has("shared");
|
|
359
|
+
const origin = request.headers.get("Origin");
|
|
360
|
+
|
|
361
|
+
// --- Origin check ---
|
|
362
|
+
if (hasOriginRestriction && origin && !isOriginAllowed(origin)) {
|
|
363
|
+
return new Response("Forbidden: Origin not allowed", { status: 403 });
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Get CORS headers for this request
|
|
367
|
+
const corsHeaders = getCorsHeaders(request);
|
|
368
|
+
const { jsResponse, tsResponse } = createResponseHelpers(corsHeaders);
|
|
334
369
|
|
|
335
370
|
// --- WebSocket upgrade ---
|
|
336
371
|
if (request.headers.get("Upgrade") === "websocket") {
|
|
372
|
+
// Origin check for WebSocket (browsers send Origin header)
|
|
373
|
+
if (hasOriginRestriction && origin && !isOriginAllowed(origin)) {
|
|
374
|
+
return new Response("Forbidden: Origin not allowed", { status: 403 });
|
|
375
|
+
}
|
|
376
|
+
|
|
337
377
|
const pair = new WebSocketPair();
|
|
338
378
|
const [client, server] = Object.values(pair);
|
|
339
379
|
server.accept();
|
|
@@ -393,7 +433,7 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
|
|
|
393
433
|
if (env?.ASSETS) {
|
|
394
434
|
return env.ASSETS.fetch(request);
|
|
395
435
|
}
|
|
396
|
-
return new Response("Not found", { status: 404 });
|
|
436
|
+
return new Response("Not found", { status: 404, headers: corsHeaders });
|
|
397
437
|
}
|
|
398
438
|
|
|
399
439
|
const { route, exportName } = resolved;
|