@decocms/apps 0.23.3 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/commerce/components/Image.tsx +129 -143
  3. package/commerce/components/JsonLd.tsx +192 -201
  4. package/commerce/components/Picture.tsx +65 -75
  5. package/commerce/sdk/analytics.ts +15 -15
  6. package/commerce/sdk/formatPrice.ts +13 -16
  7. package/commerce/sdk/url.ts +7 -7
  8. package/commerce/sdk/useOffer.ts +46 -57
  9. package/commerce/sdk/useVariantPossibilities.ts +25 -25
  10. package/commerce/types/commerce.ts +868 -875
  11. package/commerce/utils/canonical.ts +5 -8
  12. package/commerce/utils/constants.ts +5 -6
  13. package/commerce/utils/filters.ts +4 -4
  14. package/commerce/utils/productToAnalyticsItem.ts +52 -56
  15. package/commerce/utils/stateByZip.ts +42 -42
  16. package/package.json +24 -4
  17. package/shopify/actions/cart/addItems.ts +24 -25
  18. package/shopify/actions/cart/updateCoupons.ts +19 -20
  19. package/shopify/actions/cart/updateItems.ts +19 -20
  20. package/shopify/actions/user/signIn.ts +25 -30
  21. package/shopify/actions/user/signUp.ts +19 -24
  22. package/shopify/client.ts +24 -24
  23. package/shopify/index.ts +20 -18
  24. package/shopify/init.ts +18 -21
  25. package/shopify/loaders/ProductDetailsPage.ts +16 -20
  26. package/shopify/loaders/ProductList.ts +66 -69
  27. package/shopify/loaders/ProductListingPage.ts +150 -158
  28. package/shopify/loaders/RelatedProducts.ts +24 -27
  29. package/shopify/loaders/cart.ts +53 -52
  30. package/shopify/loaders/shop.ts +22 -27
  31. package/shopify/loaders/user.ts +27 -32
  32. package/shopify/utils/admin/admin.ts +33 -34
  33. package/shopify/utils/admin/queries.ts +2 -2
  34. package/shopify/utils/cart.ts +18 -14
  35. package/shopify/utils/cookies.ts +62 -65
  36. package/shopify/utils/enums.ts +424 -424
  37. package/shopify/utils/graphql.ts +44 -55
  38. package/shopify/utils/storefront/queries.ts +24 -29
  39. package/shopify/utils/storefront/storefront.graphql.gen.ts +55 -55
  40. package/shopify/utils/transform.ts +370 -376
  41. package/shopify/utils/types.ts +118 -118
  42. package/shopify/utils/user.ts +11 -11
  43. package/shopify/utils/utils.ts +135 -140
  44. package/vtex/actions/address.ts +86 -86
  45. package/vtex/actions/auth.ts +14 -27
  46. package/vtex/actions/checkout.ts +36 -49
  47. package/vtex/actions/masterData.ts +10 -27
  48. package/vtex/actions/misc.ts +101 -111
  49. package/vtex/actions/newsletter.ts +48 -52
  50. package/vtex/actions/orders.ts +13 -16
  51. package/vtex/actions/profile.ts +55 -55
  52. package/vtex/actions/session.ts +36 -35
  53. package/vtex/actions/trigger.ts +25 -25
  54. package/vtex/actions/wishlist.ts +51 -53
  55. package/vtex/client.ts +14 -42
  56. package/vtex/hooks/index.ts +4 -4
  57. package/vtex/hooks/useAutocomplete.ts +42 -48
  58. package/vtex/hooks/useCart.ts +153 -165
  59. package/vtex/hooks/useUser.ts +40 -40
  60. package/vtex/hooks/useWishlist.ts +70 -70
  61. package/vtex/inline-loaders/productDetailsPage.ts +1 -3
  62. package/vtex/inline-loaders/productList.ts +121 -127
  63. package/vtex/inline-loaders/productListShelf.ts +159 -0
  64. package/vtex/inline-loaders/productListingPage.ts +10 -34
  65. package/vtex/inline-loaders/relatedProducts.ts +1 -3
  66. package/vtex/inline-loaders/suggestions.ts +36 -39
  67. package/vtex/inline-loaders/workflowProducts.ts +45 -49
  68. package/vtex/invoke.ts +159 -194
  69. package/vtex/loaders/address.ts +49 -54
  70. package/vtex/loaders/brands.ts +19 -26
  71. package/vtex/loaders/cart.ts +24 -21
  72. package/vtex/loaders/catalog.ts +51 -53
  73. package/vtex/loaders/collections.ts +25 -27
  74. package/vtex/loaders/legacy.ts +487 -534
  75. package/vtex/loaders/logistics.ts +33 -37
  76. package/vtex/loaders/navbar.ts +5 -8
  77. package/vtex/loaders/orders.ts +28 -39
  78. package/vtex/loaders/pageType.ts +41 -35
  79. package/vtex/loaders/payment.ts +27 -37
  80. package/vtex/loaders/profile.ts +38 -38
  81. package/vtex/loaders/promotion.ts +5 -8
  82. package/vtex/loaders/search.ts +56 -59
  83. package/vtex/loaders/session.ts +22 -30
  84. package/vtex/loaders/user.ts +39 -41
  85. package/vtex/loaders/wishlist.ts +35 -35
  86. package/vtex/loaders/wishlistProducts.ts +3 -15
  87. package/vtex/loaders/workflow.ts +220 -227
  88. package/vtex/middleware.ts +116 -119
  89. package/vtex/types.ts +201 -201
  90. package/vtex/utils/batch.ts +13 -16
  91. package/vtex/utils/cookies.ts +76 -80
  92. package/vtex/utils/enrichment.ts +62 -42
  93. package/vtex/utils/fetchCache.ts +1 -4
  94. package/vtex/utils/index.ts +6 -6
  95. package/vtex/utils/intelligentSearch.ts +48 -57
  96. package/vtex/utils/legacy.ts +108 -124
  97. package/vtex/utils/pickAndOmit.ts +15 -20
  98. package/vtex/utils/proxy.ts +136 -146
  99. package/vtex/utils/resourceRange.ts +3 -3
  100. package/vtex/utils/segment.ts +100 -111
  101. package/vtex/utils/similars.ts +1 -2
  102. package/vtex/utils/sitemap.ts +91 -91
  103. package/vtex/utils/slugCache.ts +2 -6
  104. package/vtex/utils/slugify.ts +9 -9
  105. package/vtex/utils/transform.ts +1178 -1105
  106. package/vtex/utils/types.ts +1381 -1381
  107. package/vtex/utils/vtexId.ts +44 -47
  108. package/.github/workflows/release.yml +0 -34
  109. package/.releaserc.json +0 -28
  110. package/knip.json +0 -19
  111. package/tsconfig.json +0 -11
