Skip to content

v3.0.0

Latest

Choose a tag to compare

@PierreBrisorgueil PierreBrisorgueil released this 21 Oct 17:48
· 13 commits to master since this release

3.0.0 (2026-10-21)

  • feat(billing)!: declare pricing tabs in config, each carrying its own content (#4654) (e04ef23)

Bug Fixes

  • analytics: gate attribution capture on posthog config + stack-update ordering note (#4584) (9e61b85), closes #4024
  • app: keep the requested page across sign-in (#4725) (713b464)
  • auth: identify the signed-in user again on session restore (#4752) (#4755) (1447360)
  • auth: re-fetch serverConfig after in-SPA signin/signup (#4539) (1cb1c98), closes Node#3566 #4261 #4538
  • auth: signout resets serverConfig to null (#4541) (2c02fac), closes #4539 #4540 #4459
  • auth: stop signin auth-watcher from racing navigation past validate() (#4534) (0addeb9), closes #4528 #4533 #4533
  • auth: stop signup auth-watcher from racing past email verification (#4528) (cbf4947), closes #4437
  • billing: #4654 follow-ups (packs empty-state, stale comments, migration note) (#4674) (62eaedc)
  • billing: compute gauge and weekly meter go quota-only, matching quota-threshold alerts (#4550) (7d881f6), closes #4548
  • billing: guest plan CTAs go to sign-up and return to pricing (#4678) (cf553ad)
  • billing: move the zero-quota overage guard into BillingMeterProgressComponent (#4554) (7effea0), closes #4553
  • billing: render pending cancellation on the subscriptions panel (#4551) (1318444)
  • config: derive the apiBase() test expectation from the loaded config (#4690) (53e064f), closes #4689
  • home: replace real third-party brand defaults with RFC-2606 placeholders (#4623) (b0de8a5), closes Vue#4340
  • modules: warn on config.modules.* keys that match no registered module (#4529) (1ab89c4), closes #4480 #4480

Features

BREAKING CHANGES

  • the top-level plans/packs static-content collections and the
    legacy tabs: { plans, units } label object are removed, with no compatibility
    bridge. A downstream project moves its plans and packs into tabs[] entries in
    the same pass that absorbs this change, and sets annualToggle: true on each
    tab billed annually. See MIGRATIONS.md.

Claude-Session: https://claude.ai/code/session_01VAeEZbYQnihV4gcnGFnc6L

  • fix(billing): warn when tabs config is provided but unusable

normalizeTabs() silently fell back to the devkit demo catalogue
whenever the resolved tabs value was not a non-empty array, with no
signal — unlike the neighbouring both-slots-filled case, which
already warns. A downstream consumer still on the pre-migration
tabs: { plans, units } object shape would render demo plans and
packs on a live pricing page with nothing telling them why.

Thread whether the tabs key was explicitly present in config (vs.
simply absent) into normalizeTabs so it can warn only on an
explicit-but-unusable value, keeping the default-install path silent.

Claude-Session: https://claude.ai/code/session_01VAeEZbYQnihV4gcnGFnc6L

  • fix(billing): gate pricing and checkout on the active tab's annual option

Apply the pre-merge review findings on the pricing tabs change.

  • effectiveAnnual now gates every annual-dependent decision: the displayed
    amount, the /year period, the savings chip, and the price id that reaches
    checkout. A tab declaring annualToggle: false could previously still send
    an annual price id to checkout while rendering a disabled toggle.
  • resolvePlanPricing lowercases both sides of the case-insensitive Stripe
    name fallback, so a static id like Pro matches a Stripe name PRO instead
    of silently losing its price objects and leaving checkout unavailable.
  • JSDoc headers on the modified computeds.

Claude-Session: https://claude.ai/code/session_01VAeEZbYQnihV4gcnGFnc6L

  • refactor(billing): name the Stripe plan match predicate

The match logic passed to candidates.find() moves into a named
matchesStaticPlan() helper carrying a full JSDoc header, leaving a
one-line callback at the call site.

Review feedback asked for a JSDoc header on the inline predicate. The
repository documents named functions rather than inline callbacks (7 of
459 array callbacks in src/ carry one), and toAmount() sits directly
above as that same pattern, so extracting satisfies the guideline in the
codebase's own idiom instead of introducing an outlier.

Pure refactor: no behaviour change, 2755/2755 unit tests unchanged.

Claude-Session: https://claude.ai/code/session_01VAeEZbYQnihV4gcnGFnc6L