feat: release @decocms/tanstack and @decocms/nextjs in lockstep with blocks at 8.3.0

TL
2h ago
+385-44

Stacked on #697 (P5). Part 6 of the v8 framework packages stack: #693 → #694 → #695 → #696 → #697 → this.

What changes

  • Lockstep publish. .releaserc.json's publish allowlist is blocks tanstack nextjs plus the seven apps-*. Every release publishes all ten at one version, even a package with no change. @decocms/tanstack and @decocms/nextjs publish from main on latest.
  • Peer ranges. The apps-* took @decocms/blocks as workspace:* in dependencies, which publishes as an exact pin, so a blocks patch release put a second blocks next to an app. They now take it as a peer, ^8.3.0, with workspace:* in devDependencies, as the bindings already do.
  • scripts/sync-versions.mjs writes the release version, rewrites workspace: ranges only in devDependencies, and refuses to release when an in-repo package is a plain dependency or a peer floor is above the release version.
  • tests/release.test.ts (38 tests): allowlist = packages/, blocks then the bindings first; every dependent takes blocks as ^8.3.0 peer; the sync on a copy of the manifests (8.3.0, a later 8.4.1, refusing 8.2.4 and a plain dependency). New since the PO review: for each of the ten packages, every export target (through conditional branches) has the types + source + default shape (or is plain JS shipped from src, as @decocms/nextjs/config), and npm pack would ship each of those files and no test or fixture. That covers the clients' new helper subpaths, @decocms/apps-vtex/image and @decocms/apps-shopify/image (#694); a mutation that drops src/image.ts from apps-vtex's files fails it.
  • CI. The build job's plain-Node import of every built export already walks every subpath, so it imports the new /image subpaths; the job now also runs tests/release.test.ts against the dist it built.
  • Docs. CLAUDE.md (allowlist, peer-range rule), README (the packages publish tsc output in dist/ plus a source condition, not plain .ts), release workflow comment (ten packages).

Dry run (repeated after the PO review)

In a throwaway clone (deleted afterwards), origin/main plus this stack squashed into one feat: commit, with the real config (GitHub plugin included; dry run only):

  • semantic-release --dry-run --no-ci: "The next release version is 8.3.0" (last tag blocks-v8.2.3).
  • The prepare step (VERSION=8.3.0 node scripts/sync-versions.mjs && bun run build): all ten manifests at 8.3.0, no workspace: left; tanstack, nextjs and every apps-* have peerDependencies["@decocms/blocks"] = "^8.3.0".
  • The publish step rendered from the config, with npm publish --dry-run: + @decocms/blocks@8.3.0, + @decocms/tanstack@8.3.0, + @decocms/nextjs@8.3.0 and the seven apps, all "with tag latest". No test files in the binding tarballs.
  • The apps-vtex and apps-shopify tarballs hold dist/image.{js,d.ts} (+ maps) and src/image.ts; the blocks tarball has no studio module; the nextjs tarball has its own draft, studio and StudioBridge modules.
  • The ten tarballs installed together with npm: npm ls @decocms/blocks shows one copy, every other package deduped (one @decocms/blocks/package.json on disk). Plain Node imported all 38 exports of the installed packages, /image subpaths included, with no failure; @decocms/blocks exports neither STUDIO_ORIGINS nor DRAFT_PARAM, and @decocms/nextjs' and @decocms/tanstack's copies are equal. No package was published.

Dry run after the PO decisions of 2026-10-15

Re-run after merging #697 (with #693–#696's decisions: NODE_ENV dev mode, isNextDev removed, first-match multivariate, deco schema --check, leading asset-host strip, edit shortcut on by default). Throwaway local repos, deleted afterwards; nothing was pushed or published:

  • semantic-release --dry-run --no-ci (commit analyzer and release notes, against a local bare mirror of the repo with its tags): last tag blocks-v8.2.3, "The next release version is 8.3.0".
  • Prepare step: all ten manifests at 8.3.0, no workspace: left; build passes.
  • npm publish --dry-run --tag latest for all ten: each exits 0. The nextjs tarball (54 files) has no dev module.
  • blocks, tanstack, nextjs, apps-vtex and apps-shopify installed together: one @decocms/blocks (the others deduped); plain Node imported all 31 of their exports, and @decocms/nextjs exports no isNextDev.
  • tests/release.test.ts: 38/38.

