@bagelink/blox 1.13.2 → 1.13.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"CmsPageView.vue.d.ts","sourceRoot":"","sources":["../src/CmsPageView.vue"],"names":[],"mappings":";AAgTA,wBAMG"}
1
+ {"version":3,"file":"CmsPageView.vue.d.ts","sourceRoot":"","sources":["../src/CmsPageView.vue"],"names":[],"mappings":";AAoXA,wBAMG"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAe,MAAM,SAAS,CAAA;AAShF,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIrE;AA+BD,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAMxF;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAsB,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1D,UAAU,EAAE,MAAM,EAClB,EAAE,MAAM,EAAE,CAAC,EAAE,MAAU,EAAE,KAAW,EAAE,GAAE,gBAAqB,GAC3D,OAAO,CAAC,mBAAmB,GAAG;IAAE,KAAK,EAAE,CAAC,EAAE,CAAA;CAAE,CAAC,CAK/C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAe,MAAM,SAAS,CAAA;AAUhF,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIrE;AAyCD,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAMxF;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAsB,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1D,UAAU,EAAE,MAAM,EAClB,EAAE,MAAM,EAAE,CAAC,EAAE,MAAU,EAAE,KAAW,EAAE,GAAE,gBAAqB,GAC3D,OAAO,CAAC,mBAAmB,GAAG;IAAE,KAAK,EAAE,CAAC,EAAE,CAAA;CAAE,CAAC,CAK/C"}
@@ -0,0 +1,8 @@
1
+ const BLOX_STATE_WINDOW_KEY = "__BLOX_STATE__";
2
+ const BLOX_COLLECTIONS_WINDOW_KEY = "__BLOX_COLLECTIONS__";
3
+ const BLOX_WEBSITE_ID_WINDOW_KEY = "__BLOX_WEBSITE_ID__";
4
+ export {
5
+ BLOX_STATE_WINDOW_KEY as B,
6
+ BLOX_COLLECTIONS_WINDOW_KEY as a,
7
+ BLOX_WEBSITE_ID_WINDOW_KEY as b
8
+ };
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ const BLOX_STATE_WINDOW_KEY = "__BLOX_STATE__";
3
+ const BLOX_COLLECTIONS_WINDOW_KEY = "__BLOX_COLLECTIONS__";
4
+ const BLOX_WEBSITE_ID_WINDOW_KEY = "__BLOX_WEBSITE_ID__";
5
+ exports.BLOX_COLLECTIONS_WINDOW_KEY = BLOX_COLLECTIONS_WINDOW_KEY;
6
+ exports.BLOX_STATE_WINDOW_KEY = BLOX_STATE_WINDOW_KEY;
7
+ exports.BLOX_WEBSITE_ID_WINDOW_KEY = BLOX_WEBSITE_ID_WINDOW_KEY;
package/dist/index.cjs CHANGED
@@ -3,10 +3,10 @@ var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
+ const constants = require("./constants-CFB_pMvT.cjs");
6
7
  const vue = require("vue");
7
8
  const vueRouter = require("vue-router");
8
9
  const vue$1 = require("@bagelink/vue");
9
- const constants = require("./constants-fZvybj0k.cjs");
10
10
  const BASE = "/api";
11
11
  let _websiteName = "";
12
12
  let _store = "";
@@ -29,6 +29,13 @@ async function request(path, { locale: locale2, params, ...opts } = {}) {
29
29
  }
30
30
  async function getWebsiteId() {
31
31
  if (_websiteId) return _websiteId;
32
+ if (typeof window !== "undefined") {
33
+ const embedded = window[constants.BLOX_WEBSITE_ID_WINDOW_KEY];
34
+ if (typeof embedded === "string" && embedded !== "") {
35
+ _websiteId = embedded;
36
+ return _websiteId;
37
+ }
38
+ }
32
39
  const websites = await request("/cms/websites");
33
40
  const website = websites.find((w) => w.name === _websiteName);
34
41
  if (!website) throw new Error(`Website "${_websiteName}" not found. Run: npm run seed`);
@@ -111,6 +118,10 @@ const _hoisted_3 = {
111
118
  key: 2,
112
119
  class: "blox-error"
113
120
  };
121
+ const _hoisted_4 = {
122
+ key: 0,
123
+ class: "blox-nav-progress"
124
+ };
114
125
  const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
115
126
  __name: "CmsPageView",
116
127
  setup(__props) {
@@ -123,6 +134,8 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
123
134
  vue.provide("contexts", contexts);
124
135
  const notFound = vue.ref(false);
125
136
  const error = vue.ref(null);
137
+ const initialLoading = vue.ref(true);
138
+ const navigating = vue.ref(false);
126
139
  function tryHydrateFromState(path) {
127
140
  var _a, _b;
128
141
  const win = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : void 0;
@@ -136,6 +149,14 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
136
149
  setAlternates(entry.alternates ?? {});
137
150
  return true;
138
151
  }
152
+ function updateDocumentTitle(resolved) {
153
+ if (typeof document === "undefined") return;
154
+ const page = resolved.page;
155
+ const ctx = resolved.contexts ? Object.values(resolved.contexts).find((c) => c != null) : null;
156
+ const ctxTitle = ctx && typeof ctx.title === "string" ? ctx.title : null;
157
+ const title = page.meta_title || ctxTitle || page.title;
158
+ if (title) document.title = title;
159
+ }
139
160
  const initialSlug = (() => {
140
161
  var _a;
141
162
  if (strategy) {
@@ -145,24 +166,24 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
145
166
  return route.path || "/";
146
167
  })();
147
168
  const ssrHydrated = tryHydrateFromState(initialSlug);
148
- const loading = vue.ref(!ssrHydrated);
169
+ if (ssrHydrated) initialLoading.value = false;
149
170
  async function load() {
150
171
  var _a;
151
- loading.value = true;
152
172
  notFound.value = false;
153
173
  error.value = null;
154
174
  const { locale: urlLocale, slug } = strategy ? strategy.detect(typeof window !== "undefined" ? window.location.hostname : "", route.path) : { locale: locale2.value, slug: route.path || "/" };
155
175
  if (urlLocale !== locale2.value) setLocale(urlLocale);
156
- setAlternates({});
157
176
  if (tryHydrateFromState(slug)) {
158
- loading.value = false;
177
+ initialLoading.value = false;
159
178
  return;
160
179
  }
180
+ navigating.value = true;
161
181
  try {
162
182
  const resolved = await resolvePath(slug, urlLocale);
163
183
  blocks.value = ((_a = resolved.page.content) == null ? void 0 : _a.blocks) ?? [];
164
184
  contexts.value = resolved.contexts ?? {};
165
185
  setAlternates(resolved.alternates ?? {});
186
+ updateDocumentTitle(resolved);
166
187
  } catch (e) {
167
188
  const msg = e instanceof Error ? e.message : String(e);
168
189
  if (msg.includes("404")) {
@@ -172,7 +193,8 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
172
193
  error.value = msg;
173
194
  }
174
195
  } finally {
175
- loading.value = false;
196
+ initialLoading.value = false;
197
+ navigating.value = false;
176
198
  }
177
199
  }
178
200
  if (!ssrHydrated) {
@@ -182,7 +204,7 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
182
204
  return (_ctx, _cache) => {
183
205
  const _component_RouterLink = vue.resolveComponent("RouterLink");
184
206
  return vue.openBlock(), vue.createElementBlock("div", null, [
185
- loading.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$1, " Loading… ")) : notFound.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2, [
207
+ initialLoading.value && blocks.value.length === 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$1, " Loading… ")) : notFound.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2, [
186
208
  _cache[0] || (_cache[0] = vue.createElementVNode("h2", null, "404", -1)),
187
209
  vue.createElementVNode("p", null, vue.toDisplayString(vue.unref(locale2) === "he" ? "הדף לא נמצא" : "Page not found"), 1),
188
210
  vue.createVNode(_component_RouterLink, { to: "/" }, {
@@ -191,10 +213,10 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
191
213
  ]),
192
214
  _: 1
193
215
  })
194
- ])) : error.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, vue.toDisplayString(error.value), 1)) : (vue.openBlock(), vue.createBlock(vue.unref(PageRenderer), {
195
- key: 3,
196
- blocks: blocks.value
197
- }, null, 8, ["blocks"]))
216
+ ])) : error.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, vue.toDisplayString(error.value), 1)) : (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 3 }, [
217
+ navigating.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4)) : vue.createCommentVNode("", true),
218
+ vue.createVNode(vue.unref(PageRenderer), { blocks: blocks.value }, null, 8, ["blocks"])
219
+ ], 64))
198
220
  ]);
