@bagelink/blox 1.15.178 → 1.15.182

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.
Files changed (67) hide show
  1. package/dist/{PreviewApp-B8EmNfyH.cjs → PreviewApp-APyemzqx.cjs} +1 -1
  2. package/dist/{PreviewApp-DdkAQqw2.js → PreviewApp-BKTydm92.js} +1 -1
  3. package/dist/{PreviewApp.vue_vue_type_style_index_0_lang-CPBuld3j.cjs → PreviewApp.vue_vue_type_style_index_0_lang-C0ZVst7R.cjs} +6 -6
  4. package/dist/{PreviewApp.vue_vue_type_style_index_0_lang-1JKQVLm2.js → PreviewApp.vue_vue_type_style_index_0_lang-DHb-NJ4C.js} +1 -1
  5. package/dist/api/index.d.ts +22 -0
  6. package/dist/api/index.d.ts.map +1 -1
  7. package/dist/components/BloxRouterView.vue.d.ts +26 -15
  8. package/dist/components/BloxRouterView.vue.d.ts.map +1 -1
  9. package/dist/composables/useLocale.d.ts +7 -0
  10. package/dist/composables/useLocale.d.ts.map +1 -1
  11. package/dist/composables/useLocaleNav.d.ts +2 -2
  12. package/dist/composables/useLocaleNav.d.ts.map +1 -1
  13. package/dist/content/{ContentDetailHost.vue.d.ts → ContentDetailView.vue.d.ts} +1 -1
  14. package/dist/content/ContentDetailView.vue.d.ts.map +1 -0
  15. package/dist/content/ContentHost.vue.d.ts +3 -0
  16. package/dist/content/ContentHost.vue.d.ts.map +1 -0
  17. package/dist/content/index.d.ts +9 -0
  18. package/dist/content/index.d.ts.map +1 -0
  19. package/dist/content/registry.d.ts +73 -0
  20. package/dist/content/registry.d.ts.map +1 -0
  21. package/dist/content/routes.d.ts +48 -0
  22. package/dist/content/routes.d.ts.map +1 -0
  23. package/dist/content/useContentItem.d.ts +37 -5
  24. package/dist/content/useContentItem.d.ts.map +1 -1
  25. package/dist/content/useContentSeo.d.ts +20 -7
  26. package/dist/content/useContentSeo.d.ts.map +1 -1
  27. package/dist/core.d.ts +3 -1
  28. package/dist/core.d.ts.map +1 -1
  29. package/dist/createBloxApp.d.ts.map +1 -1
  30. package/dist/data/useData.d.ts +91 -0
  31. package/dist/data/useData.d.ts.map +1 -0
  32. package/dist/index.cjs +185 -138
  33. package/dist/index.d.ts +7 -13
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.mjs +177 -131
  36. package/dist/localeRoutes.d.ts +15 -0
  37. package/dist/localeRoutes.d.ts.map +1 -0
  38. package/dist/localeStrategy.d.ts +13 -0
  39. package/dist/localeStrategy.d.ts.map +1 -1
  40. package/dist/{prerender-NHAqrTm3.js → prerender-DB1R4jiQ.js} +10 -9
  41. package/dist/{prerender-H72-UrK4.cjs → prerender-DXGy7pee.cjs} +10 -9
  42. package/dist/{core-KRHc4Bob.cjs → routes-C97aL4cQ.cjs} +415 -220
  43. package/dist/{core-Bp2bf8e4.js → routes-bhjTTXfU.js} +444 -249
  44. package/dist/ssg/cli.cjs +1 -1
  45. package/dist/ssg/cli.mjs +1 -1
  46. package/dist/ssg/createSSREntry.d.ts.map +1 -1
  47. package/dist/ssg/index.cjs +28 -19
  48. package/dist/ssg/index.mjs +31 -22
  49. package/dist/ssg/render-resolved-page.d.ts +6 -0
  50. package/dist/ssg/render-resolved-page.d.ts.map +1 -1
  51. package/dist/ssg/seo.d.ts +9 -3
  52. package/dist/ssg/seo.d.ts.map +1 -1
  53. package/dist/vite-plugin.cjs +2 -2
  54. package/dist/vite-plugin.d.ts.map +1 -1
  55. package/dist/vite-plugin.mjs +2 -2
  56. package/package.json +1 -1
  57. package/dist/components/LocaleRouterLink.vue.d.ts +0 -28
  58. package/dist/components/LocaleRouterLink.vue.d.ts.map +0 -1
  59. package/dist/composables/useData.d.ts +0 -22
  60. package/dist/composables/useData.d.ts.map +0 -1
  61. package/dist/composables/useSeo.d.ts +0 -20
  62. package/dist/composables/useSeo.d.ts.map +0 -1
  63. package/dist/content/ContentDetailHost.vue.d.ts.map +0 -1
  64. package/dist/content/defineContent.d.ts +0 -22
  65. package/dist/content/defineContent.d.ts.map +0 -1
  66. package/dist/routing/withLocaleRoutes.d.ts +0 -8
  67. package/dist/routing/withLocaleRoutes.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,11 +1,44 @@
