@shopify/hydrogen-react 2024.10.0 → 2024.10.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.
Files changed (95) hide show
  1. package/dist/browser-dev/AddToCartButton.mjs.map +1 -1
  2. package/dist/browser-dev/BuyNowButton.mjs.map +1 -1
  3. package/dist/browser-dev/CartCost.mjs.map +1 -1
  4. package/dist/browser-dev/CartProvider.mjs.map +1 -1
  5. package/dist/browser-dev/ExternalVideo.mjs.map +1 -1
  6. package/dist/browser-dev/ProductPrice.mjs.map +1 -1
  7. package/dist/browser-dev/RichText.mjs.map +1 -1
  8. package/dist/browser-dev/getProductOptions.mjs +239 -0
  9. package/dist/browser-dev/getProductOptions.mjs.map +1 -0
  10. package/dist/browser-dev/index.mjs +6 -0
  11. package/dist/browser-dev/index.mjs.map +1 -1
  12. package/dist/browser-dev/packages/hydrogen-react/package.json.mjs +1 -1
  13. package/dist/browser-dev/useSelectedOptionInUrlParam.mjs +30 -0
  14. package/dist/browser-dev/useSelectedOptionInUrlParam.mjs.map +1 -0
  15. package/dist/browser-prod/AddToCartButton.mjs.map +1 -1
  16. package/dist/browser-prod/BuyNowButton.mjs.map +1 -1
  17. package/dist/browser-prod/CartCost.mjs.map +1 -1
  18. package/dist/browser-prod/CartProvider.mjs.map +1 -1
  19. package/dist/browser-prod/ExternalVideo.mjs.map +1 -1
  20. package/dist/browser-prod/ProductPrice.mjs.map +1 -1
  21. package/dist/browser-prod/RichText.mjs.map +1 -1
  22. package/dist/browser-prod/getProductOptions.mjs +239 -0
  23. package/dist/browser-prod/getProductOptions.mjs.map +1 -0
  24. package/dist/browser-prod/index.mjs +6 -0
  25. package/dist/browser-prod/index.mjs.map +1 -1
  26. package/dist/browser-prod/packages/hydrogen-react/package.json.mjs +1 -1
  27. package/dist/browser-prod/useSelectedOptionInUrlParam.mjs +30 -0
  28. package/dist/browser-prod/useSelectedOptionInUrlParam.mjs.map +1 -0
  29. package/dist/node-dev/AddToCartButton.js.map +1 -1
  30. package/dist/node-dev/AddToCartButton.mjs.map +1 -1
  31. package/dist/node-dev/BuyNowButton.js.map +1 -1
  32. package/dist/node-dev/BuyNowButton.mjs.map +1 -1
  33. package/dist/node-dev/CartCost.js.map +1 -1
  34. package/dist/node-dev/CartCost.mjs.map +1 -1
  35. package/dist/node-dev/CartProvider.js.map +1 -1
  36. package/dist/node-dev/CartProvider.mjs.map +1 -1
  37. package/dist/node-dev/ExternalVideo.js.map +1 -1
  38. package/dist/node-dev/ExternalVideo.mjs.map +1 -1
  39. package/dist/node-dev/ProductPrice.js.map +1 -1
  40. package/dist/node-dev/ProductPrice.mjs.map +1 -1
  41. package/dist/node-dev/RichText.js.map +1 -1
  42. package/dist/node-dev/RichText.mjs.map +1 -1
  43. package/dist/node-dev/getProductOptions.js +239 -0
  44. package/dist/node-dev/getProductOptions.js.map +1 -0
  45. package/dist/node-dev/getProductOptions.mjs +239 -0
  46. package/dist/node-dev/getProductOptions.mjs.map +1 -0
  47. package/dist/node-dev/index.js +6 -0
  48. package/dist/node-dev/index.js.map +1 -1
  49. package/dist/node-dev/index.mjs +6 -0
  50. package/dist/node-dev/index.mjs.map +1 -1
  51. package/dist/node-dev/packages/hydrogen-react/package.json.js +1 -1
  52. package/dist/node-dev/packages/hydrogen-react/package.json.mjs +1 -1
  53. package/dist/node-dev/useSelectedOptionInUrlParam.js +30 -0
  54. package/dist/node-dev/useSelectedOptionInUrlParam.js.map +1 -0
  55. package/dist/node-dev/useSelectedOptionInUrlParam.mjs +30 -0
  56. package/dist/node-dev/useSelectedOptionInUrlParam.mjs.map +1 -0
  57. package/dist/node-prod/AddToCartButton.js.map +1 -1
  58. package/dist/node-prod/AddToCartButton.mjs.map +1 -1
  59. package/dist/node-prod/BuyNowButton.js.map +1 -1
  60. package/dist/node-prod/BuyNowButton.mjs.map +1 -1
  61. package/dist/node-prod/CartCost.js.map +1 -1
  62. package/dist/node-prod/CartCost.mjs.map +1 -1
  63. package/dist/node-prod/CartProvider.js.map +1 -1
  64. package/dist/node-prod/CartProvider.mjs.map +1 -1
  65. package/dist/node-prod/ExternalVideo.js.map +1 -1
  66. package/dist/node-prod/ExternalVideo.mjs.map +1 -1
  67. package/dist/node-prod/ProductPrice.js.map +1 -1
  68. package/dist/node-prod/ProductPrice.mjs.map +1 -1
  69. package/dist/node-prod/RichText.js.map +1 -1
  70. package/dist/node-prod/RichText.mjs.map +1 -1
  71. package/dist/node-prod/getProductOptions.js +239 -0
  72. package/dist/node-prod/getProductOptions.js.map +1 -0
  73. package/dist/node-prod/getProductOptions.mjs +239 -0
  74. package/dist/node-prod/getProductOptions.mjs.map +1 -0
  75. package/dist/node-prod/index.js +6 -0
  76. package/dist/node-prod/index.js.map +1 -1
  77. package/dist/node-prod/index.mjs +6 -0
  78. package/dist/node-prod/index.mjs.map +1 -1
  79. package/dist/node-prod/packages/hydrogen-react/package.json.js +1 -1
  80. package/dist/node-prod/packages/hydrogen-react/package.json.mjs +1 -1
  81. package/dist/node-prod/useSelectedOptionInUrlParam.js +30 -0
  82. package/dist/node-prod/useSelectedOptionInUrlParam.js.map +1 -0
  83. package/dist/node-prod/useSelectedOptionInUrlParam.mjs +30 -0
  84. package/dist/node-prod/useSelectedOptionInUrlParam.mjs.map +1 -0
  85. package/dist/types/CartProvider.d.ts +4 -3
  86. package/dist/types/getProductOptions.d.ts +49 -0
  87. package/dist/types/index.d.cts +2 -0
  88. package/dist/types/index.d.ts +2 -0
  89. package/dist/types/storefront-api-response.types.d.ts +5 -5
  90. package/dist/types/useSelectedOptionInUrlParam.d.ts +2 -0
  91. package/dist/umd/hydrogen-react.dev.js +346 -87
  92. package/dist/umd/hydrogen-react.dev.js.map +1 -1
  93. package/dist/umd/hydrogen-react.prod.js +18 -18
  94. package/dist/umd/hydrogen-react.prod.js.map +1 -1
  95. package/package.json +1 -1