package/shopify/client.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { createGraphqlClient, type GraphQLClient } from "./utils/graphql";
2
2
 
3
3
  export interface ShopifyConfig {
4
- storeName: string;
5
- storefrontAccessToken: string;
6
- publicUrl?: string;
4
+ storeName: string;
5
+ storefrontAccessToken: string;
6
+ publicUrl?: string;
7
7
  }
8
8
 
9
9
  let _client: GraphQLClient | null = null;
@@ -22,37 +22,37 @@ let _fetch: typeof fetch | undefined;
22
22
  * ```
23
23
  */
24
24
  export function setShopifyFetch(fetchFn: typeof fetch) {
25
- _fetch = fetchFn;
26
- if (_config) configureShopify(_config);
25
+ _fetch = fetchFn;
26
+ if (_config) configureShopify(_config);
27
27
  }
28
28
 
29
29
  export function configureShopify(config: ShopifyConfig) {
30
- _config = config;
31
- _client = createGraphqlClient(
32
- `https://${config.storeName}.myshopify.com/api/2025-04/graphql.json`,
33
- {
34
- "X-Shopify-Storefront-Access-Token": config.storefrontAccessToken,
35
- },
36
- _fetch,
37
- );
30
+ _config = config;
31
+ _client = createGraphqlClient(
32
+ `https://${config.storeName}.myshopify.com/api/2025-04/graphql.json`,
33
+ {
34
+ "X-Shopify-Storefront-Access-Token": config.storefrontAccessToken,
35
+ },
36
+ _fetch,
37
+ );
38
38
  }
