@webjsdev/server 0.8.68 → 0.8.69

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webjsdev/server",
3
- "version": "0.8.68",
3
+ "version": "0.8.69",
4
4
  "type": "module",
5
5
  "description": "The server for WebJs, a full-stack JavaScript framework built on web components with server-side rendering and no build step. Provides the file-based router, SSR, server actions, route handlers, middleware, and live reload on Node 24+ or Bun.",
6
6
  "main": "index.js",
package/src/dev/config.js CHANGED
@@ -317,6 +317,28 @@ export async function readDevWatchPathsFromApp(appDir) {
317
317
  return out;
318
318
  }
319
319
 
320
+ /**
321
+ * The dev tooling switches in `webjs.dev` (#1504): `embedOrigins` (the embed
322
+ * bridge, #1498) and `sourceLocations` (#1499). Raw values; the env overrides
323
+ * and validation live with each feature (`resolveEmbedOrigins`,
324
+ * `sourceLocationsRequested`). A missing or unreadable package.json is both
325
+ * off.
326
+ *
327
+ * @param {string} appDir
328
+ * @returns {Promise<{ embedOrigins: unknown, sourceLocations: unknown }>}
329
+ */
330
+ export async function readDevToolingFromApp(appDir) {
331
+ let dev;
332
+ try {
333
+ const pkg = JSON.parse(await readFile(join(appDir, 'package.json'), 'utf8'));
334
+ dev = pkg && pkg.webjs && pkg.webjs.dev;
335
+ } catch {
336
+ dev = undefined;
337
+ }
338
+ if (!dev || typeof dev !== 'object') return { embedOrigins: undefined, sourceLocations: undefined };
339
+ return { embedOrigins: dev.embedOrigins, sourceLocations: dev.sourceLocations };
340
+ }
341
+
320
342
  /**
321
343
  * Resolve the node:http server timeouts (issue #237) from the app's
322
344
  * package.json `webjs.requestTimeoutMs` / `webjs.headersTimeoutMs` /
@@ -41,7 +41,7 @@ import { setAssetUrlProvider, setFormActionResolver } from '@webjsdev/core';
41
41
  import { resolveActionIdentity } from '../form-action-identity.js';
42
42
  import { setAssetRoots, clearAssetHashCache, setElisionFingerprint, withAssetHash, assetHashFor, resolveAssetUrl } from '../asset-hash.js';
43
43
  import { applySecurityHeaders, webRequestIsHttps } from '../headers.js';
44
- import { parseEmbedOrigins, setEmbedOrigins, allowEmbedFraming } from '../dev-embed.js';
44
+ import { resolveEmbedOrigins, setEmbedOrigins, allowEmbedFraming } from '../dev-embed.js';
45
45
  import {
46
46
  applyRedirects,
47
47
  applyTrailingSlash,
@@ -62,6 +62,7 @@ import {
62
62
  readAllowedOriginsFromApp,
63
63
  readCspConfigFromApp,
64
64
  readBodyLimitsFromApp,
65
+ readDevToolingFromApp,
65
66
  } from './config.js';
66
67
  import {
67
68
  exists, kebab, resolveRequestId, shouldAccessLog, loadAppEnv, collectRouteModules,
@@ -216,11 +217,14 @@ export async function createRequestHandler(opts) {
216
217
  // analysis pass re-reads it each rebuild so a dev toggle takes effect live.
217
218
  setClientRouterEnabled(await readClientRouterEnabled(appDir));
218
219
 
219
- // Dev embed bridge (#1498): `WEBJS_EMBED_ORIGINS` lets the listed parent
220
+ // Dev embed bridge (#1498): `WEBJS_EMBED_ORIGINS` (or `webjs.dev.embedOrigins`) lets the listed parent
220
221
  // origins frame this dev server and receive the bridge's messages. Read once
221
222
  // here, and only in dev, so a production handler records no origins and
222
223
  // neither injects the script nor relaxes a framing header.
223
- const embedOriginsValue = dev ? parseEmbedOrigins(process.env.WEBJS_EMBED_ORIGINS) : [];
224
+ // `webjs.dev.embedOrigins` in package.json is the default (#1504); a set
225
+ // env var replaces it for one run.
226
+ const devTooling = dev ? await readDevToolingFromApp(appDir) : { embedOrigins: undefined, sourceLocations: undefined };
227
+ const embedOriginsValue = dev ? resolveEmbedOrigins(process.env.WEBJS_EMBED_ORIGINS, devTooling.embedOrigins) : [];
224
228
  setEmbedOrigins(embedOriginsValue);
225
229
 
226
230
  const coreDir = locateCoreDir(appDir);
@@ -288,13 +292,15 @@ export async function createRequestHandler(opts) {
288
292
  // on seeding would mean `webjs.seed: false` silently broke every no-JS form.
289
293
  await registerActionHooks({ seed: await readSeedEnabled(appDir), dev });
290
294
 
291
- // Dev source locations (#1499): `WEBJS_SOURCE_LOCATIONS=1` under `webjs dev`
295
+ // Dev source locations (#1499): `WEBJS_SOURCE_LOCATIONS=1` (or
296
+ // `webjs.dev.sourceLocations: true`) under `webjs dev`
292
297
  // stamps `data-webjs-src="<file>:<line>"` on the element opening tags of the
293
298
  // app's `html` templates. Read once (the SSR load hook is process-global and
294
299
  // cannot be uninstalled), never in prod. The browser half reads
295
300
  // `state.sourceLocations` in serve.js; the SSR half is this load hook, which
296
301
  // must be in place before any app module is imported.
297
- const sourceLocations = dev && sourceLocationsRequested();
302
+ // `webjs.dev.sourceLocations` is the default (#1504); the env var wins.
303
+ const sourceLocations = dev && sourceLocationsRequested(process.env, devTooling.sourceLocations);
298
304
  if (sourceLocations) {
299
305
  registerSourceLocationHook(appDir);
300
306
  // Module URLs are realpaths, so a symlinked app dir registers both forms.
package/src/dev-embed.js CHANGED
@@ -2,8 +2,9 @@
2
2
  * The dev embed bridge, SERVER half (#1498).
3
3
  *
4
4
  * `webjs dev` with `WEBJS_EMBED_ORIGINS` set (a comma-separated list of parent
5
- * origins, e.g. `https://crisp.app,http://localhost:8080`) makes an app ready
6
- * to be previewed inside an iframe on those origins:
5
+ * origins, e.g. `https://builder.example,http://localhost:8080`), or the app's
6
+ * `webjs.dev.embedOrigins` list (#1504), makes an app ready to be previewed
7
+ * inside an iframe on those origins:
7
8
  *
8
9
  * 1. every HTML document gets a small inline script (the browser half,
9
10
  * `dev-embed-client.js`) that reports page state to the parent and takes
@@ -73,6 +74,23 @@ export function parseEmbedOrigins(raw, opts) {
73
74
  return out;
74
75
  }
75
76
 
77
+ /**
78
+ * The effective embed origins (#1504): `WEBJS_EMBED_ORIGINS`, when set and
79
+ * non-empty, replaces the app's `webjs.dev.embedOrigins`; otherwise the config
80
+ * list applies. Both go through `parseEmbedOrigins`, so a bad entry is dropped
81
+ * with the same warning wherever it was written.
82
+ *
83
+ * @param {string | undefined} envRaw
84
+ * @param {unknown} configured the app's `webjs.dev.embedOrigins`
85
+ * @param {{ warn?: (msg: string) => void }} [opts]
86
+ * @returns {string[]}
87
+ */
88
+ export function resolveEmbedOrigins(envRaw, configured, opts) {
89
+ if (typeof envRaw === 'string' && envRaw.trim()) return parseEmbedOrigins(envRaw, opts);
90
+ if (!Array.isArray(configured)) return [];
91
+ return parseEmbedOrigins(configured.filter((o) => typeof o === 'string').join(','), opts);
92
+ }
93
+
76
94
  /**
77
95
  * Record the allowed embed origins for this process. `createRequestHandler`
78
96
  * calls it at construction: the parsed env value in dev, `[]` otherwise.
@@ -64,15 +64,21 @@ const REGEX_AFTER_KEYWORDS = new Set([
64
64
  export const SOURCE_LOCATIONS_ENV = 'WEBJS_SOURCE_LOCATIONS';
65
65
 
66
66
  /**
67
- * Whether the env asks for source locations. Read by the dev handler once, at
68
- * construction; the caller also requires `dev`, so production never consults it.
67
+ * Whether source locations are on (#1499, #1504). The app's
68
+ * `webjs.dev.sourceLocations` sets the default; the env var, when set, wins
69
+ * either way (`1` / `true` on, `0` / `false` off), so one run can flip it
70
+ * without editing package.json. Read by the dev handler once, at construction;
71
+ * the caller also requires `dev`, so production never consults it.
69
72
  *
70
73
  * @param {Record<string, string | undefined>} [env]
74
+ * @param {unknown} [configured] the app's `webjs.dev.sourceLocations`
71
75
  * @returns {boolean}
72
76
  */
