@decocms/start 4.5.0 → 5.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.
@@ -183,17 +183,21 @@ This is per-isolate in-memory cache (V8 Map). Resets on cold start. Includes req
183
183
 
184
184
  ## Cache Versioning with BUILD_HASH
185
185
 
186
- Deploy-time cache busting uses a `BUILD_HASH` environment variable (typically the git short SHA) passed to `wrangler deploy`. The worker-entry appends this to cache keys so deploying a new version automatically serves fresh content.
187
-
188
- ```yaml
189
- # .github/workflows/deploy.yml
190
- - name: Deploy to Cloudflare Workers
191
- run: npx wrangler deploy
192
- env:
193
- BUILD_HASH: ${{ github.sha }}
194
- ```
186
+ Every cached entry's key is suffixed with `__v=<hash>` so a new deploy starts a fresh cache namespace and previously-cached HTML (which references now-deleted asset filenames like `/assets/main-XYZ.js`) stops being served the moment the new worker is live. Old entries become orphaned and expire naturally no purge endpoint call required.
187
+
188
+ The hash is resolved automatically by `decoVitePlugin()` at build time and injected into the worker bundle as `__DECO_BUILD_HASH__`. Resolution order:
189
+
190
+ 1. `WORKERS_CI_COMMIT_SHA` — Cloudflare Workers Builds default env var ([CF docs](https://developers.cloudflare.com/changelog/2025-06-10-default-env-vars/)). This is the production deploy path-of-record per `MIGRATION_TOOLING_PLAN.md` D6.3.
191
+ 2. `git rev-parse --short=12 HEAD` — for someone running `wrangler deploy` from a developer laptop.
192
+ 3. `Date.now().toString(36)` — last-resort fallback so the cache-bust invariant never silently regresses.
193
+
194
+ `createDecoWorkerEntry` reads `env.BUILD_HASH` first (explicit override path, e.g. `wrangler deploy --var BUILD_HASH:foo`) and falls back to the `__DECO_BUILD_HASH__` constant. Sites running `decoVitePlugin()` get the behaviour for free — **no per-site dashboard, `wrangler.jsonc`, or `--var` configuration required**.
195
195
 
196
- The worker-entry reads `env.BUILD_HASH` and injects it into cache keys. On new deploys, old cache entries simply expire naturally no purge needed.
196
+ The active version is exposed on every cached response via the `X-Cache-Version` header for observability. Confirm a new deploy is shipping the right hash with:
197
+
198
+ ```bash
199
+ curl -sI https://www.example.com/ | grep -i x-cache-version
200
+ ```
197
201
 
198
202
  ## Site-Level Cache Pattern Registration
199
203
 
@@ -1,5 +1,25 @@
1
1
  name: Release
2
2
 
3
+ # GOTCHA: GitHub Actions silently skips this workflow when the head
4
+ # commit message contains the canonical CI-skip token (the bracketed
5
+ # phrase "skip" + "ci") ANYWHERE in the message — title or body,
6
+ # inside backticks, inside code blocks, doesn't matter. The
7
+ # semantic-release release commit emits that token by design (so its
8
+ # own commit doesn't loop a release back through CI). Side effect:
9
+ # any PR that DESCRIBES that release commit verbatim in its body
10
+ # inherits the token through the squash-merge and silently skips
11
+ # the next release.
12
+ #
13
+ # When writing a commit message or PR body that needs to reference
14
+ # the suppression behaviour, refer to it descriptively (e.g. "the
15
+ # semantic-release skip token") instead of pasting the literal.
16
+ #
17
+ # Recover with a no-op commit whose entire message — title and body
18
+ # — does NOT contain the literal token.
19
+ #
20
+ # References:
21
+ # - https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs
22
+
3
23
  on:
4
24
  push:
5
25
  branches: [main]
@@ -23,10 +43,14 @@ jobs:
23
43
  node-version: 22
24
44
  registry-url: https://registry.npmjs.org
25
45
 
26
- - run: npm install
46
+ - uses: oven-sh/setup-bun@v2
47
+ with:
48
+ bun-version: 1.3.5
49
+
50
+ - run: bun install --frozen-lockfile
27
51
 
28
52
  - name: Release
29
- run: npx semantic-release
53
+ run: bunx semantic-release
30
54
  env:
31
55
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32
56
 
package/CHANGELOG.md ADDED
@@ -0,0 +1,113 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@decocms/start` are documented here.
4
+
5
+ The project follows [Conventional Commits](https://www.conventionalcommits.org/)
6
+ and [semver](https://semver.org/). Releases are cut by semantic-release on
7
+ merges to `main`; this file is the human-curated breaking-change ledger.
8
+ For per-release auto-generated notes (every commit, every fix), see
9
+ [GitHub Releases](https://github.com/decocms/deco-start/releases).
10
+
11
+ ## 5.0.0 — Drop in-Worker OTLP, converge on Cloudflare-native observability
12
+
13
+ ### Breaking — Observability transport rewritten
14
+
15
+ - **In-Worker OTLP exporter for logs and metrics is gone.**
16
+ - Removed `createOtelLoggerAdapter()`, `createOtelMeterAdapter()`,
17
+ `OtelLoggerAdapterOptions`, `OtelMeterAdapterOptions`.
18
+ - Removed the per-request flush registry: `flushOtelProviders()`,
19
+ `registerOtelFlushHandler()`, `_resetFlushHandlersForTests()`,
20
+ `_getFlushHandlerCountForTests()`.
21
+ - **`URLBasedSampler` and the OTel sampler module are gone.**
22
+ - Removed `URLBasedSampler`, `decodeSamplingConfig`,
23
+ `createUrlBasedHeadSampler`, `SamplingConfig`, `SamplingRule`,
24
+ `DEFAULT_SAMPLE_RATIO`, and the entire `@decocms/start/sdk/sampler`
25
+ export. Use Cloudflare's `observability.traces.head_sampling_rate`
26
+ in `wrangler.jsonc` instead.
27
+ - **OTLP-related options on `instrumentWorker(handler, options)` are gone.**
28
+ - Removed: `enableAppSideOtlpLogs`, `otlpEndpoint`, `otlpHeaders`,
29
+ `otlpMinSeverity`, `samplingConfig`, `exportIntervalMillis`.
30
+ - Kept: `serviceName`, `analyticsEngineBindingName`,
31
+ `analyticsEngineEnabled`, `decoRuntimeVersion`, `decoAppsVersion`.
32
+ - Sites that previously read `OTEL_EXPORTER_OTLP_ENDPOINT`,
33
+ `OTEL_EXPORTER_OTLP_HEADERS`, `OTEL_LOG_MIN_SEVERITY`, or
34
+ `OTEL_SAMPLING_CONFIG` should delete those secrets after deploying
35
+ against `5.0.0`:
36
+ ```bash
37
+ wrangler secret delete OTEL_EXPORTER_OTLP_ENDPOINT \
38
+ OTEL_EXPORTER_OTLP_HEADERS \
39
+ OTEL_LOG_MIN_SEVERITY \
40
+ OTEL_SAMPLING_CONFIG
41
+ ```
42
+ - **OpenTelemetry SDK dependencies dropped from `package.json`:**
43
+ `@opentelemetry/api-logs`, `@opentelemetry/exporter-logs-otlp-http`,
44
+ `@opentelemetry/exporter-metrics-otlp-http`, `@opentelemetry/resources`,
45
+ `@opentelemetry/sdk-logs`, `@opentelemetry/sdk-metrics`,
46
+ `@opentelemetry/sdk-trace-base`. Only `@opentelemetry/api` remains —
47
+ it is the bridge between `withTracing()` and the global tracer
48
+ Cloudflare's runtime exposes.
49
+
50
+ ### Required `wrangler.jsonc` change
51
+
52
+ Sites must set `observability.enabled: true` at the top level of the
53
+ `observability` block — the master switch. Without it Cloudflare captures
54
+ nothing, regardless of the sub-block flags. Run the codemod to inject
55
+ the canonical block:
56
+
57
+ ```bash
58
+ npx -p @decocms/start deco-cf-observability --write
59
+ ```
60
+
61
+ The canonical block written by `5.0.0`:
62
+
63
+ ```jsonc
64
+ "observability": {
65
+ "enabled": true,
66
+ "logs": { "enabled": true, "invocation_logs": true,
67
+ "head_sampling_rate": 1, "persist": true },
68
+ "traces": { "enabled": true,
69
+ "head_sampling_rate": 0.1, "persist": true }
70
+ }
71
+ ```
72
+
73
+ External destinations (HyperDX, Datadog, etc.) are no longer wired by
74
+ default. Opt back in via `--destination-logs <slug>` /
75
+ `--destination-traces <slug>` if your site forwards to a destination
76
+ provisioned in the Cloudflare dashboard.
77
+
78
+ ### Unchanged surface
79
+
80
+ The instrumentation API site code calls is unchanged:
81
+
82
+ - `instrumentWorker(handler, options)`
83
+ - `withTracing(name, fn, attrs)`, `getActiveSpan`, `setSpanAttribute`
84
+ - `recordRequestMetric`, `recordCacheMetric`, `MetricNames`
85
+ - `logger.{debug,info,warn,error}`, `serializeError`, `LoggerAdapter`,
86
+ `setLoggerAttributeFloor`
87
+ - `createCompositeLogger`, `createCompositeMeter`
88
+ - `createAnalyticsEngineMeterAdapter`, `setRuntimeEnv`, `getRuntimeEnv`
89
+
90
+ The `deco.runtime.version`, `deco.apps.version`, and
91
+ `deployment.environment` attribute floor that landed in 4.5.x stays —
92
+ stamped on every span and every log record so dashboards keep working
93
+ under Cloudflare's platform-managed export.
94
+
95
+ ### Future (not in 5.0.0)
96
+
97
+ `@decocms/start/sdk/otelAdapters/clickhouseCollector` ships as a
98
+ **documented stub that throws**. When the platform-side OTel collector
99
+ gateway lands (forwarding to ClickHouse), the real OTLP/HTTP exporter
100
+ implementation goes here — composed alongside the AE meter for
101
+ dual-emit. The `OtelOptions` shape will gain a single
102
+ `clickhouseCollector?: ClickhouseCollectorOptions` field at that point.
103
+
104
+ ### Migration checklist for site maintainers
105
+
106
+ 1. Bump `@decocms/start` to `^5.0.0`.
107
+ 2. `npx -p @decocms/start deco-cf-observability --write`
108
+ 3. `wrangler deploy`
109
+ 4. Verify CF dashboard captures logs + traces (~5 min):
110
+ Workers & Pages → `<site>` → Observability
111
+ 5. `wrangler secret delete OTEL_EXPORTER_OTLP_ENDPOINT
112
+ OTEL_EXPORTER_OTLP_HEADERS OTEL_LOG_MIN_SEVERITY
113
+ OTEL_SAMPLING_CONFIG`
package/bun.lock CHANGED
@@ -7,13 +7,6 @@
7
7
  "dependencies": {
8
8
  "@deco-cx/warp-node": "^0.3.16",
9
9
  "@opentelemetry/api": "^1.9.1",
10
- "@opentelemetry/api-logs": "^0.200.0",
11
- "@opentelemetry/exporter-logs-otlp-http": "^0.200.0",
12
- "@opentelemetry/exporter-metrics-otlp-http": "^0.200.0",
13
- "@opentelemetry/resources": "^2.6.1",
14
- "@opentelemetry/sdk-logs": "^0.200.0",
15
- "@opentelemetry/sdk-metrics": "^2.0.0",
16
- "@opentelemetry/sdk-trace-base": "^2.6.1",
17
10
  "clsx": "^2.1.1",
18
11
  "fast-json-patch": "^3.1.0",
19
12
  "tailwind-merge": "^3.3.1",
@@ -245,28 +238,6 @@
245
238
 
246
239
  "@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
247
240
 
248
- "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.200.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q=="],
249
-
250
- "@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="],
251
-
252
- "@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/sdk-logs": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-KfWw49htbGGp9s8N4KI8EQ9XuqKJ0VG+yVYVYFiCYSjEV32qpQ5qZ9UZBzOZ6xRb+E16SXOSCT3RkqBVSABZ+g=="],
253
-
254
- "@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-5BiR6i8yHc9+qW7F6LqkuUnIzVNA7lt0qRxIKcKT+gq3eGUPHZ3DY29sfxI3tkvnwMgtnHDMNze5DdxW39HsAw=="],
255
-
256
- "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="],
257
-
258
- "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="],
259
-
260
- "@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
261
-
262
- "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-VZG870063NLfObmQQNtCVcdXXLzI3vOjjrRENmU37HYiPFa0ZXpXVDsTD02Nh3AT3xYJzQaWKl2X2lQ2l7TWJA=="],
263
-
264
- "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="],
265
-
266
- "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw=="],
267
-
268
- "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
269
-
270
241
  "@oxc-project/types": ["@oxc-project/types@0.120.0", "", {}, "sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg=="],
271
242
 
272
243
  "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.19.1", "", { "os": "android", "cpu": "arm" }, "sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg=="],
@@ -315,26 +286,6 @@
315
286
 
316
287
  "@pnpm/npm-conf": ["@pnpm/npm-conf@3.0.2", "", { "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" } }, "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA=="],
317
288
 
318
- "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
319
-
320
- "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
321
-
322
- "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
323
-
324
- "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
325
-
326
- "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
327
-
328
- "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
329
-
330
- "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
331
-
332
- "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
333
-
334
- "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
335
-
336
- "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
337
-
338
289
  "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.10", "", { "os": "android", "cpu": "arm64" }, "sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg=="],
339
290
 
340
291
  "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w=="],
@@ -827,8 +778,6 @@
827
778
 
828
779
  "lodash.uniqby": ["lodash.uniqby@4.7.0", "", {}, "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww=="],
829
780
 
830
- "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
831
-
832
781
  "lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="],
833
782
 
834
783
  "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
@@ -951,8 +900,6 @@
951
900
 
952
901
  "proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="],
953
902
 
954
- "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
955
-
956
903
  "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
957
904
 
958
905
  "queue-microtask": ["queue-microtask@1.2.3", "", {}, ""],
@@ -1215,20 +1162,6 @@
1215
1162
 
1216
1163
  "@deco-cx/warp-node/undici": ["undici@6.23.0", "", {}, "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g=="],
1217
1164
 
1218
- "@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="],
1219
-
1220
- "@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="],
1221
-
1222
- "@opentelemetry/otlp-transformer/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-qQnYdX+ZCkonM7tA5iU4fSRsVxbFGml8jbxOgipRGMFHKaXKHQ30js03rTobYjKjIfnOsZSbHKWF0/0v0OQGfw=="],
1223
-
1224
- "@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@2.6.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g=="],
1225
-
1226
- "@opentelemetry/sdk-logs/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="],
1227
-
1228
- "@opentelemetry/sdk-metrics/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="],
1229
-
1230
- "@opentelemetry/sdk-trace-base/@opentelemetry/core": ["@opentelemetry/core@2.6.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g=="],
1231
-
1232
1165
  "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="],
1233
1166
 
1234
1167
  "@semantic-release/git/@semantic-release/error": ["@semantic-release/error@3.0.0", "", {}, "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw=="],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/start",
3
- "version": "4.5.0",
3
+ "version": "5.0.0",
4
4
  "type": "module",
5
5
  "description": "Deco framework for TanStack Start - CMS bridge, admin protocol, hooks, schema generation",
6
6
  "main": "./src/index.ts",
@@ -39,7 +39,7 @@
39
39
  "./sdk/logger": "./src/sdk/logger.ts",
40
40
  "./sdk/composite": "./src/sdk/composite.ts",
41
41
  "./sdk/otelAdapters": "./src/sdk/otelAdapters.ts",
42
- "./sdk/sampler": "./src/sdk/sampler.ts",
42
+ "./sdk/otelAdapters/clickhouseCollector": "./src/sdk/otelAdapters/clickhouseCollector.ts",
43
43
  "./sdk/observability": "./src/sdk/observability.ts",
44
44
  "./sdk/workerEntry": "./src/sdk/workerEntry.ts",
45
45
  "./sdk/abTesting": "./src/sdk/abTesting.ts",
@@ -86,7 +86,8 @@
86
86
  "lint:unused": "knip",
87
87
  "format": "biome format src/ scripts/",
88
88
  "format:fix": "biome format --write src/ scripts/",
89
- "check": "npm run typecheck && npm run lint && npm run lint:unused"
89
+ "check": "bun run typecheck && bun run lint && bun run lint:unused",
90
+ "clean": "rm -rf node_modules .cache dist .wrangler/state node_modules/.vite && bun install"
90
91
  },
91
92
  "keywords": [
92
93
  "deco",
@@ -96,6 +97,7 @@
96
97
  ],
97
98
  "author": "deco.cx",
98
99
  "license": "MIT",
100
+ "packageManager": "bun@1.3.5",
99
101
  "repository": {
100
102
  "type": "git",
101
103
  "url": "https://github.com/decocms/deco-start.git"
@@ -107,13 +109,6 @@
107
109
  "dependencies": {
108
110
  "@deco-cx/warp-node": "^0.3.16",
109
111
  "@opentelemetry/api": "^1.9.1",
110
- "@opentelemetry/api-logs": "^0.200.0",
111
- "@opentelemetry/exporter-logs-otlp-http": "^0.200.0",
112
- "@opentelemetry/exporter-metrics-otlp-http": "^0.200.0",
113
- "@opentelemetry/resources": "^2.6.1",
114
- "@opentelemetry/sdk-logs": "^0.200.0",
115
- "@opentelemetry/sdk-metrics": "^2.0.0",
116
- "@opentelemetry/sdk-trace-base": "^2.6.1",
117
112
  "clsx": "^2.1.1",
118
113
  "fast-json-patch": "^3.1.0",
119
114
  "tailwind-merge": "^3.3.1",
@@ -2,10 +2,14 @@
2
2
  * Smoke tests for the `migrate-to-cf-observability.ts` codemod.
3
3
  *
4
4
  * Drives the script as a child process against tmp wrangler.jsonc fixtures.
5
- * Verifies the three behaviors that matter operationally:
6
- * - replacing an existing `observability.logs` block (lebiscuit shape)
7
- * - appending a new `observability` block when none exists
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
8
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`
9
13
  * - result is valid JSONC (parses after stripping comments)
10
14
  */
11
15
  import * as cp from "node:child_process";
@@ -38,7 +42,7 @@ describe("migrate-to-cf-observability codemod", () => {
38
42
  fs.rmSync(tmpDir, { recursive: true, force: true });
39
43
  });
40
44
 
41
- it("replaces an existing observability.logs block in lebiscuit-shape config", () => {
45
+ it("rewrites a partial observability block to the canonical CF-native shape", () => {
42
46
  fs.writeFileSync(
43
47
  wranglerPath,
44
48
  `{
@@ -64,19 +68,58 @@ describe("migrate-to-cf-observability codemod", () => {
64
68
  expect(r.code).toBe(0);
65
69
 
66
70
  const result = fs.readFileSync(wranglerPath, "utf8");
67
- expect(result).toContain('"destinations": ["hyperdx-logs"]');
68
- expect(result).toContain('"destinations": ["hyperdx-traces"]');
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');
69
75
  expect(result).toContain('"head_sampling_rate": 0.1');
70
-
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"');
71
82
  // Result must be valid JSONC.
72
83
  expect(() => JSON.parse(stripJsoncComments(result))).not.toThrow();
84
+ });
73
85
 
74
- // Original key context preserved.
75
- expect(result).toContain('"name": "lebiscuit-tanstack"');
76
- expect(result).toContain('"binding": "DECO_METRICS"');
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();
77
120
  });
78
121
 
79
- it("appends a new observability block when none exists", () => {
122
+ it("appends a canonical observability block when none exists", () => {
80
123
  fs.writeFileSync(
81
124
  wranglerPath,
82
125
  `{
@@ -92,7 +135,10 @@ describe("migrate-to-cf-observability codemod", () => {
92
135
 
93
136
  const result = fs.readFileSync(wranglerPath, "utf8");
94
137
  expect(result).toContain('"observability"');
95
- expect(result).toContain('"destinations": ["hyperdx-logs"]');
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"');
96
142
  expect(() => JSON.parse(stripJsoncComments(result))).not.toThrow();
97
143
  });
98
144
 
@@ -114,7 +160,7 @@ describe("migrate-to-cf-observability codemod", () => {
114
160
 
115
161
  const r = runCodemod(["--source", tmpDir, "--write"]);
116
162
  expect(r.code).toBe(0);
117
- expect(r.stdout).toContain("already on CF-native");
163
+ expect(r.stdout).toContain("already on the canonical CF observability block");
118
164
 
119
165
  const after2 = fs.readFileSync(wranglerPath, "utf8");
120
166
  expect(after2).toBe(after1);
@@ -135,7 +181,7 @@ describe("migrate-to-cf-observability codemod", () => {
135
181
  expect(fs.readFileSync(wranglerPath, "utf8")).toBe(before);
136
182
  });
137
183
 
138
- it("respects --logs / --traces / --traces-rate / --persist flags", () => {
184
+ it("respects --destination-logs / --destination-traces / --traces-rate / --persist flags", () => {
139
185
  fs.writeFileSync(
140
186
  wranglerPath,
141
187
  `{
@@ -148,9 +194,9 @@ describe("migrate-to-cf-observability codemod", () => {
148
194
  runCodemod([
149
195
  "--source",
150
196
  tmpDir,
151
- "--logs",
197
+ "--destination-logs",
152
198
  "my-logs",
153
- "--traces",
199
+ "--destination-traces",
154
200
  "my-traces",
155
201
  "--traces-rate",
156
202
  "0.05",
@@ -162,7 +208,7 @@ describe("migrate-to-cf-observability codemod", () => {
162
208
  expect(result).toContain('"destinations": ["my-logs"]');
163
209
  expect(result).toContain('"destinations": ["my-traces"]');
164
210
  expect(result).toContain('"head_sampling_rate": 0.05');
165
- // Both blocks set persist:true (no logs-only persist flag).
211
+ // Both blocks set persist:true.
166
212
  const persistTrueCount = (result.match(/"persist": true/g) ?? []).length;
167
213
  expect(persistTrueCount).toBe(2);
168
214
  });