@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,220 @@
|
|
|
1
|
+
# Resolution Engine (`engine/`)
|
|
2
|
+
|
|
3
|
+
The heart of Deco — resolves configuration objects (Resolvables) into runtime values using a pipeline of resolvers.
|
|
4
|
+
|
|
5
|
+
## Core Concepts
|
|
6
|
+
|
|
7
|
+
### Resolvable
|
|
8
|
+
|
|
9
|
+
An object with `__resolveType` that tells the engine which resolver to use:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
interface Resolvable<T = unknown> {
|
|
13
|
+
__resolveType: string; // resolver key (e.g. "site/loaders/productList.ts")
|
|
14
|
+
[key: string]: unknown; // props passed to the resolver
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Resolver
|
|
19
|
+
|
|
20
|
+
A function that processes a Resolvable:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
type Resolver<T = unknown> = (
|
|
24
|
+
parent: T, // resolved props from the Resolvable
|
|
25
|
+
context: ResolverContext
|
|
26
|
+
) => T | Resolvable<T> | Promise<T | Resolvable<T>>;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### ResolverContext
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
interface ResolverContext {
|
|
33
|
+
resolvables: Record<string, Resolvable>; // decofile state
|
|
34
|
+
resolvers: Record<string, Resolver>; // all registered resolvers
|
|
35
|
+
resolve: (resolvable: Resolvable | string) => Promise<unknown>;
|
|
36
|
+
resolveChain: FieldResolver[]; // debug: resolution path
|
|
37
|
+
memo: Map<string, Promise<unknown>>; // memoization cache
|
|
38
|
+
runOnce: (key: string, fn: () => Promise<T>) => Promise<T>;
|
|
39
|
+
revision: string;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Resolution Pipeline
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
resolve(input)
|
|
47
|
+
│
|
|
48
|
+
├─ string → resolveWithType(resolveType, {}, context)
|
|
49
|
+
│
|
|
50
|
+
└─ Resolvable → resolveAny()
|
|
51
|
+
│
|
|
52
|
+
└─ resolvePropsWithHints()
|
|
53
|
+
│
|
|
54
|
+
├─ Extract __resolveType
|
|
55
|
+
├─ Call onBeforeResolveProps (if defined)
|
|
56
|
+
├─ Resolve each prop recursively (parallel)
|
|
57
|
+
└─ resolveWithType(__resolveType)
|
|
58
|
+
│
|
|
59
|
+
├─ In resolvables? → resolveResolvable() [with memo]
|
|
60
|
+
├─ In resolvers? → invokeResolverWithProps()
|
|
61
|
+
└─ Else → danglingRecover or DanglingReference error
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Key Functions (`engine/core/resolver.ts`)
|
|
65
|
+
|
|
66
|
+
| Function | Purpose |
|
|
67
|
+
|----------|---------|
|
|
68
|
+
| `resolve(input, ctx)` | Main entry — handles string refs and Resolvable objects |
|
|
69
|
+
| `resolveAny(obj, ctx)` | Resolves an arbitrary object, recursing into props |
|
|
70
|
+
| `resolvePropsWithHints(obj, ctx)` | Uses hint nodes to efficiently resolve only resolvable props |
|
|
71
|
+
| `resolveWithType(type, props, ctx)` | Resolves by looking up type in resolvables or resolvers |
|
|
72
|
+
| `resolveResolvable(id, ctx)` | Resolves a named resolvable with memoization |
|
|
73
|
+
| `invokeResolverWithProps(type, props, ctx)` | Calls the resolver function with resolved props |
|
|
74
|
+
|
|
75
|
+
### Resolution Hints (`engine/core/hints.ts`)
|
|
76
|
+
|
|
77
|
+
Optimization to avoid scanning all props. `HintNode` trees mark which properties contain resolvables:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
interface HintNode {
|
|
81
|
+
__resolveType?: boolean; // this object is a Resolvable
|
|
82
|
+
[prop: string]: HintNode; // nested hints
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`traverseAny()` builds hints from the decofile. The resolver only recurses into hinted paths.
|
|
87
|
+
|
|
88
|
+
### Memoization
|
|
89
|
+
|
|
90
|
+
`resolveResolvable` memos results by `resolverIdFromResolveChain`. Same resolvable in the same resolution tree returns the cached promise.
|
|
91
|
+
|
|
92
|
+
## Resolve Chain
|
|
93
|
+
|
|
94
|
+
A debug/tracing mechanism. Each resolution step appends a `FieldResolver`:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
type FieldResolver =
|
|
98
|
+
| { type: "prop"; value: string } // accessing a property
|
|
99
|
+
| { type: "resolvable"; value: string } // resolving a named resolvable
|
|
100
|
+
| { type: "resolver"; value: string } // invoking a resolver
|
|
101
|
+
| { type: "dangling"; value: string } // unresolved reference
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Used for cache keys, tracing, and error reporting.
|
|
105
|
+
|
|
106
|
+
## ReleaseResolver (`engine/core/mod.ts`)
|
|
107
|
+
|
|
108
|
+
Top-level coordinator:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
function ReleaseResolver(
|
|
112
|
+
decofileProvider: DecofileProvider,
|
|
113
|
+
resolvers: Record<string, Resolver>,
|
|
114
|
+
options?: { danglingRecover?, overrides?, forceFresh? }
|
|
115
|
+
): {
|
|
116
|
+
resolve: (resolvable | string) => Promise<unknown>;
|
|
117
|
+
state: () => ResolvableMap;
|
|
118
|
+
revision: () => string;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- Gets `resolvables` and `revision` from the DecofileProvider
|
|
123
|
+
- Combines default resolvers + block resolvers + app resolvers
|
|
124
|
+
- Provides the top-level `resolve()` function used by the runtime
|
|
125
|
+
|
|
126
|
+
## DecofileProvider (`engine/decofile/`)
|
|
127
|
+
|
|
128
|
+
Interface for state management (the decofile is the CMS state):
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
interface DecofileProvider {
|
|
132
|
+
state(): Record<string, Resolvable>; // current state
|
|
133
|
+
revision(): string; // version/etag
|
|
134
|
+
onChange(cb: () => void): void; // subscribe to changes
|
|
135
|
+
dispose?(): void;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Implementations
|
|
140
|
+
|
|
141
|
+
| Provider | Source | Usage |
|
|
142
|
+
|----------|--------|-------|
|
|
143
|
+
| `newFsProvider` | Local filesystem (`.json`, `.jsonl`) | Dev mode |
|
|
144
|
+
| `jsonProvider` | In-memory JSON object | Testing, static |
|
|
145
|
+
| `realtimeProvider` | CMS websocket connection | Production |
|
|
146
|
+
| `fsFolderProvider` | `folder://` directory with individual files | Legacy |
|
|
147
|
+
|
|
148
|
+
### State Lifecycle
|
|
149
|
+
|
|
150
|
+
1. `DecofileProvider.state()` returns current `ResolvableMap`
|
|
151
|
+
2. Engine creates `ReleaseResolver` with this state
|
|
152
|
+
3. Provider emits `onChange()` when CMS publishes
|
|
153
|
+
4. Engine reinstalls apps and rebuilds resolvers
|
|
154
|
+
|
|
155
|
+
## Manifest System (`engine/manifest/`)
|
|
156
|
+
|
|
157
|
+
### ManifestBuilder
|
|
158
|
+
|
|
159
|
+
Programmatically builds a manifest file:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
interface ManifestData {
|
|
163
|
+
namespace: string;
|
|
164
|
+
imports: Record<string, string>; // import name → module path
|
|
165
|
+
manifest: Record<string, string[]>; // block type → keys
|
|
166
|
+
exports: Record<string, string>;
|
|
167
|
+
statements: string[];
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Key methods:
|
|
172
|
+
- `mergeWith(other)` — merge manifests from multiple apps
|
|
173
|
+
- `addValuesOnManifestKey(key, values)` — add blocks by type
|
|
174
|
+
- `build()` — generate the `.ts` file content
|
|
175
|
+
|
|
176
|
+
### manifestGen
|
|
177
|
+
|
|
178
|
+
`decoManifestBuilder()` walks directories by block type, calling `withDefinition()` for each discovered module.
|
|
179
|
+
|
|
180
|
+
### Default Resolvers (`engine/manifest/defaults.ts`)
|
|
181
|
+
|
|
182
|
+
Built-in resolvers available in every Deco instance:
|
|
183
|
+
|
|
184
|
+
| Resolver | Purpose |
|
|
185
|
+
|----------|---------|
|
|
186
|
+
| `state` | Returns raw resolvable value |
|
|
187
|
+
| `resolvables` | Access resolvables map |
|
|
188
|
+
| `resolvers` | Access resolvers map |
|
|
189
|
+
| `once` | Run-once resolver (memoized) |
|
|
190
|
+
| `resolveTypeSelector` | Dynamic __resolveType selection |
|
|
191
|
+
| `blockSelector` | Select block by type |
|
|
192
|
+
| `selectKeys` | Pick specific keys from resolved object |
|
|
193
|
+
| `mergeProps` | Deep-merge multiple resolved objects |
|
|
194
|
+
| `resolved` | Mark value as already resolved |
|
|
195
|
+
| `preview` | `Preview@{block}` namespace |
|
|
196
|
+
| `invoke` | `Invoke@{block}` namespace |
|
|
197
|
+
|
|
198
|
+
## Schema System (`engine/schema/`)
|
|
199
|
+
|
|
200
|
+
Generates JSON Schema from TypeScript types at runtime:
|
|
201
|
+
|
|
202
|
+
| File | Purpose |
|
|
203
|
+
|------|---------|
|
|
204
|
+
| `schemeable.ts` | Core schema types (`Schemeable`) |
|
|
205
|
+
| `builder.ts` | Builds JSON Schema from scheemables |
|
|
206
|
+
| `parser.ts` | Parses TypeScript AST into scheemables |
|
|
207
|
+
| `merge.ts` | Merges schemas from multiple sources |
|
|
208
|
+
| `reader.ts` | Reads and caches module schemas |
|
|
209
|
+
| `transform.ts` | Schema transformations (refs, ids) |
|
|
210
|
+
| `lazy.ts` | Lazy schema loading for performance |
|
|
211
|
+
|
|
212
|
+
Used by the admin to generate forms for block props.
|
|
213
|
+
|
|
214
|
+
## Import Map (`engine/importmap/`)
|
|
215
|
+
|
|
216
|
+
Builds Deno import maps for blocks:
|
|
217
|
+
|
|
218
|
+
- `buildImportMap(manifest)` — generates imports for each block
|
|
219
|
+
- `FuncAddr.build()` — creates `resolve://path?export=funcName` addresses
|
|
220
|
+
- Supports JSR (`jsr:`) and URL imports
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Hooks and Components
|
|
2
|
+
|
|
3
|
+
Server-side hooks and shared components provided by the Deco framework.
|
|
4
|
+
|
|
5
|
+
## Hooks (`hooks/`)
|
|
6
|
+
|
|
7
|
+
### useSection (`useSection.ts`)
|
|
8
|
+
|
|
9
|
+
Generates a URL for partial section rendering via `/deco/render`.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
function useSection<TProps>(options: {
|
|
13
|
+
props?: Partial<TProps>; // new props for the section
|
|
14
|
+
href?: string; // target URL
|
|
15
|
+
}): string // returns URL to POST for partial render
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Used in section components to create interactive behavior without client-side JS:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
function ProductShelf({ products, page = 0 }: Props) {
|
|
22
|
+
const nextPageUrl = useSection({ props: { page: page + 1 } });
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div>
|
|
26
|
+
{products.map(p => <ProductCard product={p} />)}
|
|
27
|
+
<button hx-get={nextPageUrl} hx-target="closest section">
|
|
28
|
+
Load more
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Internals:
|
|
36
|
+
- Serializes `props`, `href`, `pathTemplate`, `resolveChain` into the URL
|
|
37
|
+
- Strips tracking query params (BLOCKED_QS: utm_*, gclid, fbclid, etc.)
|
|
38
|
+
- Supports `__cb` callback parameter
|
|
39
|
+
|
|
40
|
+
### usePartialSection (`usePartialSection.ts`)
|
|
41
|
+
|
|
42
|
+
Fresh-specific: returns attributes for Fresh `<Partial>` navigation.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
function usePartialSection<TProps>(options: {
|
|
46
|
+
props?: Partial<TProps>;
|
|
47
|
+
href?: string;
|
|
48
|
+
}): { "f-client-nav": true; "f-partial": string }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Usage:
|
|
52
|
+
```tsx
|
|
53
|
+
<button {...usePartialSection({ props: { page: 2 } })}>
|
|
54
|
+
Next Page
|
|
55
|
+
</button>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### useScript (`useScript.ts`)
|
|
59
|
+
|
|
60
|
+
Minifies a function and returns it as a script string or data URI.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// Returns minified script string
|
|
64
|
+
function useScript<Args>(fn: (...args: Args) => void, ...args: Args): string;
|
|
65
|
+
|
|
66
|
+
// Returns data:text/javascript,... URI
|
|
67
|
+
function useScriptAsDataURI<Args>(fn: (...args: Args) => void, ...args: Args): string;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Usage:
|
|
71
|
+
```tsx
|
|
72
|
+
<script dangerouslySetInnerHTML={{
|
|
73
|
+
__html: useScript((count) => {
|
|
74
|
+
document.getElementById("counter").textContent = String(count);
|
|
75
|
+
}, initialCount)
|
|
76
|
+
}} />
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Uses Terser for minification.
|
|
80
|
+
|
|
81
|
+
### useDevice (`useDevice.ts`)
|
|
82
|
+
|
|
83
|
+
Returns the current device type from the server context.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
function useDevice(): "desktop" | "mobile" | "tablet";
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Detection is done server-side via User-Agent header using `userAgent.ts`.
|
|
90
|
+
|
|
91
|
+
### useSetEarlyHints (`useSetEarlyHints.ts`)
|
|
92
|
+
|
|
93
|
+
Adds `Link` headers for HTTP 103 Early Hints (preload/preconnect).
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
function useSetEarlyHints(links: Array<{
|
|
97
|
+
href: string;
|
|
98
|
+
as?: string;
|
|
99
|
+
rel?: string;
|
|
100
|
+
}>): void;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Components (`components/`)
|
|
104
|
+
|
|
105
|
+
### Section (`section.tsx`)
|
|
106
|
+
|
|
107
|
+
Core section rendering infrastructure:
|
|
108
|
+
|
|
109
|
+
| Export | Purpose |
|
|
110
|
+
|--------|---------|
|
|
111
|
+
| `withSection(Component)` | HOC wrapping component with context + error boundary |
|
|
112
|
+
| `SectionContext` | Preact context providing section ID, resolve chain |
|
|
113
|
+
| `getSectionID(resolveChain)` | Extracts unique section ID from resolve chain |
|
|
114
|
+
| `ErrorBoundary` | Catches render errors, shows fallback UI |
|
|
115
|
+
| `Framework` | Framework-agnostic section wrapper |
|
|
116
|
+
|
|
117
|
+
### StubSection (`StubSection.tsx`)
|
|
118
|
+
|
|
119
|
+
Placeholder for dangling references (when a section's module is not found):
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
function StubSection({ component }: { component: string }) {
|
|
123
|
+
return <div>Section not found: {component}</div>;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Used as `defaultDanglingRecover` for section blocks.
|
|
128
|
+
|
|
129
|
+
### PreviewNotAvailable (`PreviewNotAvailable.tsx`)
|
|
130
|
+
|
|
131
|
+
Shown in admin when a block doesn't have a preview implementation.
|
|
132
|
+
|
|
133
|
+
### LiveControls (`LiveControls.tsx`)
|
|
134
|
+
|
|
135
|
+
Injected into every page in edit mode. Provides:
|
|
136
|
+
|
|
137
|
+
- `__DECO_STATE` global with decofile state
|
|
138
|
+
- `DomInspector` for element selection
|
|
139
|
+
- Keyboard shortcuts (`Ctrl+Shift+E` for editor)
|
|
140
|
+
- `postMessage` bridge to admin iframe
|
|
141
|
+
- Script loads from `decoAssistantUrl`
|
|
142
|
+
|
|
143
|
+
### JsonViewer (`JsonViewer.tsx`)
|
|
144
|
+
|
|
145
|
+
Renders JSON data using jQuery JSONView. Used in admin previews for loaders/actions.
|
|
146
|
+
|
|
147
|
+
## Exports Map (`hooks/mod.ts`)
|
|
148
|
+
|
|
149
|
+
All hooks are re-exported from `@deco/deco/hooks`:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
export { useSection } from "./useSection.ts";
|
|
153
|
+
export { usePartialSection } from "./usePartialSection.ts";
|
|
154
|
+
export { useScript, useScriptAsDataURI } from "./useScript.ts";
|
|
155
|
+
export { useDevice } from "./useDevice.ts";
|
|
156
|
+
export { useSetEarlyHints } from "./useSetEarlyHints.ts";
|
|
157
|
+
```
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Plugins and Clients
|
|
2
|
+
|
|
3
|
+
## Plugins (`plugins/`)
|
|
4
|
+
|
|
5
|
+
### Deco Plugin (`plugins/deco.ts`)
|
|
6
|
+
|
|
7
|
+
The main Fresh plugin that integrates Deco into a Fresh app.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { plugins } from "deco/plugins/deco.ts";
|
|
11
|
+
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
plugins: plugins({
|
|
14
|
+
manifest,
|
|
15
|
+
htmx: false,
|
|
16
|
+
site: "mysite",
|
|
17
|
+
ErrorFallback,
|
|
18
|
+
useServer: true,
|
|
19
|
+
middlewares: [],
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Internally wraps `runtime/fresh/plugin.tsx`. Registers routes (`/[...catchall]`, `/index`), island (`DispatchAsyncRender.tsx`), selects framework (Fresh or HTMX), injects middleware chain.
|
|
25
|
+
|
|
26
|
+
### Fresh + Tailwind Plugin (`plugins/fresh.ts`)
|
|
27
|
+
|
|
28
|
+
Convenience that combines Tailwind CSS and Deco:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { plugins } from "deco/plugins/fresh.ts";
|
|
32
|
+
|
|
33
|
+
export default defineConfig({
|
|
34
|
+
plugins: plugins({
|
|
35
|
+
manifest,
|
|
36
|
+
tailwind: { /* config */ },
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Returns `[tailwindPlugin(config), decoPlugin(options)]`.
|
|
42
|
+
|
|
43
|
+
### Styles Plugin (`plugins/styles.ts`)
|
|
44
|
+
|
|
45
|
+
Injects global CSS into `<head>` via `id="__DECO_GLOBAL_STYLES__"`.
|
|
46
|
+
|
|
47
|
+
## Client-side Invoke (`clients/`)
|
|
48
|
+
|
|
49
|
+
### withManifest (`clients/withManifest.ts`)
|
|
50
|
+
|
|
51
|
+
Creates a typed invoke client for calling server loaders/actions from the browser.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { proxy } from "@deco/deco/web";
|
|
55
|
+
import type { Manifest } from "./manifest.gen.ts";
|
|
56
|
+
|
|
57
|
+
const invoke = proxy<Manifest>();
|
|
58
|
+
|
|
59
|
+
// Single invoke
|
|
60
|
+
const products = await invoke["site/loaders/productList.ts"]({ query: "shoes" });
|
|
61
|
+
|
|
62
|
+
// Batch invoke
|
|
63
|
+
const [products, categories] = await invoke({
|
|
64
|
+
"site/loaders/productList.ts": { query: "shoes" },
|
|
65
|
+
"site/loaders/categories.ts": {},
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Internals: `fetchWithProps(key, props)` POSTs to `/live/invoke/${key}`. Supports `text/event-stream` for streaming.
|
|
70
|
+
|
|
71
|
+
### InvokeAwaiter (`clients/proxy.ts`)
|
|
72
|
+
|
|
73
|
+
Chainable proxy that builds invoke keys from property access:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const invoke = proxy<Manifest>();
|
|
77
|
+
|
|
78
|
+
// Equivalent calls:
|
|
79
|
+
invoke["site/loaders/productList.ts"](props);
|
|
80
|
+
invoke.site.loaders["productList.ts"](props);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Uses JavaScript `Proxy` to build the key string from nested property access.
|
|
84
|
+
|
|
85
|
+
### Stream Reading
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { readFromStream } from "@deco/deco/web";
|
|
89
|
+
|
|
90
|
+
const response = await invoke.streaming["site/loaders/feed.ts"]({});
|
|
91
|
+
for await (const chunk of readFromStream(response)) {
|
|
92
|
+
console.log(chunk);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### FormData Utils (`clients/formdata.ts`)
|
|
97
|
+
|
|
98
|
+
Converts between objects and FormData:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const formData = propsToFormData({ name: "John", address: { city: "SP" } });
|
|
102
|
+
// name=John, address.city=SP
|
|
103
|
+
|
|
104
|
+
const props = formDataToProps(formData);
|
|
105
|
+
// { name: "John", address: { city: "SP" } }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Usage in Sites
|
|
109
|
+
|
|
110
|
+
### runtime.ts Pattern
|
|
111
|
+
|
|
112
|
+
Every Deco site has a `runtime.ts`:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { proxy } from "@deco/deco/web";
|
|
116
|
+
import type { Manifest } from "./manifest.gen.ts";
|
|
117
|
+
import type { Manifest as VTEXManifest } from "apps/vtex/manifest.gen.ts";
|
|
118
|
+
|
|
119
|
+
export const invoke = proxy<Manifest & VTEXManifest>();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Merges site + VTEX manifests for typed invocation.
|
|
123
|
+
|
|
124
|
+
### Client Usage
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { invoke } from "../runtime.ts";
|
|
128
|
+
|
|
129
|
+
const products = await invoke.vtex.loaders.intelligentSearch.productListingPage({
|
|
130
|
+
query: "shoes",
|
|
131
|
+
page: 0,
|
|
132
|
+
count: 12,
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The invoke proxy builds the key from the property chain, POSTs to `/live/invoke/{key}`, and returns parsed JSON.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Runtime (`runtime/`)
|
|
2
|
+
|
|
3
|
+
The runtime handles HTTP requests, routing, rendering, and the invoke API. Built on Hono with Fresh or HTMX rendering.
|
|
4
|
+
|
|
5
|
+
## Entry Point: Deco class (`runtime/mod.ts`)
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
class Deco {
|
|
9
|
+
constructor(options: DecoOptions);
|
|
10
|
+
handler(): Deno.ServeHandler;
|
|
11
|
+
render(params): Promise<string>;
|
|
12
|
+
meta(): { schema, manifest };
|
|
13
|
+
styles(): string;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Request Flow (`runtime/handler.tsx`)
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Hono App
|
|
22
|
+
|-- Middleware: bindings (sets RENDER_FN, GLOBALS)
|
|
23
|
+
|-- Middleware: liveness (/deco/_liveness probes)
|
|
24
|
+
|-- Middleware: statebuilder (prepareState, debug, echo)
|
|
25
|
+
|-- Middleware: observability (OpenTelemetry trace/span)
|
|
26
|
+
|-- Middleware: main (CORS, Cache-Control, segment)
|
|
27
|
+
|
|
|
28
|
+
|-- GET /styles.css -> tailwind CSS
|
|
29
|
+
|-- GET /live/_meta -> schema + manifest
|
|
30
|
+
|-- GET /deco/meta -> alias for _meta
|
|
31
|
+
|-- ALL /live/release -> release info
|
|
32
|
+
|-- ALL /.decofile/reload -> trigger state reload
|
|
33
|
+
|-- GET /live/inspect/:block -> block inspector
|
|
34
|
+
|-- POST /live/invoke -> batch invoke
|
|
35
|
+
|-- ALL /live/invoke/* -> single invoke by key
|
|
36
|
+
|-- ALL /live/previews -> preview index
|
|
37
|
+
|-- ALL /live/previews/* -> block preview
|
|
38
|
+
|-- POST /deco/render -> partial section render
|
|
39
|
+
|-- ALL /live/workflows/run -> trigger workflow
|
|
40
|
+
|-- ALL * (catch-all) -> page handler (entrypoint)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Middleware Chain
|
|
44
|
+
|
|
45
|
+
### 1. Bindings
|
|
46
|
+
Sets `RENDER_FN` (framework render function) and `GLOBALS` (error handler, dev flag).
|
|
47
|
+
|
|
48
|
+
### 2. Liveness (`runtime/middlewares/liveness.ts`)
|
|
49
|
+
Health probe at `/deco/_liveness` returning memory, uptime, requestCount, requestInflight.
|
|
50
|
+
|
|
51
|
+
### 3. State Builder
|
|
52
|
+
Creates resolve/invoke functions scoped to the request. Sets `state.deco` with DecoRuntimeState.
|
|
53
|
+
|
|
54
|
+
### 4. Observability
|
|
55
|
+
OpenTelemetry spans per request with `http.method`, `http.url`, `http.status_code`, `deco.site` attributes.
|
|
56
|
+
|
|
57
|
+
### 5. Main
|
|
58
|
+
CORS headers, Cache-Control, segment cookie handling, custom response headers from flags.
|
|
59
|
+
|
|
60
|
+
## Key Routes
|
|
61
|
+
|
|
62
|
+
### Invoke (`/live/invoke/*`)
|
|
63
|
+
Single-key invocation: `POST /live/invoke/site/loaders/productList.ts` with JSON body.
|
|
64
|
+
|
|
65
|
+
### Batch Invoke (`/live/invoke`)
|
|
66
|
+
Multiple invocations in one request with a multi-key JSON body.
|
|
67
|
+
|
|
68
|
+
### Render (`/deco/render`)
|
|
69
|
+
Partial section rendering used by `useSection` hook. Receives `props`, `href`, `pathTemplate`, `resolveChain`.
|
|
70
|
+
|
|
71
|
+
### Entrypoint (`*` catch-all)
|
|
72
|
+
1. Resolves `./routes/[...catchall].tsx` via `state.resolve`
|
|
73
|
+
2. Handler processes request
|
|
74
|
+
3. Calls `ctx.render(PageData)` for HTML output
|
|
75
|
+
|
|
76
|
+
## Rendering
|
|
77
|
+
|
|
78
|
+
### PageData
|
|
79
|
+
Contains `page` (resolved sections), `routerInfo` (URL, params), `loadingMode`.
|
|
80
|
+
|
|
81
|
+
### Section Rendering
|
|
82
|
+
Each section gets: SectionContext, ErrorBoundary, `withSection` HOC, framework-specific partial support.
|
|
83
|
+
|
|
84
|
+
## Frameworks
|
|
85
|
+
|
|
86
|
+
| Framework | Islands | Partials | Usage |
|
|
87
|
+
|-----------|---------|----------|-------|
|
|
88
|
+
| Fresh | Preact islands | `<Partial>` + `f-partial` | Standard Deco sites |
|
|
89
|
+
| HTMX | None (no JS) | `hx-get/hx-swap` | Lightweight alternative |
|
|
90
|
+
|
|
91
|
+
### Fresh (`runtime/fresh/`)
|
|
92
|
+
- `plugin.ts` / `plugin.tsx` for Fresh plugin registration
|
|
93
|
+
- `Bindings.tsx` provides `<Partial>`, `<Head>`
|
|
94
|
+
- `islands/DispatchAsyncRender.tsx` for lazy section loading via IntersectionObserver
|
|
95
|
+
|
|
96
|
+
### HTMX (`runtime/htmx/`)
|
|
97
|
+
- `Renderer.tsx` uses `renderToString(Preact)` for full server rendering
|
|
98
|
+
- No islands, no client-side hydration
|
|
99
|
+
- Uses `hx-get`, `hx-trigger`, `hx-target`, `hx-swap`
|
|
100
|
+
|
|
101
|
+
## Caching (`runtime/caches/`)
|
|
102
|
+
|
|
103
|
+
| Cache | Purpose |
|
|
104
|
+
|-------|---------|
|
|
105
|
+
| `lru.ts` | In-memory LRU (weak references) |
|
|
106
|
+
| `redis.ts` | Redis-backed cache |
|
|
107
|
+
| `tiered.ts` | LRU then Redis then origin |
|
|
108
|
+
| `fileSystem.ts` | Disk-based cache |
|
|
109
|
+
|
|
110
|
+
## Fetch Instrumentation (`runtime/fetch/`)
|
|
111
|
+
|
|
112
|
+
| File | Purpose |
|
|
113
|
+
|------|---------|
|
|
114
|
+
| `mod.ts` | Main fetch wrapper with logging + caching |
|
|
115
|
+
| `fetchLog.ts` | Logs fetch calls to OpenTelemetry |
|
|
116
|
+
| `fetchCache.ts` | HTTP cache (respects Cache-Control) |
|