@weaverse/hydrogen 5.9.4 → 5.10.0

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.mjs CHANGED
@@ -4,8 +4,8 @@ export { isBrowser, isIframe, useChildInstances, useItemInstance, useParentInsta
4
4
  import { createSchema, isValidSchema } from '@weaverse/schema';
5
5
  export * from '@weaverse/schema';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+ import { createContext, memo, useMemo, Suspense, useEffect, useContext } from 'react';
7
8
  import { useMatches, Await, useRevalidator, useNavigation, useLocation, useNavigate, useRouteLoaderData } from 'react-router';
8
- import { memo, useMemo, Suspense, createContext, useEffect } from 'react';
9
9
  import { ErrorBoundary } from 'react-error-boundary';
10
10
 
11
11
  // src/index.ts
@@ -33,6 +33,59 @@ var defaultComponents = [
33
33
  schema
34
34
  }
35
35
  ];
36
+ function getNestedKey(obj, path, fallback) {
37
+ const keys = path.split(".");
38
+ let current = obj;
39
+ for (const key of keys) {
40
+ if (current == null || typeof current !== "object") {
41
+ return fallback;
42
+ }
43
+ current = current[key];
44
+ }
45
+ if (typeof current === "string") {
46
+ return current;
47
+ }
48
+ return fallback;
49
+ }
50
+ function interpolate(template, variables) {
51
+ if (!variables || Object.keys(variables).length === 0) {
52
+ return template;
53
+ }
54
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
55
+ const value = variables[key];
56
+ return value !== void 0 ? String(value) : match;
57
+ });
58
+ }
59
+ var ThemeTextContext = createContext(null);
60
+ ThemeTextContext.displayName = "WeaverseThemeTextContext";
61
+ function ThemeTextProvider({
62
+ staticContent,
63
+ merchantOverrides,
64
+ t: externalT,
65
+ children
66
+ }) {
67
+ const value = useMemo(() => {
68
+ const t = (key, variables) => {
69
+ if (externalT) {
70
+ const external = externalT(key, variables);
71
+ if (external && external !== key) {
72
+ return external;
73
+ }
74
+ }
75
+ const override = merchantOverrides ? getNestedKey(merchantOverrides, key) : void 0;
76
+ const staticValue = getNestedKey(staticContent, key);
77
+ const raw = override ?? staticValue ?? key;
78
+ return interpolate(raw, variables);
79
+ };
80
+ return { t };
81
+ }, [staticContent, merchantOverrides, externalT]);
82
+ return /* @__PURE__ */ jsx(ThemeTextContext.Provider, { value, children });
83
+ }
84
+ function useThemeText() {
85
+ const ctx = useContext(ThemeTextContext);
86
+ if (!ctx) throw new Error("useThemeText must be used within <ThemeTextProvider>. Wrap your app with withWeaverse().");
87
+ return ctx;
88
+ }
36
89
  function createFlatDataContext(matches) {
37
90
  const context = {};
38
91
  for (const match of matches) {
@@ -562,10 +615,27 @@ var WeaverseHydrogenRoot = memo(
562
615
  );
563
616
  var ThemeSettingsProvider = createContext(null);
564
617
  ThemeSettingsProvider.displayName = "WeaverseThemeSettingsProvider";
565
- function withWeaverse(Component) {
618
+ function withWeaverse(Component, options) {
566
619
  return (props) => {
567
- const { settings } = useThemeSettingsStore();
568
- return /* @__PURE__ */ jsx(ThemeSettingsProvider.Provider, { value: settings, children: /* @__PURE__ */ jsx(Component, { ...props }) });
620
+ const themeSettingsStore = useThemeSettingsStore();
621
+ const { settings } = themeSettingsStore;
622
+ const themeTextData = useThemeTextData();
623
+ return /* @__PURE__ */ jsx(ThemeSettingsProvider.Provider, { value: settings, children: /* @__PURE__ */ jsx(
624
+ ThemeTextProvider,
625
+ {
626
+ merchantOverrides: themeTextData.merchantOverrides,
627
+ staticContent: themeTextData.staticContent,
628
+ t: options?.t,
629
+ children: /* @__PURE__ */ jsx(Component, { ...props })
630
+ }
631
+ ) });
632
+ };
633
+ }
634
+ function useThemeTextData() {
635
+ const data = useRouteLoaderData("root");
636
+ return {
637
+ staticContent: data?.weaverseTheme?.staticContent ?? {},
638
+ merchantOverrides: data?.weaverseTheme?.merchantOverrides
569
639
  };
570
640
  }
571
641
  function useThemeSettings() {
@@ -981,41 +1051,52 @@ var WeaverseClient = class {
981
1051
  }
982
1052
  const url = this.getApiUrl("project_configs");
983
1053
  const body = JSON.stringify({ isDesignMode, projectId });
984
- let data = await this.fetchWithCache(url, {
985
- method: "POST",
986
- strategy,
987
- body,
988
- headers: {
989
- "Content-Type": "application/json",
990
- Accept: "application/json",
991
- "Accept-Encoding": "gzip, deflate, br"
992
- }
993
- });
994
- if (typeof data !== "object" || data === null) {
1054
+ const [data, merchantOverrides] = await Promise.all([
1055
+ this.fetchWithCache(url, {
1056
+ method: "POST",
1057
+ strategy,
1058
+ body,
1059
+ headers: {
1060
+ "Content-Type": "application/json",
1061
+ Accept: "application/json",
1062
+ "Accept-Encoding": "gzip, deflate, br"
1063
+ }
1064
+ }),
1065
+ this.fetchMerchantOverrides(strategy)
1066
+ ]);
1067
+ let result = data;
1068
+ if (typeof result !== "object" || result === null) {
995
1069
  console.warn(
996
1070
  "Invalid theme settings response format, using defaults:",
997
1071
  {
998
- type: typeof data,
999
- isString: typeof data === "string",
1000
- length: typeof data === "string" ? data.length : "N/A",
1001
- preview: typeof data === "string" ? `${data.substring(0, 50)}...` : String(data)
1072
+ type: typeof result,
1073
+ isString: typeof result === "string",
1074
+ length: typeof result === "string" ? result.length : "N/A",
1075
+ preview: typeof result === "string" ? `${result.substring(0, 50)}...` : String(result)
1002
1076
  }
1003
1077
  );
1004
- data = { theme: defaultThemeSettings };
1078
+ result = { theme: defaultThemeSettings };
1005
1079
  }
1006
1080
  if (this.themeSchema?.settings || this.themeSchema?.inspector) {
1007
- if (!data.theme || typeof data.theme !== "object") {
1008
- data.theme = defaultThemeSettings;
1081
+ if (!result.theme || typeof result.theme !== "object") {
1082
+ result.theme = defaultThemeSettings;
1009
1083
  } else {
1010
- data.theme = {
1084
+ result.theme = {
1011
1085
  ...defaultThemeSettings,
1012
- ...data.theme
1086
+ ...result.theme
1013
1087
  };
1014
1088
  }
1015
1089
  }
1090
+ const staticContent = this.themeSchema?.i18n?.staticContent;
1091
+ if (staticContent) {
1092
+ result.staticContent = staticContent;
1093
+ }
1094
+ if (merchantOverrides) {
1095
+ result.merchantOverrides = merchantOverrides;
1096
+ }
1016
1097
  if (this.configs.isDesignMode) {
1017
- data = {
1018
- ...data,
1098
+ result = {
1099
+ ...result,
1019
1100
  schema: this.themeSchema && typeof this.themeSchema === "object" ? {
1020
1101
  ...this.themeSchema,
1021
1102
  settings: this.themeSchema?.settings?.map((group) => ({
@@ -1034,18 +1115,57 @@ var WeaverseClient = class {
1034
1115
  publicEnv: this.configs.publicEnv
1035
1116
  };
1036
1117
  }
1037
- return data;
1118
+ return result;
1038
1119
  } catch (error) {
1039
1120
  const errorMessage = "Unable to load theme settings";
1040
1121
  const errorDetails = this.extractErrorMessage(error);
1041
1122
  console.error(errorMessage, errorDetails);
1042
1123
  return {
1043
1124
  theme: defaultThemeSettings,
1125
+ staticContent: this.themeSchema?.i18n?.staticContent,
1044
1126
  _error: errorDetails,
1045
1127
  _loadFailed: true
1046
1128
  };
1047
1129
  }
1048
1130
  };
1131
+ /**
1132
+ * Fetch locale-specific merchant overrides from the translation API.
1133
+ * Returns a nested JSON object with translated values.
1134
+ * Falls back to empty object on error to avoid blocking theme rendering.
1135
+ */
1136
+ fetchMerchantOverrides = async (strategy) => {
1137
+ try {
1138
+ const { projectId, weaverseHost } = this.configs;
1139
+ const i18n = this.storefront.i18n;
1140
+ if (!(projectId && weaverseHost)) {
1141
+ return void 0;
1142
+ }
1143
+ const language = (i18n?.language || "en").toLowerCase();
1144
+ const country = (i18n?.country || "us").toLowerCase();
1145
+ const locale = `${language}-${country}`;
1146
+ const url = `${weaverseHost}/api/translation/static?projectId=${projectId}&locale=${locale}`;
1147
+ const overrides = await this.fetchWithCache(
1148
+ url,
1149
+ {
1150
+ method: "GET",
1151
+ strategy,
1152
+ headers: {
1153
+ Accept: "application/json"
1154
+ }
1155
+ }
1156
+ );
1157
+ if (typeof overrides === "object" && overrides !== null) {
1158
+ return overrides;
1159
+ }
1160
+ return void 0;
1161
+ } catch (error) {
1162
+ console.warn(
1163
+ "Unable to load merchant overrides, using theme defaults:",
1164
+ this.extractErrorMessage(error)
1165
+ );
1166
+ return void 0;
1167
+ }
1168
+ };
1049
1169
  /**
1050
1170
  * Generate fallback page with default structure when page data is unavailable.
1051
1171
  * Used internally when API returns no page or when page needs default content.
@@ -1141,7 +1261,8 @@ var WeaverseClient = class {
1141
1261
  headers: {
1142
1262
  "Content-Type": "application/json",
1143
1263
  Accept: "application/json",
1144
- "Accept-Encoding": "gzip, deflate, br"
1264
+ "Accept-Encoding": "gzip, deflate, br",
1265
+ "X-Visitor-UA": request.headers.get("user-agent") || ""
1145
1266
  }
1146
1267
  };
1147
1268
  const url = this.getApiUrl("project");
@@ -1296,6 +1417,6 @@ var IMAGES_PLACEHOLDERS = {
1296
1417
  product_18: "https://cdn.shopify.com/s/files/1/0838/0052/3057/files/fpo-product-watch-2-1024x1024.svg"
1297
1418
  };
1298
1419
 
1299
- export { IMAGES_PLACEHOLDERS, WeaverseClient, WeaverseError, WeaverseHydrogen, WeaverseHydrogenItem, WeaverseHydrogenRoot, createWeaverseDataContext, defaultComponents, generateDataFromSchema, getPreviewData, getRequestQueries, getSelectedProductOptions, getWeaverseConfigs, hasError, hasWeaverseStudio, isFetchProjectPayload, isProjectIdFunction, isThemeSettingsResponse, isValidHydrogenSchema, isWithCacheFetchResponse, registerComponent, useThemeSettings, useWeaverseDataContext, withWeaverse };
1420
+ export { IMAGES_PLACEHOLDERS, ThemeTextProvider, WeaverseClient, WeaverseError, WeaverseHydrogen, WeaverseHydrogenItem, WeaverseHydrogenRoot, createWeaverseDataContext, defaultComponents, generateDataFromSchema, getNestedKey, getPreviewData, getRequestQueries, getSelectedProductOptions, getWeaverseConfigs, hasError, hasWeaverseStudio, interpolate, isFetchProjectPayload, isProjectIdFunction, isThemeSettingsResponse, isValidHydrogenSchema, isWithCacheFetchResponse, registerComponent, useThemeSettings, useThemeText, useWeaverseDataContext, withWeaverse };
1300
1421
  //# sourceMappingURL=index.mjs.map
1301
1422
  //# sourceMappingURL=index.mjs.map