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