@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,335 @@
|
|
|
1
|
+
# Adaptation Patterns: deco-cx/apps → apps-start
|
|
2
|
+
|
|
3
|
+
How to convert each Deno/Fresh/Deco pattern to TanStack/Node equivalents.
|
|
4
|
+
|
|
5
|
+
## 1. App Factory → configureVtex()
|
|
6
|
+
|
|
7
|
+
### Original (apps/vtex/mod.ts)
|
|
8
|
+
```typescript
|
|
9
|
+
import { createHttpClient } from "../utils/http.ts";
|
|
10
|
+
import { createGraphqlClient } from "../utils/graphql.ts";
|
|
11
|
+
|
|
12
|
+
export default function VTEX({ account, publicUrl, salesChannel, appKey, appToken }: Props) {
|
|
13
|
+
const vcsDeprecated = createHttpClient<VTEXCommerceStable>({
|
|
14
|
+
base: publicUrl,
|
|
15
|
+
processHeaders: removeDirtyCookies,
|
|
16
|
+
fetcher: fetchSafe,
|
|
17
|
+
});
|
|
18
|
+
const io = createGraphqlClient({
|
|
19
|
+
endpoint: `${publicUrl}/api/io/_v/private/graphql/v1`,
|
|
20
|
+
});
|
|
21
|
+
const state = { account, publicUrl, salesChannel, vcsDeprecated, io, /* 5 more clients */ };
|
|
22
|
+
return { state, manifest, middleware };
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Port (apps-start/vtex/client.ts)
|
|
27
|
+
```typescript
|
|
28
|
+
let vtexConfig: VtexConfig;
|
|
29
|
+
|
|
30
|
+
export function configureVtex(config: VtexConfig) {
|
|
31
|
+
vtexConfig = config;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getVtexConfig(): VtexConfig {
|
|
35
|
+
return vtexConfig;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function vtexFetch<T>(path: string, init?: RequestInit): Promise<T> {
|
|
39
|
+
const { account, publicUrl } = getVtexConfig();
|
|
40
|
+
const baseUrl = publicUrl || `https://${account}.vtexcommercestable.com.br`;
|
|
41
|
+
const res = await fetch(`${baseUrl}${path}`, init);
|
|
42
|
+
return res.json();
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Key difference**: No typed Proxy, no processHeaders, no fetchSafe retry. Consider adding:
|
|
47
|
+
- URL sanitization from `fetchVTEX.ts`
|
|
48
|
+
- Retry on connection closed
|
|
49
|
+
- removeDirtyCookies for cookie headers
|
|
50
|
+
|
|
51
|
+
## 2. Loader Signature
|
|
52
|
+
|
|
53
|
+
### Original
|
|
54
|
+
```typescript
|
|
55
|
+
// loaders/intelligentSearch/productDetailsPage.ts
|
|
56
|
+
import { AppContext } from "../../mod.ts";
|
|
57
|
+
import type { ProductDetailsPage } from "../../../commerce/types.ts";
|
|
58
|
+
|
|
59
|
+
export interface Props { slug: string; }
|
|
60
|
+
|
|
61
|
+
const loader = async (props: Props, req: Request, ctx: AppContext): Promise<ProductDetailsPage | null> => {
|
|
62
|
+
const { vcsDeprecated, salesChannel } = ctx;
|
|
63
|
+
const segment = getSegmentFromBag(ctx);
|
|
64
|
+
// Use typed client: ctx.vcsDeprecated["GET /api/..."]({...})
|
|
65
|
+
};
|
|
66
|
+
export default loader;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Port
|
|
70
|
+
```typescript
|
|
71
|
+
// inline-loaders/productDetailsPage.ts
|
|
72
|
+
import { vtexFetch, getVtexConfig } from "../client";
|
|
73
|
+
import type { ProductDetailsPage } from "../../commerce/types/commerce";
|
|
74
|
+
|
|
75
|
+
interface Props { slug?: string; }
|
|
76
|
+
|
|
77
|
+
export default async function vtexProductDetailsPage(props: Props): Promise<ProductDetailsPage | null> {
|
|
78
|
+
const config = getVtexConfig();
|
|
79
|
+
// Use vtexFetch: await vtexFetch<T>("/api/...");
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Key differences**:
|
|
84
|
+
- No `req`, no `ctx` params
|
|
85
|
+
- Named export function instead of anonymous `const loader = ...`
|
|
86
|
+
- Config from `getVtexConfig()` instead of `ctx`
|
|
87
|
+
- No segment from bag — must pass explicitly or read from cookies
|
|
88
|
+
|
|
89
|
+
## 3. Action Signature
|
|
90
|
+
|
|
91
|
+
### Original
|
|
92
|
+
```typescript
|
|
93
|
+
// actions/cart/addItems.ts
|
|
94
|
+
export interface Props {
|
|
95
|
+
orderItems: Array<{ id: string; quantity: number; seller: string }>;
|
|
96
|
+
}
|
|
97
|
+
const action = async (props: Props, req: Request, ctx: AppContext): Promise<OrderForm> => {
|
|
98
|
+
const { orderFormId, cookie } = parseCookie(req.headers);
|
|
99
|
+
const segment = getSegmentFromBag(ctx);
|
|
100
|
+
const response = await ctx.vcsDeprecated["POST /api/checkout/pub/orderForm/:orderFormId/items"](
|
|
101
|
+
{ orderFormId, sc: segment?.payload?.channel, allowedOutdatedData: ["paymentData"] },
|
|
102
|
+
{ body: { orderItems: props.orderItems }, headers: { cookie } },
|
|
103
|
+
);
|
|
104
|
+
proxySetCookie(response.headers, ctx.response.headers, req.url);
|
|
105
|
+
return response.json();
|
|
106
|
+
};
|
|
107
|
+
export default action;
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Port
|
|
111
|
+
```typescript
|
|
112
|
+
// actions/checkout.ts
|
|
113
|
+
export async function addItems(
|
|
114
|
+
orderFormId: string, cookie: string, orderItems: OrderItem[], sc?: string,
|
|
115
|
+
): Promise<VtexFetchResult<OrderForm>> {
|
|
116
|
+
const params = new URLSearchParams();
|
|
117
|
+
if (sc) params.set("sc", sc);
|
|
118
|
+
params.set("allowedOutdatedData", "paymentData");
|
|
119
|
+
return vtexFetchWithCookies<OrderForm>(
|
|
120
|
+
`/api/checkout/pub/orderForm/${orderFormId}/items?${params}`,
|
|
121
|
+
{
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: { cookie, "Content-Type": "application/json" },
|
|
124
|
+
body: JSON.stringify({ orderItems, expectedOrderFormSections: DEFAULT_EXPECTED_SECTIONS }),
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Key differences**:
|
|
131
|
+
- Returns `VtexFetchResult<T>` with `{ data, setCookies }` (caller handles cookie propagation)
|
|
132
|
+
- No `proxySetCookie` — the storefront layer decides how to set cookies
|
|
133
|
+
- Parameters are explicit (no `req.headers` parsing inside — caller provides them)
|
|
134
|
+
|
|
135
|
+
## 4. Middleware (ctx.bag → Function Params)
|
|
136
|
+
|
|
137
|
+
### Original
|
|
138
|
+
```typescript
|
|
139
|
+
// middleware.ts — sets per-request state in ctx.bag
|
|
140
|
+
export const middleware = (_props: unknown, req: Request, ctx: AppMiddlewareContext) => {
|
|
141
|
+
const cookies = getCookies(req.headers);
|
|
142
|
+
setSegmentBag(cookies, req, ctx); // ctx.bag.set(SEGMENT, wrappedSegment)
|
|
143
|
+
setISCookiesBag(cookies, ctx); // ctx.bag.set(IS_COOKIES, { session, anonymous })
|
|
144
|
+
return ctx.next!();
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Then in loaders:
|
|
148
|
+
const segment = getSegmentFromBag(ctx); // ctx.bag.get(SEGMENT)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Port
|
|
152
|
+
```typescript
|
|
153
|
+
// middleware.ts — extracts context for downstream use
|
|
154
|
+
export function extractVtexContext(cookieHeader: string) {
|
|
155
|
+
const config = getVtexConfig();
|
|
156
|
+
const cookies = parseCookieString(cookieHeader);
|
|
157
|
+
const segment = buildSegmentFromCookies(cookies);
|
|
158
|
+
const isSession = cookies.get("vtex_is_session") ?? crypto.randomUUID();
|
|
159
|
+
const isAnonymous = cookies.get("vtex_is_anonymous") ?? crypto.randomUUID();
|
|
160
|
+
return { ...config, segment, isSession, isAnonymous };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// In loaders — pass context explicitly:
|
|
164
|
+
export default async function productList(props: Props) {
|
|
165
|
+
const config = getVtexConfig();
|
|
166
|
+
const data = await intelligentSearch<T>(path, params, {
|
|
167
|
+
cookieHeader: `vtex_is_session=${session}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Key difference**: No global per-request storage. Context is either:
|
|
173
|
+
1. Passed explicitly through function params
|
|
174
|
+
2. Read from singleton config (for static values like `account`, `salesChannel`)
|
|
175
|
+
|
|
176
|
+
## 5. Hooks (Signals → React Query)
|
|
177
|
+
|
|
178
|
+
### Original (Preact Signals + Serial Queue)
|
|
179
|
+
```typescript
|
|
180
|
+
// hooks/context.ts
|
|
181
|
+
import { signal } from "@preact/signals";
|
|
182
|
+
const cart = signal<OrderForm | null>(null);
|
|
183
|
+
const loading = signal(true);
|
|
184
|
+
|
|
185
|
+
const enqueue = (cb) => {
|
|
186
|
+
abort();
|
|
187
|
+
loading.value = true;
|
|
188
|
+
queue = queue.then(async () => {
|
|
189
|
+
const result = await cb(controller.signal);
|
|
190
|
+
cart.value = result.cart || cart.value;
|
|
191
|
+
loading.value = false;
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// hooks/useCart.ts
|
|
196
|
+
const addItems = enqueue("vtex/actions/cart/addItems.ts");
|
|
197
|
+
export const useCart = () => ({ cart, loading, addItems });
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Port (React Query)
|
|
201
|
+
```typescript
|
|
202
|
+
// hooks/useCart.ts
|
|
203
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
204
|
+
|
|
205
|
+
export function useCart() {
|
|
206
|
+
const queryClient = useQueryClient();
|
|
207
|
+
|
|
208
|
+
const { data: cart, isLoading } = useQuery({
|
|
209
|
+
queryKey: ["vtex-cart"],
|
|
210
|
+
queryFn: fetchCart,
|
|
211
|
+
staleTime: 0,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const addItems = useMutation({
|
|
215
|
+
mutationFn: addItemsToCart,
|
|
216
|
+
onSuccess: (newCart) => {
|
|
217
|
+
queryClient.setQueryData(["vtex-cart"], newCart);
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return { cart, loading: isLoading, addItems };
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Key advantages of React Query version**:
|
|
226
|
+
- Built-in loading/error/success states
|
|
227
|
+
- Automatic refetching on mount/focus
|
|
228
|
+
- Optimistic updates via `setQueryData`
|
|
229
|
+
- No manual abort controller management
|
|
230
|
+
- Devtools integration
|
|
231
|
+
|
|
232
|
+
## 6. Typed HTTP Client → vtexFetch
|
|
233
|
+
|
|
234
|
+
### Original
|
|
235
|
+
```typescript
|
|
236
|
+
// Uses Proxy-based typed client
|
|
237
|
+
const response = await ctx.vcsDeprecated
|
|
238
|
+
["POST /api/checkout/pub/orderForm/:orderFormId/coupons"](
|
|
239
|
+
{ orderFormId: cart.orderFormId, sc: channel },
|
|
240
|
+
{ body: { text: couponCode } },
|
|
241
|
+
);
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Port
|
|
245
|
+
```typescript
|
|
246
|
+
// Direct fetch with string URL
|
|
247
|
+
const orderForm = await vtexFetch<OrderForm>(
|
|
248
|
+
`/api/checkout/pub/orderForm/${orderFormId}/coupons?sc=${sc}`,
|
|
249
|
+
{
|
|
250
|
+
method: "POST",
|
|
251
|
+
headers: { "Content-Type": "application/json" },
|
|
252
|
+
body: JSON.stringify({ text: couponCode }),
|
|
253
|
+
},
|
|
254
|
+
);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Lost**: Type safety on URL params, response type, and body type. Mitigate by keeping the same function structure as the original.
|
|
258
|
+
|
|
259
|
+
## 7. GraphQL Client
|
|
260
|
+
|
|
261
|
+
### Original
|
|
262
|
+
```typescript
|
|
263
|
+
const { io } = ctx;
|
|
264
|
+
const data = await io.query<WishlistResponse, Variables>({
|
|
265
|
+
query: MY_QUERY,
|
|
266
|
+
variables: { id },
|
|
267
|
+
}, { headers: { cookie } });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Port
|
|
271
|
+
```typescript
|
|
272
|
+
import { vtexIOGraphQL } from "../client";
|
|
273
|
+
const data = await vtexIOGraphQL<WishlistResponse>(
|
|
274
|
+
{ query: MY_QUERY, variables: { id } },
|
|
275
|
+
{ cookie: buildAuthCookieHeader(authCookie, account) },
|
|
276
|
+
);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## 8. Cookie Propagation
|
|
280
|
+
|
|
281
|
+
### Original
|
|
282
|
+
```typescript
|
|
283
|
+
import { proxySetCookie } from "../utils/cookies.ts";
|
|
284
|
+
const response = await ctx.vcsDeprecated["POST /api/..."](params, opts);
|
|
285
|
+
proxySetCookie(response.headers, ctx.response.headers, req.url);
|
|
286
|
+
return response.json();
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Port
|
|
290
|
+
```typescript
|
|
291
|
+
import { vtexFetchWithCookies } from "../client";
|
|
292
|
+
const result = await vtexFetchWithCookies<OrderForm>(url, opts);
|
|
293
|
+
// result.data = the JSON response
|
|
294
|
+
// result.setCookies = string[] of Set-Cookie headers
|
|
295
|
+
// Caller (storefront) is responsible for setting these cookies on the response
|
|
296
|
+
return result;
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## 9. Deno Standard Library
|
|
300
|
+
|
|
301
|
+
| Original (Deno std) | Port (Node/Browser) |
|
|
302
|
+
|---------------------|---------------------|
|
|
303
|
+
| `getCookies(headers)` from `std/http/cookie.ts` | Manual parsing or `cookie` npm package |
|
|
304
|
+
| `setCookie(headers, cookie)` from `std/http/cookie.ts` | `res.headers.append("Set-Cookie", ...)` |
|
|
305
|
+
| `getSetCookies(headers)` from `std/http/cookie.ts` | `response.headers.getSetCookie()` |
|
|
306
|
+
| `btoa(str)` / `atob(str)` | Same (global in Node 16+) |
|
|
307
|
+
| `crypto.randomUUID()` | Same (global in Node 19+) |
|
|
308
|
+
| `Deno.env.get("VAR")` | `process.env.VAR` |
|
|
309
|
+
|
|
310
|
+
## 10. Import Specifiers
|
|
311
|
+
|
|
312
|
+
| Original | Port |
|
|
313
|
+
|----------|------|
|
|
314
|
+
| `from "./mod.ts"` | `from "./mod"` (no .ts extension) |
|
|
315
|
+
| `from "../../commerce/types.ts"` | `from "../../commerce/types/commerce"` |
|
|
316
|
+
| `from "std/http/mod.ts"` | Remove, use built-in or npm |
|
|
317
|
+
| `from "@deco/deco"` | Remove — no framework |
|
|
318
|
+
| `from "$fresh/runtime.ts"` | Remove — `typeof document !== "undefined"` |
|
|
319
|
+
|
|
320
|
+
## Summary: Porting Checklist Per File
|
|
321
|
+
|
|
322
|
+
When porting any file from `deco-cx/apps/vtex/` to `apps-start/vtex/`:
|
|
323
|
+
|
|
324
|
+
1. [ ] Copy the business logic (API calls, data transformations)
|
|
325
|
+
2. [ ] Replace `ctx.*` with `getVtexConfig().*` or explicit params
|
|
326
|
+
3. [ ] Replace typed client calls with `vtexFetch`/`vtexFetchWithCookies`
|
|
327
|
+
4. [ ] Replace `io.query` with `vtexIOGraphQL`
|
|
328
|
+
5. [ ] Replace `proxySetCookie` with returning `{ data, setCookies }`
|
|
329
|
+
6. [ ] Replace Deno std cookie functions with manual parsing
|
|
330
|
+
7. [ ] Remove `.ts` from import paths
|
|
331
|
+
8. [ ] Remove `@deco/deco` and `$fresh` imports
|
|
332
|
+
9. [ ] Keep the same VTEX API URLs and parameters
|
|
333
|
+
10. [ ] Keep the same schema.org transform.ts usage
|
|
334
|
+
11. [ ] Add `salesChannel` where original has it
|
|
335
|
+
12. [ ] Add `expectedOrderFormSections` where original has it
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Commerce Module Porting
|
|
2
|
+
|
|
3
|
+
How the shared `commerce/` module from `deco-cx/apps` maps to `apps-start/commerce/`.
|
|
4
|
+
|
|
5
|
+
## Structure Comparison
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Original (apps/commerce/) apps-start (commerce/)
|
|
9
|
+
═══════════════════════ ════════════════════════
|
|
10
|
+
types.ts (786 lines) → types/commerce.ts (same types)
|
|
11
|
+
mod.ts (app factory) → Not needed
|
|
12
|
+
manifest.gen.ts → Not needed
|
|
13
|
+
|
|
14
|
+
utils/canonical.ts → utils/canonical.ts ✅
|
|
15
|
+
utils/constants.ts → utils/constants.ts ✅
|
|
16
|
+
utils/filters.ts → utils/filters.ts ✅
|
|
17
|
+
utils/productToAnalyticsItem → utils/productToAnalyticsItem.ts ✅
|
|
18
|
+
utils/stateByZip.ts → utils/stateByZip.ts ✅
|
|
19
|
+
|
|
20
|
+
loaders/navbar.ts → Part of vtex/loaders/navbar.ts (platform-specific)
|
|
21
|
+
loaders/extensions/* → Not needed (Deco block system)
|
|
22
|
+
loaders/product/* → Not needed (Deco block system)
|
|
23
|
+
|
|
24
|
+
sections/Seo/* → Storefront handles SEO locally
|
|
25
|
+
|
|
26
|
+
N/A (was in site sdk/) → sdk/useOffer.ts ✅ (NEW)
|
|
27
|
+
N/A → sdk/useVariantPossibilities.ts ✅ (NEW)
|
|
28
|
+
N/A → sdk/formatPrice.ts ✅ (NEW)
|
|
29
|
+
N/A → sdk/url.ts ✅ (NEW)
|
|
30
|
+
N/A → sdk/analytics.ts ✅ (NEW)
|
|
31
|
+
N/A → components/Image.tsx ✅ (NEW)
|
|
32
|
+
N/A → components/JsonLd.tsx ✅ (NEW)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Key Improvement: SDK in Library
|
|
36
|
+
|
|
37
|
+
In the original architecture, utilities like `useOffer`, `formatPrice`, `useVariantPossibilities` lived in each site's `sdk/` folder. This meant every store had its own copy that could drift.
|
|
38
|
+
|
|
39
|
+
In apps-start, these are centralized in `commerce/sdk/` and imported as:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { useOffer } from "@decocms/apps/commerce/sdk/useOffer";
|
|
43
|
+
import { formatPrice } from "@decocms/apps/commerce/sdk/formatPrice";
|
|
44
|
+
import { useVariantPossibilities } from "@decocms/apps/commerce/sdk/useVariantPossibilities";
|
|
45
|
+
import { relative } from "@decocms/apps/commerce/sdk/url";
|
|
46
|
+
import { sendEvent } from "@decocms/apps/commerce/sdk/analytics";
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Types Mapping
|
|
50
|
+
|
|
51
|
+
The `commerce/types/commerce.ts` in apps-start should be a direct port of `commerce/types.ts` from the original. All schema.org types must match exactly.
|
|
52
|
+
|
|
53
|
+
### Verification
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Compare type names between original and port
|
|
57
|
+
rg "export (interface|type|enum)" apps/commerce/types.ts | sort
|
|
58
|
+
rg "export (interface|type|enum)" apps-start/commerce/types/commerce.ts | sort
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Utils Porting
|
|
62
|
+
|
|
63
|
+
### canonical.ts — Direct Copy
|
|
64
|
+
```typescript
|
|
65
|
+
export const canonicalFromBreadcrumblist = (b?: BreadcrumbList) => {
|
|
66
|
+
const items = b?.itemListElement ?? [];
|
|
67
|
+
if (!Array.isArray(items) || items.length === 0) return undefined;
|
|
68
|
+
return items.reduce((acc, curr) => acc.position < curr.position ? curr : acc).item;
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### constants.ts — Direct Copy
|
|
73
|
+
```typescript
|
|
74
|
+
export const DEFAULT_IMAGE: ImageObject = {
|
|
75
|
+
"@type": "ImageObject",
|
|
76
|
+
encodingFormat: "image",
|
|
77
|
+
alternateName: "Default Image Placeholder",
|
|
78
|
+
url: "https://ozksgdmyrqcxcwhnbepg.supabase.co/storage/v1/object/public/assets/1818/ff6bb37e-...",
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### filters.ts — Direct Copy
|
|
83
|
+
```typescript
|
|
84
|
+
export const parseRange = (price: string) => { /* ... */ };
|
|
85
|
+
export const formatRange = (from: number, to: number) => `${from}:${to}`;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### productToAnalyticsItem.ts — Adapt Imports Only
|
|
89
|
+
Change `from "../types.ts"` to `from "../types/commerce"`.
|
|
90
|
+
|
|
91
|
+
### stateByZip.ts — Direct Copy
|
|
92
|
+
No dependencies.
|
|
93
|
+
|
|
94
|
+
## SDK Utilities
|
|
95
|
+
|
|
96
|
+
These are new to apps-start — they came from individual site repos:
|
|
97
|
+
|
|
98
|
+
### useOffer.ts
|
|
99
|
+
Extracts price/installment/seller from AggregateOffer:
|
|
100
|
+
```typescript
|
|
101
|
+
export function useOffer(aggregateOffer?: AggregateOffer) {
|
|
102
|
+
const offer = aggregateOffer?.offers?.[0]; // Best offer
|
|
103
|
+
return {
|
|
104
|
+
price: offer?.price,
|
|
105
|
+
listPrice: /* ... */,
|
|
106
|
+
seller: offer?.seller, // MUST be sellerId
|
|
107
|
+
installment: /* find best installment */,
|
|
108
|
+
availability: offer?.availability,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### useVariantPossibilities.ts
|
|
114
|
+
Groups product variants by property name:
|
|
115
|
+
```typescript
|
|
116
|
+
export function useVariantPossibilities(product: ProductGroup) {
|
|
117
|
+
// Returns Map<propertyName, Map<propertyValue, ProductLeaf>>
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### formatPrice.ts
|
|
122
|
+
Formats currency values:
|
|
123
|
+
```typescript
|
|
124
|
+
export function formatPrice(price?: number, currency = "BRL", locale = "pt-BR") {
|
|
125
|
+
return price?.toLocaleString(locale, { style: "currency", currency });
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Components
|
|
130
|
+
|
|
131
|
+
### Image.tsx
|
|
132
|
+
Thin wrapper around `<img>` with optimization hints:
|
|
133
|
+
```typescript
|
|
134
|
+
export function Image(props: ImageProps) {
|
|
135
|
+
// Handles: width/height, loading="lazy", fetchPriority, src transformation
|
|
136
|
+
return <img {...adaptedProps} />;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### JsonLd.tsx
|
|
141
|
+
Structured data renderer:
|
|
142
|
+
```typescript
|
|
143
|
+
export function JsonLd<T>(props: { data: T }) {
|
|
144
|
+
return <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(props.data) }} />;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## What the Storefront Provides Locally
|
|
149
|
+
|
|
150
|
+
These are NOT in apps-start — each site creates its own:
|
|
151
|
+
|
|
152
|
+
- `components/Picture.tsx` — `<picture>` with `<source>` for responsive images
|
|
153
|
+
- `components/Seo.tsx` — Head meta tags (title, description, og:*, etc.)
|
|
154
|
+
- `components/Theme.tsx` — CSS variable injection
|
|
155
|
+
- `types/widgets.ts` — CMS widget type aliases (ImageWidget = string, etc.)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Cookie & Auth Patterns for VTEX apps-start
|
|
2
|
+
|
|
3
|
+
Reference for cookie propagation and authentication patterns in the TanStack Start port.
|
|
4
|
+
|
|
5
|
+
## vtexFetchWithCookies
|
|
6
|
+
|
|
7
|
+
Standard `vtexFetch` discards response headers. For any mutation that generates `Set-Cookie`:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
export interface VtexFetchResult<T> {
|
|
11
|
+
data: T;
|
|
12
|
+
setCookies: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function vtexFetchWithCookies<T>(
|
|
16
|
+
path: string,
|
|
17
|
+
init?: RequestInit,
|
|
18
|
+
): Promise<VtexFetchResult<T>> {
|
|
19
|
+
const response = await vtexFetchResponse(path, init);
|
|
20
|
+
const data = await response.json() as T;
|
|
21
|
+
const setCookies: string[] = [];
|
|
22
|
+
response.headers.forEach((value, key) => {
|
|
23
|
+
if (key.toLowerCase() === "set-cookie") setCookies.push(value);
|
|
24
|
+
});
|
|
25
|
+
if (setCookies.length === 0 && typeof response.headers.getSetCookie === "function") {
|
|
26
|
+
setCookies.push(...response.headers.getSetCookie());
|
|
27
|
+
}
|
|
28
|
+
return { data, setCookies };
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use in: `checkout.ts`, `auth.ts`, `session.ts` (create/edit).
|
|
33
|
+
|
|
34
|
+
## buildAuthCookieHeader
|
|
35
|
+
|
|
36
|
+
VTEX IO GraphQL at `{account}.myvtex.com` requires both cookie names:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// vtexId.ts
|
|
40
|
+
export const VTEX_AUTH_COOKIE = "VtexIdclientAutCookie";
|
|
41
|
+
|
|
42
|
+
export function buildAuthCookieHeader(authCookie: string, account: string): string {
|
|
43
|
+
if (authCookie.includes("=")) return authCookie;
|
|
44
|
+
return `${VTEX_AUTH_COOKIE}=${authCookie}; ${VTEX_AUTH_COOKIE}_${account}=${authCookie}`;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Usage in any authenticated GraphQL action:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { buildAuthCookieHeader } from "../utils/vtexId";
|
|
52
|
+
import { getVtexConfig } from "../client";
|
|
53
|
+
|
|
54
|
+
const { account } = getVtexConfig();
|
|
55
|
+
const result = await vtexIOGraphQL<T>(
|
|
56
|
+
{ query: MY_MUTATION, variables },
|
|
57
|
+
{ cookie: buildAuthCookieHeader(authCookie, account) },
|
|
58
|
+
);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Files that use this pattern:
|
|
62
|
+
- `actions/address.ts` — gql helper
|
|
63
|
+
- `actions/misc.ts` — gql helper
|
|
64
|
+
- `actions/newsletter.ts` — gql helper
|
|
65
|
+
- `actions/profile.ts` — gql helper
|
|
66
|
+
- `actions/wishlist.ts` — buildCookieHeader
|
|
67
|
+
- `actions/session.ts` — deleteSession
|
|
68
|
+
- `utils/enrichment.ts` — simulation auth
|
|
69
|
+
|
|
70
|
+
Files that use `VTEX_AUTH_COOKIE` directly (as header name, not cookie):
|
|
71
|
+
- `actions/misc.ts` — submitReview sends `{ [VTEX_AUTH_COOKIE]: authCookie }` as HTTP header (Reviews API quirk)
|
|
72
|
+
|
|
73
|
+
## DEFAULT_EXPECTED_SECTIONS
|
|
74
|
+
|
|
75
|
+
VTEX returns incomplete OrderForm without explicit sections. Always include:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
export const DEFAULT_EXPECTED_SECTIONS = [
|
|
79
|
+
"items",
|
|
80
|
+
"totalizers",
|
|
81
|
+
"clientProfileData",
|
|
82
|
+
"shippingData",
|
|
83
|
+
"paymentData",
|
|
84
|
+
"sellers",
|
|
85
|
+
"messages",
|
|
86
|
+
"marketingData",
|
|
87
|
+
"clientPreferencesData",
|
|
88
|
+
"storePreferencesData",
|
|
89
|
+
"giftRegistryData",
|
|
90
|
+
"ratesAndBenefitsData",
|
|
91
|
+
"openTextField",
|
|
92
|
+
"commercialConditionData",
|
|
93
|
+
"customData",
|
|
94
|
+
];
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Used in:
|
|
98
|
+
- `actions/checkout.ts` — all cart mutations
|
|
99
|
+
- `loaders/cart.ts` — getCart POST body
|
|
100
|
+
- `hooks/useCart.ts` — client-side fetchCart POST body
|
|
101
|
+
|
|
102
|
+
## Intelligent Search Cookies
|
|
103
|
+
|
|
104
|
+
VTEX Intelligent Search requires tracking cookies. Generate in middleware if missing:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// middleware.ts
|
|
108
|
+
const vtexIsSession = cookies.get("vtex_is_session") ?? crypto.randomUUID();
|
|
109
|
+
const vtexIsAnonymous = cookies.get("vtex_is_anonymous") ?? crypto.randomUUID();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Pass to `intelligentSearch()` via `opts.cookieHeader`:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const data = await intelligentSearch<T>(path, params, {
|
|
116
|
+
cookieHeader: `vtex_is_session=${session}; vtex_is_anonymous=${anonymous}`,
|
|
117
|
+
locale: "pt-BR",
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## HttpOnly Cookie Rule
|
|
122
|
+
|
|
123
|
+
`VtexIdclientAutCookie` is HttpOnly — invisible to `document.cookie`.
|
|
124
|
+
|
|
125
|
+
Client-side hooks must NOT check for this cookie. Instead:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// useUser.ts — correct pattern
|
|
129
|
+
async function fetchUser(): Promise<VtexUser> {
|
|
130
|
+
const res = await fetch(
|
|
131
|
+
"/api/sessions?items=profile.email,profile.firstName,profile.lastName,profile.id",
|
|
132
|
+
{ credentials: "include" },
|
|
133
|
+
);
|
|
134
|
+
// Parse session response for user data
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## salesChannel Injection Points
|
|
139
|
+
|
|
140
|
+
| Component | How sc is injected |
|
|
141
|
+
|-----------|-------------------|
|
|
142
|
+
| `client.ts` intelligentSearch | Auto from `getVtexConfig().salesChannel` |
|
|
143
|
+
| `hooks/useCart.ts` | Reads `VTEXSC` cookie via `document.cookie` |
|
|
144
|
+
| `loaders/cart.ts` | From `getVtexConfig().salesChannel` |
|
|
145
|
+
| `loaders/catalog.ts` | From `getVtexConfig().salesChannel` |
|
|
146
|
+
| `loaders/legacy.ts` | `buildSearchParams()` includes `sc` |
|
|
147
|
+
| `actions/checkout.ts` | Helper `scParam()` / `appendSc()` |
|
|
148
|
+
| `middleware.ts` | Reads `VTEXSC` cookie from request |
|