@@ -0,0 +1,239 @@
1
+ import { isOptionValueCombinationInEncodedVariant } from "./optionValueDecoder.mjs";
2
+ function mapProductOptions(options) {
3
+ return options.map((option) => {
4
+ return Object.assign(
5
+ {},
6
+ ...(option == null ? void 0 : option.optionValues) ? option.optionValues.map((value, index) => {
7
+ return { [value.name]: index };
8
+ }) : []
9
+ );
10
+ });
11
+ }
12
+ function mapSelectedProductOptionToObject(options) {
13
+ return Object.assign(
14
+ {},
15
+ ...options.map((key) => {
16
+ return { [key.name]: key.value };
17
+ })
18
+ );
19
+ }
20
+ function mapSelectedProductOptionToObjectAsString(options) {
21
+ return JSON.stringify(mapSelectedProductOptionToObject(options));
22
+ }
23
+ function encodeSelectedProductOptionAsKey(selectedOption, productOptionMappings) {
24
+ if (Array.isArray(selectedOption)) {
25
+ return JSON.stringify(
26
+ selectedOption.map((key, index) => {
27
+ return productOptionMappings[index][key.value];
28
+ })
29
+ );
30
+ } else {
31
+ return JSON.stringify(
32
+ Object.keys(selectedOption).map((key, index) => {
33
+ return productOptionMappings[index][selectedOption[key]];
34
+ })
35
+ );
36
+ }
37
+ }
38
+ function mapVariants(variants, productOptionMappings) {
39
+ return Object.assign(
40
+ {},
41
+ ...variants.map((variant) => {
42
+ const variantKey = encodeSelectedProductOptionAsKey(
43
+ variant.selectedOptions || [],
44
+ productOptionMappings
45
+ );
46
+ return { [variantKey]: variant };
47
+ })
48
+ );
49
+ }
50
+ const PRODUCT_INPUTS = [
51
+ "options",
52
+ "selectedOrFirstAvailableVariant",
53
+ "adjacentVariants"
54
+ ];
55
+ const PRODUCT_INPUTS_EXTRA = [
56
+ "handle",
57
+ "encodedVariantExistence",
58
+ "encodedVariantAvailability"
59
+ ];
60
+ function logErrorAndReturnFalse(key) {
61
+ console.error(
62
+ `[h2:error:getProductOptions] product.${key} is missing. Make sure you query for this field from the Storefront API.`
63
+ );
64
+ return false;
65
+ }
66
+ function checkProductParam(product, checkAll = false) {
67
+ var _a;
68
+ let validParam = true;
69
+ const productKeys = Object.keys(product);
70
+ (checkAll ? [...PRODUCT_INPUTS, ...PRODUCT_INPUTS_EXTRA] : PRODUCT_INPUTS).forEach((key) => {
71
+ if (!productKeys.includes(key)) {
72
+ validParam = logErrorAndReturnFalse(key);
73
+ }
74
+ });
75
+ if (product.options) {
76
+ const firstOption = product == null ? void 0 : product.options[0];
77
+ if (checkAll && !(firstOption == null ? void 0 : firstOption.name)) {
78
+ validParam = logErrorAndReturnFalse("options.name");
79
+ }
80
+ if ((_a = product == null ? void 0 : product.options[0]) == null ? void 0 : _a.optionValues) {
81
+ const firstOptionValues = product.options[0].optionValues[0];
82
+ if (checkAll && !(firstOptionValues == null ? void 0 : firstOptionValues.name)) {
83
+ validParam = logErrorAndReturnFalse("options.optionValues.name");
84
+ }
85
+ if (firstOptionValues == null ? void 0 : firstOptionValues.firstSelectableVariant) {
86
+ validParam = checkProductVariantParam(
87
+ firstOptionValues.firstSelectableVariant,
88
+ "options.optionValues.firstSelectableVariant",
89
+ validParam,
90
+ checkAll
91
+ );
92
+ } else {
93
+ validParam = logErrorAndReturnFalse(
94
+ "options.optionValues.firstSelectableVariant"
95
+ );
96
+ }
97
+ } else {
98
+ validParam = logErrorAndReturnFalse("options.optionValues");
99
+ }
100
+ }
101
+ if (product.selectedOrFirstAvailableVariant) {
102
+ validParam = checkProductVariantParam(
103
+ product.selectedOrFirstAvailableVariant,
104
+ "selectedOrFirstAvailableVariant",
105
+ validParam,
106
+ checkAll
107
+ );
108
+ }
109
+ if (!!product.adjacentVariants && product.adjacentVariants[0]) {
110
+ validParam = checkProductVariantParam(
111
+ product.adjacentVariants[0],
112
+ "adjacentVariants",
113
+ validParam,
114
+ checkAll
115
+ );
116
+ }
117
+ return validParam ? product : {};
118
+ }
119
+ function checkProductVariantParam(variant, key, currentValidParamState, checkAll) {
120
+ var _a;
121
+ let validParam = currentValidParamState;
122
+ if (checkAll && !((_a = variant.product) == null ? void 0 : _a.handle)) {
123
+ validParam = logErrorAndReturnFalse(`${key}.product.handle`);
124
+ }
125
+ if (variant.selectedOptions) {
126
+ const firstSelectedOption = variant.selectedOptions[0];
127
+ if (!(firstSelectedOption == null ? void 0 : firstSelectedOption.name)) {
128
+ validParam = logErrorAndReturnFalse(`${key}.selectedOptions.name`);
129
+ }
130
+ if (!(firstSelectedOption == null ? void 0 : firstSelectedOption.value)) {
131
+ validParam = logErrorAndReturnFalse(`${key}.selectedOptions.value`);
132
+ }
133
+ } else {
134
+ validParam = logErrorAndReturnFalse(`${key}.selectedOptions`);
135
+ }
136
+ return validParam;
137
+ }
138
+ function getAdjacentAndFirstAvailableVariants(product) {
139
+ const checkedProduct = checkProductParam(product);
140
+ if (!checkedProduct.options)
141
+ return [];
142
+ const availableVariants = {};
143
+ checkedProduct.options.map((option) => {
144
+ var _a;
145
+ (_a = option.optionValues) == null ? void 0 : _a.map((value) => {
146
+ if (value.firstSelectableVariant) {
147
+ const variantKey = mapSelectedProductOptionToObjectAsString(
148
+ value.firstSelectableVariant.selectedOptions
149
+ );
150
+ availableVariants[variantKey] = value.firstSelectableVariant;
151
+ }
152
+ });
153
+ });
154
+ checkedProduct.adjacentVariants.map((variant) => {
155
+ const variantKey = mapSelectedProductOptionToObjectAsString(
156
+ variant.selectedOptions
157
+ );
158
+ availableVariants[variantKey] = variant;
159
+ });
160
+ const selectedVariant = checkedProduct.selectedOrFirstAvailableVariant;
161
+ if (selectedVariant) {
162
+ const variantKey = mapSelectedProductOptionToObjectAsString(
163
+ selectedVariant.selectedOptions
164
+ );
165
+ availableVariants[variantKey] = selectedVariant;
166
+ }
167
+ return Object.values(availableVariants);
168
+ }
169
+ function getProductOptions(product) {
170
+ const checkedProduct = checkProductParam(product, true);
171
+ if (!checkedProduct.options)
172
+ return [];
173
+ const {
174
+ options,
175
+ selectedOrFirstAvailableVariant: selectedVariant,
176
+ adjacentVariants,
177
+ encodedVariantExistence,
178
+ encodedVariantAvailability,
179
+ handle: productHandle
180
+ } = checkedProduct;
181
+ const productOptionMappings = mapProductOptions(options);
182
+ const variants = mapVariants(
183
+ selectedVariant ? [selectedVariant, ...adjacentVariants] : adjacentVariants,
184
+ productOptionMappings
185
+ );
186
+ const selectedOptions = mapSelectedProductOptionToObject(
187
+ selectedVariant ? selectedVariant.selectedOptions : []
188
+ );
189
+ const productOptions = options.map((option, optionIndex) => {
190
+ return {
191
+ ...option,
192
+ optionValues: option.optionValues.map((value) => {
193
+ var _a;
194
+ const targetOptionParams = { ...selectedOptions };
195
+ targetOptionParams[option.name] = value.name;
196
+ const targetKey = encodeSelectedProductOptionAsKey(
197
+ targetOptionParams || [],
198
+ productOptionMappings
199
+ );
200
+ const topDownKey = JSON.parse(targetKey).slice(
201
+ 0,
202
+ optionIndex + 1
203
+ );
204
+ const exists = isOptionValueCombinationInEncodedVariant(
205
+ topDownKey,
206
+ encodedVariantExistence || ""
207
+ );
208
+ const available = isOptionValueCombinationInEncodedVariant(
209
+ topDownKey,
210
+ encodedVariantAvailability || ""
211
+ );
212
+ const variant = variants[targetKey] || value.firstSelectableVariant;
213
+ const variantOptionParam = mapSelectedProductOptionToObject(
214
+ variant.selectedOptions || []
215
+ );
216
+ const searchParams = new URLSearchParams(variantOptionParam);
217
+ const handle = (_a = variant == null ? void 0 : variant.product) == null ? void 0 : _a.handle;
218
+ return {
219
+ ...value,
220
+ variant,
221
+ handle,
222
+ variantUriQuery: searchParams.toString(),
223
+ selected: selectedOptions[option.name] === value.name,
224
+ exists,
225
+ available,
226
+ isDifferentProduct: handle !== productHandle
227
+ };
228
+ })
229
+ };
230
+ });
231
+ return productOptions;
232
+ }
233
+ export {
234
+ checkProductParam,
235
+ getAdjacentAndFirstAvailableVariants,
236
+ getProductOptions,
237
+ mapSelectedProductOptionToObject
238
+ };
239
+ //# sourceMappingURL=getProductOptions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getProductOptions.mjs","sources":["../../src/getProductOptions.ts"],"sourcesContent":["import {isOptionValueCombinationInEncodedVariant} from './optionValueDecoder.js';\nimport type {\n Product,\n ProductOption,\n ProductOptionValue,\n ProductVariant,\n SelectedOption,\n} from './storefront-api-types';\n\nexport type RecursivePartial<T> = {\n [P in keyof T]?: RecursivePartial<T[P]>;\n};\ntype ProductOptionsMapping = Record<string, number>;\ntype ProductOptionValueState = {\n variant: ProductVariant;\n handle: string;\n variantUriQuery: string;\n selected: boolean;\n exists: boolean;\n available: boolean;\n isDifferentProduct: boolean;\n};\ntype MappedProductOptionValue = ProductOptionValue & ProductOptionValueState;\n\n/**\n * Creates a mapping of product options to their index for matching encoded values\n * For example, a product option of\n * [\n * \\{\n * name: 'Color',\n * optionValues: [\\{name: 'Red'\\}, \\{name: 'Blue'\\}]\n * \\},\n * \\{\n * name: 'Size',\n * optionValues: [\\{name: 'Small'\\}, \\{name: 'Medium'\\}, \\{name: 'Large'\\}]\n * \\}\n * ]\n * Would return\n * [\n * \\{Red: 0, Blue: 1\\},\n * \\{Small: 0, Medium: 1, Large: 2\\}\n * ]\n */\nfunction mapProductOptions(options: ProductOption[]): ProductOptionsMapping[] {\n return options.map((option: ProductOption) => {\n return Object.assign(\n {},\n ...(option?.optionValues\n ? option.optionValues.map((value, index) => {\n return {[value.name]: index};\n })\n : []),\n ) as ProductOptionsMapping;\n });\n}\n\n/**\n * Converts the product option into an Object\\<key, value\\> for building query params\n * For example, a selected product option of\n * [\n * \\{\n * name: 'Color',\n * value: 'Red',\n * \\},\n * \\{\n * name: 'Size',\n * value: 'Medium',\n * \\}\n * ]\n * Would return\n * \\{\n * Color: 'Red',\n * Size: 'Medium',\n * \\}\n */\nexport function mapSelectedProductOptionToObject(\n options: Pick<SelectedOption, 'name' | 'value'>[],\n): Record<string, string> {\n return Object.assign(\n {},\n ...options.map((key) => {\n return {[key.name]: key.value};\n }),\n ) as Record<string, string>;\n}\n\n/**\n * Returns the JSON stringify result of mapSelectedProductOptionToObject\n */\nfunction mapSelectedProductOptionToObjectAsString(\n options: Pick<SelectedOption, 'name' | 'value'>[],\n): string {\n return JSON.stringify(mapSelectedProductOptionToObject(options));\n}\n\n/**\n * Encode the selected product option as a key for mapping to the encoded variants\n * For example, a selected product option of\n * [\n * \\{\n * name: 'Color',\n * value: 'Red',\n * \\},\n * \\{\n * name: 'Size',\n * value: 'Medium',\n * \\}\n * ]\n * Would return\n * [0,1]\n *\n * Also works with the result of mapSelectedProductOption. For example:\n * \\{\n * Color: 'Red',\n * Size: 'Medium',\n * \\}\n * Would return\n * [0,1]\n *\n * @param selectedOption - The selected product option\n * @param productOptionMappings - The result of product option mapping from mapProductOptions\n * @returns\n */\nfunction encodeSelectedProductOptionAsKey(\n selectedOption:\n | Pick<SelectedOption, 'name' | 'value'>[]\n | Record<string, string>,\n productOptionMappings: ProductOptionsMapping[],\n): string {\n if (Array.isArray(selectedOption)) {\n return JSON.stringify(\n selectedOption.map((key, index) => {\n return productOptionMappings[index][key.value];\n }),\n );\n } else {\n return JSON.stringify(\n Object.keys(selectedOption).map((key, index) => {\n return productOptionMappings[index][selectedOption[key]];\n }),\n );\n }\n}\n\n/**\n * Takes an array of product variants and maps them to an object with the encoded selected option values as the key.\n * For example, a product variant of\n * [\n * \\{\n * id: 1,\n * selectedOptions: [\n * \\{name: 'Color', value: 'Red'\\},\n * \\{name: 'Size', value: 'Small'\\},\n * ],\n * \\},\n * \\{\n * id: 2,\n * selectedOptions: [\n * \\{name: 'Color', value: 'Red'\\},\n * \\{name: 'Size', value: 'Medium'\\},\n * ],\n * \\}\n * ]\n * Would return\n * \\{\n * '[0,0]': \\{id: 1, selectedOptions: [\\{name: 'Color', value: 'Red'\\}, \\{name: 'Size', value: 'Small'\\}]\\},\n * '[0,1]': \\{id: 2, selectedOptions: [\\{name: 'Color', value: 'Red'\\}, \\{name: 'Size', value: 'Medium'\\}]\\},\n * \\}\n */\nfunction mapVariants(\n variants: ProductVariant[],\n productOptionMappings: ProductOptionsMapping[],\n): Record<string, ProductVariant> {\n return Object.assign(\n {},\n ...variants.map((variant) => {\n const variantKey = encodeSelectedProductOptionAsKey(\n variant.selectedOptions || [],\n productOptionMappings,\n );\n return {[variantKey]: variant};\n }),\n ) as Record<string, ProductVariant>;\n}\n\nexport type MappedProductOptions = Omit<ProductOption, 'optionValues'> & {\n optionValues: MappedProductOptionValue[];\n};\n\nconst PRODUCT_INPUTS = [\n 'options',\n 'selectedOrFirstAvailableVariant',\n 'adjacentVariants',\n];\n\nconst PRODUCT_INPUTS_EXTRA = [\n 'handle',\n 'encodedVariantExistence',\n 'encodedVariantAvailability',\n];\n\nfunction logErrorAndReturnFalse(key: string): boolean {\n console.error(\n `[h2:error:getProductOptions] product.${key} is missing. Make sure you query for this field from the Storefront API.`,\n );\n return false;\n}\n\nexport function checkProductParam(\n product: RecursivePartial<Product>,\n checkAll = false,\n): Product {\n let validParam = true;\n const productKeys = Object.keys(product);\n\n // Check product input\n (checkAll\n ? [...PRODUCT_INPUTS, ...PRODUCT_INPUTS_EXTRA]\n : PRODUCT_INPUTS\n ).forEach((key) => {\n if (!productKeys.includes(key)) {\n validParam = logErrorAndReturnFalse(key);\n }\n });\n\n // Check for nested options requirements\n if (product.options) {\n const firstOption = product?.options[0];\n\n if (checkAll && !firstOption?.name) {\n validParam = logErrorAndReturnFalse('options.name');\n }\n\n // Check for options.optionValues\n if (product?.options[0]?.optionValues) {\n const firstOptionValues = product.options[0].optionValues[0];\n\n // Check for options.optionValues.name\n if (checkAll && !firstOptionValues?.name) {\n validParam = logErrorAndReturnFalse('options.optionValues.name');\n }\n\n // Check for options.optionValues.firstSelectableVariant\n if (firstOptionValues?.firstSelectableVariant) {\n // check product variant\n validParam = checkProductVariantParam(\n firstOptionValues.firstSelectableVariant,\n 'options.optionValues.firstSelectableVariant',\n validParam,\n checkAll,\n );\n } else {\n validParam = logErrorAndReturnFalse(\n 'options.optionValues.firstSelectableVariant',\n );\n }\n } else {\n validParam = logErrorAndReturnFalse('options.optionValues');\n }\n }\n\n // Check for nested selectedOrFirstAvailableVariant requirements\n if (product.selectedOrFirstAvailableVariant) {\n validParam = checkProductVariantParam(\n product.selectedOrFirstAvailableVariant,\n 'selectedOrFirstAvailableVariant',\n validParam,\n checkAll,\n );\n }\n\n // Check for nested adjacentVariants requirements\n if (!!product.adjacentVariants && product.adjacentVariants[0]) {\n validParam = checkProductVariantParam(\n product.adjacentVariants[0],\n 'adjacentVariants',\n validParam,\n checkAll,\n );\n }\n\n return (validParam ? product : {}) as Product;\n}\n\nfunction checkProductVariantParam(\n variant: RecursivePartial<ProductVariant>,\n key: string,\n currentValidParamState: boolean,\n checkAll: boolean,\n): boolean {\n let validParam = currentValidParamState;\n\n if (checkAll && !variant.product?.handle) {\n validParam = logErrorAndReturnFalse(`${key}.product.handle`);\n }\n if (variant.selectedOptions) {\n const firstSelectedOption = variant.selectedOptions[0];\n if (!firstSelectedOption?.name) {\n validParam = logErrorAndReturnFalse(`${key}.selectedOptions.name`);\n }\n if (!firstSelectedOption?.value) {\n validParam = logErrorAndReturnFalse(`${key}.selectedOptions.value`);\n }\n } else {\n validParam = logErrorAndReturnFalse(`${key}.selectedOptions`);\n }\n\n return validParam;\n}\n\n/**\n * Finds all the variants provided by adjacentVariants, options.optionValues.firstAvailableVariant,\n * and selectedOrFirstAvailableVariant and return them in a single array\n */\nexport function getAdjacentAndFirstAvailableVariants(\n product: RecursivePartial<Product>,\n): ProductVariant[] {\n // Checks for valid product input\n const checkedProduct = checkProductParam(product);\n\n if (!checkedProduct.options) return [];\n\n const availableVariants: Record<string, ProductVariant> = {};\n checkedProduct.options.map((option) => {\n option.optionValues?.map((value) => {\n if (value.firstSelectableVariant) {\n const variantKey = mapSelectedProductOptionToObjectAsString(\n value.firstSelectableVariant.selectedOptions,\n );\n availableVariants[variantKey] = value.firstSelectableVariant;\n }\n });\n });\n\n checkedProduct.adjacentVariants.map((variant) => {\n const variantKey = mapSelectedProductOptionToObjectAsString(\n variant.selectedOptions,\n );\n availableVariants[variantKey] = variant;\n });\n\n const selectedVariant = checkedProduct.selectedOrFirstAvailableVariant;\n if (selectedVariant) {\n const variantKey = mapSelectedProductOptionToObjectAsString(\n selectedVariant.selectedOptions,\n );\n availableVariants[variantKey] = selectedVariant;\n }\n\n return Object.values(availableVariants);\n}\n\n/**\n * Returns a product options array with its relevant information\n * about the variant\n */\nexport function getProductOptions(\n product: RecursivePartial<Product>,\n): MappedProductOptions[] {\n // Checks for valid product input\n const checkedProduct = checkProductParam(product, true);\n\n if (!checkedProduct.options) return [];\n\n const {\n options,\n selectedOrFirstAvailableVariant: selectedVariant,\n adjacentVariants,\n encodedVariantExistence,\n encodedVariantAvailability,\n handle: productHandle,\n } = checkedProduct;\n // Get a mapping of product option names to their index for matching encoded values\n const productOptionMappings = mapProductOptions(options);\n\n // Get the adjacent variants mapped to the encoded selected option values\n const variants = mapVariants(\n selectedVariant ? [selectedVariant, ...adjacentVariants] : adjacentVariants,\n productOptionMappings,\n );\n\n // Get the key:value version of selected options for building url query params\n const selectedOptions = mapSelectedProductOptionToObject(\n selectedVariant ? selectedVariant.selectedOptions : [],\n );\n\n const productOptions = options.map((option, optionIndex) => {\n return {\n ...option,\n optionValues: option.optionValues.map((value) => {\n const targetOptionParams = {...selectedOptions}; // Clones the selected options\n\n // Modify the selected option value to the current option value\n targetOptionParams[option.name] = value.name;\n\n // Encode the new selected option values as a key for mapping to the product variants\n const targetKey = encodeSelectedProductOptionAsKey(\n targetOptionParams || [],\n productOptionMappings,\n );\n\n // Top-down option check for existence and availability\n const topDownKey = (JSON.parse(targetKey) as number[]).slice(\n 0,\n optionIndex + 1,\n );\n const exists = isOptionValueCombinationInEncodedVariant(\n topDownKey,\n encodedVariantExistence || '',\n );\n const available = isOptionValueCombinationInEncodedVariant(\n topDownKey,\n encodedVariantAvailability || '',\n );\n\n // Get the variant for the current option value if exists, else use the first selectable variant\n const variant: ProductVariant =\n variants[targetKey] || value.firstSelectableVariant;\n\n // Build the query params for this option value\n const variantOptionParam = mapSelectedProductOptionToObject(\n variant.selectedOptions || [],\n );\n const searchParams = new URLSearchParams(variantOptionParam);\n const handle = variant?.product?.handle;\n\n return {\n ...value,\n variant,\n handle,\n variantUriQuery: searchParams.toString(),\n selected: selectedOptions[option.name] === value.name,\n exists,\n available,\n isDifferentProduct: handle !== productHandle,\n };\n }),\n };\n });\n\n return productOptions;\n}\n"],"names":[],"mappings":"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"}
@@ -16,6 +16,7 @@ const codegen_helpers = require("./codegen.helpers.js");
16
16
  const cookiesUtils = require("./cookies-utils.js");