199
221
  };
200
222
  }
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { computed, ref, defineComponent, inject, h, provide, watch, resolveComponent, createElementBlock, openBlock, createBlock, createElementVNode, createVNode, toDisplayString, unref, withCtx, createTextVNode, normalizeProps, guardReactiveProps, renderSlot, onMounted, onUnmounted, nextTick } from "vue";
4
+ import { b as BLOX_WEBSITE_ID_WINDOW_KEY, B as BLOX_STATE_WINDOW_KEY } from "./constants-BjitNk-W.js";
5
+ import { computed, ref, defineComponent, inject, h, provide, watch, resolveComponent, createElementBlock, openBlock, createElementVNode, createVNode, toDisplayString, unref, withCtx, createTextVNode, Fragment, createCommentVNode, createBlock, normalizeProps, guardReactiveProps, renderSlot, onMounted, onUnmounted, nextTick } from "vue";
5
6
  import { useRoute, useRouter, RouterLink } from "vue-router";
6
7
  import { getI18n } from "@bagelink/vue";
7
- import { B as BLOX_STATE_WINDOW_KEY } from "./constants-BIbQhd3z.js";
8
8
  const BASE = "/api";
9
9
  let _websiteName = "";
10
10
  let _store = "";
@@ -27,6 +27,13 @@ async function request(path, { locale: locale2, params, ...opts } = {}) {
27
27
  }
28
28
  async function getWebsiteId() {
29
29
  if (_websiteId) return _websiteId;
30
+ if (typeof window !== "undefined") {
31
+ const embedded = window[BLOX_WEBSITE_ID_WINDOW_KEY];
32
+ if (typeof embedded === "string" && embedded !== "") {
33
+ _websiteId = embedded;
34
+ return _websiteId;
35
+ }
36
+ }
30
37
  const websites = await request("/cms/websites");
31
38
  const website = websites.find((w) => w.name === _websiteName);
32
39
  if (!website) throw new Error(`Website "${_websiteName}" not found. Run: npm run seed`);
@@ -109,6 +116,10 @@ const _hoisted_3 = {
109
116
  key: 2,
110
117
  class: "blox-error"
111
118
  };
119
+ const _hoisted_4 = {
120
+ key: 0,
121
+ class: "blox-nav-progress"
122
+ };
112
123
  const _sfc_main$2 = /* @__PURE__ */ defineComponent({
113
124
  __name: "CmsPageView",
114
125
  setup(__props) {
@@ -121,6 +132,8 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
121
132
  provide("contexts", contexts);
122
133
  const notFound = ref(false);
123
134
  const error = ref(null);
135
+ const initialLoading = ref(true);
136
+ const navigating = ref(false);
124
137
  function tryHydrateFromState(path) {
125
138
  var _a, _b;
126
139
  const win = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : void 0;
@@ -134,6 +147,14 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
134
147
  setAlternates(entry.alternates ?? {});
135
148
  return true;
136
149
  }
150
+ function updateDocumentTitle(resolved) {
151
+ if (typeof document === "undefined") return;
152
+ const page = resolved.page;
153
+ const ctx = resolved.contexts ? Object.values(resolved.contexts).find((c) => c != null) : null;
154
+ const ctxTitle = ctx && typeof ctx.title === "string" ? ctx.title : null;
155
+ const title = page.meta_title || ctxTitle || page.title;
156
+ if (title) document.title = title;
157
+ }
137
158
  const initialSlug = (() => {
138
159
  var _a;
139
160
  if (strategy) {
@@ -143,24 +164,24 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
143
164
  return route.path || "/";
144
165
  })();
145
166
  const ssrHydrated = tryHydrateFromState(initialSlug);
146
- const loading = ref(!ssrHydrated);
167
+ if (ssrHydrated) initialLoading.value = false;
147
168
  async function load() {
148
169
  var _a;
149
- loading.value = true;
150
170
  notFound.value = false;
151
171
  error.value = null;
152
172
  const { locale: urlLocale, slug } = strategy ? strategy.detect(typeof window !== "undefined" ? window.location.hostname : "", route.path) : { locale: locale2.value, slug: route.path || "/" };
153
173
  if (urlLocale !== locale2.value) setLocale(urlLocale);
154
- setAlternates({});
155
174
  if (tryHydrateFromState(slug)) {
156
- loading.value = false;
175
+ initialLoading.value = false;
157
176
  return;
158
177
  }
178
+ navigating.value = true;
159
179
  try {
160
180
  const resolved = await resolvePath(slug, urlLocale);
161
181
  blocks.value = ((_a = resolved.page.content) == null ? void 0 : _a.blocks) ?? [];
162
182
  contexts.value = resolved.contexts ?? {};
163
183
  setAlternates(resolved.alternates ?? {});
184
+ updateDocumentTitle(resolved);
164
185
  } catch (e) {
165
186
  const msg = e instanceof Error ? e.message : String(e);
166
187
  if (msg.includes("404")) {
@@ -170,7 +191,8 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
170
191
  error.value = msg;
171
192
  }
172
193
  } finally {
173
- loading.value = false;
194
+ initialLoading.value = false;
195
+ navigating.value = false;
174
196
  }
175
197
  }
176
198
  if (!ssrHydrated) {
@@ -180,7 +202,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
180
202
  return (_ctx, _cache) => {
181
203
  const _component_RouterLink = resolveComponent("RouterLink");
182
204
  return openBlock(), createElementBlock("div", null, [
183
- loading.value ? (openBlock(), createElementBlock("div", _hoisted_1$1, " Loading… ")) : notFound.value ? (openBlock(), createElementBlock("div", _hoisted_2, [
205
+ initialLoading.value && blocks.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_1$1, " Loading… ")) : notFound.value ? (openBlock(), createElementBlock("div", _hoisted_2, [
184
206
  _cache[0] || (_cache[0] = createElementVNode("h2", null, "404", -1)),
185
207
  createElementVNode("p", null, toDisplayString(unref(locale2) === "he" ? "הדף לא נמצא" : "Page not found"), 1),
186
208
  createVNode(_component_RouterLink, { to: "/" }, {
@@ -189,10 +211,10 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
189
211
  ]),
190
212
  _: 1
191
213
  })
192
- ])) : error.value ? (openBlock(), createElementBlock("div", _hoisted_3, toDisplayString(error.value), 1)) : (openBlock(), createBlock(unref(PageRenderer), {
193
- key: 3,
194
- blocks: blocks.value
195
- }, null, 8, ["blocks"]))
214
+ ])) : error.value ? (openBlock(), createElementBlock("div", _hoisted_3, toDisplayString(error.value), 1)) : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
215
+ navigating.value ? (openBlock(), createElementBlock("div", _hoisted_4)) : createCommentVNode("", true),
216
+ createVNode(unref(PageRenderer), { blocks: blocks.value }, null, 8, ["blocks"])
217
+ ], 64))
196
218
  ]);
