feat(compat-v7): @decocms/compat-v7 with v7's types, device detection, scripts and clx
Summary
The product owner approved (2026-10-14) a new package, @decocms/compat-v7, as the one home for v7's pure helpers on v8, so migrated sites stop keeping their own copies. This PR adds the package and its framework-neutral helpers. Matchers, the section-loader runner, commerce types and the /tanstack and /nextjs subpaths come in the PRs stacked on this one.
No v7 runtime: no ctx, invoke, request scope or cachedLoader. There are no environment reads, and nothing new goes into @decocms/blocks.
| Import path | What |
|---|---|
@decocms/compat-v7 | everything below |
/types (+ /types/widgets) | SectionProps (the loader's awaited return, per the PO), Resolved, LoadingFallbackProps, Flag, v7's widget aliases. Types only, with no AppContext/FnContext |
/device | detectDevice, isMobileUA, Device, v7's MOBILE_RE/TABLET_RE |
/script | useScript, useScriptAsDataURI, inlineScript: v7's bytes, without v7's NODE_ENV warning |
/clx | clx (named and default) |
Pinned to v7
v7's own files are kept verbatim in src/__fixtures__/v7/ (7.x 9131e9d9, @decocms/blocks 7.79.6), with their sha256 pinned by fixtures.test.ts. Every helper is compared against them:
- device: same regex sources and flags; 25 real user agents; 50,000 seeded generated user agents, all three outcomes hit
- clx: edge cases (including v7's untrimmed edge space); 20,000 seeded argument lists
- script: 10 hand-written function shapes × 4 argument lists (comments,
//inside strings, regexes, template literals, generators, classes); 5,000 seeded generated functions with JSON arguments, run through the LRU's evictions;useScriptAsDataURIandinlineScript - types:
expectTypeOfequality with v7'sSectionPropsfor Promise-returning loaders and the default,Resolved,LoadingFallbackProps,Flagand all 9 widget aliases - mutations: adding
.trim()to clx, an extra UA token, or", "as the args separator each fail the suites
Differences from v7 (all PO-approved or forced by the rules)
SectionPropsof a synchronous loader: you get the value. v7 gave the loader type itself. PO: "SectionProps = Awaited".useScriptwarning: it no longer prints the dev warning, because packages read no env.deco schema: v8's generator reads the widget aliases by name from any import (tests/compat-v7.schema.test.ts, for root,/typesand/types/widgets). The fields come out as v8 already makes them:Colorgets formatcolor(v7 wrotecolor-input, which the site editor renders the same way), andSecretbecomes a secret block (v7 used a password string).- Not ported:
useSection/usePartialSection(Fresh HTMX stubs) andAppContext/FnContext/App/Section(v7's ctx and app runtime). scriptAsDataURI: v7 has no such name, so it ships as v7'suseScriptAsDataURI. The Fresh-erascriptAsDataURI(unminified,", "separator) was a different function.
Release and guardrails
- Lockstep release: compat-v7 joins
.releaserc.json's allowlist afternextjs, the root build order and the release tests (exports shape, tarball contents, peer@decocms/blocks^8.3.0). CI's "Plain Node imports every built export" loop picks it up (6 exports). - New npm name:
@decocms/compat-v7404s on npm, and 7.x publishes onlypackages/*/there, which has no compat-v7. Publishing uses Trusted Publishing (OIDC), so the name needs the same bootstrap the other new names got (a manual first publish, then this workflow set as its trusted publisher) before the first release. tests/compat-v7.guardrail.test.ts: the neutral subpaths (and every file they reach) use no fetch, React,"use client"or env reads, and import nothing at runtime but their own modules and@decocms/blocks. They bundle for browser and workerd with nonode:/React, and export none of v7's runtime names.@decocms/blocksis the one required peer, and a binding may only be an optional peer./nextjsnever imports tanstack, and/tanstacknever imports nextjs. No other package depends on or imports compat-v7.guides.cli.test.tsin-11: amended for the one compat package.CLAUDE.md: "No compat layers" now names this PO-approved exception (pure helpers only, no v7 runtime). Sites, blocks, the bindings and the clients still may not re-create v7 APIs. Also updated: the architecture tree, the exports table, the release allowlist (eleven packages) and the dependency direction.README.mdand the release workflow comment are updated too.
Follow-ups (not here)
- The migration skill's import map still says "copy into the site" for these helpers. The codemod PRs in this stack will point imports at compat-v7.
- The docs site's
/nextpages need a matching PR.
Test plan
-
bun run check(typecheck, biome, knip) -
bun run build - Plain-Node import of every built export, as in CI: 11 packages, compat-v7 6/6,
deco --help -
bun run testunder a 2 GB cap: 2494 passed / 1 failed / 95 skipped. The failure is DS-2, the known one: it fails the same way whendelivery.cli.test.tsruns alone (10/11), and it readsprotocol/storage/fs/git.ts, which this PR doesn't touch. - compat-v7: 14 tests, guardrail 17, schema 3
-
skills:check+skills:readme:check
Main refresh (2026-10-17, 183bc4b7)
- Merged P7
ced92e19into this branch (normal merge, no conflicts), then merged up the stack to M2 #707. - Gates at the top of the stack (M2
5131a3c1):bun run check,bun run build,skills:check,skills:readme:checkpass.bun run test(2 GB memory cap): 122 files pass, 1 skipped; 2,745 tests pass, 95 skipped, 0 fail. DS-2 now passes (fixed on main by #709); no timeouts this run. - CI on this PR's new head: all checks pass.
🤖 Generated with Claude Code
https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
Summary by cubic
Adds @decocms/compat-v7, the single home for v7's pure helpers on v8: section types and widget aliases (/types), device detection (/device), inline scripts (/script), and clx (/clx), so migrated sites stop keeping their own copies. Every helper is pinned byte-for-byte against v7's own code, kept verbatim in fixtures with pinned hashes and exercised by seeded fuzz runs.
Behavior differences from v7
SectionPropsof a synchronous loader now yields the loader's return value; v7 gave the loader type itself.useScriptno longer logs the development warning, because packages read no environment.deco schemarendersColoras formatcolorandSecretas a secret block, which the site editor renders the same way v7'scolor-inputand password field did.- Not ported:
useSection/usePartialSection(Fresh HTMX stubs) andAppContext/FnContext(v7's ctx runtime).
Release and guardrails
- The package joins the lockstep release; as a new npm name it needs one manual first publish to set up Trusted Publishing.
- Guardrail tests hold it pure: no React, network, env reads, or v7 runtime exports, and no other package depends on it.
- The migration skill's import map still says "copy into the site"; the codemod PRs in this stack will point imports at this package.