1
- import { a as BLOX_LOCALE_STRATEGY_KEY, u as useLocale, n as normalizeLink, c as createBlox, i as installSmartLinks } from "./core-Bp2bf8e4.js";
2
- import { _, b, d, e, f, h, j, k, G, l, P, m, o, p, q, r, s, g, t, v, w, x, y, z, A, C, D, E } from "./core-Bp2bf8e4.js";
3
- import { _ as _2, P as P2, s as s2, a } from "./PreviewApp.vue_vue_type_style_index_0_lang-1JKQVLm2.js";
4
- import { ref, onMounted, onUnmounted, inject, computed, toValue, onServerPrefetch, watch, defineComponent, resolveComponent, openBlock, createBlock, Suspense, withCtx, createVNode, resolveDynamicComponent, renderSlot, createApp, markRaw } from "vue";
1
+ import { h as BLOX_LOCALE_STRATEGY_KEY, u as useLocale, n as normalizeLink, j as useSeo, k as getRegistryData, l as getItemBySlug, m as getItemById, o as getContentRoute, p as provideBloxData, d as createBlox, g as generateContentRoutes, q as installContentFallback, i as installLocaleAwareResolve, a as setActiveLocale, r as installSmartLinks } from "./routes-bhjTTXfU.js";
2
+ import { _, t, v, w, B, x, y, z, A, C, D, G, P, E, c, b, F, H, I, J, K, L, f, M, N, O, Q, R, S, s, T, U, V, W } from "./routes-bhjTTXfU.js";
3
+ import { _ as _2, P as P2, s as s2, a } from "./PreviewApp.vue_vue_type_style_index_0_lang-DHb-NJ4C.js";
4
+ import { defineComponent, resolveComponent, openBlock, createBlock, withCtx, Transition, resolveDynamicComponent, ref, onMounted, onUnmounted, inject, computed, onServerPrefetch, toValue, createApp, h, Suspense, markRaw } from "vue";
5
5
  import { useRouter, useRoute } from "vue-router";
6
- import { BLOX_PAGE_SEO_WINDOW_KEY, installBloxStateCache, installCollectionCache } from "./ssg/client.mjs";
7
- import { BLOX_DATA_WINDOW_KEY } from "./ssg/client.mjs";
8
6
  import { createPinia } from "pinia";
