@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.
- package/.cursor/skills/deco-api-call-dedup/SKILL.md +443 -0
- package/.cursor/skills/deco-apps-architecture/SKILL.md +255 -0
- package/.cursor/skills/deco-apps-architecture/app-pattern.md +288 -0
- package/.cursor/skills/deco-apps-architecture/commerce-types.md +239 -0
- package/.cursor/skills/deco-apps-architecture/new-app-guide.md +268 -0
- package/.cursor/skills/deco-apps-architecture/scripts-codegen.md +148 -0
- package/.cursor/skills/deco-apps-architecture/shared-utils.md +181 -0
- package/.cursor/skills/deco-apps-architecture/vtex-deep-structure.md +253 -0
- package/.cursor/skills/deco-apps-architecture/website-app.md +169 -0
- package/.cursor/skills/deco-apps-vtex-porting/SKILL.md +189 -0
- package/.cursor/skills/deco-apps-vtex-porting/adaptation-patterns.md +335 -0
- package/.cursor/skills/deco-apps-vtex-porting/commerce-porting.md +155 -0
- package/.cursor/skills/deco-apps-vtex-porting/cookie-auth-patterns.md +148 -0
- package/.cursor/skills/deco-apps-vtex-porting/structure-map.md +234 -0
- package/.cursor/skills/deco-apps-vtex-porting/transform-mapping.md +99 -0
- package/.cursor/skills/deco-apps-vtex-porting/website-porting.md +194 -0
- package/.cursor/skills/deco-apps-vtex-review/SKILL.md +234 -0
- package/.cursor/skills/deco-async-rendering-architecture/SKILL.md +270 -0
- package/.cursor/skills/deco-async-rendering-site-guide/SKILL.md +417 -0
- package/.cursor/skills/deco-cms-layout-caching/SKILL.md +293 -0
- package/.cursor/skills/deco-cms-route-config/SKILL.md +388 -0
- package/.cursor/skills/deco-core-architecture/SKILL.md +185 -0
- package/.cursor/skills/deco-core-architecture/blocks.md +196 -0
- package/.cursor/skills/deco-core-architecture/deco-vs-deco-start.md +191 -0
- package/.cursor/skills/deco-core-architecture/engine.md +220 -0
- package/.cursor/skills/deco-core-architecture/hooks-components.md +157 -0
- package/.cursor/skills/deco-core-architecture/plugins-clients.md +136 -0
- package/.cursor/skills/deco-core-architecture/runtime.md +116 -0
- package/.cursor/skills/deco-core-architecture/site-usage.md +165 -0
- package/.cursor/skills/deco-e2e-testing/SKILL.md +372 -0
- package/.cursor/skills/deco-e2e-testing/discovery.md +337 -0
- package/.cursor/skills/deco-e2e-testing/scripts/scaffold.sh +81 -0
- package/.cursor/skills/deco-e2e-testing/selectors.md +175 -0
- package/.cursor/skills/deco-e2e-testing/templates/package.json +18 -0
- package/.cursor/skills/deco-e2e-testing/templates/playwright.config.ts +65 -0
- package/.cursor/skills/deco-e2e-testing/templates/scripts/baseline.ts +279 -0
- package/.cursor/skills/deco-e2e-testing/templates/scripts/run-e2e.ts +194 -0
- package/.cursor/skills/deco-e2e-testing/templates/specs/ecommerce-flow.spec.ts +612 -0
- package/.cursor/skills/deco-e2e-testing/templates/tsconfig.json +12 -0
- package/.cursor/skills/deco-e2e-testing/templates/utils/metrics-collector.ts +918 -0
- package/.cursor/skills/deco-e2e-testing/troubleshooting.md +602 -0
- package/.cursor/skills/deco-edge-caching/SKILL.md +316 -0
- package/.cursor/skills/deco-full-analysis/SKILL.md +898 -0
- package/.cursor/skills/deco-full-analysis/checklists/asset-optimization.md +251 -0
- package/.cursor/skills/deco-full-analysis/checklists/bug-fix.md +189 -0
- package/.cursor/skills/deco-full-analysis/checklists/cache-strategy.md +144 -0
- package/.cursor/skills/deco-full-analysis/checklists/dependency-update.md +150 -0
- package/.cursor/skills/deco-full-analysis/checklists/hydration-fix.md +191 -0
- package/.cursor/skills/deco-full-analysis/checklists/image-optimization.md +180 -0
- package/.cursor/skills/deco-full-analysis/checklists/loader-optimization.md +165 -0
- package/.cursor/skills/deco-full-analysis/checklists/seo-fix.md +183 -0
- package/.cursor/skills/deco-full-analysis/checklists/site-cleanup.md +281 -0
- package/.cursor/skills/deco-full-analysis/discovery.md +548 -0
- package/.cursor/skills/deco-incident-debugging/SKILL.md +378 -0
- package/.cursor/skills/deco-incident-debugging/headless-mode.md +510 -0
- package/.cursor/skills/deco-incident-debugging/learnings-index.md +227 -0
- package/.cursor/skills/deco-incident-debugging/triage-workflow.md +312 -0
- package/.cursor/skills/deco-islands-migration/SKILL.md +251 -0
- package/.cursor/skills/deco-loader-n-plus-1-detector/SKILL.md +275 -0
- package/.cursor/skills/deco-performance-audit/SKILL.md +530 -0
- package/.cursor/skills/deco-performance-audit/tools-reference.md +428 -0
- package/.cursor/skills/deco-performance-audit/workflow.md +457 -0
- package/.cursor/skills/deco-server-functions-invoke/SKILL.md +92 -0
- package/.cursor/skills/deco-server-functions-invoke/architecture.md +166 -0
- package/.cursor/skills/deco-server-functions-invoke/generator.md +122 -0
- package/.cursor/skills/deco-server-functions-invoke/problem.md +98 -0
- package/.cursor/skills/deco-server-functions-invoke/troubleshooting.md +110 -0
- package/.cursor/skills/deco-site-deployment/SKILL.md +396 -0
- package/.cursor/skills/deco-site-memory-debugging/SKILL.md +121 -0
- package/.cursor/skills/deco-site-memory-debugging/cdp-connection.md +222 -0
- package/.cursor/skills/deco-site-memory-debugging/memory-analysis.md +362 -0
- package/.cursor/skills/deco-site-patterns/SKILL.md +124 -0
- package/.cursor/skills/deco-site-patterns/app-composition.md +337 -0
- package/.cursor/skills/deco-site-patterns/client-patterns.md +341 -0
- package/.cursor/skills/deco-site-patterns/cms-wiring.md +230 -0
- package/.cursor/skills/deco-site-patterns/section-patterns.md +340 -0
- package/.cursor/skills/deco-site-scaling-tuning/SKILL.md +240 -0
- package/.cursor/skills/deco-site-scaling-tuning/analysis-scripts.md +267 -0
- package/.cursor/skills/deco-start-architecture/SKILL.md +218 -0
- package/.cursor/skills/deco-start-architecture/admin-protocol.md +156 -0
- package/.cursor/skills/deco-start-architecture/cms-resolution.md +201 -0
- package/.cursor/skills/deco-start-architecture/code-quality.md +158 -0
- package/.cursor/skills/deco-start-architecture/gap-analysis.md +129 -0
- package/.cursor/skills/deco-start-architecture/sdk-utilities.md +197 -0
- package/.cursor/skills/deco-start-architecture/worker-entry-caching.md +154 -0
- package/.cursor/skills/deco-startup-analysis/SKILL.md +248 -0
- package/.cursor/skills/deco-storefront-test-checklist/SKILL.md +369 -0
- package/.cursor/skills/deco-tanstack-hydration-fixes/SKILL.md +468 -0
- package/.cursor/skills/deco-tanstack-navigation/SKILL.md +681 -0
- package/.cursor/skills/deco-tanstack-search/SKILL.md +411 -0
- package/.cursor/skills/deco-tanstack-storefront-patterns/SKILL.md +1013 -0
- package/.cursor/skills/deco-to-tanstack-migration/SKILL.md +518 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/codemod-commands.md +174 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/commerce/README.md +78 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/deco-framework/README.md +128 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/gotchas.md +719 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/imports/README.md +70 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/platform-hooks/README.md +154 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/signals/README.md +220 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/vite-config/README.md +78 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/package-json.md +55 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/root-route.md +110 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/router.md +96 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/setup-ts.md +167 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/vite-config.md +122 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/worker-entry.md +67 -0
- package/.cursor/skills/deco-typescript-fixes/SKILL.md +178 -0
- package/.cursor/skills/deco-typescript-fixes/common-fixes.md +330 -0
- package/.cursor/skills/deco-typescript-fixes/strategy.md +148 -0
- package/.cursor/skills/deco-variant-selection-perf/SKILL.md +272 -0
- package/.cursor/skills/deco-vtex-fetch-cache/SKILL.md +225 -0
- package/.cursor/skills/find-skills/SKILL.md +133 -0
- package/.cursor/skills/incident-report/SKILL.md +179 -0
- package/.cursor/skills/incident-report/references/5-whys.md +75 -0
- package/.cursor/skills/incident-report/templates/client-report.md +187 -0
- package/.cursor/skills/incident-report/templates/internal-report.md +206 -0
- package/.cursor/skills/template-skill/SKILL.md +38 -0
- package/.github/workflows/release.yml +32 -0
- package/.releaserc.json +25 -0
- package/CLAUDE.md +135 -0
- package/GAP_ANALYSIS.md +224 -0
- package/GAP_ANALYSIS_V2.md +1013 -0
- package/biome.json +39 -0
- package/knip.json +5 -0
- package/package.json +87 -0
- package/scripts/generate-blocks.ts +69 -0
- package/scripts/generate-invoke.ts +378 -0
- package/scripts/generate-schema.ts +657 -0
- package/src/admin/cors.ts +29 -0
- package/src/admin/decofile.ts +72 -0
- package/src/admin/index.ts +24 -0
- package/src/admin/invoke.ts +163 -0
- package/src/admin/liveControls.ts +29 -0
- package/src/admin/meta.ts +70 -0
- package/src/admin/render.ts +205 -0
- package/src/admin/schema.ts +686 -0
- package/src/admin/setup.ts +44 -0
- package/src/cms/index.ts +59 -0
- package/src/cms/loader.ts +180 -0
- package/src/cms/registry.ts +162 -0
- package/src/cms/resolve.ts +1005 -0
- package/src/cms/sectionLoaders.ts +294 -0
- package/src/hooks/DecoPageRenderer.tsx +444 -0
- package/src/hooks/LazySection.tsx +109 -0
- package/src/hooks/LiveControls.tsx +108 -0
- package/src/hooks/SectionErrorFallback.tsx +85 -0
- package/src/hooks/index.ts +8 -0
- package/src/index.ts +5 -0
- package/src/matchers/builtins.ts +184 -0
- package/src/matchers/posthog.ts +154 -0
- package/src/middleware/decoState.ts +55 -0
- package/src/middleware/healthMetrics.ts +131 -0
- package/src/middleware/index.ts +80 -0
- package/src/middleware/liveness.ts +21 -0
- package/src/middleware/observability.ts +205 -0
- package/src/routes/adminRoutes.ts +83 -0
- package/src/routes/cmsRoute.ts +302 -0
- package/src/routes/components.tsx +34 -0
- package/src/routes/index.ts +15 -0
- package/src/sdk/analytics.ts +72 -0
- package/src/sdk/cacheHeaders.ts +268 -0
- package/src/sdk/cachedLoader.ts +206 -0
- package/src/sdk/clx.ts +3 -0
- package/src/sdk/cookie.ts +39 -0
- package/src/sdk/createInvoke.ts +57 -0
- package/src/sdk/csp.ts +59 -0
- package/src/sdk/env.ts +27 -0
- package/src/sdk/index.ts +63 -0
- package/src/sdk/instrumentedFetch.ts +137 -0
- package/src/sdk/invoke.ts +133 -0
- package/src/sdk/mergeCacheControl.ts +150 -0
- package/src/sdk/redirects.ts +217 -0
- package/src/sdk/requestContext.ts +184 -0
- package/src/sdk/serverTimings.ts +68 -0
- package/src/sdk/signal.ts +41 -0
- package/src/sdk/sitemap.ts +143 -0
- package/src/sdk/urlUtils.ts +117 -0
- package/src/sdk/useDevice.ts +82 -0
- package/src/sdk/useId.ts +7 -0
- package/src/sdk/useScript.ts +101 -0
- package/src/sdk/workerEntry.ts +703 -0
- package/src/sdk/wrapCaughtErrors.ts +107 -0
- package/src/types/index.ts +39 -0
- package/src/types/widgets.ts +13 -0
- 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
|