@xosen/site-sdk 0.0.7 → 0.0.9

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.
@@ -0,0 +1,54 @@
1
+ import type { Router } from 'vue-router';
2
+ export interface PreviewMessage {
3
+ type: 'xosen-preview-update';
4
+ page: {
5
+ slug: string;
6
+ locale: string;
7
+ title?: string;
8
+ blocks?: any[];
9
+ meta?: Record<string, any>;
10
+ layout?: string;
11
+ /** Page-level context fields (author, featuredImage, excerpt, etc.) */
12
+ [key: string]: any;
13
+ };
14
+ }
15
+ /** Check if running inside an iframe (preview mode) */
16
+ export declare function isInPreview(): boolean;
17
+ /**
18
+ * Install a router guard that blocks all navigation in preview mode.
19
+ * Call this once during app setup (e.g. in App.vue or router config).
20
+ */
21
+ export declare function setupPreviewRouter(router: Router): void;
22
+ /**
23
+ * Listen for live preview messages from the admin UI.
24
+ * When the admin sends page data via postMessage, this composable
25
+ * provides reactive access to the preview data.
26
+ */
27
+ export declare function useLivePreview(): {
28
+ previewPage: import("vue").Ref<{
29
+ [x: string]: any;
30
+ slug: string;
31
+ locale: string;
32
+ title?: string | undefined;
33
+ blocks?: any[] | undefined;
34
+ meta?: Record<string, any> | undefined;
35
+ layout?: string | undefined;
36
+ } | null, {
37
+ [key: string]: any;
38
+ slug: string;
39
+ locale: string;
40
+ title?: string;
41
+ blocks?: any[];
42
+ meta?: Record<string, any>;
43
+ layout?: string;
44
+ } | {
45
+ [x: string]: any;
46
+ slug: string;
47
+ locale: string;
48
+ title?: string | undefined;
49
+ blocks?: any[] | undefined;
50
+ meta?: Record<string, any> | undefined;
51
+ layout?: string | undefined;
52
+ } | null>;
53
+ isPreviewMode: import("vue").Ref<boolean, boolean>;
54
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Fetch data from the Xosen org API (for live data like tariffs, products).
3
+ * Uses the API base URL from site config.
4
+ */
5
+ export declare function useSiteApi(): {
6
+ get: <T>(path: string) => Promise<T>;
7
+ getTariffs: () => Promise<any[]>;
8
+ getProducts: () => Promise<any[]>;
9
+ };
@@ -0,0 +1,25 @@
1
+ import type { SiteConfig } from '../types/config.js';
2
+ /**
3
+ * Access the site configuration injected by the CF Worker.
4
+ */
5
+ export declare function useSiteConfig(): {
6
+ config: import("vue").ComputedRef<SiteConfig | null>;
7
+ components: import("vue").ComputedRef<Record<string, any>>;
8
+ navigation: import("vue").ComputedRef<import("../index.js").NavItem[]>;
9
+ locales: import("vue").ComputedRef<string[]>;
10
+ defaultLocale: import("vue").ComputedRef<string>;
11
+ branding: import("vue").ComputedRef<{
12
+ logo?: string;
13
+ favicon?: string;
14
+ siteName?: string;
15
+ }>;
16
+ footer: import("vue").ComputedRef<{
17
+ copyright?: string;
18
+ links?: Array<{
19
+ text: string;
20
+ url: string;
21
+ }>;
22
+ }>;
23
+ features: import("vue").ComputedRef<Record<string, boolean | Record<string, any>>>;
24
+ hasFeature: (key: string) => boolean;
25
+ };
@@ -0,0 +1,8 @@
1
+ import { type Ref } from 'vue';
2
+ /**
3
+ * Read preloaded site data from window.__SITE_DATA__ or fetch from API.
4
+ *
5
+ * @param key - Key in __SITE_DATA__ object (e.g. 'tariffs', 'contacts:offices')
6
+ * @param kvKey - KV key suffix for API fallback (e.g. 'tariffs', 'contacts:offices')
7
+ */
8
+ export declare function useSiteData<T>(key: string, kvKey: string): Ref<T | null>;
@@ -0,0 +1,6 @@
1
+ import type { Skin } from '../types/skin.js';
2
+ /**
3
+ * Apply skin CSS custom properties to :root.
4
+ * Reads skin from __SITE_DATA__.skin or accepts explicit skin object.
5
+ */
6
+ export declare function useSkin(skinOverride?: Skin): void;
@@ -0,0 +1,12 @@
1
+ export { useSiteData } from './composables/useSiteData.js';
2
+ export { useSiteConfig } from './composables/useSiteConfig.js';
3
+ export { useSkin } from './composables/useSkin.js';
4
+ export { useSiteApi } from './composables/useSiteApi.js';
5
+ export { useLivePreview, setupPreviewRouter, isInPreview } from './composables/useLivePreview.js';
6
+ export type { PreviewMessage } from './composables/useLivePreview.js';
7
+ export { createSiteWorker } from './worker/index.js';
8
+ export type { WorkerEnv, WorkerConfig, PageData } from './worker/index.js';
9
+ export type { Block, BlockSettings, HeroBlockData, HtmlBlockData, CardsBlockData, ImageTextBlockData, PricingBlockData, PricingPlan, ContactsBlockData, Office, GalleryBlockData, MapBlockData, } from './types/blocks.js';
10
+ export type { SiteConfig, NavItem, PageConfig, SiteData, PageContext } from './types/config.js';
11
+ export { PAGE_CONTEXT_KEY } from './types/config.js';
12
+ export type { Skin } from './types/skin.js';
package/dist/index.js CHANGED
@@ -1,31 +1,31 @@
1
- import { ref as y, onMounted as T, watch as E, computed as u, onUnmounted as b } from "vue";
2
- import { useI18n as x } from "vue-i18n";
3
- function P(o, n) {
4
- const t = y(null), { locale: e } = x(), a = window.__SITE_DATA__, s = a?.locale;
1
+ import { ref as y, onMounted as T, watch as $, computed as u, onUnmounted as b } from "vue";
2
+ import { useI18n as C } from "vue-i18n";
3
+ function R(n, o) {
4
+ const t = y(null), { locale: e } = C(), a = window.__SITE_DATA__, s = a?.locale;
5
5
  function c() {
6
- return a && a[o] && e.value === s ? (t.value = a[o], !0) : !1;
6
+ return a && a[n] && e.value === s ? (t.value = a[n], !0) : !1;
7
7
  }
8
8
  async function h() {
9
9
  try {
10
- const r = await fetch(`/api/content/${n}:${e.value}`);
10
+ const r = await fetch(`/api/content/${o}:${e.value}`);
11
11
  r.ok && (t.value = await r.json());
12
12
  } catch {
13
13
  }
14
14
  }
15
15
  return T(() => {
16
16
  c() || h();
17
- }), E(e, () => {
17
+ }), $(e, () => {
18
18
  h();
19
19
  }), t;
20
20
  }
21
21
  function O() {
22
- const o = window.__SITE_DATA__, n = u(() => o?.config || null), t = u(() => o?.components || {}), e = u(() => n.value?.navigation || []), a = u(() => n.value?.locales || []), s = u(() => n.value?.defaultLocale || "en"), c = u(() => n.value?.branding || {}), h = u(() => n.value?.footer || {}), r = u(() => n.value?.features || {});
23
- function p(g) {
24
- const f = r.value[g];
25
- return typeof f == "boolean" ? f : typeof f == "object";
22
+ const n = window.__SITE_DATA__, o = u(() => n?.config || null), t = u(() => n?.components || {}), e = u(() => o.value?.navigation || []), a = u(() => o.value?.locales || []), s = u(() => o.value?.defaultLocale || "en"), c = u(() => o.value?.branding || {}), h = u(() => o.value?.footer || {}), r = u(() => o.value?.features || {});
23
+ function f(g) {
24
+ const p = r.value[g];
25
+ return typeof p == "boolean" ? p : typeof p == "object";
26
26
  }
27
27
  return {
28
- config: n,
28
+ config: o,
29
29
  components: t,
30
30
  navigation: e,
31
31
  locales: a,
@@ -33,37 +33,37 @@ function O() {
33
33
  branding: c,
34
34
  footer: h,
35
35
  features: r,
36
- hasFeature: p
36
+ hasFeature: f
37
37
  };
38
38
  }
39
- function j(o) {
40
- function n(t) {
39
+ function j(n) {
40
+ function o(t) {
41
41
  const e = document.documentElement;
42
42
  if (t.colors)
43
43
  for (const [a, s] of Object.entries(t.colors))
44
- s && e.style.setProperty(`--x-color-${A(a)}`, s);
44
+ s && e.style.setProperty(`--x-color-${x(a)}`, s);
45
45
  t.typography?.fontFamily && e.style.setProperty("--x-font-family", t.typography.fontFamily), t.typography?.headingFont && e.style.setProperty("--x-font-heading", t.typography.headingFont), t.typography?.baseFontSize && e.style.setProperty("--x-font-size", t.typography.baseFontSize), t.shape?.borderRadius && e.style.setProperty("--x-border-radius", t.shape.borderRadius), t.shape?.maxWidth && e.style.setProperty("--x-max-width", t.shape.maxWidth);
46
46
  }
47
47
  T(() => {
48
- if (o) {
49
- n(o);
48
+ if (n) {
49
+ o(n);
50
50
  return;
51
51
  }
52
52
  const e = window.__SITE_DATA__?.skin;
53
- e && (e.colors || e.typography || e.shape) && n({
53
+ e && (e.colors || e.typography || e.shape) && o({
54
54
  colors: e.colors || {},
55
55
  typography: e.typography || {},
56
56
  shape: e.shape || {}
57
57
  });
58
58
  });
59
59
  }
60
- function A(o) {
61
- return o.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
60
+ function x(n) {
61
+ return n.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
62
62
  }
63
63
  function F() {
64
- const n = window.__SITE_DATA__?.apiBase || "";
64
+ const o = window.__SITE_DATA__?.apiBase || "";
65
65
  async function t(s) {
66
- const c = await fetch(`${n}${s}`);
66
+ const c = await fetch(`${o}${s}`);
67
67
  if (!c.ok)
68
68
  throw new Error(`API error: ${c.status}`);
69
69
  return c.json();
@@ -87,43 +87,43 @@ function S() {
87
87
  return !0;
88
88
  }
89
89
  }
90
- function D(o) {
91
- S() && o.beforeEach((n, t) => !t.name);
90
+ function D(n) {
91
+ S() && n.beforeEach((o, t) => !t.name);
92
92
  }
93
93
  function U() {
94
- const o = y(null), n = y(S());
94
+ const n = y(null), o = y(S());
95
95
  function t(e) {
96
96
  const a = e.data;
97
- a?.type === "xosen-preview-update" && a.page && (o.value = a.page, n.value = !0);
97
+ a?.type === "xosen-preview-update" && a.page && (n.value = a.page, o.value = !0);
98
98
  }
99
99
  return T(() => {
100
100
  window.addEventListener("message", t);
101
101
  }), b(() => {
102
102
  window.removeEventListener("message", t);
103
103
  }), {
104
- previewPage: o,
105
- isPreviewMode: n
104
+ previewPage: n,
105
+ isPreviewMode: o
106
106
  };
107
107
  }
108
- const C = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|webp|avif|map|json|txt|xml|webmanifest)$/;
109
- function N(o, n) {
110
- const e = new URL(o.url).searchParams.get("lang");
111
- if (e && n.supportedLocales?.includes(e)) return e;
112
- if (n.supportedLocales?.length) {
113
- const a = o.headers.get("Accept-Language") || "";
114
- for (const s of n.supportedLocales)
108
+ const A = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|webp|avif|map|json|txt|xml|webmanifest)$/;
109
+ function N(n, o) {
110
+ const e = new URL(n.url).searchParams.get("lang");
111
+ if (e && o.supportedLocales?.includes(e)) return e;
112
+ if (o.supportedLocales?.length) {
113
+ const a = n.headers.get("Accept-Language") || "";
114
+ for (const s of o.supportedLocales)
115
115
  if (a.includes(s)) return s;
116
116
  }
117
- return n.defaultLocale;
117
+ return o.defaultLocale;
118
118
  }
119
- function I(o, n, t) {
120
- if (!o.pathname.startsWith("/api/content/")) return null;
121
- const e = decodeURIComponent(o.pathname.replace("/api/content/", ""));
119
+ function I(n, o, t) {
120
+ if (!n.pathname.startsWith("/api/content/")) return null;
121
+ const e = decodeURIComponent(n.pathname.replace("/api/content/", ""));
122
122
  return e ? (async () => {
123
- let a = await n.SITE_CONTENT.get(e);
123
+ let a = await o.SITE_CONTENT.get(e);
124
124
  if (!a) {
125
125
  const s = e.lastIndexOf(":");
126
- s > 0 && e.slice(s + 1) !== t && (a = await n.SITE_CONTENT.get(e.slice(0, s + 1) + t));
126
+ s > 0 && e.slice(s + 1) !== t && (a = await o.SITE_CONTENT.get(e.slice(0, s + 1) + t));
127
127
  }
128
128
  return a ? new Response(a, {
129
129
  headers: {
@@ -134,30 +134,30 @@ function I(o, n, t) {
134
134
  }) : Response.json({ error: "Not found" }, { status: 404 });
135
135
  })() : Promise.resolve(Response.json({ error: "Key is required" }, { status: 400 }));
136
136
  }
137
- function _(o, n, t) {
138
- let e = o;
139
- if (n.title) {
137
+ function _(n, o, t) {
138
+ let e = n;
139
+ if (o.title) {
140
140
  const a = t ? ` — ${t}` : "";
141
- e = e.replace(/<title>[^<]*<\/title>/, `<title>${n.title}${a}</title>`);
141
+ e = e.replace(/<title>[^<]*<\/title>/, `<title>${o.title}${a}</title>`);
142
142
  }
143
- return n.meta?.description && (e = e.replace(/(<meta\s+name="description"\s+content=")[^"]*(")/, `$1${n.meta.description}$2`)), n.meta?.ogTitle && (e = e.replace(/(<meta\s+property="og:title"\s+content=")[^"]*(")/, `$1${n.meta.ogTitle}$2`)), n.html && (e = e.replace('<div id="app"></div>', `<div id="app">${n.html}</div>`)), e;
143
+ return o.meta?.description && (e = e.replace(/(<meta\s+name="description"\s+content=")[^"]*(")/, `$1${o.meta.description}$2`)), o.meta?.ogTitle && (e = e.replace(/(<meta\s+property="og:title"\s+content=")[^"]*(")/, `$1${o.meta.ogTitle}$2`)), o.html && (e = e.replace('<div id="app"></div>', `<div id="app">${o.html}</div>`)), e;
144
144
  }
145
- function J(o) {
145
+ function J(n) {
146
146
  return {
147
- async fetch(n, t) {
148
- const e = new URL(n.url), a = I(e, t, o.defaultLocale);
147
+ async fetch(o, t) {
148
+ const e = new URL(o.url), a = I(e, t, n.defaultLocale);
149
149
  if (a) return a;
150
- if (C.test(e.pathname))
151
- return t.ASSETS.fetch(n);
152
- const s = N(n, o), c = new URL("/index.html", n.url);
150
+ if (A.test(e.pathname))
151
+ return t.ASSETS.fetch(o);
152
+ const s = N(o, n), c = new URL("/index.html", o.url);
153
153
  let r = await (await t.ASSETS.fetch(new Request(c))).text();
154
- const p = o.defaultLocale, [g, f] = await Promise.all([
154
+ const f = n.defaultLocale, [g, p] = await Promise.all([
155
155
  t.SITE_CONTENT.get("config"),
156
156
  t.SITE_CONTENT.get(`content:${s}`)
157
157
  ]), v = g ? JSON.parse(g) : {};
158
- let d = f ? JSON.parse(f) : {};
159
- if (!f && s !== p) {
160
- const i = await t.SITE_CONTENT.get(`content:${p}`);
158
+ let d = p ? JSON.parse(p) : {};
159
+ if (!p && s !== f) {
160
+ const i = await t.SITE_CONTENT.get(`content:${f}`);
161
161
  i && (d = JSON.parse(i));
162
162
  }
163
163
  const m = e.pathname.match(/^\/p\/(.+)$/);
@@ -165,7 +165,7 @@ function J(o) {
165
165
  const i = m[1];
166
166
  if (!d[i]) {
167
167
  let l = await t.SITE_CONTENT.get(`page:${i}:${s}`);
168
- !l && s !== p && (l = await t.SITE_CONTENT.get(`page:${i}:${p}`)), l && (d[i] = JSON.parse(l));
168
+ !l && s !== f && (l = await t.SITE_CONTENT.get(`page:${i}:${f}`)), l && (d[i] = JSON.parse(l));
169
169
  }
170
170
  }
171
171
  const w = {
@@ -176,11 +176,11 @@ function J(o) {
176
176
  w[`page:${i}`] = l;
177
177
  if (m) {
178
178
  const i = m[1], l = w[`page:${i}`];
179
- l && (r = _(r, l, o.siteName));
179
+ l && (r = _(r, l, n.siteName));
180
180
  }
181
- e.pathname === "/" && d.home && (r = _(r, d.home, o.siteName));
182
- const $ = `<script>window.__SITE_DATA__ = ${JSON.stringify(w)};<\/script>`;
183
- return r = r.replace("</head>", `${$}
181
+ e.pathname === "/" && d.home && (r = _(r, d.home, n.siteName));
182
+ const E = `<script>window.__SITE_DATA__ = ${JSON.stringify(w)};<\/script>`;
183
+ return r = r.replace("</head>", `${E}
184
184
  </head>`), new Response(r, {
185
185
  headers: {
186
186
  "Content-Type": "text/html;charset=utf-8",
@@ -190,13 +190,15 @@ function J(o) {
190
190
  }
191
191
  };
192
192
  }
193
+ const M = /* @__PURE__ */ Symbol("pageContext");
193
194
  export {
195
+ M as PAGE_CONTEXT_KEY,
194
196
  J as createSiteWorker,
195
197
  S as isInPreview,
196
198
  D as setupPreviewRouter,
197
199
  U as useLivePreview,
198
200
  F as useSiteApi,
199
201
  O as useSiteConfig,
200
- P as useSiteData,
202
+ R as useSiteData,
201
203
  j as useSkin
202
204
  };
@@ -0,0 +1,103 @@
1
+ export interface BlockSettings {
2
+ id?: string;
3
+ visible?: boolean;
4
+ className?: string;
5
+ spacing?: 'none' | 'sm' | 'md' | 'lg';
6
+ }
7
+ export interface Block<T = any> {
8
+ type: string;
9
+ data?: T;
10
+ /** Alias for data — accepted for convenience */
11
+ props?: T;
12
+ settings?: BlockSettings & Record<string, any>;
13
+ }
14
+ export interface HeroBlockData {
15
+ title: string;
16
+ subtitle?: string;
17
+ image?: string;
18
+ overlay?: boolean;
19
+ buttons?: Array<{
20
+ text: string;
21
+ url: string;
22
+ variant?: 'primary' | 'outline' | 'white';
23
+ }>;
24
+ stats?: Array<{
25
+ value: string;
26
+ label: string;
27
+ }>;
28
+ }
29
+ export interface HtmlBlockData {
30
+ content: string;
31
+ }
32
+ export interface CardsBlockData {
33
+ title?: string;
34
+ subtitle?: string;
35
+ cards: Array<{
36
+ title: string;
37
+ desc: string;
38
+ icon?: string;
39
+ image?: string;
40
+ link?: string;
41
+ }>;
42
+ }
43
+ export interface ImageTextBlockData {
44
+ title?: string;
45
+ content?: string;
46
+ image?: string;
47
+ items?: Array<{
48
+ text: string;
49
+ icon?: string;
50
+ }>;
51
+ }
52
+ export interface PricingPlan {
53
+ id?: string;
54
+ name: string;
55
+ description?: string;
56
+ price: string | number;
57
+ period?: string;
58
+ features?: string[];
59
+ actionUrl?: string;
60
+ actionText?: string;
61
+ tariffGroupName?: string;
62
+ }
63
+ export interface PricingBlockData {
64
+ title?: string;
65
+ subtitle?: string;
66
+ plans?: PricingPlan[];
67
+ }
68
+ export interface Office {
69
+ name: string;
70
+ address?: string;
71
+ phone?: string;
72
+ email?: string;
73
+ hours?: string;
74
+ isMain?: boolean;
75
+ }
76
+ export interface ContactsBlockData {
77
+ title?: string;
78
+ subtitle?: string;
79
+ showBankDetails?: boolean;
80
+ showForm?: boolean;
81
+ offices?: Office[];
82
+ }
83
+ export interface GalleryBlockData {
84
+ title?: string;
85
+ images: Array<{
86
+ url: string;
87
+ alt?: string;
88
+ caption?: string;
89
+ }>;
90
+ }
91
+ export interface MapBlockData {
92
+ title?: string;
93
+ center: {
94
+ lat: number;
95
+ lng: number;
96
+ };
97
+ zoom?: number;
98
+ markers?: Array<{
99
+ lat: number;
100
+ lng: number;
101
+ label?: string;
102
+ }>;
103
+ }
@@ -0,0 +1,56 @@
1
+ import type { InjectionKey, Ref } from 'vue';
2
+ import type { Block } from './blocks.js';
3
+ export interface PageContext {
4
+ title?: string;
5
+ type?: string;
6
+ author?: string;
7
+ featuredImage?: string;
8
+ excerpt?: string;
9
+ isGated?: boolean;
10
+ termIds?: string[];
11
+ layout?: string;
12
+ publishedAt?: string;
13
+ }
14
+ export declare const PAGE_CONTEXT_KEY: InjectionKey<Ref<PageContext>>;
15
+ export interface SiteConfig {
16
+ template: string;
17
+ version: string;
18
+ skin: string;
19
+ autoUpdate?: boolean;
20
+ branding: {
21
+ logo?: string;
22
+ favicon?: string;
23
+ siteName?: string;
24
+ };
25
+ navigation: NavItem[];
26
+ locales: string[];
27
+ defaultLocale: string;
28
+ footer?: {
29
+ copyright?: string;
30
+ links?: Array<{
31
+ text: string;
32
+ url: string;
33
+ }>;
34
+ };
35
+ features?: Record<string, boolean | Record<string, any>>;
36
+ }
37
+ export interface NavItem {
38
+ text: string | Record<string, string>;
39
+ url: string;
40
+ external?: boolean;
41
+ children?: NavItem[];
42
+ }
43
+ export interface PageConfig {
44
+ title: string;
45
+ blocks: Block[];
46
+ meta?: {
47
+ description?: string;
48
+ ogTitle?: string;
49
+ ogImage?: string;
50
+ };
51
+ }
52
+ export interface SiteData {
53
+ config: SiteConfig;
54
+ locale: string;
55
+ [key: string]: any;
56
+ }
@@ -0,0 +1,41 @@
1
+ export interface Skin {
2
+ name: string;
3
+ colors: {
4
+ primary: string;
5
+ primaryDark?: string;
6
+ primaryLight?: string;
7
+ secondary?: string;
8
+ accent?: string;
9
+ background: string;
10
+ backgroundAlt?: string;
11
+ surface: string;
12
+ text: string;
13
+ textLight?: string;
14
+ border?: string;
15
+ success?: string;
16
+ error?: string;
17
+ };
18
+ typography: {
19
+ fontFamily: string;
20
+ headingFont?: string;
21
+ baseFontSize?: string;
22
+ };
23
+ shape: {
24
+ borderRadius: string;
25
+ cardElevation?: number;
26
+ maxWidth?: string;
27
+ };
28
+ components?: {
29
+ navbar?: {
30
+ variant?: 'fixed' | 'static';
31
+ transparent?: boolean;
32
+ };
33
+ footer?: {
34
+ variant?: 'dark' | 'light';
35
+ };
36
+ hero?: {
37
+ variant?: 'fullscreen' | 'compact';
38
+ overlay?: boolean;
39
+ };
40
+ };
41
+ }
@@ -0,0 +1,14 @@
1
+ import type { WorkerEnv, WorkerConfig } from './types.js';
2
+ /**
3
+ * Create a Cloudflare Worker handler for an Xosen site template.
4
+ *
5
+ * The worker:
6
+ * - Serves static assets from ASSETS binding
7
+ * - Provides /api/content/:key endpoint for KV reads
8
+ * - Injects preloaded KV data as window.__SITE_DATA__ into HTML pages
9
+ * - Detects locale from query param or Accept-Language header
10
+ * - Preloads page-specific data for /p/:slug routes
11
+ */
12
+ export declare function createSiteWorker(config: WorkerConfig): {
13
+ fetch: (request: Request, env: WorkerEnv) => Promise<Response>;
14
+ };
@@ -0,0 +1,2 @@
1
+ export { createSiteWorker } from './create-site-worker.js';
2
+ export type { WorkerEnv, WorkerConfig, PageData } from './types.js';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Minimal CF Workers type declarations to avoid requiring @cloudflare/workers-types.
3
+ * These match the Cloudflare Workers runtime API.
4
+ */
5
+ export interface CfKVNamespace {
6
+ get(key: string, options?: any): Promise<string | null>;
7
+ put(key: string, value: string | ArrayBuffer | ReadableStream, options?: any): Promise<void>;
8
+ delete(key: string): Promise<void>;
9
+ list(options?: any): Promise<any>;
10
+ }
11
+ export interface CfFetcher {
12
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
13
+ }
14
+ export interface WorkerEnv {
15
+ SITE_CONTENT: CfKVNamespace;
16
+ ASSETS: CfFetcher;
17
+ }
18
+ export interface WorkerConfig {
19
+ /** Default locale for the site */
20
+ defaultLocale: string;
21
+ /** Supported locales for Accept-Language detection */
22
+ supportedLocales?: string[];
23
+ /** Site name for meta title suffix, e.g. 'SWI' */
24
+ siteName?: string;
25
+ }
26
+ export interface PageData {
27
+ title?: string;
28
+ blocks?: unknown[];
29
+ html?: string;
30
+ meta?: {
31
+ description?: string;
32
+ ogTitle?: string;
33
+ ogImage?: string;
34
+ };
35
+ type?: string;
36
+ author?: string;
37
+ featuredImage?: string;
38
+ excerpt?: string;
39
+ termIds?: string[];
40
+ layout?: string;
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xosen/site-sdk",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Shared Vue components and composables for Xosen site templates",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,14 +25,14 @@
25
25
  "src"
26
26
  ],
27
27
  "scripts": {
28
- "build": "vue-tsc --declaration --emitDeclarationOnly --outDir dist && vite build",
28
+ "build": "vite build && vue-tsc --declaration --emitDeclarationOnly --outDir dist",
29
29
  "dev": "vite build --watch",
30
30
  "clean": "rm -rf dist"
31
31
  },
32
32
  "peerDependencies": {
33
- "vue": "catalog:",
34
- "vue-i18n": "catalog:",
35
- "vue-router": "catalog:"
33
+ "vue": "^3.5.0",
34
+ "vue-i18n": "^11.0.0",
35
+ "vue-router": "^5.0.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "vue": "catalog:",
@@ -10,6 +10,8 @@ export interface PreviewMessage {
10
10
  blocks?: any[];
11
11
  meta?: Record<string, any>;
12
12
  layout?: string;
13
+ /** Page-level context fields (author, featuredImage, excerpt, etc.) */
14
+ [key: string]: any;
13
15
  };
14
16
  }
15
17
 
package/src/index.ts CHANGED
@@ -25,5 +25,6 @@ export type {
25
25
  GalleryBlockData,
26
26
  MapBlockData,
27
27
  } from './types/blocks.js';
28
- export type { SiteConfig, NavItem, PageConfig, SiteData } from './types/config.js';
28
+ export type { SiteConfig, NavItem, PageConfig, SiteData, PageContext } from './types/config.js';
29
+ export { PAGE_CONTEXT_KEY } from './types/config.js';
29
30
  export type { Skin } from './types/skin.js';
@@ -82,7 +82,7 @@ export interface ContactsBlockData {
82
82
 
83
83
  export interface GalleryBlockData {
84
84
  title?: string;
85
- images: Array<{ src: string; alt?: string; caption?: string }>;
85
+ images: Array<{ url: string; alt?: string; caption?: string }>;
86
86
  }
87
87
 
88
88
  export interface MapBlockData {
@@ -1,5 +1,20 @@
1
+ import type { InjectionKey, Ref } from 'vue';
1
2
  import type { Block } from './blocks.js';
2
3
 
4
+ export interface PageContext {
5
+ title?: string;
6
+ type?: string;
7
+ author?: string;
8
+ featuredImage?: string;
9
+ excerpt?: string;
10
+ isGated?: boolean;
11
+ termIds?: string[];
12
+ layout?: string;
13
+ publishedAt?: string;
14
+ }
15
+
16
+ export const PAGE_CONTEXT_KEY: InjectionKey<Ref<PageContext>> = Symbol('pageContext');
17
+
3
18
  export interface SiteConfig {
4
19
  template: string;
5
20
  version: string;
@@ -36,4 +36,10 @@ export interface PageData {
36
36
  ogTitle?: string;
37
37
  ogImage?: string;
38
38
  };
39
+ type?: string;
40
+ author?: string;
41
+ featuredImage?: string;
42
+ excerpt?: string;
43
+ termIds?: string[];
44
+ layout?: string;
39
45
  }