@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,166 @@
1
+ # Three-Layer Invoke Architecture
2
+
3
+ ## Overview
4
+
5
+ Server actions in Deco storefronts follow a three-layer pattern where commerce functions are pure, the framework provides the transport bridge, and the site has a generated file that wires them together.
6
+
7
+ ## Layer 1: Pure Functions (`@decocms/apps`)
8
+
9
+ Commerce actions are regular async functions with no framework dependencies:
10
+
11
+ ```typescript
12
+ // @decocms/apps/vtex/actions/checkout.ts
13
+ export async function addItemsToCart(
14
+ orderFormId: string,
15
+ orderItems: Array<{ id: string; seller: string; quantity: number }>,
16
+ ): Promise<VtexFetchResult<OrderForm>> {
17
+ return vtexFetchWithCookies(
18
+ `/api/checkout/pub/orderForm/${orderFormId}/items?...`,
19
+ { method: "POST", body: JSON.stringify({ orderItems }) },
20
+ );
21
+ }
22
+ ```
23
+
24
+ These functions:
25
+ - Use `vtexFetch`/`vtexFetchWithCookies` which call the VTEX API with `appKey`/`appToken`
26
+ - Have no knowledge of how they'll be called (HTTP, RPC, direct)
27
+ - Can be tested independently
28
+ - Are the same for any framework (Fresh, TanStack, etc.)
29
+
30
+ The `invoke.ts` in `@decocms/apps/vtex/` serves as the **declaration file** — it lists which functions should be exposed as server actions, their input/output types, and whether to unwrap `VtexFetchResult`.
31
+
32
+ ## Layer 2: Generator (`@decocms/start`)
33
+
34
+ The `generate-invoke.ts` script bridges Layer 1 to TanStack Start:
35
+
36
+ 1. Parses `@decocms/apps/vtex/invoke.ts` with `ts-morph`
37
+ 2. Extracts each action: name, imports, input type, return type, call body, unwrap flag
38
+ 3. Generates `invoke.gen.ts` with top-level `createServerFn` declarations
39
+
40
+ The generator lives in `@decocms/start/scripts/generate-invoke.ts` and is framework-aware — it knows about `createServerFn` and TanStack Start's compiler constraints.
41
+
42
+ ## Layer 3: Generated Bridge (Site)
43
+
44
+ The site has `src/server/invoke.gen.ts` — auto-generated, committed or gitignored:
45
+
46
+ ```typescript
47
+ // Auto-generated — do not edit
48
+ import { createServerFn } from "@tanstack/react-start";
49
+ import { addItemsToCart } from "@decocms/apps/vtex/actions/checkout";
50
+
51
+ const $addItemsToCart = createServerFn({ method: "POST" })
52
+ .handler(async (ctx) => {
53
+ const result = await addItemsToCart(ctx.data.orderFormId, ctx.data.orderItems);
54
+ return unwrapResult(result);
55
+ });
56
+
57
+ export const invoke = {
58
+ vtex: {
59
+ actions: {
60
+ addItemsToCart: $addItemsToCart,
61
+ // ...
62
+ },
63
+ },
64
+ };
65
+ ```
66
+
67
+ Components import from here:
68
+ ```typescript
69
+ import { invoke } from "~/server/invoke.gen";
70
+ ```
71
+
72
+ ## Comparison: deco-cx/deco vs @decocms/start
73
+
74
+ ### deco-cx/deco (Fresh/Deno — Production)
75
+
76
+ ```
77
+ Client
78
+ → invoke.vtex.actions.addItemsToCart(props)
79
+ → Proxy builds key: "vtex/actions/addItemsToCart"
80
+ → fetch("/live/invoke/vtex/actions/addItemsToCart", { body: props })
81
+ → Hono route /live/invoke/* → resolves handler from manifest
82
+ → addItemsToCart(props, ctx)
83
+ → vtexFetch → VTEX API
84
+ ```
85
+
86
+ Key characteristics:
87
+ - **Runtime resolution**: handler is found by key in the manifest at request time
88
+ - **HTTP transport**: `fetch()` to `/live/invoke/{key}` — explicit HTTP call
89
+ - **Proxy-based DX**: `proxy<Manifest>()` creates a JavaScript Proxy that builds the URL from property chain
90
+ - **No compiler magic**: works with any bundler, no Vite plugin needed
91
+
92
+ ### @decocms/start (TanStack Start — New)
93
+
94
+ ```
95
+ Client
96
+ → invoke.vtex.actions.addItemsToCart({ data: props })
97
+ → createClientRpc("base64id")
98
+ → POST /_server with serialized function ID + data
99
+ → TanStack Start deserializes, finds handler by ID
100
+ → addItemsToCart(props)
101
+ → vtexFetch → VTEX API
102
+ ```
103
+
104
+ Key characteristics:
105
+ - **Build-time resolution**: compiler assigns each function a unique ID at build time
106
+ - **RPC transport**: `createClientRpc` handles serialization, TanStack Start routes to `/_server`
107
+ - **Direct function call DX**: the generated function is directly callable, fully typed
108
+ - **Compiler-dependent**: requires TanStack Start's Vite plugin to transform the code
109
+
110
+ ### Side-by-Side
111
+
112
+ | Aspect | deco-cx/deco (Fresh) | @decocms/start (TanStack) |
113
+ |--------|---------------------|--------------------------|
114
+ | **Transport** | `fetch("/live/invoke/key")` | `createServerFn` → `/_server` RPC |
115
+ | **Registry** | `manifest.gen.ts` (runtime) | `invoke.gen.ts` (build-time) |
116
+ | **Client code** | `Proxy` object | Typed function stubs |
117
+ | **Resolution** | Runtime (key → resolver) | Build-time (compiler extracts handler) |
118
+ | **Config** | `runtime.ts` (3 lines) | `generate-invoke.ts` (build script) |
119
+ | **Framework dep** | None (plain fetch) | TanStack Start compiler |
120
+ | **Type safety** | Manifest types (generic) | Per-function types (specific) |
121
+
122
+ ### What Stayed the Same
123
+
124
+ Both approaches share the core principle:
125
+ 1. **Commerce functions are pure** — same `addItemsToCart()` in both stacks
126
+ 2. **Client never calls VTEX directly** — always goes through the server
127
+ 3. **Credentials stay server-side** — `appKey`/`appToken` never reach the browser
128
+ 4. **Same invoke DX** — `invoke.vtex.actions.addItemsToCart({ data: {...} })`
129
+
130
+ ## Data Flow Diagram
131
+
132
+ ```
133
+ ┌─────────────────────────────────────────────────────────┐
134
+ │ BROWSER │
135
+ │ │
136
+ │ useCart() → invoke.vtex.actions.addItemsToCart() │
137
+ │ │ │
138
+ │ ▼ │
139
+ │ createClientRpc("id") → POST /_server │
140
+ │ │ (same domain, no CORS) │
141
+ └────────────────────┼─────────────────────────────────────┘
142
+
143
+
144
+ ┌─────────────────────────────────────────────────────────┐
145
+ │ SERVER (Vite SSR / Worker) │
146
+ │ │
147
+ │ TanStack Start deserializes → finds $addItemsToCart │
148
+ │ │ │
149
+ │ ▼ │
150
+ │ addItemsToCart(orderFormId, orderItems) │
151
+ │ (from @decocms/apps/vtex/actions/checkout.ts) │
152
+ │ │ │
153
+ │ ▼ │
154
+ │ vtexFetchWithCookies( │
155
+ │ "/api/checkout/pub/orderForm/{id}/items", │
156
+ │ { headers: { X-VTEX-API-AppKey, X-VTEX-API-AppToken }}│
157
+ │ ) │
158
+ └────────────────────┼─────────────────────────────────────┘
159
+
160
+
161
+ ┌─────────────────────────────────────────────────────────┐
162
+ │ VTEX API (server-to-server) │
163
+ │ vtexcommercestable.com.br │
164
+ │ ✓ Has credentials, ✓ No CORS, ✓ Full response │
165
+ └─────────────────────────────────────────────────────────┘
166
+ ```
@@ -0,0 +1,122 @@
1
+ # The generate-invoke.ts Script
2
+
3
+ ## Location
4
+
5
+ `@decocms/start/scripts/generate-invoke.ts`
6
+
7
+ ## What It Does
8
+
9
+ 1. Finds `@decocms/apps/vtex/invoke.ts` (checks `node_modules/@decocms/apps` and `../apps-start`)
10
+ 2. Parses it with `ts-morph` to extract action definitions
11
+ 3. For each action, extracts: name, import source, imported function, input type, return type, unwrap flag, call body
12
+ 4. Generates `src/server/invoke.gen.ts` with:
13
+ - Top-level `createServerFn().handler()` declarations (one per action)
14
+ - An `invoke` object that re-exports them with proper types
15
+
16
+ ## Usage
17
+
18
+ ```bash
19
+ # From site root
20
+ npx tsx node_modules/@decocms/start/scripts/generate-invoke.ts
21
+
22
+ # With custom output
23
+ npx tsx node_modules/@decocms/start/scripts/generate-invoke.ts --out-file src/invoke.gen.ts
24
+
25
+ # With custom apps location
26
+ npx tsx node_modules/@decocms/start/scripts/generate-invoke.ts --apps-dir ../my-apps
27
+ ```
28
+
29
+ ## CLI Options
30
+
31
+ | Option | Default | Description |
32
+ |--------|---------|-------------|
33
+ | `--out-file` | `src/server/invoke.gen.ts` | Output file path |
34
+ | `--apps-dir` | Auto-detected | Path to `@decocms/apps` root |
35
+
36
+ ## How It Parses invoke.ts
37
+
38
+ The script navigates the AST:
39
+
40
+ ```
41
+ invoke (VariableDeclaration)
42
+ └── { vtex: { actions: { ... } } } as const (AsExpression → ObjectLiteral)
43
+ └── vtex (PropertyAssignment)
44
+ └── actions (PropertyAssignment)
45
+ └── getOrCreateCart: createInvokeFn(...) as (...)
46
+ └── addItemsToCart: createInvokeFn(...) as (...)
47
+ └── ...
48
+ ```
49
+
50
+ For each property in `actions`:
51
+ 1. Strips the `as Type` assertion to get the `createInvokeFn(...)` call
52
+ 2. Extracts the arrow function parameter type → `inputType`
53
+ 3. Extracts the arrow function body → `callBody`
54
+ 4. Checks for `{ unwrap: true }` in the second argument
55
+ 5. Resolves which imported function is called (matches against import map)
56
+ 6. Extracts the return type from the `as` assertion
57
+
58
+ ## Generated Output Structure
59
+
60
+ ```typescript
61
+ // invoke.gen.ts
62
+
63
+ import { createServerFn } from "@tanstack/react-start";
64
+ import { addItemsToCart, ... } from "@decocms/apps/vtex/actions/checkout";
65
+ import type { OrderForm } from "@decocms/apps/vtex/types";
66
+
67
+ function unwrapResult<T>(result: unknown): T { ... }
68
+
69
+ // Top-level — compiler can transform these
70
+ const $addItemsToCart = createServerFn({ method: "POST" })
71
+ .handler(async (ctx: { data: { orderFormId: string; ... } }) => {
72
+ const result = await addItemsToCart(ctx.data.orderFormId, ctx.data.orderItems);
73
+ return unwrapResult(result); // strips VtexFetchResult wrapper
74
+ });
75
+
76
+ // Re-export with types
77
+ export const invoke = {
78
+ vtex: {
79
+ actions: {
80
+ addItemsToCart: $addItemsToCart as (...) => Promise<OrderForm>,
81
+ },
82
+ },
83
+ } as const;
84
+ ```
85
+
86
+ ## Adding New Actions
87
+
88
+ 1. Add the pure function to `@decocms/apps/vtex/actions/{module}.ts`
89
+ 2. Add it to `@decocms/apps/vtex/invoke.ts` using `createInvokeFn`
90
+ 3. Re-run `npm run generate:invoke` in the site
91
+ 4. The new action is automatically available in `invoke.vtex.actions.*`
92
+
93
+ ## unwrapResult
94
+
95
+ VTEX checkout functions return `VtexFetchResult<T>` which wraps `{ data: T, setCookies: string[] }`. The `unwrap: true` flag causes the generated handler to extract `.data` before returning to the client:
96
+
97
+ ```typescript
98
+ // Without unwrap:
99
+ const result = await simulateCart(items, postalCode);
100
+ return result; // returns raw simulation response
101
+
102
+ // With unwrap:
103
+ const result = await getOrCreateCart(orderFormId);
104
+ return unwrapResult(result); // returns OrderForm, not { data: OrderForm, setCookies }
105
+ ```
106
+
107
+ ## Integration with Build Pipeline
108
+
109
+ The script fits into the existing build pipeline alongside other generators:
110
+
111
+ ```json
112
+ {
113
+ "scripts": {
114
+ "generate:blocks": "tsx .../generate-blocks.ts",
115
+ "generate:invoke": "tsx .../generate-invoke.ts",
116
+ "generate:schema": "tsx .../generate-schema.ts",
117
+ "build": "generate:blocks && generate:invoke && generate:schema && tsr generate && vite build"
118
+ }
119
+ }
120
+ ```
121
+
122
+ Order matters: `generate:invoke` should run before `generate:schema` (schema needs to see all imports), but after `generate:blocks` (blocks don't depend on invoke).
@@ -0,0 +1,98 @@
1
+ # The CORS Problem — Root Cause Analysis
2
+
3
+ ## Symptom
4
+
5
+ When clicking "Add to Cart" or "Compra Rápida" in a Deco TanStack Start storefront running on `localhost:5173`, the browser makes a direct request to `https://{account}.vtexcommercestable.com.br/api/checkout/pub/orderForm/...`, which fails with a CORS error because the VTEX API doesn't allow cross-origin requests from `localhost`.
6
+
7
+ ## Expected Behavior
8
+
9
+ The `invoke.vtex.actions.addItemsToCart()` call should go through TanStack Start's server function mechanism:
10
+ 1. Client calls the function
11
+ 2. TanStack Start serializes the call and POSTs to `/_server` (same domain)
12
+ 3. Server deserializes, runs the handler (which calls VTEX API server-to-server)
13
+ 4. Server returns the result to the client
14
+
15
+ ## Root Cause: Compiler Fast Path
16
+
17
+ TanStack Start uses a Vite plugin (`tanstack-start-core::server-fn`) that transforms `createServerFn().handler()` calls. On the **client bundle**, it replaces the handler body with a `createClientRpc()` stub that makes an HTTP call to `/_server`.
18
+
19
+ The compiler has two code paths:
20
+
21
+ ### Fast Path (ServerFn only)
22
+ When a file only contains `ServerFn` kind (detected by `/\bcreateServerFn\b|\.\s*handler\s*\(/`), the compiler uses a fast path that **only scans top-level statements**:
23
+
24
+ ```typescript
25
+ // compiler.ts — fast path
26
+ function areAllKindsTopLevelOnly(kinds: Set<LookupKind>): boolean {
27
+ return kinds.size === 1 && kinds.has('ServerFn')
28
+ }
29
+
30
+ // Only visits top-level VariableDeclarators
31
+ // VariableDeclarator -> VariableDeclaration -> Program
32
+ ```
33
+
34
+ ### Normal Path
35
+ For files with multiple kinds (Middleware, IsomorphicFn, etc.), it does a full AST traversal.
36
+
37
+ ## Why createInvokeFn Fails
38
+
39
+ The original `@decocms/start/sdk/createInvoke.ts`:
40
+
41
+ ```typescript
42
+ import { createServerFn } from "@tanstack/react-start";
43
+
44
+ export function createInvokeFn(action, opts) {
45
+ return createServerFn({ method: "POST" }).handler(async (ctx) => {
46
+ const result = await action(ctx.data);
47
+ // ...
48
+ });
49
+ }
50
+ ```
51
+
52
+ The file contains `createServerFn` → triggers `ServerFn` detection → **fast path activates** → only scans top-level → `.handler()` is inside `createInvokeFn` function body → **skipped**.
53
+
54
+ Result: the client bundle receives the raw code, `createServerFn` returns a function that calls `vtexFetch` directly in the browser.
55
+
56
+ ## Verification
57
+
58
+ You can verify the transformation by fetching the module from Vite dev server:
59
+
60
+ ```bash
61
+ # BROKEN — raw code, no transformation
62
+ curl "http://localhost:5173/@fs/.../createInvoke.ts"
63
+ # Shows: createServerFn({ method: "POST" }).handler(async (ctx) => { ... })
64
+
65
+ # FIXED — compiler transformed to RPC
66
+ curl "http://localhost:5174/src/server/invoke.gen.ts"
67
+ # Shows: createServerFn({ method: "POST" }).handler(createClientRpc("eyJ..."))
68
+ ```
69
+
70
+ The `createClientRpc("base64id")` is the RPC stub — it serializes the call and POSTs to `/_server`.
71
+
72
+ ## The Fix
73
+
74
+ Each `createServerFn().handler()` must be a **top-level const declaration**:
75
+
76
+ ```typescript
77
+ // WORKS — top-level const
78
+ const $addItemsToCart = createServerFn({ method: "POST" })
79
+ .handler(async (ctx) => {
80
+ return await addItemsToCart(ctx.data.orderFormId, ctx.data.orderItems);
81
+ });
82
+
83
+ // DOES NOT WORK — inside a function
84
+ function createInvokeFn(action) {
85
+ return createServerFn({ method: "POST" })
86
+ .handler(async (ctx) => { ... }); // ← skipped by fast path
87
+ }
88
+ ```
89
+
90
+ ## Compiler Source Reference
91
+
92
+ The relevant code is in `@tanstack/start-plugin-core/src/start-compiler-plugin/`:
93
+
94
+ - `compiler.ts:88-95` — `KindDetectionPatterns.ServerFn = /\bcreateServerFn\b|\.\s*handler\s*\(/`
95
+ - `compiler.ts:226-228` — `areAllKindsTopLevelOnly` returns true for ServerFn-only files
96
+ - `compiler.ts:660-663` — `canUseFastPath` check
97
+ - `compiler.ts:715-717` — early exit when no top-level candidates found
98
+ - `plugin.ts:239-247` — transform filter: `id.include = /\.[cm]?[tj]sx?($|\?)/`, `code.include` from `KindDetectionPatterns`
@@ -0,0 +1,110 @@
1
+ # Troubleshooting
2
+
3
+ ## CORS Error on Add to Cart / Checkout
4
+
5
+ **Symptom**: Browser console shows CORS error when calling VTEX API directly.
6
+
7
+ **Check**: Open browser DevTools → Network tab. If you see requests going to `vtexcommercestable.com.br` instead of `/_server`, the server functions aren't transformed.
8
+
9
+ **Fix**:
10
+ 1. Verify `invoke.gen.ts` exists in `src/server/`
11
+ 2. Verify imports point to `~/server/invoke.gen`, not `@decocms/apps/vtex/invoke`
12
+ 3. Re-run `npm run generate:invoke`
13
+ 4. Restart the dev server (Vite caches transforms)
14
+
15
+ ## Verify Transformation
16
+
17
+ Fetch the generated file from Vite to see if the compiler transformed it:
18
+
19
+ ```bash
20
+ # Replace port with your dev server port
21
+ curl "http://localhost:5173/src/server/invoke.gen.ts" | head -20
22
+ ```
23
+
24
+ **Good** — you should see `createClientRpc`:
25
+ ```js
26
+ const $addItemsToCart = createServerFn({ method: "POST" })
27
+ .handler(createClientRpc("eyJmaWxlIjoi..."));
28
+ ```
29
+
30
+ **Bad** — you see the raw handler code:
31
+ ```js
32
+ const $addItemsToCart = createServerFn({ method: "POST" })
33
+ .handler(async (ctx) => {
34
+ const result = await addItemsToCart(ctx.data.orderFormId, ...);
35
+ ```
36
+
37
+ If you see the raw code, the compiler didn't transform it. Possible causes:
38
+ - The file is not in `src/` (must be inside the site's source directory)
39
+ - The Vite plugin is not loaded (check `vite.config.ts` has `tanstackStart()`)
40
+ - The `createServerFn` is not at top-level (check the generated code)
41
+
42
+ ## Server Logs Don't Show VTEX Calls
43
+
44
+ **Symptom**: When clicking add to cart, no `[vtex] POST ...` lines appear in the terminal.
45
+
46
+ **Cause**: The calls are going directly from the browser, not through the server.
47
+
48
+ **Fix**: Same as CORS fix above — ensure `invoke.gen.ts` is being used.
49
+
50
+ ## "invoke.vtex.actions.X is not a function"
51
+
52
+ **Cause**: The generated file doesn't include that action.
53
+
54
+ **Fix**:
55
+ 1. Check `@decocms/apps/vtex/invoke.ts` — is the action declared there?
56
+ 2. Re-run `npm run generate:invoke`
57
+ 3. Check the generated `invoke.gen.ts` — is the action present?
58
+
59
+ ## Generator Fails: "Could not find @decocms/apps"
60
+
61
+ **Cause**: The script can't locate the apps package.
62
+
63
+ **Fix**: Use `--apps-dir`:
64
+ ```bash
65
+ npx tsx .../generate-invoke.ts --apps-dir ../apps-start
66
+ # or
67
+ npx tsx .../generate-invoke.ts --apps-dir node_modules/@decocms/apps
68
+ ```
69
+
70
+ ## Generator Fails: "Could not find 'export const invoke'"
71
+
72
+ **Cause**: The `invoke.ts` in `@decocms/apps` changed structure.
73
+
74
+ **Fix**: The generator expects:
75
+ ```typescript
76
+ export const invoke = {
77
+ vtex: {
78
+ actions: {
79
+ actionName: createInvokeFn(...) as ...,
80
+ },
81
+ },
82
+ } as const;
83
+ ```
84
+
85
+ If the structure changed, update `generate-invoke.ts` to match.
86
+
87
+ ## New Action Not Available After Re-generating
88
+
89
+ **Checklist**:
90
+ 1. Added to `@decocms/apps/vtex/invoke.ts`?
91
+ 2. Ran `npm run generate:invoke`?
92
+ 3. Check `src/server/invoke.gen.ts` — is the new action in the file?
93
+ 4. Restarted dev server? (HMR may not pick up `.gen.ts` changes)
94
+
95
+ ## TypeScript Errors in invoke.gen.ts
96
+
97
+ The generated file uses `as unknown as` casts for typing. If you see TS errors:
98
+ - They're usually harmless in the generated file
99
+ - The consumer types (what components see) are correct
100
+ - If a type is wrong, fix it in `@decocms/apps/vtex/invoke.ts` and re-generate
101
+
102
+ ## Performance: Are Server Functions Slow?
103
+
104
+ Each `invoke.*` call from the client makes one HTTP request to `/_server`. In dev mode, this goes to the local Vite server. In production (Cloudflare Workers), it's handled within the same worker — effectively zero network latency since it's an in-process function call during SSR, and a single HTTP round-trip during client-side navigation.
105
+
106
+ The overhead vs. direct VTEX calls: one extra hop through the worker, but this is necessary for:
107
+ - Hiding VTEX credentials from the browser
108
+ - Avoiding CORS
109
+ - Allowing server-side cookie propagation
110
+ - Enabling future features like response caching