Tests

  • bun run check, bun run build, skills:check, skills:readme:check: pass.
  • bun run test: 2,396 passed, 5 failed, all known or load timeouts: DS-2 in delivery.cli.test.ts, and timeouts under full-suite load in delivery.cli.test.ts DS-5, guides.cli.test.ts tr-13, protocol/storage/fs/git.test.ts and schema.test.ts. Rerun on their own: the first three files pass apart from DS-2, and schema.test.ts passes 58/58 alone.

For the PO: landing and npm commands (not run)

Landing as 8.3.0. semantic-release releases on every push to main. To ship all three at 8.3.0 together, land the stack as one commit on main: merge this PR into #697, #697 into #696, and so on down to #693, then squash #693 into main with a feat: title. If the PRs land on main one by one, P1 alone becomes 8.3.0 and the bindings first publish at a later minor (their ^8.3.0 peer would then also need raising to that version). If another feat: reaches main first, the release is 8.4.0: raise the peer floors in this PR to ^8.4.0 before landing.

Before landing (7.x). Merge #698 first (7.x branch). Today 7.x publishes both bindings with --tag latest, so the next 7.x release would take latest back from 8.3.0. #698 moves them to latest-7.

npm commands, run by the PO with an npm owner login, before the stack lands:

# 1. Keep v7 installable by tag. Use 7.79.6, or the newest 7.x if a 7.x release has run since
#    (merging #698 releases 7.79.7 and sets latest-7 itself; then skip this step).
npm dist-tag add @decocms/tanstack@7.79.6 latest-7
npm dist-tag add @decocms/nextjs@7.79.6 latest-7

# 2. Rewrite the 8.0.0 deprecation (it says "use @latest (7.x)", which is wrong once latest is 8.3.0).
npm deprecate @decocms/tanstack@8.0.0 "Published by mistake. Use ^8.3.0 for v8, or 7.x for v7."
npm deprecate @decocms/nextjs@8.0.0 "Published by mistake. Use ^8.3.0 for v8, or 7.x for v7."

# 3. Deprecate the two prereleases (not deprecated today).
npm deprecate @decocms/tanstack@"8.0.0-next.0 || 8.1.0-next.0" "Prerelease. Use ^8.3.0 for v8, or 7.x for v7."
npm deprecate @decocms/nextjs@"8.0.0-next.0 || 8.1.0-next.0" "Prerelease. Use ^8.3.0 for v8, or 7.x for v7."

latest moves to 8.3.0 by itself when the release publishes; no command is needed for it. Check afterwards:

npm view @decocms/tanstack dist-tags   # { latest: '8.3.0', 'latest-7': '7.79.x' }
npm view @decocms/nextjs dist-tags

Other things to know:

  • Trusted Publishing. The bindings were last published from the 7.x branch. npm's trusted publisher names the repo and the workflow file (release.yml), not a branch, so main should be accepted. If either package's trusted publisher is tied to a GitHub environment, add it for main before landing, or its publish fails while the other nine succeed.
  • No CI check against the oldest blocks in range yet. The plan lists one. It needs blocks 8.3.0 on npm first, so it's a follow-up. Until then, the rule (CLAUDE.md) is to raise every peer floor together when a package starts using a newer blocks API, and sync-versions.mjs refuses a floor above the release.
  • npm publish prints "repository.url was normalized" for every package, as it already does today. It's harmless.

https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig

Main refresh (2026-10-17, 6ce49dcf)

  • Merged P5 73b9b98d into 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:check pass. 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_016ZsmzpZ2zahRBoATBb2tBu