Skip to content

chore: upgrade Node 22 / Next 16 / Tailwind 4 / Biome 2 / TS 6 / Zod 4 / Better Auth#1

Open
notsuhas wants to merge 9 commits into
rently-com:mainfrom
notsuhas:chore/upgrade
Open

chore: upgrade Node 22 / Next 16 / Tailwind 4 / Biome 2 / TS 6 / Zod 4 / Better Auth#1
notsuhas wants to merge 9 commits into
rently-com:mainfrom
notsuhas:chore/upgrade

Conversation

@notsuhas

@notsuhas notsuhas commented Apr 25, 2026

Copy link
Copy Markdown

Summary

Brings react_native_ota_updater up to a modern toolchain — Node 22, Next.js 16, Tailwind v4, Biome 2, TypeScript 6, Zod 4, Better Auth — and bumps every workspace dependency. Nine logically scoped commits. Workspace packages are bumped to v3.0.0 (major) in commit 9 via Changesets.

Commits in order

# SHA Title Files
1 7fb6082 chore: upgrade Node.js to 22, pnpm 10.33.x, and bump workspace dep versions 17
2 98a312b feat: migrate auth from next-auth to better-auth + drop Zustand path-store 32
3 6adb2aa chore: migrate to Next.js 16 + Tailwind v4 + Biome 2 39
4 03827e2 chore: migrate to TypeScript 6 + Zod 4 16
5 01f6cff chore: adapt source to upgraded library APIs 78
6 862817d refactor(seed): use returning() for platform inserts and build nested app→platform structure 1
7 8e35002 chore: set Biome as default formatter for TypeScript / TSX in .vscode/settings.json 1
8 e7c805f docs(env): fix GitHub OAuth callback URL hint in .env.example 1
9 d25c494 chore: release v3.0.0 + refresh README for upgraded stack 22

Total: 197 files changed, +11111 / -8296.

What's done

  • Toolchain: Node 22, pnpm 10.33.2, Turbo 2.9.x, TypeScript 6, Biome 2.
  • Frameworks: Next.js 15.5 → 16.2, Tailwind CSS 3.4 → 4.2 (CSS-first config, no tailwind.config.ts), middleware.tsproxy.ts (Next 16 rename).
  • Auth: next-auth@5.0.0-beta → Better Auth across the auth package, ensure-auth middleware, web auth routes/login pages/providers, and the DB schema (account, session, user, new verification); ships migration 0002_better_auth.sql. AUTH_ALLOWED_DOMAIN enforcement moved from hooks.before (silently null on OAuth /callback) to databaseHooks.user.create.before + databaseHooks.session.create.before.
  • Path-store removal: AppStoreProvider / AppStoreFragmentProvider + per-segment templates replaced with a thin useCodePushRouteParams hook. Query factories in codepush-queries.ts now take explicit { appName, platform, … } args instead of calling hooks internally.
  • Schema/runtime upgrades: Zod 3.25 → 4.3 across schemas + consumers; Drizzle ORM bump; Hono / @hono/node-server v2 + @scalar/hono-api-reference 0.10; oclif refresh; mocha 11 + chai 6; sonner 2; lucide-react 1; pino 10; faker 10; aws-sdk 3.1035; @alwatr/parse-duration 9; conf 15 / which 6 / shx 0.4.
  • Lint hygiene: biome check --write across the workspace, Biome v2 schema in biome.json, regenerated components.json, refreshed Drizzle metadata snapshots.
  • Seed: capture inserted platform rows via .returning() and group them under their app instead of re-querying.

Verification done locally

  • pnpm install --frozen-lockfile
  • pnpm typecheck — 14/14 turbo tasks green
  • pnpm dlx @biomejs/biome@2 check — clean
  • pnpm build:next — 7/7 turbo tasks, all routes compiled

What's NOT done

  • 🚧 No DB migration applied. 0002_better_auth.sql ships in the diff but has not been run against any database. Plan a downtime/maintenance window to apply it (it adds the verification table and reshapes account/session/user).
  • 🚧 turbo.json globalEnv cleanup in commit 5 may be too aggressive for environments that read additional vars at build time — diff that file with care.

Reviewer instructions — running this branch locally

