Skip to content

v5.0.0

Choose a tag to compare

@Tobbe Tobbe released this 10 Jul 16:53

Release Notes

This release brings two exciting new features:

  • (Flagged) support for pnpm and npm
  • (Flagged) support for modern serverless functions on Netlify and Vercel (including support for Vercel Fluid Compute)

Unflagged support will come in a future major version.

I still need community help with testing those features, so they're both behind flags. Use pnpm create cedar-app@5.0.0 --pm pnpm to set up a new project that uses pnpm. And use yarn cedar build --ud, yarn cedar dev --ud etc to use the new universal-deploy support in Cedar which is what enables the modern functions support on Netlify and Vercel.

If you do end up trying universal-deploy out, you should write your serverless functions like this:

export async function handleRequest(request: Request) {
  const url = new URL(request.url)

  return new Response(JSON.stringify({ data: 'Hello from Cedar', url }), {
    status: 200,
    headers: { 'Content-Type': 'application/json' },
  })
}

From that, Cedar will generate a fetchable when deploying.

I'll make the create-cedar-app script auto-detect the package manager in a future release. So if you invoke it with pnpm, it'll use pnpm when creating your new app. And if you invoke it with npx or npm it'll use npm.
And I'll also make universal-deploy the default way to deploy Cedar apps (or, if Vite lands internal support for this, I'll use that instead (see vitejs/vite#20907 if you're interested in Vite's support for this)

Breaking Changes

index.html moved to web/index.html

In an effort to align with Vite standards the web index file has moved from web/src/index.html to web/index.html

Note for users with customizations in web/src/index.html: Moving the file to web/index.html might require updating relative paths inside the file (e.g. favicon, fonts) that referenced files relative to web/src/ to be relative to web/ instead.

See more at #1799

Custom generator templates path removed

The deprecated api/generators/ and web/generators/ directories for custom generator templates are no longer supported. This was deprecated in v2.3.0.

Move any custom templates to the root generatorTemplates/ directory instead:

  • api/generators/<generator>/<template>generatorTemplates/api/<generator>/<template>
  • web/generators/<generator>/<template>generatorTemplates/web/<generator>/<template>

RWJS_DELAY_RESTART removed

The RWJS_DELAY_RESTART environment variable has been removed. It was renamed to CEDAR_DELAY_API_RESTART in a previous release. If you still have RWJS_DELAY_RESTART in your .env file, rename it to CEDAR_DELAY_API_RESTART.

cedar-gen

This bin was renamed from rw-gen to cedar-gen

(There was also an internal rename of REDWOOD_ENV_FILES_LOADED to CEDAR_ENV_FILES_LOADED)

Baremetal

If you have baremetal deployments set up in an automated workflow you should know that it will now show an interactive warning if it detected unpushed commits. See #1904

.cedar/

This isn't breaking, but I wanted to call it out anyway, and recommend you update your own projects too

Cedar apps now default to a top level .cedar/ directory for generated types, GraphQL schema, and other transitory data

With both a cedar.toml file and a .cedar/ directory it should be much more clear to those working on the app that it's a Cedar app and nothing else.

ctx.query is now a URLSearchParams

Cedar's handleRequest function receives ctx.query as a standard web-platform URLSearchParams instead of a flat Record<string, string>. This gives you multi-value support, iteration, and serialisation for free — no extra dependencies needed.

(NOTE: This is only breaking for those of you who have already started using the new handleRequest style functions. If you're still using Lambda style handlers, you don't have to do anything.)

Migrating from the legacy handler

Legacy handler functions receive event.queryStringParameters parsed by
picoquery, which expands bracket notation into arrays and nested objects. The
table below shows the equivalent using ctx.query.

Raw query string Legacy handler Cedar handle
?page=1 event.queryStringParameters.page'1' ctx.query.get('page')'1'
?tag=cedar&tag=framework event.queryStringParameters.tag['cedar', 'framework'] ctx.query.getAll('tag')['cedar', 'framework']
?ids[]=1&ids[]=2 event.queryStringParameters.ids['1', '2'] ctx.query.getAll('ids[]')['1', '2']
?user[name]=alice event.queryStringParameters.user.name'alice' ctx.query.get('user[name]')'alice'

The key difference with bracket notation is that URLSearchParams preserves the
raw key name literally — ids[]=1 is stored under the key 'ids[]', not 'ids'.
For the common case of plain flat parameters the migration is a straightforward
swap: event.queryStringParameters.fooctx.query.get('foo').

Not ready to migrate? Use picoquery directly

If you rely heavily on bracket-notation parsing and can't migrate yet, you can
reproduce the legacy behaviour inside a handle function by parsing request.url
with picoquery yourself:

import { parse } from 'picoquery'

export const handle = async (request, ctx) => {
  const rawSearch = new URL(request.url).search
  const query = parse(rawSearch ? rawSearch.slice(1) : '', {
    nestingSyntax: 'index',
    arrayRepeat: true,
    arrayRepeatSyntax: 'bracket',
  })

  // query.ids        → ['1', '2']   for ?ids[]=1&ids[]=2
  // query.user.name  → 'alice'      for ?user[name]=alice
  // query.page       → '1'          for ?page=1
}

Deprecations

RedwoodGraphQLError has been renamed to CedarGraphQLError. The old name RedwoodGraphQLError is still exported as a deprecated alias for backwards compatibility. Please migrate to importing CedarGraphQLError from @cedarjs/graphql-server instead.

Changelog

🚀 Features

feat(ud): --apiRootPath support for build (#1793) by @Tobbe

This code snippet explains pretty well what this PR is about:

    .option('apiRootPath', {
      type: 'string',
      description:
        'Root path where API functions are served. Overrides the ' +
        'apiRootPath option on cedarUniversalDeployPlugin() in your ' +
        'vite config. Defaults to "/".',
    })

The apiRootPath option on cedarUniversalDeployPlugin() controls the URL prefix for all API routes. It feeds into discoverCedarRoutes() which generates entries like /myPrefix/graphql, /myPrefix/hello instead of the default /graphql, /hello.

feat(ud): Use srvx only (#1800) by @Tobbe

Don't use Fastify for the new --ud code path

feat(web): Support "cedar-app" div id (#1857) by @Tobbe

Default to using "cedar-app" as the main mount target for Cedar apps, while still supporting "redwood-app" for existing apps.

feat(ud): adapter-agnostic build and serve (#1782) by @Tobbe
  • Use user's vite config instead of hardcoded plugin list
  • Replace absolute dist paths with relative import.meta.url resolution
  • Clear stale UD store entries between build steps (unsure if this is really needed)
  • Build fetchables
  • Make yarn cedar serve --ud start a srvx server and tell it to serve the fetchables
feat(gqlorm): Add web workspace setup steps (#1775) by @Tobbe

Add the necessary web side wiring for using gqlorm

feat(vite)!: move index.html from web/src to web (#1799) by @lisa-assistant

Summary

Standard Vite convention is index.html at the project root (web/), not inside the source directory (web/src/). Cedar inherited web/src/index.html from Redwood, which forced getMergedConfig to set root: web/src explicitly so Vite could find it.

Moving index.html to web/ lets Vite find it by default — configFile lives in web/, so root defaults to web/. This removes the need for the explicit root override entirely. This also lets us use default vite configs in other places, like Storybook, prerender, etc. This simplifies the entire Vite config story.

Changes:

  • Move index.html from web/src/web/ in all fixtures, templates, and test fixtures
  • packages/project-config/src/paths.ts: web.html now points to web/index.html
  • packages/vite/src/lib/getMergedConfig.ts: remove root: web/src override — Vite defaults are now correct
  • packages/vite/src/plugins/vite-plugin-cedar-entry-injection.ts: compute entry script path relative to web/ instead of web/src/ (injected src changes from /entry.client.tsx/src/entry.client.tsx)
  • Update error messages in entry.client files and docs referencing the old path

Note for users with customizations in web/src/index.html: Moving the file to web/index.html is a straightforward rename — no content changes needed. Any relative paths inside the file (e.g. favicon, fonts) that referenced files relative to web/src/ may need updating to be relative to web/ instead, but the default Cedar index.html has no such relative paths.

feat(eslint-config)!: replace `eslint-plugin-import` with `eslint-plugin-import-x` (#1650) by @gameroman

eslint-plugin-import-x is faster, smaller, more modern than eslint-plugin-import
https://github.com/un-ts/eslint-plugin-import-x

As you can see in the README though, it does have some changes that eslint-plugin-import considered breaking. From what I can tell though, it's only related to removing support for old Node and eslint versions, which Cedar doesn't support anyway.

This is a breaking change for anyone with custom import/ rules, or eslint comments disabling specific import/ rules

feat(gqlorm): Single item CRUD mutation schema support (#1804) by @Tobbe

Generate SDL and resolvers for basic single item CRUD operations

feat(cli): Add provider specific ud setup commands (#1818) by @Tobbe

Add UD support to the netlify and vercel deploy setup commands

feat(pm): Package manager agnostic deploy commands (#1925) by @Tobbe

Deploy with yarn, npm or pnpm

feat(ud): auth support (#1943) by @Tobbe

Add full auth support to UD deployments

feat(pm): Package manager agnostic generate command (#1922) by @Tobbe

Support for yarn, npm and pnpm for cedar generate ...

feat(ud): add universal-deploy docs + fix setup URL (#1819) by @lisa-assistant

Summary

Two small follow-ups to the Universal Deploy setup PRs (#1817, #1818):

Fix setup URL

The yarn cedar setup deploy universal-deploy notes show:

See: https://cedarjs.com/docs/deploy#universal-deploy

Updated to match the actual doc path:

See: https://cedarjs.com/docs/deploy/universal-deploy

Add docs page

Adds docs/docs/deploy/universal-deploy.md covering:

  • Setup (yarn cedar setup deploy universal-deploy)
  • Build and serve locally (--ud flags)
  • Provider-specific setup (Netlify, Vercel)
  • How the Fetch-native server entry works
  • Troubleshooting

Test plan

  • Verify yarn cedar setup deploy universal-deploy shows the correct URL
  • Review docs page content
feat(pm): Package manager agnostic. Slice 3 finish (#1883) by @Tobbe

Finish up Slice 3 of the package manager agnostic work

feat(gql): CedarGraphQLError (#1946) by @Tobbe

Continue rebranding to Cedar.

Old RedwoodGraphQLError is still exported, but is now deprecated

feat(ud): Deploy to Vercel (#1832) by @Tobbe

All the crazy workarounds needed for Vercel

feat(cli): Make the dev command package manager agnostic (#1914) by @Tobbe

pnpm cedar dev should now work

feat(baremetal)!: warn on unpushed commits before deploying (#1904) by @lisa-assistant

Closes #1142

Before starting a baremetal deploy, check whether the local branch has commits that haven't been pushed to the remote. Since the server does a git pull (not a local copy), unpushed commits won't be included in the deploy — which is easy to miss.

What this does

  • Runs git log @{u}.. --oneline locally before connecting to any server
  • If unpushed commits are found, prints a warning listing them and prompts "Deploy anyway? (y/N)"
  • If the user declines (or hits Ctrl-C), aborts with a message to push first
  • Pass --no-git-check to bypass entirely
feat(cli): Setup universal-deploy (#1817) by @Tobbe

Add deploy setup command for universal-deploy

feat(pm): Finalize support for using yarn, npm and pnpm (#1950) by @Tobbe

This PR finalizes multi-package-manager support across the CedarJS framework, replacing hardcoded yarn/execa calls with PM-agnostic helpers (runBin, runBinSync, runWithNode) and adds a new CI smoke-test workflow that exercises npm and pnpm on every CLI-related change.

feat(vite)!: Remove vite setup command (#1860) by @Tobbe

It's assumed that all Cedar apps are using Vite at this point

feat(pm): bump pnpm to v11.8.0 (#1978) by @Tobbe

pnpm v11 uses pnpm-workspace.yaml for more of its config

Have to use an exact pnpm version: pnpm/pnpm#11388

feat(pm): Fixing most remaining hardcoded yarn occurrences (#1972) by @Tobbe

Fixing most remaining files in the cli package for pm agnostic support.

There's one known gap is in dockerHandler.js. I'll do that one in a separate PR
We also have yarn patches support, which I haven't decided how to handle for other package managers yet. Currently we don't actually have any patches though, so it's not a problem rn

feat(pm): experimental and setup commands (#1936) by @Tobbe

Make more of the CLI package manager agnostic

🛠️ Fixes

fix(internal): set output.exports: named in API Vite build (#1778) by @lisa-assistant

Silences this Rollup warning emitted on yarn cedar build when using the --ud (Universal Deploy) flag or when streaming SSR is enabled:

Entry module "requireAuth.ts" is using named and default exports together.
Consumers of your bundle will have to use `chunk.default` to access the
default export. Use `output.exports: "named"` to disable this warning.

(Same warning for skipAuth.ts.)

Root cause: Two code paths use Rollup for the API build, both missing exports: 'named' in their output config:

  • buildApiWithVite in packages/internal/src/build/api.ts — used when experimental.streamingSsr.enabled is true
  • buildCedarApp in packages/vite/src/buildApp.ts — used when building with --ud (Universal Deploy)

The default buildApi path still uses esbuild, which doesn't emit this warning. Directives intentionally export both schema (named) and the directive itself (default). Rollup warns about this mixed-export pattern unless output.exports: 'named' is set.

Fix: Add exports: 'named' to the rollup output config in both functions.

fix(ud): Deps correction. Explicitly depend on srvx (#1788) by @Tobbe

We now explicitly use srvx from the cli package. This should be reflected in our deps. We also no longer directly use @universal-deploy/node

fix(ud): Make Vercel use --apiRootPath (#1841) by @Tobbe

Make Vercel match Netlify, and the docs for UD

fix(ud): emit to chunks/ dir (#1822) by @Tobbe

Fixing this error on Netlify

Functions bundling
────────────────────────────────────────────────────────────────

Packaging Functions generated by your framework:
 - server.mjs

Packaging Functions from api/dist/ud directory:
 - .api_functions_foo-handler.js
 - .api_functions_graphql-handler.js
 - index.js


(Functions bundling completed in 11.8s)

Deploying to Netlify
────────────────────────────────────────────────────────────────

Deploy path:        /Users/tobbe/tmp/cedar-canary-ud-netlify/web/dist
Functions path:     /Users/tobbe/tmp/cedar-canary-ud-netlify/api/dist/ud
Configuration path: /Users/tobbe/tmp/cedar-canary-ud-netlify/netlify.toml

[...]

Internal error during "options.onPostBuild"
────────────────────────────────────────────────────────────────

  Error message
  JSONHTTPError: Unprocessable Entity

  Error location
  During options.onPostBuild
      at parseResponse (file:///usr/local/lib/node_modules/netlify-cli/node_modules/@netlify/api/lib/methods/response.js:34:39)
      at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
      at async callMethod (file:///usr/local/lib/node_modules/netlify-cli/node_modules/@netlify/api/lib/methods/index.js:26:28)
      at async deploySite (file:///usr/local/lib/node_modules/netlify-cli/dist/utils/deploy/deploy-site.js:132:18)
      at async runDeploy (file:///usr/local/lib/node_modules/netlify-cli/dist/commands/deploy/deploy.js:471:19)
      at async prepAndRunDeploy (file:///usr/local/lib/node_modules/netlify-cli/dist/commands/deploy/deploy.js:712:21)
      at async deployHandler (file:///usr/local/lib/node_modules/netlify-cli/dist/commands/deploy/deploy.js:1045:31)

  Error properties
  {
    name: 'JSONHTTPError',
    status: 422,
    json: {
      code: 422,
      message: 'Incorrect function names. Name should consist of only alphanumeric characters, hyphen & underscores'
    }
  }

Netlify only looks at top level files. So putting the generated files into a chunks/ directory effectively hides them from Netlify

fix(api): import.meta.resolve (#1816) by @Tobbe

The old condition, typeof require === 'function' && !(0, require).toString().includes('@rollup'), was designed to distinguish two cases:

  1. Real CJS require → use it directly
  2. Rollup's stubbed require (its .toString() contains '@rollup') → fall back to createRequire(import.meta.url)

But there's a third case it didn't account for: Netlify bootstrap's require, which is created via createRequire(import.meta.url) in the bootstrap's ESM context. That require function:

  • passes typeof require === 'function'
  • doesn't contain '@rollup' in its .toString()

So the code incorrectly treated it as a real CJS require and used it. Then customRequire.resolve('@cedarjs/api') asked that require — which resolves using the "require" condition from package.json exports — to find @cedarjs/api, and it returned dist/cjs/index.js, which wasn't in the bundle.


The bug has always been latent in the ESM dist/index.js, but before UD we just never tried that path. For CJS, because every caller that needed @cedarjs/api was in a CJS bundle and used require("@cedarjs/api"), which goes straight to dist/cjs/index.js, we were completely bypassing dist/index.js.

UD is the first time we try ESM on Netlify and @cedarjs/api gets loaded as ESM in a Netlify deployment, which is what finally surfaced the bug.

fix(ci): log yarn.lock state after tarsync (debug) (#1798) by @lisa-assistant

Summary

Adds diagnostic logging after yarn project:tarsync --verbose runs in the test project setup, to help understand the intermittent Windows CI failure where yarn cedar g secret --raw fails with:

Internal Error: root-workspace-0b6124@workspace:.: This package doesn't seem to be present in your lockfile

What this adds

After tarsync completes, we now check:

  1. Did yarn.lock get created at all? (tarsync's internal yarn install errors are silently swallowed — we'd never know if it didn't run)
  2. Does the lockfile contain the root-workspace- entry? — the missing entry is exactly what causes the downstream failure

The next time this fails on Windows, the log will show one of:

  • WARNING: yarn.lock was not created by tarsync! → tarsync's yarn install didn't run or failed very early
  • WARNING: yarn.lock exists but has no root-workspace- entry! → yarn install ran but produced an incomplete lockfile
  • Root workspace entry found: root-workspace-... → lockfile is fine, look elsewhere for the failure

Relates to the investigation in docs/implementation-plans/flaky-windows-smoke-tests-investigation.md.

Test plan

  • Merge and wait for next Windows CI failure to collect diagnostic info

🤖 Generated with Claude Code

fix(ud): netlify's functions dir (#1823) by @Tobbe

The UD build for Netlify puts the build output in api/dist/ud

fix(web): eliminate Vite build warnings for FatalErrorPage (#1777) by @lisa-assistant

Fixes this warning you see when you build your Cedar app:

(!) FatalErrorPage is dynamically imported by Routes.tsx but also statically
imported by App.tsx — dynamic import will not move module into another chunk.

Root cause: babel-plugin-redwood-routes-auto-loader wraps every page in src/pages/ in a lazy(() => import(...)) spec. It already de-registers pages that are explicitly imported in Routes.tsx, but not pages imported in other files like App.tsx. FatalErrorPage lives in src/pages/ and gets a lazy spec injected into Routes.tsx, but App.tsx also statically imports it for the <FatalErrorBoundary>.

Fix: In Program.enter(), read App.tsx and strip any of its page imports from the lazy-loading list before the lazy specs are emitted.

File: packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts

fix(deps): Downgrade otel to non-breaking version (#1842) by @Tobbe

Revert a breaking change introduced in #1558

fix(vite): register ~__CEDAR__USER_ROUTES_FOR_MOCK alias in Vitest (#1849) by @lisa-assistant

Fixes #427

Problem

Web-side tests that don't use the router log a spurious error:

Error: Cannot find module '~__CEDAR__USER_ROUTES_FOR_MOCK'
Require stack:
- .../node_modules/@cedarjs/testing/dist/web/MockProviders.js

MockProviders.tsx does a require('~__CEDAR__USER_ROUTES_FOR_MOCK') wrapped in a try/catch, intending it to be silent when the module isn't found. The catch checks error.code === 'MODULE_NOT_FOUND' && error.moduleName === module — but this check doesn't hold in Vitest's environment, so the error leaks through to console.warn.

The root cause is that ~__CEDAR__USER_ROUTES_FOR_MOCK is registered as a Jest moduleNameMapper entry in jest-preset.ts, but there's no equivalent alias registered in the Vite/Vitest config. Vitest handles routes via globRoutesImporter.ts (import.meta.glob), but the require() path in MockProviders runs unconditionally before that and fails.

Fix

Add ~__CEDAR__USER_ROUTES_FOR_MOCK to getMergedConfig's resolve.alias when running in test mode (env.mode === 'test'), pointing to cedarPaths.web.routes — the same target Jest uses.

// packages/vite/src/lib/getMergedConfig.ts
resolve: {
  alias: {
    ...workspaceAliases,
    ...(env.mode === 'test'
      ? { '~__CEDAR__USER_ROUTES_FOR_MOCK': cedarPaths.web.routes }
      : {}),
  },
},

This is the same one-line fix that the Storybook preset already applies in its own Vite config for the same alias.

fix(storybook): prevent esbuild from pre-bundling storybook-framework-cedarjs (#1805) by @lisa-assistant

Problem

When esbuild pre-bundles storybook-framework-cedarjs, it follows the import chain through MockProviders → user's Routes.tsx → page components → Cell files. Cedar's Cell transform plugin doesn't run during esbuild dep optimization, so Cell files have no default export at that stage.

With the CJS require() try/catch that was in MockProviders, esbuild silently produced undefined for Cell components in the bundled output (missing named exports just become undefined in CJS semantics — not a build error). This masked the issue, but it meant Cell components were always undefined in the pre-bundled context.

The root fix is to stop pre-bundling this package entirely.

Fix

  • Exclude storybook-framework-cedarjs from optimizeDeps so Vite serves it directly through its full transform pipeline, which does run Cedar's Cell plugin — giving Cell files their generated default export
  • Replace the CJS require() try/catch in MockProviders with a static ESM import so Vite can resolve the ~__REDWOOD__USER_ROUTES_FOR_MOCK alias at transform time rather than relying on esbuild to inline it during pre-bundling
  • Add ambient.d.ts + tsconfig.json paths entry so TypeScript resolves the virtual module alias without errors

Why wasn't this caught before?

The require() in try/catch masked the issue: esbuild treated missing Cell default exports as undefined (not an error), so pre-bundling completed and routes were populated from the Routes tree at runtime. The problem only becomes visible when using a static import, which turns the missing export into a hard esbuild build error.

🤖 Generated with Claude Code

fix(ci): log stderr when yarn cedar g secret fails on Windows (#1789) by @lisa-assistant

Summary

Fixes a recurring flaky CI failure where yarn cedar g secret --raw exits with code 1 on Windows (seen in PRs #1757, #1775, #1778, #1773) but produces no visible diagnostic output — making it impossible to debug.

Root cause of the silence: the command was called with silent: true. When getExecOutput from @actions/exec throws on a non-zero exit code, the buffered stdout/stderr are discarded — so nothing about the actual failure was ever logged.

Changes

.github/actions/set-up-test-project/setUpTestProject.mts

  • Switch yarn cedar g secret --raw from silent: true to ignoreReturnCode: true
  • Manually check the exit code and log captured stdout + stderr before re-throwing
  • Update the Args TypeScript interface to expose ignoreReturnCode?: boolean in options and exitCode: number in the return type (the underlying getExecOutput already supports/returns both — the interface was just incomplete)

docs/implementation-plans/flaky-windows-smoke-tests-investigation.md

  • Documents this as the 4th occurrence across 4 unrelated PRs
  • Clarifies what's actually failing: yarn cedar g secret --raw (not yarn install — the log interleaving is misleading)
  • Notes the debug improvement applied

What this doesn't fix

The underlying cause of yarn cedar g secret --raw failing on Windows is still unknown. This PR just ensures the next failure will show the actual error output so we can diagnose it.

fix(rename): Cedar prefixed telemetry env vars (#1836) by @Tobbe

Rename telemetry related env vars to CEDAR_*
Old REDWOOD_* names still work for now, but will be removed in a future (major) version of Cedar

fix(api-server): use consistent apiRootPath default (#1884) by @Tobbe
  • Add getAPIRootPath() helper that reads CEDAR_API_ROOT_PATH env var
  • Default to '/' to match other code paths (bothCLIConfigHandler, api plugin, graphql plugin)
  • Fix serveBothHandler.ts not having a default, instead potentially expanding into 'undefined'
  • Both serveBothHandler and createServerHelpers now use the same source for defaults

Previously serveBothHandler defaulted to undefined while createServerHelpers defaulted to '/',
which could cause subtle inconsistencies. Now both respect the existing CEDAR_API_ROOT_PATH
env var and default to '/' when not set.

fix(structure): exempt Node.js built-in env vars from check warning (#1851) by @lisa-assistant

Fixes #1850

Problem

After running yarn cedar setup auth dbAuth, yarn cedar check emits a false-positive warning:

env value NODE_ENV is not available. add it to your .env file

This happens because RWEnvHelper scans all project source files for process.env.* usage and warns for any variable not defined in .env or .env.defaults. It had no exemption for standard Node.js runtime variables.

dbAuth uses process.env.NODE_ENV in:

  • packages/auth-providers/dbAuth/api/src/shared.ts:42
  • packages/auth-providers/dbAuth/middleware/src/index.ts:154, 162

NODE_ENV is a well-known runtime variable that tooling sets — users should never add it to .env.

Fix

Add a NODE_BUILTIN_ENV_VARS set of well-known Node.js runtime variables and skip the diagnostic check for any of them. NODE_ENV is the main one; the others (NODE_PATH, NODE_OPTIONS, etc.) are included proactively to avoid the same false-positive pattern for any future code that reads them.

fix(storage): select only upload fields before deleting old files (#1808) by @lisa-assistant

Summary

Resolves #1639.

When update, updateMany, or upsert are called with upload fields, the extension fetches the existing record(s) first so it can delete the old uploaded files afterward. Previously these queries fetched all columns — only the upload field values are actually needed.

This PR adds select clauses to all three pre-fetch queries so Prisma returns only the relevant columns.

The @TODO comments in update and updateMany are replaced by the actual fix.

Changes

  • update: findFirstOrThrow now selects only uploadFieldsToUpdate
  • updateMany: findMany now selects only uploadFieldsToUpdate
  • upsert: findUnique now selects only uploadFieldsToUpdate

Test plan

  • Existing upload storage tests pass
  • Verify that update/updateMany/upsert still correctly delete old uploaded files

🤖 Generated with Claude Code

fix(ud): Suppress warnings for all environments (#1876) by @Tobbe

Suppress warnings not only for the api environment, but for all environments

fix(ud): Server guard for externalized optional peer deps (#1879) by @Tobbe

Greptile left this review on #1876


This PR generalises Rollup warning suppression (Prisma EVAL and graphql-scalars INVALID_ANNOTATION) from the api environment only to all Vite environments, by replacing the inline rollupOptions.onwarn with a configResolved plugin that correctly composes with any pre-existing handler. It also adds a new cedar-optional-peer-deps plugin that resolves bare-specifier dynamic imports from node_modules as external to avoid UNRESOLVED_IMPORT warnings for optional peer dependencies.

  • The onwarn composition now correctly chains existing handlers (existingOnwarn ? wrapper : onwarn), addressing the concerns from previous review threads.
  • The new cedar-optional-peer-deps plugin's resolveDynamicImport hook has no environment guard and fires for the browser client environment too; returning { external: true } for bare specifiers in a browser bundle produces native import('pkg') calls at runtime, which fail in all standard browsers, silently trading a build-time warning for an opaque runtime error.

Confidence Score: 4/5

The onwarn changes are safe; the new cedar-optional-peer-deps plugin can cause silent browser runtime import failures for any web-side dependency that uses bare-specifier dynamic imports, because it applies to the client environment without a guard.

The onwarn refactor is correct and the warning-suppression composition is well-implemented. The new cedar-optional-peer-deps plugin lacks an environment check, which means it marks bare-specifier dynamic imports as external in the browser bundle too — in practice this converts build-time unresolved-import warnings into opaque runtime failures in any browser that encounters such an import.

packages/vite/src/buildApp.ts — specifically the cedar-optional-peer-deps plugin's resolveDynamicImport hook and its effect on the client environment.


I hope the changes in this PR addresses the browser environment concern

fix(ud): task.taskFn is not a function (#1820) by @Tobbe

Fixes this:

❯ yarn cedar setup deploy netlify --ud
✔ Checking if Universal Deploy is set up...
◼ Adding Netlify plugins to vite config...
◼ Adding config...
◼ One more thing...
this.taskFn is not a function
fix(ud): Tweak build for better compatibility across envs (#1796) by @Tobbe

A few tweaks made as I'm trying to get UD working for Netlify

fix(ud): Better support for non-standard vite configs (#1825) by @Tobbe

Support things like defineConfig(({ mode }) => ({...}))

fix(pm): pm agnostic templates (#1981) by @Tobbe

Tripped up on the seed command in another PR, and when I fixed that I found a few more templates that still had yarn hardcoded

fix(ud): Set CEDAR_API_ROOT_PATH earlier (#1865) by @Tobbe

Make the env var available to cedarUniversalDeployPlugin's config hook for all invocations, including during buildCedarApp

fix(cca): placeholder glob patterns (#1988) by @Tobbe

Should have been part of #1981 but got lost in a diff shuffle

The files we look for when doing placeholder replacements now include *.cjs and *.mjs files at all levels

fix(ud): Wrap gql response in Response (#1894) by @Tobbe

Construct a proper web standards Response from the PonyfillResponse yoga gives us. Netliy requires this.

fix(ud): Fix graphql function support (#1885) by @Tobbe

Support graphql requests by adding explicit support for api/ imports, which we need for database support
And also by removing the call to createGraphQLHandler() since we don't use that for UD. Instead we call createGrahpQLYoga directly

fix(cli): prettier-plugin-tailwindcss 0.8.0 (#1956) by @Tobbe

#1940 bumped the plugin to v0.8.0, but the setup commands were never updated along with that PR.
This PR brings them in sync

fix(cca): Allow build scripts for more packages with pnpm (#1980) by @Tobbe

CI failed because it wasn't allowed to build these packages

fix(create-cedar-app): add neon-postgres .env.defaults (#1856) by @lisa-assistant

Fixes #1511

Problem

The neon-postgres overlay was missing a .env.defaults. New apps created with the neon-postgres database option inherited the base template's DATABASE_URL=file:./db/dev.db (SQLite), which causes PrismaPg to error at startup. There was also no hint that DIRECT_DATABASE_URL is required by prisma.config.cjs for migrations.

Fix

Add packages/create-cedar-app/database-overlays/neon-postgres/.env.defaults with:

  • Commented-out example values for both DATABASE_URL (pooled) and DIRECT_DATABASE_URL (direct/non-pooled), following the Neon URL pattern
  • A note clarifying these must be real PostgreSQL URLs, not the SQLite path from the base template
  • Standard PRISMA_HIDE_UPDATE_MESSAGE and LOG_LEVEL entries (matching the pglite overlay and base template)

The pglite overlay already has this pattern — this brings neon-postgres in line with it.

fix(cca): Use cwd with existing files (#1970) by @Tobbe

I hit a bug when I tried to create a new cedar app in the current directory and the directory already had files in it.

The cca script would blow the dir out under me.

fix(ud): Use 200.html (#1937) by @Tobbe
  1. SPA fallback was returning the prerendered index.html instead of the bare unprerendered shell. Routes like /contact (not prerendered) were getting the prerendered home page's HTML, which made the client think it was prerendered and crash on missing page modules. Fixed by reading web/dist/200.html first (the unprerendered shell that the prerender step copies from index.html), falling back to index.html if 200.html doesn't exist. Mirrors the Fastify web adapter exactly.
  2. fetch to the API proxy crashed on POST bodies with TypeError: RequestInit: duplex option is required when sending a body. Added duplex: 'half' with @ts-expect-error and a comment explaining why (Node 18+ fetch requirement, not yet in lib.dom types).
fix(api): More robust version fetching (#1893) by @Tobbe

Wrap in try/catch to fall through to createRequire backup code path. And then rethrow error if that also doesn't work

fix(cca): pnpm: Allow building needed packages (#1977) by @Tobbe

Prevents this error in CI

Progress: resolved 1758, reused 0, downloaded 1642, added 0
Progress: resolved 1780, reused 0, downloaded 1668, added 0
[WARN] 16 deprecated subdependencies found: @babel/plugin-proposal-private-methods@7.18.6, @humanwhocodes/config-array@0.13.0, @humanwhocodes/object-schema@2.0.3, abab@2.0.6, domexception@4.0.0, eslint@8.57.1, glob@10.5.0, glob@11.1.0, glob@7.2.3, glob@9.3.5, inflight@1.0.6, node-domexception@1.0.0, react-server-dom-webpack@19.2.4, rimraf@3.0.2, whatwg-encoding@2.0.0, whatwg-encoding@3.1.1
Packages: +1673
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 1780, reused 0, downloaded 1668, added 1
Progress: resolved 1780, reused 0, downloaded 1668, added 1673, done
[WARN] Issues with peer dependencies found. Run "pnpm peers check" to list them.

devDependencies:
+ @cedarjs/core 4.2.0
+ @cedarjs/eslint-config 4.2.0
+ @cedarjs/project-config 4.2.0
+ @cedarjs/testing 4.2.0
+ prettier-plugin-tailwindcss 0.8.0

[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: @prisma/engines@7.8.0, @swc/core@1.15.33, esbuild@0.27.7, msw@1.3.5, prisma@7.8.0, protobufjs@7.6.4

Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts.
/home/runner/work/cedar/cedar/node_modules/@actions/exec/lib/toolrunner.js:600
                error = new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`);
                        ^

Error: The process '/usr/local/bin/pnpm' failed with exit code 1
    at ExecState._setResult (/home/runner/work/cedar/cedar/node_modules/@actions/exec/lib/toolrunner.js:600:25)
    at ExecState.CheckComplete (/home/runner/work/cedar/cedar/node_modules/@actions/exec/lib/toolrunner.js:583:18)
    at ChildProcess.<anonymous> (/home/runner/work/cedar/cedar/node_modules/@actions/exec/lib/toolrunner.js:478:27)
    at ChildProcess.emit (node:events:509:28)
    at maybeClose (node:internal/child_process:1124:16)
    at ChildProcess._handle.onexit (node:internal/child_process:306:5)

Node.js v24.16.0

And similar errors for users when they create pnpm based Cedar apps

📚 Docs

docs(gql): Golden Path - Error Handling (#1813) by @Tobbe

What would it take for Cedar to follow the golden path regarding error handling?
https://goldenpath.graphql.org/practices/error-hardening/

docs(gql): Batch Execution (#1810) by @Tobbe

Plan for implementing batched execution in Cedar, which is part of the GraphQL Golden Path.

https://goldenpath.graphql.org/solutions/batch-resolvers/

docs(versioning): 2.x and 3.x (#1839) by @Tobbe

Consolidate old v2 and v3 docs into just one docs entry

docs(tutorial): update chapters 3 & 4 to use Cedar CLI commands (#1786) by @lisa-assistant

Summary

  • Replace all yarn rw / yarn redwood references with yarn cedar in tutorial chapters 3 & 4
  • Update community link from community.redwoodjs.com to Cedar Discord
  • Change example deployment URL from redwood-tutorial.netlify.app to cedar-tutorial.netlify.app

Files changed:

  • docs/docs/tutorial/chapter3/forms.md — 1 CLI reference
  • docs/docs/tutorial/chapter3/saving-data.md — 5 CLI references
  • docs/docs/tutorial/chapter4/authentication.md — 4 CLI references
  • docs/docs/tutorial/chapter4/deployment.md — community link + example URL
docs(ud): API route prefix (#1838) by @Tobbe

Document route prefix

docs(mdx): migrate all admonitions to bracket title syntax (:::info[Title]) (#1785) by @lisa-assistant

Summary

Converts all admonition blocks that use the old space-separated title syntax to the new bracket syntax required by Docusaurus v4.

Before: :::info Prerequisites
After: :::info[Prerequisites]

With future.v4: true in docusaurus.config.ts, the mdx1Compat.admonitions preprocessor is disabled by default — meaning the old :::info Title format is no longer auto-converted before remark-directive parsing, causing callout boxes to render as literal text.

This migrates all 851 affected files (current docs + versioned docs) to the bracket syntax, fixing the rendering without relying on the compatibility shim.

docs(ai): merge GEMINI.md into AGENTS.md (CLAUDE.md) (#1928) by @lisa-assistant

Summary

GEMINI.md and AGENTS.md/CLAUDE.md covered the same ground with different levels of detail. This consolidates them into one file so agents always read one source of truth.

Added from GEMINI.md:

  • Project overview section at the top
  • Development workflow (sync method, CLI dev method, test project generation)
  • Comments placement convention
  • @ts-ignore@ts-expect-error rule (merged into Type Safety section)
  • yarn dedupe / yarn check notes

Kept from AGENTS.md:

  • All existing detail (e as Error rule, Netlify E2E section, PR review comment command, etc.)

Deleted: GEMINI.md

CLAUDE.md already symlinks to AGENTS.md, so both Claude Code and Gemini read the same file after this change.

Test plan

  • Verify CLAUDE.md symlink still resolves correctly (ls -la CLAUDE.md)
  • Read through merged AGENTS.md for any contradictions or duplicate content

📦 Dependencies

Click to see all 71 dependency updates

🧹 Chore

Click to see all chore contributions
  • chore(yarn): dedupe (0abbbf5) by @Tobbe
  • chore(cli): Fix #1944 source code comments (96a9115) by @Tobbe
  • chore(yarn): Browserlist yarn.lock update (fb4d7bc) by @Tobbe
  • chore(docs): gqlorm auth implementation plan (not) (#1783) by @Tobbe
  • chore(ud): Add Netlify tests (#1821) by @Tobbe
  • chore(ud): Simplify virtual modules (#1815) by @Tobbe
  • chore(ci): surface tarsync failures that were silently absorbed (#1807) by @lisa-assistant
  • chore(test-project): Postcss 8.5.15 (#1828) by @Tobbe
  • chore(ud): Rename netlify e2e (#1837) by @Tobbe
  • chore(ud): Add decision log to UD refactor docs (#1791) by @Tobbe
  • chore(testing): remove debug ls -la from build scripts (#1861) by @lisa-assistant
  • chore(yarn): Browserlist yarn.lock update (#1858) by @Tobbe
  • chore(testing): Rename module names to cedar (#1806) by @Tobbe
  • chore(rename): Switch local variables to "cedar" (#1827) by @Tobbe
  • chore(cli): JS to TS for better DX (#1824) by @Tobbe
  • chore(ci): add workflow to warm better-sqlite3 prebuild cache on main (#1776) by @lisa-assistant
  • chore(ci): Fix flaky cca tests (#1811) by @Tobbe
  • chore(ud): Netlify e2e (#1826) by @Tobbe
  • chore(vite): suppress EVAL warnings from @prisma/internals (#1834) by @Tobbe
  • chore(ud): Clean up Netlify e2e (#1831) by @Tobbe
  • chore(docs): Add query aggregation and squadsync plans (#1829) by @Tobbe
  • chore(cli): Migrate destroy commands to TypeScript (#1882) by @lisa-assistant
  • chore(ci): Upgrade github-actions-cpu-cores to v3 (#1840) by @Tobbe
  • chore(docs): release-tooling unify version bumping (#1784) by @Tobbe
  • chore(ci): pin all GitHub Actions to commit SHAs (#1794) by @lisa-assistant
  • chore(vite): suppress INVALID_ANNOTATION warnings from graphql-scalars (#1833) by @Tobbe
  • chore(api): Inject versions at build time (#1895) by @Tobbe
  • chore(ci): Don't run Netlify CI for fork PRs (#1853) by @Tobbe
  • chore(ci): Pin Node on Windows to work around libuv bug (#1844) by @Tobbe
  • chore(ud): Only bundle graphql options from gql function (#1912) by @Tobbe
  • chore(ci): cache Playwright browsers to avoid hanging downloads (#1835) by @Tobbe
  • chore(cli): Migrate 10 CLI files from JS to TypeScript (#1899) by @lisa-assistant
  • chore(test): Fix locator in dev auth smoke tests (#1945) by @Tobbe
  • chore(ci): skip create-cedar-rsc-app install on cache hit, retry on failure (#1812) by @lisa-assistant
  • chore(docs): add CLAUDE.md symlink and update type safety guidelines (#1923) by @lisa-assistant
  • chore(ud): Add comment explaining Netlify workaround (#1866) by @Tobbe
  • chore(ud): Update early return checks in Cedar UD plugin (#1867) by @Tobbe
  • chore(vite): Explain lazy loaded esbuild (#1916) by @Tobbe
  • chore(cli): TS cleanups after 1958 (#1969) by @Tobbe
  • chore(ci): Don't run Vercel CI for fork PRs (#1889) by @Tobbe
  • chore(cli): migrate setup handlers from JS to TS (#1958) by @lisa-assistant
  • chore(cli): migrate JS files to TypeScript (#1920) by @lisa-assistant
  • chore(lefthook): Run smart-format without tsx. Node supports ts (#1964) by @Tobbe
  • chore(cli): migrate JS files to TypeScript (#1927) by @lisa-assistant
  • chore(cli): Migrate 10 CLI files from JS to TypeScript (#1902) by @lisa-assistant
  • chore(ai): Teach agents to fetch review comments (#1888) by @Tobbe
  • chore(cli): migrate dbAuth, dataMigration, i18n, uploads, docker, deploy, flightcontrol, render, coherence, generator from JS to TS (#1952) by @lisa-assistant
  • chore(ci): Drop Playwright cache. Separate deps install step (#1845) by @Tobbe
  • chore(tasks): Script to clear GH cache (#1848) by @Tobbe
  • chore(babel): Rename extracted gql server options to __cedar_... (#1911) by @Tobbe
  • chore(test-project): Use GQL error in contacts resolver (#1908) by @Tobbe
  • chore(docs): Rename fragment cell impl plan (#1852) by @Tobbe
  • chore(api-server): Simplify routePath construction (#1909) by @Tobbe
  • chore(cli): migrate files from JS to TypeScript (#1906) by @lisa-assistant
  • chore(cli): migrate 10 files from JS to TS (#1971) by @lisa-assistant
  • chore(docs): Update project overview after latest UD change (#1787) by @Tobbe
  • chore(ai): taste updates (#1907) by @Tobbe
  • chore(test-project): Remove unneeded step and related dead code (#1982) by @Tobbe
  • chore(cli): JS -> TS merge fixes (#1926) by @Tobbe
  • chore(cli): migrate JS files to TypeScript (#1935) by @lisa-assistant
  • chore(cli): HandlerArgv type (#1933) by @Tobbe
  • chore(ud): Post-merge auth support tweaks (#1947) by @Tobbe
  • chore(test-project): Include shared packages in pnpm workspace (#1979) by @Tobbe
  • chore(ci): Fix ci.yml for pm cli smoke tests (#1963) by @Tobbe
  • chore(cli): migrate JS files to TypeScript (#1944) by @lisa-assistant