Skip to content

usertour

v0.7.4 Breaking

This release includes 2 breaking changes for platform teams planning a safe upgrade.

✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →

✓ No known CVEs patched in this version

Topics

announcements appcues chameleon checklist in-app nps
+8 more
onboarding pendo surveys tooltips tour userflow userpilot walkme

Affected surfaces

auth rbac

ReleasePort's take

Light signal
editorial:auto 13d

Release v0.7.4 introduces a unified PlanFeatures matrix that centralizes subscription overrides and feature gating across server logic, UI components, and pricing pages.

Why it matters: Adopt the new Subscription.overridePlan API for consistent per‑customer grants; update services using ProjectsService checks to align with the shared matrix before your next deployment cycle.

Summary

AI summary

Unified plan-features matrix enables Subscription.overridePlan across server enforcement and pricing UI.

Changes in this release

Feature Medium

Introduces shared plan-features matrix across server and UI.

Introduces shared plan-features matrix across server and UI.

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

New PlanFeatures type in @usertour/types with comprehensive per-tier gates.

New PlanFeatures type in @usertour/types with comprehensive per-tier gates.

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

Shared PLAN_FEATURES matrix in @usertour-packages/constants/billing covers all current product gates.

Shared PLAN_FEATURES matrix in @usertour-packages/constants/billing covers all current product gates.

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

resolvePlanFeatures, parseOverridePlan, and isWithinLimit helpers now run on server and web.

resolvePlanFeatures, parseOverridePlan, and isWithinLimit helpers now run on server and web.

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

Subscription.overridePlan becomes canonical layer for per-customer grants.

Subscription.overridePlan becomes canonical layer for per-customer grants.

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

EnvironmentsService.create now delegates environment limit checks to ProjectsService.

EnvironmentsService.create now delegates environment limit checks to ProjectsService.

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

New errors TeamMemberAlreadyInvitedError and TeamMemberAlreadyInProjectError added for invite deduplication.

New errors TeamMemberAlreadyInvitedError and TeamMemberAlreadyInProjectError added for invite deduplication.

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

SubscriptionContext now exposes effective PlanFeatures to consumers.

SubscriptionContext now exposes effective PlanFeatures to consumers.

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

PlanType enum gains ENTERPRISE for self-hosted license path.

PlanType enum gains ENTERPRISE for self-hosted license path.

Source: llm_adapter@2026-05-21

Confidence: low

Feature Medium

Unlimited sessions render as 123 / Unlimited with hidden caption for unbounded caps.

Unlimited sessions render as 123 / Unlimited with hidden caption for unbounded caps.

Source: llm_adapter@2026-05-21

Confidence: low

Feature Medium

use-plan-limits web hook provides environment, team member, and sessions limit utilities with override support.

use-plan-limits web hook provides environment, team member, and sessions limit utilities with override support.

Source: llm_adapter@2026-05-21

Confidence: low

Feature Medium

Pricing page comparison table aligns with usertour.io marketing copy.

Pricing page comparison table aligns with usertour.io marketing copy.

Source: llm_adapter@2026-05-21

Confidence: low

Feature Medium

Per-customer overrides surface only on user's current plan card.

Per-customer overrides surface only on user's current plan card.

Source: llm_adapter@2026-05-21

Confidence: low

Feature Medium

Extends PlanType enum with ENTERPRISE value for self‑hosted licensing.

Extends PlanType enum with ENTERPRISE value for self‑hosted licensing.

Source: granite4.1:30b@2026-05-23-audit

Confidence: low

Feature Medium

Adds useEnvironmentLimit, useTeamMemberLimit, and useSessionsLimit hooks in web with override support.

Adds useEnvironmentLimit, useTeamMemberLimit, and useSessionsLimit hooks in web with override support.

Source: granite4.1:30b@2026-05-23-audit

Confidence: low

Feature Low

Displays unlimited sessions as "Unlimited" instead of "Infinity" and hides threshold caption when unbounded.

Displays unlimited sessions as "Unlimited" instead of "Infinity" and hides threshold caption when unbounded.

Source: granite4.1:30b@2026-05-23-audit

Confidence: low

Bugfix Medium

