bun-plugin-stx 0.2.293 → 0.2.295

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.
@@ -986,8 +986,8 @@ function getRouterScript() {
986
986
  // Unlike a fragment, a document holds the answer directly, so there is
987
987
  // nothing to infer from markers or ask the server for: look for the script.
988
988
  // Both emitted shapes carry data-stx-runtime \u2014 inline, and the serve-mode
989
- // src="/_stx/runtime.js" \u2014 with the content sniff behind it for a document
990
- // rendered by a server old enough not to stamp the attribute.
989
+ // src="/_stx/runtime.<hash>.js" \u2014 with the content sniff behind it for a
990
+ // document rendered by a server old enough not to stamp the attribute.
991
991
  function documentShipsRuntime(doc,html){
992
992
  if(doc&&doc.querySelector&&doc.querySelector('script[data-stx-runtime]'))return true;
993
993
  return html.indexOf("'use strict';var cloakStyle")!==-1
@@ -998,6 +998,22 @@ function getRouterScript() {
998
998
  ||(code.indexOf('_cleanupContainer')!==-1&&code.indexOf('signals runtime loading')!==-1)
999
999
  ||code.indexOf("'use strict';var cloakStyle")!==-1;
1000
1000
  }
1001
+ // The runtime and the router themselves, as external script tags.
1002
+ //
1003
+ // Serve mode links both by content hash, so after a deploy the incoming
1004
+ // page's copy has a different URL from the one this document loaded. Every
1005
+ // path below that loads a script[src] it has not seen yet would then run a
1006
+ // second router and a second runtime over the live ones \u2014 two click
1007
+ // handlers on every link, a fresh window.stx under scopes created by the
1008
+ // old one. A new version is picked up by the build-skew reload instead; it
1009
+ // is never loaded into a running page. The attrs form is for the fragment
1010
+ // path, which sees a tag as text.
1011
+ function isSharedClientScript(el){
1012
+ return !!(el&&el.hasAttribute&&(el.hasAttribute('data-stx-runtime')||el.hasAttribute('data-stx-router')));
1013
+ }
1014
+ function isSharedClientScriptAttrs(attrs){
1015
+ return /(?:^|\\s)data-stx-(?:runtime|router)(?:[\\s=]|$)/i.test(attrs||'');
1016
+ }
1001
1017
  // A fragment never carries the signals runtime: the server strips the runtime
1002
1018
  // IIFE out of it, and every re-execution path here filters it out again. So a
1003
1019
  // page that never needed a runtime cannot hydrate a fragment that does \u2014 the
@@ -1554,7 +1570,7 @@ else {
1554
1570
  // file is where the page's factory usually lives.
1555
1571
  var srcMatch=attrs.match(/\\bsrc\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))/i);
1556
1572
  var fragSrc=srcMatch&&(srcMatch[1]||srcMatch[2]||srcMatch[3]);
1557
- if(fragSrc){fragExternalScripts.push(fragSrc);return ''}
1573
+ if(fragSrc){if(!isSharedClientScriptAttrs(attrs))fragExternalScripts.push(fragSrc);return ''}
1558
1574
  if(code&&code.trim()&&!isSignalsRuntimeScript({hasAttribute:function(name){return name==='data-stx-runtime'&&attrs.indexOf('data-stx-runtime')!==-1}},code)){
1559
1575
  var slot='fragment-'+(++fragScriptId);
1560
1576
  var scoped=/(?:^|\\s)data-stx-scoped(?:\\s|=|$)/i.test(attrs);
@@ -1902,7 +1918,8 @@ else {
1902
1918
  var text=s.textContent||'';
1903
1919
  // Not discarded: queued against docBase, so a relative src resolves
1904
1920
  // against the page being navigated to rather than the current URL.
1905
- if(s.hasAttribute('src')){routedExternalScripts.push(s.getAttribute('src'));s.remove();return}
1921
+ // The runtime and router are dropped, not queued: see isSharedClientScript.
1922
+ if(s.hasAttribute('src')){if(!isSharedClientScript(s))routedExternalScripts.push(s.getAttribute('src'));s.remove();return}
1906
1923
  var type=(s.getAttribute('type')||'').trim().toLowerCase();
1907
1924
  var executable=!type||type==='text/javascript'||type==='application/javascript'||type==='module';
1908
1925
  if(!executable||!text.trim())return;
@@ -1959,6 +1976,7 @@ else {
1959
1976
  document.querySelectorAll('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});
1960
1977
  var extPromises=[];
1961
1978
  doc.querySelectorAll('head script[src]').forEach(function(s){
1979
+ if(isSharedClientScript(s))return;
1962
1980
  // Fetched-document script: resolve against the page being navigated to
1963
1981
  // (docBase), not the origin root \u2014 same rationale as the <link> reconcile (#1777).
1964
1982
  var src=new URL(s.getAttribute('src'),docBase).href;
package/dist/serve.d.ts CHANGED
@@ -99,6 +99,23 @@ export declare function isProductionServe(): boolean;
99
99
  * given URL, so it gets the long immutable lifetime.
100
100
  */
101
101
  export declare function staticCacheControl(pathname: string, production?: boolean): string;
102
+ /**
103
+ * `Cache-Control` for the shared runtime and router scripts.
104
+ *
105
+ * Pages link them by content hash (getServeClientAsset), so a request whose
106
+ * hash is the current content's can never see different bytes under that URL:
107
+ * cache it for a year.
108
+ *
109
+ * Anything else is answered with today's content but must not be kept. The
110
+ * unhashed URL is what HTML rendered before content addressing still points
111
+ * at, and a stale hash is a page from the previous deploy; either one caching
112
+ * the current bytes would pin them there. `no-cache`, not a short max-age:
113
+ * Cloudflare raises a max-age below its browser-TTL setting (four hours by
114
+ * default) to that setting, which is exactly how the old fixed URL served a
115
+ * previous release's router for hours after a deploy. It leaves `no-cache`
116
+ * alone, and the ETag keeps the revalidation to a 304.
117
+ */
118
+ export declare function sharedScriptCacheControl(requestedHash: string | undefined, currentHash: string): string;
102
119
  /**
103
120
  * Render the built-in fallback 404 page.
104
121
  *
@@ -170,6 +187,8 @@ export declare function serve(options: ServeOptions): Promise<void>;
170
187
  * is the name every double-submit implementation in this ecosystem uses.
171
188
  */
172
189
  export declare const CSRF_COOKIE: 'X-CSRF-Token';
190
+ /** `/_stx/runtime.js`, `/_stx/router.<16 hex>.js`, … — the shared client scripts. */
191
+ export declare const SHARED_SCRIPT_PATH: unknown;
173
192
  /**
174
193
  * Build the candidate regexes for a dynamic route file (stacksjs/stx#1927).
175
194
  *
@@ -227,6 +246,7 @@ export declare interface ServeOptions {
227
246
  cssCacheLimit?: number
228
247
  prewarmRenderCache?: boolean | number
229
248
  imageWarmupGraceMs?: number
249
+ imageWarmup?: boolean
230
250
  publicDir?: string
231
251
  quiet?: boolean
232
252
  autoIncrementPort?: boolean | number
package/dist/serve.js CHANGED
@@ -8940,6 +8940,10 @@ function staticCacheControl(pathname, production = isProductionServe()) {
8940
8940
  const fingerprinted = /[.-][0-9a-f]{8,}\.[a-z0-9]+$/i.test(pathname);
8941
8941
  return fingerprinted ? "public, max-age=31536000, immutable" : "public, max-age=3600";
8942
8942
  }
8943
+ var SHARED_SCRIPT_PATH = /^\/_stx\/(runtime|router)(?:\.([0-9a-f]{16}))?\.js$/;
8944
+ function sharedScriptCacheControl(requestedHash, currentHash) {
8945
+ return requestedHash === currentHash ? "public, max-age=31536000, immutable" : "no-cache";
8946
+ }
8943
8947
  function render404Page(opts) {
8944
8948
  const { path, routes = [], isProduction } = opts;
8945
8949
  const baseStyles = `
@@ -9206,6 +9210,7 @@ async function serve(options) {
9206
9210
  markPlaceholdersReady = resolve13;
9207
9211
  });
9208
9212
  let placeholdersAreReady = false;
9213
+ const imageWarmupEnabled = (options.imageWarmup ?? stxConfig.imageWarmup ?? true) !== false;
9209
9214
  const imageWarmupGraceMs = Math.max(0, options.imageWarmupGraceMs ?? 1000);
9210
9215
  function awaitImageWarmup() {
9211
9216
  if (placeholdersAreReady || imageWarmupGraceMs === 0)
@@ -9700,7 +9705,7 @@ function __stxOverlay(errs){
9700
9705
  pageMiddlewarePatterns.length = 0;
9701
9706
  await Promise.all(files.map((f) => detectPageMiddleware(f)));
9702
9707
  try {
9703
- const { Router } = await import("./chunk-nb1x6zdk.js");
9708
+ const { Router } = await import("./chunk-awcgpf66.js");
9704
9709
  const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
9705
9710
  const router = new Router(process19.cwd(), { pagesDirs, stateDir: stateDirName() });
9706
9711
  if (!options.quiet)
@@ -10277,6 +10282,11 @@ ${block}`;
10277
10282
  });
10278
10283
  }
10279
10284
  async function runImageWarmup() {
10285
+ if (!imageWarmupEnabled) {
10286
+ placeholdersAreReady = true;
10287
+ markPlaceholdersReady();
10288
+ return;
10289
+ }
10280
10290
  try {
10281
10291
  const stx = await stxModule;
10282
10292
  const publicRoot = nodePath.resolve(process19.cwd(), publicDir);
@@ -10427,13 +10437,15 @@ ${block}`;
10427
10437
  }
10428
10438
  });
10429
10439
  }
10430
- if (path === "/_stx/runtime.js" || path === "/_stx/router.js") {
10440
+ const sharedScript = path.match(SHARED_SCRIPT_PATH);
10441
+ if (sharedScript) {
10431
10442
  const stx = await stxModule;
10432
- const content2 = path === "/_stx/runtime.js" ? await stx.getCachedSignalsRuntime(stxConfig.debug === true) : await stx.getCachedRouterScript();
10433
- const etag = `"${Bun.hash(content2).toString(16)}"`;
10443
+ const asset = await stx.getServeClientAsset(sharedScript[1], sharedScript[1] === "runtime" && stxConfig.debug === true);
10444
+ const content2 = asset.content;
10445
+ const etag = `"${asset.hash}"`;
10434
10446
  const headers = {
10435
10447
  "Content-Type": "application/javascript; charset=utf-8",
10436
- "Cache-Control": production ? "public, max-age=3600, stale-while-revalidate=86400" : "public, max-age=0, must-revalidate",
10448
+ "Cache-Control": sharedScriptCacheControl(sharedScript[2], asset.hash),
10437
10449
  ETag: etag,
10438
10450
  ...corsHeaders
10439
10451
  };
@@ -11090,6 +11102,7 @@ if (import.meta.main) {
11090
11102
  export {
11091
11103
  usableErrorPage,
11092
11104
  staticCacheControl,
11105
+ sharedScriptCacheControl,
11093
11106
  serve,
11094
11107
  routeSpecificity,
11095
11108
  resolveServeOpenUrl,
@@ -11105,5 +11118,6 @@ export {
11105
11118
  bundleBrowserAsset,
11106
11119
  buildDynamicRouteRegexes,
11107
11120
  boundedCache,
11121
+ SHARED_SCRIPT_PATH,
11108
11122
  CSRF_COOKIE
11109
11123
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bun-plugin-stx",
3
3
  "type": "module",
4
- "version": "0.2.293",
4
+ "version": "0.2.295",
5
5
  "description": "A Bun plugin that allows for using Laravel Blade-like syntax.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",