197
219
  };
198
220
  }
@@ -51,7 +51,8 @@ async function fetchCmsSiteData(apiBase, websiteName) {
51
51
  }
52
52
  } catch {
53
53
  }
54
- return { websiteId: site.id, website, paths, redirects };
54
+ const collections = extractCollectionRefs(pages);
55
+ return { websiteId: site.id, website, paths, redirects, collections };
55
56
  }
56
57
  async function fetchCmsPrerenderPaths(apiBase, websiteName) {
57
58
  const { paths } = await fetchCmsSiteData(apiBase, websiteName);
@@ -93,6 +94,24 @@ async function expandRoutes(apiBase, pages) {
93
94
  }
94
95
  return [...new Set(routes)];
95
96
  }
97
+ function extractCollectionRefs(pages) {
98
+ const seen = /* @__PURE__ */ new Set();
99
+ const refs = [];
100
+ for (const page of pages) {
101
+ if (!page.data_bindings) continue;
102
+ for (const binding of Object.values(page.data_bindings)) {
103
+ const b = binding;
104
+ if (b.adapter === "datastore" && b.collection != null && b.collection !== "" && b.store != null && b.store !== "") {
105
+ const key = `${b.store}/${b.collection}`;
106
+ if (!seen.has(key)) {
107
+ seen.add(key);
108
+ refs.push({ store: b.store, collection: b.collection });
109
+ }
110
+ }
111
+ }
112
+ }
113
+ return refs;
114
+ }
96
115
  async function polyfillBloxSsgGlobals(options) {
97
116
  const { env } = await import("node:process");
98
117
  const pageUrl = (options == null ? void 0 : options.pageUrl) ?? env.BAGELINK_API_URL ?? "https://example.com/";
@@ -292,7 +311,9 @@ async function prerender({
292
311
  maxPages = 5e3,
293
312
  mode = "dir",
294
313
  website = null,
295
- redirects = []
314
+ websiteId = "",
315
+ redirects = [],
316
+ collections = {}
296
317
  } = {}) {
297
318
  const absRoot = path.resolve(root);
298
319
  const absClient = path.resolve(absRoot, clientOutDir);
@@ -334,7 +355,9 @@ async function prerender({
334
355
  const { html, head = "", htmlAttrs = "" } = await serverMod.render(urlPath, {
335
356
  manifest,
336
357
  template,
337
- website
358
+ website,
359
+ websiteId,
360
+ collections
338
361
  });
339
362
  const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
340
363
  const outfile = outFilePath(absClient, urlPath, mode);
@@ -28,7 +28,8 @@ async function fetchCmsSiteData(apiBase, websiteName) {
28
28
  }
29
29
  } catch {
30
30
  }
31
- return { websiteId: site.id, website, paths, redirects };
31
+ const collections = extractCollectionRefs(pages);
32
+ return { websiteId: site.id, website, paths, redirects, collections };
32
33
  }
33
34
  async function fetchCmsPrerenderPaths(apiBase, websiteName) {
34
35
  const { paths } = await fetchCmsSiteData(apiBase, websiteName);
@@ -70,6 +71,24 @@ async function expandRoutes(apiBase, pages) {
70
71
  }
71
72
  return [...new Set(routes)];
72
73
  }
74
+ function extractCollectionRefs(pages) {
75
+ const seen = /* @__PURE__ */ new Set();
76
+ const refs = [];
77
+ for (const page of pages) {
78
+ if (!page.data_bindings) continue;
79
+ for (const binding of Object.values(page.data_bindings)) {
80
+ const b = binding;
81
+ if (b.adapter === "datastore" && b.collection != null && b.collection !== "" && b.store != null && b.store !== "") {
82
+ const key = `${b.store}/${b.collection}`;
83
+ if (!seen.has(key)) {
84
+ seen.add(key);
85
+ refs.push({ store: b.store, collection: b.collection });
86
+ }
87
+ }
88
+ }
89
+ }
90
+ return refs;
91
+ }
73
92
  async function polyfillBloxSsgGlobals(options) {
74
93
  const { env } = await import("node:process");
75
94
  const pageUrl = (options == null ? void 0 : options.pageUrl) ?? env.BAGELINK_API_URL ?? "https://example.com/";
@@ -269,7 +288,9 @@ async function prerender({
269
288
  maxPages = 5e3,
270
289
  mode = "dir",
271
290
  website = null,
272
- redirects = []
291
+ websiteId = "",
292
+ redirects = [],
293
+ collections = {}
273
294
  } = {}) {
274
295
  const absRoot = path.resolve(root);
275
296
  const absClient = path.resolve(absRoot, clientOutDir);
@@ -311,7 +332,9 @@ async function prerender({
311
332
  const { html, head = "", htmlAttrs = "" } = await serverMod.render(urlPath, {
312
333
  manifest,
313
334
  template,
314
- website
335
+ website,
336
+ websiteId,
337
+ collections
315
338
  });
316
339
  const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
317
340
  const outfile = outFilePath(absClient, urlPath, mode);
package/dist/ssg/cli.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  "use strict";
3
3
  const process = require("node:process");
4
- const prerender = require("../prerender-DN7Ugbtc.cjs");
4
+ const prerender = require("../prerender-CjJwDXkC.cjs");
5
5
  async function resolveApiBase(mode) {
6
6
  if (process.env.BAGELINK_API_URL != null && process.env.BAGELINK_API_URL !== "") {
7
7
  return { apiBase: process.env.BAGELINK_API_URL };
@@ -73,14 +73,33 @@ Environment:
73
73
  console.log(`Fetching routes from ${apiBase} (mode: ${mode}, site: ${websiteName})…`);
74
74
  let paths = [];
75
75
  let website = null;
76
+ let websiteId = "";
76
77
  let redirects = [];
78
+ let collections = {};
77
79
  try {
78
80
  const siteData = await prerender.fetchCmsSiteData(apiBase, websiteName);
79
81
  paths = siteData.paths;
80
82
  website = siteData.website;
83
+ websiteId = siteData.websiteId;
81
84
  redirects = siteData.redirects;
82
85
  console.log(` Found ${paths.length} CMS routes`);
83
86
  if (redirects.length > 0) console.log(` Found ${redirects.length} redirects`);
87
+ if (siteData.collections.length > 0) {
88
+ console.log(` Pre-fetching ${siteData.collections.length} datastore collection(s)…`);
89
+ for (const ref of siteData.collections) {
90
+ try {
91
+ const url = `${apiBase}/datastore/${ref.store}/collections/${ref.collection}?limit=500`;
92
+ const res = await fetch(url);
93
+ const data = await res.json();
94
+ const items = Array.isArray(data) ? data : data.data ?? [];
95
+ const key = `${ref.store}/${ref.collection}`;
96
+ collections[key] = items;
97
+ console.log(` ${key}: ${items.length} items`);
98
+ } catch (e) {
99
+ console.warn(` Failed to fetch ${ref.store}/${ref.collection}: ${e.message}`);
100
+ }
101
+ }
102
+ }
84
103
  } catch (err) {
85
104
  console.error(" Failed to fetch CMS data:", err.message);
86
105
  paths = ["/"];
@@ -100,7 +119,9 @@ Environment:
100
119
  maxPages: 5e3,
101
120
  mode: "file",
102
121
  website,
103
- redirects
122
+ websiteId,
123
+ redirects,
124
+ collections
104
125
  });
105
126
  const elapsed = ((Date.now() - startTime) / 1e3).toFixed(2);
106
127
  console.log(`
package/dist/ssg/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import process from "node:process";
3
- import { p as polyfillBloxSsgGlobals, a as fetchCmsSiteData, c as prerender } from "../prerender-BXf1VF7r.js";
3
+ import { p as polyfillBloxSsgGlobals, a as fetchCmsSiteData, c as prerender } from "../prerender-oMLxrKGs.js";
4
4
  async function resolveApiBase(mode) {
5
5
  if (process.env.BAGELINK_API_URL != null && process.env.BAGELINK_API_URL !== "") {
6
6
  return { apiBase: process.env.BAGELINK_API_URL };
@@ -72,14 +72,33 @@ Environment:
72
72
  console.log(`Fetching routes from ${apiBase} (mode: ${mode}, site: ${websiteName})…`);
73
73
  let paths = [];
74
74
  let website = null;
75
+ let websiteId = "";
75
76
  let redirects = [];
77
+ let collections = {};
76
78
  try {
77
79
  const siteData = await fetchCmsSiteData(apiBase, websiteName);
78
80
  paths = siteData.paths;
79
81
  website = siteData.website;
82
+ websiteId = siteData.websiteId;
80
83
  redirects = siteData.redirects;
81
84
  console.log(` Found ${paths.length} CMS routes`);
82
85
  if (redirects.length > 0) console.log(` Found ${redirects.length} redirects`);
86
+ if (siteData.collections.length > 0) {
87
+ console.log(` Pre-fetching ${siteData.collections.length} datastore collection(s)…`);
88
+ for (const ref of siteData.collections) {
89
+ try {
90
+ const url = `${apiBase}/datastore/${ref.store}/collections/${ref.collection}?limit=500`;
91
+ const res = await fetch(url);
92
+ const data = await res.json();
93
+ const items = Array.isArray(data) ? data : data.data ?? [];
94
+ const key = `${ref.store}/${ref.collection}`;
95
+ collections[key] = items;
96
+ console.log(` ${key}: ${items.length} items`);
97
+ } catch (e) {
98
+ console.warn(` Failed to fetch ${ref.store}/${ref.collection}: ${e.message}`);
99
+ }
100
+ }
101
+ }
83
102
  } catch (err) {
84
103
  console.error(" Failed to fetch CMS data:", err.message);
85
104
  paths = ["/"];
@@ -99,7 +118,9 @@ Environment:
99
118
  maxPages: 5e3,
100
119
  mode: "file",
101
120
  website,
102
- redirects
121
+ websiteId,
122
+ redirects,
123
+ collections
103
124
  });
104
125
  const elapsed = ((Date.now() - startTime) / 1e3).toFixed(2);
105
126
  console.log(`
@@ -1,6 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const constants = require("../constants-fZvybj0k.cjs");
3
+ const constants = require("../constants-CFB_pMvT.cjs");
4
+ let _cache = null;
5
+ function readCache() {
6
+ if (_cache != null) return _cache;
7
+ if (typeof window === "undefined") return null;
8
+ const raw = window[constants.BLOX_COLLECTIONS_WINDOW_KEY];
9
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
10
+ _cache = raw;
11
+ return _cache;
12
+ }
13
+ return null;
14
+ }
15
+ function getCollectionCache() {
16
+ return readCache();
17
+ }
18
+ function getEmbeddedWebsiteId() {
19
+ if (typeof window === "undefined") return "";
20
+ const id = window[constants.BLOX_WEBSITE_ID_WINDOW_KEY];
21
+ return typeof id === "string" ? id : "";
22
+ }
23
+ function installCollectionCache() {
24
+ const cache = readCache();
25
+ if (!cache || Object.keys(cache).length === 0) return null;
26
+ return (config) => {
27
+ if (config.method && config.method.toLowerCase() !== "get") return config;
28
+ const url = config.url ?? "";
29
+ const match = url.match(/\/datastore\/([^/?]+)\/collections\/([^/?]+)/);
30
+ if (!match) return config;
31
+ const key = `${match[1]}/${match[2]}`;
32
+ if (cache[key] == null) return config;
33
+ const items = cache[key];
34
+ config.adapter = () => Promise.resolve({
35
+ data: items,
36
+ status: 200,
37
+ statusText: "OK",
38
+ headers: { "content-type": "application/json", "x-blox-cache": "hit" },
39
+ config
40
+ });
41
+ return config;
42
+ };
43
+ }
4
44
  function installBloxStateCache(globalKey = constants.BLOX_STATE_WINDOW_KEY) {
5
45
  if (typeof window === "undefined") return;
6
46
  const state = window[globalKey];
@@ -26,5 +66,10 @@ function installBloxStateCache(globalKey = constants.BLOX_STATE_WINDOW_KEY) {
26
66
  return originalFetch(input, init);
27
67
  };
28
68
  }
69
+ exports.BLOX_COLLECTIONS_WINDOW_KEY = constants.BLOX_COLLECTIONS_WINDOW_KEY;
29
70
  exports.BLOX_STATE_WINDOW_KEY = constants.BLOX_STATE_WINDOW_KEY;
71
+ exports.BLOX_WEBSITE_ID_WINDOW_KEY = constants.BLOX_WEBSITE_ID_WINDOW_KEY;
72
+ exports.getCollectionCache = getCollectionCache;
73
+ exports.getEmbeddedWebsiteId = getEmbeddedWebsiteId;
30
74
  exports.installBloxStateCache = installBloxStateCache;
75
+ exports.installCollectionCache = installCollectionCache;
@@ -4,6 +4,7 @@
4
4
  * Use this import in client entry files (main.ts / entry-client.ts).
5
5
  * It does NOT pull in Node.js modules (fs, path, url, happy-dom).
6
6
  */
7
- export { BLOX_STATE_WINDOW_KEY } from './constants';
7
+ export { BLOX_COLLECTIONS_WINDOW_KEY, BLOX_STATE_WINDOW_KEY, BLOX_WEBSITE_ID_WINDOW_KEY } from './constants';
8
+ export { getCollectionCache, getEmbeddedWebsiteId, installCollectionCache } from './collection-cache';
8
9
  export { installBloxStateCache } from './state-cache';
9
10
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/ssg/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/ssg/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AAC5G,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AACrG,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
@@ -1,4 +1,44 @@
1
- import { B as BLOX_STATE_WINDOW_KEY } from "../constants-BIbQhd3z.js";
1
+ import { b as BLOX_WEBSITE_ID_WINDOW_KEY, a as BLOX_COLLECTIONS_WINDOW_KEY, B as BLOX_STATE_WINDOW_KEY } from "../constants-BjitNk-W.js";
2
+ let _cache = null;
3
+ function readCache() {
4
+ if (_cache != null) return _cache;
5
+ if (typeof window === "undefined") return null;
6
+ const raw = window[BLOX_COLLECTIONS_WINDOW_KEY];
7
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
8
+ _cache = raw;
9
+ return _cache;
10
+ }
11
+ return null;
12
+ }
13
+ function getCollectionCache() {
14
+ return readCache();
15
+ }
16
+ function getEmbeddedWebsiteId() {
17
+ if (typeof window === "undefined") return "";
18
+ const id = window[BLOX_WEBSITE_ID_WINDOW_KEY];
19
+ return typeof id === "string" ? id : "";
20
+ }
21
+ function installCollectionCache() {
22
+ const cache = readCache();
23
+ if (!cache || Object.keys(cache).length === 0) return null;
24
+ return (config) => {
25
+ if (config.method && config.method.toLowerCase() !== "get") return config;
26
+ const url = config.url ?? "";
27
+ const match = url.match(/\/datastore\/([^/?]+)\/collections\/([^/?]+)/);
28
+ if (!match) return config;
29
+ const key = `${match[1]}/${match[2]}`;
30
+ if (cache[key] == null) return config;
31
+ const items = cache[key];
32
+ config.adapter = () => Promise.resolve({
33
+ data: items,
34
+ status: 200,
35
+ statusText: "OK",
36
+ headers: { "content-type": "application/json", "x-blox-cache": "hit" },
37
+ config
38
+ });
39
+ return config;
40
+ };
41
+ }
2
42
  function installBloxStateCache(globalKey = BLOX_STATE_WINDOW_KEY) {
3
43
  if (typeof window === "undefined") return;
4
44
  const state = window[globalKey];
@@ -25,6 +65,11 @@ function installBloxStateCache(globalKey = BLOX_STATE_WINDOW_KEY) {
25
65
  };
26
66
  }
27
67
  export {
68
+ BLOX_COLLECTIONS_WINDOW_KEY,
28
69
  BLOX_STATE_WINDOW_KEY,
29
- installBloxStateCache
70
+ BLOX_WEBSITE_ID_WINDOW_KEY,
71
+ getCollectionCache,
72
+ getEmbeddedWebsiteId,
73
+ installBloxStateCache,
74
+ installCollectionCache
30
75
  };
@@ -1,10 +1,17 @@
1
1
  import { WebsiteRead } from '../api/types';
2
2
  import { RedirectEntry } from './seo';
3
+ /** A datastore collection reference discovered from page data_bindings. */
4
+ export interface CollectionRef {
5
+ store: string;
6
+ collection: string;
7
+ }
3
8
  export interface CmsSiteData {
4
9
  websiteId: string;
5
10
  website: WebsiteRead;
6
11
  paths: string[];
7
12
  redirects: RedirectEntry[];
13
+ /** Datastore collections referenced by any page's data_bindings. */
14
+ collections: CollectionRef[];
8
15
  }
9
16
  /**
10
17
  * Fetch everything the SSG needs from the CMS in one go:
@@ -1 +1 @@
1
- {"version":3,"file":"cms-routes.d.ts","sourceRoot":"","sources":["../../src/ssg/cms-routes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAY1C,MAAM,WAAW,WAAW;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,WAAW,CAAA;IACpB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,SAAS,EAAE,aAAa,EAAE,CAAA;CAC1B;AAMD;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,CAAC,CAqCtB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC3C,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAGnB"}
1
+ {"version":3,"file":"cms-routes.d.ts","sourceRoot":"","sources":["../../src/ssg/cms-routes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAY1C,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,WAAW,CAAA;IACpB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,oEAAoE;IACpE,WAAW,EAAE,aAAa,EAAE,CAAA;CAC5B;AAMD;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,CAAC,CAwCtB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC3C,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAGnB"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Pre-fetched collection cache for zero-fetch client hydration.
3
+ *
4
+ * During SSG, `blox-ssg` embeds pre-fetched datastore collections as
5
+ * `window.__BLOX_COLLECTIONS__ = { "store/collection": [...items] }`.
6
+ *
7
+ * This module provides:
8
+ * 1. `installCollectionCache()` — returns an axios request interceptor
9
+ * that short-circuits matching datastore GET requests with cached data.
10
+ * 2. `getCollectionCache()` — direct access to the cache for manual use.
11
+ *
12
+ * Usage in client entry (main.ts):
13
+ *
14
+ * ```ts
15
+ * import { installCollectionCache } from '@bagelink/blox/ssg/client'
16
+ * import { axios } from '@shared/api'
17
+ *
18
+ * const interceptor = installCollectionCache()
19
+ * if (interceptor) axios.interceptors.request.use(interceptor)
20
+ * ```
21
+ */
22
+ type CollectionCacheMap = Record<string, unknown[]>;
23
+ /**
24
+ * Get direct access to the pre-fetched collections cache.
25
+ * Returns null if not available (e.g. page wasn't pre-rendered).
26
+ */
27
+ export declare function getCollectionCache(): CollectionCacheMap | null;
28
+ /**
29
+ * Get the pre-embedded website ID (avoids GET /cms/websites on client).
30
+ * Returns empty string if not available.
31
+ */
32
+ export declare function getEmbeddedWebsiteId(): string;
33
+ /**
34
+ * Install collection cache and return an axios request interceptor.
35
+ *
36
+ * The interceptor matches `GET /datastore/{store}/collections/{name}` requests
37
+ * and returns the cached data as a fulfilled promise (zero network).
38
+ *
39
+ * Returns `null` if no cache is available (page wasn't pre-rendered).
40
+ */
41
+ export declare function installCollectionCache(): ((config: AxiosLikeConfig) => AxiosLikeConfig) | null;
42
+ /**
43
+ * Minimal axios config shape — avoids importing axios as a dependency.
44
+ * Only the fields we need for interception.
45
+ */
46
+ interface AxiosLikeConfig {
47
+ url?: string;
48
+ method?: string;
49
+ adapter?: (config: AxiosLikeConfig) => Promise<unknown>;
50
+ [key: string]: unknown;
51
+ }
52
+ export {};
53
+ //# sourceMappingURL=collection-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-cache.d.ts","sourceRoot":"","sources":["../../src/ssg/collection-cache.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;AAenD;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,kBAAkB,GAAG,IAAI,CAE9D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAI7C;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,CAAC,CAAC,MAAM,EAAE,eAAe,KAAK,eAAe,CAAC,GAAG,IAAI,CA+B9F;AAED;;;GAGG;AACH,UAAU,eAAe;IACxB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACtB"}
@@ -1,3 +1,7 @@
1
1
  /** Global key for embedded prerender payload (must match client interceptor). */
2
2
  export declare const BLOX_STATE_WINDOW_KEY: "__BLOX_STATE__";
3
+ /** Global key for pre-fetched datastore collections (keyed by "store/collection"). */
4
+ export declare const BLOX_COLLECTIONS_WINDOW_KEY: "__BLOX_COLLECTIONS__";
5
+ /** Global key for the pre-resolved website ID (avoids GET /cms/websites on client). */
6
+ export declare const BLOX_WEBSITE_ID_WINDOW_KEY: "__BLOX_WEBSITE_ID__";
3
7
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/ssg/constants.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,eAAO,MAAM,qBAAqB,EAAG,gBAAyB,CAAA"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/ssg/constants.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,eAAO,MAAM,qBAAqB,EAAG,gBAAyB,CAAA;AAE9D,sFAAsF;AACtF,eAAO,MAAM,2BAA2B,EAAG,sBAA+B,CAAA;AAE1E,uFAAuF;AACvF,eAAO,MAAM,0BAA0B,EAAG,qBAA8B,CAAA"}
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const prerender = require("../prerender-DN7Ugbtc.cjs");
4
- const constants = require("../constants-fZvybj0k.cjs");
3
+ const prerender = require("../prerender-CjJwDXkC.cjs");
5
4
  const ssg_client = require("./client.cjs");
5
+ const constants = require("../constants-CFB_pMvT.cjs");
6
6
  async function renderBloxSsgPage(options) {
7
7
  const {
8
8
  url,
@@ -10,7 +10,9 @@ async function renderBloxSsgPage(options) {
10
10
  renderToString,
11
11
  createAppForUrl,
12
12
  stateWindowKey = constants.BLOX_STATE_WINDOW_KEY,
13
- website = null
13
+ website = null,
14
+ websiteId = "",
15
+ collections
14
16
  } = options;
15
17
  const g = globalThis;
16
18
  const prevState = g[stateWindowKey];
@@ -23,11 +25,13 @@ async function renderBloxSsgPage(options) {
23
25
  await router.isReady();
24
26
  const html = await renderToString(app);
25
27
  const stateScript = resolvedData != null ? `<script>window[${JSON.stringify(stateWindowKey)}]=${JSON.stringify({ [url]: resolvedData })};${"<"}/script>` : "";
28
+ const collectionsScript = collections && Object.keys(collections).length > 0 ? `<script>window[${JSON.stringify(constants.BLOX_COLLECTIONS_WINDOW_KEY)}]=${JSON.stringify(collections)};${"<"}/script>` : "";
29
+ const websiteIdScript = websiteId ? `<script>window[${JSON.stringify(constants.BLOX_WEBSITE_ID_WINDOW_KEY)}]=${JSON.stringify(websiteId)};${"<"}/script>` : "";
26
30
  const head = prerender.buildPageHead({
27
31
  url,
28
32
  resolvedData,
29
33
  website,
30
- stateScript
34
+ stateScript: [stateScript, collectionsScript, websiteIdScript].filter(Boolean).join("\n")
31
35
  });
32
36
  const lang = (website == null ? void 0 : website.default_locale) || "en";
33
37
  const htmlAttrs = `lang="${lang}"`;
@@ -49,6 +53,11 @@ exports.generateRobotsTxt = prerender.generateRobotsTxt;
49
53
  exports.generateSitemapXml = prerender.generateSitemapXml;
50
54
  exports.polyfillBloxSsgGlobals = prerender.polyfillBloxSsgGlobals;
51
55
  exports.prerender = prerender.prerender;
52
- exports.BLOX_STATE_WINDOW_KEY = constants.BLOX_STATE_WINDOW_KEY;
56
+ exports.getCollectionCache = ssg_client.getCollectionCache;
57
+ exports.getEmbeddedWebsiteId = ssg_client.getEmbeddedWebsiteId;
53
58
  exports.installBloxStateCache = ssg_client.installBloxStateCache;
59
+ exports.installCollectionCache = ssg_client.installCollectionCache;
60
+ exports.BLOX_COLLECTIONS_WINDOW_KEY = constants.BLOX_COLLECTIONS_WINDOW_KEY;
61
+ exports.BLOX_STATE_WINDOW_KEY = constants.BLOX_STATE_WINDOW_KEY;
62
+ exports.BLOX_WEBSITE_ID_WINDOW_KEY = constants.BLOX_WEBSITE_ID_WINDOW_KEY;
54
63
  exports.renderBloxSsgPage = renderBloxSsgPage;
@@ -1,5 +1,6 @@
1
1
  export { fetchCmsPrerenderPaths, fetchCmsSiteData } from './cms-routes';
2
- export type { CmsSiteData } from './cms-routes';
2
+ export type { CmsSiteData, CollectionRef } from './cms-routes';
3
+ export { getCollectionCache, getEmbeddedWebsiteId, installCollectionCache } from './collection-cache';
3
4
  /**
4
5
  * @bagelink/blox/ssg — Static Site Generation helpers for Blox CMS sites.
5
6
  *
@@ -11,7 +12,7 @@ export type { CmsSiteData } from './cms-routes';
11
12
  * - Full prerender orchestrator with crawl support
12
13
  * - SEO asset generation (sitemap.xml, robots.txt, _redirects)
13
14
  */
14
- export { BLOX_STATE_WINDOW_KEY } from './constants';
15
+ export { BLOX_COLLECTIONS_WINDOW_KEY, BLOX_STATE_WINDOW_KEY, BLOX_WEBSITE_ID_WINDOW_KEY } from './constants';
15
16
  export { polyfillBloxSsgGlobals } from './polyfill-node';
16
17
  export { prerender } from './prerender';
17
18
  export type { PrerenderOptions, PrerenderResult, RenderContext, RenderResult } from './prerender';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ssg/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACvE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C;;;;;;;;;;GAUG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACjG,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAA;AACrH,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ssg/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACvE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC9D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AACrG;;;;;;;;;;GAUG;AACH,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AAC5G,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACjG,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAA;AACrH,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
@@ -1,7 +1,7 @@
1
- import { b as buildPageHead } from "../prerender-BXf1VF7r.js";
2
- import { d, f, a, g, e, h, p, c } from "../prerender-BXf1VF7r.js";
3
- import { B as BLOX_STATE_WINDOW_KEY } from "../constants-BIbQhd3z.js";
4
- import { installBloxStateCache } from "./client.mjs";
1
+ import { b as buildPageHead } from "../prerender-oMLxrKGs.js";
2
+ import { d, f, a, g, e, h, p, c } from "../prerender-oMLxrKGs.js";
3
+ import { getCollectionCache, getEmbeddedWebsiteId, installBloxStateCache, installCollectionCache } from "./client.mjs";
4
+ import { B as BLOX_STATE_WINDOW_KEY, a as BLOX_COLLECTIONS_WINDOW_KEY, b as BLOX_WEBSITE_ID_WINDOW_KEY } from "../constants-BjitNk-W.js";
5
5
  async function renderBloxSsgPage(options) {
6
6
  const {
7
7
  url,
@@ -9,7 +9,9 @@ async function renderBloxSsgPage(options) {
9
9
  renderToString,
10
10
  createAppForUrl,
11
11
  stateWindowKey = BLOX_STATE_WINDOW_KEY,
12
- website = null
12
+ website = null,
13
+ websiteId = "",
14
+ collections
13
15
  } = options;
14
16
  const g2 = globalThis;
15
17
  const prevState = g2[stateWindowKey];
@@ -22,11 +24,13 @@ async function renderBloxSsgPage(options) {
22
24
  await router.isReady();
23
25
  const html = await renderToString(app);
24
26
  const stateScript = resolvedData != null ? `<script>window[${JSON.stringify(stateWindowKey)}]=${JSON.stringify({ [url]: resolvedData })};${"<"}/script>` : "";
27
+ const collectionsScript = collections && Object.keys(collections).length > 0 ? `<script>window[${JSON.stringify(BLOX_COLLECTIONS_WINDOW_KEY)}]=${JSON.stringify(collections)};${"<"}/script>` : "";
28
+ const websiteIdScript = websiteId ? `<script>window[${JSON.stringify(BLOX_WEBSITE_ID_WINDOW_KEY)}]=${JSON.stringify(websiteId)};${"<"}/script>` : "";
25
29
  const head = buildPageHead({
26
30
  url,
27
31
  resolvedData,
28
32
  website,
29
- stateScript
33
+ stateScript: [stateScript, collectionsScript, websiteIdScript].filter(Boolean).join("\n")
30
34
  });
31
35
  const lang = (website == null ? void 0 : website.default_locale) || "en";
32
36
  const htmlAttrs = `lang="${lang}"`;
@@ -40,7 +44,9 @@ async function renderBloxSsgPage(options) {
40
44
  }
41
45
  }
42
46
  export {
47
+ BLOX_COLLECTIONS_WINDOW_KEY,
43
48
  BLOX_STATE_WINDOW_KEY,
49
+ BLOX_WEBSITE_ID_WINDOW_KEY,
44
50
  buildPageHead,
45
51
  d as buildSiteHead,
46
52
  f as fetchCmsPrerenderPaths,
@@ -48,7 +54,10 @@ export {
48
54
  g as generateNetlifyRedirects,
49
55
  e as generateRobotsTxt,
50
56
  h as generateSitemapXml,
57
+ getCollectionCache,
58
+ getEmbeddedWebsiteId,
51
59
  installBloxStateCache,
60
+ installCollectionCache,
52
61
  p as polyfillBloxSsgGlobals,
53
62
  c as prerender,
54
63
  renderBloxSsgPage
@@ -13,14 +13,22 @@ export interface PrerenderOptions {
13
13
  mode?: 'dir' | 'file';
14
14
  /** Website settings for SEO injection. */
15
15
  website?: WebsiteRead | null;
16
+ /** Resolved website ID (embedded for client-side cache). */
17
+ websiteId?: string;
16
18
  /** Redirects to write as _redirects (Netlify). */
17
19
  redirects?: RedirectEntry[];
20
+ /** Pre-fetched datastore collections keyed by "store/collection". */
21
+ collections?: Record<string, unknown[]>;
18
22
  }
19
23
  export interface RenderContext {
20
24
  manifest: Record<string, string[]> | null;
21
25
  template: string;
22
26
  /** Website settings passed through to the render function. */
23
27
  website?: WebsiteRead | null;
28
+ /** Resolved website ID for client embedding. */
29
+ websiteId?: string;
30
+ /** Pre-fetched datastore collections keyed by "store/collection". */
31
+ collections?: Record<string, unknown[]>;
24
32
  }
25
33
  export interface RenderResult {
26
34
  html: string;
@@ -49,6 +57,6 @@ export interface PrerenderResult {
49
57
  * - Generates sitemap.xml, robots.txt, and _redirects post-render.
50
58
  * - Strips hardcoded SEO tags from template and injects per-page equivalents.
51
59
  */
52
- export declare function prerender({ root, clientOutDir, serverEntry, paths, crawl, excludePaths, failFast, maxPages, mode, website, redirects, }?: PrerenderOptions): Promise<PrerenderResult>;
60
+ export declare function prerender({ root, clientOutDir, serverEntry, paths, crawl, excludePaths, failFast, maxPages, mode, website, websiteId, redirects, collections, }?: PrerenderOptions): Promise<PrerenderResult>;
53
61
  export {};
54
62
  //# sourceMappingURL=prerender.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/ssg/prerender.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAS1C,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAA;AAEhE,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,YAAY,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAA;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACrB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,kDAAkD;IAClD,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAA;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;CAC5B;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAMD,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAC7D,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,SAAS,CAAC,EAC/B,IAAoB,EACpB,YAA4B,EAC5B,WAA0C,EAC1C,KAAU,EACV,KAAY,EACZ,YAAiB,EACjB,QAAgB,EAChB,QAAe,EACf,IAAY,EACZ,OAAc,EACd,SAAc,GACd,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0HlD"}
1
+ {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/ssg/prerender.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAS1C,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAA;AAEhE,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,YAAY,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAA;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACrB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kDAAkD;IAClD,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;IAC3B,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;CACvC;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAA;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;CACvC;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAMD,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAC7D,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,SAAS,CAAC,EAC/B,IAAoB,EACpB,YAA4B,EAC5B,WAA0C,EAC1C,KAAU,EACV,KAAY,EACZ,YAAiB,EACjB,QAAgB,EAChB,QAAe,EACf,IAAY,EACZ,OAAc,EACd,SAAc,EACd,SAAc,EACd,WAAgB,GAChB,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA4HlD"}
@@ -26,6 +26,10 @@ export declare function renderBloxSsgPage<TApp>(options: {
26
26
  stateWindowKey?: string;
27
27
  /** Website settings for SEO tags. If omitted, only state script is emitted. */
28
28
  website?: WebsiteRead | null;
29
+ /** Resolved website ID — embedded so client skips GET /cms/websites. */
30
+ websiteId?: string;
31
+ /** Pre-fetched datastore collections — embedded for zero-fetch hydration. */
32
+ collections?: Record<string, unknown[]>;
29
33
  }): Promise<{
30
34
  html: string;
31
35
  head?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"render-resolved-page.d.ts","sourceRoot":"","sources":["../../src/ssg/render-resolved-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAK/C,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE;IACtD,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,OAAO,GAAG,IAAI,CAAA;IAC5B,cAAc,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9C,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK;QAAE,GAAG,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAA;KAAE,CAAA;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;CAC5B,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAkD/D"}
1
+ {"version":3,"file":"render-resolved-page.d.ts","sourceRoot":"","sources":["../../src/ssg/render-resolved-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAK/C,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE;IACtD,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,OAAO,GAAG,IAAI,CAAA;IAC5B,cAAc,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9C,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK;QAAE,GAAG,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAA;KAAE,CAAA;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;CACvC,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA4D/D"}
package/dist/style.css CHANGED
@@ -1,4 +1,21 @@
1
1
 
2
+ .blox-nav-progress {
3
+ position: fixed;
4
+ top: 0;
5
+ left: 0;
6
+ width: 100%;
7
+ height: 3px;
8
+ z-index: 9999;
9
+ background: linear-gradient(90deg, transparent, var(--blox-accent, #3b82f6), transparent);
10
+ animation: blox-nav-slide 1s ease-in-out infinite;
11
+ }
12
+ @keyframes blox-nav-slide {
13
+ 0% { transform: translateX(-100%);
14
+ }
15
+ 100% { transform: translateX(100%);
16
+ }
17
+ }
18
+
2
19
  * {
3
20
  box-sizing: border-box;
4
21
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/blox",
3
3
  "type": "module",
4
- "version": "1.13.2",
4
+ "version": "1.13.4",
5
5
  "description": "Blox page builder library for drag-and-drop page building and static data management",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -1,4 +0,0 @@
1
- const BLOX_STATE_WINDOW_KEY = "__BLOX_STATE__";
2
- export {
3
- BLOX_STATE_WINDOW_KEY as B
4
- };
@@ -1,3 +0,0 @@
1
- "use strict";
2
- const BLOX_STATE_WINDOW_KEY = "__BLOX_STATE__";
3
- exports.BLOX_STATE_WINDOW_KEY = BLOX_STATE_WINDOW_KEY;