@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,268 @@
|
|
|
1
|
+
# Creating a New Deco App
|
|
2
|
+
|
|
3
|
+
## Quick Start
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
deno task new
|
|
7
|
+
# Select: APP or MCP
|
|
8
|
+
# Enter name: my-integration (kebab-case)
|
|
9
|
+
# Template is cloned, deco.ts updated, manifests generated
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Manual Setup
|
|
13
|
+
|
|
14
|
+
1. Create the directory: `mkdir my-integration`
|
|
15
|
+
2. Add to `deco.ts`:
|
|
16
|
+
```typescript
|
|
17
|
+
app("my-integration"),
|
|
18
|
+
```
|
|
19
|
+
3. Create the minimum files:
|
|
20
|
+
|
|
21
|
+
### `client.ts` — API Interface
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
export interface MyClient {
|
|
25
|
+
"GET /users/:id": {
|
|
26
|
+
response: User;
|
|
27
|
+
searchParams: { fields?: string };
|
|
28
|
+
};
|
|
29
|
+
"POST /users": {
|
|
30
|
+
response: User;
|
|
31
|
+
body: CreateUserInput;
|
|
32
|
+
};
|
|
33
|
+
"GET /products": {
|
|
34
|
+
response: { items: Product[]; total: number };
|
|
35
|
+
searchParams: {
|
|
36
|
+
page?: number;
|
|
37
|
+
limit?: number;
|
|
38
|
+
q?: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
"DELETE /users/:id": {
|
|
42
|
+
response: void;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Key rules:**
|
|
48
|
+
- Key format: `"VERB /path/:param/:optional?"`
|
|
49
|
+
- `:param` = required URL param
|
|
50
|
+
- `:param?` = optional URL param
|
|
51
|
+
- `*` or `*name` = wildcard
|
|
52
|
+
- `response` = return type of `.json()`
|
|
53
|
+
- `body` = POST/PUT/PATCH body (auto-JSON-serialized)
|
|
54
|
+
- `searchParams` = query string parameters
|
|
55
|
+
|
|
56
|
+
### `mod.ts` — App Factory
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { createHttpClient } from "../utils/http.ts";
|
|
60
|
+
import manifest, { Manifest } from "./manifest.gen.ts";
|
|
61
|
+
import type { Secret } from "../website/loaders/secret.ts";
|
|
62
|
+
import type { App, AppContext as AC } from "@deco/deco";
|
|
63
|
+
import { fetchSafe } from "../utils/fetch.ts";
|
|
64
|
+
import { MyClient } from "./client.ts";
|
|
65
|
+
|
|
66
|
+
export type AppContext = AC<ReturnType<typeof MyApp>>;
|
|
67
|
+
|
|
68
|
+
export interface Props {
|
|
69
|
+
/**
|
|
70
|
+
* @description API account identifier
|
|
71
|
+
*/
|
|
72
|
+
account: string;
|
|
73
|
+
/**
|
|
74
|
+
* @title API Key
|
|
75
|
+
* @format password
|
|
76
|
+
*/
|
|
77
|
+
apiKey?: Secret;
|
|
78
|
+
/**
|
|
79
|
+
* @hide true
|
|
80
|
+
*/
|
|
81
|
+
platform: "my-integration";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default function MyApp({ account, apiKey, ...props }: Props) {
|
|
85
|
+
const headers = new Headers();
|
|
86
|
+
if (apiKey) {
|
|
87
|
+
const key = typeof apiKey === "string" ? apiKey : apiKey?.get?.() ?? "";
|
|
88
|
+
headers.set("Authorization", `Bearer ${key}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const api = createHttpClient<MyClient>({
|
|
92
|
+
base: `https://api.example.com/v1/${account}`,
|
|
93
|
+
headers,
|
|
94
|
+
fetcher: fetchSafe,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const state = { ...props, account, api };
|
|
98
|
+
|
|
99
|
+
const app: App<Manifest, typeof state> = {
|
|
100
|
+
state,
|
|
101
|
+
manifest,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return app;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Loaders
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// loaders/products.ts
|
|
112
|
+
import { AppContext } from "../mod.ts";
|
|
113
|
+
|
|
114
|
+
export interface Props {
|
|
115
|
+
/** @description Search query */
|
|
116
|
+
query?: string;
|
|
117
|
+
/** @description Items per page */
|
|
118
|
+
count?: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const loader = async (props: Props, _req: Request, ctx: AppContext) => {
|
|
122
|
+
const { api } = ctx;
|
|
123
|
+
const response = await api["GET /products"]({
|
|
124
|
+
q: props.query,
|
|
125
|
+
limit: props.count ?? 12,
|
|
126
|
+
});
|
|
127
|
+
return response.json();
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export default loader;
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Actions
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// actions/createUser.ts
|
|
137
|
+
import { AppContext } from "../mod.ts";
|
|
138
|
+
|
|
139
|
+
export interface Props {
|
|
140
|
+
name: string;
|
|
141
|
+
email: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const action = async (props: Props, _req: Request, ctx: AppContext) => {
|
|
145
|
+
const { api } = ctx;
|
|
146
|
+
const response = await api["POST /users"]({}, { body: props });
|
|
147
|
+
return response.json();
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default action;
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
4. Run `deno task start` to generate `manifest.gen.ts`
|
|
154
|
+
|
|
155
|
+
## Commerce App Template
|
|
156
|
+
|
|
157
|
+
For e-commerce integrations, additional files are needed:
|
|
158
|
+
|
|
159
|
+
### `utils/transform.ts`
|
|
160
|
+
|
|
161
|
+
Maps platform-specific API responses to schema.org types:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { Product, Offer } from "../../commerce/types.ts";
|
|
165
|
+
|
|
166
|
+
export const toProduct = (raw: PlatformProduct): Product => ({
|
|
167
|
+
"@type": "Product",
|
|
168
|
+
productID: String(raw.skuId),
|
|
169
|
+
sku: String(raw.skuId),
|
|
170
|
+
name: raw.name,
|
|
171
|
+
url: `/${raw.slug}/p`,
|
|
172
|
+
image: raw.images.map(img => ({
|
|
173
|
+
"@type": "ImageObject" as const,
|
|
174
|
+
url: img.url,
|
|
175
|
+
alternateName: img.alt,
|
|
176
|
+
})),
|
|
177
|
+
offers: {
|
|
178
|
+
"@type": "AggregateOffer",
|
|
179
|
+
priceCurrency: "BRL",
|
|
180
|
+
highPrice: raw.listPrice,
|
|
181
|
+
lowPrice: raw.price,
|
|
182
|
+
offerCount: 1,
|
|
183
|
+
offers: [{
|
|
184
|
+
"@type": "Offer",
|
|
185
|
+
seller: String(raw.sellerId), // MUST be ID, not name!
|
|
186
|
+
price: raw.price,
|
|
187
|
+
availability: raw.available
|
|
188
|
+
? "https://schema.org/InStock"
|
|
189
|
+
: "https://schema.org/OutOfStock",
|
|
190
|
+
priceSpecification: [
|
|
191
|
+
{
|
|
192
|
+
"@type": "UnitPriceSpecification",
|
|
193
|
+
priceType: "https://schema.org/ListPrice",
|
|
194
|
+
price: raw.listPrice,
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"@type": "UnitPriceSpecification",
|
|
198
|
+
priceType: "https://schema.org/SalePrice",
|
|
199
|
+
price: raw.price,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
}],
|
|
203
|
+
},
|
|
204
|
+
isVariantOf: {
|
|
205
|
+
"@type": "ProductGroup",
|
|
206
|
+
productGroupID: String(raw.productId),
|
|
207
|
+
hasVariant: [],
|
|
208
|
+
name: raw.productName,
|
|
209
|
+
url: `/${raw.slug}/p`,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Required Commerce Loaders
|
|
215
|
+
|
|
216
|
+
| Loader | Return Type | Purpose |
|
|
217
|
+
|--------|-------------|---------|
|
|
218
|
+
| `productDetailsPage.ts` | `ProductDetailsPage` | PDP |
|
|
219
|
+
| `productListingPage.ts` | `ProductListingPage` | PLP/Category |
|
|
220
|
+
| `productList.ts` | `Product[]` | Product shelf/carousel |
|
|
221
|
+
| `suggestions.ts` | `Suggestion` | Search autocomplete |
|
|
222
|
+
| `cart.ts` | `OrderForm` (platform-specific) | Shopping cart |
|
|
223
|
+
| `user.ts` | `Person` | Current user |
|
|
224
|
+
|
|
225
|
+
### Required Commerce Actions
|
|
226
|
+
|
|
227
|
+
| Action | Purpose |
|
|
228
|
+
|--------|---------|
|
|
229
|
+
| `cart/addItems.ts` | Add to cart |
|
|
230
|
+
| `cart/updateItems.ts` | Update quantities |
|
|
231
|
+
| `cart/removeItems.ts` | Remove/clear cart |
|
|
232
|
+
| `cart/updateCoupons.ts` | Apply coupon |
|
|
233
|
+
| `authentication/signIn.ts` | User login |
|
|
234
|
+
| `authentication/logout.ts` | User logout |
|
|
235
|
+
|
|
236
|
+
### Required Hooks
|
|
237
|
+
|
|
238
|
+
| Hook | State | Purpose |
|
|
239
|
+
|------|-------|---------|
|
|
240
|
+
| `context.ts` | `{ cart, user, wishlist }` | Central reactive state |
|
|
241
|
+
| `useCart.ts` | Cart mutations | Cart operations |
|
|
242
|
+
| `useUser.ts` | User state | User info |
|
|
243
|
+
| `useWishlist.ts` | Wishlist CRUD | Wishlist operations |
|
|
244
|
+
|
|
245
|
+
## MCP App Template
|
|
246
|
+
|
|
247
|
+
For AI/MCP integrations:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
deno task new
|
|
251
|
+
# Select: MCP
|
|
252
|
+
# Template: Oauth
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
MCP apps expose tools for AI assistants via the Deco MCP protocol. They use `mcp/` utilities for OAuth, bindings, and context.
|
|
256
|
+
|
|
257
|
+
## Checklist
|
|
258
|
+
|
|
259
|
+
- [ ] `client.ts` with typed API interface
|
|
260
|
+
- [ ] `mod.ts` with Props and app factory
|
|
261
|
+
- [ ] `manifest.gen.ts` generated via `deno task start`
|
|
262
|
+
- [ ] Loaders for read operations
|
|
263
|
+
- [ ] Actions for write operations
|
|
264
|
+
- [ ] `deco.ts` updated with `app("my-app")`
|
|
265
|
+
- [ ] README.md with usage instructions
|
|
266
|
+
- [ ] (Commerce) `transform.ts` for schema.org mapping
|
|
267
|
+
- [ ] (Commerce) All required loaders and actions
|
|
268
|
+
- [ ] (Commerce) Client-side hooks
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Scripts & Codegen Reference
|
|
2
|
+
|
|
3
|
+
## `scripts/start.ts`
|
|
4
|
+
|
|
5
|
+
Main codegen script, runs via `deno task start`. Performs three steps in sequence:
|
|
6
|
+
|
|
7
|
+
### 1. OpenAPI Type Generation
|
|
8
|
+
|
|
9
|
+
Walks the entire repo looking for `*.openapi.json` files, then:
|
|
10
|
+
|
|
11
|
+
1. Parses the OpenAPI 3.x spec
|
|
12
|
+
2. Extracts every endpoint: `VERB /path` → typed interface with `response`, `body`, `searchParams`
|
|
13
|
+
3. Converts URL path params: `/{userId}` → `/:userId`
|
|
14
|
+
4. Handles nullable types (OpenAPI `nullable: true` → TypeScript union with `null`)
|
|
15
|
+
5. Compiles via `json-schema-to-typescript`
|
|
16
|
+
6. Outputs `*.openapi.gen.ts` next to the JSON spec
|
|
17
|
+
7. Formats with `deno fmt` and lints with `deno lint`
|
|
18
|
+
|
|
19
|
+
**Location pattern:** `{app}/utils/openapi/{name}.openapi.json` → `{app}/utils/openapi/{name}.openapi.gen.ts`
|
|
20
|
+
|
|
21
|
+
**Usage:** The generated types are consumed by `createHttpClient<GeneratedType>()`.
|
|
22
|
+
|
|
23
|
+
### 2. GraphQL Type Generation
|
|
24
|
+
|
|
25
|
+
Walks the repo for `*.graphql.json` files (GraphQL SDL schemas), then:
|
|
26
|
+
|
|
27
|
+
1. Uses `@graphql-codegen/cli` with plugins:
|
|
28
|
+
- `typescript` — base types from schema
|
|
29
|
+
- `typescript-operations` — types from query documents (`.ts` files with `gql` tags)
|
|
30
|
+
2. Outputs `*.graphql.gen.ts` in the same directory
|
|
31
|
+
3. Scans all `**/*.ts` files for query/mutation documents
|
|
32
|
+
|
|
33
|
+
**Location pattern:** `{app}/utils/storefront/{name}.graphql.json` → `{app}/utils/storefront/{name}.graphql.gen.ts`
|
|
34
|
+
|
|
35
|
+
### 3. Deco Bundle
|
|
36
|
+
|
|
37
|
+
Calls `@deco/deco/scripts/bundle` which:
|
|
38
|
+
- Scans all apps listed in `deco.ts`
|
|
39
|
+
- Generates `manifest.gen.ts` for each app
|
|
40
|
+
- Registers actions, loaders, handlers, sections, workflows
|
|
41
|
+
|
|
42
|
+
## `scripts/new.ts`
|
|
43
|
+
|
|
44
|
+
Interactive project scaffolder, runs via `deno task new`:
|
|
45
|
+
|
|
46
|
+
1. Prompts for type: `APP` or `MCP`
|
|
47
|
+
2. Lists available templates per type
|
|
48
|
+
3. Prompts for project name (validated as kebab-case)
|
|
49
|
+
4. Clones the template repository:
|
|
50
|
+
- APP → `https://github.com/deco-cx/app-template`
|
|
51
|
+
- MCP → `https://github.com/deco-cx/mcp-oauth-template`
|
|
52
|
+
5. Updates `deco.ts` to register the new app: `app("{name}")`
|
|
53
|
+
6. Removes template `.git` directory
|
|
54
|
+
7. Removes template `deno.json` (uses root config)
|
|
55
|
+
8. Runs `deno task start` to generate manifests
|
|
56
|
+
|
|
57
|
+
## `deno.json` Tasks
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"tasks": {
|
|
62
|
+
"check": "deno fmt --check && deno lint && deno check **/mod.ts",
|
|
63
|
+
"start": "deno run -A ./scripts/start.ts",
|
|
64
|
+
"bundle": "deno run -A jsr:@deco/deco/scripts/bundle",
|
|
65
|
+
"new": "deno run -A ./scripts/new.ts",
|
|
66
|
+
"release": "deno eval 'import \"deco/scripts/release.ts\"'",
|
|
67
|
+
"link": "deno eval 'import \"deco/scripts/apps/link.ts\"'",
|
|
68
|
+
"unlink": "deno eval 'import \"deco/scripts/apps/unlink.ts\"'",
|
|
69
|
+
"serve": "deno eval 'import \"deco/scripts/apps/serve.ts\"'",
|
|
70
|
+
"watcher": "deno eval 'import \"deco/scripts/apps/watcher.ts\"'",
|
|
71
|
+
"update": "deno eval 'import \"deco/scripts/update.ts\"'",
|
|
72
|
+
"reload": "deno cache -r https://deco.cx/run"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Task | Purpose |
|
|
78
|
+
|------|---------|
|
|
79
|
+
| `start` | Full codegen (OpenAPI + GraphQL + manifest) |
|
|
80
|
+
| `check` | CI quality gate (format + lint + type-check) |
|
|
81
|
+
| `bundle` | Only manifest generation (skip OpenAPI/GraphQL) |
|
|
82
|
+
| `new` | Scaffold new app from template |
|
|
83
|
+
| `release` | Publish new version |
|
|
84
|
+
| `link`/`unlink` | Link local apps for development |
|
|
85
|
+
| `serve` | Start local development server |
|
|
86
|
+
| `watcher` | File watcher for auto-rebuild |
|
|
87
|
+
| `update` | Update Deco dependencies |
|
|
88
|
+
| `reload` | Cache bust |
|
|
89
|
+
|
|
90
|
+
## CI/CD (`.github/workflows/`)
|
|
91
|
+
|
|
92
|
+
### `ci.yaml` (Push/PR)
|
|
93
|
+
1. `deno task start` — full build
|
|
94
|
+
2. `deno task check` — format + lint + type-check
|
|
95
|
+
3. `deno test` (continue-on-error)
|
|
96
|
+
4. `deno bench` (continue-on-error)
|
|
97
|
+
|
|
98
|
+
### `release.yaml` (Tag push)
|
|
99
|
+
Publishes release on tag creation.
|
|
100
|
+
|
|
101
|
+
### `releaser.yaml` (PR/Comments)
|
|
102
|
+
- On PR: adds comment with version bump options (patch/minor/major)
|
|
103
|
+
- On comment reaction: updates version in `deno.json`, creates git tag
|
|
104
|
+
|
|
105
|
+
## `deco.ts` — App Registry
|
|
106
|
+
|
|
107
|
+
Central configuration listing all apps:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const app = (name: string) => ({ dir: name, name });
|
|
111
|
+
|
|
112
|
+
const compatibilityApps = [
|
|
113
|
+
{ dir: "./compat/$live", name: "$live" },
|
|
114
|
+
{ dir: "./compat/std", name: "deco-sites/std" },
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
const config = {
|
|
118
|
+
apps: [
|
|
119
|
+
app("vtex"),
|
|
120
|
+
app("shopify"),
|
|
121
|
+
app("website"),
|
|
122
|
+
app("commerce"),
|
|
123
|
+
// ... ~90 apps total
|
|
124
|
+
...compatibilityApps,
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export default config;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The `name` must match the directory name. Order matters for dependency resolution.
|
|
132
|
+
|
|
133
|
+
## Adding OpenAPI Types to an App
|
|
134
|
+
|
|
135
|
+
1. Place your OpenAPI 3.x spec at `{app}/utils/openapi/{name}.openapi.json`
|
|
136
|
+
2. Run `deno task start`
|
|
137
|
+
3. Import the generated types:
|
|
138
|
+
```typescript
|
|
139
|
+
import { OpenAPI } from "./utils/openapi/{name}.openapi.gen.ts";
|
|
140
|
+
const client = createHttpClient<OpenAPI>({ base: "https://api.example.com" });
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Adding GraphQL Types to an App
|
|
144
|
+
|
|
145
|
+
1. Place your GraphQL SDL at `{app}/utils/{name}.graphql.json`
|
|
146
|
+
2. Write queries in `.ts` files using the `gql` tag
|
|
147
|
+
3. Run `deno task start`
|
|
148
|
+
4. Import generated types from `{name}.graphql.gen.ts`
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Shared Utils — `/utils/` Reference
|
|
2
|
+
|
|
3
|
+
The root `/utils/` directory provides platform-agnostic utilities used by all apps.
|
|
4
|
+
|
|
5
|
+
## `http.ts` — Typed HTTP Client
|
|
6
|
+
|
|
7
|
+
### `createHttpClient<T>(options)`
|
|
8
|
+
|
|
9
|
+
Creates a Proxy-based HTTP client typed against an interface `T` where keys follow the pattern `"VERB /path/:param"`.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { createHttpClient } from "../utils/http.ts";
|
|
13
|
+
|
|
14
|
+
interface MyAPI {
|
|
15
|
+
"GET /users/:id": {
|
|
16
|
+
response: User;
|
|
17
|
+
searchParams: { fields?: string };
|
|
18
|
+
};
|
|
19
|
+
"POST /users": {
|
|
20
|
+
response: User;
|
|
21
|
+
body: { name: string; email: string };
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const client = createHttpClient<MyAPI>({
|
|
26
|
+
base: "https://api.example.com",
|
|
27
|
+
headers: new Headers({ "Authorization": "Bearer ..." }),
|
|
28
|
+
fetcher: fetchSafe, // Optional custom fetcher
|
|
29
|
+
processHeaders: removeDirtyCookies, // Optional header processor
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Usage — fully typed params, body, and response
|
|
33
|
+
const res = await client["GET /users/:id"]({ id: "123", fields: "name" });
|
|
34
|
+
const user = await res.json(); // typed as User
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Interface Pattern for Typed Endpoints
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
interface API {
|
|
41
|
+
"VERB /path/:required/:optional?/*wildcard": {
|
|
42
|
+
response: ResponseType; // Return type of .json()
|
|
43
|
+
body: BodyType; // Required for POST/PUT/PATCH
|
|
44
|
+
searchParams: { // Query string parameters
|
|
45
|
+
required: string;
|
|
46
|
+
optional?: number;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- URL params with `:` are extracted from the first argument
|
|
53
|
+
- Remaining keys become query string params
|
|
54
|
+
- `body` auto-serializes objects to JSON
|
|
55
|
+
|
|
56
|
+
### `HttpError`
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
class HttpError extends Error {
|
|
60
|
+
status: number;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### `nullOnNotFound`
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const product = await client["GET /product/:id"]({ id }).catch(nullOnNotFound);
|
|
68
|
+
// Returns null for 404, rethrows other errors
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## `graphql.ts` — GraphQL Client
|
|
74
|
+
|
|
75
|
+
### `createGraphqlClient(options)`
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { createGraphqlClient, gql } from "../utils/graphql.ts";
|
|
79
|
+
|
|
80
|
+
const client = createGraphqlClient({
|
|
81
|
+
endpoint: "https://api.example.com/graphql",
|
|
82
|
+
fetcher: fetchSafe,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const data = await client.query<ResponseType, VariablesType>({
|
|
86
|
+
query: gql`query GetUser($id: ID!) { user(id: $id) { name } }`,
|
|
87
|
+
variables: { id: "123" },
|
|
88
|
+
operationName: "GetUser", // optional
|
|
89
|
+
fragments: [fragmentStr], // optional — appended to query
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `gql` Template Tag
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const MY_QUERY = gql`
|
|
97
|
+
query GetProducts($first: Int!) {
|
|
98
|
+
products(first: $first) { id name }
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## `fetch.ts` — Safe Fetch with Retry
|
|
106
|
+
|
|
107
|
+
### `fetchSafe(input, init?)`
|
|
108
|
+
|
|
109
|
+
Wraps `fetch` with:
|
|
110
|
+
- Retry on connection closed errors (1 retry, exponential backoff)
|
|
111
|
+
- Throws `HttpError` on non-OK responses
|
|
112
|
+
- Handles 301/302 with `redirect: "manual"`
|
|
113
|
+
|
|
114
|
+
### `fetchAPI<T>(input, init?)`
|
|
115
|
+
|
|
116
|
+
Convenience that sets `Accept: application/json` and returns `response.json()`.
|
|
117
|
+
|
|
118
|
+
### `STALE`
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
export const STALE = {
|
|
122
|
+
deco: { cache: "stale-while-revalidate" },
|
|
123
|
+
} as const;
|
|
124
|
+
|
|
125
|
+
// Usage: fetch(url, { ...init, ...STALE })
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## `cookie.ts` — Cookie Utilities
|
|
131
|
+
|
|
132
|
+
### `proxySetCookie(from, to, toDomain?)`
|
|
133
|
+
|
|
134
|
+
Copies Set-Cookie headers from one response to another, optionally rewriting the domain.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
proxySetCookie(vtexResponse.headers, ctx.response.headers, req.url);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `getFlagsFromCookies(cookies)`
|
|
141
|
+
|
|
142
|
+
Extracts Deco feature flags from the `DECO_SEGMENT` cookie.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## `normalize.ts` — String Sanitization
|
|
147
|
+
|
|
148
|
+
| Function | Purpose |
|
|
149
|
+
|----------|---------|
|
|
150
|
+
| `removeScriptChars(str)` | Removes `+`, brackets, slashes, dots, diacritics |
|
|
151
|
+
| `removeNonLatin1Chars(str)` | Strips non-ASCII and quotes |
|
|
152
|
+
| `removeNonAscChars(str)` | Same as above (alias) |
|
|
153
|
+
| `removeDirtyCookies(headers)` | Sanitizes the `cookie` header — removes brackets and diacritics |
|
|
154
|
+
|
|
155
|
+
Used as `processHeaders` in HTTP clients to prevent malformed cookies from breaking API calls.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Other Utils
|
|
160
|
+
|
|
161
|
+
| File | Purpose |
|
|
162
|
+
|------|---------|
|
|
163
|
+
| `lru.ts` | LRU cache with `get`, `set`, `delete` |
|
|
164
|
+
| `shortHash.ts` | `hashString` (SHA-256 async), `hashStringSync` (simple hash) |
|
|
165
|
+
| `pool.ts` | Resource pool with `acquire`/`release` (uses Deferred) |
|
|
166
|
+
| `worker.ts` | Web Worker abstraction (Comlink-style `postMessage` RPC) |
|
|
167
|
+
| `dataURI.ts` | Converts scripts to `data:` URIs for inline `<script>` tags |
|
|
168
|
+
| `capitalize.ts` | Capitalizes first letter of each word |
|
|
169
|
+
| `deferred.ts` | `__DECO_FBT` and `shouldForceRender` for async rendering |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Commerce Utils (`/commerce/utils/`)
|
|
174
|
+
|
|
175
|
+
| File | Purpose |
|
|
176
|
+
|------|---------|
|
|
177
|
+
| `canonical.ts` | `canonicalFromBreadcrumblist(b)` — extracts URL from last breadcrumb item |
|
|
178
|
+
| `constants.ts` | `DEFAULT_IMAGE` — placeholder ImageObject for products without images |
|
|
179
|
+
| `filters.ts` | `parseRange("10:100")` → `{ from: 10, to: 100 }`, `formatRange(10, 100)` → `"10:100"` |
|
|
180
|
+
| `productToAnalyticsItem.ts` | `mapProductToAnalyticsItem({ product, breadcrumbList, price, ... })` → `AnalyticsItem` |
|
|
181
|
+
| `stateByZip.ts` | `getStateFromZip("01001000")` → `"SP"` — maps Brazilian ZIP to state |
|