@decocms/apps 0.20.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 (113) hide show
  1. package/.github/workflows/release.yml +34 -0
  2. package/.releaserc.json +25 -0
  3. package/commerce/components/Image.tsx +209 -0
  4. package/commerce/components/JsonLd.tsx +285 -0
  5. package/commerce/sdk/analytics.ts +24 -0
  6. package/commerce/sdk/formatPrice.ts +23 -0
  7. package/commerce/sdk/url.ts +9 -0
  8. package/commerce/sdk/useOffer.ts +75 -0
  9. package/commerce/sdk/useVariantPossibilities.ts +43 -0
  10. package/commerce/types/commerce.ts +1105 -0
  11. package/commerce/utils/canonical.ts +11 -0
  12. package/commerce/utils/constants.ts +9 -0
  13. package/commerce/utils/filters.ts +10 -0
  14. package/commerce/utils/productToAnalyticsItem.ts +67 -0
  15. package/commerce/utils/stateByZip.ts +50 -0
  16. package/knip.json +19 -0
  17. package/package.json +77 -0
  18. package/shopify/actions/cart/addItems.ts +37 -0
  19. package/shopify/actions/cart/updateCoupons.ts +32 -0
  20. package/shopify/actions/cart/updateItems.ts +32 -0
  21. package/shopify/actions/user/signIn.ts +45 -0
  22. package/shopify/actions/user/signUp.ts +36 -0
  23. package/shopify/client.ts +58 -0
  24. package/shopify/index.ts +32 -0
  25. package/shopify/init.ts +40 -0
  26. package/shopify/loaders/ProductDetailsPage.ts +35 -0
  27. package/shopify/loaders/ProductList.ts +101 -0
  28. package/shopify/loaders/ProductListingPage.ts +180 -0
  29. package/shopify/loaders/RelatedProducts.ts +45 -0
  30. package/shopify/loaders/cart.ts +73 -0
  31. package/shopify/loaders/shop.ts +40 -0
  32. package/shopify/loaders/user.ts +44 -0
  33. package/shopify/utils/admin/admin.ts +57 -0
  34. package/shopify/utils/admin/queries.ts +29 -0
  35. package/shopify/utils/cart.ts +28 -0
  36. package/shopify/utils/cookies.ts +85 -0
  37. package/shopify/utils/enums.ts +438 -0
  38. package/shopify/utils/graphql.ts +69 -0
  39. package/shopify/utils/storefront/queries.ts +530 -0
  40. package/shopify/utils/storefront/storefront.graphql.gen.ts +113 -0
  41. package/shopify/utils/transform.ts +436 -0
  42. package/shopify/utils/types.ts +191 -0
  43. package/shopify/utils/user.ts +23 -0
  44. package/shopify/utils/utils.ts +164 -0
  45. package/tsconfig.json +11 -0
  46. package/vtex/README.md +6 -0
  47. package/vtex/actions/address.ts +211 -0
  48. package/vtex/actions/auth.ts +337 -0
  49. package/vtex/actions/checkout.ts +497 -0
  50. package/vtex/actions/index.ts +11 -0
  51. package/vtex/actions/masterData.ts +170 -0
  52. package/vtex/actions/misc.ts +196 -0
  53. package/vtex/actions/newsletter.ts +108 -0
  54. package/vtex/actions/orders.ts +37 -0
  55. package/vtex/actions/profile.ts +119 -0
  56. package/vtex/actions/session.ts +87 -0
  57. package/vtex/actions/trigger.ts +43 -0
  58. package/vtex/actions/wishlist.ts +116 -0
  59. package/vtex/client.ts +423 -0
  60. package/vtex/hooks/index.ts +4 -0
  61. package/vtex/hooks/useAutocomplete.ts +89 -0
  62. package/vtex/hooks/useCart.ts +219 -0
  63. package/vtex/hooks/useUser.ts +78 -0
  64. package/vtex/hooks/useWishlist.ts +119 -0
  65. package/vtex/index.ts +14 -0
  66. package/vtex/inline-loaders/productDetailsPage.ts +75 -0
  67. package/vtex/inline-loaders/productList.ts +163 -0
  68. package/vtex/inline-loaders/productListingPage.ts +447 -0
  69. package/vtex/inline-loaders/relatedProducts.ts +83 -0
  70. package/vtex/inline-loaders/suggestions.ts +49 -0
  71. package/vtex/inline-loaders/workflowProducts.ts +68 -0
  72. package/vtex/invoke.ts +202 -0
  73. package/vtex/loaders/address.ts +120 -0
  74. package/vtex/loaders/brands.ts +51 -0
  75. package/vtex/loaders/cart.ts +49 -0
  76. package/vtex/loaders/catalog.ts +165 -0
  77. package/vtex/loaders/collections.ts +57 -0
  78. package/vtex/loaders/index.ts +19 -0
  79. package/vtex/loaders/legacy.ts +671 -0
  80. package/vtex/loaders/logistics.ts +115 -0
  81. package/vtex/loaders/navbar.ts +29 -0
  82. package/vtex/loaders/orders.ts +103 -0
  83. package/vtex/loaders/pageType.ts +62 -0
  84. package/vtex/loaders/payment.ts +107 -0
  85. package/vtex/loaders/profile.ts +138 -0
  86. package/vtex/loaders/promotion.ts +33 -0
  87. package/vtex/loaders/search.ts +127 -0
  88. package/vtex/loaders/session.ts +91 -0
  89. package/vtex/loaders/user.ts +89 -0
  90. package/vtex/loaders/wishlist.ts +89 -0
  91. package/vtex/loaders/wishlistProducts.ts +81 -0
  92. package/vtex/loaders/workflow.ts +323 -0
  93. package/vtex/logo.png +0 -0
  94. package/vtex/middleware.ts +229 -0
  95. package/vtex/types.ts +248 -0
  96. package/vtex/utils/batch.ts +21 -0
  97. package/vtex/utils/cookies.ts +76 -0
  98. package/vtex/utils/enrichment.ts +540 -0
  99. package/vtex/utils/fetchCache.ts +150 -0
  100. package/vtex/utils/index.ts +17 -0
  101. package/vtex/utils/intelligentSearch.ts +84 -0
  102. package/vtex/utils/legacy.ts +155 -0
  103. package/vtex/utils/pickAndOmit.ts +30 -0
  104. package/vtex/utils/proxy.ts +196 -0
  105. package/vtex/utils/resourceRange.ts +10 -0
  106. package/vtex/utils/segment.ts +163 -0
  107. package/vtex/utils/similars.ts +38 -0
  108. package/vtex/utils/sitemap.ts +133 -0
  109. package/vtex/utils/slugCache.ts +32 -0
  110. package/vtex/utils/slugify.ts +13 -0
  111. package/vtex/utils/transform.ts +1331 -0
  112. package/vtex/utils/types.ts +1884 -0
  113. package/vtex/utils/vtexId.ts +103 -0
