@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,341 @@
1
+ # Client-Side Patterns
2
+
3
+ How Deco storefronts handle client-side interactivity via islands, the invoke proxy, signals, analytics, and SDK utilities.
4
+
5
+ ## Invoke Proxy (`runtime.ts`)
6
+
7
+ Every site creates a typed invoke proxy that bridges client-side code to server loaders/actions:
8
+
9
+ ```typescript
10
+ // runtime.ts
11
+ import { proxy } from "@deco/deco/web";
12
+ import type { Manifest } from "./manifest.gen.ts";
13
+ import type { Manifest as VTEXManifest } from "apps/vtex/manifest.gen.ts";
14
+
15
+ export const invoke = proxy<Manifest & VTEXManifest>();
16
+ ```
17
+
18
+ Merging `Manifest & VTEXManifest` gives typed access to both site-level and VTEX loaders/actions.
19
+
20
+ ### Usage Patterns
21
+
22
+ **Direct typed call:**
23
+
24
+ ```typescript
25
+ import { invoke } from "../runtime.ts";
26
+
27
+ const products = await invoke.vtex.loaders.legacy.productList({
28
+ fq: `productId:${id}`,
29
+ count: 1,
30
+ });
31
+ ```
32
+
33
+ **Key-based call (dynamic loader from CMS):**
34
+
35
+ ```typescript
36
+ const suggestions = await invoke({
37
+ key: __resolveType, // e.g., "vtex/loaders/intelligentSearch/suggestions.ts"
38
+ props: { query, count: 5 },
39
+ });
40
+ ```
41
+
42
+ This pattern is used when the loader key comes from a `Resolved<T>` prop set in the CMS, allowing the admin to choose which loader provides data.
43
+
44
+ **Action call:**
45
+
46
+ ```typescript
47
+ const user = await invoke.site.actions.checkUser();
48
+ const orders = await invoke.site.loaders.listActiveOrders();
49
+ ```
50
+
51
+ ## Islands Architecture
52
+
53
+ Islands are Preact components that hydrate on the client. They're the boundary between server-rendered HTML and client-side interactivity.
54
+
55
+ ### When to Use Islands
56
+
57
+ | Use Island | Use Section/Component |
58
+ |------------|----------------------|
59
+ | Click handlers, form inputs | Static display |
60
+ | Client-side state (signals) | Server-rendered data |
61
+ | API calls (invoke) | CMS-configured layout |
62
+ | Animations, IntersectionObserver | SEO-critical content |
63
+
64
+ ### Island File Convention
65
+
66
+ Islands live in `islands/` and are registered in `fresh.gen.ts`:
67
+
68
+ ```typescript
69
+ // islands/CartRetrofit/AddToCartButton.tsx
70
+ import { useSignal } from "@preact/signals";
71
+ import { useCart } from "apps/vtex/hooks/useCart.ts";
72
+ import { sendAnalyticsEvent } from "$store/sdk/analyticsRetrofit.tsx";
73
+
74
+ interface Props {
75
+ skuId: string;
76
+ sellerId: string;
77
+ productName: string;
78
+ }
79
+
80
+ export default function AddToCartButton({ skuId, sellerId, productName }: Props) {
81
+ const loading = useSignal(false);
82
+ const { addItems } = useCart();
83
+
84
+ const handleClick = async () => {
85
+ loading.value = true;
86
+ await addItems({ orderItems: [{ id: skuId, seller: sellerId, quantity: 1 }] });
87
+ sendAnalyticsEvent({ name: "add_to_cart", params: { items: [{ item_name: productName }] } });
88
+ loading.value = false;
89
+ };
90
+
91
+ return (
92
+ <button onClick={handleClick} disabled={loading.value}>
93
+ {loading.value ? "Adding..." : "Add to Cart"}
94
+ </button>
95
+ );
96
+ }
97
+ ```
98
+
99
+ ## Signals (`@preact/signals`)
100
+
101
+ Deco sites use Preact signals for reactive client-side state. Common patterns:
102
+
103
+ ### Global UI State
104
+
105
+ ```typescript
106
+ // sdk/useUIRetrofit.ts
107
+ import { signal } from "@preact/signals";
108
+
109
+ const displayCart = signal(false);
110
+ const displayMenu = signal(false);
111
+ const displaySearchDrawer = signal(false);
112
+ const displayWishlistModal = signal(false);
113
+
114
+ export const useUI = () => ({
115
+ displayCart,
116
+ displayMenu,
117
+ displaySearchDrawer,
118
+ displayWishlistModal,
119
+ });
120
+ ```
121
+
122
+ Islands import `useUI()` to toggle drawers, modals, and overlays:
123
+
124
+ ```typescript
125
+ const { displayCart } = useUI();
126
+ displayCart.value = true; // opens cart drawer
127
+ ```
128
+
129
+ ### Loading / Pagination State
130
+
131
+ ```typescript
132
+ // sdk/useShowMoreRetrofit.ts
133
+ import { signal } from "@preact/signals";
134
+
135
+ const currentPage = signal(0);
136
+ const loading = signal(false);
137
+
138
+ export const useShowMore = () => ({ currentPage, loading });
139
+ ```
140
+
141
+ ### Local Component State
142
+
143
+ ```typescript
144
+ import { useSignal } from "@preact/signals";
145
+
146
+ export default function SizeSelector() {
147
+ const selectedSize = useSignal<string | null>(null);
148
+ const expandText = useSignal(false);
149
+
150
+ return (
151
+ <div>
152
+ <button onClick={() => (expandText.value = !expandText.value)}>
153
+ {expandText.value ? "Less" : "More"}
154
+ </button>
155
+ {/* size buttons update selectedSize.value */}
156
+ </div>
157
+ );
158
+ }
159
+ ```
160
+
161
+ ## usePartialSection (Infinite Scroll)
162
+
163
+ `usePartialSection` enables server-rendered partial updates without full page navigation. Used for "Load More" in PLPs:
164
+
165
+ ```typescript
166
+ // components/searchRetrofit/Result.tsx
167
+ import { usePartialSection } from "deco/hooks/usePartialSection.ts";
168
+
169
+ function LoadMoreButton({ nextPage }: { nextPage: string }) {
170
+ return (
171
+ <button
172
+ {...usePartialSection({
173
+ href: nextPage,
174
+ mode: "append", // appends new content below existing
175
+ })}
176
+ >
177
+ Load More
178
+ </button>
179
+ );
180
+ }
181
+ ```
182
+
183
+ How it works:
184
+ 1. Button click triggers a fetch to `/deco/render` with the section's resolve chain
185
+ 2. Server renders the section with updated props (next page)
186
+ 3. Response HTML is appended to the DOM (in `append` mode)
187
+ 4. No client-side JS needed for the content itself
188
+
189
+ ## Analytics
190
+
191
+ ### Event Dispatch
192
+
193
+ Two systems work together:
194
+
195
+ ```typescript
196
+ // sdk/analyticsRetrofit.tsx
197
+ export function sendEvent(event: AnalyticsEvent) {
198
+ globalThis.window.DECO.events.dispatch(event);
199
+ }
200
+ ```
201
+
202
+ ```typescript
203
+ // components/scriptRetrofit/sendAnalyticsEvent.tsx
204
+ export function sendAnalyticsEvent(event: AnalyticsEvent) {
205
+ // 1. Push to GTM dataLayer
206
+ globalThis.dataLayer?.push({ event: event.name, ecommerce: event.params });
207
+ // 2. Dispatch to Deco events system
208
+ globalThis.window.DECO?.events?.dispatch(event);
209
+ }
210
+ ```
211
+
212
+ ### Event Components
213
+
214
+ **SendEventOnClick** -- fires when element is clicked:
215
+
216
+ ```tsx
217
+ <SendEventOnClick event={{
218
+ name: "select_item",
219
+ params: { items: [{ item_id: product.sku, item_name: product.name }] }
220
+ }} id={elementId} />
221
+ ```
222
+
223
+ Injects a script that adds a click listener to the element.
224
+
225
+ **SendEventOnLoad** -- fires when component mounts:
226
+
227
+ ```tsx
228
+ <SendEventOnLoad event={{
229
+ name: "view_item_list",
230
+ params: { items: products.map(productToAnalyticsItem) }
231
+ }} />
232
+ ```
233
+
234
+ **IntersectionObserver-based** -- fires when element is visible:
235
+
236
+ ```tsx
237
+ <SendAnalyticsEventOnLoad rootId={elementId} event={{
238
+ name: "view_promotion",
239
+ params: { creative_name: bannerName }
240
+ }} />
241
+ ```
242
+
243
+ ### Data Attributes
244
+
245
+ Product cards use `data-deco` and `data-product-*` for tracking:
246
+
247
+ ```tsx
248
+ <a href={url} data-deco="view-product" data-product-id={productId} data-product-name={name}>
249
+ ...
250
+ </a>
251
+ ```
252
+
253
+ ### VTEX Search Events
254
+
255
+ ```typescript
256
+ // sdk/useSearchEvents.ts
257
+ // Sends events to VTEX SP (search personalization):
258
+ // - search.query -- when user searches
259
+ // - search.autocomplete.query -- autocomplete queries
260
+ // - search.click -- when user clicks a search result
261
+ // - session.ping -- heartbeat
262
+ // - page.confirmation -- page view
263
+ ```
264
+
265
+ ## Cart / Wishlist / User Hooks
266
+
267
+ These come from the VTEX app (`apps/vtex/hooks/`):
268
+
269
+ ### useCart
270
+
271
+ ```typescript
272
+ import { useCart } from "apps/vtex/hooks/useCart.ts";
273
+
274
+ const { cart, addItems, updateItems, loading } = useCart();
275
+ ```
276
+
277
+ Provides reactive cart state with mutations. Automatically syncs with VTEX OrderForm API.
278
+
279
+ ### useWishlist
280
+
281
+ ```typescript
282
+ import { useWishlist } from "apps/vtex/hooks/useWishlist.ts";
283
+
284
+ const { addItem, removeItem, getItem, loading } = useWishlist();
285
+
286
+ const isInWishlist = useComputed(() => !!getItem({ productId, skuId }));
287
+ ```
288
+
289
+ ### useUser
290
+
291
+ ```typescript
292
+ import { useUser } from "apps/vtex/hooks/useUser.ts";
293
+
294
+ const { user } = useUser();
295
+ ```
296
+
297
+ Sites often wrap this with a custom `useCheckUser` that caches in sessionStorage:
298
+
299
+ ```typescript
300
+ // hooks/useCheckUser.ts
301
+ export function useCheckUser() {
302
+ const cached = sessionStorage.getItem("user");
303
+ if (cached) return JSON.parse(cached);
304
+ const user = await invoke.site.actions.checkUser();
305
+ sessionStorage.setItem("user", JSON.stringify(user));
306
+ return user;
307
+ }
308
+ ```
309
+
310
+ ## SDK Utilities
311
+
312
+ Common SDK utilities found in production sites:
313
+
314
+ | Utility | Purpose |
315
+ |---------|---------|
316
+ | `useUI` | Global UI state signals (cart drawer, menu, search, wishlist modal) |
317
+ | `formatPrice` | `Intl.NumberFormat` with BRL/locale caching |
318
+ | `useOffer` | Extract price, listPrice, installments, availability from `AggregateOffer` |
319
+ | `useLazyLoad` | IntersectionObserver hook returning `[isVisible, setTarget]` |
320
+ | `useAddToCart` | Combines `useCart` + `useUI` + analytics for add-to-cart flow |
321
+ | `useSuggestions` | Debounced autocomplete via invoke + signals |
322
+ | `useVariantPossibilities` | Map product variants by spec (color, size) for selector UI |
323
+ | `useSizes` | Ordered size map (PP, P, M, G, GG) from variants |
324
+ | `useColumns` | Grid column count signals for PLP layout toggle |
325
+ | `useShowMore` | Pagination signals for infinite scroll |
326
+ | `useWindowSize` | Window dimensions with resize listener |
327
+ | `useIdRetrofit` | Unique ID generator (Fresh/Preact workaround) |
328
+ | `fetchStockBySku` | Batch SKU stock check via VTEX API |
329
+
330
+ ## TanStack Migration Notes
331
+
332
+ When migrating these patterns to TanStack Start:
333
+
334
+ | Fresh/Preact Pattern | TanStack Equivalent |
335
+ |---------------------|---------------------|
336
+ | `invoke` proxy | Direct function imports + React Query |
337
+ | `@preact/signals` | `@tanstack/react-store` or `useState` |
338
+ | Islands | Regular React components (all hydrated) |
339
+ | `usePartialSection` | TanStack Router search params + React Query |
340
+ | `sendAnalyticsEvent` | Same pattern, use `useEffect` for load events |
341
+ | `useCart` / `useWishlist` | React Query mutations from `apps-start/vtex/hooks/` |
@@ -0,0 +1,230 @@
1
+ # CMS Wiring
2
+
3
+ How the Deco CMS connects to the storefront via the decofile and `__resolveType` references.
4
+
5
+ ## The Decofile (`.deco/blocks/`)
6
+
7
+ The CMS state lives in `.deco/blocks/` as JSON files. Each file is a "block" -- a configuration object that the resolution engine processes at runtime.
8
+
9
+ ### File Types
10
+
11
+ | Pattern | Purpose | Example |
12
+ |---------|---------|---------|
13
+ | `site.json` | Root configuration (global sections, routes, SEO, theme) | Always one per site |
14
+ | `everyone.json` | Route definitions via flag (matches all requests) | URL-to-page mapping |
15
+ | `pages-*.json` | Page blocks with sections, variants, matchers | `pages-homeretrofit-ecosystem-2794ebefc7d8.json` |
16
+ | `redirects-*.json` | Individual redirect definitions | `redirects-masculino-33185.json` |
17
+ | `redirects-from-csv.json` | Bulk redirects from CSV file | One per import |
18
+ | `vtex.json` | VTEX app configuration (account, secrets) | One per VTEX site |
19
+ | `vtex_proxy.json` | VTEX proxy route configuration | Checkout/API proxy |
20
+ | `*.json` (other) | Reusable blocks (shelves, videos, banners) | `shelf-suggestions.json` |
21
+
22
+ ## `__resolveType` -- The Wiring Mechanism
23
+
24
+ Every block has a `__resolveType` field that tells the engine which resolver (loader, section, handler, matcher) to use:
25
+
26
+ ```json
27
+ {
28
+ "__resolveType": "vtex/loaders/intelligentSearch/productListingPage.ts",
29
+ "query": "shoes",
30
+ "count": 12,
31
+ "sort": "OrderByScoreDESC"
32
+ }
33
+ ```
34
+
35
+ The engine:
36
+ 1. Finds the resolver registered for that key in the manifest
37
+ 2. Resolves all nested `__resolveType` references in props (recursive)
38
+ 3. Calls the resolver function with the resolved props
39
+ 4. Returns the result
40
+
41
+ ### Nesting Example
42
+
43
+ A section's props in the CMS can reference a loader:
44
+
45
+ ```json
46
+ {
47
+ "__resolveType": "site/sections/ProductRetrofit/ProductShelf.tsx",
48
+ "products": {
49
+ "__resolveType": "vtex/loaders/intelligentSearch/productListingPage.ts",
50
+ "query": "new arrivals",
51
+ "count": 8
52
+ },
53
+ "title": "New Arrivals"
54
+ }
55
+ ```
56
+
57
+ The engine resolves `products` first (calls the VTEX loader), then passes the result to the section component as the `products` prop.
58
+
59
+ ## `site.json` -- Root Configuration
60
+
61
+ The root block defines the site's global configuration:
62
+
63
+ ```json
64
+ {
65
+ "global": [
66
+ { "__resolveType": "site/sections/Theme/Theme.tsx", "mainColors": { "base-100": "#FFFFFF" } },
67
+ { "__resolveType": "site/sections/Analytics/Analytics.tsx", "trackingIds": ["GTM-XXXX"] },
68
+ { "__resolveType": "site/sections/PromoBar/PromoBar.tsx", "text": "Free shipping" }
69
+ ],
70
+ "routes": [
71
+ { "__resolveType": "website/loaders/pages.ts" },
72
+ { "__resolveType": "vtex/loaders/proxy.ts" },
73
+ { "__resolveType": "website/loaders/redirectsFromCsv.ts", "from": "utils/redirects.csv" },
74
+ { "__resolveType": "website/loaders/redirects.ts" }
75
+ ],
76
+ "seo": {
77
+ "title": "My Store",
78
+ "description": "...",
79
+ "titleTemplate": "%s | My Store",
80
+ "image": "https://..."
81
+ },
82
+ "theme": { "__resolveType": "site/sections/Theme/Theme.tsx" },
83
+ "commerce": { "platform": "vtex" },
84
+ "__resolveType": "site/apps/site.ts"
85
+ }
86
+ ```
87
+
88
+ ### Global Sections
89
+
90
+ `global` contains sections rendered on every page (analytics, promo bars, chat widgets). They're resolved once and injected into every response.
91
+
92
+ ### Routes
93
+
94
+ `routes` defines the URL resolution order:
95
+ 1. `website/loaders/pages.ts` -- CMS page matching
96
+ 2. `vtex/loaders/proxy.ts` -- VTEX checkout/API proxy
97
+ 3. `website/loaders/redirectsFromCsv.ts` -- bulk redirects from CSV
98
+ 4. `website/loaders/redirects.ts` -- individual CMS redirect blocks
99
+
100
+ Order matters -- first match wins.
101
+
102
+ ## `everyone.json` -- Route Definitions
103
+
104
+ The flag `$live/flags/everyone.ts` wraps route definitions that apply to all requests:
105
+
106
+ ```json
107
+ {
108
+ "__resolveType": "$live/flags/everyone.ts",
109
+ "true": {
110
+ "routes": [
111
+ {
112
+ "pathTemplate": "/",
113
+ "handler": {
114
+ "__resolveType": "$live/handlers/fresh.ts",
115
+ "page": { "__resolveType": "pages-homeretrofit-ecosystem-2794ebefc7d8" }
116
+ }
117
+ },
118
+ {
119
+ "pathTemplate": "/*",
120
+ "handler": {
121
+ "__resolveType": "$live/handlers/fresh.ts",
122
+ "page": { "__resolveType": "pages-category-42a3f" }
123
+ }
124
+ }
125
+ ]
126
+ }
127
+ }
128
+ ```
129
+
130
+ Each route maps a `pathTemplate` to a handler that references a page block.
131
+
132
+ ## Page Blocks (`pages-*.json`)
133
+
134
+ Page blocks define which sections render on a page, with optional A/B testing via variants:
135
+
136
+ ```json
137
+ {
138
+ "name": "Home",
139
+ "path": "/",
140
+ "sections": {
141
+ "__resolveType": "website/flags/multivariate.ts",
142
+ "variants": [
143
+ {
144
+ "rule": { "__resolveType": "website/matchers/device.ts", "mobile": true },
145
+ "value": [
146
+ { "__resolveType": "site/sections/HeroBannerRetrofit/HeroBanner.tsx", "image": "..." },
147
+ { "__resolveType": "site/sections/ProductRetrofit/ProductShelf.tsx", "products": { "__resolveType": "vtex/loaders/..." } }
148
+ ]
149
+ },
150
+ {
151
+ "rule": { "__resolveType": "$live/matchers/MatchAlways.ts" },
152
+ "value": [
153
+ { "__resolveType": "site/sections/HeroBannerRetrofit/HeroBanner.tsx", "image": "..." }
154
+ ]
155
+ }
156
+ ]
157
+ }
158
+ }
159
+ ```
160
+
161
+ The resolver evaluates matchers in order. First matching variant wins. `MatchAlways` is the default fallback.
162
+
163
+ ## Redirect System
164
+
165
+ ### Individual Redirects
166
+
167
+ Each redirect is a separate block in `.deco/blocks/`:
168
+
169
+ ```json
170
+ {
171
+ "redirect": {
172
+ "from": "/masculino",
173
+ "to": "/masculino-ver-todos",
174
+ "type": "temporary"
175
+ },
176
+ "__resolveType": "website/loaders/redirect.ts"
177
+ }
178
+ ```
179
+
180
+ ### Bulk Redirects from CSV
181
+
182
+ The `redirects-from-csv.json` block references a CSV file:
183
+
184
+ ```json
185
+ {
186
+ "from": "utils/redirects.csv",
187
+ "forcePermanentRedirects": true,
188
+ "__resolveType": "website/loaders/redirectsFromCsv.ts"
189
+ }
190
+ ```
191
+
192
+ CSV format: `from,to,type` (one redirect per line).
193
+
194
+ ### Redirect Aggregation
195
+
196
+ `website/loaders/redirects.ts` in the routes array aggregates all individual `website/loaders/redirect.ts` blocks into a single redirect handler.
197
+
198
+ ## Dependency Resolution via `ctx.get()`
199
+
200
+ Loaders and actions can resolve other blocks at runtime using `ctx.get()`:
201
+
202
+ ```typescript
203
+ export const loader = async (props: Props, req: Request, ctx: AppContext) => {
204
+ const { credentials } = await ctx.get({
205
+ "__resolveType": "Tokens",
206
+ });
207
+ const { appKey, appToken } = credentials;
208
+ // Use VTEX API with credentials
209
+ };
210
+ ```
211
+
212
+ This pattern is used for:
213
+ - **Secrets** (API keys, tokens) -- resolved from CMS-managed Secret blocks
214
+ - **Configuration** -- any shared config block referenced by multiple loaders
215
+ - **Nested block resolution** -- composing data from multiple sources
216
+
217
+ ## Admin Selector
218
+
219
+ When a section prop's TypeScript type matches a loader's return type, the admin shows a dropdown to select which loader provides the data. For example:
220
+
221
+ ```typescript
222
+ interface Props {
223
+ page: ProductDetailsPage | null;
224
+ products: Product[] | null;
225
+ }
226
+ ```
227
+
228
+ The admin knows that `ProductDetailsPage` can be produced by `vtex/loaders/intelligentSearch/productDetailsPage.ts` or `vtex/loaders/legacy/productDetailsPage.ts`, and shows both options.
229
+
230
+ This is powered by the schema system -- `generate-schema.ts` extracts prop types and the admin matches them against loader return types.