@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,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 |