TeamService.inviteTeamMember gate now uses unified ProjectsService check, removing duplicate logic.

TeamService.inviteTeamMember gate now uses unified ProjectsService check, removing duplicate logic.

Source: llm_adapter@2026-05-21

Confidence: high

Bugfix Medium

Invite mutation now soft-deletes failed invites and surfaces InvitationDeliveryFailedError.

Invite mutation now soft-deletes failed invites and surfaces InvitationDeliveryFailedError.

Source: llm_adapter@2026-05-21

Confidence: low

Bugfix Medium

Soft‑deletes failed invites and returns InvitationDeliveryFailedError on email delivery failure.

Soft‑deletes failed invites and returns InvitationDeliveryFailedError on email delivery failure.

Source: granite4.1:30b@2026-05-23-audit

Confidence: low

Refactor Medium

ProjectsService gains resolveProjectFeatures, checkEnvironmentLimit, and checkTeamMemberLimit.

ProjectsService gains resolveProjectFeatures, checkEnvironmentLimit, and checkTeamMemberLimit.

Source: llm_adapter@2026-05-21

Confidence: high

Refactor Medium

Drift-prone rows in pricing table now use matrix-driven typed helper instead of hard-coded values.

Drift-prone rows in pricing table now use matrix-driven typed helper instead of hard-coded values.

Source: llm_adapter@2026-05-21

Confidence: low

Refactor Medium

@usertour-packages/constants promoted from P1 to P2 package type.

@usertour-packages/constants promoted from P1 to P2 package type.

Source: llm_adapter@2026-05-21

Confidence: low

Refactor Medium

Pricing page icons switched from lucide-react to remix icons.

Pricing page icons switched from lucide-react to remix icons.

Source: llm_adapter@2026-05-21

Confidence: low

Refactor Low

Replaces hard‑coded rows in pricing table with matrixRow typed helper.

Replaces hard‑coded rows in pricing table with matrixRow typed helper.

Source: granite4.1:30b@2026-05-23-audit

Confidence: low

Full changelog

This release lands a shared plan-features matrix that finally unifies how the server enforces plan gates and how the pricing page presents
them — Subscription.overridePlan lights up across the stack so per-customer CS grants take effect everywhere instead of just one layer. The
team-invite path also gains long-missing safety nets, and the pricing settings page aligns with usertour.io's marketing copy.

What's Changed

🧱 Plan features matrix (cross-cutting infrastructure)

  • New PlanFeatures type in @usertour/types and a shared PLAN_FEATURES matrix in @usertour-packages/constants/billing that covers every
    per-tier value the product gates on today: removeBranding, sessionsLimit, teamMemberLimit, environmentLimit, dataRetentionYears,
    apiRateLimit, plus placeholders for upcoming auditLogs / ssoSaml / ssoOidc gates.
  • resolvePlanFeatures(planType, overridePlan), parseOverridePlan, and isWithinLimit helpers live in @usertour/helpers and run on both server
    and web — the same merged feature set drives runtime enforcement and UI rendering.
  • Subscription.overridePlan (the JSONB column that was wired up but unread) is now the canonical layer for per-customer grants. Override
    fields replace base fields via spread, so a CS-granted seat bump or a legacy benefit ({"removeBranding": true} for grandfathered Starter
    projects) lands in resolution without any code change.
  • @usertour-packages/constants was promoted from P1 (source-direct) to P2 (pre-built dist) since the NestJS server now requires it at
    runtime. The P1 → P2 migration steps are codified in docs/architecture/packages.md.
  • PlanType enum gains ENTERPRISE so the self-hosted license path stops using bare strings; the two existing Record<PlanType, …> maps on the
    web pick up the new key.

🛡 Server-side quota enforcement

  • ProjectsService gains resolveProjectFeatures, checkEnvironmentLimit, and checkTeamMemberLimit. The cloud / self-hosted bypass, subscription
    lookup, override merge, count query, and error throw all live in one place and accept a Prisma TransactionClient.
  • EnvironmentsService.create was previously gated only by the client — useEnvironmentLimit disabled the form button but any consumer that
    bypassed the form (stale UI, direct mutation call) could create past the cap. The mutation now delegates to
    projectsService.checkEnvironmentLimit inside its own transaction. New EnvironmentLimitError (E0030) with en/zh messages.
  • TeamService.inviteTeamMember's hand-rolled hobby/starter/growth if-chain is gone; the gate now goes through
    projectsService.checkTeamMemberLimit, which honours overridePlan the same way the client does. The roughly 230-line dup between
    projects.service and web-socket.service for cloud / self-hosted config resolution also collapses — web-socket.service.getConfig delegates to
    projects.

