@dutchiesdk/ecommerce-extensions-sdk 0.35.0 → 0.36.1
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/components/client-only.cjs +56 -0
- package/dist/components/client-only.d.ts +25 -0
- package/dist/context/ecommerce-data-bridge.cjs +9 -1
- package/dist/context/ecommerce-data-bridge.d.ts +20 -0
- package/dist/context/use-host-location.cjs +44 -0
- package/dist/context/use-host-location.d.ts +14 -0
- package/dist/esm/components/client-only.d.ts +25 -0
- package/dist/esm/components/client-only.js +19 -0
- package/dist/esm/context/ecommerce-data-bridge.d.ts +20 -0
- package/dist/esm/context/ecommerce-data-bridge.js +7 -2
- package/dist/esm/context/use-host-location.d.ts +14 -0
- package/dist/esm/context/use-host-location.js +10 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/types/interface.d.ts +60 -1
- package/dist/esm/types/product-card.d.ts +2 -0
- package/dist/esm/utils/environment.d.ts +8 -0
- package/dist/esm/utils/environment.js +3 -0
- package/dist/esm/utils/safe-storage.d.ts +14 -0
- package/dist/esm/utils/safe-storage.js +40 -0
- package/dist/esm/utils/use-isomorphic-layout-effect.d.ts +9 -0
- package/dist/esm/utils/use-isomorphic-layout-effect.js +4 -0
- package/dist/index.cjs +72 -27
- package/dist/index.d.ts +5 -0
- package/dist/types/interface.d.ts +60 -1
- package/dist/types/product-card.d.ts +2 -0
- package/dist/utils/environment.cjs +40 -0
- package/dist/utils/environment.d.ts +8 -0
- package/dist/utils/safe-storage.cjs +77 -0
- package/dist/utils/safe-storage.d.ts +14 -0
- package/dist/utils/use-isomorphic-layout-effect.cjs +38 -0
- package/dist/utils/use-isomorphic-layout-effect.d.ts +9 -0
- package/package.json +2 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
useIsHydrated: ()=>useIsHydrated,
|
|
28
|
+
ClientOnly: ()=>ClientOnly
|
|
29
|
+
});
|
|
30
|
+
const jsx_runtime_namespaceObject = require("react/jsx-runtime");
|
|
31
|
+
const external_react_namespaceObject = require("react");
|
|
32
|
+
function useIsHydrated() {
|
|
33
|
+
const [isHydrated, setIsHydrated] = (0, external_react_namespaceObject.useState)(false);
|
|
34
|
+
(0, external_react_namespaceObject.useEffect)(()=>{
|
|
35
|
+
setIsHydrated(true);
|
|
36
|
+
}, []);
|
|
37
|
+
return isHydrated;
|
|
38
|
+
}
|
|
39
|
+
function ClientOnly({ children, fallback = null }) {
|
|
40
|
+
const isHydrated = useIsHydrated();
|
|
41
|
+
if (!isHydrated) return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(jsx_runtime_namespaceObject.Fragment, {
|
|
42
|
+
children: fallback
|
|
43
|
+
});
|
|
44
|
+
return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(jsx_runtime_namespaceObject.Fragment, {
|
|
45
|
+
children: children
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
exports.ClientOnly = __webpack_exports__.ClientOnly;
|
|
49
|
+
exports.useIsHydrated = __webpack_exports__.useIsHydrated;
|
|
50
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
51
|
+
"ClientOnly",
|
|
52
|
+
"useIsHydrated"
|
|
53
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
54
|
+
Object.defineProperty(exports, '__esModule', {
|
|
55
|
+
value: true
|
|
56
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* True once the component has mounted on the client. Always `false` during server
|
|
4
|
+
* rendering and during the client's first render (pre-hydration) -- this is what
|
|
5
|
+
* lets consumers avoid a hydration mismatch: server and first client render agree.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useIsHydrated(): boolean;
|
|
8
|
+
export type ClientOnlyProps = {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
/** Rendered on the server and during the pre-hydration client render. Defaults to nothing. */
|
|
11
|
+
fallback?: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Renders `children` only once mounted on the client; renders `fallback` (default: nothing)
|
|
15
|
+
* on the server and during the initial client render.
|
|
16
|
+
*
|
|
17
|
+
* Use this for anything that is genuinely browser-only -- maps, video players,
|
|
18
|
+
* geolocation-driven widgets, anything reading `window`/`document` at render time --
|
|
19
|
+
* instead of a hand-rolled `typeof window !== 'undefined'` guard. A guard like that
|
|
20
|
+
* still causes a hydration mismatch (the server renders one thing, the client's first
|
|
21
|
+
* render renders another); `ClientOnly` renders the *same* fallback on the server and
|
|
22
|
+
* the first client pass, then swaps to the real content post-hydration, so there's
|
|
23
|
+
* nothing for React to reconcile against.
|
|
24
|
+
*/
|
|
25
|
+
export declare function ClientOnly({ children, fallback }: ClientOnlyProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -25,18 +25,24 @@ var __webpack_exports__ = {};
|
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
27
|
DataBridgeContext: ()=>DataBridgeContext,
|
|
28
|
+
useCommerceData: ()=>useCommerceData,
|
|
28
29
|
DataBridgeVersion: ()=>DataBridgeVersion,
|
|
29
30
|
useAsyncLoader: ()=>useAsyncLoader,
|
|
30
31
|
useDataBridge: ()=>useDataBridge
|
|
31
32
|
});
|
|
32
33
|
const external_react_namespaceObject = require("react");
|
|
33
|
-
const DataBridgeVersion = '0.
|
|
34
|
+
const DataBridgeVersion = '0.36.1';
|
|
34
35
|
const DataBridgeContext = (0, external_react_namespaceObject.createContext)(void 0);
|
|
35
36
|
const useDataBridge = ()=>{
|
|
36
37
|
const context = (0, external_react_namespaceObject.useContext)(DataBridgeContext);
|
|
37
38
|
if (void 0 === context) throw new Error('useDataBridge must be used within a DataBridgeProvider');
|
|
38
39
|
return context;
|
|
39
40
|
};
|
|
41
|
+
const EMPTY_COMMERCE_DATA = {};
|
|
42
|
+
const useCommerceData = ()=>{
|
|
43
|
+
const { data } = useDataBridge();
|
|
44
|
+
return data ?? EMPTY_COMMERCE_DATA;
|
|
45
|
+
};
|
|
40
46
|
const useAsyncLoader = (fn, params)=>{
|
|
41
47
|
const [data, setData] = (0, external_react_namespaceObject.useState)(null);
|
|
42
48
|
(0, external_react_namespaceObject.useEffect)(()=>{
|
|
@@ -53,11 +59,13 @@ const useAsyncLoader = (fn, params)=>{
|
|
|
53
59
|
exports.DataBridgeContext = __webpack_exports__.DataBridgeContext;
|
|
54
60
|
exports.DataBridgeVersion = __webpack_exports__.DataBridgeVersion;
|
|
55
61
|
exports.useAsyncLoader = __webpack_exports__.useAsyncLoader;
|
|
62
|
+
exports.useCommerceData = __webpack_exports__.useCommerceData;
|
|
56
63
|
exports.useDataBridge = __webpack_exports__.useDataBridge;
|
|
57
64
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
58
65
|
"DataBridgeContext",
|
|
59
66
|
"DataBridgeVersion",
|
|
60
67
|
"useAsyncLoader",
|
|
68
|
+
"useCommerceData",
|
|
61
69
|
"useDataBridge"
|
|
62
70
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
63
71
|
Object.defineProperty(exports, '__esModule', {
|
|
@@ -19,9 +19,29 @@ export declare const DataBridgeContext: import("react").Context<CommerceComponen
|
|
|
19
19
|
* @returns The data bridge context.
|
|
20
20
|
*/
|
|
21
21
|
export declare const useDataBridge: () => CommerceComponentsDataInterface;
|
|
22
|
+
/**
|
|
23
|
+
* Reads the V2 synchronous commerce data (see `CommerceDataV2`) off the data bridge.
|
|
24
|
+
* Prefer this over `useDataBridge().dataLoaders` in new theme code -- no async, no
|
|
25
|
+
* `useAsyncLoader`, no loading state to manage for data the host already has. Fields
|
|
26
|
+
* are `undefined` until the host resolves them for the current page; this returns an
|
|
27
|
+
* empty object (not `undefined`) when the host hasn't populated `data` at all, so
|
|
28
|
+
* `useCommerceData().brands` is always safe to read without an extra null check.
|
|
29
|
+
*/
|
|
30
|
+
export declare const useCommerceData: () => NonNullable<CommerceComponentsDataInterface["data"]>;
|
|
22
31
|
/**
|
|
23
32
|
* A hook to load data asynchronously.
|
|
24
33
|
*
|
|
34
|
+
* CSR-only by design: `useEffect` never runs during server rendering, so this can only
|
|
35
|
+
* ever produce a loading placeholder on the server. We looked at making this
|
|
36
|
+
* SSR-available (a keyed, host-resolvable variant with a collect-then-render pass) and
|
|
37
|
+
* decided against shipping it for now -- the host doesn't yet prefetch most theme data
|
|
38
|
+
* server-side (see PLAN.md's "V2 pivot"/SSR-today correction), so a general SSR
|
|
39
|
+
* mechanism here would have zero real consumers. Host-provided commerce data should go
|
|
40
|
+
* through `useCommerceData()` (`CommerceDataV2`) instead, which the host resolves
|
|
41
|
+
* synchronously as its own SSR data-fetching expands. This hook remains the right tool
|
|
42
|
+
* for genuinely dynamic, per-call data (e.g. `dataLoaders.integrationValue(key)`) and
|
|
43
|
+
* theme-original async work that the host has no way to pre-resolve.
|
|
44
|
+
*
|
|
25
45
|
* @param fn - The function to load the data.
|
|
26
46
|
* @param params - Optional parameters to pass to the function.
|
|
27
47
|
* @returns The data and a boolean indicating if the data is loading.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
useHostLocation: ()=>useHostLocation
|
|
28
|
+
});
|
|
29
|
+
const external_ecommerce_data_bridge_cjs_namespaceObject = require("./ecommerce-data-bridge.cjs");
|
|
30
|
+
const EMPTY_LOCATION = {
|
|
31
|
+
path: '',
|
|
32
|
+
search: ''
|
|
33
|
+
};
|
|
34
|
+
function useHostLocation() {
|
|
35
|
+
const { request } = (0, external_ecommerce_data_bridge_cjs_namespaceObject.useDataBridge)();
|
|
36
|
+
return request ?? EMPTY_LOCATION;
|
|
37
|
+
}
|
|
38
|
+
exports.useHostLocation = __webpack_exports__.useHostLocation;
|
|
39
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
40
|
+
"useHostLocation"
|
|
41
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
42
|
+
Object.defineProperty(exports, '__esModule', {
|
|
43
|
+
value: true
|
|
44
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HostLocation } from '../types/interface';
|
|
2
|
+
/**
|
|
3
|
+
* SSR-safe access to the current request's path/query, sourced from the host via the
|
|
4
|
+
* data bridge instead of `window.location`. Populated identically on the server and the
|
|
5
|
+
* client, so unlike `typeof window !== 'undefined' ? window.location.pathname : ''`, this
|
|
6
|
+
* never produces a hydration mismatch (the guarded form renders `''` on the server and
|
|
7
|
+
* the client's first render, then the "real" value only once effects run -- two different
|
|
8
|
+
* outputs for what should be the same render).
|
|
9
|
+
*
|
|
10
|
+
* Returns an empty location (not undefined) when the host hasn't supplied one (e.g. a
|
|
11
|
+
* host that hasn't been upgraded to populate `request` yet), so call sites don't need
|
|
12
|
+
* their own fallback handling.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useHostLocation(): HostLocation;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* True once the component has mounted on the client. Always `false` during server
|
|
4
|
+
* rendering and during the client's first render (pre-hydration) -- this is what
|
|
5
|
+
* lets consumers avoid a hydration mismatch: server and first client render agree.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useIsHydrated(): boolean;
|
|
8
|
+
export type ClientOnlyProps = {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
/** Rendered on the server and during the pre-hydration client render. Defaults to nothing. */
|
|
11
|
+
fallback?: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Renders `children` only once mounted on the client; renders `fallback` (default: nothing)
|
|
15
|
+
* on the server and during the initial client render.
|
|
16
|
+
*
|
|
17
|
+
* Use this for anything that is genuinely browser-only -- maps, video players,
|
|
18
|
+
* geolocation-driven widgets, anything reading `window`/`document` at render time --
|
|
19
|
+
* instead of a hand-rolled `typeof window !== 'undefined'` guard. A guard like that
|
|
20
|
+
* still causes a hydration mismatch (the server renders one thing, the client's first
|
|
21
|
+
* render renders another); `ClientOnly` renders the *same* fallback on the server and
|
|
22
|
+
* the first client pass, then swaps to the real content post-hydration, so there's
|
|
23
|
+
* nothing for React to reconcile against.
|
|
24
|
+
*/
|
|
25
|
+
export declare function ClientOnly({ children, fallback }: ClientOnlyProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
function useIsHydrated() {
|
|
4
|
+
const [isHydrated, setIsHydrated] = useState(false);
|
|
5
|
+
useEffect(()=>{
|
|
6
|
+
setIsHydrated(true);
|
|
7
|
+
}, []);
|
|
8
|
+
return isHydrated;
|
|
9
|
+
}
|
|
10
|
+
function ClientOnly({ children, fallback = null }) {
|
|
11
|
+
const isHydrated = useIsHydrated();
|
|
12
|
+
if (!isHydrated) return /*#__PURE__*/ jsx(Fragment, {
|
|
13
|
+
children: fallback
|
|
14
|
+
});
|
|
15
|
+
return /*#__PURE__*/ jsx(Fragment, {
|
|
16
|
+
children: children
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export { ClientOnly, useIsHydrated };
|
|
@@ -19,9 +19,29 @@ export declare const DataBridgeContext: import("react").Context<CommerceComponen
|
|
|
19
19
|
* @returns The data bridge context.
|
|
20
20
|
*/
|
|
21
21
|
export declare const useDataBridge: () => CommerceComponentsDataInterface;
|
|
22
|
+
/**
|
|
23
|
+
* Reads the V2 synchronous commerce data (see `CommerceDataV2`) off the data bridge.
|
|
24
|
+
* Prefer this over `useDataBridge().dataLoaders` in new theme code -- no async, no
|
|
25
|
+
* `useAsyncLoader`, no loading state to manage for data the host already has. Fields
|
|
26
|
+
* are `undefined` until the host resolves them for the current page; this returns an
|
|
27
|
+
* empty object (not `undefined`) when the host hasn't populated `data` at all, so
|
|
28
|
+
* `useCommerceData().brands` is always safe to read without an extra null check.
|
|
29
|
+
*/
|
|
30
|
+
export declare const useCommerceData: () => NonNullable<CommerceComponentsDataInterface["data"]>;
|
|
22
31
|
/**
|
|
23
32
|
* A hook to load data asynchronously.
|
|
24
33
|
*
|
|
34
|
+
* CSR-only by design: `useEffect` never runs during server rendering, so this can only
|
|
35
|
+
* ever produce a loading placeholder on the server. We looked at making this
|
|
36
|
+
* SSR-available (a keyed, host-resolvable variant with a collect-then-render pass) and
|
|
37
|
+
* decided against shipping it for now -- the host doesn't yet prefetch most theme data
|
|
38
|
+
* server-side (see PLAN.md's "V2 pivot"/SSR-today correction), so a general SSR
|
|
39
|
+
* mechanism here would have zero real consumers. Host-provided commerce data should go
|
|
40
|
+
* through `useCommerceData()` (`CommerceDataV2`) instead, which the host resolves
|
|
41
|
+
* synchronously as its own SSR data-fetching expands. This hook remains the right tool
|
|
42
|
+
* for genuinely dynamic, per-call data (e.g. `dataLoaders.integrationValue(key)`) and
|
|
43
|
+
* theme-original async work that the host has no way to pre-resolve.
|
|
44
|
+
*
|
|
25
45
|
* @param fn - The function to load the data.
|
|
26
46
|
* @param params - Optional parameters to pass to the function.
|
|
27
47
|
* @returns The data and a boolean indicating if the data is loading.
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { createContext, useContext, useEffect, useState } from "react";
|
|
2
|
-
const DataBridgeVersion = '0.
|
|
2
|
+
const DataBridgeVersion = '0.36.1';
|
|
3
3
|
const DataBridgeContext = createContext(void 0);
|
|
4
4
|
const useDataBridge = ()=>{
|
|
5
5
|
const context = useContext(DataBridgeContext);
|
|
6
6
|
if (void 0 === context) throw new Error('useDataBridge must be used within a DataBridgeProvider');
|
|
7
7
|
return context;
|
|
8
8
|
};
|
|
9
|
+
const EMPTY_COMMERCE_DATA = {};
|
|
10
|
+
const useCommerceData = ()=>{
|
|
11
|
+
const { data } = useDataBridge();
|
|
12
|
+
return data ?? EMPTY_COMMERCE_DATA;
|
|
13
|
+
};
|
|
9
14
|
const useAsyncLoader = (fn, params)=>{
|
|
10
15
|
const [data, setData] = useState(null);
|
|
11
16
|
useEffect(()=>{
|
|
@@ -19,4 +24,4 @@ const useAsyncLoader = (fn, params)=>{
|
|
|
19
24
|
isLoading: null === data
|
|
20
25
|
};
|
|
21
26
|
};
|
|
22
|
-
export { DataBridgeContext, DataBridgeVersion, useAsyncLoader, useDataBridge };
|
|
27
|
+
export { DataBridgeContext, DataBridgeVersion, useAsyncLoader, useCommerceData, useDataBridge };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HostLocation } from '../types/interface';
|
|
2
|
+
/**
|
|
3
|
+
* SSR-safe access to the current request's path/query, sourced from the host via the
|
|
4
|
+
* data bridge instead of `window.location`. Populated identically on the server and the
|
|
5
|
+
* client, so unlike `typeof window !== 'undefined' ? window.location.pathname : ''`, this
|
|
6
|
+
* never produces a hydration mismatch (the guarded form renders `''` on the server and
|
|
7
|
+
* the client's first render, then the "real" value only once effects run -- two different
|
|
8
|
+
* outputs for what should be the same render).
|
|
9
|
+
*
|
|
10
|
+
* Returns an empty location (not undefined) when the host hasn't supplied one (e.g. a
|
|
11
|
+
* host that hasn't been upgraded to populate `request` yet), so call sites don't need
|
|
12
|
+
* their own fallback handling.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useHostLocation(): HostLocation;
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
export * from './components/client-only';
|
|
1
2
|
export * from './components/remote-boundary';
|
|
2
3
|
export * from './context/ecommerce-data-bridge';
|
|
4
|
+
export * from './context/use-host-location';
|
|
3
5
|
export * from './types/actions';
|
|
4
6
|
export * from './types/data';
|
|
5
7
|
export * from './types/ecommerce-extension';
|
|
@@ -7,3 +9,6 @@ export * from './types/events';
|
|
|
7
9
|
export * from './types/filter-side-panel';
|
|
8
10
|
export * from './types/interface';
|
|
9
11
|
export * from './types/product-card';
|
|
12
|
+
export * from './utils/environment';
|
|
13
|
+
export * from './utils/safe-storage';
|
|
14
|
+
export * from './utils/use-isomorphic-layout-effect';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
export * from "./components/client-only.js";
|
|
1
2
|
export * from "./components/remote-boundary.js";
|
|
2
3
|
export * from "./context/ecommerce-data-bridge.js";
|
|
4
|
+
export * from "./context/use-host-location.js";
|
|
3
5
|
export * from "./types/actions.js";
|
|
4
6
|
export * from "./types/data.js";
|
|
5
7
|
export * from "./types/ecommerce-extension.js";
|
|
@@ -7,3 +9,6 @@ export * from "./types/events.js";
|
|
|
7
9
|
export * from "./types/filter-side-panel.js";
|
|
8
10
|
export * from "./types/interface.js";
|
|
9
11
|
export * from "./types/product-card.js";
|
|
12
|
+
export * from "./utils/environment.js";
|
|
13
|
+
export * from "./utils/safe-storage.js";
|
|
14
|
+
export * from "./utils/use-isomorphic-layout-effect.js";
|
|
@@ -1,10 +1,69 @@
|
|
|
1
1
|
import type { Actions } from './actions';
|
|
2
|
-
import type { Cart, DataLoaders, Dispensary, MenuContext, User } from './data';
|
|
2
|
+
import type { Brand, Cart, Category, Collection, DataLoaders, Dispensary, LoyaltyData, MenuContext, Product, Special, User } from './data';
|
|
3
|
+
/**
|
|
4
|
+
* The current request's URL info, provided by the host so theme code never needs
|
|
5
|
+
* `window.location` (which is unavailable during server rendering and, even when guarded
|
|
6
|
+
* with a `typeof window` check, causes a hydration mismatch since the server and the
|
|
7
|
+
* client's first render would otherwise disagree). Populated identically on the server
|
|
8
|
+
* and the client -- see `useHostLocation()`.
|
|
9
|
+
*/
|
|
10
|
+
export type HostLocation = {
|
|
11
|
+
/** Pathname only, e.g. `/product/some-product` (no query string). */
|
|
12
|
+
path: string;
|
|
13
|
+
/** Raw query string, including the leading `?` if present, or `''`. */
|
|
14
|
+
search: string;
|
|
15
|
+
/** Request host/domain, when known. May be omitted in contexts where the host can't supply it. */
|
|
16
|
+
host?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* V2 commerce data: plain, already-resolved values instead of promise-returning loader
|
|
20
|
+
* functions (see {@link DataLoaders} / `dataLoaders`).
|
|
21
|
+
*
|
|
22
|
+
* Most `dataLoaders` fields wrap data the host has already fetched for its own
|
|
23
|
+
* (non-themed) rendering of the same page -- `dataLoaders.brands()` and
|
|
24
|
+
* `dataLoaders.categories()`, for example, are `Promise.resolve()`-wrapped values that
|
|
25
|
+
* are never actually asynchronous by the time a theme calls them. Forcing every theme
|
|
26
|
+
* to treat that data as async (via `useAsyncLoader` or hand-rolled promise handling) is
|
|
27
|
+
* unnecessary ceremony, and it's specifically what stood in the way of real SSR: an
|
|
28
|
+
* `useEffect`-based fetch can't produce data during server rendering, but a value that's
|
|
29
|
+
* already sitting in memory doesn't need an effect at all.
|
|
30
|
+
*
|
|
31
|
+
* Prefer this over `dataLoaders` in new theme code. Fields are `undefined` until the
|
|
32
|
+
* host has resolved them for the current page -- check for presence rather than
|
|
33
|
+
* awaiting. See the SSR readiness guide for the full rationale and the `dataLoaders`
|
|
34
|
+
* migration path.
|
|
35
|
+
*/
|
|
36
|
+
export type CommerceDataV2 = {
|
|
37
|
+
brands?: Brand[];
|
|
38
|
+
categories?: Category[];
|
|
39
|
+
collections?: Collection[];
|
|
40
|
+
/** Only populated on a PDP. */
|
|
41
|
+
product?: Product | null;
|
|
42
|
+
products?: Product[];
|
|
43
|
+
specials?: Special[];
|
|
44
|
+
/** All dispensary locations of the chain. */
|
|
45
|
+
locations?: Dispensary[];
|
|
46
|
+
loyalty?: LoyaltyData | null;
|
|
47
|
+
};
|
|
3
48
|
export type CommerceComponentsDataInterface = {
|
|
4
49
|
actions: Actions;
|
|
5
50
|
cart?: Cart;
|
|
51
|
+
/**
|
|
52
|
+
* V2 synchronous commerce data. Prefer this over `dataLoaders` in new theme code --
|
|
53
|
+
* see {@link CommerceDataV2}.
|
|
54
|
+
*/
|
|
55
|
+
data?: CommerceDataV2;
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated Prefer `data` (see {@link CommerceDataV2}) where the field you need is
|
|
58
|
+
* available there. Kept fully functional for backward compatibility -- not being
|
|
59
|
+
* removed, just superseded for new theme code. `integrationValue` has no `data`
|
|
60
|
+
* equivalent (it's a genuinely dynamic, caller-parameterized lookup) and stays here.
|
|
61
|
+
*/
|
|
6
62
|
dataLoaders: DataLoaders;
|
|
63
|
+
/** Note: this is the current dispensary location, not URL info -- see `request` for that. */
|
|
7
64
|
location?: Dispensary;
|
|
8
65
|
menuContext: MenuContext;
|
|
66
|
+
/** Current request URL info (path/search/host). See {@link HostLocation}. */
|
|
67
|
+
request?: HostLocation;
|
|
9
68
|
user?: User;
|
|
10
69
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical environment check. Use this instead of ad-hoc `typeof window !== 'undefined'`
|
|
3
|
+
* checks scattered through theme code -- a single source of truth is easier to audit
|
|
4
|
+
* (and to lint for) than dozens of hand-rolled variants.
|
|
5
|
+
*/
|
|
6
|
+
export declare const isBrowser: () => boolean;
|
|
7
|
+
/** Inverse of {@link isBrowser}, provided so call sites can read either direction naturally. */
|
|
8
|
+
export declare const isServer: () => boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type SafeStorage = {
|
|
2
|
+
getItem: (key: string) => string | null;
|
|
3
|
+
setItem: (key: string, value: string) => void;
|
|
4
|
+
removeItem: (key: string) => void;
|
|
5
|
+
clear: () => void;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* `window.localStorage`, but safe to call unconditionally during server rendering
|
|
9
|
+
* (returns/no-ops instead of throwing `window is not defined`) and safe in
|
|
10
|
+
* environments where storage access itself can throw (private browsing, quota, policy).
|
|
11
|
+
*/
|
|
12
|
+
export declare const safeLocalStorage: SafeStorage;
|
|
13
|
+
/** `window.sessionStorage` counterpart to {@link safeLocalStorage}. */
|
|
14
|
+
export declare const safeSessionStorage: SafeStorage;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { isBrowser } from "./environment.js";
|
|
2
|
+
const noopStorage = {
|
|
3
|
+
getItem: ()=>null,
|
|
4
|
+
setItem: ()=>{},
|
|
5
|
+
removeItem: ()=>{},
|
|
6
|
+
clear: ()=>{}
|
|
7
|
+
};
|
|
8
|
+
function createSafeStorage(getStorage) {
|
|
9
|
+
return {
|
|
10
|
+
getItem: (key)=>{
|
|
11
|
+
if (!isBrowser()) return null;
|
|
12
|
+
try {
|
|
13
|
+
return getStorage().getItem(key);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
setItem: (key, value)=>{
|
|
19
|
+
if (!isBrowser()) return;
|
|
20
|
+
try {
|
|
21
|
+
getStorage().setItem(key, value);
|
|
22
|
+
} catch {}
|
|
23
|
+
},
|
|
24
|
+
removeItem: (key)=>{
|
|
25
|
+
if (!isBrowser()) return;
|
|
26
|
+
try {
|
|
27
|
+
getStorage().removeItem(key);
|
|
28
|
+
} catch {}
|
|
29
|
+
},
|
|
30
|
+
clear: ()=>{
|
|
31
|
+
if (!isBrowser()) return;
|
|
32
|
+
try {
|
|
33
|
+
getStorage().clear();
|
|
34
|
+
} catch {}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const safeLocalStorage = isBrowser() ? createSafeStorage(()=>window.localStorage) : noopStorage;
|
|
39
|
+
const safeSessionStorage = isBrowser() ? createSafeStorage(()=>window.sessionStorage) : noopStorage;
|
|
40
|
+
export { safeLocalStorage, safeSessionStorage };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* `useLayoutEffect` in the browser, `useEffect` on the server.
|
|
4
|
+
*
|
|
5
|
+
* Plain `useLayoutEffect` is a no-op during server rendering and logs a React warning
|
|
6
|
+
* ("useLayoutEffect does nothing on the server"). Use this instead anywhere theme code
|
|
7
|
+
* needs layout-effect timing on the client but must render cleanly on the server.
|
|
8
|
+
*/
|
|
9
|
+
export declare const useIsomorphicLayoutEffect: typeof useEffect;
|
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_modules__ = {
|
|
3
|
+
"./components/client-only": function(module) {
|
|
4
|
+
module.exports = require("./components/client-only.cjs");
|
|
5
|
+
},
|
|
3
6
|
"./components/remote-boundary": function(module) {
|
|
4
7
|
module.exports = require("./components/remote-boundary.cjs");
|
|
5
8
|
},
|
|
6
9
|
"./context/ecommerce-data-bridge": function(module) {
|
|
7
10
|
module.exports = require("./context/ecommerce-data-bridge.cjs");
|
|
8
11
|
},
|
|
12
|
+
"./context/use-host-location": function(module) {
|
|
13
|
+
module.exports = require("./context/use-host-location.cjs");
|
|
14
|
+
},
|
|
9
15
|
"./types/actions": function(module) {
|
|
10
16
|
module.exports = require("./types/actions.cjs");
|
|
11
17
|
},
|
|
@@ -26,6 +32,15 @@ var __webpack_modules__ = {
|
|
|
26
32
|
},
|
|
27
33
|
"./types/product-card": function(module) {
|
|
28
34
|
module.exports = require("./types/product-card.cjs");
|
|
35
|
+
},
|
|
36
|
+
"./utils/environment": function(module) {
|
|
37
|
+
module.exports = require("./utils/environment.cjs");
|
|
38
|
+
},
|
|
39
|
+
"./utils/safe-storage": function(module) {
|
|
40
|
+
module.exports = require("./utils/safe-storage.cjs");
|
|
41
|
+
},
|
|
42
|
+
"./utils/use-isomorphic-layout-effect": function(module) {
|
|
43
|
+
module.exports = require("./utils/use-isomorphic-layout-effect.cjs");
|
|
29
44
|
}
|
|
30
45
|
};
|
|
31
46
|
var __webpack_module_cache__ = {};
|
|
@@ -71,58 +86,88 @@ function __webpack_require__(moduleId) {
|
|
|
71
86
|
var __webpack_exports__ = {};
|
|
72
87
|
(()=>{
|
|
73
88
|
__webpack_require__.r(__webpack_exports__);
|
|
74
|
-
var
|
|
89
|
+
var _components_client_only__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./components/client-only");
|
|
90
|
+
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
91
|
+
for(var __WEBPACK_IMPORT_KEY__ in _components_client_only__WEBPACK_IMPORTED_MODULE_0__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
92
|
+
return _components_client_only__WEBPACK_IMPORTED_MODULE_0__[key];
|
|
93
|
+
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
94
|
+
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
95
|
+
var _components_remote_boundary__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./components/remote-boundary");
|
|
96
|
+
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
97
|
+
for(var __WEBPACK_IMPORT_KEY__ in _components_remote_boundary__WEBPACK_IMPORTED_MODULE_1__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
98
|
+
return _components_remote_boundary__WEBPACK_IMPORTED_MODULE_1__[key];
|
|
99
|
+
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
100
|
+
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
101
|
+
var _context_ecommerce_data_bridge__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("./context/ecommerce-data-bridge");
|
|
102
|
+
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
103
|
+
for(var __WEBPACK_IMPORT_KEY__ in _context_ecommerce_data_bridge__WEBPACK_IMPORTED_MODULE_2__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
104
|
+
return _context_ecommerce_data_bridge__WEBPACK_IMPORTED_MODULE_2__[key];
|
|
105
|
+
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
106
|
+
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
107
|
+
var _context_use_host_location__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./context/use-host-location");
|
|
108
|
+
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
109
|
+
for(var __WEBPACK_IMPORT_KEY__ in _context_use_host_location__WEBPACK_IMPORTED_MODULE_3__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
110
|
+
return _context_use_host_location__WEBPACK_IMPORTED_MODULE_3__[key];
|
|
111
|
+
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
112
|
+
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
113
|
+
var _types_actions__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("./types/actions");
|
|
114
|
+
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
115
|
+
for(var __WEBPACK_IMPORT_KEY__ in _types_actions__WEBPACK_IMPORTED_MODULE_4__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
116
|
+
return _types_actions__WEBPACK_IMPORTED_MODULE_4__[key];
|
|
117
|
+
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
118
|
+
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
119
|
+
var _types_data__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__("./types/data");
|
|
75
120
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
76
|
-
for(var __WEBPACK_IMPORT_KEY__ in
|
|
77
|
-
return
|
|
121
|
+
for(var __WEBPACK_IMPORT_KEY__ in _types_data__WEBPACK_IMPORTED_MODULE_5__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
122
|
+
return _types_data__WEBPACK_IMPORTED_MODULE_5__[key];
|
|
78
123
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
79
124
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
80
|
-
var
|
|
125
|
+
var _types_ecommerce_extension__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__("./types/ecommerce-extension");
|
|
81
126
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
82
|
-
for(var __WEBPACK_IMPORT_KEY__ in
|
|
83
|
-
return
|
|
127
|
+
for(var __WEBPACK_IMPORT_KEY__ in _types_ecommerce_extension__WEBPACK_IMPORTED_MODULE_6__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
128
|
+
return _types_ecommerce_extension__WEBPACK_IMPORTED_MODULE_6__[key];
|
|
84
129
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
85
130
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
86
|
-
var
|
|
131
|
+
var _types_events__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__("./types/events");
|
|
87
132
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
88
|
-
for(var __WEBPACK_IMPORT_KEY__ in
|
|
89
|
-
return
|
|
133
|
+
for(var __WEBPACK_IMPORT_KEY__ in _types_events__WEBPACK_IMPORTED_MODULE_7__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
134
|
+
return _types_events__WEBPACK_IMPORTED_MODULE_7__[key];
|
|
90
135
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
91
136
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
92
|
-
var
|
|
137
|
+
var _types_filter_side_panel__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__("./types/filter-side-panel");
|
|
93
138
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
94
|
-
for(var __WEBPACK_IMPORT_KEY__ in
|
|
95
|
-
return
|
|
139
|
+
for(var __WEBPACK_IMPORT_KEY__ in _types_filter_side_panel__WEBPACK_IMPORTED_MODULE_8__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
140
|
+
return _types_filter_side_panel__WEBPACK_IMPORTED_MODULE_8__[key];
|
|
96
141
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
97
142
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
98
|
-
var
|
|
143
|
+
var _types_interface__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__("./types/interface");
|
|
99
144
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
100
|
-
for(var __WEBPACK_IMPORT_KEY__ in
|
|
101
|
-
return
|
|
145
|
+
for(var __WEBPACK_IMPORT_KEY__ in _types_interface__WEBPACK_IMPORTED_MODULE_9__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
146
|
+
return _types_interface__WEBPACK_IMPORTED_MODULE_9__[key];
|
|
102
147
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
103
148
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
104
|
-
var
|
|
149
|
+
var _types_product_card__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__("./types/product-card");
|
|
105
150
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
106
|
-
for(var __WEBPACK_IMPORT_KEY__ in
|
|
107
|
-
return
|
|
151
|
+
for(var __WEBPACK_IMPORT_KEY__ in _types_product_card__WEBPACK_IMPORTED_MODULE_10__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
152
|
+
return _types_product_card__WEBPACK_IMPORTED_MODULE_10__[key];
|
|
108
153
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
109
154
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
110
|
-
var
|
|
155
|
+
var _utils_environment__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__("./utils/environment");
|
|
111
156
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
112
|
-
for(var __WEBPACK_IMPORT_KEY__ in
|
|
113
|
-
return
|
|
157
|
+
for(var __WEBPACK_IMPORT_KEY__ in _utils_environment__WEBPACK_IMPORTED_MODULE_11__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
158
|
+
return _utils_environment__WEBPACK_IMPORTED_MODULE_11__[key];
|
|
114
159
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
115
160
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
116
|
-
var
|
|
161
|
+
var _utils_safe_storage__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__("./utils/safe-storage");
|
|
117
162
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
118
|
-
for(var __WEBPACK_IMPORT_KEY__ in
|
|
119
|
-
return
|
|
163
|
+
for(var __WEBPACK_IMPORT_KEY__ in _utils_safe_storage__WEBPACK_IMPORTED_MODULE_12__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
164
|
+
return _utils_safe_storage__WEBPACK_IMPORTED_MODULE_12__[key];
|
|
120
165
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
121
166
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
122
|
-
var
|
|
167
|
+
var _utils_use_isomorphic_layout_effect__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__("./utils/use-isomorphic-layout-effect");
|
|
123
168
|
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
124
|
-
for(var __WEBPACK_IMPORT_KEY__ in
|
|
125
|
-
return
|
|
169
|
+
for(var __WEBPACK_IMPORT_KEY__ in _utils_use_isomorphic_layout_effect__WEBPACK_IMPORTED_MODULE_13__)if ("default" !== __WEBPACK_IMPORT_KEY__) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
170
|
+
return _utils_use_isomorphic_layout_effect__WEBPACK_IMPORTED_MODULE_13__[key];
|
|
126
171
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
127
172
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
128
173
|
})();
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
export * from './components/client-only';
|
|
1
2
|
export * from './components/remote-boundary';
|
|
2
3
|
export * from './context/ecommerce-data-bridge';
|
|
4
|
+
export * from './context/use-host-location';
|
|
3
5
|
export * from './types/actions';
|
|
4
6
|
export * from './types/data';
|
|
5
7
|
export * from './types/ecommerce-extension';
|
|
@@ -7,3 +9,6 @@ export * from './types/events';
|
|
|
7
9
|
export * from './types/filter-side-panel';
|
|
8
10
|
export * from './types/interface';
|
|
9
11
|
export * from './types/product-card';
|
|
12
|
+
export * from './utils/environment';
|
|
13
|
+
export * from './utils/safe-storage';
|
|
14
|
+
export * from './utils/use-isomorphic-layout-effect';
|
|
@@ -1,10 +1,69 @@
|
|
|
1
1
|
import type { Actions } from './actions';
|
|
2
|
-
import type { Cart, DataLoaders, Dispensary, MenuContext, User } from './data';
|
|
2
|
+
import type { Brand, Cart, Category, Collection, DataLoaders, Dispensary, LoyaltyData, MenuContext, Product, Special, User } from './data';
|
|
3
|
+
/**
|
|
4
|
+
* The current request's URL info, provided by the host so theme code never needs
|
|
5
|
+
* `window.location` (which is unavailable during server rendering and, even when guarded
|
|
6
|
+
* with a `typeof window` check, causes a hydration mismatch since the server and the
|
|
7
|
+
* client's first render would otherwise disagree). Populated identically on the server
|
|
8
|
+
* and the client -- see `useHostLocation()`.
|
|
9
|
+
*/
|
|
10
|
+
export type HostLocation = {
|
|
11
|
+
/** Pathname only, e.g. `/product/some-product` (no query string). */
|
|
12
|
+
path: string;
|
|
13
|
+
/** Raw query string, including the leading `?` if present, or `''`. */
|
|
14
|
+
search: string;
|
|
15
|
+
/** Request host/domain, when known. May be omitted in contexts where the host can't supply it. */
|
|
16
|
+
host?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* V2 commerce data: plain, already-resolved values instead of promise-returning loader
|
|
20
|
+
* functions (see {@link DataLoaders} / `dataLoaders`).
|
|
21
|
+
*
|
|
22
|
+
* Most `dataLoaders` fields wrap data the host has already fetched for its own
|
|
23
|
+
* (non-themed) rendering of the same page -- `dataLoaders.brands()` and
|
|
24
|
+
* `dataLoaders.categories()`, for example, are `Promise.resolve()`-wrapped values that
|
|
25
|
+
* are never actually asynchronous by the time a theme calls them. Forcing every theme
|
|
26
|
+
* to treat that data as async (via `useAsyncLoader` or hand-rolled promise handling) is
|
|
27
|
+
* unnecessary ceremony, and it's specifically what stood in the way of real SSR: an
|
|
28
|
+
* `useEffect`-based fetch can't produce data during server rendering, but a value that's
|
|
29
|
+
* already sitting in memory doesn't need an effect at all.
|
|
30
|
+
*
|
|
31
|
+
* Prefer this over `dataLoaders` in new theme code. Fields are `undefined` until the
|
|
32
|
+
* host has resolved them for the current page -- check for presence rather than
|
|
33
|
+
* awaiting. See the SSR readiness guide for the full rationale and the `dataLoaders`
|
|
34
|
+
* migration path.
|
|
35
|
+
*/
|
|
36
|
+
export type CommerceDataV2 = {
|
|
37
|
+
brands?: Brand[];
|
|
38
|
+
categories?: Category[];
|
|
39
|
+
collections?: Collection[];
|
|
40
|
+
/** Only populated on a PDP. */
|
|
41
|
+
product?: Product | null;
|
|
42
|
+
products?: Product[];
|
|
43
|
+
specials?: Special[];
|
|
44
|
+
/** All dispensary locations of the chain. */
|
|
45
|
+
locations?: Dispensary[];
|
|
46
|
+
loyalty?: LoyaltyData | null;
|
|
47
|
+
};
|
|
3
48
|
export type CommerceComponentsDataInterface = {
|
|
4
49
|
actions: Actions;
|
|
5
50
|
cart?: Cart;
|
|
51
|
+
/**
|
|
52
|
+
* V2 synchronous commerce data. Prefer this over `dataLoaders` in new theme code --
|
|
53
|
+
* see {@link CommerceDataV2}.
|
|
54
|
+
*/
|
|
55
|
+
data?: CommerceDataV2;
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated Prefer `data` (see {@link CommerceDataV2}) where the field you need is
|
|
58
|
+
* available there. Kept fully functional for backward compatibility -- not being
|
|
59
|
+
* removed, just superseded for new theme code. `integrationValue` has no `data`
|
|
60
|
+
* equivalent (it's a genuinely dynamic, caller-parameterized lookup) and stays here.
|
|
61
|
+
*/
|
|
6
62
|
dataLoaders: DataLoaders;
|
|
63
|
+
/** Note: this is the current dispensary location, not URL info -- see `request` for that. */
|
|
7
64
|
location?: Dispensary;
|
|
8
65
|
menuContext: MenuContext;
|
|
66
|
+
/** Current request URL info (path/search/host). See {@link HostLocation}. */
|
|
67
|
+
request?: HostLocation;
|
|
9
68
|
user?: User;
|
|
10
69
|
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
isBrowser: ()=>isBrowser,
|
|
28
|
+
isServer: ()=>isServer
|
|
29
|
+
});
|
|
30
|
+
const isBrowser = ()=>'undefined' != typeof window && 'undefined' != typeof document;
|
|
31
|
+
const isServer = ()=>!isBrowser();
|
|
32
|
+
exports.isBrowser = __webpack_exports__.isBrowser;
|
|
33
|
+
exports.isServer = __webpack_exports__.isServer;
|
|
34
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
35
|
+
"isBrowser",
|
|
36
|
+
"isServer"
|
|
37
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
38
|
+
Object.defineProperty(exports, '__esModule', {
|
|
39
|
+
value: true
|
|
40
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical environment check. Use this instead of ad-hoc `typeof window !== 'undefined'`
|
|
3
|
+
* checks scattered through theme code -- a single source of truth is easier to audit
|
|
4
|
+
* (and to lint for) than dozens of hand-rolled variants.
|
|
5
|
+
*/
|
|
6
|
+
export declare const isBrowser: () => boolean;
|
|
7
|
+
/** Inverse of {@link isBrowser}, provided so call sites can read either direction naturally. */
|
|
8
|
+
export declare const isServer: () => boolean;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
safeLocalStorage: ()=>safeLocalStorage,
|
|
28
|
+
safeSessionStorage: ()=>safeSessionStorage
|
|
29
|
+
});
|
|
30
|
+
const external_environment_cjs_namespaceObject = require("./environment.cjs");
|
|
31
|
+
const noopStorage = {
|
|
32
|
+
getItem: ()=>null,
|
|
33
|
+
setItem: ()=>{},
|
|
34
|
+
removeItem: ()=>{},
|
|
35
|
+
clear: ()=>{}
|
|
36
|
+
};
|
|
37
|
+
function createSafeStorage(getStorage) {
|
|
38
|
+
return {
|
|
39
|
+
getItem: (key)=>{
|
|
40
|
+
if (!(0, external_environment_cjs_namespaceObject.isBrowser)()) return null;
|
|
41
|
+
try {
|
|
42
|
+
return getStorage().getItem(key);
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
setItem: (key, value)=>{
|
|
48
|
+
if (!(0, external_environment_cjs_namespaceObject.isBrowser)()) return;
|
|
49
|
+
try {
|
|
50
|
+
getStorage().setItem(key, value);
|
|
51
|
+
} catch {}
|
|
52
|
+
},
|
|
53
|
+
removeItem: (key)=>{
|
|
54
|
+
if (!(0, external_environment_cjs_namespaceObject.isBrowser)()) return;
|
|
55
|
+
try {
|
|
56
|
+
getStorage().removeItem(key);
|
|
57
|
+
} catch {}
|
|
58
|
+
},
|
|
59
|
+
clear: ()=>{
|
|
60
|
+
if (!(0, external_environment_cjs_namespaceObject.isBrowser)()) return;
|
|
61
|
+
try {
|
|
62
|
+
getStorage().clear();
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const safeLocalStorage = (0, external_environment_cjs_namespaceObject.isBrowser)() ? createSafeStorage(()=>window.localStorage) : noopStorage;
|
|
68
|
+
const safeSessionStorage = (0, external_environment_cjs_namespaceObject.isBrowser)() ? createSafeStorage(()=>window.sessionStorage) : noopStorage;
|
|
69
|
+
exports.safeLocalStorage = __webpack_exports__.safeLocalStorage;
|
|
70
|
+
exports.safeSessionStorage = __webpack_exports__.safeSessionStorage;
|
|
71
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
72
|
+
"safeLocalStorage",
|
|
73
|
+
"safeSessionStorage"
|
|
74
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
75
|
+
Object.defineProperty(exports, '__esModule', {
|
|
76
|
+
value: true
|
|
77
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type SafeStorage = {
|
|
2
|
+
getItem: (key: string) => string | null;
|
|
3
|
+
setItem: (key: string, value: string) => void;
|
|
4
|
+
removeItem: (key: string) => void;
|
|
5
|
+
clear: () => void;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* `window.localStorage`, but safe to call unconditionally during server rendering
|
|
9
|
+
* (returns/no-ops instead of throwing `window is not defined`) and safe in
|
|
10
|
+
* environments where storage access itself can throw (private browsing, quota, policy).
|
|
11
|
+
*/
|
|
12
|
+
export declare const safeLocalStorage: SafeStorage;
|
|
13
|
+
/** `window.sessionStorage` counterpart to {@link safeLocalStorage}. */
|
|
14
|
+
export declare const safeSessionStorage: SafeStorage;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
useIsomorphicLayoutEffect: ()=>useIsomorphicLayoutEffect
|
|
28
|
+
});
|
|
29
|
+
const external_react_namespaceObject = require("react");
|
|
30
|
+
const external_environment_cjs_namespaceObject = require("./environment.cjs");
|
|
31
|
+
const useIsomorphicLayoutEffect = (0, external_environment_cjs_namespaceObject.isBrowser)() ? external_react_namespaceObject.useLayoutEffect : external_react_namespaceObject.useEffect;
|
|
32
|
+
exports.useIsomorphicLayoutEffect = __webpack_exports__.useIsomorphicLayoutEffect;
|
|
33
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
34
|
+
"useIsomorphicLayoutEffect"
|
|
35
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
36
|
+
Object.defineProperty(exports, '__esModule', {
|
|
37
|
+
value: true
|
|
38
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* `useLayoutEffect` in the browser, `useEffect` on the server.
|
|
4
|
+
*
|
|
5
|
+
* Plain `useLayoutEffect` is a no-op during server rendering and logs a React warning
|
|
6
|
+
* ("useLayoutEffect does nothing on the server"). Use this instead anywhere theme code
|
|
7
|
+
* needs layout-effect timing on the client but must render cleanly on the server.
|
|
8
|
+
*/
|
|
9
|
+
export declare const useIsomorphicLayoutEffect: typeof useEffect;
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"url": "git+https://github.com/GetDutchie/ecommerce-extensions-sdk.git",
|
|
10
10
|
"directory": "packages/sdk"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.
|
|
12
|
+
"version": "0.36.1",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"type": "module",
|
|
15
15
|
"module": "./dist/esm/index.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@testing-library/react": "^12.1.5",
|
|
42
42
|
"@testing-library/react-hooks": "^8.0.1",
|
|
43
43
|
"@types/react": "^17.0.0",
|
|
44
|
+
"@types/react-dom": "^17.0.0",
|
|
44
45
|
"jsdom": "^26.1.0",
|
|
45
46
|
"react": "^17.0.0",
|
|
46
47
|
"react-dom": "^17.0.0",
|