7
+ import { installBloxStateCache, installCollectionCache } from "./ssg/client.mjs";
8
+ const _sfc_main = /* @__PURE__ */ defineComponent({
9
+ __name: "BloxRouterView",
10
+ props: {
11
+ transition: { default: "" }
12
+ },
13
+ setup(__props) {
14
+ function viewKey(matched, fallback) {
15
+ const leaf = matched[matched.length - 1];
16
+ return (leaf == null ? void 0 : leaf.path) ?? fallback;
17
+ }
18
+ return (_ctx, _cache) => {
19
+ const _component_RouterView = resolveComponent("RouterView");
20
+ return openBlock(), createBlock(_component_RouterView, null, {
21
+ default: withCtx(({ Component, route: r }) => [
22
+ __props.transition ? (openBlock(), createBlock(Transition, {
23
+ key: 0,
24
+ name: __props.transition,
25
+ mode: "out-in"
26
+ }, {
27
+ default: withCtx(() => [
28
+ (openBlock(), createBlock(resolveDynamicComponent(Component), {
29
+ key: viewKey(r.matched, r.path)
30
+ }))
31
+ ]),
32
+ _: 2
33
+ }, 1032, ["name"])) : (openBlock(), createBlock(resolveDynamicComponent(Component), {
34
+ key: viewKey(r.matched, r.path)
35
+ }))
36
+ ]),
37
+ _: 1
38
+ });
39
+ };
40
+ }
41
+ });
9
42
  function useGlobalBlock(blockType, loader) {
10
43
  const data = ref(null);
11
44
  const loading = ref(false);
@@ -14,8 +47,8 @@ function useGlobalBlock(blockType, loader) {
14
47
  try {
15
48
  const loaded = await loader();
16
49
  if (loaded) data.value = loaded;
17
- } catch (e2) {
18
- console.warn(`[useGlobalBlock] failed to load "${blockType}", using defaults`, e2);
50
+ } catch (e) {
51
+ console.warn(`[useGlobalBlock] failed to load "${blockType}", using defaults`, e);
19
52
  } finally {
20
53
  loading.value = false;
21
54
  }
@@ -63,11 +96,17 @@ function useLocaleNav() {
63
96
  return strategy.toPath(normalized, loc || void 0);
64
97
  }
65
98
  function detect(path) {
66
- const hostname = typeof window !== "undefined" ? window.location.hostname : "";
67
- if (!strategy) return { locale: String(locale.value || "en"), slug: path || "/" };
68
- return strategy.detect(hostname, path);
99
+ const def = (strategy == null ? void 0 : strategy.defaultLocale) ?? String(locale.value || "en");
100
+ const supported = (strategy == null ? void 0 : strategy.locales) ?? [];
101
+ const segments = (path || "/").replace(/^\//, "").split("/");
102
+ const first = segments[0];
103
+ if (first && supported.includes(first)) {
104
+ const rest = segments.slice(1).join("/");
105
+ return { locale: first, slug: rest ? `/${rest}` : "/" };
106
+ }
107
+ return { locale: def, slug: path || "/" };
69
108
  }
70
- return { switchLocale, toPath, locale, availableLocales, detect };
109
+ return { switchLocale, toPath, detect, availableLocales, locale };
71
110
  }
72
111
  function usePageContext(key) {
73
112
  const contexts = inject("contexts");
@@ -77,104 +116,64 @@ function usePageContext(key) {
77
116
  return value;
78
117
  });
79
118
  }
80
- function isServer() {
81
- try {
82
- if (false) ;
83
- } catch {
84
- }
85
- return typeof window === "undefined";
86
- }
87
- function writePayload(seo) {
88
- const root = typeof window !== "undefined" ? window : globalThis;
89
- if (!seo) {
90
- delete root[BLOX_PAGE_SEO_WINDOW_KEY];
91
- return;
92
- }
93
- root[BLOX_PAGE_SEO_WINDOW_KEY] = { ...seo };
94
- }
95
- function applyClientHead(seo) {
96
- if (typeof document === "undefined" || !seo) return;
97
- if (seo.title) document.title = seo.title;
98
- const ensureMeta = (attr, key, content) => {
99
- let el = document.head.querySelector(`meta[${attr}="${key}"]`);
100
- if (!el) {
101
- el = document.createElement("meta");
102
- el.setAttribute(attr, key);
103
- document.head.appendChild(el);
104
- }
105
- el.setAttribute("content", content);
119
+ function useContentSeo(item, head) {
120
+ const route = safeRoute();
121
+ const { locale } = useLocale();
122
+ const strategy = inject(BLOX_LOCALE_STRATEGY_KEY, null);
123
+ const registration = () => {
124
+ var _a;
125
+ const name = (_a = route == null ? void 0 : route.meta) == null ? void 0 : _a.content;
126
+ return name ? getContentRoute(name) : void 0;
106
127
  };
107
- if (seo.description) {
108
- ensureMeta("name", "description", seo.description);
109
- ensureMeta("property", "og:description", seo.description);
110
- }
111
- if (seo.title) {
112
- ensureMeta("property", "og:title", seo.title);
113
- }
114
- if (seo.image) {
115
- ensureMeta("property", "og:image", seo.image);
116
- }
117
- }
118
- function useSeo(getter) {
119
- const resolved = computed(() => toValue(getter) ?? null);
120
- function sync() {
121
- const seo = resolved.value;
122
- writePayload(seo ?? void 0);
123
- if (!isServer()) applyClientHead(seo ?? void 0);
124
- }
125
- if (isServer()) {
126
- onServerPrefetch(() => {
127
- sync();
128
+ let alternates;
129
+ useSeo(() => {
130
+ const base = head();
131
+ if (!base) return null;
132
+ return alternates ? { ...base, alternates } : base;
133
+ });
134
+ const registry = getRegistryData();
135
+ if (registry == null ? void 0 : registry.isServer) {
136
+ onServerPrefetch(async () => {
137
+ var _a, _b;
138
+ const reg = registration();
139
+ if (!reg) return;
140
+ const slug = String(((_a = route == null ? void 0 : route.params) == null ? void 0 : _a.slug) ?? ((_b = toValue(item)) == null ? void 0 : _b.slug) ?? "");
141
+ const activeLoc = String(locale.value || "en").slice(0, 2).toLowerCase();
142
+ const entity = slug ? await getItemBySlug(reg.collection, slug, activeLoc).catch(() => null) : toValue(item);
143
+ if (!entity) return;
144
+ const id = String(entity.id ?? entity._id ?? "");
145
+ const supported = (strategy == null ? void 0 : strategy.locales) ?? [locale.value];
146
+ const locales = (Array.isArray(entity.available_locales) ? entity.available_locales : []).filter((l) => supported.includes(l));
147
+ if (!id || !locales.length) return;
148
+ const built = {};
149
+ await Promise.all(locales.map(async (loc) => {
150
+ var _a2;
151
+ const locSlug = loc === activeLoc ? String(entity.slug ?? "") : String(((_a2 = await getItemById(reg.collection, id, loc).catch(() => null)) == null ? void 0 : _a2.slug) ?? "");
152
+ if (locSlug) built[loc] = reg.route.replace(/:slug(?=\/|$)/, locSlug);
153
+ }));
154
+ if (Object.keys(built).length > 1) alternates = built;
128
155
  });
129
- sync();
130
- } else {
131
- watch(resolved, sync, { immediate: true, deep: true });
132
156
  }
133
- onUnmounted(() => {
134
- writePayload(void 0);
135
- });
136
157
  }
137
- const _sfc_main = /* @__PURE__ */ defineComponent({
138
- __name: "BloxRouterView",
139
- setup(__props) {
140
- return (_ctx, _cache) => {
141
- const _component_RouterView = resolveComponent("RouterView");
142
- return openBlock(), createBlock(Suspense, null, {
143
- fallback: withCtx(() => [
144
- renderSlot(_ctx.$slots, "fallback")
145
- ]),
146
- default: withCtx(() => [
147
- createVNode(_component_RouterView, null, {
148
- default: withCtx(({ Component }) => [
149
- (openBlock(), createBlock(resolveDynamicComponent(Component)))
150
- ]),
151
- _: 1
152
- })
153
- ]),
154
- _: 3
155
- });
156
- };
158
+ function safeRoute() {
159
+ try {
160
+ return useRoute();
161
+ } catch {
162
+ return null;
157
163
  }
158
- });
159
- function useContentSeo(item, getter) {
160
- const route = useRoute();
161
- const { detect } = useLocaleNav();
162
- useSeo(() => {
163
- const base = toValue(getter);
164
- if (!base) return null;
165
- const row = item.value;
166
- if (!row) return base;
167
- const locales = Array.isArray(row.available_locales) ? row.available_locales.map((l2) => String(l2).slice(0, 2).toLowerCase()) : [];
168
- if (!locales.length) return base;
169
- const bare = detect(route.path).slug || route.path || "/";
170
- const alternates = {};
171
- for (const loc of locales) alternates[loc] = bare;
172
- return { ...base, alternates };
173
- });
174
164
  }
175
165
  const DEFAULT_PLAUSIBLE_HOST = "https://analytics.bagel.to";
176
166
  function injectPlausible(config) {
167
+ var _a, _b;
177
168
  if (typeof document === "undefined") return;
169
+ if (typeof location !== "undefined" && /^(localhost|127\.0\.0\.1)$/i.test(location.hostname)) return;
170
+ try {
171
+ const inIframe = typeof window !== "undefined" && window.top !== window.self;
172
+ const isPreview = typeof window !== "undefined" && ((_b = (_a = window.location) == null ? void 0 : _a.pathname) == null ? void 0 : _b.startsWith("/_blox_preview"));
173
+ if (inIframe || isPreview) return;
174
+ } catch {
175
+ return;
176
+ }
178
177
  const opts = typeof config === "string" ? { domain: config } : config;
179
178
  const { domain } = opts;
180
179
  if (!domain) return;
@@ -215,7 +214,11 @@ function createBloxApp(options) {
215
214
  if (collectionInterceptor && axiosInstance) {
216
215
  axiosInstance.interceptors.request.use(collectionInterceptor);
217
216
  }
218
- const app = createApp(rootComponent);
217
+ const app = createApp({
218
+ name: "BloxRoot",
219
+ render: () => h(Suspense, null, { default: () => h(rootComponent) })
220
+ });
221
+ provideBloxData(app);
219
222
  const bloxOpts = {
220
223
  modules,
221
224
  websiteName,
@@ -229,13 +232,34 @@ function createBloxApp(options) {
229
232
  pinia.use(({ store: s3 }) => {
230
233
  s3.router = markRaw(router);
231
234
  });
232
- blox.install(app, {});
233
235
  app.use(pinia);
234
236
  app.use(router);
237
+ blox.install(app, {});
238
+ for (const record of generateContentRoutes(supportedLocales, locale)) {
239
+ if (!router.hasRoute(record.name)) router.addRoute(record);
240
+ }
241
+ installContentFallback(router, supportedLocales, locale);
242
+ const strategy = blox.strategy;
243
+ installLocaleAwareResolve(router, strategy);
244
+ const localeOf = (path) => {
245
+ const first = path.replace(/^\//, "").split("/")[0];
246
+ return first && supportedLocales.includes(first) ? first : locale;
247
+ };
248
+ router.beforeEach((to, from) => {
249
+ const intended = from && from.matched.length ? localeOf(from.path) : localeOf(to.path);
250
+ const normalized = strategy.normalize(to.fullPath, intended);
251
+ if (normalized && normalized !== to.fullPath) {
252
+ setActiveLocale(localeOf(normalized));
253
+ return normalized;
254
+ }
255
+ setActiveLocale(localeOf(to.path));
256
+ return true;
257
+ });
258
+ setActiveLocale(localeOf(router.currentRoute.value.path));
235
259
  for (const plugin of plugins) {
236
260
  if (Array.isArray(plugin)) {
237
- const [p2, ...args] = plugin;
238
- app.use(p2, ...args);
261
+ const [p, ...args] = plugin;
262
+ app.use(p, ...args);
239
263
  } else {
240
264
  app.use(plugin);
241
265
  }
@@ -268,6 +292,16 @@ function defineBlock(meta) {
268
292
  }
269
293
  return meta;
270
294
  }
295
+ function withLocaleRoutes(path, component, supportedLocales, defaultLocale = "en") {
296
+ const normalized = path.startsWith("/") ? path : `/${path}`;
297
+ const prefixed = supportedLocales.filter((l) => l !== defaultLocale);
298
+ const make = (p) => ({ path: p, component });
299
+ const routes = [make(normalized)];
300
+ if (prefixed.length) {
301
+ routes.push(make(`/:locale(${prefixed.join("|")})${normalized}`));
302
+ }
303
+ return routes;
304
+ }
271
305
  const MANIFEST_VERSION = 1;
272
306
  function definitionToEntry(type, def) {
273
307
  const { schema } = def;
@@ -304,52 +338,64 @@ function buildBloxManifest(blox) {
304
338
  }
305
339
  export {
306
340
  _ as AdminEditBar,
307
- b as BLOX_ADMIN_CONFIG_KEY,
308
- d as BLOX_CONFIG_KEY,
309
- e as BLOX_CONTENT_ITEM_KEY,
310
- BLOX_DATA_WINDOW_KEY,
311
- f as BLOX_GLOBAL_RESOLVER_KEY,
341
+ t as BLOX_ADMIN_CONFIG_KEY,
342
+ v as BLOX_CONFIG_KEY,
343
+ w as BLOX_DATA_KEY,
344
+ B as BLOX_DATA_WINDOW_KEY,
345
+ x as BLOX_GLOBAL_RESOLVER_KEY,
312
346
  BLOX_LOCALE_STRATEGY_KEY,
313
- h as BloxInstance,
347
+ y as BloxInstance,
314
348
  _sfc_main as BloxRouterView,
315
- j as CmsPageView,
316
- k as GLOBAL_REF_KEY,
349
+ z as CmsPageView,
350
+ A as ContentDetailView,
351
+ C as ContentHost,
352
+ D as GLOBAL_REF_KEY,
317
353
  G as GlobalBlockHost,
318
- l as LocaleRouterLink,
319
354
  P as PageRenderer,
320
355
  _2 as PreviewApp,
321
356
  P2 as PreviewRenderer,
322
357
  buildBloxManifest,
323
- m as classifyLink,
324
- o as configureApi,
325
- p as contentRoutes,
358
+ E as classifyLink,
359
+ c as collectBloxData,
360
+ b as collectBloxSeo,
361
+ F as configureApi,
362
+ H as contentRoutes,
326
363
  createBlox,
327
364
  createBloxApp,
328
365
  defineBlock,
329
- q as defineContent,
330
- r as getWebsiteId,
331
- s as getWebsiteIdSync,
332
- g as globalRefOf,
366
+ I as defineContent,
367
+ generateContentRoutes,
368
+ getContentRoute,
369
+ J as getContentRouteByCollection,
370
+ getItemById,
371
+ getItemBySlug,
372
+ K as getWebsiteId,
373
+ L as getWebsiteIdSync,
374
+ f as globalRefOf,
333
375
  injectPlausible,
376
+ installContentFallback,
334
377
  installSmartLinks,
335
- t as isAbsoluteExternal,
336
- v as isSpecialScheme,
337
- w as listItems,
378
+ M as isAbsoluteExternal,
379
+ N as isSpecialScheme,
380
+ O as listItems,
338
381
  normalizeLink,
339
- x as normalizeTo,
340
- y as resolvePath,
382
+ Q as normalizeTo,
383
+ provideBloxData,
384
+ R as provideContentItem,
385
+ S as resolvePath,
341
386
  s2 as sendToEditor,
342
387
  a as sendToPreview,
343
388
  serializeRegistry,
344
- z as toInternalPath,
345
- A as useBloxEdit,
346
- C as useContentItem,
389
+ s as setApiOrigin,
390
+ T as toInternalPath,
391
+ U as useBloxEdit,
392
+ V as useContentItem,
347
393
  useContentSeo,
348
- D as useData,
394
+ W as useData,
349
395
  useGlobalBlock,
350
396
  useLocale,
351
397
  useLocaleNav,
352
398
  usePageContext,
353
399
  useSeo,
354
- E as withLocaleRoutes
400
+ withLocaleRoutes
355
401
  };
@@ -0,0 +1,15 @@
1
+ import { RouteRecordRaw } from 'vue-router';
2
+ /**
3
+ * Duplicate a route so it matches both the default-locale (bare) path and every
4
+ * `/:locale/...` prefixed variant, e.g.:
5
+ *
6
+ * withLocaleRoutes('/event/:slug', Page, ['en', 'it', 'fr'])
7
+ * // → { path: '/event/:slug' }
8
+ * // { path: '/:locale(it|fr)/event/:slug' }
9
+ *
10
+ * The default locale is intentionally excluded from the prefixed matcher (it's
11
+ * served unprefixed). Locale detection/normalization is handled globally by the
12
+ * nav guard in `createBloxApp`; this helper only makes the routes resolvable.
13
+ */
14
+ export declare function withLocaleRoutes(path: string, component: RouteRecordRaw['component'], supportedLocales: string[], defaultLocale?: string): RouteRecordRaw[];
15
+ //# sourceMappingURL=localeRoutes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localeRoutes.d.ts","sourceRoot":"","sources":["../src/localeRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,cAAc,CAAC,WAAW,CAAC,EACtC,gBAAgB,EAAE,MAAM,EAAE,EAC1B,aAAa,SAAO,GAClB,cAAc,EAAE,CASlB"}
@@ -17,6 +17,19 @@ export interface LocaleStrategy {
17
17
  * Falls back to '/' if no slug given.
18
18
  */
19
19
  switchTo: (locale: string, slug: string, router: Router) => void;
20
+ /**
21
+ * Normalize a full path so its locale prefix matches `intendedLocale`.
22
+ *
23
+ * Used by the global navigation guard to prevent locale "leaks": a path with
24
+ * no prefix (or the wrong-locale prefix) navigated to from an
25
+ * `intendedLocale` page is rewritten to carry the correct prefix. This only
26
+ * touches the prefix — the bare slug is preserved (per-locale detail slugs
27
+ * are the caller's responsibility, via `localePath`). Returns `null` when the
28
+ * path is already correct (idempotent — caller can skip the redirect).
29
+ *
30
+ * For the `domain` strategy this is a no-op (returns `null`).
31
+ */
32
+ normalize: (path: string, intendedLocale: string) => string | null;
20
33
  /** All known locales. */
21
34
  readonly locales: string[];
22
35
  /** Default / canonical locale (pages resolve by this locale's slug). */
@@ -1 +1 @@
1
- {"version":3,"file":"localeStrategy.d.ts","sourceRoot":"","sources":["../src/localeStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,MAAM,WAAW,cAAc;IAC9B,gEAAgE;IAChE,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5E;;;;;OAKG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACjD;;;OAGG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAChE,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,gDAAgD;IAChD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC9B;AAMD,MAAM,WAAW,mBAAmB;IACnC,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAC1B;AAoED,MAAM,WAAW,qBAAqB;IACrC,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC;AAgDD,MAAM,WAAW,eAAe;IAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;IAClC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACtC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CAQ7E"}
1
+ {"version":3,"file":"localeStrategy.d.ts","sourceRoot":"","sources":["../src/localeStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,MAAM,WAAW,cAAc;IAC9B,gEAAgE;IAChE,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5E;;;;;OAKG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACjD;;;OAGG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAChE;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;IAClE,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,gDAAgD;IAChD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC9B;AAMD,MAAM,WAAW,mBAAmB;IACnC,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAC1B;AAsFD,MAAM,WAAW,qBAAqB;IACrC,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC;AAoDD,MAAM,WAAW,eAAe;IAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;IAClC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACtC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CAQ7E"}
@@ -163,17 +163,18 @@ function buildStructuredData(meta, locale) {
163
163
  }
164
164
  }
165
165
  function buildPageHead(options) {
166
- const { url, resolvedData, website, stateScript, locale, pageSeo } = options;
166
+ const { url, resolvedData, website, stateScript, locale, override } = options;
167
167
  const parts = [];
168
168
  const meta = (website == null ? void 0 : website.meta) ?? {};
169
169
  const page = resolvedData == null ? void 0 : resolvedData.page;
170
170
  const ctx = extractPrimaryContext(resolvedData == null ? void 0 : resolvedData.contexts);
171
- const title = (pageSeo == null ? void 0 : pageSeo.title) || (page == null ? void 0 : page.meta_title) || ctx.title || ((page == null ? void 0 : page.title) && meta.og_site_name ? `${page.title} · ${meta.og_site_name}` : null) || (page == null ? void 0 : page.title) || meta.default_meta_title;
171
+ const title = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || ((page == null ? void 0 : page.title) && meta.og_site_name ? `${page.title} · ${meta.og_site_name}` : null) || (page == null ? void 0 : page.title) || meta.default_meta_title;
172
172
  if (title) {
173
- const fullTitle = !(pageSeo == null ? void 0 : pageSeo.title) && ctx.title && meta.og_site_name && !ctx.title.includes(meta.og_site_name) ? `${esc(ctx.title)} · ${esc(meta.og_site_name)}` : esc(title);
173
+ const bare = (override == null ? void 0 : override.title) || ctx.title;
174
+ const fullTitle = bare && meta.og_site_name && !bare.includes(meta.og_site_name) ? `${esc(bare)} · ${esc(meta.og_site_name)}` : esc(title);
174
175
  parts.push(`<title>${fullTitle}</title>`);
175
176
  }
176
- const description = (pageSeo == null ? void 0 : pageSeo.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.default_meta_description;
177
+ const description = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.default_meta_description;
177
178
  if (description) {
178
179
  parts.push(`<meta name="description" content="${esc(description)}">`);
179
180
  }
@@ -181,9 +182,9 @@ function buildPageHead(options) {
181
182
  if (canonicalBase) {
182
183
  parts.push(`<link rel="canonical" href="${esc(canonicalBase + url)}">`);
183
184
  }
184
- const ogTitle = (pageSeo == null ? void 0 : pageSeo.title) || (page == null ? void 0 : page.meta_title) || ctx.title || (page == null ? void 0 : page.title) || meta.og_site_name || meta.default_meta_title;
185
- const ogDesc = (pageSeo == null ? void 0 : pageSeo.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.og_description || meta.default_meta_description;
186
- const ogImage = absUrl((pageSeo == null ? void 0 : pageSeo.image) || (page == null ? void 0 : page.og_image) || ctx.image || meta.default_og_image);
185
+ const ogTitle = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || (page == null ? void 0 : page.title) || meta.og_site_name || meta.default_meta_title;
186
+ const ogDesc = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.og_description || meta.default_meta_description;
187
+ const ogImage = absUrl((override == null ? void 0 : override.image) || (page == null ? void 0 : page.og_image) || ctx.image || meta.default_og_image);
187
188
  const ogType = meta.og_type || "website";
188
189
  if (ogTitle) parts.push(`<meta property="og:title" content="${esc(ogTitle)}">`);
189
190
  if (ogDesc) parts.push(`<meta property="og:description" content="${esc(ogDesc)}">`);
@@ -197,7 +198,7 @@ function buildPageHead(options) {
197
198
  if (ogDesc) parts.push(`<meta name="twitter:description" content="${esc(ogDesc)}">`);
198
199
  if (ogImage) parts.push(`<meta name="twitter:image" content="${esc(ogImage)}">`);
199
200
  if (meta.twitter_site) parts.push(`<meta name="twitter:site" content="${esc(meta.twitter_site)}">`);
200
- const alternates = (pageSeo == null ? void 0 : pageSeo.alternates) || (resolvedData == null ? void 0 : resolvedData.alternates);
201
+ const alternates = (override == null ? void 0 : override.alternates) && Object.keys(override.alternates).length ? override.alternates : resolvedData == null ? void 0 : resolvedData.alternates;
201
202
  const defaultLocale = (website == null ? void 0 : website.default_locale) ?? "en";
202
203
  if (alternates && canonicalBase) {
203
204
  for (const [loc, slug] of Object.entries(alternates)) {
@@ -291,7 +292,7 @@ function generateNetlifyRedirects(redirects) {
291
292
  }
292
293
  function stripTemplateSeoTags(template, options = {}) {
293
294
  const { hasFavicon = false, hasWebclip = false } = options;
294
- let out = template.replace(/<title>[^<]*<\/title>/gi, "<!--ssg:title-->").replace(/<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi, "").replace(/<meta\s[^>]*property\s*=\s*["']og:[^"']*["'][^>]*>/gi, "").replace(/<meta\s[^>]*(?:property|name)\s*=\s*["']twitter:[^"']*["'][^>]*>/gi, "");
295
+ let out = template.replace(/<title>[^<]*<\/title>/gi, "<!--ssg:title-->").replace(/<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi, "").replace(/<meta\s[^>]*property\s*=\s*["']og:[^"']*["'][^>]*>/gi, "").replace(/<meta\s[^>]*(?:property|name)\s*=\s*["']twitter:[^"']*["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']canonical["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']alternate["'][^>]*hreflang\s*=[^>]*>/gi, "");
295
296
  if (hasWebclip) {
296
297
  out = out.replace(/<link\s[^>]*rel\s*=\s*["']apple-touch-icon["'][^>]*>/gi, "");
297
298
  }
@@ -186,17 +186,18 @@ function buildStructuredData(meta, locale) {
186
186
  }
187
187
  }
188
188
  function buildPageHead(options) {
189
- const { url, resolvedData, website, stateScript, locale, pageSeo } = options;
189
+ const { url, resolvedData, website, stateScript, locale, override } = options;
190
190
  const parts = [];
191
191
  const meta = (website == null ? void 0 : website.meta) ?? {};
192
192
  const page = resolvedData == null ? void 0 : resolvedData.page;
193
193
  const ctx = extractPrimaryContext(resolvedData == null ? void 0 : resolvedData.contexts);
194
- const title = (pageSeo == null ? void 0 : pageSeo.title) || (page == null ? void 0 : page.meta_title) || ctx.title || ((page == null ? void 0 : page.title) && meta.og_site_name ? `${page.title} · ${meta.og_site_name}` : null) || (page == null ? void 0 : page.title) || meta.default_meta_title;
194
+ const title = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || ((page == null ? void 0 : page.title) && meta.og_site_name ? `${page.title} · ${meta.og_site_name}` : null) || (page == null ? void 0 : page.title) || meta.default_meta_title;
195
195
  if (title) {
196
- const fullTitle = !(pageSeo == null ? void 0 : pageSeo.title) && ctx.title && meta.og_site_name && !ctx.title.includes(meta.og_site_name) ? `${esc(ctx.title)} · ${esc(meta.og_site_name)}` : esc(title);
196
+ const bare = (override == null ? void 0 : override.title) || ctx.title;
197
+ const fullTitle = bare && meta.og_site_name && !bare.includes(meta.og_site_name) ? `${esc(bare)} · ${esc(meta.og_site_name)}` : esc(title);
197
198
  parts.push(`<title>${fullTitle}</title>`);
198
199
  }
199
- const description = (pageSeo == null ? void 0 : pageSeo.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.default_meta_description;
200
+ const description = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.default_meta_description;
200
201
  if (description) {
201
202
  parts.push(`<meta name="description" content="${esc(description)}">`);
202
203
  }
@@ -204,9 +205,9 @@ function buildPageHead(options) {
204
205
  if (canonicalBase) {
205
206
  parts.push(`<link rel="canonical" href="${esc(canonicalBase + url)}">`);
206
207
  }
207
- const ogTitle = (pageSeo == null ? void 0 : pageSeo.title) || (page == null ? void 0 : page.meta_title) || ctx.title || (page == null ? void 0 : page.title) || meta.og_site_name || meta.default_meta_title;
208
- const ogDesc = (pageSeo == null ? void 0 : pageSeo.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.og_description || meta.default_meta_description;
209
- const ogImage = absUrl((pageSeo == null ? void 0 : pageSeo.image) || (page == null ? void 0 : page.og_image) || ctx.image || meta.default_og_image);
208
+ const ogTitle = (override == null ? void 0 : override.title) || (page == null ? void 0 : page.meta_title) || ctx.title || (page == null ? void 0 : page.title) || meta.og_site_name || meta.default_meta_title;
209
+ const ogDesc = (override == null ? void 0 : override.description) || (page == null ? void 0 : page.meta_description) || ctx.description || meta.og_description || meta.default_meta_description;
210
+ const ogImage = absUrl((override == null ? void 0 : override.image) || (page == null ? void 0 : page.og_image) || ctx.image || meta.default_og_image);
210
211
  const ogType = meta.og_type || "website";
211
212
  if (ogTitle) parts.push(`<meta property="og:title" content="${esc(ogTitle)}">`);
212
213
  if (ogDesc) parts.push(`<meta property="og:description" content="${esc(ogDesc)}">`);
@@ -220,7 +221,7 @@ function buildPageHead(options) {
220
221
  if (ogDesc) parts.push(`<meta name="twitter:description" content="${esc(ogDesc)}">`);
221
222
  if (ogImage) parts.push(`<meta name="twitter:image" content="${esc(ogImage)}">`);
222
223
  if (meta.twitter_site) parts.push(`<meta name="twitter:site" content="${esc(meta.twitter_site)}">`);
223
- const alternates = (pageSeo == null ? void 0 : pageSeo.alternates) || (resolvedData == null ? void 0 : resolvedData.alternates);
224
+ const alternates = (override == null ? void 0 : override.alternates) && Object.keys(override.alternates).length ? override.alternates : resolvedData == null ? void 0 : resolvedData.alternates;
224
225
  const defaultLocale = (website == null ? void 0 : website.default_locale) ?? "en";
225
226
  if (alternates && canonicalBase) {
226
227
  for (const [loc, slug] of Object.entries(alternates)) {
@@ -314,7 +315,7 @@ function generateNetlifyRedirects(redirects) {
314
315
  }
315
316
  function stripTemplateSeoTags(template, options = {}) {
316
317
  const { hasFavicon = false, hasWebclip = false } = options;
317
- let out = template.replace(/<title>[^<]*<\/title>/gi, "<!--ssg:title-->").replace(/<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi, "").replace(/<meta\s[^>]*property\s*=\s*["']og:[^"']*["'][^>]*>/gi, "").replace(/<meta\s[^>]*(?:property|name)\s*=\s*["']twitter:[^"']*["'][^>]*>/gi, "");
318
+ let out = template.replace(/<title>[^<]*<\/title>/gi, "<!--ssg:title-->").replace(/<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi, "").replace(/<meta\s[^>]*property\s*=\s*["']og:[^"']*["'][^>]*>/gi, "").replace(/<meta\s[^>]*(?:property|name)\s*=\s*["']twitter:[^"']*["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']canonical["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']alternate["'][^>]*hreflang\s*=[^>]*>/gi, "");
318
319
  if (hasWebclip) {
319
320
  out = out.replace(/<link\s[^>]*rel\s*=\s*["']apple-touch-icon["'][^>]*>/gi, "");
320
321
  }