@xosen/site-sdk 0.0.15 → 0.0.16

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/dist/index.js CHANGED
@@ -1,28 +1,28 @@
1
- import { ref as v, onMounted as S, watch as _, computed as u, onUnmounted as N, reactive as L, provide as P } from "vue";
1
+ import { ref as v, onMounted as S, watch as _, computed as f, onUnmounted as N, reactive as L, provide as P } from "vue";
2
2
  import { useI18n as T } from "vue-i18n";
3
3
  import { useRoute as I } from "vue-router";
4
4
  function Z(o, a) {
5
5
  const t = v(null), { locale: e } = T(), s = window.__SITE_DATA__, n = s?.locale;
6
- function r() {
6
+ function i() {
7
7
  return s && s[o] && e.value === n ? (t.value = s[o], !0) : !1;
8
8
  }
9
- async function i() {
9
+ async function l() {
10
10
  try {
11
- const c = await fetch(`/api/content/${a}:${e.value}`);
12
- c.ok && (t.value = await c.json());
11
+ const r = await fetch(`/api/content/${a}:${e.value}`);
12
+ r.ok && (t.value = await r.json());
13
13
  } catch {
14
14
  }
15
15
  }
16
16
  return S(() => {
17
- r() || i();
17
+ i() || l();
18
18
  }), _(e, () => {
19
- i();
19
+ l();
20
20
  }), t;
21
21
  }
22
22
  function q() {
23
- const o = window.__SITE_DATA__, a = u(() => o?.config || null), t = u(() => o?.components || {}), e = u(() => a.value?.navigation || []), s = u(() => a.value?.locales || []), n = u(() => a.value?.defaultLocale || "en"), r = u(() => a.value?.branding || {}), i = u(() => a.value?.footer || {}), c = u(() => a.value?.features || {});
24
- function p(m) {
25
- const g = c.value[m];
23
+ const o = window.__SITE_DATA__, a = f(() => o?.config || null), t = f(() => o?.components || {}), e = f(() => a.value?.navigation || []), s = f(() => a.value?.locales || []), n = f(() => a.value?.defaultLocale || "en"), i = f(() => a.value?.branding || {}), l = f(() => a.value?.footer || {}), r = f(() => a.value?.features || {});
24
+ function u(m) {
25
+ const g = r.value[m];
26
26
  return typeof g == "boolean" ? g : typeof g == "object";
27
27
  }
28
28
  return {
@@ -31,10 +31,10 @@ function q() {
31
31
  navigation: e,
32
32
  locales: s,
33
33
  defaultLocale: n,
34
- branding: r,
35
- footer: i,
36
- features: c,
37
- hasFeature: p
34
+ branding: i,
35
+ footer: l,
36
+ features: r,
37
+ hasFeature: u
38
38
  };
39
39
  }
40
40
  function H(o) {
@@ -64,10 +64,10 @@ function D(o) {
64
64
  function Q() {
65
65
  const a = window.__SITE_DATA__?.apiBase || "";
66
66
  async function t(n) {
67
- const r = await fetch(`${a}${n}`);
68
- if (!r.ok)
69
- throw new Error(`API error: ${r.status}`);
70
- return r.json();
67
+ const i = await fetch(`${a}${n}`);
68
+ if (!i.ok)
69
+ throw new Error(`API error: ${i.status}`);
70
+ return i.json();
71
71
  }
72
72
  async function e() {
73
73
  return t("/v1/billing/tariffs");
@@ -116,101 +116,106 @@ function j() {
116
116
  }
117
117
  j();
118
118
  function ee(o) {
119
- const { locale: a } = T(), { previewPage: t, isPreviewMode: e } = C(), s = window.__SITE_DATA__, n = v(null), r = v(!0), i = v(!1), c = u(() => typeof o == "string" ? o : o.value), p = u(() => `page:${c.value}`), m = u(() => {
120
- const l = e.value ? t.value : n.value;
121
- if (!l) return {};
122
- const { blocks: f, meta: d, slug: W, locale: J, ...E } = l;
119
+ const { locale: a } = T(), { previewPage: t, isPreviewMode: e } = C(), s = window.__SITE_DATA__, n = v(null), i = v(!0), l = v(!1), r = f(() => typeof o == "string" ? o : o.value), u = f(() => `page:${r.value}`), m = f(() => {
120
+ const c = e.value ? t.value : n.value;
121
+ if (!c) return {};
122
+ const { blocks: p, meta: d, slug: W, locale: J, ...E } = c;
123
123
  return E;
124
124
  });
125
125
  P(R, m);
126
- const g = u(() => e.value && t.value?.slug === c.value ? t.value.blocks || [] : n.value?.blocks || []);
127
- function b(l) {
128
- n.value = l, l && (k[p.value] = l);
126
+ const g = f(() => e.value && t.value?.slug === r.value ? t.value.blocks || [] : n.value?.blocks || []);
127
+ function b(c) {
128
+ n.value = c, c && (k[u.value] = c);
129
129
  }
130
130
  function y() {
131
- const l = s?.[p.value];
132
- return l && a.value === s?.locale ? (b(l), i.value = !1, w(), !0) : !1;
131
+ const c = s?.[u.value];
132
+ return c && a.value === s?.locale ? (b(c), l.value = !1, w(), !0) : !1;
133
133
  }
134
134
  async function h() {
135
- r.value = !0, i.value = !1;
135
+ i.value = !0, l.value = !1;
136
136
  try {
137
- const l = await fetch(`/api/content/${p.value}:${a.value}`);
138
- l.ok ? (b(await l.json()), w()) : (b(null), i.value = !0);
137
+ const c = await fetch(`/api/content/${u.value}:${a.value}`);
138
+ c.ok ? (b(await c.json()), w()) : (b(null), l.value = !0);
139
139
  } catch {
140
- b(null), i.value = !0;
140
+ b(null), l.value = !0;
141
141
  } finally {
142
- r.value = !1;
142
+ i.value = !1;
143
143
  }
144
144
  }
145
145
  function w() {
146
146
  if (!n.value) return;
147
147
  if (n.value.title) {
148
- const f = s?.config?.branding, d = f?.siteName ? ` — ${f.siteName}` : "";
148
+ const p = s?.config?.branding, d = p?.siteName ? ` — ${p.siteName}` : "";
149
149
  document.title = `${n.value.title}${d}`;
150
150
  }
151
- const l = n.value.meta?.description;
152
- if (l) {
153
- const f = document.querySelector('meta[name="description"]');
154
- f && f.setAttribute("content", l);
151
+ const c = n.value.meta?.description;
152
+ if (c) {
153
+ const p = document.querySelector('meta[name="description"]');
154
+ p && p.setAttribute("content", c);
155
155
  }
156
156
  }
157
- return _(t, (l) => {
158
- l?.slug === c.value && (r.value = !1, i.value = !1, (l.title || l.blocks) && (n.value = {
157
+ return _(t, (c) => {
158
+ c?.slug === r.value && (i.value = !1, l.value = !1, (c.title || c.blocks) && (n.value = {
159
159
  ...n.value || {},
160
- ...l.title ? { title: l.title } : {},
161
- ...l.blocks ? { blocks: l.blocks } : {}
160
+ ...c.title ? { title: c.title } : {},
161
+ ...c.blocks ? { blocks: c.blocks } : {}
162
162
  }, w()));
163
163
  }), S(() => {
164
- y() ? r.value = !1 : h();
165
- }), _(a, () => h()), _(c, () => {
166
- y() ? r.value = !1 : h();
164
+ y() ? i.value = !1 : h();
165
+ }), _(a, () => h()), _(r, () => {
166
+ y() ? i.value = !1 : h();
167
167
  }), {
168
168
  page: n,
169
169
  blocks: g,
170
170
  pageContext: m,
171
- loading: r,
172
- notFound: i,
171
+ loading: i,
172
+ notFound: l,
173
173
  isPreviewMode: e,
174
174
  reload: h
175
175
  };
176
176
  }
177
177
  function te() {
178
178
  const { locale: o } = T(), a = window.__SITE_DATA__, t = a?.locale, e = v({ ...a?.components || {} });
179
- async function s(r) {
180
- if (!$()) {
179
+ let s = t, n = null;
180
+ async function i(r) {
181
+ if (!$() && r !== s) {
181
182
  if (r === t) {
182
- e.value = { ...a?.components || {} };
183
+ e.value = { ...a?.components || {} }, s = r;
183
184
  return;
184
185
  }
185
- try {
186
- const i = await fetch(`/api/content/components:${r}`);
187
- i.ok && (e.value = await i.json());
188
- } catch {
189
- }
186
+ n || (n = (async () => {
187
+ try {
188
+ const u = await fetch(`/api/content/components:${r}`);
189
+ u.ok && (e.value = await u.json(), s = r);
190
+ } catch {
191
+ } finally {
192
+ n = null;
193
+ }
194
+ })(), await n);
190
195
  }
191
196
  }
192
- _(o, (r) => s(r)), S(() => {
193
- o.value !== t && s(o.value);
197
+ _(o, (r) => i(r)), S(() => {
198
+ o.value !== t && i(o.value);
194
199
  });
195
- function n(r) {
196
- return u(() => e.value[r] || null);
200
+ function l(r) {
201
+ return f(() => e.value[r] || null);
197
202
  }
198
203
  return {
199
204
  /** All components (reactive, locale-aware) */
200
205
  components: e,
201
206
  /** Get a single component by key */
202
- getComponent: n,
207
+ getComponent: l,
203
208
  /** Force refetch for current locale */
204
- reload: () => s(o.value)
209
+ reload: () => i(o.value)
205
210
  };
206
211
  }
207
212
  function ae() {
208
- const { locale: o } = T(), a = window.__SITE_DATA__, t = u(() => a?.config?.locales || []), e = u(() => a?.config?.defaultLocale || "en"), s = u(() => t.value.length > 1);
209
- function n(i) {
210
- o.value = i, localStorage.setItem("x-site-locale", i);
213
+ const { locale: o } = T(), a = window.__SITE_DATA__, t = f(() => a?.config?.locales || []), e = f(() => a?.config?.defaultLocale || "en"), s = f(() => t.value.length > 1);
214
+ function n(l) {
215
+ o.value = l, localStorage.setItem("x-site-locale", l);
211
216
  }
212
- function r(i) {
213
- return typeof i == "string" ? i : i[o.value] || i[e.value] || Object.values(i)[0] || "";
217
+ function i(l) {
218
+ return typeof l == "string" ? l : l[o.value] || l[e.value] || Object.values(l)[0] || "";
214
219
  }
215
220
  return {
216
221
  locale: o,
@@ -218,7 +223,7 @@ function ae() {
218
223
  defaultLocale: e,
219
224
  hasMultipleLocales: s,
220
225
  switchLocale: n,
221
- resolveText: r
226
+ resolveText: i
222
227
  };
223
228
  }
224
229
  const O = {
@@ -267,32 +272,32 @@ function G(o, a) {
267
272
  for (const s of ["sm", "md", "lg", "xl"]) {
268
273
  const n = e[s];
269
274
  if (!n) continue;
270
- const r = x(n, a);
271
- r && t.push(`@media (min-width: ${O[s]}px) { ${r} }`);
275
+ const i = x(n, a);
276
+ i && t.push(`@media (min-width: ${O[s]}px) { ${i} }`);
272
277
  }
273
278
  return t.join(`
274
279
  `);
275
280
  }
276
281
  function oe() {
277
- const o = I(), { previewPage: a, isPreviewMode: t } = C(), e = window.__SITE_DATA__, s = `dl-${Math.random().toString(36).slice(2, 8)}`, n = u(() => ["dynamic-layout", s]), r = u(() => {
282
+ const o = I(), { previewPage: a, isPreviewMode: t } = C(), e = window.__SITE_DATA__, s = `dl-${Math.random().toString(36).slice(2, 8)}`, n = f(() => ["dynamic-layout", s]), i = f(() => {
278
283
  if (o.path === "/_preview_")
279
284
  return a.value?.layout || "landing";
280
285
  if (t.value && a.value?.layout)
281
286
  return a.value.layout;
282
- const p = o.path === "/" ? "page:home" : `page:${o.params.slug || ""}`;
283
- return (k[p] || e?.[p])?.layout || "default";
284
- }), i = u(() => {
285
- const p = e?.config?.layouts;
286
- if (p) {
287
- const m = p[r.value] || p.default;
287
+ const u = o.path === "/" ? "page:home" : `page:${o.params.slug || ""}`;
288
+ return (k[u] || e?.[u])?.layout || "default";
289
+ }), l = f(() => {
290
+ const u = e?.config?.layouts;
291
+ if (u) {
292
+ const m = u[i.value] || u.default;
288
293
  if (m?.zones) return m;
289
294
  }
290
- return r.value === "landing" ? F : z;
291
- }), c = u(() => G(i.value, `.${s}`));
295
+ return i.value === "landing" ? F : z;
296
+ }), r = f(() => G(l.value, `.${s}`));
292
297
  return {
293
- layoutName: r,
294
- layoutConfig: i,
295
- layoutCSS: c,
298
+ layoutName: i,
299
+ layoutConfig: l,
300
+ layoutCSS: r,
296
301
  layoutClass: n
297
302
  };
298
303
  }
@@ -453,39 +458,39 @@ function re(o) {
453
458
  if (s) return s;
454
459
  if (M.test(e.pathname))
455
460
  return t.ASSETS.fetch(a);
456
- const n = U(a, o), r = new URL("/index.html", a.url);
457
- let c = await (await t.ASSETS.fetch(new Request(r))).text();
458
- const p = o.defaultLocale, [m, g] = await Promise.all([
461
+ const n = U(a, o), i = new URL("/index.html", a.url);
462
+ let r = await (await t.ASSETS.fetch(new Request(i))).text();
463
+ const u = o.defaultLocale, [m, g] = await Promise.all([
459
464
  t.SITE_CONTENT.get("config"),
460
465
  t.SITE_CONTENT.get(`content:${n}`)
461
466
  ]), b = m ? JSON.parse(m) : {};
462
467
  let y = g ? JSON.parse(g) : {};
463
- if (!g && n !== p) {
464
- const f = await t.SITE_CONTENT.get(`content:${p}`);
465
- f && (y = JSON.parse(f));
468
+ if (!g && n !== u) {
469
+ const p = await t.SITE_CONTENT.get(`content:${u}`);
470
+ p && (y = JSON.parse(p));
466
471
  }
467
472
  const h = e.pathname.match(/^\/p\/(.+)$/);
468
473
  if (h) {
469
- const f = h[1];
470
- if (!y[f]) {
471
- let d = await t.SITE_CONTENT.get(`page:${f}:${n}`);
472
- !d && n !== p && (d = await t.SITE_CONTENT.get(`page:${f}:${p}`)), d && (y[f] = JSON.parse(d));
474
+ const p = h[1];
475
+ if (!y[p]) {
476
+ let d = await t.SITE_CONTENT.get(`page:${p}:${n}`);
477
+ !d && n !== u && (d = await t.SITE_CONTENT.get(`page:${p}:${u}`)), d && (y[p] = JSON.parse(d));
473
478
  }
474
479
  }
475
480
  const w = {
476
481
  locale: n,
477
482
  config: b
478
483
  };
479
- for (const [f, d] of Object.entries(y))
480
- w[`page:${f}`] = d;
484
+ for (const [p, d] of Object.entries(y))
485
+ w[`page:${p}`] = d;
481
486
  if (h) {
482
- const f = h[1], d = w[`page:${f}`];
483
- d && (c = A(c, d, o.siteName));
487
+ const p = h[1], d = w[`page:${p}`];
488
+ d && (r = A(r, d, o.siteName));
484
489
  }
485
- e.pathname === "/" && y.home && (c = A(c, y.home, o.siteName));
486
- const l = `<script>window.__SITE_DATA__ = ${JSON.stringify(w)};<\/script>`;
487
- return c = c.replace("</head>", `${l}
488
- </head>`), new Response(c, {
490
+ e.pathname === "/" && y.home && (r = A(r, y.home, o.siteName));
491
+ const c = `<script>window.__SITE_DATA__ = ${JSON.stringify(w)};<\/script>`;
492
+ return r = r.replace("</head>", `${c}
493
+ </head>`), new Response(r, {
489
494
  headers: {
490
495
  "Content-Type": "text/html;charset=utf-8",
491
496
  "Cache-Control": "public, max-age=60"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xosen/site-sdk",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "description": "Shared Vue components and composables for Xosen site templates",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,24 +16,41 @@ export function useSiteComponents() {
16
16
  const initialLocale = siteData?.locale;
17
17
 
18
18
  const components = ref<Record<string, any>>({ ...((siteData as any)?.components || {}) });
19
+ let lastFetchedLocale: string | undefined = initialLocale;
20
+ let fetchPromise: Promise<void> | null = null;
19
21
 
20
22
  async function fetchComponents(loc: string) {
21
23
  // Skip fetching in preview mode — components come via postMessage
22
24
  if (isInPreview()) return;
23
25
 
26
+ // Skip if already fetched/loaded for this locale
27
+ if (loc === lastFetchedLocale) return;
28
+
24
29
  // If same as initial locale, use preloaded data
25
30
  if (loc === initialLocale) {
26
31
  components.value = { ...((siteData as any)?.components || {}) };
32
+ lastFetchedLocale = loc;
27
33
  return;
28
34
  }
29
- try {
30
- const res = await fetch(`/api/content/components:${loc}`);
31
- if (res.ok) {
32
- components.value = await res.json();
35
+
36
+ // Deduplicate concurrent fetches
37
+ if (fetchPromise) return;
38
+
39
+ fetchPromise = (async () => {
40
+ try {
41
+ const res = await fetch(`/api/content/components:${loc}`);
42
+ if (res.ok) {
43
+ components.value = await res.json();
44
+ lastFetchedLocale = loc;
45
+ }
46
+ } catch {
47
+ // Keep current
48
+ } finally {
49
+ fetchPromise = null;
33
50
  }
34
- } catch {
35
- // Keep current
36
- }
51
+ })();
52
+
53
+ await fetchPromise;
37
54
  }
38
55
 
39
56
  watch(locale, (newLocale) => fetchComponents(newLocale));