Pre-requisites

  • Node 22 (.nvmrc is 22; nvm use will pick it up).
  • pnpm 10.33.2 (corepack reads packageManager from root package.json; run corepack enable once if you haven't).
  • Postgres + Redis running. The repo ships start-services.sh for Docker.
  • A fresh DB backup before step 4 — the auth migration is destructive.

Steps

  1. Pull and switch Node

    git fetch origin chore/upgrade && git checkout chore/upgrade
    nvm use
    corepack enable
    
  2. Install

    pnpm install --frozen-lockfile
    
  3. Static checks (must pass before touching the DB)

    pnpm typecheck
    pnpm dlx @biomejs/biome@2 check
    pnpm build:next
    
  4. Back up the database
    0002_better_auth.sql reshapes account / session / user and adds verification. Do not skip.

    pg_dump "$DATABASE_URL" > backup-pre-better-auth.sql
    
  5. Apply the migration

    pnpm db:migrate
    

    Inspect internal/db/drizzle/0002_better_auth.sql first if you want to see the exact DDL.

  6. Env / OAuth callback check
    Better Auth reuses the same env vars as the previous next-auth setup (AUTH_URL, AUTH_SECRET, AUTH_GITHUB_ID, AUTH_GITHUB_SECRET, AUTH_ALLOWED_DOMAIN) — nothing to add or rotate. The .env.example hint comment for the GitHub OAuth Authorization callback URL has been corrected (commit 8) to http://localhost:3000/api/auth/callback/github; if your existing GitHub OAuth app was set to just …/api/auth, update it on github.com/settings/developers to match.

  7. Run dev

    pnpm dev
    
  8. Smoke test in this order (auth is the riskiest change — verify it first)

    • Login flow: /login → "Continue with GitHub" → callback → land on /codepush. UserNav shows the user. An email outside AUTH_ALLOWED_DOMAIN produces a redirect-style failure (not a silent null).
    • Sign-out + sign-in again: confirms databaseHooks.session.create.before doesn't block returning users.
    • Apps: /codepush lists apps; "Add app" creates one (validates the Drizzle .returning() path from commit 6).
    • Deployments + releases: open an app → platform → release table. Confirm sonner toasts and lucide icons render (commit 5 surface).
    • CLI: cd packages/cli && pnpm link:cli && rnota --help to verify the oclif wiring (commit 5).

If something breaks

  • Migration fails partway → restore the backup (psql "$DATABASE_URL" < backup-pre-better-auth.sql), open a comment with the error, do NOT hand-patch tables. The schema reshape is interdependent.
  • OAuth lands on /api/auth/error → callback URL on the GitHub app doesn't match <AUTH_URL>callback/github. Fix it on github.com, not in code.
  • AUTH_SECRET errors from Better Auth → set it: openssl rand -base64 32.
  • Typecheck fails on first run → re-run pnpm install --frozen-lockfile; node_modules can desync after the lockfile bump.

Rolling back (code + DB together)

git checkout main
psql "$DATABASE_URL" < backup-pre-better-auth.sql
pnpm install --frozen-lockfile

Migration prompt — for porting these changes onto a private/internal fork

The Rently production codebase is a private fork that has diverged from the public OSS repo. It cannot directly merge this PR. Use the prompt below to point an LLM (Claude Code / Cursor / Codex) at this PR and have it apply the equivalent changes to a target repo.

Copy-paste prompt:

You are migrating a clean dependency/framework upgrade from this PR
(<paste-this-PR-url>) onto a private fork of the same project that has
diverged from upstream `react_native_ota_updater`.

Source PR: 9 commits, +11111/-8296 across 183 files. Reachable from the
PR's `chore/upgrade` branch. Each commit is a self-contained topic:
  1. Node 22 + pnpm 10.33 + dep version bumps         (package.json + lockfile)
  2. better-auth migration + Zustand path-store drop  (auth + db schema + web app)
  3. Next.js 16 + Tailwind v4 + Biome 2               (config + UI + biome reformat)
  4. TypeScript 6 + Zod 4                             (tsconfig + Zod schemas)
  5. Library API surface adaptation                   (Hono v2, oclif, sonner, lucide, pino, faker, aws-sdk, mocha/chai)
  6. Seed restructure                                 (Drizzle .returning())
  7. VSCode default formatter → Biome                 (.vscode/settings.json)
  8. .env.example OAuth callback URL fix              (docs only)
  9. Changesets release to v3.0.0 + README refresh    (CHANGELOGs + version bumps)

Goals
  G1. Land the equivalent of every commit on the target repo, in order,
      preserving the topical split if at all possible.
  G2. Reconcile divergences. The private fork likely has additional
      features, schema columns, route handlers, CLI commands, or
      branding-coupled lines that don't exist upstream. For each
      hunk in the source PR, decide:
        - apply verbatim,
        - apply with adaptation (e.g., extra Drizzle columns the fork
          added must be preserved when porting `account`/`session`/
          `user`/`verification` schema changes),
        - or skip (when upstream rewrote a file the fork has
          deliberately replaced).
  G3. Re-run dep installation and code generation in the target repo
      (pnpm install, drizzle-kit generate if needed). The lockfile in
      this PR is generated against upstream's package.json shape; the
      target repo's lockfile must be regenerated from its own
      package.json shape after the version bumps land.
  G4. Validate: `pnpm typecheck`, `pnpm dlx @biomejs/biome@2 check`,
      `pnpm build:next`, plus whatever build/test scripts the fork has
      that aren't in upstream.

Hard rules
  R1. Cherry-picking blindly will fail. Read each commit, then port
      content semantically into the fork's existing files.
  R2. The Better Auth migration (commit 2) is the riskiest. The fork
      may have:
        - extra columns on `user` / `account` / `session`,
        - additional auth flows (e.g., LDAP, SAML, custom token issuer),
        - existing custom routes under `/api/auth/**`,
        - middleware / RBAC layers wrapping `ensureAuth`.
      Surface every divergence as a question/PR-comment before
      writing code. Do NOT silently drop fork-specific auth logic.
  R3. The migration `0002_better_auth.sql` is destructive on
      production data (table reshape). Do NOT auto-apply. Generate the
      fork-specific migration based on the fork's current schema +
      Better Auth's required shape, not by literally copying
      `0002_better_auth.sql`.
  R4. The Zustand path-store drop (in commit 2) flips
      `codepush-queries.ts` factory signatures from "call-the-hook"
      to "explicit-args". If the fork has additional consumers of
      these hooks/queries, every callsite must be updated.
  R5. Biome 2 will reformat lots of files. Apply
      `biome check --write` once per logical commit so reformat
      churn lives with its topic, not bundled.
  R6. ONE GOAL AT A TIME. Complete exactly one of the 9 commits,
      then STOP and wait for explicit human approval before starting
      the next. Do not chain commits autonomously. The whole point of
      the per-commit split is reviewability — bundling all 9 into a
      single run defeats it and makes divergences impossible to audit.
      If unsure whether a hunk applies, ask before writing code.

Suggested working order
  Step 1: Branch off the fork's `main` as `chore/upgrade-from-oss`.
  Step 2: For each of the 9 source commits, in order:
            a. read the source PR commit + diff,
            b. port the equivalent change into the fork's tree,
            c. run `pnpm install` if dep-related,
            d. run `pnpm typecheck` and resolve type errors before
               moving to the next commit,
            e. commit with the same topical message + a footnote of
               adaptations made,
            f. STOP. Post the commit (or push a draft PR / comment)
               and wait for human review + explicit approval before
               starting the next commit. Do not auto-advance.
  Step 3: After all 9 commits are individually approved, run the full
          validation set (typecheck, biome, build). Open the PR onto
          the fork's `main`.

Deliverables
  D1. A `chore/upgrade-from-oss` branch on the fork with 9 commits.
  D2. A PR description listing every divergence the fork required
      vs. the upstream PR, so reviewers don't have to diff manually.
  D3. A separate proposed migration script for `0002_better_auth.sql`
      that's correct against the fork's current schema (NOT the
      upstream-shaped one in this PR).

Out of scope for the LLM
  O1. Do not deploy. Do not run db migrations. Do not push to a shared
      remote without human review.
  O2. Do not modify branding, env keys, or CI files in the fork.
  O3. Do not "clean up" comments / TODOs that aren't in the source PR's
      diff — the fork owns its own code-style decisions.

🤖 Generated with Claude Code

…rsions

Move every workspace's package.json + the root lockfile to the upgraded
baseline (Node 22, pnpm 10.33.2, Turbo 2.9.x, TypeScript 6, Zod 4,
Better Auth, Next 16, Tailwind 4, Biome 2, Drizzle, Hono v2, sonner v2,
lucide-react v1, faker 10, pino 10, aws-sdk 3.1035, etc.). Source code
is migrated in subsequent commits in this PR; this commit is the
specifications + lockfile.
…store

Replaces the next-auth v5 beta wiring with Better Auth across the
auth package, the API ensure-auth middleware, the web app's auth
routes/login pages/providers, and the DB schema (account, session,
user, verification). Adds the 0002_better_auth.sql Drizzle migration.

Also drops the Zustand path-store: per-segment templates +
AppStoreProvider/AppStoreFragmentProvider are replaced with a thin
useCodePushRouteParams hook reading from next/navigation, and query
factories in codepush-queries.ts now take explicit { appName,
platform, ... } args instead of calling hooks internally.

AUTH_ALLOWED_DOMAIN enforcement moves from a silent hooks.before on
/callback (where ctx.context.newSession is null and ctx.body.email is
absent for OAuth) to databaseHooks.user.create.before for new sign-ups
and databaseHooks.session.create.before for returning logins.
- Next 16: rename middleware.ts → proxy.ts (Next 16 renamed the public
  hook), refresh next.config.ts.
- Tailwind v4: drop tailwind.config.ts, switch to CSS-first config in
  globals.css + postcss.config.mjs, regenerate components.json.
- Biome 2: bump biome.json to v2 schema, run biome check --write across
  the workspace (UI components, drizzle metadata snapshots,
  .changeset/config, .vscode/settings).
- Drop the Suspense template wrappers under (main)/codepush/** that
  the Zustand store-fragment cleanup made unnecessary.
- Bump every workspace tsconfig.json to the new TS 6 base.
- Migrate Zod 3.25 → 4.3 across packages/api/src/schemas/** and the
  consumers (openapi schemas, types.ts, web add-app + collaborators
  forms). Switch to the new error-map / refinement / inference shapes.
Catches up the source code with the bumped runtime libraries:
- Hono / @hono/node-server 2 + @scalar/hono-api-reference 0.10:
  refresh packages/api routes, lib/create/app, openapi config,
  middlewares, and the apps/web …/route.ts handler.
- oclif CLI stack: regenerate packages/cli/bin/{dev,run}, retarget
  commands and common base classes, and refresh the README index.
- mocha 11 + chai 6: rewrite the hello-world tests, switch
  .mocharc.json to the new loader.
- sonner 2 + lucide-react 1: update Toaster wiring + icon imports
  across the web UI.
- pino 10, faker 10, aws-sdk 3.1035, @alwatr/parse-duration 9, conf
  15 / which 6 / shx 0.4: small touch-ups in db/manager, scripts,
  cli services, and shared/api-client.
- Drop the redundant turbo.json globalEnv entries the bump exposed.
… app→platform structure

Capture the inserted platform rows and group them under their app
instead of re-querying schema.codepush_platform downstream. Avoids
re-pulling stale rows from prior seed runs and keeps the
unique-constraint contract clean during the deployment-insertion step.
The hint comment said `http://localhost:3000/api/auth` but the
actual callback path under both the previous next-auth setup and
Better Auth is `/api/auth/callback/github`. Anyone copying the
hint verbatim into their GitHub OAuth app would land on the wrong
callback and see an OAuth error after the migration.
- Apply changeset: bump every workspace package from 2.0.0 to 3.0.0
  in lockstep (fixed group: @rentlydev/rnota-*).
- Sync pnpm-lock.yaml with the new version refs.
- README: node badge 18 → 22, 'Auth.js' → 'Better Auth', 'Next.js 15'
  → 'Next.js 16'.
@notsuhas

Copy link
Copy Markdown
Author

cc: @rahulmr-rently @prasad-rently

Comment thread internal/auth/src/auth.ts
console.warn(
`Blocking sign-in for ${owner.email} — does not match AUTH_ALLOWED_DOMAIN=${env.AUTH_ALLOWED_DOMAIN}`,
);
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return false here relies on Better Auth's internal control flow converting a falsy return into a null session → error redirect. This isn't documented in their public API. If Better Auth changes how hook return values are handled in a patch release, blocked users could silently get through. Add an integration test that verifies a non-matching domain user cannot obtain a session.

@notsuhas notsuhas May 25, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is documented: https://better-auth.com/docs/concepts/database#1-before-hook

I don't think Better Auth is going to make such a change and put it as non-breaking. Even if they did, on upgrade, TS compilation would fail.

"lib": ["ES2022", "DOM", "DOM.Iterable"],
"types": ["node"],
"allowJs": true,
"ignoreDeprecations": "6.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for the initial TS 6 migration, but this suppresses deprecation warnings for esModuleInterop (line 4), resolveJsonModule (line 11), and forceConsistentCasingInFileNames (line 19). Consider opening a tracking issue to remove these deprecated options in a follow-up so this doesn't become permanent tech debt.

@notsuhas notsuhas May 25, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three options you named (esModuleInterop, resolveJsonModule, forceConsistentCasingInFileNames) aren't deprecated in TS 6 — they're stable and have no deprecation notice. The flags actually targeted by ignoreDeprecations: "6.0" are charset, target: "ES3", importsNotUsedAsValues, preserveValueImports, noImplicitUseStrict, noStrictGenericChecks, keyofStringsOnly, suppressExcessPropertyErrors, suppressImplicitAnyIndexErrors, out, and prepend.

Also grepped every tsconfig in the repo (base + internal-package + the 9 consumers) and none of them set any of those.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants