@webjsdev/server 0.9.0 → 0.10.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/README.md +4 -1
- package/index.d.ts +793 -125
- package/index.js +8 -4
- package/package.json +3 -3
- package/src/action-flash.js +185 -0
- package/src/action-params.js +156 -0
- package/src/action-route.js +8 -9
- package/src/action-seed.js +44 -5
- package/src/actions.js +97 -15
- package/src/api.js +67 -10
- package/src/app-layout.js +133 -0
- package/src/asset-hash.js +82 -49
- package/src/auth.js +612 -108
- package/src/browser-entries.js +2 -17
- package/src/build-epoch.js +1 -1
- package/src/cache-tags.js +5 -1
- package/src/check/rules-actions.js +37 -1
- package/src/check/rules-components.js +114 -40
- package/src/check/rules-imports.js +175 -4
- package/src/check/rules-route-modules.js +200 -0
- package/src/check/rules-routes.js +55 -0
- package/src/check/rules-routing.js +143 -95
- package/src/check/rules-typescript.js +24 -2
- package/src/check/rules.js +104 -9
- package/src/check/runner-support.js +53 -171
- package/src/check/runner.js +22 -6
- package/src/client-signals.js +171 -0
- package/src/component-scanner.js +22 -16
- package/src/context.js +152 -16
- package/src/cookie-store.js +269 -0
- package/src/core-exports.js +190 -0
- package/src/csrf.js +21 -3
- package/src/dev/bun-app-source.js +95 -5
- package/src/dev/config.js +7 -51
- package/src/dev/dev-service-worker.js +48 -0
- package/src/dev/esm-hot-hooks.js +373 -0
- package/src/dev/handler.js +502 -155
- package/src/dev/helpers.js +127 -96
- package/src/dev/hot-host.js +33 -6
- package/src/dev/poke-state.js +110 -0
- package/src/dev/serve.js +177 -129
- package/src/dev/server.js +92 -33
- package/src/dev/specifier-versions.js +125 -0
- package/src/dev/watch-ignore.js +49 -4
- package/src/dev/watch-poll.js +78 -0
- package/src/dev/watch-tree.js +59 -22
- package/src/dev-classify.js +59 -26
- package/src/dev-error.js +104 -3
- package/src/dev-hot-classify.js +291 -0
- package/src/dev-hot-client.js +245 -0
- package/src/dev-import.js +22 -5
- package/src/dev-overlay.js +26 -0
- package/src/dev-ready-probe.js +69 -0
- package/src/dev-regenerate.js +2 -2
- package/src/dev-reload-hold.js +49 -20
- package/src/dev-reload-reason.js +61 -0
- package/src/dev-reload-worker.js +62 -13
- package/src/dev-secret.js +73 -0
- package/src/dev-source-locations-hook.js +4 -1
- package/src/dev-source-locations.js +33 -2
- package/src/dev.js +0 -1
- package/src/env-files.js +124 -0
- package/src/file-storage.js +2 -2
- package/src/foreign-imports.js +228 -0
- package/src/form-action-identity.js +52 -25
- package/src/form-dispatch.js +142 -66
- package/src/html-cache.js +35 -4
- package/src/importmap.js +2 -2
- package/src/instrumentation.js +59 -3
- package/src/js-scan.js +5 -4
- package/src/jwe.js +155 -0
- package/src/lazy-components.js +166 -0
- package/src/listener-core.js +4 -2
- package/src/lit-hints.js +172 -0
- package/src/middleware-load.js +248 -0
- package/src/module-graph.js +73 -47
- package/src/module-side-effect.js +284 -0
- package/src/public-files.js +73 -0
- package/src/redirect-location.js +19 -0
- package/src/redirects.js +9 -1
- package/src/route-frontier.js +151 -0
- package/src/route-report.js +130 -0
- package/src/router.js +120 -13
- package/src/session.js +32 -10
- package/src/ssr/client-router-flag.js +1 -1
- package/src/ssr/document.js +37 -13
- package/src/ssr/head.js +78 -23
- package/src/ssr/preloads.js +31 -45
- package/src/ssr/render.js +241 -55
- package/src/ssr/responses.js +51 -4
- package/src/ssr/shipped-modules.js +22 -16
- package/src/testing.d.ts +71 -2
- package/src/testing.js +103 -21
- package/src/thenable-params.js +26 -0
- package/src/vendor/jspm.js +12 -1
- package/src/vendor/pins.js +9 -8
- package/src/vendor/scanner.js +14 -9
- package/src/vendor.js +5 -3
- package/webjs-config.schema.json +1 -6
- package/src/component-elision.js +0 -1828
- package/src/elision-differential.js +0 -101
- package/src/elision-report.js +0 -193
package/src/asset-hash.js
CHANGED
|
@@ -71,25 +71,46 @@ let _appDir = '';
|
|
|
71
71
|
let _coreDir = '';
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* A fingerprint of the current
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* property of the IMPORTED component
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* immutable
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* `ensureReady` after `analyzeElision`, re-set on every rebuild.
|
|
74
|
+
* A fingerprint of the current `static lazy` component set, folded into an
|
|
75
|
+
* APP MODULE's hash. An app module's SERVED body is not its raw source:
|
|
76
|
+
* `dev.js` runs `deferLazyImportsFromSource` over it, rewriting a side-effect
|
|
77
|
+
* import of a lazy component into a lazy-loader registration. That rewrite is
|
|
78
|
+
* a property of the IMPORTED component, not of the importer's own bytes, so
|
|
79
|
+
* when a component gains or loses `static lazy` the importer's served body
|
|
80
|
+
* changes while its source is byte-identical. Hashing the source alone would
|
|
81
|
+
* keep the same `?v`, and a returning client would hold the stale immutable
|
|
82
|
+
* copy for the full immutable TTL. Folding the fingerprint into the hash busts
|
|
83
|
+
* every app module's url on a change, so the stale-copy window cannot open.
|
|
84
|
+
* Empty when no component is lazy (then an app module's hash is exactly
|
|
85
|
+
* `sha256(bytes)`), and only mixed for files under `_appDir` (core / public
|
|
86
|
+
* assets are never rewritten at serve time). Set by `dev.js`'s `ensureReady`
|
|
87
|
+
* after the boot analysis, re-set on every rebuild.
|
|
89
88
|
*
|
|
90
89
|
* @type {string}
|
|
91
90
|
*/
|
|
92
|
-
let
|
|
91
|
+
let _serveFp = '';
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The dev browser module versions (#1689), or null outside dev. When set,
|
|
95
|
+
* `withAssetHash` appends `?t=<n>` to a same-origin url whose file has a
|
|
96
|
+
* version above 0 instead of returning it unchanged, so the boot script, the
|
|
97
|
+
* modulepreload hints and the lazy-loader map name a bumped module by the same
|
|
98
|
+
* url its importers' rewritten specifiers do (`dev/specifier-versions.js`).
|
|
99
|
+
* One module, one url per document: two urls would be two module instances.
|
|
100
|
+
* @type {((abs: string) => number) | null}
|
|
101
|
+
*/
|
|
102
|
+
let _devVersionOf = null;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Install (or clear, with null) the dev browser module version lookup.
|
|
106
|
+
* `dev/handler.js` passes `state.browserVersions.versionOf` in dev only.
|
|
107
|
+
* @param {((abs: string) => number) | null} fn
|
|
108
|
+
* @returns {void}
|
|
109
|
+
*/
|
|
110
|
+
export function setDevModuleVersions(fn) {
|
|
111
|
+
_devVersionOf = typeof fn === 'function' ? fn : null;
|
|
112
|
+
bumpBuildEpoch();
|
|
113
|
+
}
|
|
93
114
|
|
|
94
115
|
/**
|
|
95
116
|
* Memoized per-file content hash. Keyed by absolute path. Cleared on the
|
|
@@ -118,20 +139,20 @@ export function setAssetRoots(opts) {
|
|
|
118
139
|
}
|
|
119
140
|
|
|
120
141
|
/**
|
|
121
|
-
* Set the
|
|
122
|
-
* `
|
|
123
|
-
* hash cache so every app module re-hashes against the new
|
|
124
|
-
* calls this from `ensureReady` after
|
|
125
|
-
* rebuild), passing a stable digest of the
|
|
126
|
-
*
|
|
142
|
+
* Set the lazy-component fingerprint folded into app-module hashes (see
|
|
143
|
+
* `_serveFp`). A no-op when the value is unchanged; on a change it clears the
|
|
144
|
+
* hash cache so every app module re-hashes against the new set. `dev.js`
|
|
145
|
+
* calls this from `ensureReady` after the boot analysis (and again on each
|
|
146
|
+
* rebuild), passing a stable digest of the lazy-component set (or `''` when
|
|
147
|
+
* no component is lazy).
|
|
127
148
|
*
|
|
128
149
|
* @param {string} fp
|
|
129
150
|
* @returns {void}
|
|
130
151
|
*/
|
|
131
|
-
export function
|
|
152
|
+
export function setServeFingerprint(fp) {
|
|
132
153
|
const next = fp || '';
|
|
133
|
-
if (next ===
|
|
134
|
-
|
|
154
|
+
if (next === _serveFp) return;
|
|
155
|
+
_serveFp = next;
|
|
135
156
|
_hashCache.clear();
|
|
136
157
|
// Past the early return on purpose: an unchanged fingerprint moves no hash,
|
|
137
158
|
// so the epoch-keyed memos over those hashes stay valid (#1644).
|
|
@@ -183,8 +204,8 @@ export function assetHashFor(absPath) {
|
|
|
183
204
|
try {
|
|
184
205
|
const bytes = readFileSync(absPath);
|
|
185
206
|
const h = createHash('sha256').update(bytes);
|
|
186
|
-
// An app module's served body is
|
|
187
|
-
// fingerprint in (see `
|
|
207
|
+
// An app module's served body is rewritten at serve time, so fold the
|
|
208
|
+
// fingerprint in (see `_serveFp`). Core and `public/` files are never
|
|
188
209
|
// transformed, so they hash over their bytes alone, which the two
|
|
189
210
|
// exclusions below enforce rather than merely assume. Empty fp leaves an
|
|
190
211
|
// app module's hash at exactly `sha256(bytes)`.
|
|
@@ -200,18 +221,18 @@ export function assetHashFor(absPath) {
|
|
|
200
221
|
// in this monorepo, where the workspace symlink lands outside, which
|
|
201
222
|
// is exactly why no in-repo test ever caught it.
|
|
202
223
|
//
|
|
203
|
-
// Neither file set is
|
|
204
|
-
// `fileResponse`), so folding the
|
|
205
|
-
// correctness: any deploy that merely flips an UNRELATED component
|
|
206
|
-
//
|
|
224
|
+
// Neither file set is rewritten (core is served verbatim by
|
|
225
|
+
// `fileResponse`), so folding the fingerprint in buys nothing and costs
|
|
226
|
+
// correctness: any deploy that merely flips an UNRELATED component's
|
|
227
|
+
// `static lazy` would move their urls, re-downloading the whole
|
|
207
228
|
// core runtime plus every marked stylesheet, image, and script even though
|
|
208
229
|
// not one of those bytes changed. That is the precise cost a content hash
|
|
209
230
|
// exists to avoid.
|
|
210
|
-
if (
|
|
231
|
+
if (_serveFp && _appDir && absPath.startsWith(_appDir + sep)
|
|
211
232
|
&& !absPath.startsWith(join(_appDir, 'public') + sep)
|
|
212
233
|
&& !(_coreDir && absPath.startsWith(_coreDir + sep))) {
|
|
213
234
|
h.update('\0');
|
|
214
|
-
h.update(
|
|
235
|
+
h.update(_serveFp);
|
|
215
236
|
}
|
|
216
237
|
const hash = h.digest('hex').slice(0, HASH_LEN);
|
|
217
238
|
_hashCache.set(absPath, hash);
|
|
@@ -310,7 +331,7 @@ function resolveUrlToFile(url) {
|
|
|
310
331
|
* @returns {string}
|
|
311
332
|
*/
|
|
312
333
|
export function withAssetHash(url, basePath = '') {
|
|
313
|
-
if (!_enabled) return url;
|
|
334
|
+
if (!_enabled && !_devVersionOf) return url;
|
|
314
335
|
if (typeof url !== 'string' || url[0] !== '/' || url[1] === '/') return url;
|
|
315
336
|
// Resolve against the file system using the base-path-stripped url, so a
|
|
316
337
|
// sub-path-deployed `<basePath>/app/foo.js` maps to `<appDir>/app/foo.js`.
|
|
@@ -322,6 +343,11 @@ export function withAssetHash(url, basePath = '') {
|
|
|
322
343
|
}
|
|
323
344
|
const abs = resolveUrlToFile(forResolve);
|
|
324
345
|
if (!abs) return url;
|
|
346
|
+
if (!_enabled) {
|
|
347
|
+
// Dev: no content hash, only the module's browser version when it moved.
|
|
348
|
+
const n = /** @type {(abs: string) => number} */ (_devVersionOf)(abs);
|
|
349
|
+
return n > 0 ? `${url}${url.includes('?') ? '&' : '?'}t=${n}` : url;
|
|
350
|
+
}
|
|
325
351
|
const hash = assetHashFor(abs);
|
|
326
352
|
if (!hash) return url;
|
|
327
353
|
// The freshly-emitted url never already carries a query, but stay robust:
|
|
@@ -335,14 +361,12 @@ export function withAssetHash(url, basePath = '') {
|
|
|
335
361
|
* body behind the isomorphic `asset()` helper (#1194); `dev.js` installs it
|
|
336
362
|
* via `setAssetUrlProvider` at boot.
|
|
337
363
|
*
|
|
338
|
-
* The
|
|
339
|
-
*
|
|
340
|
-
* user-derived data in (`asset(user.avatarPath)`)
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
* `public/`, no traversal. Anything else returns unchanged WITHOUT touching
|
|
345
|
-
* the disk, so a rejected path costs nothing and reveals nothing.
|
|
364
|
+
* The path is resolved under `<appDir>/public` ONLY (public files are served at
|
|
365
|
+
* the url root, so `/app.css` is `public/app.css`), never the project root, so
|
|
366
|
+
* an app that passed request- or user-derived data in (`asset(user.avatarPath)`)
|
|
367
|
+
* cannot get a content hash published for `/.env`, `/db/app.db`, or a
|
|
368
|
+
* `.server.ts`. Mirror the static-asset route: inside `public/`, no traversal.
|
|
369
|
+
* A path naming no public file returns unchanged.
|
|
346
370
|
*
|
|
347
371
|
* A fragment is split before hashing. `withAssetHash` appends the query, so
|
|
348
372
|
* passing the whole thing would emit `/x.svg#icon?v=H`: the browser then
|
|
@@ -360,14 +384,23 @@ export function resolveAssetUrl(p, bp = '') {
|
|
|
360
384
|
const cuts = [probe.indexOf('?'), probe.indexOf('#')].filter((i) => i !== -1);
|
|
361
385
|
let decoded = probe.slice(0, cuts.length ? Math.min(...cuts) : probe.length);
|
|
362
386
|
try { decoded = decodeURIComponent(decoded); } catch { /* keep raw */ }
|
|
363
|
-
|
|
387
|
+
// Public files are served at the url ROOT (`public/app.css` is `/app.css`,
|
|
388
|
+
// Next.js parity), so the path names a file under `<appDir>/public`. The
|
|
389
|
+
// gate keeps the lookup inside that directory: no traversal, no other file.
|
|
390
|
+
if (decoded.includes('..') || decoded.startsWith('/__webjs/')) return p;
|
|
364
391
|
const h = p.indexOf('#');
|
|
365
392
|
const path = h === -1 ? p : p.slice(0, h);
|
|
366
393
|
const frag = h === -1 ? '' : p.slice(h);
|
|
367
394
|
// An author-supplied query is left alone: it may carry meaning we do not
|
|
368
395
|
// own, and appending would merge with `&`.
|
|
369
396
|
if (path.includes('?')) return p;
|
|
370
|
-
|
|
397
|
+
if (!_enabled || !_appDir) return p;
|
|
398
|
+
const root = join(_appDir, 'public') + sep;
|
|
399
|
+
const abs = join(root, decoded);
|
|
400
|
+
if (!abs.startsWith(root)) return p;
|
|
401
|
+
const hash = assetHashFor(abs);
|
|
402
|
+
if (!hash) return p;
|
|
403
|
+
return `${path}?v=${hash}` + frag;
|
|
371
404
|
}
|
|
372
405
|
|
|
373
406
|
/**
|
|
@@ -458,7 +491,7 @@ function normalizeSpecToResolved(spec, abs) {
|
|
|
458
491
|
*
|
|
459
492
|
* The appended hash is `assetHashFor(resolvedTarget)`, the exact value
|
|
460
493
|
* `withAssetHash` computes for the modulepreload href of the same file (same
|
|
461
|
-
* absolute path -> same hash,
|
|
494
|
+
* absolute path -> same hash, serve-fingerprint fold included), so the two
|
|
462
495
|
* URLs are byte-identical by construction.
|
|
463
496
|
*
|
|
464
497
|
* The fetched URL is made to equal the preload href even when the specifier's
|
|
@@ -488,11 +521,11 @@ function normalizeSpecToResolved(spec, abs) {
|
|
|
488
521
|
* splicing `?v` into the served string's value).
|
|
489
522
|
*
|
|
490
523
|
* A pure pass-through when fingerprinting is disabled (dev, or roots unset), so
|
|
491
|
-
* dev output stays byte-identical. Runs AFTER `
|
|
492
|
-
* serve path, so
|
|
493
|
-
* is not matched.
|
|
524
|
+
* dev output stays byte-identical. Runs AFTER `deferLazyImportsFromSource` in
|
|
525
|
+
* the serve path, so a deferred side-effect import (already replaced by a
|
|
526
|
+
* lazy-loader registration that carries its own versioned url) is not matched.
|
|
494
527
|
*
|
|
495
|
-
* @param {string} source the served module source (
|
|
528
|
+
* @param {string} source the served module source (lazy imports deferred, type-stripped if TS)
|
|
496
529
|
* @param {string} importerAbs absolute path of the module being served
|
|
497
530
|
* @returns {string}
|
|
498
531
|
*/
|