bosia 0.6.18 → 0.6.20
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 +1 -1
- package/src/core/build.ts +11 -1
- package/src/core/client/hydrate.ts +39 -0
- package/src/core/html.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosia",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A fast, batteries-included fullstack framework — SSR · Svelte 5 Runes · Bun · ElysiaJS. File-based routing inspired by SvelteKit. No Node.js, no Vite, no adapters.",
|
|
6
6
|
"keywords": [
|
package/src/core/build.ts
CHANGED
|
@@ -159,7 +159,7 @@ const clientPromise = Bun.build({
|
|
|
159
159
|
target: "browser",
|
|
160
160
|
conditions: ["svelte"],
|
|
161
161
|
splitting: true,
|
|
162
|
-
naming: { chunk: "[name]-[hash].[ext]" },
|
|
162
|
+
naming: { entry: "[name]-[hash].[ext]", chunk: "[name]-[hash].[ext]" },
|
|
163
163
|
minify: isProduction,
|
|
164
164
|
sourcemap: isProduction ? "none" : "linked",
|
|
165
165
|
define: {
|
|
@@ -222,10 +222,19 @@ if (!isProduction && svelteMapCache.size > 0) {
|
|
|
222
222
|
// 6. Collect output files for dist/manifest.json
|
|
223
223
|
const jsFiles: string[] = [];
|
|
224
224
|
const cssFiles: string[] = [];
|
|
225
|
+
// 0.6.19 hashed the entry filename; the old `f === "hydrate.js"` exact-match
|
|
226
|
+
// no longer hits, and the fallback `startsWith("hydrate")` picks the first
|
|
227
|
+
// hydrate-* chunk by array order — which can be a small leaf module (e.g.
|
|
228
|
+
// `src/lib/version.ts`) that sorts before the real entry. Use Bun's
|
|
229
|
+
// `output.kind === "entry-point"` instead so we pin the actual entry.
|
|
230
|
+
let clientEntry: string | null = null;
|
|
225
231
|
for (const output of clientResult.outputs) {
|
|
226
232
|
const rel = relative(`${OUT_DIR}/client`, output.path);
|
|
227
233
|
if (output.path.endsWith(".js")) jsFiles.push(rel);
|
|
228
234
|
if (output.path.endsWith(".css")) cssFiles.push(rel);
|
|
235
|
+
if ((output as { kind?: string }).kind === "entry-point" && output.path.endsWith(".js")) {
|
|
236
|
+
clientEntry = rel;
|
|
237
|
+
}
|
|
229
238
|
}
|
|
230
239
|
|
|
231
240
|
// Entry is always "index.js" due to naming: { entry: "index.[ext]" }
|
|
@@ -241,6 +250,7 @@ const distManifest = {
|
|
|
241
250
|
js: jsFiles,
|
|
242
251
|
css: cssFiles,
|
|
243
252
|
entry:
|
|
253
|
+
clientEntry ??
|
|
244
254
|
jsFiles.find((f) => f === "hydrate.js") ??
|
|
245
255
|
jsFiles.find((f) => f.startsWith("hydrate")) ??
|
|
246
256
|
"hydrate.js",
|
|
@@ -136,6 +136,45 @@ main().catch((err) => {
|
|
|
136
136
|
console.error("[bosia] hydration failed", err);
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
+
// ─── Stale-Build Recovery ─────────────────────────────────
|
|
140
|
+
// After a deploy, browsers with the old HTML/entry cached may try to import
|
|
141
|
+
// hashed chunks that no longer exist on the server. The dynamic import()
|
|
142
|
+
// rejects and the router's promise chain has no .catch, so the rejection
|
|
143
|
+
// surfaces here as an unhandledrejection. Trigger a single full reload with a
|
|
144
|
+
// cache-busting query so the browser can't serve the old HTML from disk.
|
|
145
|
+
// A 10s sessionStorage guard prevents an infinite reload loop if the new
|
|
146
|
+
// build is also broken.
|
|
147
|
+
if (typeof window !== "undefined" && process.env.NODE_ENV === "production") {
|
|
148
|
+
const KEY = "bosia:reload-attempt";
|
|
149
|
+
const STALE_CHUNK =
|
|
150
|
+
/Failed to fetch dynamically imported module|Importing a module script failed|error loading dynamically imported module|Loading chunk|ChunkLoadError/i;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const last = Number(sessionStorage.getItem(KEY) ?? 0);
|
|
154
|
+
if (last && Date.now() - last < 10_000) {
|
|
155
|
+
sessionStorage.removeItem(KEY);
|
|
156
|
+
console.error("[bosia] reload guard hit — new build may also be broken.");
|
|
157
|
+
} else {
|
|
158
|
+
window.addEventListener("unhandledrejection", (e) => {
|
|
159
|
+
const msg = String((e.reason as { message?: unknown })?.message ?? e.reason ?? "");
|
|
160
|
+
if (!STALE_CHUNK.test(msg)) return;
|
|
161
|
+
e.preventDefault();
|
|
162
|
+
try {
|
|
163
|
+
sessionStorage.setItem(KEY, String(Date.now()));
|
|
164
|
+
} catch {
|
|
165
|
+
// Storage may be unavailable (private mode) — best effort.
|
|
166
|
+
}
|
|
167
|
+
const sep = window.location.search ? "&" : "?";
|
|
168
|
+
window.location.replace(
|
|
169
|
+
`${window.location.pathname}${window.location.search}${sep}_v=${Date.now()}${window.location.hash}`,
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
// sessionStorage may throw in restricted contexts — give up silently.
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
139
178
|
// ─── Hot Reload (dev only) ────────────────────────────────
|
|
140
179
|
|
|
141
180
|
if (process.env.NODE_ENV !== "production") {
|