bosia 0.9.2 → 0.9.3
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/client/App.svelte +23 -12
- package/src/core/html.ts +8 -3
- package/src/core/server.ts +10 -1
package/package.json
CHANGED
|
@@ -394,18 +394,29 @@
|
|
|
394
394
|
|
|
395
395
|
settleScroll();
|
|
396
396
|
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
397
|
+
// Re-emit the head from server metadata. `data-bosia-meta` (html.ts `OWNED`)
|
|
398
|
+
// marks exactly the tags metadata() owns, so headExtras, the framework's own
|
|
399
|
+
// static tags and <svelte:head> output are never touched. `result === null`
|
|
400
|
+
// with server data means the fetch failed — leave the head as it is; without
|
|
401
|
+
// server data there is no metadata() at all, so clearing matches SSR.
|
|
402
|
+
if (result || !match.route.hasServerData) {
|
|
403
|
+
const md = result?.metadata;
|
|
404
|
+
document.querySelectorAll("[data-bosia-meta]").forEach((el) => el.remove());
|
|
405
|
+
// A page that declares no title keeps the previous one rather than flashing
|
|
406
|
+
// the "Bosia App" fallback. Give metadata() a title if that matters.
|
|
407
|
+
if (md?.title) document.title = md.title;
|
|
408
|
+
if (md?.lang) document.documentElement.lang = md.lang;
|
|
409
|
+
const emit = (tag: "meta" | "link", attrs: Record<string, string | undefined>) => {
|
|
410
|
+
const el = document.createElement(tag);
|
|
411
|
+
for (const [k, v] of Object.entries(attrs)) if (v != null) el.setAttribute(k, v);
|
|
412
|
+
el.setAttribute("data-bosia-meta", "");
|
|
413
|
+
document.head.appendChild(el);
|
|
414
|
+
};
|
|
415
|
+
if (md?.description) emit("meta", { name: "description", content: md.description });
|
|
416
|
+
for (const m of md?.meta ?? [])
|
|
417
|
+
emit("meta", { name: m.name, property: m.property, content: m.content });
|
|
418
|
+
for (const l of md?.link ?? [])
|
|
419
|
+
emit("link", { rel: l.rel, href: l.href, hreflang: l.hreflang });
|
|
409
420
|
}
|
|
410
421
|
|
|
411
422
|
settle({ url, params: match.params });
|
package/src/core/html.ts
CHANGED
|
@@ -287,6 +287,11 @@ const SPINNER =
|
|
|
287
287
|
`border-radius:50%;animation:__bs__ .8s linear infinite}` +
|
|
288
288
|
`@keyframes __bs__{to{transform:rotate(360deg)}}</style><i></i></div>`;
|
|
289
289
|
|
|
290
|
+
/** Marks the tags `metadata()` owns, so the client router can replace exactly
|
|
291
|
+
* these on navigation and leave `headExtras`, the framework's own static tags
|
|
292
|
+
* and `<svelte:head>` output alone. Read by `client/App.svelte`. */
|
|
293
|
+
export const OWNED = "data-bosia-meta";
|
|
294
|
+
|
|
290
295
|
/** The `metadata()` tags themselves, indented head-ready. Shared by the streaming
|
|
291
296
|
* path (buildMetadataChunk) and the non-streaming one (buildHtml) so the two
|
|
292
297
|
* renderers cannot drift on what `metadata()` emits. */
|
|
@@ -295,21 +300,21 @@ export function metadataTags(metadata: Metadata | null): string {
|
|
|
295
300
|
let out = "";
|
|
296
301
|
if (metadata.title) out += ` <title>${escapeHtml(metadata.title)}</title>\n`;
|
|
297
302
|
if (metadata.description) {
|
|
298
|
-
out += ` <meta name="description" content="${escapeAttr(metadata.description)}">\n`;
|
|
303
|
+
out += ` <meta name="description" content="${escapeAttr(metadata.description)}" ${OWNED}>\n`;
|
|
299
304
|
}
|
|
300
305
|
if (metadata.meta) {
|
|
301
306
|
for (const m of metadata.meta) {
|
|
302
307
|
const attrs = m.name
|
|
303
308
|
? `name="${escapeAttr(m.name)}"`
|
|
304
309
|
: `property="${escapeAttr(m.property ?? "")}"`;
|
|
305
|
-
out += ` <meta ${attrs} content="${escapeAttr(m.content)}">\n`;
|
|
310
|
+
out += ` <meta ${attrs} content="${escapeAttr(m.content)}" ${OWNED}>\n`;
|
|
306
311
|
}
|
|
307
312
|
}
|
|
308
313
|
if (metadata.link) {
|
|
309
314
|
for (const l of metadata.link) {
|
|
310
315
|
let attrs = `rel="${escapeAttr(l.rel)}" href="${escapeAttr(l.href)}"`;
|
|
311
316
|
if (l.hreflang) attrs += ` hreflang="${escapeAttr(l.hreflang)}"`;
|
|
312
|
-
out += ` <link ${attrs}>\n`;
|
|
317
|
+
out += ` <link ${attrs} ${OWNED}>\n`;
|
|
313
318
|
}
|
|
314
319
|
}
|
|
315
320
|
return out;
|
package/src/core/server.ts
CHANGED
|
@@ -303,7 +303,16 @@ async function resolve(event: RequestEvent): Promise<Response> {
|
|
|
303
303
|
cookies,
|
|
304
304
|
request,
|
|
305
305
|
);
|
|
306
|
-
|
|
306
|
+
// Explicit whitelist, not a spread: `metadata.data` feeds load() on the
|
|
307
|
+
// server and may hold secrets — it must not reach the client.
|
|
308
|
+
if (meta)
|
|
309
|
+
metadata = {
|
|
310
|
+
title: meta.title,
|
|
311
|
+
description: meta.description,
|
|
312
|
+
meta: meta.meta,
|
|
313
|
+
link: meta.link,
|
|
314
|
+
lang: meta.lang,
|
|
315
|
+
};
|
|
307
316
|
} catch {
|
|
308
317
|
/* non-fatal */
|
|
309
318
|
}
|