@decocms/start 0.19.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 (185) hide show
  1. package/.cursor/skills/deco-api-call-dedup/SKILL.md +443 -0
  2. package/.cursor/skills/deco-apps-architecture/SKILL.md +255 -0
  3. package/.cursor/skills/deco-apps-architecture/app-pattern.md +288 -0
  4. package/.cursor/skills/deco-apps-architecture/commerce-types.md +239 -0
  5. package/.cursor/skills/deco-apps-architecture/new-app-guide.md +268 -0
  6. package/.cursor/skills/deco-apps-architecture/scripts-codegen.md +148 -0
  7. package/.cursor/skills/deco-apps-architecture/shared-utils.md +181 -0
  8. package/.cursor/skills/deco-apps-architecture/vtex-deep-structure.md +253 -0
  9. package/.cursor/skills/deco-apps-architecture/website-app.md +169 -0
  10. package/.cursor/skills/deco-apps-vtex-porting/SKILL.md +189 -0
  11. package/.cursor/skills/deco-apps-vtex-porting/adaptation-patterns.md +335 -0
  12. package/.cursor/skills/deco-apps-vtex-porting/commerce-porting.md +155 -0
  13. package/.cursor/skills/deco-apps-vtex-porting/cookie-auth-patterns.md +148 -0
  14. package/.cursor/skills/deco-apps-vtex-porting/structure-map.md +234 -0
  15. package/.cursor/skills/deco-apps-vtex-porting/transform-mapping.md +99 -0
  16. package/.cursor/skills/deco-apps-vtex-porting/website-porting.md +194 -0
  17. package/.cursor/skills/deco-apps-vtex-review/SKILL.md +234 -0
  18. package/.cursor/skills/deco-async-rendering-architecture/SKILL.md +270 -0
  19. package/.cursor/skills/deco-async-rendering-site-guide/SKILL.md +417 -0
  20. package/.cursor/skills/deco-cms-layout-caching/SKILL.md +293 -0
  21. package/.cursor/skills/deco-cms-route-config/SKILL.md +388 -0
  22. package/.cursor/skills/deco-core-architecture/SKILL.md +185 -0
  23. package/.cursor/skills/deco-core-architecture/blocks.md +196 -0
  24. package/.cursor/skills/deco-core-architecture/deco-vs-deco-start.md +191 -0
  25. package/.cursor/skills/deco-core-architecture/engine.md +220 -0
  26. package/.cursor/skills/deco-core-architecture/hooks-components.md +157 -0
  27. package/.cursor/skills/deco-core-architecture/plugins-clients.md +136 -0
  28. package/.cursor/skills/deco-core-architecture/runtime.md +116 -0
  29. package/.cursor/skills/deco-core-architecture/site-usage.md +165 -0
  30. package/.cursor/skills/deco-e2e-testing/SKILL.md +372 -0
  31. package/.cursor/skills/deco-e2e-testing/discovery.md +337 -0
  32. package/.cursor/skills/deco-e2e-testing/scripts/scaffold.sh +81 -0
  33. package/.cursor/skills/deco-e2e-testing/selectors.md +175 -0
  34. package/.cursor/skills/deco-e2e-testing/templates/package.json +18 -0
  35. package/.cursor/skills/deco-e2e-testing/templates/playwright.config.ts +65 -0
  36. package/.cursor/skills/deco-e2e-testing/templates/scripts/baseline.ts +279 -0
  37. package/.cursor/skills/deco-e2e-testing/templates/scripts/run-e2e.ts +194 -0
  38. package/.cursor/skills/deco-e2e-testing/templates/specs/ecommerce-flow.spec.ts +612 -0
  39. package/.cursor/skills/deco-e2e-testing/templates/tsconfig.json +12 -0
  40. package/.cursor/skills/deco-e2e-testing/templates/utils/metrics-collector.ts +918 -0
  41. package/.cursor/skills/deco-e2e-testing/troubleshooting.md +602 -0
  42. package/.cursor/skills/deco-edge-caching/SKILL.md +316 -0
  43. package/.cursor/skills/deco-full-analysis/SKILL.md +898 -0
  44. package/.cursor/skills/deco-full-analysis/checklists/asset-optimization.md +251 -0
  45. package/.cursor/skills/deco-full-analysis/checklists/bug-fix.md +189 -0
  46. package/.cursor/skills/deco-full-analysis/checklists/cache-strategy.md +144 -0
  47. package/.cursor/skills/deco-full-analysis/checklists/dependency-update.md +150 -0
  48. package/.cursor/skills/deco-full-analysis/checklists/hydration-fix.md +191 -0
  49. package/.cursor/skills/deco-full-analysis/checklists/image-optimization.md +180 -0
  50. package/.cursor/skills/deco-full-analysis/checklists/loader-optimization.md +165 -0
  51. package/.cursor/skills/deco-full-analysis/checklists/seo-fix.md +183 -0
  52. package/.cursor/skills/deco-full-analysis/checklists/site-cleanup.md +281 -0
  53. package/.cursor/skills/deco-full-analysis/discovery.md +548 -0
  54. package/.cursor/skills/deco-incident-debugging/SKILL.md +378 -0
  55. package/.cursor/skills/deco-incident-debugging/headless-mode.md +510 -0
  56. package/.cursor/skills/deco-incident-debugging/learnings-index.md +227 -0
  57. package/.cursor/skills/deco-incident-debugging/triage-workflow.md +312 -0
  58. package/.cursor/skills/deco-islands-migration/SKILL.md +251 -0
  59. package/.cursor/skills/deco-loader-n-plus-1-detector/SKILL.md +275 -0
  60. package/.cursor/skills/deco-performance-audit/SKILL.md +530 -0
  61. package/.cursor/skills/deco-performance-audit/tools-reference.md +428 -0
  62. package/.cursor/skills/deco-performance-audit/workflow.md +457 -0
  63. package/.cursor/skills/deco-server-functions-invoke/SKILL.md +92 -0
  64. package/.cursor/skills/deco-server-functions-invoke/architecture.md +166 -0
  65. package/.cursor/skills/deco-server-functions-invoke/generator.md +122 -0
  66. package/.cursor/skills/deco-server-functions-invoke/problem.md +98 -0
  67. package/.cursor/skills/deco-server-functions-invoke/troubleshooting.md +110 -0
  68. package/.cursor/skills/deco-site-deployment/SKILL.md +396 -0
  69. package/.cursor/skills/deco-site-memory-debugging/SKILL.md +121 -0
  70. package/.cursor/skills/deco-site-memory-debugging/cdp-connection.md +222 -0
  71. package/.cursor/skills/deco-site-memory-debugging/memory-analysis.md +362 -0
  72. package/.cursor/skills/deco-site-patterns/SKILL.md +124 -0
  73. package/.cursor/skills/deco-site-patterns/app-composition.md +337 -0
  74. package/.cursor/skills/deco-site-patterns/client-patterns.md +341 -0
  75. package/.cursor/skills/deco-site-patterns/cms-wiring.md +230 -0
  76. package/.cursor/skills/deco-site-patterns/section-patterns.md +340 -0
  77. package/.cursor/skills/deco-site-scaling-tuning/SKILL.md +240 -0
  78. package/.cursor/skills/deco-site-scaling-tuning/analysis-scripts.md +267 -0
  79. package/.cursor/skills/deco-start-architecture/SKILL.md +218 -0
  80. package/.cursor/skills/deco-start-architecture/admin-protocol.md +156 -0
  81. package/.cursor/skills/deco-start-architecture/cms-resolution.md +201 -0
  82. package/.cursor/skills/deco-start-architecture/code-quality.md +158 -0
  83. package/.cursor/skills/deco-start-architecture/gap-analysis.md +129 -0
  84. package/.cursor/skills/deco-start-architecture/sdk-utilities.md +197 -0
  85. package/.cursor/skills/deco-start-architecture/worker-entry-caching.md +154 -0
  86. package/.cursor/skills/deco-startup-analysis/SKILL.md +248 -0
  87. package/.cursor/skills/deco-storefront-test-checklist/SKILL.md +369 -0
  88. package/.cursor/skills/deco-tanstack-hydration-fixes/SKILL.md +468 -0
  89. package/.cursor/skills/deco-tanstack-navigation/SKILL.md +681 -0
  90. package/.cursor/skills/deco-tanstack-search/SKILL.md +411 -0
  91. package/.cursor/skills/deco-tanstack-storefront-patterns/SKILL.md +1013 -0
  92. package/.cursor/skills/deco-to-tanstack-migration/SKILL.md +518 -0
  93. package/.cursor/skills/deco-to-tanstack-migration/references/codemod-commands.md +174 -0
  94. package/.cursor/skills/deco-to-tanstack-migration/references/commerce/README.md +78 -0
  95. package/.cursor/skills/deco-to-tanstack-migration/references/deco-framework/README.md +128 -0
  96. package/.cursor/skills/deco-to-tanstack-migration/references/gotchas.md +719 -0
  97. package/.cursor/skills/deco-to-tanstack-migration/references/imports/README.md +70 -0
  98. package/.cursor/skills/deco-to-tanstack-migration/references/platform-hooks/README.md +154 -0
  99. package/.cursor/skills/deco-to-tanstack-migration/references/signals/README.md +220 -0
  100. package/.cursor/skills/deco-to-tanstack-migration/references/vite-config/README.md +78 -0
  101. package/.cursor/skills/deco-to-tanstack-migration/templates/package-json.md +55 -0
  102. package/.cursor/skills/deco-to-tanstack-migration/templates/root-route.md +110 -0
  103. package/.cursor/skills/deco-to-tanstack-migration/templates/router.md +96 -0
  104. package/.cursor/skills/deco-to-tanstack-migration/templates/setup-ts.md +167 -0
  105. package/.cursor/skills/deco-to-tanstack-migration/templates/vite-config.md +122 -0
  106. package/.cursor/skills/deco-to-tanstack-migration/templates/worker-entry.md +67 -0
  107. package/.cursor/skills/deco-typescript-fixes/SKILL.md +178 -0
  108. package/.cursor/skills/deco-typescript-fixes/common-fixes.md +330 -0
  109. package/.cursor/skills/deco-typescript-fixes/strategy.md +148 -0
  110. package/.cursor/skills/deco-variant-selection-perf/SKILL.md +272 -0
  111. package/.cursor/skills/deco-vtex-fetch-cache/SKILL.md +225 -0
  112. package/.cursor/skills/find-skills/SKILL.md +133 -0
  113. package/.cursor/skills/incident-report/SKILL.md +179 -0
  114. package/.cursor/skills/incident-report/references/5-whys.md +75 -0
  115. package/.cursor/skills/incident-report/templates/client-report.md +187 -0
  116. package/.cursor/skills/incident-report/templates/internal-report.md +206 -0
  117. package/.cursor/skills/template-skill/SKILL.md +38 -0
  118. package/.github/workflows/release.yml +32 -0
  119. package/.releaserc.json +25 -0
  120. package/CLAUDE.md +135 -0
  121. package/GAP_ANALYSIS.md +224 -0
  122. package/GAP_ANALYSIS_V2.md +1013 -0
  123. package/biome.json +39 -0
  124. package/knip.json +5 -0
  125. package/package.json +87 -0
  126. package/scripts/generate-blocks.ts +69 -0
  127. package/scripts/generate-invoke.ts +378 -0
  128. package/scripts/generate-schema.ts +657 -0
  129. package/src/admin/cors.ts +29 -0
  130. package/src/admin/decofile.ts +72 -0
  131. package/src/admin/index.ts +24 -0
  132. package/src/admin/invoke.ts +163 -0
  133. package/src/admin/liveControls.ts +29 -0
  134. package/src/admin/meta.ts +70 -0
  135. package/src/admin/render.ts +205 -0
  136. package/src/admin/schema.ts +686 -0
  137. package/src/admin/setup.ts +44 -0
  138. package/src/cms/index.ts +59 -0
  139. package/src/cms/loader.ts +180 -0
  140. package/src/cms/registry.ts +162 -0
  141. package/src/cms/resolve.ts +1005 -0
  142. package/src/cms/sectionLoaders.ts +294 -0
  143. package/src/hooks/DecoPageRenderer.tsx +444 -0
  144. package/src/hooks/LazySection.tsx +109 -0
  145. package/src/hooks/LiveControls.tsx +108 -0
  146. package/src/hooks/SectionErrorFallback.tsx +85 -0
  147. package/src/hooks/index.ts +8 -0
  148. package/src/index.ts +5 -0
  149. package/src/matchers/builtins.ts +184 -0
  150. package/src/matchers/posthog.ts +154 -0
  151. package/src/middleware/decoState.ts +55 -0
  152. package/src/middleware/healthMetrics.ts +131 -0
  153. package/src/middleware/index.ts +80 -0
  154. package/src/middleware/liveness.ts +21 -0
  155. package/src/middleware/observability.ts +205 -0
  156. package/src/routes/adminRoutes.ts +83 -0
  157. package/src/routes/cmsRoute.ts +302 -0
  158. package/src/routes/components.tsx +34 -0
  159. package/src/routes/index.ts +15 -0
  160. package/src/sdk/analytics.ts +72 -0
  161. package/src/sdk/cacheHeaders.ts +268 -0
  162. package/src/sdk/cachedLoader.ts +206 -0
  163. package/src/sdk/clx.ts +3 -0
  164. package/src/sdk/cookie.ts +39 -0
  165. package/src/sdk/createInvoke.ts +57 -0
  166. package/src/sdk/csp.ts +59 -0
  167. package/src/sdk/env.ts +27 -0
  168. package/src/sdk/index.ts +63 -0
  169. package/src/sdk/instrumentedFetch.ts +137 -0
  170. package/src/sdk/invoke.ts +133 -0
  171. package/src/sdk/mergeCacheControl.ts +150 -0
  172. package/src/sdk/redirects.ts +217 -0
  173. package/src/sdk/requestContext.ts +184 -0
  174. package/src/sdk/serverTimings.ts +68 -0
  175. package/src/sdk/signal.ts +41 -0
  176. package/src/sdk/sitemap.ts +143 -0
  177. package/src/sdk/urlUtils.ts +117 -0
  178. package/src/sdk/useDevice.ts +82 -0
  179. package/src/sdk/useId.ts +7 -0
  180. package/src/sdk/useScript.ts +101 -0
  181. package/src/sdk/workerEntry.ts +703 -0
  182. package/src/sdk/wrapCaughtErrors.ts +107 -0
  183. package/src/types/index.ts +39 -0
  184. package/src/types/widgets.ts +13 -0
  185. package/tsconfig.json +13 -0
