@weaverse/hydrogen 5.16.1 → 5.16.3
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.d.mts +16 -7
- package/dist/index.d.ts +16 -7
- package/dist/index.js +142 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +142 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -256,10 +256,18 @@ type WithWeaverseOptions = {
|
|
|
256
256
|
t?: TranslateFunction;
|
|
257
257
|
};
|
|
258
258
|
/**
|
|
259
|
-
* Higher-order component that wraps your app with Weaverse theme settings
|
|
260
|
-
* and
|
|
259
|
+
* Higher-order component that wraps your app with Weaverse theme settings and
|
|
260
|
+
* translation context, and mounts the Studio connect bridge.
|
|
261
261
|
*
|
|
262
|
-
*
|
|
262
|
+
* Export the result as your root route's **`Layout`** (not the default route
|
|
263
|
+
* `Component`). In React Router the root `Layout` wraps both the matched route
|
|
264
|
+
* and its `ErrorBoundary`, so the Studio bridge keeps rendering on 404s and
|
|
265
|
+
* loader/render errors — exactly the pages where Studio must still answer its
|
|
266
|
+
* handshake instead of reporting a false "Connection lost". A default-component
|
|
267
|
+
* wrapper (`export default withWeaverse(App)`) is replaced by the error
|
|
268
|
+
* boundary on those routes and would drop the bridge.
|
|
269
|
+
*
|
|
270
|
+
* @param Component - The root layout component to wrap
|
|
263
271
|
* @param options - Optional configuration (e.g. an external `t` function)
|
|
264
272
|
* @returns Wrapped component with theme settings + translation providers
|
|
265
273
|
*
|
|
@@ -267,11 +275,11 @@ type WithWeaverseOptions = {
|
|
|
267
275
|
* ```typescript
|
|
268
276
|
* import { withWeaverse } from '@weaverse/hydrogen'
|
|
269
277
|
*
|
|
270
|
-
* // Basic usage
|
|
271
|
-
* export
|
|
278
|
+
* // Basic usage — export as the root route Layout
|
|
279
|
+
* export const Layout = withWeaverse(RootLayout)
|
|
272
280
|
*
|
|
273
|
-
* // With external translation function
|
|
274
|
-
* export
|
|
281
|
+
* // With an external translation function
|
|
282
|
+
* export const Layout = withWeaverse(RootLayout, { t: myI18nFunction })
|
|
275
283
|
* ```
|
|
276
284
|
*/
|
|
277
285
|
declare function withWeaverse(Component: ComponentType<any>, options?: WithWeaverseOptions): (props: JSX.IntrinsicAttributes) => react_jsx_runtime.JSX.Element;
|
|
@@ -394,6 +402,7 @@ declare class WeaverseClient {
|
|
|
394
402
|
* Returns same format as withCache.fetch for consistency.
|
|
395
403
|
* Includes 10-second timeout to prevent hanging requests.
|
|
396
404
|
*/
|
|
405
|
+
private readonly fetchOnce;
|
|
397
406
|
private readonly directFetch;
|
|
398
407
|
private getApiUrl;
|
|
399
408
|
private isExternalPublicApiUrl;
|
package/dist/index.d.ts
CHANGED
|
@@ -256,10 +256,18 @@ type WithWeaverseOptions = {
|
|
|
256
256
|
t?: TranslateFunction;
|
|
257
257
|
};
|
|
258
258
|
/**
|
|
259
|
-
* Higher-order component that wraps your app with Weaverse theme settings
|
|
260
|
-
* and
|
|
259
|
+
* Higher-order component that wraps your app with Weaverse theme settings and
|
|
260
|
+
* translation context, and mounts the Studio connect bridge.
|
|
261
261
|
*
|
|
262
|
-
*
|
|
262
|
+
* Export the result as your root route's **`Layout`** (not the default route
|
|
263
|
+
* `Component`). In React Router the root `Layout` wraps both the matched route
|
|
264
|
+
* and its `ErrorBoundary`, so the Studio bridge keeps rendering on 404s and
|
|
265
|
+
* loader/render errors — exactly the pages where Studio must still answer its
|
|
266
|
+
* handshake instead of reporting a false "Connection lost". A default-component
|
|
267
|
+
* wrapper (`export default withWeaverse(App)`) is replaced by the error
|
|
268
|
+
* boundary on those routes and would drop the bridge.
|
|
269
|
+
*
|
|
270
|
+
* @param Component - The root layout component to wrap
|
|
263
271
|
* @param options - Optional configuration (e.g. an external `t` function)
|
|
264
272
|
* @returns Wrapped component with theme settings + translation providers
|
|
265
273
|
*
|
|
@@ -267,11 +275,11 @@ type WithWeaverseOptions = {
|
|
|
267
275
|
* ```typescript
|
|
268
276
|
* import { withWeaverse } from '@weaverse/hydrogen'
|
|
269
277
|
*
|
|
270
|
-
* // Basic usage
|
|
271
|
-
* export
|
|
278
|
+
* // Basic usage — export as the root route Layout
|
|
279
|
+
* export const Layout = withWeaverse(RootLayout)
|
|
272
280
|
*
|
|
273
|
-
* // With external translation function
|
|
274
|
-
* export
|
|
281
|
+
* // With an external translation function
|
|
282
|
+
* export const Layout = withWeaverse(RootLayout, { t: myI18nFunction })
|
|
275
283
|
* ```
|
|
276
284
|
*/
|
|
277
285
|
declare function withWeaverse(Component: ComponentType<any>, options?: WithWeaverseOptions): (props: JSX.IntrinsicAttributes) => react_jsx_runtime.JSX.Element;
|
|
@@ -394,6 +402,7 @@ declare class WeaverseClient {
|
|
|
394
402
|
* Returns same format as withCache.fetch for consistency.
|
|
395
403
|
* Includes 10-second timeout to prevent hanging requests.
|
|
396
404
|
*/
|
|
405
|
+
private readonly fetchOnce;
|
|
397
406
|
private readonly directFetch;
|
|
398
407
|
private getApiUrl;
|
|
399
408
|
private isExternalPublicApiUrl;
|
package/dist/index.js
CHANGED
|
@@ -251,6 +251,67 @@ function hasWeaverseStudio(window2) {
|
|
|
251
251
|
return "weaverseStudio" in window2 && typeof window2.weaverseStudio === "object";
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
// src/utils/studio-script-src.ts
|
|
255
|
+
var DEFAULT_WEAVERSE_HOST = "https://studio.weaverse.io";
|
|
256
|
+
var TRUSTED_STUDIO_DOMAINS = ["weaverse.io", "weaverse.dev"];
|
|
257
|
+
function isLoopbackHostname(hostname) {
|
|
258
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".localhost");
|
|
259
|
+
}
|
|
260
|
+
function isTrustedStudioHost(host, options) {
|
|
261
|
+
let parsed;
|
|
262
|
+
try {
|
|
263
|
+
parsed = new URL(host);
|
|
264
|
+
} catch {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
let { hostname, protocol } = parsed;
|
|
268
|
+
if (options?.allowLoopback && isLoopbackHostname(hostname)) {
|
|
269
|
+
return protocol === "http:" || protocol === "https:";
|
|
270
|
+
}
|
|
271
|
+
if (protocol !== "https:") {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
return TRUSTED_STUDIO_DOMAINS.some(
|
|
275
|
+
(domain) => hostname === domain || hostname.endsWith(`.${domain}`)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
function getStudioScriptSrc(mode) {
|
|
279
|
+
let {
|
|
280
|
+
isDesignMode,
|
|
281
|
+
isPreviewMode,
|
|
282
|
+
isRevisionPreview,
|
|
283
|
+
weaverseHost,
|
|
284
|
+
weaverseVersion
|
|
285
|
+
} = mode;
|
|
286
|
+
let base = `${weaverseHost}/static/studio/hydrogen`;
|
|
287
|
+
let version = `?v=${weaverseVersion}`;
|
|
288
|
+
if (isRevisionPreview || isPreviewMode) {
|
|
289
|
+
return `${base}/preview.js${version}`;
|
|
290
|
+
}
|
|
291
|
+
if (isDesignMode) {
|
|
292
|
+
return `${base}/index.js${version}`;
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
function resolveStudioScriptSrc(search, storefrontHostname = "") {
|
|
297
|
+
let params = new URLSearchParams(search);
|
|
298
|
+
let last = (key) => params.getAll(key).at(-1);
|
|
299
|
+
let host = last("weaverseHost");
|
|
300
|
+
if (host) {
|
|
301
|
+
let allowLoopback = isLoopbackHostname(storefrontHostname);
|
|
302
|
+
if (!isTrustedStudioHost(host, { allowLoopback })) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return getStudioScriptSrc({
|
|
307
|
+
isDesignMode: last("isDesignMode") === "true",
|
|
308
|
+
isPreviewMode: last("isPreviewMode") === "true",
|
|
309
|
+
isRevisionPreview: params.has("__revisionId"),
|
|
310
|
+
weaverseHost: host || DEFAULT_WEAVERSE_HOST,
|
|
311
|
+
weaverseVersion: last("weaverseVersion") || ""
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
254
315
|
// src/utils/index.ts
|
|
255
316
|
var warnedTypes = /* @__PURE__ */ new Set();
|
|
256
317
|
function logOnce(type, message) {
|
|
@@ -314,8 +375,9 @@ function getWeaverseConfigs(request, env) {
|
|
|
314
375
|
let url = new URL(request.url);
|
|
315
376
|
let isRevisionPreview = url.searchParams.has("__revisionId");
|
|
316
377
|
let configuredWeaverseHost = WEAVERSE_HOST || envFromProcess.WEAVERSE_HOST;
|
|
317
|
-
let
|
|
318
|
-
let
|
|
378
|
+
let trustedUrlHost = typeof weaverseHost === "string" && isTrustedStudioHost(weaverseHost) ? weaverseHost : "";
|
|
379
|
+
let resolvedWeaverseHost = trustedUrlHost || configuredWeaverseHost || "https://studio.weaverse.io";
|
|
380
|
+
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
381
|
return {
|
|
320
382
|
projectId: weaverseProjectId || WEAVERSE_PROJECT_ID || envFromProcess.WEAVERSE_PROJECT_ID || "",
|
|
321
383
|
weaverseHost: resolvedWeaverseHost,
|
|
@@ -720,6 +782,16 @@ function waitForStudio() {
|
|
|
720
782
|
check();
|
|
721
783
|
});
|
|
722
784
|
}
|
|
785
|
+
function useStudioConnect() {
|
|
786
|
+
let { search } = reactRouter.useLocation();
|
|
787
|
+
react$1.useEffect(() => {
|
|
788
|
+
let hostname = typeof window === "undefined" ? "" : window.location.hostname;
|
|
789
|
+
let src = resolveStudioScriptSrc(search, hostname);
|
|
790
|
+
if (src) {
|
|
791
|
+
react.loadScript(src).catch(console.error);
|
|
792
|
+
}
|
|
793
|
+
}, [search]);
|
|
794
|
+
}
|
|
723
795
|
function useStudio(weaverse) {
|
|
724
796
|
let { revalidate } = reactRouter.useRevalidator();
|
|
725
797
|
let navigation = reactRouter.useNavigation();
|
|
@@ -735,22 +807,31 @@ function useStudio(weaverse) {
|
|
|
735
807
|
isRevisionPreview
|
|
736
808
|
} = weaverse;
|
|
737
809
|
react$1.useEffect(() => {
|
|
738
|
-
if (navigation.state
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
810
|
+
if (navigation.state !== "idle") {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
let src = getStudioScriptSrc({
|
|
814
|
+
isDesignMode,
|
|
815
|
+
isPreviewMode,
|
|
816
|
+
isRevisionPreview,
|
|
817
|
+
weaverseHost,
|
|
818
|
+
weaverseVersion
|
|
819
|
+
});
|
|
820
|
+
if (!src) {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (isRevisionPreview || isPreviewMode) {
|
|
824
|
+
react.loadScript(src).catch(console.error);
|
|
825
|
+
} else {
|
|
826
|
+
weaverse.internal = {
|
|
827
|
+
...weaverse.internal,
|
|
828
|
+
navigate,
|
|
829
|
+
revalidate,
|
|
830
|
+
merchantOverrides,
|
|
831
|
+
themeSettingsStore,
|
|
832
|
+
themeTextStore
|
|
833
|
+
};
|
|
834
|
+
react.loadScript(src).then(waitForStudio).then((studio) => studio?.init(weaverse)).catch(console.error);
|
|
754
835
|
}
|
|
755
836
|
}, [pathname, search, navigation.state]);
|
|
756
837
|
usePixel(weaverse);
|
|
@@ -959,6 +1040,10 @@ var StudioBridge = react$1.memo(({ context }) => {
|
|
|
959
1040
|
useStudio(context);
|
|
960
1041
|
return null;
|
|
961
1042
|
});
|
|
1043
|
+
var StudioConnect = react$1.memo(() => {
|
|
1044
|
+
useStudioConnect();
|
|
1045
|
+
return null;
|
|
1046
|
+
});
|
|
962
1047
|
var registeredComponentLists = /* @__PURE__ */ new WeakSet();
|
|
963
1048
|
function RenderRoot(props) {
|
|
964
1049
|
const { data, components, dataContext } = props;
|
|
@@ -1046,14 +1131,17 @@ function withWeaverse(Component, options) {
|
|
|
1046
1131
|
if (!themeTextStoreRef.current) {
|
|
1047
1132
|
themeTextStoreRef.current = new ThemeTextStore();
|
|
1048
1133
|
}
|
|
1049
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ThemeSettingsStoreContext.Provider, { value: themeSettingsStore, children: /* @__PURE__ */ jsxRuntime.
|
|
1134
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ThemeSettingsStoreContext.Provider, { value: themeSettingsStore, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1050
1135
|
ThemeTextProvider,
|
|
1051
1136
|
{
|
|
1052
1137
|
merchantOverrides: themeTextData.merchantOverrides,
|
|
1053
1138
|
staticContent: themeTextData.staticContent,
|
|
1054
1139
|
t: options?.t,
|
|
1055
1140
|
themeTextStore: themeTextStoreRef.current,
|
|
1056
|
-
children:
|
|
1141
|
+
children: [
|
|
1142
|
+
/* @__PURE__ */ jsxRuntime.jsx(StudioConnect, {}),
|
|
1143
|
+
/* @__PURE__ */ jsxRuntime.jsx(Component, { ...props })
|
|
1144
|
+
]
|
|
1057
1145
|
}
|
|
1058
1146
|
) });
|
|
1059
1147
|
};
|
|
@@ -1089,7 +1177,11 @@ function normalizePageUrl(url) {
|
|
|
1089
1177
|
);
|
|
1090
1178
|
kept.sort(([a], [b]) => a.localeCompare(b));
|
|
1091
1179
|
const search = new URLSearchParams(kept).toString();
|
|
1092
|
-
|
|
1180
|
+
let { pathname } = parsed;
|
|
1181
|
+
if (pathname.endsWith(".data")) {
|
|
1182
|
+
pathname = pathname.slice(0, -".data".length);
|
|
1183
|
+
}
|
|
1184
|
+
return `${parsed.origin}${pathname}${search ? `?${search}` : ""}`;
|
|
1093
1185
|
}
|
|
1094
1186
|
|
|
1095
1187
|
// src/weaverse-client.ts
|
|
@@ -1104,6 +1196,17 @@ var CACHE_DURATIONS = {
|
|
|
1104
1196
|
STATIC: 3600
|
|
1105
1197
|
};
|
|
1106
1198
|
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
1199
|
+
var MAX_FETCH_ATTEMPTS = 2;
|
|
1200
|
+
var FETCH_RETRY_BACKOFF_MS = 300;
|
|
1201
|
+
function isRetryableFetchError(error) {
|
|
1202
|
+
if (!(error instanceof WeaverseError)) {
|
|
1203
|
+
return false;
|
|
1204
|
+
}
|
|
1205
|
+
if (error.code === "TIMEOUT_ERROR" || error.code === "NETWORK_ERROR") {
|
|
1206
|
+
return true;
|
|
1207
|
+
}
|
|
1208
|
+
return error.code === "FETCH_ERROR" && (error.statusCode ?? 0) >= 500;
|
|
1209
|
+
}
|
|
1107
1210
|
var API_PATH = "v1";
|
|
1108
1211
|
var DEFAULT_CACHE_STRATEGY = {
|
|
1109
1212
|
maxAge: CACHE_DURATIONS.SHORT,
|
|
@@ -1394,7 +1497,7 @@ var WeaverseClient = class {
|
|
|
1394
1497
|
* Returns same format as withCache.fetch for consistency.
|
|
1395
1498
|
* Includes 10-second timeout to prevent hanging requests.
|
|
1396
1499
|
*/
|
|
1397
|
-
|
|
1500
|
+
fetchOnce = async (url, options, timeoutMs) => {
|
|
1398
1501
|
const controller = new AbortController();
|
|
1399
1502
|
const timeoutId = setTimeout(
|
|
1400
1503
|
() => controller.abort(),
|
|
@@ -1440,6 +1543,23 @@ var WeaverseClient = class {
|
|
|
1440
1543
|
}
|
|
1441
1544
|
}
|
|
1442
1545
|
};
|
|
1546
|
+
directFetch = async (url, options = {}, timeoutMs = this.fetchTimeoutMs) => {
|
|
1547
|
+
let lastError;
|
|
1548
|
+
for (let attempt = 1; attempt <= MAX_FETCH_ATTEMPTS; attempt++) {
|
|
1549
|
+
try {
|
|
1550
|
+
return await this.fetchOnce(url, options, timeoutMs);
|
|
1551
|
+
} catch (err) {
|
|
1552
|
+
lastError = err;
|
|
1553
|
+
if (attempt >= MAX_FETCH_ATTEMPTS || !isRetryableFetchError(err)) {
|
|
1554
|
+
throw err;
|
|
1555
|
+
}
|
|
1556
|
+
await new Promise(
|
|
1557
|
+
(resolve) => setTimeout(resolve, FETCH_RETRY_BACKOFF_MS * attempt)
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
throw lastError;
|
|
1562
|
+
};
|
|
1443
1563
|
// Helper method for building API URLs consistently
|
|
1444
1564
|
getApiUrl(endpoint) {
|
|
1445
1565
|
const { weaverseApiBase } = this.configs;
|