@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,340 @@
1
+ # Section Patterns
2
+
3
+ Sections are the primary building blocks of a Deco storefront. They are Preact components registered in the manifest and configurable via the CMS admin.
4
+
5
+ ## Section File Conventions
6
+
7
+ ### Pattern 1: Re-export
8
+
9
+ The section file re-exports a component from `components/`. Keeps the section thin and the component reusable.
10
+
11
+ ```typescript
12
+ // sections/HeaderRetrofit/Header.tsx
13
+ export { default } from "$store/components/headerRetrofit/Header.tsx";
14
+
15
+ export const LoadingFallback = () => <div class="absolute" />;
16
+ ```
17
+
18
+ ### Pattern 2: Wrapper with Loader
19
+
20
+ The section wraps a component and adds a loader for server-side data:
21
+
22
+ ```typescript
23
+ // sections/HeroBannerRetrofit/HeroBanner.tsx
24
+ import HeroBanner from "$store/components/uiRetrofit/HeroBanner/HeroBanner.tsx";
25
+ import { MatchDevice } from "apps/website/matchers/device.ts";
26
+
27
+ export interface Props {
28
+ /** @title Banner desktop */
29
+ desktop: BannerProps;
30
+ /** @title Banner mobile */
31
+ mobile: BannerProps;
32
+ }
33
+
34
+ export default function HeroBannerWrapper(props: Props & { isMobile: boolean }) {
35
+ return <HeroBanner {...props} />;
36
+ }
37
+
38
+ export function loader(props: Props, req: Request, ctx: AppContext) {
39
+ return { ...props, isMobile: MatchDevice(req.headers, "mobile") };
40
+ }
41
+ ```
42
+
43
+ ### Pattern 3: Full Implementation
44
+
45
+ The section contains both component and loader logic in one file (or re-exports both from a component file):
46
+
47
+ ```typescript
48
+ // sections/ProductRetrofit/ProductDetails.tsx
49
+ export { default, loader } from "../../components/productRetrofit/Details/index.tsx";
50
+ ```
51
+
52
+ Where the component file has:
53
+
54
+ ```typescript
55
+ // components/productRetrofit/Details/index.tsx
56
+ export interface Props {
57
+ page: ProductDetailsPage | null;
58
+ /** @title Enable 3D View */
59
+ enableThreeDView?: boolean;
60
+ }
61
+
62
+ export default function ProductDetails({ page, enableThreeDView }: Props) {
63
+ if (!page) return <NotFound />;
64
+ return <div>...</div>;
65
+ }
66
+
67
+ export const loader = async (props: Props, req: Request, ctx: AppContext) => {
68
+ const { credentials } = await ctx.get({ "__resolveType": "Tokens" });
69
+ const isMobile = MatchDevice(req.headers, "mobile");
70
+ // Fetch additional data, check user, etc.
71
+ return { ...props, isMobile, credentials };
72
+ };
73
+ ```
74
+
75
+ ## JSDoc Annotations for Admin
76
+
77
+ JSDoc tags on props control how the admin renders form fields.
78
+
79
+ ### @title -- Field Label
80
+
81
+ ```typescript
82
+ interface Props {
83
+ /** @title Titulo principal */
84
+ heading: string;
85
+ }
86
+ ```
87
+
88
+ ### @description -- Help Text
89
+
90
+ ```typescript
91
+ interface Props {
92
+ /**
93
+ * @title Cor de fundo
94
+ * @description Define a cor de fundo da pagina
95
+ */
96
+ backgroundColor?: string;
97
+ }
98
+ ```
99
+
100
+ ### @format -- Input Type
101
+
102
+ | Format | Admin Input | Example |
103
+ |--------|-------------|---------|
104
+ | `color` | Color picker | `@format color` on `string` |
105
+ | `html` | Rich text editor | `@format html` on `string` |
106
+ | `textarea` | Multiline text | `@format textarea` on `string` |
107
+
108
+ ```typescript
109
+ interface Props {
110
+ /**
111
+ * @title Cor primaria
112
+ * @format color
113
+ * @default #003232
114
+ */
115
+ primary: string;
116
+
117
+ /** @format html */
118
+ richContent: string;
119
+
120
+ /** @format textarea */
121
+ description: string;
122
+ }
123
+ ```
124
+
125
+ ### @default -- Default Value
126
+
127
+ ```typescript
128
+ interface Props {
129
+ /**
130
+ * @title Cor base
131
+ * @format color
132
+ * @default #FFFFFF
133
+ */
134
+ "base-100": string;
135
+ }
136
+ ```
137
+
138
+ ### @ignore -- Hide from Admin
139
+
140
+ ```typescript
141
+ interface Props {
142
+ /** @ignore */
143
+ isMobile?: boolean;
144
+ }
145
+ ```
146
+
147
+ Used for loader-injected props that shouldn't appear in the admin form.
148
+
149
+ ## Widget Types
150
+
151
+ Import from `apps/admin/widgets.ts` to get special admin input types:
152
+
153
+ ```typescript
154
+ import { ImageWidget } from "apps/admin/widgets.ts";
155
+ import { HTMLWidget } from "apps/admin/widgets.ts";
156
+ import { VideoWidget } from "apps/admin/widgets.ts";
157
+ import { Secret } from "apps/website/loaders/secret.ts";
158
+ ```
159
+
160
+ | Type | Admin Input | Usage |
161
+ |------|-------------|-------|
162
+ | `ImageWidget` | Image uploader (Deco CDN) | Banners, logos, product images |
163
+ | `HTMLWidget` | Rich text editor | Descriptions, content blocks |
164
+ | `VideoWidget` | Video URL input | Video sections |
165
+ | `Secret` | Encrypted value (not shown) | API keys, tokens |
166
+
167
+ Legacy alias: `ImageWidget as LiveImage` is common in older sites.
168
+
169
+ ```typescript
170
+ interface Props {
171
+ /** @description Mobile optimized image */
172
+ srcMobile: ImageWidget;
173
+ /** @description Desktop optimized image */
174
+ srcDesktop?: ImageWidget;
175
+ alt: string;
176
+ href: string;
177
+ }
178
+ ```
179
+
180
+ ## Loader Patterns
181
+
182
+ ### Sync Loader (MatchDevice)
183
+
184
+ The most common pattern. Adds `isMobile` for responsive rendering:
185
+
186
+ ```typescript
187
+ import { MatchDevice } from "apps/website/matchers/device.ts";
188
+ import { AppContext } from "site/apps/site.ts";
189
+
190
+ export function loader(props: Props, req: Request, _ctx: AppContext) {
191
+ return {
192
+ ...props,
193
+ isMobile: MatchDevice(req.headers, "mobile"),
194
+ };
195
+ }
196
+ ```
197
+
198
+ Used in ~18 sections (HeroBanner, PromoBar, ContentSection, CollectionBlock, etc.).
199
+
200
+ ### Async Loader (Data Fetching)
201
+
202
+ For sections that need server-side data:
203
+
204
+ ```typescript
205
+ export const loader = async (props: Props, req: Request, ctx: AppContext) => {
206
+ // Resolve secrets via __resolveType
207
+ const { credentials } = await ctx.get({ "__resolveType": "Tokens" });
208
+
209
+ // Call VTEX API
210
+ const response = await fetch(`https://${account}.vtexcommercestable.com.br/api/...`, {
211
+ headers: { "X-VTEX-API-AppKey": credentials.appKey },
212
+ });
213
+
214
+ // Check user status
215
+ const user = await ctx.invoke("site/actions/checkUser.ts");
216
+
217
+ return { ...props, data: await response.json(), user, isMobile: MatchDevice(req.headers, "mobile") };
218
+ };
219
+ ```
220
+
221
+ Used in ProductDetails, SearchResult, OurStores, InstagramPosts.
222
+
223
+ ### CMS-Wired Data (No Loader Needed)
224
+
225
+ When data comes from the CMS via `__resolveType`, the section receives it as a resolved prop -- no loader needed:
226
+
227
+ ```typescript
228
+ interface Props {
229
+ products: Product[] | null; // Admin shows loader selector
230
+ title: string; // Admin shows text input
231
+ }
232
+
233
+ export default function ProductShelf({ products, title }: Props) {
234
+ if (!products) return null;
235
+ return <div>{title}{products.map(p => <Card product={p} />)}</div>;
236
+ }
237
+ ```
238
+
239
+ The admin lets the editor choose which loader provides `products` (e.g., `vtex/loaders/intelligentSearch/productListingPage.ts`).
240
+
241
+ ## LoadingFallback Pattern
242
+
243
+ Sections can export a `LoadingFallback` component that renders while the section's loader is pending (async rendering):
244
+
245
+ ### Simple Placeholder
246
+
247
+ ```typescript
248
+ export const LoadingFallback = () => <div class="absolute" />;
249
+ ```
250
+
251
+ Prevents layout shift with a minimal placeholder.
252
+
253
+ ### Spinner
254
+
255
+ ```typescript
256
+ export function LoadingFallback() {
257
+ return (
258
+ <div style={{ height: "600px" }} class="flex justify-center items-center">
259
+ <span class="loading loading-spinner" />
260
+ </div>
261
+ );
262
+ }
263
+ ```
264
+
265
+ Fixed-height container with a loading spinner.
266
+
267
+ ### Skeleton with Props
268
+
269
+ Uses `Partial<Props>` to render a structural skeleton that matches the final layout:
270
+
271
+ ```typescript
272
+ export function LoadingFallback(props: Partial<Props>) {
273
+ return (
274
+ <div class="container">
275
+ {props.title && <h2>{props.title}</h2>}
276
+ {props.breadcrumb && <Breadcrumb items={props.breadcrumb} />}
277
+ <div class="flex justify-center items-center h-[400px]">
278
+ <span class="loading loading-spinner" />
279
+ </div>
280
+ </div>
281
+ );
282
+ }
283
+ ```
284
+
285
+ The CMS static props (title, breadcrumb) render immediately while loader data is pending.
286
+
287
+ ### Animated Skeleton
288
+
289
+ Full skeleton with pulse animation placeholders:
290
+
291
+ ```typescript
292
+ function CollectionContentSkeleton() {
293
+ return (
294
+ <div class="grid grid-cols-2 lg:grid-cols-4 gap-4">
295
+ {Array.from({ length: 8 }).map((_, i) => (
296
+ <div key={i} class="animate-pulse">
297
+ <div class="bg-gray-200 aspect-square rounded" />
298
+ <div class="bg-gray-200 h-4 mt-2 rounded w-3/4" />
299
+ <div class="bg-gray-200 h-4 mt-1 rounded w-1/2" />
300
+ </div>
301
+ ))}
302
+ </div>
303
+ );
304
+ }
305
+
306
+ export function LoadingFallback(props: Partial<Props>) {
307
+ return (
308
+ <div class="container">
309
+ <CollectionContentSkeleton />
310
+ </div>
311
+ );
312
+ }
313
+ ```
314
+
315
+ ## Section Organization
316
+
317
+ ### Naming Convention
318
+
319
+ - `{Domain}Retrofit/` prefix groups related sections (e.g., `ProductRetrofit/`, `HeaderRetrofit/`)
320
+ - "Retrofit" indicates a redesigned version; new sites may drop this suffix
321
+ - One file per section, named after the component
322
+
323
+ ### Grouping
324
+
325
+ | Group | Sections |
326
+ |-------|----------|
327
+ | Product | ProductDetails, ProductShelf, SearchResult, WishlistRetrofit |
328
+ | Navigation | Header, Footer, PromoBar |
329
+ | Content | HeroBanner, Banner, Carousel, ImageGallery, Video, Text |
330
+ | Institutional | FAQ, About, ContactUs, OurStores |
331
+ | Marketing | Newsletter, PopupFirstTime, CampaignTimer |
332
+ | Infrastructure | Theme, Seo, Analytics, CookieConsent |
333
+
334
+ ### Section vs Component vs Island
335
+
336
+ | Layer | Location | Renders | Purpose |
337
+ |-------|----------|---------|---------|
338
+ | Section | `sections/` | Server (SSR) | CMS block, registered in manifest |
339
+ | Component | `components/` | Server (SSR) | Reusable, not in manifest |
340
+ | Island | `islands/` | Client (hydrated) | Interactive, has client-side JS |
@@ -0,0 +1,240 @@
1
+ ---
2
+ name: deco-site-scaling-tuning
3
+ description: Discover optimal autoscaling parameters for a Deco site by analyzing Prometheus metrics. Correlates CPU, concurrency, and latency to find the right scaling target and method.
4
+ ---
5
+
6
+ # Deco Site Scaling Tuning
7
+
8
+ Analyze a site's Prometheus metrics to discover the optimal autoscaling parameters. This skill helps you find the CPU/concurrency threshold where latency degrades and recommends scaling configuration accordingly.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - A site is overscaled (too many pods for its traffic)
13
+ - A site oscillates between scaling up and down (panic mode loop)
14
+ - Need to switch scaling metric (concurrency vs CPU vs RPS)
15
+ - Need to find the right target value for a site
16
+ - After deploying scaling changes, to verify they're working
17
+
18
+ ## Prerequisites
19
+
20
+ - `kubectl` access to the target cluster
21
+ - Prometheus accessible via port-forward (from `kube-prometheus-stack` in monitoring namespace)
22
+ - Python 3 for analysis scripts
23
+ - At least 6 hours of metric history for meaningful analysis
24
+ - **For direct latency data**: queue-proxy PodMonitor must be applied (see Step 0)
25
+
26
+ ## Quick Start
27
+
28
+ ```
29
+ 0. ENABLE METRICS → Apply queue-proxy PodMonitor if not already done
30
+ 1. PORT-FORWARD → kubectl port-forward prometheus-pod 19090:9090
31
+ 2. COLLECT DATA → Run analysis scripts against Prometheus
32
+ 3. ANALYZE → Find CPU threshold where latency degrades
33
+ 4. RECOMMEND → Choose scaling metric and target
34
+ 5. APPLY → Use deco-site-deployment skill to apply changes
35
+ 6. VERIFY → Monitor for 1-2 hours after change
36
+ ```
37
+
38
+ ## Files in This Skill
39
+
40
+ | File | Purpose |
41
+ |------|---------|
42
+ | `SKILL.md` | Overview, methodology, analysis procedures |
43
+ | `analysis-scripts.md` | Ready-to-use Python scripts for Prometheus queries |
44
+
45
+ ## Step 0: Enable Queue-Proxy Metrics (one-time)
46
+
47
+ Queue-proxy runs as a sidecar on every Knative pod and exposes request latency histograms. These are critical for precise tuning but are **not scraped by default**.
48
+
49
+ Apply this PodMonitor:
50
+
51
+ ```yaml
52
+ apiVersion: monitoring.coreos.com/v1
53
+ kind: PodMonitor
54
+ metadata:
55
+ name: knative-queue-proxy
56
+ namespace: monitoring
57
+ labels:
58
+ release: kube-prometheus-stack
59
+ spec:
60
+ namespaceSelector:
61
+ any: true
62
+ selector:
63
+ matchExpressions:
64
+ - key: serving.knative.dev/revision
65
+ operator: Exists
66
+ podMetricsEndpoints:
67
+ - port: http-usermetric
68
+ path: /metrics
69
+ interval: 15s
70
+ ```
71
+
72
+ ```bash
73
+ kubectl apply -f queue-proxy-podmonitor.yaml
74
+ # Wait 2-3 hours for data to accumulate before running latency analysis
75
+ ```
76
+
77
+ **Metrics unlocked by this PodMonitor:**
78
+ - `revision_app_request_latencies_bucket` — request latency histogram (p50/p95/p99)
79
+ - `revision_app_request_latencies_sum` / `_count` — for avg latency
80
+ - `revision_app_request_count` — request rate by response code
81
+
82
+ ## Step 1: Establish Prometheus Connection
83
+
84
+ ```bash
85
+ PROM_POD=$(kubectl get pods -n monitoring -l app.kubernetes.io/name=prometheus -o jsonpath='{.items[0].metadata.name}')
86
+ kubectl port-forward -n monitoring $PROM_POD 19090:9090 &
87
+ # Verify
88
+ curl -s "http://127.0.0.1:19090/api/v1/query?query=up" | jq '.status'
89
+ ```
90
+
91
+ ## Step 2: Collect Current State
92
+
93
+ Before analyzing, understand what the site is currently configured for.
94
+
95
+ ### 2a. Read current autoscaler config
96
+
97
+ ```bash
98
+ SITENAME="<sitename>"
99
+ NS="sites-${SITENAME}"
100
+
101
+ # Current revision annotations
102
+ kubectl get rev -n $NS -o json | \
103
+ jq '.items[] | select(.status.conditions[]?.status == "True" and .status.conditions[]?.type == "Active") |
104
+ {name: .metadata.name, annotations: .metadata.annotations | with_entries(select(.key | startswith("autoscaling")))}'
105
+
106
+ # Global autoscaler defaults
107
+ kubectl get cm config-autoscaler -n knative-serving -o json | jq '.data | del(._example)'
108
+ ```
109
+
110
+ ### 2b. Current pod count and resources
111
+
112
+ ```bash
113
+ kubectl get pods -n $NS --no-headers | wc -l
114
+ kubectl top pods -n $NS --no-headers | head -20
115
+ ```
116
+
117
+ ## Step 3: Run Analysis
118
+
119
+ Use the scripts in `analysis-scripts.md`. The analysis follows this methodology:
120
+
121
+ ### Methodology: Finding the Optimal CPU Target
122
+
123
+ **Goal:** Find the CPU level at which latency starts to degrade. This is your scaling target — keep pods below this CPU to maintain good latency.
124
+
125
+ **Approach:**
126
+
127
+ 1. **Collect CPU per pod, concurrency per pod, pod count, and (if available) request latency** over 6-12 hours
128
+ 2. **Bucket data by CPU range** (0-200m, 200-300m, ..., 700m+)
129
+ 3. **For each bucket**, compute avg/p95 concurrency per pod
130
+ 4. **Compute the "latency inflation factor"** — how much concurrency increases beyond what the pod count reduction explains:
131
+ ```
132
+ excess = (avg_conc_above_threshold / avg_conc_below_threshold) / (avg_pods_below / avg_pods_above)
133
+ ```
134
+ - excess = 1.0 → concurrency increase fully explained by fewer pods (no latency degradation)
135
+ - excess > 1.0 → latency is inflating concurrency (pods are slowing down)
136
+ - The CPU level where excess crosses ~1.5x is your inflection point
137
+
138
+ 5. **If queue-proxy latency is available**, directly plot avg latency vs CPU — the hockey stick inflection is your target
139
+
140
+ ### What to Look For
141
+
142
+ ```
143
+ CPU vs Concurrency/pod:
144
+
145
+ Low CPU (0-200m) → Low conc/pod → Pods are idle (overprovisioned)
146
+ Medium CPU (200-400m) → Moderate conc → Healthy range
147
+ ★ INFLECTION ★ → Conc jumps → Latency starting to degrade
148
+ High CPU (500m+) → High conc/pod → Pods overloaded, latency bad
149
+ ```
150
+
151
+ The inflection point is where you want your scaling target.
152
+
153
+ ### Decision Matrix
154
+
155
+ **IMPORTANT:** CPU target is in **millicores** (not percentage). E.g., `target: 400` means scale when CPU reaches 400m.
156
+
157
+ | Inflection CPU | Recommended metric | Target | Notes |
158
+ |---------------|-------------------|--------|-------|
159
+ | < CPU request | CPU scaling | target = inflection value in millicores | Standard case |
160
+ | ~ CPU request | CPU scaling | target = CPU_request × 0.8 | Conservative |
161
+ | > CPU request (no limit) | CPU scaling | target = CPU_request × 0.8, increase CPU request | Need more CPU headroom |
162
+ | No clear inflection | Concurrency scaling | Keep current but tune target | CPU isn't the bottleneck |
163
+
164
+ ### Common Patterns
165
+
166
+ **Pattern: CPU-bound app (Deno SSR)**
167
+ - Baseline CPU: 200-300m (Deno runtime + V8 JIT)
168
+ - Inflection: 400-500m
169
+ - Recommendation: CPU scaling with target = inflection (e.g., 400 millicores)
170
+
171
+ **Pattern: IO-bound app (mostly external API calls)**
172
+ - CPU stays low even under high concurrency
173
+ - Inflection not visible in CPU
174
+ - Recommendation: Keep concurrency scaling, tune the target
175
+
176
+ **Pattern: Oscillating (panic loop)**
177
+ - Symptoms: pods cycle between min and max
178
+ - Cause: concurrency scaling + low target + `scale-down-delay` ratchet
179
+ - Fix: Switch to CPU scaling (breaks the latency→concurrency feedback loop)
180
+
181
+ ## Step 4: Apply Changes
182
+
183
+ Use the `deco-site-deployment` skill to:
184
+ 1. Update the `state` secret with new scaling config
185
+ 2. Redeploy on both clouds
186
+
187
+ Example for CPU-based scaling (target is in millicores):
188
+ ```bash
189
+ NEW_STATE=$(echo "$STATE" | jq '
190
+ .scaling.metric = {
191
+ "type": "cpu",
192
+ "target": 400
193
+ }
194
+ ')
195
+ ```
196
+
197
+ ## Step 5: Verify After Change
198
+
199
+ Monitor for 1-2 hours after applying changes:
200
+
201
+ ```bash
202
+ # Watch pod count stabilize
203
+ watch -n 10 "kubectl get pods -n sites-<sitename> --no-headers | wc -l"
204
+
205
+ # Check if panic mode triggers (should be N/A for HPA/CPU)
206
+ # HPA doesn't have panic mode — this is one of the advantages
207
+
208
+ # Verify HPA is active
209
+ kubectl get hpa -n sites-<sitename>
210
+
211
+ # Check HPA status
212
+ kubectl describe hpa -n sites-<sitename>
213
+ ```
214
+
215
+ ### Success Criteria
216
+
217
+ - Pod count stabilizes (no more oscillation)
218
+ - Avg CPU per pod stays below your target during normal traffic
219
+ - CPU crosses target only during genuine traffic spikes (and scales up proportionally)
220
+ - No panic mode events (HPA doesn't have panic mode)
221
+ - Latency stays acceptable (check with queue-proxy metrics if available)
222
+
223
+ ### Rollback
224
+
225
+ If the new scaling is worse, revert by changing the state secret back to concurrency scaling:
226
+ ```bash
227
+ NEW_STATE=$(echo "$STATE" | jq '
228
+ .scaling.metric = {
229
+ "type": "concurrency",
230
+ "target": 15,
231
+ "targetUtilizationPercentage": 70
232
+ }
233
+ ')
234
+ ```
235
+
236
+ ## Related Skills
237
+
238
+ - `deco-site-deployment` — Apply scaling changes and redeploy
239
+ - `deco-site-memory-debugging` — Debug memory issues on running pods
240
+ - `deco-incident-debugging` — Incident response and triage