@@ -0,0 +1,272 @@
1
+ ---
2
+ name: deco-variant-selection-perf
3
+ description: Optimize product variant selection in Deco TanStack storefronts. Eliminates server re-fetches when switching SKU variants of the same product using replaceState, adds loading states for cross-product variant navigation, and removes preload="intent" from variant links to prevent double-fetch. Use when variant changes are slow, HAR analysis shows duplicate loadCmsPage calls, or when implementing variant selectors in a PDP.
4
+ ---
5
+
6
+ # Product Variant Selection Performance
7
+
8
+ Patterns for making variant selection instant in Deco storefronts on TanStack Start. Discovered while optimizing `espacosmart-storefront` where clicking a variant triggered 2 full `loadCmsPage` server calls (1300ms+ each).
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Variant changes on PDP are slow (>500ms)
13
+ - HAR analysis shows duplicate `loadCmsPage` calls with/without `?skuId`
14
+ - `preload="intent"` on variant `<Link>` causes double-fetch
15
+ - Need to add loading feedback for cross-product variant navigation
16
+ - Implementing a new variant selector component
17
+
18
+ ---
19
+
20
+ ## Key Insight: Two Types of "Variant" Navigation
21
+
22
+ | Type | Example | Data needed | Approach |
23
+ |------|---------|-------------|----------|
24
+ | **Same product, different SKU** | Size 90x0.8 → 90x0.95 | Already loaded in `isVariantOf.hasVariant` | `replaceState` — zero fetch |
25
+ | **Different product** | Product A → Product B (visual variation) | New product data | `navigate()` — single fetch |
26
+
27
+ The CMS block "PDP Loader" does NOT pass `skuId` to the server loader. All SKU data comes in the first load via `isVariantOf.hasVariant`. The `?skuId` in the URL is purely for bookmarking/sharing.
28
+
29
+ ---
30
+
31
+ ## Problem: Double-Fetch on Variant Click
32
+
33
+ ### Root Cause
34
+
35
+ When using `<Link to="/slug/p?skuId=160" preload="intent">`:
36
+
37
+ 1. **Hover** → TanStack Router fires a prefetch. The `loaderDeps` filters `skuId`, so it sends `loadCmsPage({ data: "/slug/p" })`.
38
+ 2. **Click** → Router fires the real navigation. Depending on timing and staleTime, it may fire `loadCmsPage({ data: "/slug/p?skuId=160" })`.
39
+
40
+ Result: **2 server calls** for one variant click, each taking 1-2 seconds (full CMS resolution + section loaders + VTEX API calls).
41
+
42
+ ### How to Diagnose
43
+
44
+ Export a HAR from Chrome DevTools (Network tab → Export HAR). Analyze:
45
+
46
+ ```python
47
+ # Find all loadCmsPage calls
48
+ import json, urllib.parse, base64
49
+ with open('localhost.har') as f:
50
+ har = json.load(f)
51
+ for e in har['log']['entries']:
52
+ url = e['request']['url']
53
+ if '/_serverFn/' not in url:
54
+ continue
55
+ qs = url.split('?')[1] if '?' in url else ''
56
+ params = urllib.parse.parse_qs(qs)
57
+ if 'payload' in params:
58
+ payload = json.loads(urllib.parse.unquote(params['payload'][0]))
59
+ # Extract the "data" (path) from TanStack's serialized payload
60
+ print(f"{e.get('time',0):.0f}ms skuId={'skuId' in str(payload)}")
61
+ ```
62
+
63
+ If you see two calls for the same slug (one with `skuId`, one without), this is the double-fetch.
64
+
65
+ ---
66
+
67
+ ## Fix 1: Same-Product Variants — `replaceState` (Zero Fetch)
68
+
69
+ Replace `<Link>` with `<a>` + `window.history.replaceState`. This changes the URL for bookmarking without triggering any TanStack Router navigation or loader.
70
+
71
+ ### Before (slow)
72
+
73
+ ```typescript
74
+ import { Link } from "@tanstack/react-router";
75
+
76
+ function VariantSelector({ product }: { product: Product }) {
77
+ const possibilities = useVariantPossibilities(hasVariant, product);
78
+ return (
79
+ // ...
80
+ <Link to={relativeLink} preload="intent">
81
+ <Avatar variant={relativeLink === relative(url) ? "active" : "default"} />
82
+ </Link>
83
+ );
84
+ }
85
+ ```
86
+
87
+ ### After (instant)
88
+
89
+ ```typescript
90
+ import { useState, useCallback } from "react";
91
+
92
+ function VariantSelector({ product }: { product: Product }) {
93
+ const possibilities = useVariantPossibilities(hasVariant, product);
94
+ const [currentUrl, setCurrentUrl] = useState(() => relative(product.url));
95
+
96
+ const handleVariantClick = useCallback(
97
+ (e: React.MouseEvent<HTMLAnchorElement>, link: string) => {
98
+ e.preventDefault();
99
+ setCurrentUrl(link);
100
+ window.history.replaceState(null, "", link);
101
+ },
102
+ [],
103
+ );
104
+
105
+ return (
106
+ // ...
107
+ <a
108
+ href={relativeLink ?? "#"}
109
+ onClick={(e) => relativeLink && handleVariantClick(e, relativeLink)}
110
+ >
111
+ <Avatar
112
+ variant={relativeLink === currentUrl ? "active" : relativeLink ? "default" : "disabled"}
113
+ />
114
+ </a>
115
+ );
116
+ }
117
+ ```
118
+
119
+ ### Why This Works
120
+
121
+ 1. `replaceState` changes browser URL without notifying TanStack Router → **zero loader execution**
122
+ 2. `useState(currentUrl)` tracks the active variant for UI highlighting → **instant re-render**
123
+ 3. `<a href>` preserves accessibility (right-click, ctrl+click open in new tab)
124
+ 4. The CMS PDP Loader does NOT use `skuId` from the URL — all variant data is in `isVariantOf.hasVariant`
125
+
126
+ ### When NOT to Use replaceState
127
+
128
+ - The variant changes the **product** (different `productGroupID`) — use `navigate()` instead
129
+ - The server loader actually reads `skuId` from the request URL to fetch different data
130
+ - SEO requires each variant to be a separate indexable page with unique server-rendered content
131
+
132
+ ---
133
+
134
+ ## Fix 2: Cross-Product Variants — `navigate()` with Loading
135
+
136
+ For `SkuVariation` (different products shown as visual variations), use `navigate()` but WITHOUT `preload="intent"` and WITH a loading state.
137
+
138
+ ```typescript
139
+ import { useNavigate } from "@tanstack/react-router";
140
+ import { useState, useCallback, useEffect, useRef } from "react";
141
+
142
+ export default function SkuVariation({ products }: { products: Product[] | null }) {
143
+ const [loadingIdx, setLoadingIdx] = useState<number | null>(null);
144
+ const navigate = useNavigate();
145
+ const prevProducts = useRef(products);
146
+
147
+ // Reset loading when products change (navigation completed, component reused)
148
+ useEffect(() => {
149
+ if (prevProducts.current !== products) {
150
+ setLoadingIdx(null);
151
+ prevProducts.current = products;
152
+ }
153
+ }, [products]);
154
+
155
+ const handleClick = useCallback(
156
+ (e: React.MouseEvent<HTMLAnchorElement>, link: string, idx: number) => {
157
+ e.preventDefault();
158
+ setLoadingIdx(idx);
159
+ navigate({ to: link });
160
+ },
161
+ [navigate],
162
+ );
163
+
164
+ if (!products?.length) return null;
165
+
166
+ return (
167
+ <ul>
168
+ {products.map((product, index) => {
169
+ const link = relative(product.url) ?? "#";
170
+ const isLoading = loadingIdx === index;
171
+ return (
172
+ <li key={index}>
173
+ <a href={link} onClick={(e) => handleClick(e, link, index)} className="relative">
174
+ <div className={`transition-opacity ${isLoading ? "opacity-30" : ""}`}>
175
+ <img src={product.image?.[0]?.url} width={35} height={35} />
176
+ </div>
177
+ {isLoading && (
178
+ <span className="loading loading-spinner loading-xs absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2" />
179
+ )}
180
+ </a>
181
+ </li>
182
+ );
183
+ })}
184
+ </ul>
185
+ );
186
+ }
187
+ ```
188
+
189
+ ### Key Details
190
+
191
+ | Aspect | Why |
192
+ |--------|-----|
193
+ | `preload={false}` / no preload | Prevents duplicate fetch (hover + click) |
194
+ | `useRef(prevProducts)` + `useEffect` | Resets loading when component reuses with new props |
195
+ | No `await navigate()` | `navigate` resolves when route renders — component may already be unmounted |
196
+ | `<a href>` not `<Link>` | Avoids TanStack Router's built-in prefetch behavior |
197
+
198
+ ---
199
+
200
+ ## Fix 3: Remove `preload="intent"` from All Variant Links
201
+
202
+ Any `<Link>` with `preload="intent"` in variant selectors causes prefetch on hover, leading to:
203
+ - Extra server calls
204
+ - Race conditions with the click navigation
205
+ - Wasted bandwidth
206
+
207
+ Replace with `preload={false}` or use `<a>` elements:
208
+
209
+ ```bash
210
+ # Find all variant links with preload="intent"
211
+ rg 'preload="intent"' src/components/product/ --glob '*.tsx' -l
212
+ ```
213
+
214
+ Files to check:
215
+ - `ProductVariantSelector.tsx`
216
+ - `SkuVariation.tsx`
217
+ - `ProductCardCategory.tsx` (variant selector in PLP cards)
218
+
219
+ ---
220
+
221
+ ## Verification
222
+
223
+ After applying fixes, export a new HAR and verify:
224
+
225
+ 1. **Same-product variant click**: Zero `loadCmsPage` calls (only image requests)
226
+ 2. **Cross-product variant click**: Exactly 1 `loadCmsPage` call per product
227
+ 3. **No duplicate calls**: Each unique slug appears at most once
228
+
229
+ ```python
230
+ # Quick HAR verification
231
+ server_calls = [e for e in har['log']['entries'] if '/_serverFn/' in e['request']['url']]
232
+ print(f"Server calls: {len(server_calls)}")
233
+ # Should be 0 for same-product variants, N for N different products
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Related Configuration
239
+
240
+ ### `ignoreSearchParams` in Route Config
241
+
242
+ The CMS catch-all route should filter `skuId` from `loaderDeps`:
243
+
244
+ ```typescript
245
+ const config = cmsRouteConfig({
246
+ siteName: "My Store",
247
+ defaultTitle: "My Store",
248
+ ignoreSearchParams: ["skuId"],
249
+ });
250
+ ```
251
+
252
+ This prevents `skuId` changes from being treated as dependency changes by TanStack Router.
253
+
254
+ ### `staleTime` in Dev Mode
255
+
256
+ With `staleTime: 0` (default in dev), even identical `loaderDeps` trigger re-fetch. Set a minimum staleTime in dev:
257
+
258
+ ```typescript
259
+ // In routeCacheDefaults()
260
+ if (isDev) return { staleTime: 5_000, gcTime: 30_000 };
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Related Skills
266
+
267
+ | Skill | Purpose |
268
+ |-------|---------|
269
+ | `deco-cms-layout-caching` | Cache layout sections (Header/Footer) to avoid redundant API calls |
270
+ | `deco-api-call-dedup` | In-flight deduplication for VTEX API calls |
271
+ | `deco-cms-route-config` | CMS route configuration in `@decocms/start` |
272
+ | `deco-tanstack-storefront-patterns` | General patterns for deco-start storefronts |
@@ -0,0 +1,225 @@
1
+ ---
2
+ name: deco-vtex-fetch-cache
3
+ description: SWR in-memory fetch cache for VTEX API responses in @decocms/apps. Ported from deco-cx/deco runtime/fetch/fetchCache.ts. Provides in-flight deduplication + stale-while-revalidate for all VTEX GET requests. Covers fetchWithCache utility, vtexCachedFetch client function, LRU eviction, TTL by HTTP status, integration with intelligentSearch and cross-selling calls. Use when adding caching to VTEX API calls, debugging stale responses, or understanding how the fetch cache layer works.
4
+ ---
5
+
6
+ # VTEX Fetch Cache (SWR In-Memory)
7
+
8
+ Server-side SWR cache for all VTEX GET API responses. Ported from `deco-cx/deco` `runtime/fetch/fetchCache.ts` and adapted for `@decocms/apps` on TanStack Start.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Adding SWR caching to new VTEX API calls
13
+ - Understanding why a VTEX response is served stale
14
+ - Debugging cache hit/miss behavior
15
+ - Tuning TTL for specific endpoints
16
+ - Understanding the relationship between `fetchWithCache`, `vtexCachedFetch`, and `createCachedLoader`
17
+
18
+ ---
19
+
20
+ ## Architecture
21
+
22
+ ```
23
+ Site Setup
24
+ └→ createCachedLoader (loader-level SWR, 30-120s TTL)
25
+ └→ vtexCachedFetch (HTTP-level SWR, 3min TTL)
26
+ └→ fetchWithCache (core cache engine)
27
+ └→ _fetch (instrumented fetch)
28
+ ```
29
+
30
+ | Layer | File | Scope | TTL |
31
+ |-------|------|-------|-----|
32
+ | `createCachedLoader` | `deco-start/src/sdk/cachedLoader.ts` | Loader result (parsed + transformed) | 30-120s per loader |
33
+ | `vtexCachedFetch` | `apps-start/vtex/client.ts` | Raw HTTP JSON response | 3 min (200), 10s (404) |
34
+ | `fetchWithCache` | `apps-start/vtex/utils/fetchCache.ts` | Core SWR + dedup engine | Status-based |
35
+
36
+ ---
37
+
38
+ ## Core: `fetchWithCache` (`vtex/utils/fetchCache.ts`)
39
+
40
+ ### Features
41
+
42
+ - **LRU eviction**: Max 500 entries, oldest evicted first
43
+ - **TTL by HTTP status**: 200-299 → 3 min, 404 → 10s, 500+ → never cached
44
+ - **In-flight deduplication**: Concurrent calls for the same URL share one Promise
45
+ - **Stale-while-revalidate**: Stale entries served immediately, refreshed in background
46
+ - **Custom TTL**: Override per-call via `opts.ttl`
47
+
48
+ ### API
49
+
50
+ ```typescript
51
+ import { fetchWithCache, FetchCacheOptions } from "@decocms/apps/vtex/utils/fetchCache";
52
+
53
+ // Basic usage
54
+ const data = await fetchWithCache<ProductType>(
55
+ fullUrl, // Cache key (typically the full URL)
56
+ () => fetch(fullUrl, init), // Fetch callback (returns Response)
57
+ { ttl: 60_000 }, // Optional: override TTL (1 min)
58
+ );
59
+ ```
60
+
61
+ ### How SWR Works
62
+
63
+ ```
64
+ Call fetchWithCache(key, doFetch)
65
+ ├→ Entry exists & fresh? → return cached body
66
+ ├→ Entry exists & stale? → return stale, fire background refresh
67
+ ├→ Entry missing, inflight exists? → await inflight Promise
68
+ └→ Entry missing, no inflight → execute doFetch(), cache result
69
+ ```
70
+
71
+ ### TTL Configuration
72
+
73
+ ```typescript
74
+ const TTL_BY_STATUS: Record<string, number> = {
75
+ "2xx": 180_000, // 3 min — success responses
76
+ "404": 10_000, // 10s — not found (may become available)
77
+ "5xx": 0, // never cache server errors
78
+ };
79
+ ```
80
+
81
+ ### Error Handling
82
+
83
+ Non-ok responses (status >= 400) throw an error. They are NOT cached. The error propagates to the caller, who should `.catch()` gracefully:
84
+
85
+ ```typescript
86
+ const data = await fetchWithCache<T>(url, doFetch).catch(() => fallback);
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Client Integration: `vtexCachedFetch` (`vtex/client.ts`)
92
+
93
+ Convenience wrapper that routes GET requests through `fetchWithCache`:
94
+
95
+ ```typescript
96
+ import { vtexCachedFetch } from "@decocms/apps/vtex";
97
+
98
+ // Automatically uses SWR cache for GET
99
+ const products = await vtexCachedFetch<Product[]>(
100
+ `/api/catalog_system/pub/products/search/${slug}/p`,
101
+ );
102
+
103
+ // Non-GET falls through to regular vtexFetch
104
+ const result = await vtexCachedFetch<OrderForm>(
105
+ `/api/checkout/pub/orderForms/simulation`,
106
+ { method: "POST", body: JSON.stringify(items) },
107
+ );
108
+ ```
109
+
110
+ ### Custom TTL per call
111
+
112
+ ```typescript
113
+ const pageType = await vtexCachedFetch<PageType>(
114
+ `/api/catalog_system/pub/portal/pagetype/${term}`,
115
+ undefined,
116
+ { cacheTTL: 300_000 }, // 5 min for page types
117
+ );
118
+ ```
119
+
120
+ ---
121
+
122
+ ## What Uses `vtexCachedFetch` (Current)
123
+
124
+ | Module | Endpoint | Before | After |
125
+ |--------|----------|--------|-------|
126
+ | `slugCache.ts` | `search/{slug}/p` | Manual inflight Map + 5s timeout | `vtexCachedFetch` SWR 3min |
127
+ | `relatedProducts.ts` | `crossselling/{type}/{id}` | Manual `crossSellingInflight` Map | `vtexCachedFetch` SWR 3min |
128
+ | `productDetailsPage.ts` | Kit items search | Plain `vtexFetch` | `vtexCachedFetch` SWR 3min |
129
+ | `client.ts` `cachedPageType` | `pagetype/{term}` | Manual inflight Map | `vtexCachedFetch` SWR 3min |
130
+
131
+ ## What Uses `fetchWithCache` Directly
132
+
133
+ | Module | Endpoint | Notes |
134
+ |--------|----------|-------|
135
+ | `client.ts` `intelligentSearch` | IS `product_search`, `facets` | Wrapped inline, uses default TTL |
136
+
137
+ ---
138
+
139
+ ## Comparison with `deco-cx/deco` `fetchCache.ts`
140
+
141
+ | Feature | deco-cx/deco | @decocms/apps |
142
+ |---------|-------------|---------------|
143
+ | Storage | `CacheStorage` (Web Cache API) | In-memory `Map` (LRU) |
144
+ | Persistence | Disk-backed (Deno CacheStorage) | Process-lifetime only |
145
+ | Max entries | Unlimited (disk) | 500 (memory) |
146
+ | TTL source | HTTP `Cache-Control` headers | Status-based defaults |
147
+ | SWR | `stale-while-revalidate` header parsing | Manual background refresh |
148
+ | Dedup | Separate `singleFlight` wrapper | Built into `fetchWithCache` |
149
+ | Redis/FS tiers | Yes (`tiered.ts`: LRU → FS → Redis) | No — single in-memory tier |
150
+
151
+ ### Why In-Memory Only?
152
+
153
+ Cloudflare Workers don't have persistent storage APIs accessible during SSR. The Cache API (`caches.default`) is for edge HTTP responses, not arbitrary data. In-memory with LRU is the practical choice for Workers.
154
+
155
+ ---
156
+
157
+ ## Diagnostics
158
+
159
+ ### Check Cache Stats
160
+
161
+ ```typescript
162
+ import { getFetchCacheStats, clearFetchCache } from "@decocms/apps/vtex/utils/fetchCache";
163
+
164
+ console.log(getFetchCacheStats());
165
+ // { entries: 42, inflight: 0 }
166
+ ```
167
+
168
+ ### Clear Cache
169
+
170
+ ```typescript
171
+ clearFetchCache(); // Useful after decofile hot-reload
172
+ ```
173
+
174
+ ### Verify Cache Hits in Logs
175
+
176
+ With instrumented fetch (`createInstrumentedFetch("vtex")`), look for timing:
177
+ - **Cache HIT**: No `[vtex] GET ...` log appears (response served from cache)
178
+ - **Cache MISS**: `[vtex] GET ...` followed by `[vtex] 200 GET ... Xms`
179
+ - **SWR refresh**: `[vtex] GET ...` appears AFTER the response was already served
180
+
181
+ ### Common Issues
182
+
183
+ **Stale data after CMS update**: Wait 3 min for TTL to expire, or restart the dev server to clear in-memory cache.
184
+
185
+ **Cache not working in dev**: `fetchWithCache` works in both dev and prod. Unlike `createCachedLoader` which skips SWR in dev (only dedup), `fetchWithCache` always caches.
186
+
187
+ **POST requests not cached**: By design — `vtexCachedFetch` only caches GET. Use `usePriceSimulationBatch` for simulation POST optimization.
188
+
189
+ ---
190
+
191
+ ## Adding Cache to a New VTEX Endpoint
192
+
193
+ ```typescript
194
+ // Before — no cache
195
+ const data = await vtexFetch<MyType>(`/api/my-endpoint/${id}`);
196
+
197
+ // After — with SWR cache
198
+ const data = await vtexCachedFetch<MyType>(`/api/my-endpoint/${id}`);
199
+
200
+ // With custom TTL
201
+ const data = await vtexCachedFetch<MyType>(
202
+ `/api/my-endpoint/${id}`,
203
+ undefined,
204
+ { cacheTTL: 60_000 }, // 1 min
205
+ );
206
+ ```
207
+
208
+ For non-VTEX APIs, use `fetchWithCache` directly:
209
+
210
+ ```typescript
211
+ import { fetchWithCache } from "@decocms/apps/vtex/utils/fetchCache";
212
+
213
+ const data = await fetchWithCache<MyType>(url, () => fetch(url, init));
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Related Skills
219
+
220
+ | Skill | Purpose |
221
+ |-------|---------|
222
+ | `deco-api-call-dedup` | Higher-level dedup patterns (slugCache, batching, PLP filtering) |
223
+ | `deco-cms-layout-caching` | Layout section caching (works on top of fetch cache) |
224
+ | `deco-edge-caching` | Cloudflare edge caching (HTTP level, outside the Worker) |
225
+ | `deco-tanstack-storefront-patterns` | General storefront patterns + `createCachedLoader` |
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: find-skills
3
+ description: Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
4
+ ---
5
+
6
+ # Find Skills
7
+
8
+ This skill helps you discover and install skills from the open agent skills ecosystem.
9
+
10
+ ## When to Use This Skill
11
+
12
+ Use this skill when the user:
13
+
14
+ - Asks "how do I do X" where X might be a common task with an existing skill
15
+ - Says "find a skill for X" or "is there a skill for X"
16
+ - Asks "can you do X" where X is a specialized capability
17
+ - Expresses interest in extending agent capabilities
18
+ - Wants to search for tools, templates, or workflows
19
+ - Mentions they wish they had help with a specific domain (design, testing, deployment, etc.)
20
+
21
+ ## What is the Skills CLI?
22
+
23
+ The Skills CLI (`npx skills`) is the package manager for the open agent skills ecosystem. Skills are modular packages that extend agent capabilities with specialized knowledge, workflows, and tools.
24
+
25
+ **Key commands:**
26
+
27
+ - `npx skills find [query]` - Search for skills interactively or by keyword
28
+ - `npx skills add <package>` - Install a skill from GitHub or other sources
29
+ - `npx skills check` - Check for skill updates
30
+ - `npx skills update` - Update all installed skills
31
+
32
+ **Browse skills at:** https://skills.sh/
33
+
34
+ ## How to Help Users Find Skills
35
+
36
+ ### Step 1: Understand What They Need
37
+
38
+ When a user asks for help with something, identify:
39
+
40
+ 1. The domain (e.g., React, testing, design, deployment)
41
+ 2. The specific task (e.g., writing tests, creating animations, reviewing PRs)
42
+ 3. Whether this is a common enough task that a skill likely exists
43
+
44
+ ### Step 2: Search for Skills
45
+
46
+ Run the find command with a relevant query:
47
+
48
+ ```bash
49
+ npx skills find [query]
50
+ ```
51
+
52
+ For example:
53
+
54
+ - User asks "how do I make my React app faster?" → `npx skills find react performance`
55
+ - User asks "can you help me with PR reviews?" → `npx skills find pr review`
56
+ - User asks "I need to create a changelog" → `npx skills find changelog`
57
+
58
+ The command will return results like:
59
+
60
+ ```
61
+ Install with npx skills add <owner/repo@skill>
62
+
63
+ vercel-labs/agent-skills@vercel-react-best-practices
64
+ └ https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
65
+ ```
66
+
67
+ ### Step 3: Present Options to the User
68
+
69
+ When you find relevant skills, present them to the user with:
70
+
71
+ 1. The skill name and what it does
72
+ 2. The install command they can run
73
+ 3. A link to learn more at skills.sh
74
+
75
+ Example response:
76
+
77
+ ```
78
+ I found a skill that might help! The "vercel-react-best-practices" skill provides
79
+ React and Next.js performance optimization guidelines from Vercel Engineering.
80
+
81
+ To install it:
82
+ npx skills add vercel-labs/agent-skills@vercel-react-best-practices
83
+
84
+ Learn more: https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
85
+ ```
86
+
87
+ ### Step 4: Offer to Install
88
+
89
+ If the user wants to proceed, you can install the skill for them:
90
+
91
+ ```bash
92
+ npx skills add <owner/repo@skill> -g -y
93
+ ```
94
+
95
+ The `-g` flag installs globally (user-level) and `-y` skips confirmation prompts.
96
+
97
+ ## Common Skill Categories
98
+
99
+ When searching, consider these common categories:
100
+
101
+ | Category | Example Queries |
102
+ | --------------- | ---------------------------------------- |
103
+ | Web Development | react, nextjs, typescript, css, tailwind |
104
+ | Testing | testing, jest, playwright, e2e |
105
+ | DevOps | deploy, docker, kubernetes, ci-cd |
106
+ | Documentation | docs, readme, changelog, api-docs |
107
+ | Code Quality | review, lint, refactor, best-practices |
108
+ | Design | ui, ux, design-system, accessibility |
109
+ | Productivity | workflow, automation, git |
110
+
111
+ ## Tips for Effective Searches
112
+
113
+ 1. **Use specific keywords**: "react testing" is better than just "testing"
114
+ 2. **Try alternative terms**: If "deploy" doesn't work, try "deployment" or "ci-cd"
115
+ 3. **Check popular sources**: Many skills come from `vercel-labs/agent-skills` or `ComposioHQ/awesome-claude-skills`
116
+
117
+ ## When No Skills Are Found
118
+
119
+ If no relevant skills exist:
120
+
121
+ 1. Acknowledge that no existing skill was found
122
+ 2. Offer to help with the task directly using your general capabilities
123
+ 3. Suggest the user could create their own skill with `npx skills init`
124
+
125
+ Example:
126
+
127
+ ```
128
+ I searched for skills related to "xyz" but didn't find any matches.
129
+ I can still help you with this task directly! Would you like me to proceed?
130
+
131
+ If this is something you do often, you could create your own skill:
132
+ npx skills init my-xyz-skill
133
+ ```