v5.0.0
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.foo → ctx.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(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 --udstart 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.htmlfromweb/src/→web/in all fixtures, templates, and test fixtures packages/project-config/src/paths.ts:web.htmlnow points toweb/index.htmlpackages/vite/src/lib/getMergedConfig.ts: removeroot: web/srcoverride — Vite defaults are now correctpackages/vite/src/plugins/vite-plugin-cedar-entry-injection.ts: compute entry script path relative toweb/instead ofweb/src/(injected src changes from/entry.client.tsx→/src/entry.client.tsx)- Update error messages in
entry.clientfiles 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 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 (
--udflags) - Provider-specific setup (Netlify, Vercel)
- How the Fetch-native server entry works
- Troubleshooting
Test plan
- Verify
yarn cedar setup deploy universal-deployshows 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(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}.. --onelinelocally 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-checkto bypass entirely
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:
buildApiWithViteinpackages/internal/src/build/api.ts— used whenexperimental.streamingSsr.enabledis truebuildCedarAppinpackages/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:
- Real CJS
require→ use it directly - Rollup's stubbed
require(its.toString()contains'@rollup') → fall back tocreateRequire(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:
- Did
yarn.lockget created at all? (tarsync's internalyarn installerrors are silently swallowed — we'd never know if it didn't run) - 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 earlyWARNING: yarn.lock exists but has no root-workspace- entry!→ yarn install ran but produced an incomplete lockfileRoot 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-cedarjsfromoptimizeDepsso Vite serves it directly through its full transform pipeline, which does run Cedar's Cell plugin — giving Cell files their generateddefaultexport - Replace the CJS
require()try/catch inMockProviderswith a static ESMimportso Vite can resolve the~__REDWOOD__USER_ROUTES_FOR_MOCKalias at transform time rather than relying on esbuild to inline it during pre-bundling - Add
ambient.d.ts+tsconfig.jsonpathsentry 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 --rawfromsilent: truetoignoreReturnCode: true - Manually check the exit code and log captured
stdout+stderrbefore re-throwing - Update the
ArgsTypeScript interface to exposeignoreReturnCode?: booleanin options andexitCode: numberin the return type (the underlyinggetExecOutputalready 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(notyarn 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 readsCEDAR_API_ROOT_PATHenv var - Default to
'/'to match other code paths (bothCLIConfigHandler, api plugin, graphql plugin) - Fix
serveBothHandler.tsnot having a default, instead potentially expanding into'undefined' - Both
serveBothHandlerandcreateServerHelpersnow 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:42packages/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:findFirstOrThrownow selects onlyuploadFieldsToUpdateupdateMany:findManynow selects onlyuploadFieldsToUpdateupsert:findUniquenow selects onlyuploadFieldsToUpdate
Test plan
- Existing upload storage tests pass
- Verify that
update/updateMany/upsertstill 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
onwarncomposition now correctly chains existing handlers (existingOnwarn ? wrapper : onwarn), addressing the concerns from previous review threads. - The new
cedar-optional-peer-depsplugin'sresolveDynamicImporthook has no environment guard and fires for the browserclientenvironment too; returning{ external: true }for bare specifiers in a browser bundle produces nativeimport('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) andDIRECT_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_MESSAGEandLOG_LEVELentries (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
- SPA fallback was returning the prerendered
index.htmlinstead 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. - 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-errorand a comment explaining why (Node 18+ fetch requirement, not yet inlib.domtypes).
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.
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 redwoodreferences withyarn cedarin tutorial chapters 3 & 4 - Update community link from
community.redwoodjs.comto Cedar Discord - Change example deployment URL from
redwood-tutorial.netlify.apptocedar-tutorial.netlify.app
Files changed:
docs/docs/tutorial/chapter3/forms.md— 1 CLI referencedocs/docs/tutorial/chapter3/saving-data.md— 5 CLI referencesdocs/docs/tutorial/chapter4/authentication.md— 4 CLI referencesdocs/docs/tutorial/chapter4/deployment.md— community link + example URL
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-errorrule (merged into Type Safety section)yarn dedupe/yarn checknotes
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.mdsymlink still resolves correctly (ls -la CLAUDE.md) - Read through merged
AGENTS.mdfor any contradictions or duplicate content
📦 Dependencies
Click to see all 71 dependency updates
- fix(deps): update dependency empathic to v2.0.1 (#1770) by @renovate-bot
- fix(deps): update dependency @ast-grep/napi to v0.42.2 (#1772) by @renovate-bot
- chore(deps): update github/codeql-action digest to 9e0d7b8 (#1792) by @renovate-bot
- fix(deps): update dependency @supabase/ssr to v0.10.3 (#1766) by @renovate-bot
- chore(deps): update actions/checkout action to v6.0.3 (#1862) by @renovate-bot
- chore(deps): update dependency publint to v0.3.21 (#1802) by @renovate-bot
- fix(deps): update dependency rollup to v4.60.4 (#1803) by @renovate-bot
- fix(deps): update dependency express to v4.22.2 (#1795) by @renovate-bot
- chore(deps): update dependency @eslint-community/eslint-plugin-eslint-comments to v4.7.2 (#1854) by @renovate-bot
- chore(deps): update dependency nx to v22.7.1 (#1790) by @renovate-bot
- chore(deps): update dependency memfs to v4.57.6 (#1872) by @renovate-bot
- chore(deps): update dependency lefthook to v2.1.9 (#1871) by @renovate-bot
- chore(deps): update dependency @auth0/auth0-spa-js to v2.20.0 (#1801) by @renovate-bot
- fix(deps): update dependency @graphql-yoga/plugin-graphql-sse to v3.21.1 (#1875) by @renovate-bot
- chore(deps): update dependency @universal-deploy/vite to v0.1.10 (#1870) by @renovate-bot
- chore(deps): update actions/checkout digest to df4cb1c (#1859) by @renovate-bot
- fix(deps): update dependency lru-cache to v11.3.6 (#1773) by @renovate-bot
- fix(deps): update dependency @graphql-yoga/plugin-persisted-operations to v3.21.1 (#1877) by @renovate-bot
- fix(deps): update typescript-eslint monorepo to v8.60.1 (#1878) by @renovate-bot
- fix(deps): update dependency @types/aws-lambda to v8.10.162 (#1892) by @renovate-bot
- chore(deps): Bump vite to 7.3.5 (#1869) by @Tobbe
- fix(deps): update dependency isbot to v5.1.41 (#1887) by @renovate-bot
- fix(deps): update opentelemetry-js monorepo (#1558) by @renovate-bot
- fix(deps): update dependency @graphql-yoga/plugin-defer-stream to v3.21.1 (#1874) by @renovate-bot
- fix(deps): update dependency @graphql-yoga/plugin-defer-stream to v3.21.2 (#1896) by @renovate-bot
- fix(deps): update dependency @fastify/http-proxy to v11.5.0 (#1965) by @renovate-bot
- chore(deps): update dependency @prisma/dev to v0.24.12 (#1910) by @renovate-bot
- fix(deps): update dependency @graphql-yoga/plugin-graphql-sse to v3.21.2 (#1897) by @renovate-bot
- fix(deps): update dependency systeminformation to v5.31.7 (#1891) by @renovate-bot
- chore(deps): update actions/checkout digest to df4cb1c (#1880) by @renovate-bot
- chore(deps): update dependency memfs to v4.57.7 (#1915) by @renovate-bot
- chore(deps): update dependency human-id to v4.2.0 (#1938) by @renovate-bot
- fix(deps): update dependency eslint-plugin-prettier to v5.5.6 (#1881) by @renovate-bot
- fix(deps): update dependency isbot to v5.1.43 (#1949) by @renovate-bot
- fix(deps): update babel monorepo to v7.29.7 (#1959) by @renovate-bot
- chore(deps): Bump Playwright to 1.60.0 (#1846) by @Tobbe
- chore(deps): update dependency @actions/cache to v6.0.1 (#1847) by @renovate-bot
- chore(deps): update dependency @prisma/dev to v0.24.9 (#1864) by @renovate-bot
- chore(deps): update dependency @types/nodemailer to v6.4.24 (#1913) by @renovate-bot
- chore(deps): update dependency @easyops-cn/docusaurus-search-local to v0.55.2 (#1863) by @renovate-bot
- fix(deps): update dependency http-proxy-middleware to v3.0.6 (#1901) by @renovate-bot
- chore(deps): update dependency @auth0/auth0-spa-js to v2.21.1 (#1932) by @renovate-bot
- fix(deps): update dependency prettier to v3.8.4 (#1918) by @renovate-bot
- fix(deps): update dependency @electric-sql/pglite-socket to ^0.2.0 (#1962) by @renovate-bot
- chore(deps): update dependency tsx to v4.22.4 (#1954) by @renovate-bot
- fix(deps): update dependency @ast-grep/napi to v0.43.0 (#1960) by @renovate-bot
- fix(deps): update dependency isbot to v5.1.42 (#1917) by @renovate-bot
- chore(deps): update dependency prettier-plugin-tailwindcss to ^0.8.0 (#1951) by @renovate-bot
- fix(deps): update dependency http-proxy-middleware to v3.0.7 (#1948) by @renovate-bot
- chore(deps): Drop dependency human-id (#1941) by @Tobbe
- fix(deps): update dependency isbot to v5.1.40 (#1771) by @renovate-bot
- fix(deps): update dependency graphql-yoga to v5.21.1 (#1886) by @renovate-bot
- chore(deps): update dependency ts-dedent to v2.3.0 (#1953) by @renovate-bot
- chore(deps): update playwright monorepo to v1.61.0 (#1955) by @renovate-bot
- fix(deps): update dependency @arethetypeswrong/cli to v0.18.3 (#1873) by @renovate-bot
- chore(deps): update dependency ioredis to v5.11.1 (#1939) by @renovate-bot
- chore(deps): update noisy packages to v22.7.5 (#1921) by @renovate-bot
- chore(deps): update pnpm to v10.34.3 (#1957) by @renovate-bot
- chore(deps): update dependency @npmcli/arborist to v9.8.0 (#1934) by @renovate-bot
- fix(deps): update docusaurus monorepo to v3.10.1 (#1919) by @renovate-bot
- fix(deps): update vitest monorepo to v3.2.6 (#1930) by @renovate-bot
- chore(deps): update dependency prettier-plugin-tailwindcss to v0.8.0 (#1940) by @renovate-bot
- fix(deps): update dependency @graphql-yoga/plugin-persisted-operations to v3.21.2 (#1898) by @renovate-bot
- fix(deps): update dependency srvx to v0.11.16 (#1890) by @renovate-bot
- fix(deps): update dependency @supabase/ssr to v0.12.0 (#1967) by @renovate-bot
- fix(deps): update dependency @rollup/pluginutils to v5.4.0 (#1966) by @renovate-bot
- fix(deps): update dependency @electric-sql/pglite to ^0.5.0 (#1961) by @renovate-bot
- fix(deps): update dependency graphql-yoga to v5.21.2 (#1900) by @renovate-bot
- fix(deps): update swc monorepo to v1.15.41 (#1924) by @renovate-bot
- chore(deps): update actions/stale action to v10.3.0 (#1931) by @renovate-bot
- chore(deps): update dependency @prisma/dev to v0.24.14 (#1929) by @renovate-bot
🧹 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