17
17
  const ExternalVideo = require("./ExternalVideo.js");
18
18
  const flattenConnection = require("./flatten-connection.js");
19
+ const getProductOptions = require("./getProductOptions.js");
19
20
  const Image = require("./Image.js");
20
21
  const loadScript = require("./load-script.js");
21
22
  const MediaFile = require("./MediaFile.js");
@@ -30,6 +31,7 @@ const ShopifyProvider = require("./ShopifyProvider.js");
30
31
  const ShopPayButton = require("./ShopPayButton.js");
31
32
  const storefrontClient = require("./storefront-client.js");
32
33
  const useMoney = require("./useMoney.js");
34
+ const useSelectedOptionInUrlParam = require("./useSelectedOptionInUrlParam.js");
33
35
  const useShopifyCookies = require("./useShopifyCookies.js");
34
36
  const Video = require("./Video.js");
35
37
  exports.AddToCartButton = AddToCartButton.AddToCartButton;
@@ -58,6 +60,9 @@ exports.storefrontApiCustomScalars = codegen_helpers.storefrontApiCustomScalars;
58
60
  exports.getShopifyCookies = cookiesUtils.getShopifyCookies;
59
61
  exports.ExternalVideo = ExternalVideo.ExternalVideo;
60
62
  exports.flattenConnection = flattenConnection.flattenConnection;