39
39
 
40
40
  export function getShopifyClient(): GraphQLClient {
41
- if (!_client || !_config) {
42
- throw new Error(
43
- "Shopify not configured. Call configureShopify() first or check deco-shopify.json block."
44
- );
45
- }
46
- return _client;
41
+ if (!_client || !_config) {
42
+ throw new Error(
43
+ "Shopify not configured. Call configureShopify() first or check deco-shopify.json block.",
44
+ );
45
+ }
46
+ return _client;
47
47
  }
48
48
 
49
49
  export function getShopifyConfig(): ShopifyConfig {
50
- if (!_config) {
51
- throw new Error("Shopify not configured.");
52
- }
53
- return _config;
50
+ if (!_config) {
51
+ throw new Error("Shopify not configured.");
52
+ }
53
+ return _config;
54
54
  }
55
55
 
56
56
  export function getBaseUrl(): string {
57
- return _config?.publicUrl || "";
57
+ return _config?.publicUrl || "";
58
58
  }
package/shopify/index.ts CHANGED
@@ -1,32 +1,34 @@
1
1
  // Client & Config
2
- export { configureShopify, getShopifyClient, getShopifyConfig, getBaseUrl, setShopifyFetch } from "./client";
2
+
3
+ export { default as addItems } from "./actions/cart/addItems";
4
+ export { default as updateCoupons } from "./actions/cart/updateCoupons";
5
+ export { default as updateItems } from "./actions/cart/updateItems";
6
+ export { default as signIn } from "./actions/user/signIn";
7
+ export { default as signUp } from "./actions/user/signUp";
3
8
  export type { ShopifyConfig } from "./client";
9
+ export {
10
+ configureShopify,
11
+ getBaseUrl,
12
+ getShopifyClient,
13
+ getShopifyConfig,
14
+ setShopifyFetch,
15
+ } from "./client";
4
16
  export { initShopify, initShopifyFromBlocks } from "./init";
5
-
17
+ export type { CartLine, ShopifyCart } from "./loaders/cart";
18
+ // Cart
19
+ export { createCart, getCart } from "./loaders/cart";
20
+ export { default as productDetailsPageLoader } from "./loaders/ProductDetailsPage";
6
21
  // Product Loaders
7
22
  export { default as productListLoader } from "./loaders/ProductList";
8
- export { default as productDetailsPageLoader } from "./loaders/ProductDetailsPage";
9
23
  export { default as productListingPageLoader } from "./loaders/ProductListingPage";
10
24
  export { default as relatedProductsLoader } from "./loaders/RelatedProducts";
11
-
12
- // Cart
13
- export { getCart, createCart } from "./loaders/cart";
14
- export type { ShopifyCart, CartLine } from "./loaders/cart";
15
- export { default as addItems } from "./actions/cart/addItems";
16
- export { default as updateItems } from "./actions/cart/updateItems";
17
- export { default as updateCoupons } from "./actions/cart/updateCoupons";
18
-
25
+ export type { Shop } from "./loaders/shop";
19
26
  // Shop
20
27
  export { default as shopLoader } from "./loaders/shop";
21
- export type { Shop } from "./loaders/shop";
22
-
28
+ export type { ShopifyUser } from "./loaders/user";
23
29
  // User
24
30
  export { default as userLoader } from "./loaders/user";
