@weaverse/hydrogen 5.16.2 → 5.16.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.
package/dist/index.js CHANGED
@@ -56,77 +56,70 @@ function interpolate(template, variables) {
56
56
  return value === void 0 ? match : String(value);
57
57
  });
58
58
  }
59
- var ThemeTextContext = react$1.createContext(null);
60
- ThemeTextContext.displayName = "WeaverseThemeTextContext";
61
- function unflattenKeys(flat) {
62
- const result = {};
63
- for (const [dotPath, value] of Object.entries(flat)) {
64
- const keys = dotPath.split(".");
65
- let current = result;
66
- for (let i = 0; i < keys.length - 1; i++) {
67
- const k = keys[i];
68
- if (!(k in current) || typeof current[k] !== "object") {
69
- current[k] = {};
59
+ var TranslationContext = react$1.createContext(null);
60
+ TranslationContext.displayName = "WeaverseTranslationContext";
61
+ function createTranslate(sources) {
62
+ const { staticContent, merchantOverrides, designOverrides, externalT } = sources;
63
+ return (key, variables) => {
64
+ if (externalT) {
65
+ const external = externalT(key, variables);
66
+ if (external && external !== key) {
67
+ return external;
70
68
  }
71
- current = current[k];
72
69
  }
73
- current[keys[keys.length - 1]] = value;
74
- }
75
- return result;
70
+ const design = designOverrides && Object.hasOwn(designOverrides, key) ? designOverrides[key] : void 0;
71
+ const override = merchantOverrides ? getNestedKey(merchantOverrides, key) : void 0;
72
+ const staticValue = getNestedKey(staticContent, key);
73
+ const raw = design ?? override ?? staticValue ?? key;
74
+ return interpolate(raw, variables);
75
+ };
76
76
  }
77
77
  var EMPTY = {};
78
78
  var NOOP_SUBSCRIBE = () => () => {
79
79
  };
80
80
  var NOOP_SNAPSHOT = () => EMPTY;
81
- function ThemeTextProvider({
81
+ function TranslationProvider({
82
82
  staticContent,
83
83
  merchantOverrides,
84
84
  t: externalT,
85
+ translationStore,
85
86
  themeTextStore,
86
87
  children
87
88
  }) {
89
+ const store = translationStore ?? themeTextStore ?? null;
88
90
  const designOverrides = react.useSafeExternalStore(
89
- themeTextStore?.subscribe ?? NOOP_SUBSCRIBE,
90
- themeTextStore?.getSnapshot ?? NOOP_SNAPSHOT,
91
- themeTextStore?.getServerSnapshot ?? NOOP_SNAPSHOT
91
+ store?.subscribe ?? NOOP_SUBSCRIBE,
92
+ store?.getSnapshot ?? NOOP_SNAPSHOT,
93
+ store?.getServerSnapshot ?? NOOP_SNAPSHOT
92
94
  );
93
- const mergedOverrides = designOverrides !== EMPTY && Object.keys(designOverrides).length > 0 ? { ...merchantOverrides, ...unflattenKeys(designOverrides) } : merchantOverrides;
94
- const value = react$1.useMemo(() => {
95
- const t = (key, variables) => {
96
- if (externalT) {
97
- const external = externalT(key, variables);
98
- if (external && external !== key) {
99
- return external;
100
- }
101
- }
102
- const override = mergedOverrides ? getNestedKey(mergedOverrides, key) : void 0;
103
- const staticValue = getNestedKey(staticContent, key);
104
- const raw = override ?? staticValue ?? key;
105
- return interpolate(raw, variables);
106
- };
107
- return {
108
- t,
109
- themeTextStore: themeTextStore ?? null,
95
+ const value = react$1.useMemo(
96
+ () => ({
97
+ t: createTranslate({
98
+ staticContent,
99
+ merchantOverrides,
100
+ designOverrides,
101
+ externalT
102
+ }),
103
+ translationStore: store,
104
+ // Deprecated alias — same instance, kept for backward compatibility.
105
+ themeTextStore: store,
110
106
  merchantOverrides: merchantOverrides ?? null
111
- };
112
- }, [
113
- staticContent,
114
- merchantOverrides,
115
- mergedOverrides,
116
- externalT,
117
- themeTextStore
118
- ]);
119
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeTextContext.Provider, { value, children });
107
+ }),
108
+ [staticContent, merchantOverrides, designOverrides, externalT, store]
109
+ );
110
+ return /* @__PURE__ */ jsxRuntime.jsx(TranslationContext.Provider, { value, children });
120
111
  }
121
- function useThemeText() {
122
- const ctx = react$1.useContext(ThemeTextContext);
112
+ function useTranslation() {
113
+ const ctx = react$1.useContext(TranslationContext);
123
114
  if (!ctx) {
124
115
  throw new Error(
125
- "useThemeText must be used within <ThemeTextProvider>. Wrap your app with withWeaverse()."
116
+ "useTranslation must be used within <TranslationProvider>. Wrap your app with withWeaverse()."
126
117
  );
127
118
  }
128
119
  return ctx;
129
120
  }
121
+ var ThemeTextProvider = TranslationProvider;
122
+ var useThemeText = useTranslation;
130
123
  function createFlatDataContext(matches) {
131
124
  const context = {};
132
125
  for (const match of matches) {
@@ -251,6 +244,67 @@ function hasWeaverseStudio(window2) {
251
244
  return "weaverseStudio" in window2 && typeof window2.weaverseStudio === "object";
252
245
  }
253
246
 
247
+ // src/utils/studio-script-src.ts
248
+ var DEFAULT_WEAVERSE_HOST = "https://studio.weaverse.io";
249
+ var TRUSTED_STUDIO_DOMAINS = ["weaverse.io", "weaverse.dev"];
250
+ function isLoopbackHostname(hostname) {
251
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".localhost");
252
+ }
253
+ function isTrustedStudioHost(host, options) {
254
+ let parsed;
255
+ try {
256
+ parsed = new URL(host);
257
+ } catch {
258
+ return false;
259
+ }
260
+ let { hostname, protocol } = parsed;
261
+ if (options?.allowLoopback && isLoopbackHostname(hostname)) {
262
+ return protocol === "http:" || protocol === "https:";
263
+ }
264
+ if (protocol !== "https:") {
265
+ return false;
266
+ }
267
+ return TRUSTED_STUDIO_DOMAINS.some(
268
+ (domain) => hostname === domain || hostname.endsWith(`.${domain}`)
269
+ );
270
+ }
271
+ function getStudioScriptSrc(mode) {
272
+ let {
273
+ isDesignMode,
274
+ isPreviewMode,
275
+ isRevisionPreview,
276
+ weaverseHost,
277
+ weaverseVersion
278
+ } = mode;
279
+ let base = `${weaverseHost}/static/studio/hydrogen`;
280
+ let version = `?v=${weaverseVersion}`;
281
+ if (isRevisionPreview || isPreviewMode) {
282
+ return `${base}/preview.js${version}`;
283
+ }
284
+ if (isDesignMode) {
285
+ return `${base}/index.js${version}`;
286
+ }
287
+ return null;
288
+ }
289
+ function resolveStudioScriptSrc(search, storefrontHostname = "") {
290
+ let params = new URLSearchParams(search);
291
+ let last = (key) => params.getAll(key).at(-1);
292
+ let host = last("weaverseHost");
293
+ if (host) {
294
+ let allowLoopback = isLoopbackHostname(storefrontHostname);
295
+ if (!isTrustedStudioHost(host, { allowLoopback })) {
296
+ return null;
297
+ }
298
+ }
299
+ return getStudioScriptSrc({
300
+ isDesignMode: last("isDesignMode") === "true",
301
+ isPreviewMode: last("isPreviewMode") === "true",
302
+ isRevisionPreview: params.has("__revisionId"),
303
+ weaverseHost: host || DEFAULT_WEAVERSE_HOST,
304
+ weaverseVersion: last("weaverseVersion") || ""
305
+ });
306
+ }
307
+
254
308
  // src/utils/index.ts
255
309
  var warnedTypes = /* @__PURE__ */ new Set();
256
310
  function logOnce(type, message) {
@@ -314,8 +368,9 @@ function getWeaverseConfigs(request, env) {
314
368
  let url = new URL(request.url);
315
369
  let isRevisionPreview = url.searchParams.has("__revisionId");
316
370
  let configuredWeaverseHost = WEAVERSE_HOST || envFromProcess.WEAVERSE_HOST;
317
- let resolvedWeaverseHost = weaverseHost || configuredWeaverseHost || "https://studio.weaverse.io";
318
- let resolvedWeaverseApiBase = weaverseHost ? weaverseHost : WEAVERSE_PUBLIC_API_BASE || envFromProcess.WEAVERSE_PUBLIC_API_BASE || (configuredWeaverseHost && configuredWeaverseHost !== "https://studio.weaverse.io" ? configuredWeaverseHost : "https://api.weaverse.io");
371
+ let trustedUrlHost = typeof weaverseHost === "string" && isTrustedStudioHost(weaverseHost) ? weaverseHost : "";
372
+ let resolvedWeaverseHost = trustedUrlHost || configuredWeaverseHost || "https://studio.weaverse.io";
373
+ let resolvedWeaverseApiBase = trustedUrlHost ? trustedUrlHost : WEAVERSE_PUBLIC_API_BASE || envFromProcess.WEAVERSE_PUBLIC_API_BASE || (configuredWeaverseHost && configuredWeaverseHost !== "https://studio.weaverse.io" ? configuredWeaverseHost : "https://api.weaverse.io");
319
374
  return {
320
375
  projectId: weaverseProjectId || WEAVERSE_PROJECT_ID || envFromProcess.WEAVERSE_PROJECT_ID || "",
321
376
  weaverseHost: resolvedWeaverseHost,
@@ -458,8 +513,8 @@ function getPreviewData(type, components, weaverseHost) {
458
513
  };
459
514
  }
460
515
 
461
- // src/utils/theme-text-store.ts
462
- var ThemeTextStore = class {
516
+ // src/utils/translation-store.ts
517
+ var TranslationStore = class {
463
518
  overrides = {};
464
519
  listeners = /* @__PURE__ */ new Set();
465
520
  setOverrides = (overrides) => {
@@ -483,11 +538,12 @@ var ThemeTextStore = class {
483
538
  try {
484
539
  listener();
485
540
  } catch (error) {
486
- console.error("ThemeTextStore: Listener error", error);
541
+ console.error("TranslationStore: Listener error", error);
487
542
  }
488
543
  }
489
544
  };
490
545
  };
546
+ var ThemeTextStore = TranslationStore;
491
547
 
492
548
  // src/utils/pick-weaverse-data.ts
493
549
  function pickWeaverseData(dataProp, ownLoaderData, matches) {
@@ -577,11 +633,14 @@ function syncReusedInstance(weaverse, params) {
577
633
  var currentNavigationKey = null;
578
634
  var firedPageIds = /* @__PURE__ */ new Set();
579
635
  var mountedInstances = 0;
580
- function shouldFirePixel(navigationKey, pageId) {
636
+ function observeNavigation(navigationKey) {
581
637
  if (navigationKey !== currentNavigationKey) {
582
638
  currentNavigationKey = navigationKey;
583
639
  firedPageIds.clear();
584
640
  }
641
+ }
642
+ function shouldFirePixel(navigationKey, pageId) {
643
+ observeNavigation(navigationKey);
585
644
  if (firedPageIds.has(pageId)) {
586
645
  return false;
587
646
  }
@@ -720,13 +779,23 @@ function waitForStudio() {
720
779
  check();
721
780
  });
722
781
  }
782
+ function useStudioConnect() {
783
+ let { search } = reactRouter.useLocation();
784
+ react$1.useEffect(() => {
785
+ let hostname = typeof window === "undefined" ? "" : window.location.hostname;
786
+ let src = resolveStudioScriptSrc(search, hostname);
787
+ if (src) {
788
+ react.loadScript(src).catch(console.error);
789
+ }
790
+ }, [search]);
791
+ }
723
792
  function useStudio(weaverse) {
724
793
  let { revalidate } = reactRouter.useRevalidator();
725
794
  let navigation = reactRouter.useNavigation();
726
795
  let { pathname, search } = reactRouter.useLocation();
727
796
  let navigate = reactRouter.useNavigate();
728
797
  let themeSettingsStore = useThemeSettingsStore();
729
- let { themeTextStore, merchantOverrides } = useThemeText();
798
+ let { translationStore, themeTextStore, merchantOverrides } = useTranslation();
730
799
  let {
731
800
  isDesignMode,
732
801
  weaverseHost,
@@ -735,22 +804,33 @@ function useStudio(weaverse) {
735
804
  isRevisionPreview
736
805
  } = weaverse;
737
806
  react$1.useEffect(() => {
738
- if (navigation.state === "idle") {
739
- if (isRevisionPreview || isPreviewMode) {
740
- let previewSrc = `${weaverseHost}/static/studio/hydrogen/preview.js?v=${weaverseVersion}`;
741
- react.loadScript(previewSrc).catch(console.error);
742
- } else if (isDesignMode) {
743
- weaverse.internal = {
744
- ...weaverse.internal,
745
- navigate,
746
- revalidate,
747
- merchantOverrides,
748
- themeSettingsStore,
749
- themeTextStore
750
- };
751
- let studioSrc = `${weaverseHost}/static/studio/hydrogen/index.js?v=${weaverseVersion}`;
752
- react.loadScript(studioSrc).then(waitForStudio).then((studio) => studio?.init(weaverse)).catch(console.error);
753
- }
807
+ if (navigation.state !== "idle") {
808
+ return;
809
+ }
810
+ let src = getStudioScriptSrc({
811
+ isDesignMode,
812
+ isPreviewMode,
813
+ isRevisionPreview,
814
+ weaverseHost,
815
+ weaverseVersion
816
+ });
817
+ if (!src) {
818
+ return;
819
+ }
820
+ if (isRevisionPreview || isPreviewMode) {
821
+ react.loadScript(src).catch(console.error);
822
+ } else {
823
+ weaverse.internal = {
824
+ ...weaverse.internal,
825
+ navigate,
826
+ revalidate,
827
+ merchantOverrides,
828
+ themeSettingsStore,
829
+ translationStore,
830
+ // Deprecated alias for existing Studio bridge integrations.
831
+ themeTextStore
832
+ };
833
+ react.loadScript(src).then(waitForStudio).then((studio) => studio?.init(weaverse)).catch(console.error);
754
834
  }
755
835
  }, [pathname, search, navigation.state]);
756
836
  usePixel(weaverse);
@@ -758,6 +838,12 @@ function useStudio(weaverse) {
758
838
  function usePixel(context) {
759
839
  let { projectId, pageId, isDesignMode, weaverseHost } = context;
760
840
  let { key: navigationKey } = reactRouter.useLocation();
841
+ react$1.useEffect(() => {
842
+ if (!(projectId && pageId) || isDesignMode) {
843
+ return;
844
+ }
845
+ observeNavigation(navigationKey);
846
+ }, [navigationKey, projectId, pageId, isDesignMode]);
761
847
  react$1.useEffect(() => {
762
848
  if (!(projectId && pageId) || isDesignMode) {
763
849
  return;
@@ -959,6 +1045,10 @@ var StudioBridge = react$1.memo(({ context }) => {
959
1045
  useStudio(context);
960
1046
  return null;
961
1047
  });
1048
+ var StudioConnect = react$1.memo(() => {
1049
+ useStudioConnect();
1050
+ return null;
1051
+ });
962
1052
  var registeredComponentLists = /* @__PURE__ */ new WeakSet();
963
1053
  function RenderRoot(props) {
964
1054
  const { data, components, dataContext } = props;
@@ -1041,24 +1131,27 @@ var WeaverseHydrogenRoot = react$1.memo(
1041
1131
  function withWeaverse(Component, options) {
1042
1132
  return (props) => {
1043
1133
  const themeSettingsStore = useCreateThemeSettingsStore();
1044
- const themeTextData = useThemeTextData();
1045
- const themeTextStoreRef = react$1.useRef(null);
1046
- if (!themeTextStoreRef.current) {
1047
- themeTextStoreRef.current = new ThemeTextStore();
1134
+ const translationData = useTranslationData();
1135
+ const translationStoreRef = react$1.useRef(null);
1136
+ if (!translationStoreRef.current) {
1137
+ translationStoreRef.current = new TranslationStore();
1048
1138
  }
1049
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeSettingsStoreContext.Provider, { value: themeSettingsStore, children: /* @__PURE__ */ jsxRuntime.jsx(
1050
- ThemeTextProvider,
1139
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeSettingsStoreContext.Provider, { value: themeSettingsStore, children: /* @__PURE__ */ jsxRuntime.jsxs(
1140
+ TranslationProvider,
1051
1141
  {
1052
- merchantOverrides: themeTextData.merchantOverrides,
1053
- staticContent: themeTextData.staticContent,
1142
+ merchantOverrides: translationData.merchantOverrides,
1143
+ staticContent: translationData.staticContent,
1054
1144
  t: options?.t,
1055
- themeTextStore: themeTextStoreRef.current,
1056
- children: /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props })
1145
+ translationStore: translationStoreRef.current,
1146
+ children: [
1147
+ /* @__PURE__ */ jsxRuntime.jsx(StudioConnect, {}),
1148
+ /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props })
1149
+ ]
1057
1150
  }
1058
1151
  ) });
1059
1152
  };
1060
1153
  }
1061
- function useThemeTextData() {
1154
+ function useTranslationData() {
1062
1155
  const data = reactRouter.useRouteLoaderData("root");
1063
1156
  return {
1064
1157
  staticContent: data?.weaverseTheme?.staticContent ?? {},
@@ -1636,6 +1729,9 @@ var WeaverseClient = class {
1636
1729
  try {
1637
1730
  const { projectId, weaverseHost } = this.configs;
1638
1731
  const i18n = this.storefront.i18n;
1732
+ if (!this.themeSchema?.i18n) {
1733
+ return;
1734
+ }
1639
1735
  if (!(projectId && weaverseHost)) {
1640
1736
  return;
1641
1737
  }
@@ -1736,7 +1832,7 @@ var WeaverseClient = class {
1736
1832
  * Fetches page content, executes component loaders, and handles empty items fallback.
1737
1833
  *
1738
1834
  * @param params - Optional page loading parameters
1739
- * @param params.type - Page type (HOME, PAGE, PRODUCT, COLLECTION, etc.)
1835
+ * @param params.type - Page type (INDEX, PAGE, PRODUCT, COLLECTION, etc.)
1740
1836
  * @param params.handle - Page handle for dynamic routes
1741
1837
  * @param params.locale - Locale override
1742
1838
  * @param params.projectId - Route-level project ID override (for multi-project)
@@ -1749,7 +1845,7 @@ var WeaverseClient = class {
1749
1845
  * const data = await weaverse.loadPage()
1750
1846
  *
1751
1847
  * // With page type
1752
- * const data = await weaverse.loadPage({ type: 'HOME' })
1848
+ * const data = await weaverse.loadPage({ type: 'INDEX' })
1753
1849
  *
1754
1850
  * // Route-level project override
1755
1851
  * const data = await weaverse.loadPage({
@@ -1992,6 +2088,8 @@ Object.defineProperty(exports, "useWeaverse", {
1992
2088
  exports.IMAGES_PLACEHOLDERS = IMAGES_PLACEHOLDERS;
1993
2089
  exports.ThemeTextProvider = ThemeTextProvider;
1994
2090
  exports.ThemeTextStore = ThemeTextStore;
2091
+ exports.TranslationProvider = TranslationProvider;
2092
+ exports.TranslationStore = TranslationStore;
1995
2093
  exports.WeaverseClient = WeaverseClient;
1996
2094
  exports.WeaverseError = WeaverseError;
1997
2095
  exports.WeaverseHydrogen = WeaverseHydrogen;
@@ -2018,6 +2116,7 @@ exports.isWithCacheFetchResponse = isWithCacheFetchResponse;
2018
2116
  exports.registerComponent = registerComponent;
2019
2117
  exports.useThemeSettings = useThemeSettings;
2020
2118
  exports.useThemeText = useThemeText;
2119
+ exports.useTranslation = useTranslation;
2021
2120
  exports.useWeaverseDataContext = useWeaverseDataContext;
2022
2121
  exports.withWeaverse = withWeaverse;
2023
2122
  Object.keys(schema$1).forEach(function (k) {