@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,267 @@
1
+ # Analysis Scripts
2
+
3
+ Ready-to-use Python scripts for discovering optimal scaling parameters via Prometheus.
4
+
5
+ ## Prerequisites
6
+
7
+ ```bash
8
+ # Port-forward Prometheus
9
+ PROM_POD=$(kubectl get pods -n monitoring -l app.kubernetes.io/name=prometheus -o jsonpath='{.items[0].metadata.name}')
10
+ kubectl port-forward -n monitoring $PROM_POD 19090:9090 &
11
+ ```
12
+
13
+ ## Script 1: Full Scaling Analysis
14
+
15
+ Collects all relevant metrics and produces a CPU vs latency correlation table.
16
+
17
+ **Usage:** Change `SITENAME` and run. Uses 12h of data by default.
18
+
19
+ ```python
20
+ #!/usr/bin/env python3
21
+ """Discover optimal scaling parameters for a Deco site."""
22
+ import json, urllib.request, urllib.parse, time
23
+ from datetime import datetime
24
+
25
+ PROM = "http://127.0.0.1:19090"
26
+ SITENAME = "CHANGE_ME" # <-- Set this
27
+ NS = f"sites-{SITENAME}"
28
+ HOURS = 12
29
+ END = int(time.time())
30
+ START = END - HOURS * 3600
31
+
32
+ def prom_qr(query, step=120):
33
+ params = urllib.parse.urlencode({"query": query, "start": START, "end": END, "step": step})
34
+ with urllib.request.urlopen(f"{PROM}/api/v1/query_range?{params}") as r:
35
+ return json.loads(r.read())
36
+
37
+ def prom_q(query):
38
+ params = urllib.parse.urlencode({"query": query})
39
+ with urllib.request.urlopen(f"{PROM}/api/v1/query?{params}") as r:
40
+ return json.loads(r.read())
41
+
42
+ def to_map(data):
43
+ m = {}
44
+ for r in data['data']['result']:
45
+ for ts, v in r['values']:
46
+ try:
47
+ f = float(v)
48
+ if str(v) not in ('NaN', '+Inf', '-Inf'):
49
+ m[int(float(ts))] = f
50
+ except: pass
51
+ return m
52
+
53
+ # --- Current config ---
54
+ print("=" * 70)
55
+ print(f"SCALING ANALYSIS: {SITENAME} (last {HOURS}h)")
56
+ print("=" * 70)
57
+
58
+ target = prom_q(f'autoscaler_target_concurrency_per_pod{{namespace_name="{NS}"}}')
59
+ for r in target['data']['result']:
60
+ print(f" Current target: {r['value'][1]} conc/pod")
61
+
62
+ # --- Collect metrics ---
63
+ cpu = to_map(prom_qr(f'avg(rate(container_cpu_usage_seconds_total{{namespace="{NS}", container="app"}}[2m]))', 120))
64
+ # Fallback: try container="user-container" if "app" yields nothing
65
+ if not cpu:
66
+ cpu = to_map(prom_qr(f'avg(rate(container_cpu_usage_seconds_total{{namespace="{NS}", container!="", container!="POD", container!="queue-proxy"}}[2m]))', 120))
67
+
68
+ pods_m = to_map(prom_qr(f'autoscaler_actual_pods{{namespace_name="{NS}"}}', 120))
69
+ conc_m = to_map(prom_qr(f'autoscaler_stable_request_concurrency{{namespace_name="{NS}"}}', 120))
70
+ panic_m = to_map(prom_qr(f'autoscaler_panic_mode{{namespace_name="{NS}"}}', 120))
71
+
72
+ # Try direct latency (requires queue-proxy PodMonitor)
73
+ lat_m = to_map(prom_qr(
74
+ f'avg(rate(revision_app_request_latencies_sum{{namespace="{NS}"}}[2m]) / rate(revision_app_request_latencies_count{{namespace="{NS}"}}[2m]))', 120
75
+ ))
76
+ has_latency = bool(lat_m)
77
+ if has_latency:
78
+ print(" ✓ Direct latency metrics available (queue-proxy)")
79
+ else:
80
+ print(" ✗ No direct latency metrics — using concurrency as proxy")
81
+
82
+ # --- Build data points ---
83
+ common = sorted(set(cpu.keys()) & set(pods_m.keys()) & set(conc_m.keys()))
84
+ points = []
85
+ for ts in common:
86
+ c = cpu[ts]; p = pods_m[ts]; tc = conc_m[ts]
87
+ if c > 0 and p > 0:
88
+ points.append({
89
+ 'ts': ts, 'cpu_m': c * 1000, 'pods': p,
90
+ 'total_conc': tc, 'cpod': tc / p,
91
+ 'panic': panic_m.get(ts, 0),
92
+ 'lat_ms': lat_m.get(ts),
93
+ })
94
+
95
+ if not points:
96
+ print(" ERROR: No data points. Check namespace and metrics availability.")
97
+ exit(1)
98
+
99
+ print(f" Data points: {len(points)}")
100
+
101
+ # --- Panic events ---
102
+ panic_count = sum(1 for i in range(1, len(points)) if points[i]['panic'] == 1 and points[i-1]['panic'] == 0)
103
+ print(f" Panic events: {panic_count}")
104
+
105
+ # --- Pod count stats ---
106
+ pod_vals = [p['pods'] for p in points]
107
+ print(f" Pods: avg={sum(pod_vals)/len(pod_vals):.0f}, min={min(pod_vals):.0f}, max={max(pod_vals):.0f}")
108
+
109
+ # --- CPU per pod bucketed analysis ---
110
+ print(f"\n{'CPU range':>12} {'Avg C/pod':>10} {'P95 C/pod':>10} {'Avg Pods':>9} {'Points':>7}", end="")
111
+ if has_latency:
112
+ print(f" {'Avg Lat':>9} {'P95 Lat':>9}", end="")
113
+ print()
114
+
115
+ cpu_buckets = [
116
+ (0, 200, '0-200m'), (200, 300, '200-300m'), (300, 400, '300-400m'),
117
+ (400, 500, '400-500m'), (500, 600, '500-600m'), (600, 700, '600-700m'),
118
+ (700, 1500, '700m+'),
119
+ ]
120
+
121
+ for lo, hi, label in cpu_buckets:
122
+ bp = [p for p in points if lo <= p['cpu_m'] < hi]
123
+ if bp:
124
+ concs = sorted([p['cpod'] for p in bp])
125
+ avg_c = sum(concs) / len(concs)
126
+ p95_i = min(int(len(concs) * 0.95), len(concs) - 1)
127
+ avg_pods = sum(p['pods'] for p in bp) / len(bp)
128
+ line = f" {label:>12} {avg_c:10.1f} {concs[p95_i]:10.1f} {avg_pods:9.0f} {len(bp):7}"
129
+ if has_latency:
130
+ lats = sorted([p['lat_ms'] for p in bp if p['lat_ms'] is not None])
131
+ if lats:
132
+ avg_l = sum(lats) / len(lats)
133
+ p95_l = lats[min(int(len(lats) * 0.95), len(lats) - 1)]
134
+ line += f" {avg_l:8.1f}ms {p95_l:8.1f}ms"
135
+ print(line)
136
+
137
+ # --- Latency inflation factor ---
138
+ print(f"\nLatency inflation factor (excess > 1.0 = latency degrading):")
139
+ for threshold in [300, 350, 400, 450, 500, 550, 600]:
140
+ below = [p for p in points if p['cpu_m'] < threshold]
141
+ above = [p for p in points if p['cpu_m'] >= threshold]
142
+ if below and above:
143
+ avg_c_b = sum(p['cpod'] for p in below) / len(below)
144
+ avg_c_a = sum(p['cpod'] for p in above) / len(above)
145
+ avg_p_b = sum(p['pods'] for p in below) / len(below)
146
+ avg_p_a = sum(p['pods'] for p in above) / len(above)
147
+ conc_ratio = avg_c_a / avg_c_b if avg_c_b > 0 else 0
148
+ pods_ratio = avg_p_b / avg_p_a if avg_p_a > 0 else 0
149
+ excess = conc_ratio / pods_ratio if pods_ratio > 0 else 0
150
+ marker = " ★ INFLECTION" if 1.3 < excess < 1.7 else ""
151
+ print(f" CPU <{threshold}m vs >={threshold}m: excess={excess:.2f}x{marker}")
152
+
153
+ # --- Recommendation ---
154
+ print(f"\n{'='*70}")
155
+ print("RECOMMENDATION")
156
+ print(f"{'='*70}")
157
+
158
+ # Find inflection: first threshold where excess drops below 1.5
159
+ inflection = None
160
+ for threshold in [300, 350, 400, 450, 500, 550, 600]:
161
+ below = [p for p in points if p['cpu_m'] < threshold]
162
+ above = [p for p in points if p['cpu_m'] >= threshold]
163
+ if below and above:
164
+ avg_c_b = sum(p['cpod'] for p in below) / len(below)
165
+ avg_c_a = sum(p['cpod'] for p in above) / len(above)
166
+ avg_p_b = sum(p['pods'] for p in below) / len(below)
167
+ avg_p_a = sum(p['pods'] for p in above) / len(above)
168
+ conc_ratio = avg_c_a / avg_c_b if avg_c_b > 0 else 0
169
+ pods_ratio = avg_p_b / avg_p_a if avg_p_a > 0 else 0
170
+ excess = conc_ratio / pods_ratio if pods_ratio > 0 else 0
171
+ if excess < 1.5 and inflection is None:
172
+ inflection = threshold
173
+
174
+ # Get CPU request
175
+ cpu_req = prom_q(f'kube_pod_container_resource_requests{{namespace="{NS}", container!="queue-proxy", resource="cpu"}}')
176
+ cpu_req_m = 0
177
+ for r in cpu_req['data']['result']:
178
+ cpu_req_m = float(r['value'][1]) * 1000
179
+ break
180
+
181
+ if inflection:
182
+ print(f" Latency inflection point: ~{inflection}m CPU")
183
+ print(f" CPU request: {cpu_req_m:.0f}m")
184
+ print(f" Recommended: CPU scaling at target={inflection} (millicores)")
185
+ print(f"\n SiteState scaling config:")
186
+ print(f' .scaling.metric = {{"type": "cpu", "target": {inflection}}}')
187
+ else:
188
+ print(" No clear inflection found. Consider:")
189
+ print(" - More data needed (run for 12-24h)")
190
+ print(" - App may be IO-bound (keep concurrency scaling)")
191
+ if panic_count > 0:
192
+ print(f" - {panic_count} panic events detected — consider switching to CPU scaling anyway to break the loop")
193
+ ```
194
+
195
+ ## Script 2: Post-Change Monitoring
196
+
197
+ Run after applying scaling changes to verify they're working.
198
+
199
+ ```python
200
+ #!/usr/bin/env python3
201
+ """Monitor scaling behavior after parameter change."""
202
+ import json, urllib.request, urllib.parse, time
203
+ from datetime import datetime
204
+
205
+ PROM = "http://127.0.0.1:19090"
206
+ SITENAME = "CHANGE_ME" # <-- Set this
207
+ NS = f"sites-{SITENAME}"
208
+ END = int(time.time())
209
+ START = END - 2 * 3600 # last 2 hours
210
+
211
+ def prom_qr(query, step=60):
212
+ params = urllib.parse.urlencode({"query": query, "start": START, "end": END, "step": step})
213
+ with urllib.request.urlopen(f"{PROM}/api/v1/query_range?{params}") as r:
214
+ return json.loads(r.read())
215
+
216
+ def to_map(data):
217
+ m = {}
218
+ for r in data['data']['result']:
219
+ for ts, v in r['values']:
220
+ try:
221
+ f = float(v)
222
+ if str(v) not in ('NaN', '+Inf', '-Inf'):
223
+ m[int(float(ts))] = f
224
+ except: pass
225
+ return m
226
+
227
+ cpu = to_map(prom_qr(f'avg(rate(container_cpu_usage_seconds_total{{namespace="{NS}", container="app"}}[2m]))'))
228
+ if not cpu:
229
+ cpu = to_map(prom_qr(f'avg(rate(container_cpu_usage_seconds_total{{namespace="{NS}", container!="", container!="POD", container!="queue-proxy"}}[2m]))'))
230
+ pods_m = to_map(prom_qr(f'autoscaler_actual_pods{{namespace_name="{NS}"}}'))
231
+ panic_m = to_map(prom_qr(f'autoscaler_panic_mode{{namespace_name="{NS}"}}'))
232
+
233
+ common = sorted(set(cpu.keys()) & set(pods_m.keys()))
234
+
235
+ print(f"POST-CHANGE MONITORING: {SITENAME} (last 2h)")
236
+ print(f"{'Time':>6} {'Pods':>5} {'CPU/pod':>8} {'Panic':>5}")
237
+ prev_pods = None
238
+ for ts in common:
239
+ p = pods_m[ts]; c = cpu[ts] * 1000; pa = panic_m.get(ts, 0)
240
+ t = datetime.fromtimestamp(ts).strftime('%H:%M')
241
+ ps = "PANIC" if pa == 1 else ""
242
+ change = ""
243
+ if prev_pods and abs(p - prev_pods) >= 2:
244
+ direction = "↑" if p > prev_pods else "↓"
245
+ change = f" {direction}{abs(p-prev_pods):.0f}"
246
+ print(f" {t} {p:5.0f} {c:7.0f}m {ps}{change}")
247
+ prev_pods = p
248
+
249
+ # Summary
250
+ if common:
251
+ pod_vals = [pods_m[ts] for ts in common]
252
+ cpu_vals = [cpu[ts] * 1000 for ts in common]
253
+ panics = sum(1 for i in range(1, len(common)) if panic_m.get(common[i], 0) == 1 and panic_m.get(common[i-1], 0) == 0)
254
+ print(f"\n Pods: avg={sum(pod_vals)/len(pod_vals):.0f}, min={min(pod_vals):.0f}, max={max(pod_vals):.0f}")
255
+ print(f" CPU/pod: avg={sum(cpu_vals)/len(cpu_vals):.0f}m, max={max(cpu_vals):.0f}m")
256
+ print(f" Panic events: {panics}")
257
+ print(f" Pod stability: {'STABLE' if max(pod_vals) - min(pod_vals) < 5 else 'OSCILLATING'}")
258
+
259
+ # Check for HPA (if using CPU scaling)
260
+ print(f"\nHPA status:")
261
+ import subprocess
262
+ try:
263
+ result = subprocess.run(['kubectl', 'get', 'hpa', '-n', NS], capture_output=True, text=True)
264
+ print(result.stdout if result.stdout else " No HPA found (still using KPA?)")
265
+ except:
266
+ print(" Could not check HPA")
267
+ ```
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: deco-start-architecture
3
+ description: Architecture reference for @decocms/start — the Deco framework for TanStack Start/React/Cloudflare Workers. Covers the three-layer architecture (@decocms/start + @decocms/apps + site), admin protocol (meta, decofile, invoke, render), CMS block resolution with generic recursive resolver, dynamic schema registries, section registry, worker entry with edge caching, SDK utilities (useDevice, useScript with minification, observability, health metrics), matchers, middleware, hooks, schema generation, and the comprehensive gap analysis vs deco-cx/deco. Includes a prioritized roadmap (Tier 0-3 complete, plus Tier 2.5 framework improvements). Use when working on deco-start, understanding the framework, adding features, debugging admin protocol issues, or planning what to port next from deco-cx/deco.
4
+ globs:
5
+ - "**/workerEntry.ts"
6
+ - "**/cacheHeaders.ts"
7
+ - "**/LiveControls.tsx"
8
+ - "**/DecoPageRenderer.tsx"
9
+ - "**/resolve.ts"
10
+ - "**/setup.ts"
11
+ - "**/.decofile"
12
+ - "**/meta.gen.json"
13
+ - "**/blocks.gen.ts"
14
+ ---
15
+
16
+ ## Sub-documents
17
+
18
+ | Document | Topic |
19
+ |----------|-------|
20
+ | [admin-protocol.md](./admin-protocol.md) | Admin protocol — meta, decofile, invoke, render, CORS, LiveControls |
21
+ | [cms-resolution.md](./cms-resolution.md) | CMS block loading, page resolution, section registry, matchers |
22
+ | [worker-entry-caching.md](./worker-entry-caching.md) | Cloudflare Worker entry, edge caching, segment keys, cache profiles |
23
+ | [sdk-utilities.md](./sdk-utilities.md) | All SDK utilities — useScript, signal, analytics, cookies, redirects, sitemap |
24
+ | [gap-analysis.md](./gap-analysis.md) | Feature-by-feature comparison with deco-cx/deco + prioritized roadmap |
25
+ | [code-quality.md](./code-quality.md) | Code quality tools, scripts, recommendations |
26
+
27
+ # @decocms/start Architecture
28
+
29
+ Reference for `@decocms/start` — the Deco framework for TanStack Start storefronts on Cloudflare Workers.
30
+
31
+ ## Three-Layer Architecture
32
+
33
+ ```
34
+ Layer 1: @decocms/start (this repo)
35
+ Framework: CMS bridge, admin protocol, worker entry, caching, rendering
36
+ |
37
+ Layer 2: @decocms/apps (apps-start)
38
+ Commerce: VTEX/Shopify loaders, types, hooks, transforms
39
+ |
40
+ Layer 3: Site repo (e.g., espacosmart-storefront)
41
+ UI: components, routes, styles, contexts, sections
42
+ ```
43
+
44
+ ## Repository Structure
45
+
46
+ ```
47
+ deco-start/
48
+ |-- package.json # v0.6.0, exports map, peer deps
49
+ |-- tsconfig.json # ES2022, bundler resolution, strictNullChecks
50
+ |-- .releaserc.json # semantic-release (Angular preset)
51
+ |-- CLAUDE.md # AI guidance document
52
+ |-- GAP_ANALYSIS_V2.md # Detailed gap analysis vs deco-cx/deco
53
+ |
54
+ |-- src/
55
+ | |-- index.ts # Barrel: re-exports admin, cms, hooks, types, middleware
56
+ | |
57
+ | |-- admin/ # Admin protocol handlers (9 files)
58
+ | | |-- setup.ts # Client-safe config (setMetaData, setInvokeLoaders, setInvokeActions, setRenderShell, register*Schema)
59
+ | | |-- meta.ts # GET /live/_meta handler (auto-invalidates on decofile change)
60
+ | | |-- schema.ts # composeMeta(), dynamic schema registries (loaders + matchers)
61
+ | | |-- decofile.ts # GET/POST /.decofile handlers (revision tracking, cache invalidation)
62
+ | | |-- invoke.ts # POST /deco/invoke handler (form-data, select, actions, batch, nested resolve)
63
+ | | |-- render.ts # POST /live/previews/* handler
64
+ | | |-- liveControls.ts # Admin-storefront bridge script
65
+ | | |-- cors.ts # CORS for admin origins
66
+ | | |-- index.ts # Barrel export
67
+ | |
68
+ | |-- cms/ # CMS block resolution (4 files)
69
+ | | |-- loader.ts # loadBlocks, findPageByPath, getAllPages, withBlocksOverride, getRevision, onChange
70
+ | | |-- registry.ts # registerSection, getSection, getSectionRegistry
71
+ | | |-- resolve.ts # resolveValue, resolveDecoPage, internalResolve, registerCommerceLoader, registerMatcher, addSkipResolveType, set*Handler
72
+ | | |-- index.ts # Barrel export
73
+ | |
74
+ | |-- hooks/ # React components/hooks (5 files)
75
+ | | |-- LiveControls.tsx # Admin bridge component
76
+ | | |-- DecoPageRenderer.tsx # Renders sections with Suspense
77
+ | | |-- LazySection.tsx # IntersectionObserver lazy loading
78
+ | | |-- SectionErrorFallback.tsx # Per-section error boundary
79
+ | | |-- index.ts
80
+ | |
81
+ | |-- middleware/ # Request middleware (5 files)
82
+ | | |-- observability.ts # configureTracer, configureMeter, withTracing, logRequest, MetricNames, recordRequestMetric, recordCacheMetric
83
+ | | |-- healthMetrics.ts # trackRequest, getHealthMetrics, handleHealthCheck (/deco/_health)
84
+ | | |-- liveness.ts # /deco/_liveness health probe (integrated with /deco/_health)
85
+ | | |-- decoState.ts # buildDecoState per request
86
+ | | |-- index.ts
87
+ | |
88
+ | |-- sdk/ # SDK utilities (21 files)
89
+ | | |-- workerEntry.ts # createDecoWorkerEntry (CF Worker wrapper)
90
+ | | |-- cacheHeaders.ts # detectCacheProfile, routeCacheDefaults
91
+ | | |-- cachedLoader.ts # In-memory SWR loader cache
92
+ | | |-- mergeCacheControl.ts # Cache-Control merge
93
+ | | |-- analytics.ts # useSendEvent, ANALYTICS_SCRIPT, gtmScript
94
+ | | |-- useScript.ts # useScript (with minification + LRU cache), usePartialSection, useSection
95
+ | | |-- useDevice.ts # Server-side device detection (detectDevice, useDevice, checkMobile/Tablet/Desktop)
96
+ | | |-- signal.ts # Reactive signal (replaces @preact/signals)
97
+ | | |-- clx.ts # CSS class utility
98
+ | | |-- cookie.ts # Cookie get/set/delete (client + server)
99
+ | | |-- invoke.ts # createInvokeProxy, batchInvoke, invokeQueryOptions
100
+ | | |-- redirects.ts # CMS redirect loading + matching
101
+ | | |-- sitemap.ts # Sitemap XML generation
102
+ | | |-- csp.ts # CSP frame-ancestors for admin
103
+ | | |-- urlUtils.ts # UTM stripping, canonical URLs
104
+ | | |-- requestContext.ts # AsyncLocalStorage per-request context
105
+ | | |-- serverTimings.ts # Server-Timing header
106
+ | | |-- instrumentedFetch.ts # Fetch with logging/tracing
107
+ | | |-- useId.ts # React useId wrapper
108
+ | | |-- wrapCaughtErrors.ts # Deferred error proxy for resilient rendering
109
+ | | |-- index.ts
110
+ | |
111
+ | |-- matchers/ # Feature flag matchers (2 files)
112
+ | | |-- builtins.ts # cookie, cron, host, pathname, queryString
113
+ | | |-- posthog.ts # PostHog integration
114
+ | |
115
+ | |-- types/ # Type definitions (2 files)
116
+ | | |-- index.ts # FnContext, App, Section, SectionProps, Flag, etc.
117
+ | | |-- widgets.ts # ImageWidget, HTMLWidget, VideoWidget aliases
118
+ |
119
+ |-- scripts/
120
+ | |-- generate-blocks.ts # .deco/blocks/*.json -> blocks.gen.ts
121
+ | |-- generate-schema.ts # TypeScript props -> JSON Schema (meta.gen.json)
122
+ ```
123
+
124
+ ## Package Exports
125
+
126
+ | Import Path | File | Purpose |
127
+ |-------------|------|---------|
128
+ | `@decocms/start` | `src/index.ts` | Main barrel |
129
+ | `@decocms/start/admin` | `src/admin/index.ts` | Admin protocol |
130
+ | `@decocms/start/cms` | `src/cms/index.ts` | Block resolution |
131
+ | `@decocms/start/hooks` | `src/hooks/index.ts` | React components |
132
+ | `@decocms/start/middleware` | `src/middleware/index.ts` | Request middleware |
133
+ | `@decocms/start/sdk` | `src/sdk/index.ts` | SDK utilities |
134
+ | `@decocms/start/sdk/workerEntry` | `src/sdk/workerEntry.ts` | CF Worker entry |
135
+ | `@decocms/start/sdk/cacheHeaders` | `src/sdk/cacheHeaders.ts` | Cache profiles |
136
+ | `@decocms/start/sdk/invoke` | `src/sdk/invoke.ts` | Invoke proxy |
137
+ | `@decocms/start/types` | `src/types/index.ts` | Type definitions |
138
+ | `@decocms/start/types/widgets` | `src/types/widgets.ts` | Widget type aliases |
139
+ | `@decocms/start/sdk/useDevice` | `src/sdk/useDevice.ts` | Server-side device detection |
140
+ | `@decocms/start/middleware/healthMetrics` | `src/middleware/healthMetrics.ts` | Health metrics + `/deco/_health` |
141
+ | `@decocms/start/matchers/builtins` | `src/matchers/builtins.ts` | Built-in matchers |
142
+
143
+ ## Key Concepts
144
+
145
+ ### 1. Worker Entry (Edge Layer)
146
+
147
+ ```
148
+ Request -> createDecoWorkerEntry(serverEntry, options)
149
+ |-- tryAdminRoute() <- /live/_meta, /.decofile, /live/previews/*
150
+ |-- cache purge check <- __deco_purge_cache
151
+ |-- static asset bypass <- /assets/*, favicon, sprites
152
+ |-- Cloudflare edge cache <- caches.open() with profile-based TTLs
153
+ |-- serverEntry.fetch() <- TanStack Start handles the rest
154
+ ```
155
+
156
+ ### 2. Admin Protocol
157
+
158
+ | Route | Method | Handler | Purpose |
159
+ |-------|--------|---------|---------|
160
+ | `/live/_meta` | GET | `handleMeta` | JSON Schema + manifest |
161
+ | `/.decofile` | GET | `handleDecofileRead` | CMS content blocks |
162
+ | `/.decofile` | POST | `handleDecofileReload` | Hot reload blocks |
163
+ | `/deco/invoke` | POST | `handleInvoke` | Execute loaders/actions |
164
+ | `/live/previews/*` | POST | `handleRender` | Section preview in admin |
165
+ | `/deco/_liveness` | GET | `handleLiveness` | Health probe |
166
+ | `/deco/_health` | GET | `handleHealthCheck` | Detailed health metrics (uptime, memory, cache, requests) |
167
+
168
+ ### 3. CMS Resolution
169
+
170
+ ```
171
+ [CMS decofile] [setup.ts] [resolveDecoPage]
172
+ Section props with Commerce loaders Generic recursive resolver:
173
+ __resolveType: "vtex/..." --> registered by key --> 1. Check commerce loaders
174
+ + matchers 2. Check decofile blocks
175
+ + schema registries 3. DanglingReference fallback
176
+ + memoization + depth protection
177
+ |
178
+ v
179
+ [React Component]
180
+ Receives plain data
181
+ ```
182
+
183
+ ### 4. Section Rendering
184
+
185
+ ```tsx
186
+ <DecoPageRenderer sections={resolvedSections}>
187
+ {sections.map((section, i) => (
188
+ <SectionErrorBoundary key={i}>
189
+ <Suspense fallback={<div />}>
190
+ {isBelowFold(i) ? (
191
+ <LazySection><SectionComponent {...section.props} /></LazySection>
192
+ ) : (
193
+ <SectionComponent {...section.props} />
194
+ )}
195
+ </Suspense>
196
+ </SectionErrorBoundary>
197
+ ))}
198
+ </DecoPageRenderer>
199
+ ```
200
+
201
+ ## Dependencies
202
+
203
+ - **Peer**: `@tanstack/store` >= 0.7.0, `react` ^19, `react-dom` ^19
204
+ - **Dev**: `ts-morph` (schema gen), `typescript` ^5.9
205
+
206
+ ## Implementation Status
207
+
208
+ Tier 0 (production-blocking), Tier 1 (quality), Tier 2 (DX/completeness), and Tier 2.5 (framework improvements) are ALL DONE. See [gap-analysis.md](./gap-analysis.md) for details on Tier 3 items remaining.
209
+
210
+ ### Tier 2.5 Highlights (PR #3: feat/framework-improvements)
211
+ - Dynamic schema registries (loaders + matchers) — replaces hardcoded `KNOWN_LOADERS`
212
+ - Generic recursive resolver with memoization, depth protection, DanglingReference handler
213
+ - `useDevice` server-side (User-Agent + RequestContext)
214
+ - `/deco/_health` endpoint with uptime, memory, cache stats, request metrics
215
+ - Enhanced observability: `MeterAdapter`, `MetricNames`, context propagation
216
+ - Enhanced invoke: FormData/URLEncoded parsing, `?select=`, actions, nested `__resolveType`
217
+ - Decofile revision tracking + `onChange` listeners + meta auto-invalidation
218
+ - `useScript` minification + LRU cache
@@ -0,0 +1,156 @@
1
+ # Admin Protocol
2
+
3
+ The admin panel (`admin.deco.cx`) communicates with self-hosted storefronts via HTTP endpoints handled in the Cloudflare Worker entry, NOT inside TanStack Start's server.
4
+
5
+ Important: Admin routes MUST be handled in `workerEntry.ts`, NOT inside `createServerEntry` - Vite strips custom fetch logic from server entries in production builds.
6
+
7
+ ## Endpoints
8
+
9
+ ### GET /live/_meta (`admin/meta.ts`)
10
+
11
+ Returns JSON Schema + manifest for the admin form builder.
12
+
13
+ ```typescript
14
+ handleMeta(request: Request): Response
15
+ ```
16
+
17
+ - Schema is composed at runtime via `composeMeta()`
18
+ - Content-hash ETag (DJB2) for caching
19
+ - ETag included in JSON body for admin cache busting
20
+ - Returns sections, pages, loaders, matchers, flags definitions
21
+ - Auto-invalidates cached ETag when decofile changes (via `onChange` listener)
22
+
23
+ ### GET /.decofile (`admin/decofile.ts`)
24
+
25
+ Returns the current CMS content blocks.
26
+
27
+ ```typescript
28
+ handleDecofileRead(request: Request): Response
29
+ ```
30
+
31
+ - Includes content-hash `revision` in JSON body
32
+ - Sets `ETag` header from current revision for HTTP caching
33
+
34
+ ### POST /.decofile (`admin/decofile.ts`)
35
+
36
+ Hot-reloads CMS blocks without redeployment.
37
+
38
+ ```typescript
39
+ handleDecofileReload(request: Request): Response
40
+ ```
41
+
42
+ - Calls `setBlocks(newBlocks)` which updates in-memory state, recomputes revision, and notifies `onChange` listeners
43
+ - Explicitly calls `clearLoaderCache()` to ensure data consistency after reload
44
+
45
+ ### POST /deco/invoke (`admin/invoke.ts`)
46
+
47
+ Executes loaders/actions by key.
48
+
49
+ ```typescript
50
+ handleInvoke(request: Request): Response
51
+ ```
52
+
53
+ - Loaders registered via `setInvokeLoaders(map)`
54
+ - Actions registered via `setInvokeActions(map)`
55
+ - Supports multiple body formats: `application/json`, `multipart/form-data`, `application/x-www-form-urlencoded`, URL search params (`?props=...`)
56
+ - `?select=field1,field2` query parameter for partial response filtering
57
+ - Batch invoke: send array of `{ key, props }` to execute multiple in parallel
58
+ - Nested `__resolveType` within payload props are recursively resolved
59
+ - Development mode includes stack traces in error responses
60
+
61
+ ### POST /live/previews/* (`admin/render.ts`)
62
+
63
+ Renders a section for preview in the admin iframe.
64
+
65
+ ```typescript
66
+ handleRender(request: Request): Response
67
+ ```
68
+
69
+ - Uses `setRenderShell(config)` to wrap preview in HTML shell
70
+ - Shell must include `data-theme="light"` for DaisyUI v4 color variables
71
+ - Renders section component with provided props
72
+
73
+ ## Setup (`admin/setup.ts`)
74
+
75
+ Client-safe configuration (no `node:` imports or AsyncLocalStorage):
76
+
77
+ ```typescript
78
+ // Called in site's setup.ts
79
+ setMetaData(metaJson); // Set schema data from meta.gen.json
80
+ setInvokeLoaders(loaderMap); // Register loaders for /deco/invoke
81
+ setInvokeActions(actionMap); // Register actions for /deco/invoke
82
+ setRenderShell(shellConfig); // Configure preview HTML wrapper
83
+ registerLoaderSchema(key, schema); // Register a single loader schema dynamically
84
+ registerLoaderSchemas(schemas); // Register multiple loader schemas at once
85
+ registerMatcherSchema(key, schema); // Register a single matcher schema dynamically
86
+ registerMatcherSchemas(schemas); // Register multiple matcher schemas at once
87
+ ```
88
+
89
+ ## Schema Composition (`admin/schema.ts`)
90
+
91
+ `composeMeta()` injects framework-level schemas at runtime:
92
+
93
+ ```
94
+ [generate-schema.ts] [setup.ts] [composeMeta()]
95
+ Scans src/sections/ --> Imports meta.gen.json --> Injects page schema,
96
+ Produces section-only Calls setMetaData() merges definitions,
97
+ meta.gen.json populates pages root
98
+ ```
99
+
100
+ Key: `toBase64()` must produce padded output matching `btoa()` - admin uses `btoa()` for definition refs.
101
+
102
+ ### Dynamic Schema Registries
103
+
104
+ Loader and matcher schemas are now managed via runtime registries instead of hardcoded lists:
105
+
106
+ ```typescript
107
+ // Loaders — runtime registry replaces old KNOWN_LOADERS array
108
+ registerLoaderSchema("vtex/loaders/productList.ts", {
109
+ inputSchema: { ... },
110
+ outputSchema: { ... },
111
+ tags: ["product-list"], // used by wrapResolvableProperties to filter product-list loaders
112
+ });
113
+
114
+ // Matchers — same pattern
115
+ registerMatcherSchema("website/matchers/device.ts", {
116
+ inputSchema: { ... },
117
+ });
118
+ ```
119
+
120
+ `buildLoaderDefinitions()` and `buildMatcherDefinitions()` now read from these registries at composition time. The `getProductListLoaderKeys()` function dynamically filters loaders tagged with `product-list`.
121
+
122
+ ## CORS (`admin/cors.ts`)
123
+
124
+ ```typescript
125
+ isAdminOrLocalhost(origin: string): boolean // Check if origin is admin
126
+ corsHeaders(origin: string): Headers // CORS headers for admin
127
+ ```
128
+
129
+ Allows: `admin.deco.cx`, `localhost:*`.
130
+
131
+ ## LiveControls (`admin/liveControls.ts`)
132
+
133
+ Inline script injected into every page when in admin context:
134
+
135
+ ```typescript
136
+ LIVE_CONTROLS_SCRIPT: string // postMessage bridge script
137
+ ```
138
+
139
+ Provides:
140
+ - `__DECO_STATE` global with decofile state
141
+ - `postMessage` bridge to admin iframe
142
+ - Section selection/highlighting
143
+ - Environment info (site, deploymentId)
144
+
145
+ ## LiveControls Component (`hooks/LiveControls.tsx`)
146
+
147
+ React component that renders the bridge script:
148
+
149
+ ```tsx
150
+ <LiveControls
151
+ site={site}
152
+ siteId={siteId}
153
+ />
154
+ ```
155
+
156
+ Injected in the site's `__root.tsx`.