63
+ exports.getAdjacentAndFirstAvailableVariants = getProductOptions.getAdjacentAndFirstAvailableVariants;
64
+ exports.getProductOptions = getProductOptions.getProductOptions;
65
+ exports.mapSelectedProductOptionToObject = getProductOptions.mapSelectedProductOptionToObject;
61
66
  exports.IMAGE_FRAGMENT = Image.IMAGE_FRAGMENT;
62
67
  exports.Image = Image.Image;
63
68
  exports.useLoadScript = loadScript.useLoadScript;
@@ -76,6 +81,7 @@ exports.useShop = ShopifyProvider.useShop;
76
81
  exports.ShopPayButton = ShopPayButton.ShopPayButton;
77
82
  exports.createStorefrontClient = storefrontClient.createStorefrontClient;
78
83
  exports.useMoney = useMoney.useMoney;
84
+ exports.useSelectedOptionInUrlParam = useSelectedOptionInUrlParam.useSelectedOptionInUrlParam;
79
85
  exports.useShopifyCookies = useShopifyCookies.useShopifyCookies;
80
86
  exports.Video = Video.Video;
81
87
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -14,6 +14,7 @@ import { customerAccountApiCustomScalars, storefrontApiCustomScalars } from "./c
14
14
  import { getShopifyCookies } from "./cookies-utils.mjs";