📧 Team invite hardening

  • The same email used to accumulate multiple Invite pending rows on the team settings page because there was no dedup before
    prisma.invite.create. Each row counted against the seat quota, letting a project artificially fill its allotment by inviting one address
    twice. Two new errors register pre-create: TeamMemberAlreadyInvitedError (E0031) and TeamMemberAlreadyInProjectError (E0032).
  • When the SMTP layer rejected the recipient (e.g. 550 / EENVELOPE for a bad mailbox), the freshly created invite row stayed in the DB while
    the client got a generic 500 and the new dedup check blocked the user from retrying. The mutation now wraps sendInviteEmail in a try / catch
    — failure soft-deletes the invite (matching the cancel / accept lifecycle) and surfaces InvitationDeliveryFailedError (E0033).

🎨 Pricing page polish

  • Comparison table aligns with usertour.io: Support & service section becomes Community / Email / Priority with priority gated to Business
    only, the ghost Concierge support row is gone, and the Growth card's redundant Live chat support line drops back to Email support.
  • Every drift-prone row (Sessions / Data Retention / Environments / API rate / Team members / No-branding) is now matrix-driven via a typed
    matrixRow helper instead of a hardcoded values: [false, true, true, true] literal.
  • Per-customer override surfaces only on the user's current plan card and current plan column. Other cards stay base so a CS-granted sessions
    bump on Growth doesn't make the Starter or Business columns claim the same number. The comparison table stays apples-to-apples for upgrade
    decisions while honouring the user's actual entitlement on their own row.
  • Pricing page icons switch from lucide-react + a custom BoxIcon to the project's standard remix icons (@usertour-packages/icons).
  • Unlimited sessions render as 123 / Unlimited instead of 123 / Infinity; the percent / threshold caption hides when the cap is unbounded.

🪝 Quota hooks on the web

  • SubscriptionContext now exposes effective features: PlanFeatures so consumers stop re-resolving themselves.
  • apps/web/src/hooks/use-plan-limits.ts adds useEnvironmentLimit, useTeamMemberLimit, and useSessionsLimit. Each hook composes the
    subscription features, the relevant resource list, and the self-hosted bypass into a single { limit, current, canUseMore } shape. Consumers
    like EnvironmentCreateForm and MemberInviteDialog shrink to one line and now honour override on the client side too — the server check the
    hook mirrors used to be the only place that saw the override.

📘 Architecture doctrine

  • docs/architecture/packages.md gets a @usertour/types vs @usertour-packages/constants split (contracts vs values, with the "delete-the-line,
    what breaks?" heuristic), a P1 / P2 package-shape section with the 6-step migration checklist that this branch's constants promotion
    follows, and a note that runtime values should share as soon as a second real consumer exists — including code that copies the same business
    contract values without importing them.

Full Changelog: https://github.com/usertour/usertour/compare/v0.7.3...v0.7.4

Breaking Changes

  • @usertour-packages/constants promoted from P1 (source‑direct) to P2 (pre‑built dist); migration steps documented in docs/architecture/packages.md.
  • PlanType enum now includes ENTERPRISE; existing code using bare strings for self‑hosted license path must be updated.

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track usertour

Get notified when new releases ship.

Sign up free

About usertour

Usertour is an open-source user onboarding platform. It allows you to create in-app product tours, checklists, and surveys in minutes—effortlessly and with full control.The open-source alternative to Userflow and Appcues

All releases →

Related context

Earlier breaking changes

  • v0.8.3 Removes the insecure 'development-key-not-secure' fallback for ENCRYPTION_KEY; operators must set the env var explicitly.
  • v0.7.5 All 60 workspace packages move to @usertour/* from @usertour-packages/*

Beta — feedback welcome: [email protected]