25
- export type { ShopifyUser } from "./loaders/user";
26
- export { default as signIn } from "./actions/user/signIn";
27
- export { default as signUp } from "./actions/user/signUp";
28
-
31
+ export { getCartCookie, setCartCookie } from "./utils/cart";
29
32
  // Cookie utils
30
33
  export { getCookies, setCookie } from "./utils/cookies";
31
- export { getCartCookie, setCartCookie } from "./utils/cart";
32
34
  export { getUserCookie, setUserCookie } from "./utils/user";
package/shopify/init.ts CHANGED
@@ -6,20 +6,17 @@ let initialized = false;
6
6
  * Initialize Shopify from raw block data.
7
7
  * The site is responsible for reading the blocks and passing the config here.
8
8
  */
9
- export function initShopify(config: {
10
- storeName: string;
11
- storefrontAccessToken: string;
12
- }) {
13
- if (initialized) return;
9
+ export function initShopify(config: { storeName: string; storefrontAccessToken: string }) {
10
+ if (initialized) return;
14
11
 
15
- if (!config.storeName || !config.storefrontAccessToken) {
16
- console.warn("[Shopify] Missing storeName or storefrontAccessToken.");
17
- return;
18
- }
12
+ if (!config.storeName || !config.storefrontAccessToken) {
13
+ console.warn("[Shopify] Missing storeName or storefrontAccessToken.");
14
+ return;
15
+ }
19
16
 
20
- console.log(`[Shopify] Initializing: ${config.storeName}.myshopify.com`);
21
- configureShopify(config);
22
- initialized = true;
17
+ console.log(`[Shopify] Initializing: ${config.storeName}.myshopify.com`);
18
+ configureShopify(config);
19
+ initialized = true;
23
20
  }
24
21
 
25
22
  /**
@@ -27,14 +24,14 @@ export function initShopify(config: {
27
24
  * Looks for the "deco-shopify" block and extracts credentials.
28
25
  */
29
26
  export function initShopifyFromBlocks(blocks: Record<string, any>) {
30
- const shopifyBlock = blocks["deco-shopify"];
31
- if (!shopifyBlock) {
32
- console.warn("[Shopify] No deco-shopify block found.");
33
- return;
34
- }
27
+ const shopifyBlock = blocks["deco-shopify"];
28
+ if (!shopifyBlock) {
29
+ console.warn("[Shopify] No deco-shopify block found.");
30
+ return;
31
+ }
35
32
 
36
- initShopify({
37
- storeName: shopifyBlock.storeName,
38
- storefrontAccessToken: shopifyBlock.storefrontAccessToken,
39
- });
33
+ initShopify({
34
+ storeName: shopifyBlock.storeName,
35
+ storefrontAccessToken: shopifyBlock.storefrontAccessToken,
36
+ });
40
37
  }
@@ -1,35 +1,31 @@
1
1
  import type { ProductDetailsPage } from "../../commerce/types/commerce";
2
2
  import { getShopifyClient } from "../client";
3
3
  import { GetProduct } from "../utils/storefront/queries";
4
- import { toProductPage, type ProductShopify } from "../utils/transform";
4
+ import { type ProductShopify, toProductPage } from "../utils/transform";
5
5
  import type { Metafield } from "../utils/types";
6
6
 
7
7
  export interface Props {
8
- slug: string;
9
- metafields?: Metafield[];
8
+ slug: string;
9
+ metafields?: Metafield[];
10
10
  }
11
11
 
12
12
  export default async function productDetailsPageLoader(
13
- props: Props,
14
- url?: URL,
13
+ props: Props,
14
+ url?: URL,
15
15
  ): Promise<ProductDetailsPage | null> {
16
- const client = getShopifyClient();
17
- const { slug, metafields = [] } = props;
16
+ const client = getShopifyClient();
17
+ const { slug, metafields = [] } = props;
18
18
 
19
- const splitted = slug?.split("-") ?? [];
20
- const maybeSkuId = Number(splitted[splitted.length - 1]);
21
- const handle = splitted.slice(0, maybeSkuId ? -1 : undefined).join("-");
19
+ const splitted = slug?.split("-") ?? [];
20
+ const maybeSkuId = Number(splitted[splitted.length - 1]);
21
+ const handle = splitted.slice(0, maybeSkuId ? -1 : undefined).join("-");
22
22
 
23
- const data = await client.query<{ product?: ProductShopify }>(
24
- GetProduct,
25
- { handle, identifiers: metafields },
26
- );
23
+ const data = await client.query<{ product?: ProductShopify }>(GetProduct, {
24
+ handle,
25
+ identifiers: metafields,
26
+ });
27
27
 
28
- if (!data?.product) return null;
28
+ if (!data?.product) return null;
29
29
 
30
- return toProductPage(
31
- data.product,
32
- url ?? new URL("https://localhost"),
33
- maybeSkuId || undefined,
34
- );
30
+ return toProductPage(data.product, url ?? new URL("https://localhost"), maybeSkuId || undefined);
35
31
  }
@@ -1,101 +1,98 @@
1
1
  import type { Product } from "../../commerce/types/commerce";
2
2
  import { getShopifyClient } from "../client";
3
3
  import { ProductsByCollection, SearchProducts } from "../utils/storefront/queries";
4
- import { toProduct, type ProductShopify } from "../utils/transform";
4
+ import { type ProductShopify, toProduct } from "../utils/transform";
5
5
  import type { Metafield } from "../utils/types";
6
6
  import {
7
- type CollectionSortKeys,
8
- type SearchSortKeys,
9
- searchSortShopify,
10
- sortShopify,
7
+ type CollectionSortKeys,
8
+ type SearchSortKeys,
9
+ searchSortShopify,
10
+ sortShopify,
11
11
  } from "../utils/utils";
12
12
 
13
13
  export interface QueryProps {
14
- query: string;
15
- count: number;
16
- sort?: SearchSortKeys;
14
+ query: string;
15
+ count: number;
16
+ sort?: SearchSortKeys;
17
17
  }
18
18
 
19
19
  export interface CollectionProps {
20
- collection: string;
21
- count: number;
22
- sort?: CollectionSortKeys;
20
+ collection: string;
21
+ count: number;
22
+ sort?: CollectionSortKeys;
23
23
  }
24
24
 
25
25
  export interface FilterProps {
26
- tags?: string[];
27
- productTypes?: string[];
28
- productVendors?: string[];
29
- priceMin?: number;
30
- priceMax?: number;
31
- variantOptions?: { name: string; value: string }[];
26
+ tags?: string[];
27
+ productTypes?: string[];
28
+ productVendors?: string[];
29
+ priceMin?: number;
30
+ priceMax?: number;
31
+ variantOptions?: { name: string; value: string }[];
32
32
  }
33
33
 
34
34
  export type Props = {
35
- props: QueryProps | CollectionProps;
36
- filters?: FilterProps;
37
- metafields?: Metafield[];
35
+ props: QueryProps | CollectionProps;
36
+ filters?: FilterProps;
37
+ metafields?: Metafield[];
38
38
  };
39
39
 
40
40
  const isQueryList = (p: any): p is QueryProps =>
41
- typeof p.query === "string" && typeof p.count === "number";
41
+ typeof p.query === "string" && typeof p.count === "number";
42
42
 
43
43
  export default async function productListLoader(
44
- expandedProps: Props,
45
- url?: URL,
44
+ expandedProps: Props,
45
+ url?: URL,
46
46
  ): Promise<Product[] | null> {
47
- const client = getShopifyClient();
47
+ const client = getShopifyClient();
48
48
 
49
- const props = expandedProps.props ??
50
- (expandedProps as unknown as Props["props"]);
49
+ const props = expandedProps.props ?? (expandedProps as unknown as Props["props"]);
51
50
 
52
- const count = props.count ?? 12;
53
- const metafields = expandedProps.metafields || [];
54
- const sort = props.sort ?? "";
51
+ const count = props.count ?? 12;
52
+ const metafields = expandedProps.metafields || [];
53
+ const sort = props.sort ?? "";
55
54
 
56
- const filters: any[] = [];
57
- expandedProps.filters?.tags?.forEach((tag) => filters.push({ tag }));
58
- expandedProps.filters?.productTypes?.forEach((productType) => filters.push({ productType }));
59
- expandedProps.filters?.productVendors?.forEach((productVendor) => filters.push({ productVendor }));
60
- if (expandedProps.filters?.priceMin) filters.push({ price: { min: expandedProps.filters.priceMin } });
61
- if (expandedProps.filters?.priceMax) filters.push({ price: { max: expandedProps.filters.priceMax } });
62
- expandedProps.filters?.variantOptions?.forEach((variantOption) => filters.push({ variantOption }));
55
+ const filters: any[] = [];
56
+ expandedProps.filters?.tags?.forEach((tag) => filters.push({ tag }));
57
+ expandedProps.filters?.productTypes?.forEach((productType) => filters.push({ productType }));
58
+ expandedProps.filters?.productVendors?.forEach((productVendor) =>
59
+ filters.push({ productVendor }),
60
+ );
61
+ if (expandedProps.filters?.priceMin != null)
62
+ filters.push({ price: { min: expandedProps.filters.priceMin } });
63
+ if (expandedProps.filters?.priceMax != null)
64
+ filters.push({ price: { max: expandedProps.filters.priceMax } });
65
+ expandedProps.filters?.variantOptions?.forEach((variantOption) =>
66
+ filters.push({ variantOption }),
67
+ );
63
68
 
64
- let shopifyProducts: { nodes: ProductShopify[] } | undefined;
69
+ let shopifyProducts: { nodes: ProductShopify[] } | undefined;
65
70
 
66
- if (isQueryList(props)) {
67
- const data = await client.query<{ search: { nodes: ProductShopify[] } }>(
68
- SearchProducts,
69
- {
70
- first: count,
71
- query: props.query,
72
- productFilters: filters,
73
- identifiers: metafields,
74
- ...searchSortShopify[sort],
75
- },
76
- );
77
- shopifyProducts = data.search;
78
- } else {
79
- const data = await client.query<{
80
- collection?: { products: { nodes: ProductShopify[] } };
81
- }>(
82
- ProductsByCollection,
83
- {
84
- first: count,
85
- handle: (props as CollectionProps).collection,
86
- filters,
87
- identifiers: metafields,
88
- ...sortShopify[sort],
89
- },
90
- );
91
- shopifyProducts = data.collection?.products;
92
- }
71
+ if (isQueryList(props)) {
72
+ const data = await client.query<{ search: { nodes: ProductShopify[] } }>(SearchProducts, {
73
+ first: count,
74
+ query: props.query,
75
+ productFilters: filters,
76
+ identifiers: metafields,
77
+ ...searchSortShopify[sort],
78
+ });
79
+ shopifyProducts = data.search;
80
+ } else {
81
+ const data = await client.query<{
82
+ collection?: { products: { nodes: ProductShopify[] } };
83
+ }>(ProductsByCollection, {
84
+ first: count,
85
+ handle: (props as CollectionProps).collection,
86
+ filters,
87
+ identifiers: metafields,
88
+ ...sortShopify[sort],
89
+ });
90
+ shopifyProducts = data.collection?.products;
91
+ }
93
92
 
94
- const baseUrl = url ?? new URL("https://localhost");
93
+ const baseUrl = url ?? new URL("https://localhost");
95
94
 
96
- const products = shopifyProducts?.nodes.map((p) =>
97
- toProduct(p, p.variants.nodes[0], baseUrl)
98
- );
95
+ const products = shopifyProducts?.nodes.map((p) => toProduct(p, p.variants.nodes[0], baseUrl));
99
96
 
100
- return products ?? [];
97
+ return products ?? [];
101
98
  }