@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,279 @@
1
+ #!/usr/bin/env -S deno run -A
2
+ /**
3
+ * Baseline Management Script
4
+ * ==========================
5
+ * Save current test results as baseline and compare against it.
6
+ *
7
+ * Usage:
8
+ * deno run -A scripts/baseline.ts save [name]
9
+ * deno run -A scripts/baseline.ts compare [name]
10
+ * deno run -A scripts/baseline.ts list
11
+ * deno run -A scripts/baseline.ts delete <name>
12
+ */
13
+
14
+ import { ensureDirSync, existsSync } from "https://deno.land/std@0.224.0/fs/mod.ts";
15
+ import { join, dirname, fromFileUrl } from "https://deno.land/std@0.224.0/path/mod.ts";
16
+
17
+ interface PerformanceMetrics {
18
+ TTFB: number | null
19
+ FCP: number | null
20
+ LCP: number | null
21
+ CLS: number | null
22
+ }
23
+
24
+ interface PageMetrics {
25
+ pageName: string
26
+ performance: PerformanceMetrics
27
+ network: { totalRequests: number; totalBytes: number }
28
+ }
29
+
30
+ interface Report {
31
+ metrics: PageMetrics[]
32
+ timestamp: string
33
+ savedAt?: string
34
+ name?: string
35
+ }
36
+
37
+ interface ComparisonResult {
38
+ pageName: string
39
+ metric: string
40
+ baseline: number
41
+ current: number
42
+ diff: number
43
+ diffPercent: number
44
+ status: 'improved' | 'regressed' | 'unchanged'
45
+ }
46
+
47
+ const SCRIPT_DIR = dirname(fromFileUrl(import.meta.url))
48
+ const REPORTS_DIR = join(SCRIPT_DIR, '..', 'reports')
49
+ const BASELINES_DIR = join(REPORTS_DIR, 'baselines')
50
+ const LATEST_REPORT = join(REPORTS_DIR, 'report-latest.json')
51
+ const DEFAULT_BASELINE = join(BASELINES_DIR, 'baseline.json')
52
+
53
+ // Threshold percentages for regression detection
54
+ const THRESHOLDS: Record<string, number> = {
55
+ TTFB: 10, // 10% slower = regression
56
+ FCP: 10,
57
+ LCP: 15,
58
+ CLS: 50, // CLS is more variable
59
+ }
60
+
61
+ function loadReport(filepath: string): Report | null {
62
+ try {
63
+ const content = Deno.readTextFileSync(filepath)
64
+ return JSON.parse(content)
65
+ } catch {
66
+ return null
67
+ }
68
+ }
69
+
70
+ function saveBaseline(name?: string): void {
71
+ ensureDirSync(BASELINES_DIR)
72
+
73
+ const latest = loadReport(LATEST_REPORT)
74
+ if (!latest) {
75
+ console.error('❌ No report found at', LATEST_REPORT)
76
+ console.error(' Run the e2e tests first: deno task test:e2e')
77
+ Deno.exit(1)
78
+ }
79
+
80
+ const targetPath = name
81
+ ? join(BASELINES_DIR, `baseline-${name}.json`)
82
+ : DEFAULT_BASELINE
83
+
84
+ const baseline: Report = {
85
+ ...latest,
86
+ savedAt: new Date().toISOString(),
87
+ name: name || 'default',
88
+ }
89
+
90
+ Deno.writeTextFileSync(targetPath, JSON.stringify(baseline, null, 2))
91
+ console.log(`✅ Baseline saved: ${targetPath}`)
92
+ console.log(` Timestamp: ${latest.timestamp}`)
93
+ console.log(` Pages: ${latest.metrics.map(m => m.pageName).join(', ')}`)
94
+ }
95
+
96
+ function listBaselines(): void {
97
+ ensureDirSync(BASELINES_DIR)
98
+
99
+ let files: string[] = []
100
+ try {
101
+ files = [...Deno.readDirSync(BASELINES_DIR)]
102
+ .filter(f => f.isFile && f.name.endsWith('.json'))
103
+ .map(f => f.name)
104
+ } catch {
105
+ // Directory might not exist yet
106
+ }
107
+
108
+ if (files.length === 0) {
109
+ console.log('📋 No baselines saved yet')
110
+ console.log(' Save one with: deno task test:e2e:baseline:save')
111
+ return
112
+ }
113
+
114
+ console.log('📋 Saved baselines:\n')
115
+ for (const file of files) {
116
+ const filepath = join(BASELINES_DIR, file)
117
+ const baseline = loadReport(filepath)
118
+ if (baseline) {
119
+ const name = file.replace('baseline-', '').replace('.json', '')
120
+ console.log(` ${name}`)
121
+ console.log(` Saved: ${baseline.savedAt || baseline.timestamp}`)
122
+ console.log(` Pages: ${baseline.metrics.length}`)
123
+ console.log('')
124
+ }
125
+ }
126
+ }
127
+
128
+ function deleteBaseline(name: string): void {
129
+ const targetPath = name === 'default'
130
+ ? DEFAULT_BASELINE
131
+ : join(BASELINES_DIR, `baseline-${name}.json`)
132
+
133
+ if (!existsSync(targetPath)) {
134
+ console.error(`❌ Baseline not found: ${name}`)
135
+ Deno.exit(1)
136
+ }
137
+
138
+ Deno.removeSync(targetPath)
139
+ console.log(`✅ Deleted baseline: ${name}`)
140
+ }
141
+
142
+ function compare(baselineName?: string): void {
143
+ const baselinePath = baselineName
144
+ ? join(BASELINES_DIR, `baseline-${baselineName}.json`)
145
+ : DEFAULT_BASELINE
146
+
147
+ const baseline = loadReport(baselinePath)
148
+ if (!baseline) {
149
+ console.error('❌ No baseline found at', baselinePath)
150
+ console.error(' Save a baseline first: deno task test:e2e:baseline:save')
151
+ Deno.exit(1)
152
+ }
153
+
154
+ const current = loadReport(LATEST_REPORT)
155
+ if (!current) {
156
+ console.error('❌ No current report found at', LATEST_REPORT)
157
+ console.error(' Run the e2e tests first: deno task test:e2e')
158
+ Deno.exit(1)
159
+ }
160
+
161
+ console.log('📊 Performance Comparison\n')
162
+ console.log(` Baseline: ${baseline.savedAt || baseline.timestamp}`)
163
+ console.log(` Current: ${current.timestamp}\n`)
164
+
165
+ const results: ComparisonResult[] = []
166
+ let hasRegression = false
167
+
168
+ // Compare each page
169
+ for (const currentPage of current.metrics) {
170
+ const baselinePage = baseline.metrics.find(m => m.pageName === currentPage.pageName)
171
+ if (!baselinePage) continue
172
+
173
+ // Compare key metrics
174
+ for (const metric of ['TTFB', 'FCP', 'LCP'] as const) {
175
+ const baseVal = baselinePage.performance[metric]
176
+ const currVal = currentPage.performance[metric]
177
+
178
+ if (baseVal === null || currVal === null) continue
179
+
180
+ const diff = currVal - baseVal
181
+ const diffPercent = (diff / baseVal) * 100
182
+ const threshold = THRESHOLDS[metric]
183
+
184
+ let status: 'improved' | 'regressed' | 'unchanged'
185
+ if (diffPercent < -threshold) {
186
+ status = 'improved'
187
+ } else if (diffPercent > threshold) {
188
+ status = 'regressed'
189
+ hasRegression = true
190
+ } else {
191
+ status = 'unchanged'
192
+ }
193
+
194
+ results.push({
195
+ pageName: currentPage.pageName,
196
+ metric,
197
+ baseline: baseVal,
198
+ current: currVal,
199
+ diff,
200
+ diffPercent,
201
+ status,
202
+ })
203
+ }
204
+ }
205
+
206
+ // Print results grouped by page
207
+ const pageNames = [...new Set(results.map(r => r.pageName))]
208
+ for (const pageName of pageNames) {
209
+ console.log(` ${pageName}`)
210
+ const pageResults = results.filter(r => r.pageName === pageName)
211
+
212
+ for (const r of pageResults) {
213
+ const icon = r.status === 'improved' ? '✅' : r.status === 'regressed' ? '❌' : '➖'
214
+ const sign = r.diff > 0 ? '+' : ''
215
+ const diffStr = `${sign}${r.diff.toFixed(0)}ms (${sign}${r.diffPercent.toFixed(1)}%)`
216
+
217
+ console.log(` ${icon} ${r.metric}: ${r.baseline.toFixed(0)}ms → ${r.current.toFixed(0)}ms ${diffStr}`)
218
+ }
219
+ console.log('')
220
+ }
221
+
222
+ // Summary
223
+ const improved = results.filter(r => r.status === 'improved').length
224
+ const regressed = results.filter(r => r.status === 'regressed').length
225
+ const unchanged = results.filter(r => r.status === 'unchanged').length
226
+
227
+ console.log(' Summary:')
228
+ console.log(` ✅ Improved: ${improved}`)
229
+ console.log(` ❌ Regressed: ${regressed}`)
230
+ console.log(` ➖ Unchanged: ${unchanged}`)
231
+
232
+ if (hasRegression) {
233
+ console.log('\n⚠️ Performance regressions detected!')
234
+ Deno.exit(1)
235
+ } else {
236
+ console.log('\n✅ No regressions detected')
237
+ }
238
+ }
239
+
240
+ // CLI
241
+ const [command, arg] = Deno.args
242
+
243
+ switch (command) {
244
+ case 'save':
245
+ saveBaseline(arg)
246
+ break
247
+ case 'compare':
248
+ compare(arg)
249
+ break
250
+ case 'list':
251
+ listBaselines()
252
+ break
253
+ case 'delete':
254
+ if (!arg) {
255
+ console.error('❌ Please specify baseline name to delete')
256
+ Deno.exit(1)
257
+ }
258
+ deleteBaseline(arg)
259
+ break
260
+ default:
261
+ console.log(`
262
+ Baseline Management Script
263
+ ==========================
264
+
265
+ Commands:
266
+ save [name] Save current report as baseline (default name: "default")
267
+ compare [name] Compare current report against baseline
268
+ list List all saved baselines
269
+ delete <name> Delete a specific baseline
270
+
271
+ Examples:
272
+ deno task test:e2e:baseline:save
273
+ deno task test:e2e:baseline:compare
274
+
275
+ # With names:
276
+ deno run -A scripts/baseline.ts save pre-release
277
+ deno run -A scripts/baseline.ts compare pre-release
278
+ `)
279
+ }
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env -S deno run -A
2
+ /**
3
+ * E2E Test Runner Script
4
+ * ======================
5
+ * This script:
6
+ * 1. Checks if the dev server is already running on port 8000
7
+ * 2. If not, starts it in the background
8
+ * 3. Waits for the server to be ready (liveness check)
9
+ * 4. Runs the e2e tests
10
+ * 5. Reports the results
11
+ * 6. Cleans up server on exit (including Ctrl+C)
12
+ *
13
+ * Usage:
14
+ * deno task test:e2e # Run tests (auto-start server if needed)
15
+ * deno task test:e2e:headed # Run tests with visible browser
16
+ *
17
+ * Placeholders to replace:
18
+ * {{SITE_NAME}} - Your site name (e.g., "lojastorra-2")
19
+ */
20
+
21
+ const SITE_URL = "http://localhost:8000";
22
+ const LIVENESS_PATH = "/deco/_liveness";
23
+ const E2E_DIR = "./tests/e2e";
24
+ const MAX_LIVENESS_RETRIES = 60;
25
+ const LIVENESS_RETRY_DELAY = 1000;
26
+
27
+ // Global state for cleanup
28
+ let serverProcess: Deno.ChildProcess | null = null;
29
+ let serverStartedByUs = false;
30
+ let isCleaningUp = false;
31
+
32
+ function cleanup(exitCode: number = 1): void {
33
+ if (isCleaningUp) return;
34
+ isCleaningUp = true;
35
+
36
+ if (serverProcess && serverStartedByUs) {
37
+ console.log("\n🛑 Stopping dev server...");
38
+ try {
39
+ serverProcess.kill("SIGTERM");
40
+ } catch {
41
+ // Process may already be dead
42
+ }
43
+ // Give it a moment to terminate gracefully, then force kill
44
+ setTimeout(() => {
45
+ try {
46
+ serverProcess?.kill("SIGKILL");
47
+ } catch {
48
+ // Ignore
49
+ }
50
+ Deno.exit(exitCode);
51
+ }, 1000);
52
+ } else {
53
+ Deno.exit(exitCode);
54
+ }
55
+ }
56
+
57
+ // Handle Ctrl+C and other termination signals
58
+ Deno.addSignalListener("SIGINT", () => {
59
+ console.log("\n⚠️ Interrupted (Ctrl+C)");
60
+ cleanup(130); // Standard exit code for SIGINT
61
+ });
62
+
63
+ Deno.addSignalListener("SIGTERM", () => {
64
+ console.log("\n⚠️ Terminated");
65
+ cleanup(143); // Standard exit code for SIGTERM
66
+ });
67
+
68
+ // Handle uncaught errors
69
+ globalThis.addEventListener("unhandledrejection", (event) => {
70
+ console.error("❌ Unhandled error:", event.reason);
71
+ cleanup(1);
72
+ });
73
+
74
+ async function isServerRunning(): Promise<boolean> {
75
+ try {
76
+ const res = await fetch(`${SITE_URL}${LIVENESS_PATH}`, {
77
+ signal: AbortSignal.timeout(2000),
78
+ });
79
+ return res.ok;
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
84
+
85
+ async function waitForServer(): Promise<boolean> {
86
+ console.log("⏳ Waiting for server to be ready...");
87
+
88
+ for (let i = 0; i < MAX_LIVENESS_RETRIES; i++) {
89
+ if (await isServerRunning()) {
90
+ console.log(`✅ Server is ready (attempt ${i + 1})`);
91
+ return true;
92
+ }
93
+ await new Promise((r) => setTimeout(r, LIVENESS_RETRY_DELAY));
94
+ if ((i + 1) % 10 === 0) {
95
+ console.log(` Still waiting... (${i + 1}/${MAX_LIVENESS_RETRIES})`);
96
+ }
97
+ }
98
+
99
+ return false;
100
+ }
101
+
102
+ async function startServer(): Promise<Deno.ChildProcess> {
103
+ console.log("🚀 Starting dev server...");
104
+
105
+ const command = new Deno.Command("deno", {
106
+ args: ["task", "dev"],
107
+ stdout: "piped",
108
+ stderr: "piped",
109
+ });
110
+
111
+ const process = command.spawn();
112
+
113
+ // Log server output in background
114
+ (async () => {
115
+ const decoder = new TextDecoder();
116
+ for await (const chunk of process.stdout) {
117
+ const text = decoder.decode(chunk);
118
+ if (text.includes("Fresh ready") || text.includes("Listening")) {
119
+ console.log(" 📡 Server started");
120
+ }
121
+ }
122
+ })();
123
+
124
+ return process;
125
+ }
126
+
127
+ async function runTests(headed: boolean = false): Promise<boolean> {
128
+ console.log(`\n🧪 Running e2e tests${headed ? " (headed mode)" : ""}...\n`);
129
+
130
+ const args = ["test", "--project=desktop-chrome"];
131
+ if (headed) {
132
+ args.push("--headed");
133
+ }
134
+
135
+ const command = new Deno.Command("npm", {
136
+ args,
137
+ cwd: E2E_DIR,
138
+ env: {
139
+ ...Deno.env.toObject(),
140
+ SITE_URL,
141
+ HEADED: headed ? "true" : "false",
142
+ },
143
+ stdout: "inherit",
144
+ stderr: "inherit",
145
+ });
146
+
147
+ const { code } = await command.output();
148
+ return code === 0;
149
+ }
150
+
151
+ async function main() {
152
+ const args = Deno.args;
153
+ const headed = args.includes("--headed") || args.includes("-h");
154
+ const skipServerCheck = args.includes("--skip-server-check");
155
+
156
+ try {
157
+ // Check if server is already running
158
+ if (!skipServerCheck) {
159
+ const serverAlreadyRunning = await isServerRunning();
160
+
161
+ if (serverAlreadyRunning) {
162
+ console.log("✅ Dev server already running");
163
+ } else {
164
+ // Start the server
165
+ serverProcess = await startServer();
166
+ serverStartedByUs = true;
167
+
168
+ // Wait for it to be ready
169
+ const ready = await waitForServer();
170
+ if (!ready) {
171
+ console.error("❌ Server failed to start within timeout");
172
+ cleanup(1);
173
+ return;
174
+ }
175
+ }
176
+ }
177
+
178
+ // Run the tests
179
+ const testsPassed = await runTests(headed);
180
+
181
+ if (testsPassed) {
182
+ console.log("\n✅ All tests passed!");
183
+ } else {
184
+ console.log("\n❌ Some tests failed");
185
+ }
186
+
187
+ cleanup(testsPassed ? 0 : 1);
188
+ } catch (err) {
189
+ console.error("❌ Error:", err);
190
+ cleanup(1);
191
+ }
192
+ }
193
+
194
+ main();