73
- export function sourceLocationsRequested(env = process.env) {
77
+ export function sourceLocationsRequested(env = process.env, configured = false) {
74
78
  const v = String(env[SOURCE_LOCATIONS_ENV] || '').trim().toLowerCase();
75
- return v === '1' || v === 'true';
79
+ if (v === '1' || v === 'true') return true;
80
+ if (v === '0' || v === 'false') return false;
81
+ return configured === true;
76
82
  }
77
83
 
78
84
  /**
@@ -202,6 +202,15 @@
202
202
  "description": "Extra directories the dev live-reload watcher follows in addition to the appDir (#894), for content the app reads from outside its own tree (e.g. blog markdown in a repo-root blog/ dir). Each entry is resolved relative to the app root and may escape it (../blog). Read by the server (readDevWatchPathsFromApp).",
203
203
  "type": "array",
204
204
  "items": { "type": "string" }
205
+ },
206
+ "embedOrigins": {
207
+ "description": "Dev embed bridge (#1498, #1504), DEV-ONLY and off by default. Parent origins (e.g. https://builder.example) allowed to frame the dev server: X-Frame-Options is dropped and frame-ancestors widened for them, and every document carries a nonce-signed script that posts { source: 'webjs-embed', type } messages to such a parent and takes { source: 'webjs-embed-host', type } commands from it. A set WEBJS_EMBED_ORIGINS env var replaces this list. Read by the server (readDevToolingFromApp). Ignored by webjs start.",
208
+ "type": "array",
209
+ "items": { "type": "string" }
210
+ },
211
+ "sourceLocations": {
212
+ "description": "Dev source locations (#1499, #1504), DEV-ONLY and off by default. When true, element opening tags in the app's html templates carry data-webjs-src=\"<app-relative-file>:<line>\" in SSR output and client renders. WEBJS_SOURCE_LOCATIONS=1 or 0 overrides it for one run. Read by the server (readDevToolingFromApp). Ignored by webjs start.",
213
+ "type": "boolean"
205
214
  }
206
215
  }
207
216
  },