export-runtime 0.0.17 → 0.0.19

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.
@@ -10,40 +10,55 @@ import { fileURLToPath } from "url";
10
10
  const cwd = process.cwd();
11
11
 
12
12
  // --- Read package.json for configuration ---
13
+ // First check local package.json, then parent (for exportc-style projects)
13
14
 
14
- const pkgPath = path.join(cwd, "package.json");
15
- if (!fs.existsSync(pkgPath)) {
16
- console.error("package.json not found in", cwd);
17
- process.exit(1);
15
+ const localPkgPath = path.join(cwd, "package.json");
16
+ const parentPkgPath = path.join(cwd, "..", "package.json");
17
+
18
+ let pkg = null;
19
+ let rootPkg = null;
20
+ let isExportcProject = false;
21
+
22
+ if (fs.existsSync(localPkgPath)) {
23
+ pkg = JSON.parse(fs.readFileSync(localPkgPath, "utf8"));
24
+ }
25
+
26
+ // Check if parent has cloudflare config (exportc-style project)
27
+ if (fs.existsSync(parentPkgPath)) {
28
+ rootPkg = JSON.parse(fs.readFileSync(parentPkgPath, "utf8"));
29
+ if (rootPkg.cloudflare) {
30
+ isExportcProject = true;
31
+ }
18
32
  }
19
33
 
20
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
34
+ // Use root package.json's cloudflare config if available (exportc project)
35
+ const cloudflareConfig = isExportcProject ? (rootPkg.cloudflare || {}) : (pkg?.cloudflare || {});
36
+ const securityConfig = isExportcProject ? (rootPkg.security || {}) : (pkg?.security || {});
21
37
 
22
- // Required fields
23
- const workerName = pkg.name;
38
+ // Worker name - from cloudflare.name, or package name
39
+ const workerName = cloudflareConfig.name || pkg?.name;
24
40
  if (!workerName) {
25
- console.error("package.json must have a 'name' field for the Worker name");
41
+ console.error("Worker name required: set 'cloudflare.name' in package.json or provide a package 'name'");
26
42
  process.exit(1);
27
43
  }
28
44
 
29
- const exportsEntry = pkg.exports;
45
+ // Source entry - from cloudflare.exports or package exports
46
+ const exportsEntry = cloudflareConfig.exports || pkg?.exports;
30
47
  if (!exportsEntry) {
31
- console.error("package.json must have an 'exports' field pointing to the source entry (e.g., './src' or './src/index.ts')");
48
+ console.error("package.json must have 'cloudflare.exports' or 'exports' field pointing to the source entry (e.g., './src' or './')");
32
49
  process.exit(1);
33
50
  }
34
51
 
35
- // Optional: static assets directory
36
- const assetsDir = pkg.main || null;
52
+ // Optional: static assets directory (from cloudflare.assets only, not main)
53
+ const assetsDir = cloudflareConfig.assets || null;
37
54
 
38
- // Optional: Cloudflare bindings configuration (d1, r2, kv, auth)
39
- const cloudflareConfig = pkg.cloudflare || {};
55
+ // Cloudflare bindings configuration (d1, r2, kv, auth)
40
56
  const d1Bindings = cloudflareConfig.d1 || [];
41
57
  const r2Bindings = cloudflareConfig.r2 || [];
42
58
  const kvBindings = cloudflareConfig.kv || [];
43
59
  const authConfig = cloudflareConfig.auth || null;
44
60
 
45
- // Optional: Security configuration
46
- const securityConfig = pkg.security || {};
61
+ // Security configuration
47
62
  const accessConfig = securityConfig.access || {};
48
63
  const allowedOrigins = accessConfig.origin || []; // empty = allow all (default Workers behavior)
49
64
 
@@ -68,7 +83,9 @@ validateBindings(kvBindings, "KV");
68
83
 
69
84
  // --- Resolve source directory from exports field ---
70
85
 
71
- const exportsPath = path.resolve(cwd, exportsEntry.replace(/^\.\//, ""));
86
+ // For exportc projects, resolve relative to parent directory
87
+ const baseDir = isExportcProject ? path.join(cwd, "..") : cwd;
88
+ const exportsPath = path.resolve(baseDir, exportsEntry.replace(/^\.\//, ""));
72
89
  const srcDir = fs.existsSync(exportsPath) && fs.statSync(exportsPath).isDirectory()
73
90
  ? exportsPath
74
91
  : path.dirname(exportsPath);
@@ -397,14 +414,17 @@ const wranglerLines = [
397
414
  ``,
398
415
  ];
399
416
 
400
- // Add static assets configuration if main is specified and directory exists
417
+ // Add static assets configuration if assets is specified and directory exists
401
418
  if (assetsDir) {
419
+ // For exportc projects, assets path is relative to parent directory
402
420
  const normalizedAssetsDir = assetsDir.startsWith("./") ? assetsDir : `./${assetsDir}`;
403
- const absoluteAssetsDir = path.resolve(cwd, normalizedAssetsDir);
421
+ const absoluteAssetsDir = path.resolve(baseDir, normalizedAssetsDir);
422
+ // In wrangler.toml, path should be relative to export/ directory for exportc projects
423
+ const wranglerAssetsPath = isExportcProject ? `../${normalizedAssetsDir.replace(/^\.\//, "")}` : normalizedAssetsDir;
404
424
  if (fs.existsSync(absoluteAssetsDir)) {
405
425
  wranglerLines.push(
406
426
  `[assets]`,
407
- `directory = "${normalizedAssetsDir}"`,
427
+ `directory = "${wranglerAssetsPath}"`,
408
428
  `binding = "ASSETS"`,
409
429
  `run_worker_first = true`,
410
430
  ``,
package/handler.js CHANGED
@@ -320,10 +320,22 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
320
320
  };
321
321
 
322
322
  const wireWebSocket = (server, dispatcher, env, onClose) => {
323
- // Track session state for this WebSocket connection
323
+ // Track session state and connection state for this WebSocket connection
324
324
  const wsSession = { token: null };
325
+ let isClosed = false;
326
+
327
+ // Safe send that ignores errors when connection is closed
328
+ const safeSend = (data) => {
329
+ if (isClosed) return;
330
+ try {
331
+ server.send(data);
332
+ } catch {
333
+ // Connection already closed, ignore
334
+ }
335
+ };
325
336
 
326
337
  server.addEventListener("message", async (event) => {
338
+ if (isClosed) return;
327
339
  let id;
328
340
  try {
329
341
  const msg = parse(event.data);
@@ -333,7 +345,7 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
333
345
  if (msg.type === "auth" && msg.token && !msg.method) {
334
346
  // Direct token send on reconnect - just update session
335
347
  wsSession.token = msg.token;
336
- server.send(stringify({ type: "auth-result", id, success: true }));
348
+ safeSend(stringify({ type: "auth-result", id, success: true }));
337
349
  return;
338
350
  }
339
351
 
@@ -344,12 +356,20 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
344
356
  wsSession.token = result.value.token;
345
357
  }
346
358
 
347
- if (result) server.send(stringify({ ...result, id }));
359
+ if (result) safeSend(stringify({ ...result, id }));
348
360
  } catch (err) {
349
- if (id !== undefined) server.send(stringify({ type: "error", id, error: String(err) }));
361
+ if (id !== undefined) safeSend(stringify({ type: "error", id, error: String(err) }));
350
362
  }
351
363
  });
352
- if (onClose) server.addEventListener("close", onClose);
364
+
365
+ server.addEventListener("close", () => {
366
+ isClosed = true;
367
+ if (onClose) onClose();
368
+ });
369
+
370
+ server.addEventListener("error", () => {
371
+ isClosed = true;
372
+ });
353
373
  };
354
374
 
355
375
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "export-runtime",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "Cloudflare Workers ESM Export Framework Runtime",
5
5
  "keywords": [
6
6
  "cloudflare",