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