@decocms/blocks-cli 7.0.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 (93) hide show
  1. package/package.json +44 -0
  2. package/scripts/analyze-traces.mjs +1117 -0
  3. package/scripts/audit-observability-config.test.ts +446 -0
  4. package/scripts/audit-observability-config.ts +511 -0
  5. package/scripts/deco-migrate-cli.ts +444 -0
  6. package/scripts/fast-deploy-kv.test.ts +131 -0
  7. package/scripts/generate-blocks.test.ts +94 -0
  8. package/scripts/generate-blocks.ts +274 -0
  9. package/scripts/generate-invoke.test.ts +195 -0
  10. package/scripts/generate-invoke.ts +469 -0
  11. package/scripts/generate-loaders.ts +217 -0
  12. package/scripts/generate-schema.ts +1287 -0
  13. package/scripts/generate-sections.ts +237 -0
  14. package/scripts/htmx-analyze.ts +226 -0
  15. package/scripts/lib/blocks-dedupe.test.ts +179 -0
  16. package/scripts/lib/blocks-dedupe.ts +142 -0
  17. package/scripts/lib/cf-kv-rest.ts +78 -0
  18. package/scripts/lib/jsonc.ts +122 -0
  19. package/scripts/lib/kv-snapshot.ts +51 -0
  20. package/scripts/lib/read-decofile.ts +70 -0
  21. package/scripts/lib/sync-helpers.ts +44 -0
  22. package/scripts/migrate/analyzers/htmx-analyze.test.ts +372 -0
  23. package/scripts/migrate/analyzers/htmx-analyze.ts +425 -0
  24. package/scripts/migrate/analyzers/island-classifier.ts +96 -0
  25. package/scripts/migrate/analyzers/loader-inventory.ts +63 -0
  26. package/scripts/migrate/analyzers/section-metadata.ts +147 -0
  27. package/scripts/migrate/analyzers/theme-extractor.ts +122 -0
  28. package/scripts/migrate/colors.ts +46 -0
  29. package/scripts/migrate/config.test.ts +202 -0
  30. package/scripts/migrate/config.ts +186 -0
  31. package/scripts/migrate/phase-analyze.test.ts +63 -0
  32. package/scripts/migrate/phase-analyze.ts +782 -0
  33. package/scripts/migrate/phase-cleanup-audit.test.ts +137 -0
  34. package/scripts/migrate/phase-cleanup-audit.ts +105 -0
  35. package/scripts/migrate/phase-cleanup.test.ts +141 -0
  36. package/scripts/migrate/phase-cleanup.ts +1588 -0
  37. package/scripts/migrate/phase-compile.test.ts +193 -0
  38. package/scripts/migrate/phase-compile.ts +177 -0
  39. package/scripts/migrate/phase-report.ts +243 -0
  40. package/scripts/migrate/phase-scaffold.ts +593 -0
  41. package/scripts/migrate/phase-transform.ts +310 -0
  42. package/scripts/migrate/phase-verify.test.ts +127 -0
  43. package/scripts/migrate/phase-verify.ts +572 -0
  44. package/scripts/migrate/post-cleanup/rules.ts +1708 -0
  45. package/scripts/migrate/post-cleanup/runner.test.ts +1771 -0
  46. package/scripts/migrate/post-cleanup/runner.ts +137 -0
  47. package/scripts/migrate/post-cleanup/shim-classify.test.ts +352 -0
  48. package/scripts/migrate/post-cleanup/shim-classify.ts +246 -0
  49. package/scripts/migrate/post-cleanup/types.ts +106 -0
  50. package/scripts/migrate/source-layout.test.ts +111 -0
  51. package/scripts/migrate/source-layout.ts +103 -0
  52. package/scripts/migrate/templates/app-css.ts +366 -0
  53. package/scripts/migrate/templates/cache-config.ts +26 -0
  54. package/scripts/migrate/templates/commerce-loaders.ts +230 -0
  55. package/scripts/migrate/templates/cursor-rules.test.ts +59 -0
  56. package/scripts/migrate/templates/cursor-rules.ts +70 -0
  57. package/scripts/migrate/templates/hooks.test.ts +141 -0
  58. package/scripts/migrate/templates/hooks.ts +152 -0
  59. package/scripts/migrate/templates/knip-config.ts +27 -0
  60. package/scripts/migrate/templates/lib-utils.test.ts +139 -0
  61. package/scripts/migrate/templates/lib-utils.ts +326 -0
  62. package/scripts/migrate/templates/lockfile-check-yml.test.ts +26 -0
  63. package/scripts/migrate/templates/lockfile-check-yml.ts +66 -0
  64. package/scripts/migrate/templates/package-json.ts +177 -0
  65. package/scripts/migrate/templates/routes.ts +237 -0
  66. package/scripts/migrate/templates/sdk-gen.ts +59 -0
  67. package/scripts/migrate/templates/section-loaders.ts +456 -0
  68. package/scripts/migrate/templates/server-entry.ts +561 -0
  69. package/scripts/migrate/templates/setup.ts +148 -0
  70. package/scripts/migrate/templates/tsconfig.ts +21 -0
  71. package/scripts/migrate/templates/types-gen.ts +174 -0
  72. package/scripts/migrate/templates/ui-components.ts +144 -0
  73. package/scripts/migrate/templates/vite-config.ts +101 -0
  74. package/scripts/migrate/transforms/dead-code.ts +455 -0
  75. package/scripts/migrate/transforms/deno-isms.ts +85 -0
  76. package/scripts/migrate/transforms/fresh-apis.ts +223 -0
  77. package/scripts/migrate/transforms/htmx-on-events.test.ts +305 -0
  78. package/scripts/migrate/transforms/htmx-on-events.ts +193 -0
  79. package/scripts/migrate/transforms/imports.ts +385 -0
  80. package/scripts/migrate/transforms/jsx.ts +317 -0
  81. package/scripts/migrate/transforms/section-conventions.ts +210 -0
  82. package/scripts/migrate/transforms/tailwind.ts +739 -0
  83. package/scripts/migrate/types.ts +244 -0
  84. package/scripts/migrate-blocks-to-kv.ts +104 -0
  85. package/scripts/migrate-post-cleanup.ts +191 -0
  86. package/scripts/migrate-to-cf-observability.test.ts +215 -0
  87. package/scripts/migrate-to-cf-observability.ts +699 -0
  88. package/scripts/migrate.ts +282 -0
  89. package/scripts/smoke-otlp-errorlog.ts +46 -0
  90. package/scripts/smoke-otlp-meter.ts +48 -0
  91. package/scripts/sync-blocks-to-kv.ts +153 -0
  92. package/scripts/tailwind-lint.ts +518 -0
  93. package/tsconfig.json +7 -0
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Smoke tests for the `migrate-to-cf-observability.ts` codemod.
3
+ *
4
+ * Drives the script as a child process against tmp wrangler.jsonc fixtures.
5
+ * Verifies the operationally important behaviors:
6
+ * - rewriting an existing observability block to the canonical CF-native
7
+ * shape (no destinations, master enabled flag set)
8
+ * - appending a new canonical block when none exists
9
+ * - second run is a no-op (idempotency / CI guard)
10
+ * - HyperDX-style destinations are stripped on the next run
11
+ * - opt-in destination forwarding via `--destination-logs` /
12
+ * `--destination-traces`
13
+ * - result is valid JSONC (parses after stripping comments)
14
+ */
15
+ import * as cp from "node:child_process";
16
+ import * as fs from "node:fs";
17
+ import * as os from "node:os";
18
+ import * as path from "node:path";
19
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
20
+
21
+ const SCRIPT = path.resolve(__dirname, "migrate-to-cf-observability.ts");
22
+
23
+ function runCodemod(args: string[]): { stdout: string; stderr: string; code: number } {
24
+ const r = cp.spawnSync("npx", ["tsx", SCRIPT, ...args], { encoding: "utf8" });
25
+ return { stdout: r.stdout || "", stderr: r.stderr || "", code: r.status ?? 0 };
26
+ }
27
+
28
+ function stripJsoncComments(s: string): string {
29
+ return s.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "");
30
+ }
31
+
32
+ describe("migrate-to-cf-observability codemod", () => {
33
+ let tmpDir: string;
34
+ let wranglerPath: string;
35
+
36
+ beforeEach(() => {
37
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "cf-codemod-"));
38
+ wranglerPath = path.join(tmpDir, "wrangler.jsonc");
39
+ });
40
+
41
+ afterEach(() => {
42
+ fs.rmSync(tmpDir, { recursive: true, force: true });
43
+ });
44
+
45
+ it("rewrites a partial observability block to the canonical CF-native shape", () => {
46
+ fs.writeFileSync(
47
+ wranglerPath,
48
+ `{
49
+ "name": "lebiscuit-tanstack",
50
+ "compatibility_date": "2026-02-14",
51
+ "main": "./src/worker-entry.ts",
52
+ "kv_namespaces": [{ "binding": "SITES_KV", "id": "abc" }],
53
+ "version_metadata": { "binding": "CF_VERSION_METADATA" },
54
+ "analytics_engine_datasets": [
55
+ { "binding": "DECO_METRICS", "dataset": "deco_metrics_lebiscuit" }
56
+ ],
57
+ "observability": {
58
+ "logs": {
59
+ "enabled": true,
60
+ "invocation_logs": true
61
+ }
62
+ }
63
+ }
64
+ `,
65
+ );
66
+
67
+ const r = runCodemod(["--source", tmpDir, "--write"]);
68
+ expect(r.code).toBe(0);
69
+
70
+ const result = fs.readFileSync(wranglerPath, "utf8");
71
+ // Master switch present.
72
+ expect(result).toContain('"enabled": true');
73
+ // Both leaves present with rates and persist.
74
+ expect(result).toContain('"head_sampling_rate": 1');
75
+ expect(result).toContain('"head_sampling_rate": 0.1');
76
+ expect(result.match(/"persist": true/g)?.length).toBe(2);
77
+ // No destinations by default.
78
+ expect(result).not.toContain('"destinations"');
79
+ // Original keys preserved.
80
+ expect(result).toContain('"name": "lebiscuit-tanstack"');
81
+ expect(result).toContain('"binding": "DECO_METRICS"');
82
+ // Result must be valid JSONC.
83
+ expect(() => JSON.parse(stripJsoncComments(result))).not.toThrow();
84
+ });
85
+
86
+ it("strips stale HyperDX-style destinations from a config that already has them", () => {
87
+ fs.writeFileSync(
88
+ wranglerPath,
89
+ `{
90
+ "name": "lebiscuit-tanstack",
91
+ "main": "./src/worker-entry.ts",
92
+ "observability": {
93
+ "enabled": true,
94
+ "logs": {
95
+ "enabled": true,
96
+ "invocation_logs": true,
97
+ "head_sampling_rate": 1,
98
+ "persist": true,
99
+ "destinations": ["hyperdx-logs"]
100
+ },
101
+ "traces": {
102
+ "enabled": true,
103
+ "head_sampling_rate": 0.1,
104
+ "persist": true,
105
+ "destinations": ["hyperdx-traces"]
106
+ }
107
+ }
108
+ }
109
+ `,
110
+ );
111
+
112
+ const r = runCodemod(["--source", tmpDir, "--write"]);
113
+ expect(r.code).toBe(0);
114
+
115
+ const result = fs.readFileSync(wranglerPath, "utf8");
116
+ expect(result).not.toContain("hyperdx-logs");
117
+ expect(result).not.toContain("hyperdx-traces");
118
+ expect(result).not.toContain('"destinations"');
119
+ expect(() => JSON.parse(stripJsoncComments(result))).not.toThrow();
120
+ });
121
+
122
+ it("appends a canonical observability block when none exists", () => {
123
+ fs.writeFileSync(
124
+ wranglerPath,
125
+ `{
126
+ "name": "fresh-site",
127
+ "compatibility_date": "2026-02-14",
128
+ "main": "./src/worker-entry.ts"
129
+ }
130
+ `,
131
+ );
132
+
133
+ const r = runCodemod(["--source", tmpDir, "--write"]);
134
+ expect(r.code).toBe(0);
135
+
136
+ const result = fs.readFileSync(wranglerPath, "utf8");
137
+ expect(result).toContain('"observability"');
138
+ expect(result).toContain('"enabled": true');
139
+ expect(result).toContain('"head_sampling_rate": 1');
140
+ expect(result).toContain('"head_sampling_rate": 0.1');
141
+ expect(result).not.toContain('"destinations"');
142
+ expect(() => JSON.parse(stripJsoncComments(result))).not.toThrow();
143
+ });
144
+
145
+ it("is idempotent: second run is a no-op", () => {
146
+ fs.writeFileSync(
147
+ wranglerPath,
148
+ `{
149
+ "name": "lebiscuit-tanstack",
150
+ "main": "./src/worker-entry.ts",
151
+ "observability": {
152
+ "logs": { "enabled": true }
153
+ }
154
+ }
155
+ `,
156
+ );
157
+
158
+ runCodemod(["--source", tmpDir, "--write"]);
159
+ const after1 = fs.readFileSync(wranglerPath, "utf8");
160
+
161
+ const r = runCodemod(["--source", tmpDir, "--write"]);
162
+ expect(r.code).toBe(0);
163
+ expect(r.stdout).toContain("already on the canonical CF observability block");
164
+
165
+ const after2 = fs.readFileSync(wranglerPath, "utf8");
166
+ expect(after2).toBe(after1);
167
+ });
168
+
169
+ it("dry-run exits 1 and does not modify the file (CI signal)", () => {
170
+ const before = `{
171
+ "name": "site",
172
+ "main": "./src/worker-entry.ts"
173
+ }
174
+ `;
175
+ fs.writeFileSync(wranglerPath, before);
176
+
177
+ const r = runCodemod(["--source", tmpDir]);
178
+ expect(r.code).toBe(1);
179
+ expect(r.stdout).toContain("Dry-run");
180
+
181
+ expect(fs.readFileSync(wranglerPath, "utf8")).toBe(before);
182
+ });
183
+
184
+ it("respects --destination-logs / --destination-traces / --traces-rate / --persist flags", () => {
185
+ fs.writeFileSync(
186
+ wranglerPath,
187
+ `{
188
+ "name": "site",
189
+ "main": "./src/worker-entry.ts"
190
+ }
191
+ `,
192
+ );
193
+
194
+ runCodemod([
195
+ "--source",
196
+ tmpDir,
197
+ "--destination-logs",
198
+ "my-logs",
199
+ "--destination-traces",
200
+ "my-traces",
201
+ "--traces-rate",
202
+ "0.05",
203
+ "--persist",
204
+ "--write",
205
+ ]);
206
+
207
+ const result = fs.readFileSync(wranglerPath, "utf8");
208
+ expect(result).toContain('"destinations": ["my-logs"]');
209
+ expect(result).toContain('"destinations": ["my-traces"]');
210
+ expect(result).toContain('"head_sampling_rate": 0.05');
211
+ // Both blocks set persist:true.
212
+ const persistTrueCount = (result.match(/"persist": true/g) ?? []).length;
213
+ expect(persistTrueCount).toBe(2);
214
+ });
215
+ }, 30_000);