15
15
  import { ExternalVideo } from "./ExternalVideo.mjs";
16
16
  import { flattenConnection } from "./flatten-connection.mjs";
17
+ import { getAdjacentAndFirstAvailableVariants, getProductOptions, mapSelectedProductOptionToObject } from "./getProductOptions.mjs";
17
18
  import { IMAGE_FRAGMENT, Image } from "./Image.mjs";
18
19
  import { useLoadScript } from "./load-script.mjs";
19
20
  import { MediaFile } from "./MediaFile.mjs";
@@ -28,6 +29,7 @@ import { ShopifyProvider, useShop } from "./ShopifyProvider.mjs";
28
29
  import { ShopPayButton } from "./ShopPayButton.mjs";
29
30
  import { createStorefrontClient } from "./storefront-client.mjs";
30
31
  import { useMoney } from "./useMoney.mjs";
32
+ import { useSelectedOptionInUrlParam } from "./useSelectedOptionInUrlParam.mjs";
31
33
  import { useShopifyCookies } from "./useShopifyCookies.mjs";
32
34
  import { Video } from "./Video.mjs";
33
35
  export {
@@ -63,9 +65,12 @@ export {
63
65
  customerAccountApiCustomScalars,
64
66
  decodeEncodedVariant,
65
67
  flattenConnection,
68
+ getAdjacentAndFirstAvailableVariants,
66
69
  getClientBrowserParameters,
70
+ getProductOptions,
67
71
  getShopifyCookies,
68
72
  isOptionValueCombinationInEncodedVariant,
73
+ mapSelectedProductOptionToObject,
69
74
  parseGid,
70
75
  parseMetafield,
71
76
  sendShopifyAnalytics,
@@ -75,6 +80,7 @@ export {
75
80
  useLoadScript,
76
81
  useMoney,
77
82
  useProduct,
83
+ useSelectedOptionInUrlParam,
78
84
  useShop,
79
85
  useShopifyCookies
80
86
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const version = "2024.10.0";
3
+ const version = "2024.10.1";
4
4
  exports.version = version;
5
5
  //# sourceMappingURL=package.json.js.map
@@ -1,4 +1,4 @@
1
- const version = "2024.10.0";
1
+ const version = "2024.10.1";
2
2
  export {
3
3
  version
4
4
  };
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const React = require("react");
4
+ const getProductOptions = require("./getProductOptions.js");
5
+ function useSelectedOptionInUrlParam(selectedOptions) {
6
+ React.useEffect(() => {
7
+ const optionsSearchParams = new URLSearchParams(
8
+ getProductOptions.mapSelectedProductOptionToObject(selectedOptions || [])
9
+ );
10
+ const currentSearchParams = new URLSearchParams(window.location.search);
11
+ const combinedSearchParams = new URLSearchParams({
12
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
13
+ // @ts-ignore
14
+ ...Object.fromEntries(currentSearchParams),
15
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
16
+ // @ts-ignore
17
+ ...Object.fromEntries(optionsSearchParams)
18
+ });
19
+ if (combinedSearchParams.size > 0) {
20
+ window.history.replaceState(
21
+ {},
22
+ "",
23
+ `${window.location.pathname}?${combinedSearchParams.toString()}`
24
+ );
25
+ }
26
+ }, [JSON.stringify(selectedOptions)]);
27
+ return null;
28
+ }
29
+ exports.useSelectedOptionInUrlParam = useSelectedOptionInUrlParam;
30
+ //# sourceMappingURL=useSelectedOptionInUrlParam.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSelectedOptionInUrlParam.js","sources":["../../src/useSelectedOptionInUrlParam.tsx"],"sourcesContent":["import {useEffect} from 'react';\nimport {mapSelectedProductOptionToObject} from './getProductOptions.js';\nimport {SelectedOption} from './storefront-api-types.js';\n\nexport function useSelectedOptionInUrlParam(\n selectedOptions: Pick<SelectedOption, 'name' | 'value'>[],\n): null {\n useEffect(() => {\n const optionsSearchParams = new URLSearchParams(\n mapSelectedProductOptionToObject(selectedOptions || []),\n );\n const currentSearchParams = new URLSearchParams(window.location.search);\n\n // ts ignoring the URLSearchParams not iterable error for now\n // https://stackoverflow.com/questions/72522489/urlsearchparams-not-accepting-string#answer-72522838\n // TODO: update ts lib\n const combinedSearchParams = new URLSearchParams({\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n ...Object.fromEntries(currentSearchParams),\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n ...Object.fromEntries(optionsSearchParams),\n });\n\n if (combinedSearchParams.size > 0) {\n window.history.replaceState(\n {},\n '',\n `${window.location.pathname}?${combinedSearchParams.toString()}`,\n );\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(selectedOptions)]);\n\n return null;\n}\n"],"names":["useEffect","mapSelectedProductOptionToObject"],"mappings":";;;;AAIO,SAAS,4BACd,iBACM;AACNA,QAAAA,UAAU,MAAM;AACd,UAAM,sBAAsB,IAAI;AAAA,MAC9BC,kBAAA,iCAAiC,mBAAmB,EAAE;AAAA,IAAA;AAExD,UAAM,sBAAsB,IAAI,gBAAgB,OAAO,SAAS,MAAM;AAKhE,UAAA,uBAAuB,IAAI,gBAAgB;AAAA;AAAA;AAAA,MAG/C,GAAG,OAAO,YAAY,mBAAmB;AAAA;AAAA;AAAA,MAGzC,GAAG,OAAO,YAAY,mBAAmB;AAAA,IAAA,CAC1C;AAEG,QAAA,qBAAqB,OAAO,GAAG;AACjC,aAAO,QAAQ;AAAA,QACb,CAAC;AAAA,QACD;AAAA,QACA,GAAG,OAAO,SAAS,QAAQ,IAAI,qBAAqB,UAAU;AAAA,MAAA;AAAA,IAElE;AAAA,KAEC,CAAC,KAAK,UAAU,eAAe,CAAC,CAAC;AAE7B,SAAA;AACT;;"}
@@ -0,0 +1,30 @@
1
+ import { useEffect } from "react";
2
+ import { mapSelectedProductOptionToObject } from "./getProductOptions.mjs";
3
+ function useSelectedOptionInUrlParam(selectedOptions) {
4
+ useEffect(() => {
5
+ const optionsSearchParams = new URLSearchParams(
6
+ mapSelectedProductOptionToObject(selectedOptions || [])
7
+ );
8
+ const currentSearchParams = new URLSearchParams(window.location.search);
9
+ const combinedSearchParams = new URLSearchParams({
10
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
11
+ // @ts-ignore
12
+ ...Object.fromEntries(currentSearchParams),
13
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
14
+ // @ts-ignore
15
+ ...Object.fromEntries(optionsSearchParams)
16
+ });
17
+ if (combinedSearchParams.size > 0) {
18
+ window.history.replaceState(
19
+ {},
20
+ "",
21
+ `${window.location.pathname}?${combinedSearchParams.toString()}`
22
+ );
23
+ }
24
+ }, [JSON.stringify(selectedOptions)]);
25
+ return null;
26
+ }
27
+ export {
28
+ useSelectedOptionInUrlParam
29
+ };
30
+ //# sourceMappingURL=useSelectedOptionInUrlParam.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSelectedOptionInUrlParam.mjs","sources":["../../src/useSelectedOptionInUrlParam.tsx"],"sourcesContent":["import {useEffect} from 'react';\nimport {mapSelectedProductOptionToObject} from './getProductOptions.js';\nimport {SelectedOption} from './storefront-api-types.js';\n\nexport function useSelectedOptionInUrlParam(\n selectedOptions: Pick<SelectedOption, 'name' | 'value'>[],\n): null {\n useEffect(() => {\n const optionsSearchParams = new URLSearchParams(\n mapSelectedProductOptionToObject(selectedOptions || []),\n );\n const currentSearchParams = new URLSearchParams(window.location.search);\n\n // ts ignoring the URLSearchParams not iterable error for now\n // https://stackoverflow.com/questions/72522489/urlsearchparams-not-accepting-string#answer-72522838\n // TODO: update ts lib\n const combinedSearchParams = new URLSearchParams({\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n ...Object.fromEntries(currentSearchParams),\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n ...Object.fromEntries(optionsSearchParams),\n });\n\n if (combinedSearchParams.size > 0) {\n window.history.replaceState(\n {},\n '',\n `${window.location.pathname}?${combinedSearchParams.toString()}`,\n );\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(selectedOptions)]);\n\n return null;\n}\n"],"names":[],"mappings":";;AAIO,SAAS,4BACd,iBACM;AACN,YAAU,MAAM;AACd,UAAM,sBAAsB,IAAI;AAAA,MAC9B,iCAAiC,mBAAmB,EAAE;AAAA,IAAA;AAExD,UAAM,sBAAsB,IAAI,gBAAgB,OAAO,SAAS,MAAM;AAKhE,UAAA,uBAAuB,IAAI,gBAAgB;AAAA;AAAA;AAAA,MAG/C,GAAG,OAAO,YAAY,mBAAmB;AAAA;AAAA;AAAA,MAGzC,GAAG,OAAO,YAAY,mBAAmB;AAAA,IAAA,CAC1C;AAEG,QAAA,qBAAqB,OAAO,GAAG;AACjC,aAAO,QAAQ;AAAA,QACb,CAAC;AAAA,QACD;AAAA,QACA,GAAG,OAAO,SAAS,QAAQ,IAAI,qBAAqB,UAAU;AAAA,MAAA;AAAA,IAElE;AAAA,KAEC,CAAC,KAAK,UAAU,eAAe,CAAC,CAAC;AAE7B,SAAA;AACT;"}
@@ -53,7 +53,7 @@ type CartProviderProps = {
53
53
  customerAccessToken?: CartBuyerIdentityInput['customerAccessToken'];
54
54
  /** The ISO country code for i18n. */
55
55
  countryCode?: CountryCode;
56
- /** The ISO luanguage code for i18n. */
56
+ /** The ISO language code for i18n. */
57
57
  languageCode?: LanguageCode;
58
58
  };
59
59
  /**
@@ -67,8 +67,9 @@ type CartProviderProps = {
67
67
  * The `CartProvider` component must be a descendant of the `ShopifyProvider` component.
68
68
  */
69
69
  export declare function CartProvider({ children, numCartLines, onCreate, onLineAdd, onLineRemove, onLineUpdate, onNoteUpdate, onBuyerIdentityUpdate, onAttributesUpdate, onDiscountCodesUpdate, onCreateComplete, onLineAddComplete, onLineRemoveComplete, onLineUpdateComplete, onNoteUpdateComplete, onBuyerIdentityUpdateComplete, onAttributesUpdateComplete, onDiscountCodesUpdateComplete, data: cart, cartFragment, customerAccessToken, countryCode, languageCode, }: CartProviderProps): JSX.Element;
70
- /** Check for storage availability funciton obtained from
71
- * https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
70
+ /**
71
+ * Check for storage availability function obtained from
72
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
72
73
  */
73
74
  export declare function storageAvailable(type: 'localStorage' | 'sessionStorage'): boolean;
74
75
  export {};
@@ -0,0 +1,49 @@
1
+ import type { Product, ProductOption, ProductOptionValue, ProductVariant, SelectedOption } from './storefront-api-types';
2
+ export type RecursivePartial<T> = {
3
+ [P in keyof T]?: RecursivePartial<T[P]>;
4
+ };
5
+ type ProductOptionValueState = {
6
+ variant: ProductVariant;
7
+ handle: string;
8
+ variantUriQuery: string;
9
+ selected: boolean;
10
+ exists: boolean;
11
+ available: boolean;
12
+ isDifferentProduct: boolean;
13
+ };
14
+ type MappedProductOptionValue = ProductOptionValue & ProductOptionValueState;
15
+ /**
16
+ * Converts the product option into an Object\<key, value\> for building query params
17
+ * For example, a selected product option of
18
+ * [
19
+ * \{
20
+ * name: 'Color',
21
+ * value: 'Red',
22
+ * \},
23
+ * \{
24
+ * name: 'Size',
25
+ * value: 'Medium',
26
+ * \}
27
+ * ]
28
+ * Would return
29
+ * \{
30
+ * Color: 'Red',
31
+ * Size: 'Medium',
32
+ * \}
33
+ */
34
+ export declare function mapSelectedProductOptionToObject(options: Pick<SelectedOption, 'name' | 'value'>[]): Record<string, string>;
35
+ export type MappedProductOptions = Omit<ProductOption, 'optionValues'> & {
36
+ optionValues: MappedProductOptionValue[];
37
+ };
38
+ export declare function checkProductParam(product: RecursivePartial<Product>, checkAll?: boolean): Product;
39
+ /**
40
+ * Finds all the variants provided by adjacentVariants, options.optionValues.firstAvailableVariant,
41
+ * and selectedOrFirstAvailableVariant and return them in a single array
42
+ */
43
+ export declare function getAdjacentAndFirstAvailableVariants(product: RecursivePartial<Product>): ProductVariant[];
44
+ /**
45
+ * Returns a product options array with its relevant information
46
+ * about the variant
47
+ */
48
+ export declare function getProductOptions(product: RecursivePartial<Product>): MappedProductOptions[];
49
+ export {};
@@ -16,6 +16,7 @@ export { customerAccountApiCustomScalars, storefrontApiCustomScalars, } from './
16
16
  export { getShopifyCookies } from './cookies-utils.js';
17
17
  export { ExternalVideo } from './ExternalVideo.js';
18
18
  export { flattenConnection } from './flatten-connection.js';
19
+ export { getAdjacentAndFirstAvailableVariants, getProductOptions, type MappedProductOptions, mapSelectedProductOptionToObject, } from './getProductOptions.js';
19
20
  export { Image, IMAGE_FRAGMENT } from './Image.js';
20
21
  export { useLoadScript } from './load-script.js';
21
22
  export { MediaFile } from './MediaFile.js';
@@ -32,5 +33,6 @@ export type { StorefrontApiResponse, StorefrontApiResponseError, StorefrontApiRe
32
33
  export type { StorefrontClientProps } from './storefront-client.js';
33
34
  export { createStorefrontClient } from './storefront-client.js';
34
35
  export { useMoney } from './useMoney.js';
36
+ export { useSelectedOptionInUrlParam } from './useSelectedOptionInUrlParam.js';
35
37
  export { useShopifyCookies } from './useShopifyCookies.js';
36
38
  export { Video } from './Video.js';
@@ -16,6 +16,7 @@ export { customerAccountApiCustomScalars, storefrontApiCustomScalars, } from './
16
16
  export { getShopifyCookies } from './cookies-utils.js';
17
17
  export { ExternalVideo } from './ExternalVideo.js';
18
18
  export { flattenConnection } from './flatten-connection.js';
19
+ export { getAdjacentAndFirstAvailableVariants, getProductOptions, type MappedProductOptions, mapSelectedProductOptionToObject, } from './getProductOptions.js';
19
20
  export { Image, IMAGE_FRAGMENT } from './Image.js';
20
21
  export { useLoadScript } from './load-script.js';
21
22
  export { MediaFile } from './MediaFile.js';
@@ -32,5 +33,6 @@ export type { StorefrontApiResponse, StorefrontApiResponseError, StorefrontApiRe
32
33
  export type { StorefrontClientProps } from './storefront-client.js';
33
34
  export { createStorefrontClient } from './storefront-client.js';
34
35
  export { useMoney } from './useMoney.js';
36
+ export { useSelectedOptionInUrlParam } from './useSelectedOptionInUrlParam.js';
35
37
  export { useShopifyCookies } from './useShopifyCookies.js';
36
38
  export { Video } from './Video.js';
@@ -7,14 +7,14 @@ import type { PartialDeep } from 'type-fest';
7
7
  type StorefrontApiExtensions = {
8
8
  /**
9
9
  * Shows error codes common to Shopify. Additional error codes may also be shown.
10
- * https://shopify.dev/api/storefront#status_and_error_codes
10
+ * @see https://shopify.dev/api/storefront#status_and_error_codes
11
11
  */
12
12
  code?: 'THROTTLED' | 'ACCESS_DENIED' | 'SHOP_INACTIVE' | 'INTERNAL_SERVER_ERROR' | (string & {});
13
13
  };
14
14
  /**
15
15
  * The Storefront API can return a 200 OK response code when the query succeeds, and will populate the `data` property. The generic you pass in through type parameters will be the shape of the `data` object if successful. If you prefer a "deeply-partial" version of that generic object, consider using `StorefrontApiResponseOkPartial` instead.
16
16
  *
17
- * The Storefront API can also return a 200 OK response code in cases that would typically produce 4xx errors in REST. This will propulate the `errors` property.
17
+ * The Storefront API can also return a 200 OK response code in cases that would typically produce 4xx errors in REST. This will populate the `errors` property.
18
18
  *
19
19
  * Refer to https://shopify.dev/api/storefront#status_and_error_codes for more information.
20
20
  */
@@ -22,7 +22,7 @@ export type StorefrontApiResponseOk<DataGeneric> = FormattedExecutionResult<Data
22
22
  /**
23
23
  * The Storefront API can return a 200 OK response code when the query succeeds, and will populate the `data` property. The generic you pass in through type parameters will be the shape of the `data` object, with `Partial` deeply-applied to all nested objects and properties, if successful. If you prefer not using this "deeply-partial" generic data object, consider using `StorefrontApiResponseOk` instead.
24
24
  *
25
- * The Storefront API can also return a 200 OK response code in cases that would typically produce 4xx errors in REST. This will propulate the `errors` property.
25
+ * The Storefront API can also return a 200 OK response code in cases that would typically produce 4xx errors in REST. This will populate the `errors` property.
26
26
  *
27
27
  * Refer to https://shopify.dev/api/storefront#status_and_error_codes for more information.
28
28
  */
@@ -48,7 +48,7 @@ export type StorefrontApiResponseError = string | {
48
48
  /**
49
49
  * The Storefront API can return a 200 OK response code when the query succeeds, and will populate the `data` property. The generic you pass in through type parameters will be the shape of the `data` object if successful. If you prefer a "deeply-partial" version of that generic object, consider using `StorefrontApiResponseOkPartial` instead.
50
50
  *
51
- * The Storefront API can also return a 200 OK response code in cases that would typically produce 4xx errors in REST. This will propulate the `errors` property.
51
+ * The Storefront API can also return a 200 OK response code in cases that would typically produce 4xx errors in REST. This will populate the `errors` property.
52
52
  *
53
53
  * 4xx and 5xx errors occur infrequently. They are often related to network communications, your account, or an issue with Shopify’s services. Commonly the response is a `JSON.parse`-able string, but in rare occasions may also return HTML which is not `JSON.parse()`-able.
54
54
  *
@@ -58,7 +58,7 @@ export type StorefrontApiResponse<DataGeneric> = StorefrontApiResponseOk<DataGen
58
58
  /**
59
59
  * The Storefront API can return a 200 OK response code when the query succeeds, and will populate the `data` property. The generic you pass in through type parameters will be the shape of the `data` object, with `Partial` deeply-applied to all nested objects and properties, if successful. If you prefer not using this "deeply-partial" generic data object, consider using `StorefrontApiResponseOk` instead.
60
60
  *
61
- * The Storefront API can also return a 200 OK response code in cases that would typically produce 4xx errors in REST. This will propulate the `errors` property.
61
+ * The Storefront API can also return a 200 OK response code in cases that would typically produce 4xx errors in REST. This will populate the `errors` property.
62
62
  *
63
63
  * 4xx and 5xx errors occur infrequently. They are often related to network communications, your account, or an issue with Shopify’s services. Commonly the response is a `JSON.parse`-able string, but in rare occasions may also return HTML which is not `JSON.parse()`-able.
64
64
  *
@@ -0,0 +1,2 @@
1
+ import { SelectedOption } from './storefront-api-types.js';
2
+ export declare function useSelectedOptionInUrlParam(selectedOptions: Pick<SelectedOption, 'name' | 'value'>[]): null;