@@ -0,0 +1,133 @@
1
+ /**
2
+ * VTEX Sitemap utility.
3
+ *
4
+ * Fetches product and category URLs from VTEX's sitemap API
5
+ * and converts them to SitemapEntry format for composition
6
+ * with the CMS sitemap generator.
7
+ */
8
+
9
+ import { getVtexConfig, vtexFetchResponse, vtexHost } from "../client";
10
+
11
+ export interface SitemapEntry {
12
+ loc: string;
13
+ lastmod?: string;
14
+ changefreq?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
15
+ priority?: number;
16
+ }
17
+
18
+ /**
19
+ * Fetch sitemap entries from VTEX's sitemap API.
20
+ *
21
+ * VTEX exposes /sitemap.xml which contains links to sub-sitemaps
22
+ * (products, categories, brands, etc.). This function fetches the
23
+ * main sitemap index and extracts all <loc> entries from the
24
+ * referenced sub-sitemaps.
25
+ *
26
+ * @param origin - The storefront origin (e.g., "https://www.mystore.com")
27
+ * @param options.maxDepth - How many levels of sub-sitemaps to follow (default: 1)
28
+ * @param options.rewriteHost - Whether to rewrite VTEX hostnames to the storefront origin (default: true)
29
+ */
30
+ export async function getVtexSitemapEntries(
31
+ origin: string,
32
+ options?: {
33
+ maxDepth?: number;
34
+ rewriteHost?: boolean;
35
+ includeBrands?: boolean;
36
+ includeCategories?: boolean;
37
+ includeProducts?: boolean;
38
+ },
39
+ ): Promise<SitemapEntry[]> {
40
+ const config = getVtexConfig();
41
+ const vtexSitemapHost = vtexHost("vtexcommercestable", config);
42
+ const rewrite = options?.rewriteHost !== false;
43
+ const includeProducts = options?.includeProducts !== false;
44
+ const includeCategories = options?.includeCategories !== false;
45
+ const includeBrands = options?.includeBrands !== false;
46
+
47
+ try {
48
+ const mainSitemapUrl = `https://${vtexSitemapHost}/sitemap.xml`;
49
+ const mainResponse = await vtexFetchResponse(mainSitemapUrl);
50
+ const mainXml = await mainResponse.text();
51
+
52
+ const subSitemapUrls = extractLocs(mainXml);
53
+ const entries: SitemapEntry[] = [];
54
+
55
+ const filteredUrls = subSitemapUrls.filter((url) => {
56
+ const lower = url.toLowerCase();
57
+ if (!includeProducts && lower.includes("product")) return false;
58
+ if (!includeCategories && lower.includes("categor")) return false;
59
+ if (!includeBrands && lower.includes("brand")) return false;
60
+ return true;
61
+ });
62
+
63
+ const maxDepth = options?.maxDepth ?? 1;
64
+ if (maxDepth < 1) {
65
+ return filteredUrls.map((url) => ({
66
+ loc: rewrite ? rewriteUrl(url, vtexSitemapHost, origin) : url,
67
+ changefreq: "daily" as const,
68
+ priority: 0.5,
69
+ }));
70
+ }
71
+
72
+ const settled = await Promise.allSettled(
73
+ filteredUrls.map(async (subUrl) => {
74
+ try {
75
+ const resp = await vtexFetchResponse(subUrl);
76
+ const xml = await resp.text();
77
+ return extractLocs(xml);
78
+ } catch {
79
+ return [];
80
+ }
81
+ }),
82
+ );
83
+
84
+ const today = new Date().toISOString().split("T")[0];
85
+
86
+ for (const result of settled) {
87
+ if (result.status !== "fulfilled") continue;
88
+ for (const loc of result.value) {
89
+ entries.push({
90
+ loc: rewrite ? rewriteUrl(loc, vtexSitemapHost, origin) : loc,
91
+ lastmod: today,
92
+ changefreq: "daily",
93
+ priority: 0.5,
94
+ });
95
+ }
96
+ }
97
+
98
+ return entries;
99
+ } catch (error) {
100
+ console.error("[VTEX Sitemap] Failed to fetch VTEX sitemap:", error);
101
+ return [];
102
+ }
103
+ }
104
+
105
+ function extractLocs(xml: string): string[] {
106
+ const locs: string[] = [];
107
+ const regex = /<loc>\s*(.*?)\s*<\/loc>/g;
108
+ let match: RegExpExecArray | null;
109
+ while ((match = regex.exec(xml)) !== null) {
110
+ if (match[1]) locs.push(match[1].trim());
111
+ }
112
+ return locs;
113
+ }
114
+
115
+ function rewriteUrl(url: string, vtexSitemapHost: string, origin: string): string {
116
+ try {
117
+ const parsed = new URL(url);
118
+ const originParsed = new URL(origin);
119
+ const config = getVtexConfig();
120
+ const domain = config.domain ?? "com.br";
121
+ if (
122
+ parsed.hostname === vtexSitemapHost ||
123
+ parsed.hostname.endsWith(`.vtexcommercestable.${domain}`)
124
+ ) {
125
+ parsed.protocol = originParsed.protocol;
126
+ parsed.hostname = originParsed.hostname;
127
+ parsed.port = originParsed.port;
128
+ }
129
+ return parsed.toString();
130
+ } catch {
131
+ return url.replace(`https://${vtexSitemapHost}`, origin);
132
+ }
133
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * In-flight dedup + SWR cache for VTEX Legacy Catalog slug→product lookups.
3
+ *
4
+ * Multiple loaders on the same page (PDP, relatedProducts x3, BuyTogether)
5
+ * all call `/api/catalog_system/pub/products/search/{slug}/p` for the same slug.
6
+ * This module routes through vtexCachedFetch which provides in-flight dedup
7
+ * and stale-while-revalidate caching (3 min TTL for 200 responses).
8
+ */
9
+ import { getVtexConfig, vtexCachedFetch } from "../client";
10
+ import type { LegacyProduct } from "./types";
11
+
12
+ export function searchBySlug(
13
+ linkText: string,
14
+ ): Promise<LegacyProduct[] | null> {
15
+ const config = getVtexConfig();
16
+ const sc = config.salesChannel;
17
+ const scParam = sc ? `?sc=${sc}` : "";
18
+
19
+ return vtexCachedFetch<LegacyProduct[]>(
20
+ `/api/catalog_system/pub/products/search/${encodeURIComponent(linkText)}/p${scParam}`,
21
+ ).catch((err) => {
22
+ console.error(`[VTEX] searchBySlug error for "${linkText}":`, err);
23
+ return null;
24
+ });
25
+ }
26
+
27
+ export async function resolveProductIdBySlug(
28
+ linkText: string,
29
+ ): Promise<string | null> {
30
+ const products = await searchBySlug(linkText);
31
+ return products?.length ? products[0].productId : null;
32
+ }
@@ -0,0 +1,13 @@
1
+ const mapped = JSON.parse(
2
+ `{"Á":"A","Ä":"A","Â":"A","À":"A","Ã":"A","Å":"A","Č":"C","Ç":"C","Ć":"C","Ď":"D","É":"E","Ě":"E","Ë":"E","È":"E","Ê":"E","Ẽ":"E","Ĕ":"E","Ȇ":"E","Í":"I","Ì":"I","Î":"I","Ï":"I","Ň":"N","Ñ":"N","Ó":"O","Ö":"O","Ò":"O","Ô":"O","Õ":"O","Ø":"O","Ř":"R","Ŕ":"R","Š":"S","Ť":"T","Ú":"U","Ů":"U","Ü":"U","Ù":"U","Û":"U","Ý":"Y","Ÿ":"Y","Ž":"Z","á":"a","ä":"a","â":"a","à":"a","ã":"a","å":"a","č":"c","ç":"c","ć":"c","ď":"d","é":"e","ě":"e","ë":"e","è":"e","ê":"e","ẽ":"e","ĕ":"e","ȇ":"e","í":"i","ì":"i","î":"i","ï":"i","ň":"n","ñ":"n","ó":"o","ö":"o","ò":"o","ô":"o","õ":"o","ø":"o","ð":"o","ř":"r","ŕ":"r","š":"s","ť":"t","ú":"u","ů":"u","ü":"u","ù":"u","û":"u","ý":"y","ÿ":"y","ž":"z","þ":"b","Þ":"B","Đ":"D","đ":"d","ß":"B","Æ":"A","a":"a"}`,
3
+ );
4
+
5
+ export const slugify = (str: string) =>
6
+ str
7
+ .replace(/,/g, "")
8
+ .replace(/[·/_,:]/g, "-")
9
+ .replace(/[*+~.()'"!:@&\[\]`/ %$#?{}|><=_^]/g, "-")
10
+ .split("")
11
+ .map((char) => mapped[char] ?? char)
12
+ .join("")
13
+ .toLowerCase();