Skip to content

Conversation

@kentcdodds
Copy link
Member

@kentcdodds kentcdodds commented Jan 21, 2026

Test Plan

  1. Run npm install to ensure all new dependencies are installed.
  2. Run npm test to verify all unit and integration tests pass.
  3. Start the development server (npm run dev).
  4. Manually test core functionalities:
    • User authentication (login, signup, forgot password, reset password).
    • User onboarding.
    • Profile settings (change email, password, photo, 2FA).
    • Note creation, editing, and viewing.
    • User search functionality.
    • Theme switch.

Checklist

  • Tests updated
  • Docs updated

Screenshots


Open in Cursor Open in Web


Note

Upgrade and refactors for DB, forms, and caching

  • Migrate to Prisma v7 with PrismaBetterSqlite3 adapter and CJS client import; update searchUsers typed SQL call to accept two params; add prisma/env/test setup tweaks
  • Switch all @conform-to/zod imports to @conform-to/zod/v4 and refactor validation (checkbox .coerce.boolean(), required fields via .superRefine, hidden-field handling) across auth, onboarding, profile, 2FA, and notes routes
  • Rework cache: lazy-load node:sqlite via createRequire, centralize prepared statements, and fall back to in-memory LRU in test env; keep buffer (de)serialization
  • Storage: send file directly in PUT body with signed headers in uploadToStorage
  • Tests and minor behavior tweaks: add unstable_pattern to auth callback tests; adjust 2FA verify refinement; minor route logic/typing cleanups

Written by Cursor Bugbot for commit 468ed86. This will update automatically on new commits. Configure here.

cursoragent and others added 14 commits January 21, 2026 19:46
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
@cursor
Copy link

cursor bot commented Jan 21, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

cursoragent and others added 3 commits January 21, 2026 21:36
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Comment on lines 1 to 15
-- @param {String} $1:like
-- @param {String} $2:like2
SELECT
"User".id,
"User".username,
"User".name,
"UserImage".id AS imageId,
"UserImage".objectKey AS imageObjectKey
FROM "User"
LEFT JOIN "UserImage" ON "User".id = "UserImage".userId
WHERE "User".username LIKE :like
OR "User".name LIKE :like
WHERE "User".username LIKE ?
OR "User".name LIKE ?
ORDER BY (
SELECT "Note".updatedAt
FROM "Note"
Copy link
Member Author

Choose a reason for hiding this comment

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

@cursor, are the changes in this file necessary? Here's a snippet from the docs:


title: 'TypedSQL'
metaTitle: 'Writing Type-safe SQL with TypedSQL and Prisma Client'
metaDescription: 'Learn how to use TypedSQL to write fully type-safe SQL queries that are compatible with any SQL console and Prisma Client.'
sidebar_class_name: preview-badge

Getting started with TypedSQL

To start using TypedSQL in your Prisma project, follow these steps:

  1. Ensure you have @prisma/client and prisma installed and updated to at least version 5.19.0.

    npm install @prisma/client@latest
    npm install -D prisma@latest
    
  2. Add the typedSql preview feature flag to your schema.prisma file:

    generator client {
     provider = "prisma-client" // or "prisma-client-js"
     previewFeatures = ["typedSql"]
     output = "../src/generated/prisma"
    }

    :::tip[Using driver adapters with TypedSQL]

    If you are deploying Prisma in serverless or edge environments, you can use driver adapters to connect through JavaScript database drivers. Driver adapters are compatible with TypedSQL, with the exception of @prisma/adapter-better-sqlite3. For SQLite support, use @prisma/adapter-libsql instead. All other driver adapters are supported.

    :::

  3. Create a sql directory inside your prisma directory. This is where you'll write your SQL queries.

    mkdir -p prisma/sql
    

    :::note[Custom SQL folder location]

    Starting with Prisma 6.12.0, you can configure a custom location for your SQL files using the Prisma config file. Create a prisma.config.ts file in your project root and specify the typedSql.path option:

    import 'dotenv/config'
    import { defineConfig } from 'prisma/config'
    
    export default defineConfig({
      schema: './prisma/schema.prisma',
      typedSql: {
        path: './prisma/sql',
      },
    })

    :::

  4. Create a new .sql file in your prisma/sql directory. For example, getUsersWithPosts.sql. Note that the file name must be a valid JS identifier and cannot start with a $.

  5. Write your SQL queries in your new .sql file. For example:

    SELECT u.id, u.name, COUNT(p.id) as "postCount"
    FROM "User" u
    LEFT JOIN "Post" p ON u.id = p."authorId"
    GROUP BY u.id, u.name
  6. Generate Prisma Client with the sql flag to ensure TypeScript functions and types for your SQL queries are created:

    :::warning

    Make sure that any pending migrations are applied before generating the client with the sql flag.

    :::

    prisma generate --sql
    

    If you don't want to regenerate the client after every change, this command also works with the existing --watch flag:

    prisma generate --sql --watch
    
  7. Now you can import and use your SQL queries in your TypeScript code:

import { PrismaClient } from './generated/prisma/client'
import { getUsersWithPosts } from './generated/prisma/sql'

const prisma = new PrismaClient()

 const usersWithPostCounts = await prisma.$queryRawTyped(getUsersWithPosts())
 console.log(usersWithPostCounts)

:::note

If you do not customize the generator output, you can import from @prisma/client and @prisma/client/sql instead.

:::

Passing Arguments to TypedSQL Queries

To pass arguments to your TypedSQL queries, you can use parameterized queries. This allows you to write flexible and reusable SQL statements while maintaining type safety. Here's how to do it:

  1. In your SQL file, use placeholders for the parameters you want to pass. The syntax for placeholders depends on your database engine:

    In SQLite, there are a number of different placeholders you can use. Positional placeholders ($1, $2, etc.), general placeholders (?), and named placeholders (:minAge, :maxAge, etc.) are all available. For this example, we'll use named placeholders :minAge and :maxAge:

    SELECT id, name, age
    FROM users
    WHERE age > :minAge AND age < :maxAge

    :::note

    See below for information on how to define argument types in your SQL files.

    :::

  2. When using the generated function in your TypeScript code, pass the arguments as additional parameters to $queryRawTyped:

import { PrismaClient } from './generated/prisma/client'
import { getUsersByAge } from './generated/prisma/sql'

 const prisma = new PrismaClient()

 const minAge = 18
 const maxAge = 30
 const users = await prisma.$queryRawTyped(getUsersByAge(minAge, maxAge))
 console.log(users)

By using parameterized queries, you ensure type safety and protect against SQL injection vulnerabilities. The TypedSQL generator will create the appropriate TypeScript types for the parameters based on your SQL query, providing full type checking for both the query results and the input parameters.

Passing array arguments to TypedSQL

TypedSQL supports passing arrays as arguments for PostgreSQL. Use PostgreSQL's ANY operator with an array parameter.

SELECT id, name, email
FROM users
WHERE id = ANY($1)
import { PrismaClient } from './generated/prisma/client'
import { getUsersByIds } from './generated/prisma/sql'

const prisma = new PrismaClient()

const userIds = [1, 2, 3]
const users = await prisma.$queryRawTyped(getUsersByIds(userIds))
console.log(users)

TypedSQL will generate the appropriate TypeScript types for the array parameter, ensuring type safety for both the input and the query results.

:::note

When passing array arguments, be mindful of the maximum number of placeholders your database supports in a single query. For very large arrays, you may need to split the query into multiple smaller queries.

:::

Defining argument types in your SQL files

Argument typing in TypedSQL is accomplished via specific comments in your SQL files. These comments are of the form:

-- @param {Type} $N:alias optional description

Where Type is a valid database type, N is the position of the argument in the query, and alias is an optional alias for the argument that is used in the TypeScript type.

As an example, if you needed to type a single string argument with the alias name and the description "The name of the user", you would add the following comment to your SQL file:

-- @param {String} $1:name The name of the user

To indicate that a parameter is nullable, add a question mark after the alias:

-- @param {String} $1:name? The name of the user (optional)

Currently accepted types are Int, BigInt, Float, Boolean, String, DateTime, Json, Bytes, null, and Decimal.

Taking the example from above, the SQL file would look like this:

-- @param {Int} $1:minAge
-- @param {Int} $2:maxAge
SELECT id, name, age
FROM users
WHERE age > $1 AND age < $2

The format of argument type definitions is the same regardless of the database engine.

:::note

Manual argument type definitions are not supported for array arguments. For these arguments, you will need to rely on the type inference provided by TypedSQL.

:::

Examples

For practical examples of how to use TypedSQL, please refer to the TypedSQL example in the Prisma Examples repo.

Limitations of TypedSQL

Supported Databases

TypedSQL supports modern versions of MySQL and PostgreSQL without any further configuration. For MySQL versions older than 8.0 and all SQLite versions, you will need to manually describe argument types in your SQL files. The types of inputs are inferred in all supported versions of PostgreSQL and MySQL 8.0 and later.

TypedSQL does not work with MongoDB, as it is specifically designed for SQL databases.

Active Database Connection Required

TypedSQL requires an active database connection to function properly. This means you need to have a running database instance that Prisma can connect to when generating the client with the --sql flag. TypedSQL uses the connection string defined in prisma.config.ts (datasource.url) to establish this connection.

Dynamic SQL Queries with Dynamic Columns

TypedSQL does not natively support constructing SQL queries with dynamically added columns. When you need to create a query where the columns are determined at runtime, you must use the $queryRaw and $executeRaw methods. These methods allow for the execution of raw SQL, which can include dynamic column selections.

Example of a query using dynamic column selection:

const columns = 'name, email, age'; // Columns determined at runtime
const result = await prisma.$queryRawUnsafe(
  `SELECT ${columns} FROM Users WHERE active = true`
);

In this example, the columns to be selected are defined dynamically and included in the SQL query. While this approach provides flexibility, it requires careful attention to security, particularly to avoid SQL injection vulnerabilities. Additionally, using raw SQL queries means foregoing the type-safety and DX of TypedSQL.

Acknowledgements

This feature was heavily inspired by PgTyped and SQLx. Additionally, SQLite parsing is handled by SQLx.

Copy link

@cursor cursor bot Jan 21, 2026

Choose a reason for hiding this comment

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

Yes, this change is necessary with our current setup. We’re using @prisma/adapter-better-sqlite3, and with that adapter the SQLite driver rejects positional placeholders like ?1 (and likewise named placeholders) at runtime — it throws “Too many parameter values were provided.” The same query works with plain ? placeholders, which is why the SQL now uses ? and the call passes both args explicitly.【F:/opt/cursor/artifacts/sqlite-param-test.log†L1-L16】【F:prisma/sql/searchUsers.sql†L1-L20】【F:app/routes/users/index.tsx†L11-L19】

This lines up with the TypedSQL docs caveat that @prisma/adapter-better-sqlite3 is not supported; so we need to use the form that the underlying driver accepts to keep TypedSQL working without swapping adapters.【F:/opt/cursor/artifacts/sqlite-param-test.log†L1-L16】【F:prisma/sql/searchUsers.sql†L1-L20】

Open in Cursor Open in Web

@kentcdodds
Copy link
Member Author

@cursor, check CI and fix it please.

@cursor
Copy link

cursor bot commented Jan 21, 2026

Summary

  • Checked CI logs and found all failing jobs stopped at Prisma setup due to missing DATABASE_URL; fixed by loading dotenv in prisma.config.ts so CI’s copied .env is read during prisma commands.【F:/opt/cursor/artifacts/ci-fail.log†L1-L32】【F:prisma.config.ts†L1-L10】

Testing

  • set -o pipefail && { npx prisma migrate deploy && npx prisma generate --sql; } | tee /opt/cursor/artifacts/prisma-ci-test.log (validates the CI “Setup Database” step with .env; manual UI testing not run because this is a Prisma config-only change).【F:/opt/cursor/artifacts/prisma-ci-test.log†L1-L19】

Open in Cursor Open in Web

Co-authored-by: me <me@kentcdodds.com>
@kentcdodds
Copy link
Member Author

@cursor, thanks! CI is still failing for the type checking and vitest with the following errors:

Run npm run test -- --coverage

> test
> vitest --coverage

🖼️  Generated SVG spritesheet in app/components/ui/icons
TS Generated icon types in app/components/ui/icons/types.ts

 RUN  v4.0.17 /home/runner/work/epic-stack/epic-stack
      Coverage enabled with v8

(node:2234) ExperimentalWarning: SQLite is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
Loaded Prisma config from prisma.config.ts.


! unknown or unexpected option: --skip-seed

Reset your database and apply all migrations, all data will be lost

Usage

  $ prisma migrate reset [options]

  The datasource URL configuration is read from the Prisma config file (e.g., prisma.config.ts).

Options

       -h, --help   Display this help message
         --config   Custom path to your Prisma config file
         --schema   Custom path to your Prisma schema
      -f, --force   Skip the confirmation prompt

Examples

  Reset your database and apply all migrations, all data will be lost
  $ prisma migrate reset

  Specify a schema
  $ prisma migrate reset --schema=./schema.prisma

  Use --force to skip the confirmation prompt
  $ prisma migrate reset --force

No test files found, exiting with code 1

include: ./app/**/*.test.{ts,tsx}
exclude:  **/node_modules/**, **/.git/**

 % Coverage report from v8
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |       0 |        0 |       0 |       0 |                   
 app               |       0 |        0 |       0 |       0 |                   
  entry.client.tsx |       0 |        0 |       0 |       0 | 5-10              
  entry.server.tsx |       0 |        0 |       0 |       0 | 20-141            
  root.tsx         |       0 |        0 |       0 |       0 | 44-263            
  routes.ts        |       0 |        0 |       0 |       0 |                   
 app/components    |       0 |        0 |       0 |       0 |                   
  ...-boundary.tsx |       0 |        0 |       0 |       0 | 18-43             
  ...g-toolbar.tsx |       0 |      100 |     100 |       0 | 2                 
  forms.tsx        |       0 |        0 |       0 |       0 | 24-187            
  progress-bar.tsx |       0 |        0 |       0 |       0 | 8-30              
  search-bar.tsx   |       0 |        0 |       0 |       0 | 18-35             
  spacer.tsx       |       0 |      100 |       0 |       0 | 42-56             
  toaster.tsx      |       0 |        0 |       0 |       0 | 6-9               
  ...-dropdown.tsx |       0 |        0 |       0 |       0 | 17-26             
 app/components/ui |       0 |        0 |       0 |       0 |                   
  button.tsx       |       0 |        0 |       0 |       0 | 7-51              
  checkbox.tsx     |       0 |      100 |       0 |       0 | 13-17             
  ...down-menu.tsx |       0 |        0 |       0 |       0 | 9-219             
  icon.tsx         |       0 |        0 |       0 |       0 | 9-68              
  input-otp.tsx    |       0 |        0 |       0 |       0 | 6-67              
  input.tsx        |       0 |      100 |       0 |       0 | 5-10              
  label.tsx        |       0 |      100 |       0 |       0 | 7-15              
  sonner.tsx       |       0 |      100 |       0 |       0 | 5-6               
  ...us-button.tsx |       0 |        0 |       0 |       0 | 12-78             
  textarea.tsx     |       0 |      100 |       0 |       0 | 5-9               
  tooltip.tsx      |       0 |        0 |       0 |       0 | 9-31              
 ...nents/ui/icons |       0 |      100 |     100 |       0 |                   
  types.ts         |       0 |      100 |     100 |       0 | 3                 
 app/routes        |       0 |        0 |       0 |       0 |                   
  $.tsx            |       0 |      100 |       0 |       0 | 13-32             
  me.tsx           |       0 |        0 |       0 |       0 | 7-18              
 app/routes/_auth  |       0 |        0 |       0 |       0 |                   
  ...-password.tsx |       0 |        0 |       0 |       0 | 18-188            
  login.server.ts  |       0 |        0 |       0 |       0 | 13-157            
  login.tsx        |       0 |        0 |       0 |       0 | 25-306            
  logout.tsx       |       0 |      100 |       0 |       0 | 6-10              
  ...ord.server.ts |       0 |        0 |       0 |       0 | 9-29              
  ...-password.tsx |       0 |        0 |       0 |       0 | 18-150            
  signup.tsx       |       0 |        0 |       0 |       0 | 24-193            
  verify.server.ts |       0 |        0 |       0 |       0 | 49-197            
  verify.tsx       |       0 |        0 |       0 |       0 | 16-143            
 ...auth.$provider |       0 |        0 |       0 |       0 |                   
  callback.ts      |       0 |        0 |       0 |       0 | 29-196            
  index.ts         |       0 |        0 |       0 |       0 | 10-32             
 ...uth/onboarding |       0 |        0 |       0 |       0 |                   
  ...der.server.ts |       0 |      100 |       0 |       0 | 8-14              
  $provider.tsx    |       0 |        0 |       0 |       0 | 35-187            
  index.server.ts  |       0 |      100 |       0 |       0 | 8-14              
  index.tsx        |       0 |        0 |       0 |       0 | 29-155            
 ..._auth/webauthn |       0 |        0 |       0 |       0 |                   
  ...entication.ts |       0 |        0 |       0 |       0 | 16-105            
  registration.ts  |       0 |        0 |       0 |       0 | 17-131            
  utils.server.ts  |       0 |      100 |       0 |       0 | 9-84              
 ...tes/_marketing |       0 |      100 |       0 |       0 |                   
  about.tsx        |       0 |      100 |       0 |       0 | 2                 
  index.tsx        |       0 |      100 |       0 |       0 | 11-74             
  privacy.tsx      |       0 |      100 |       0 |       0 | 2                 
  support.tsx      |       0 |      100 |       0 |       0 | 2                 
  tos.tsx          |       0 |      100 |       0 |       0 | 2                 
 ...rketing/+logos |       0 |      100 |     100 |       0 |                   
  logos.ts         |       0 |      100 |     100 |       0 | 25                
 app/routes/_seo   |       0 |      100 |       0 |       0 |                   
  robots[.]txt.ts  |       0 |      100 |       0 |       0 | 6                 
  sitemap[.]xml.ts |       0 |      100 |       0 |       0 | 8                 
 ...es/admin/cache |       0 |        0 |       0 |       0 |                   
  index.tsx        |       0 |        0 |       0 |       0 | 30-239            
  lru.$cacheKey.ts |       0 |        0 |       0 |       0 | 12-22             
  ....$cacheKey.ts |       0 |        0 |       0 |       0 | 12-22             
  sqlite.server.ts |       0 |        0 |       0 |       0 | 17-58             
  sqlite.tsx       |       0 |        0 |       0 |       0 |                   
 ...utes/resources |       0 |        0 |       0 |       0 |                   
  ...user-data.tsx |       0 |        0 |       0 |       0 | 7-55              
  healthcheck.tsx  |       0 |        0 |       0 |       0 | 7-24              
  images.tsx       |       0 |        0 |       0 |       0 | 8-74              
  theme-switch.tsx |       0 |        0 |       0 |       0 | 15-142            
 ...ttings/profile |       0 |        0 |       0 |       0 |                   
  _layout.tsx      |       0 |        0 |       0 |       0 | 13-63             
  ...il.server.tsx |       0 |        0 |       0 |       0 | 18-99             
  change-email.tsx |       0 |        0 |       0 |       0 | 23-116            
  connections.tsx  |       0 |        0 |       0 |       0 | 29-201            
  index.tsx        |       0 |        0 |       0 |       0 | 21-352            
  passkeys.tsx     |       0 |        0 |       0 |       0 | 12-150            
  password.tsx     |       0 |        0 |       0 |       0 | 23-140            
  ...d_.create.tsx |       0 |        0 |       0 |       0 | 20-103            
  photo.tsx        |       0 |        0 |       0 |       0 | 24-173            
 ...ile/two-factor |       0 |        0 |       0 |       0 |                   
  _layout.tsx      |       0 |      100 |       0 |       0 | 7-15              
  disable.tsx      |       0 |        0 |       0 |       0 | 14-40             
  index.tsx        |       0 |        0 |       0 |       0 | 12-46             
  verify.tsx       |       0 |        0 |       0 |       0 | 20-143            
 app/routes/users  |       0 |        0 |       0 |       0 |                   
  index.tsx        |       0 |        0 |       0 |       0 | 12-81             
 ...sers/$username |       0 |        0 |       0 |       0 |                   
  index.tsx        |       0 |        0 |       0 |       0 | 19-125            
 ...username/notes |       0 |        0 |       0 |       0 |                   
  $noteId.tsx      |       0 |        0 |       0 |       0 | 25-232            
  ...eId_.edit.tsx |       0 |      100 |       0 |       0 | 11-46             
  _layout.tsx      |       0 |        0 |       0 |       0 | 12-97             
  index.tsx        |       0 |        0 |       0 |       0 | 5-20              
  new.tsx          |       0 |      100 |       0 |       0 | 8-9               
 .../notes/+shared |       0 |        0 |       0 |       0 |                   
  ...or.server.tsx |       0 |        0 |       0 |       0 | 18-128            
  note-editor.tsx  |       0 |        0 |       0 |       0 | 26-289            
 app/utils         |       0 |        0 |       0 |       0 |                   
  auth.server.ts   |       0 |        0 |       0 |       0 | 14-292            
  cache.server.ts  |       0 |        0 |       0 |       0 | 22-211            
  client-hints.tsx |       0 |      100 |       0 |       0 | 15-48             
  ...ons.server.ts |       0 |      100 |       0 |       0 | 7-20              
  connections.tsx  |       0 |        0 |       0 |       0 | 7-34              
  db.server.ts     |       0 |        0 |       0 |       0 | 7-47              
  email.server.ts  |       0 |        0 |       0 |       0 | 5-97              
  env.server.ts    |       0 |        0 |       0 |       0 | 3-60              
  ...ers.server.ts |       0 |        0 |       0 |       0 | 18-111            
  ...pot.server.ts |       0 |        0 |       0 |       0 | 3-15              
  litefs.server.ts |       0 |        0 |       0 |       0 |                   
  misc.tsx         |       0 |        0 |       0 |       0 | 9-290             
  ...ng.client.tsx |       0 |        0 |       0 |       0 | 4-18              
  ...e-provider.ts |       0 |      100 |       0 |       0 | 3-5               
  ...ons.server.ts |       0 |        0 |       0 |       0 | 10-59             
  ...kie.server.ts |       0 |        0 |       0 |       0 | 3-16              
  request-info.ts  |       0 |      100 |       0 |       0 | 9-18              
  ...ion.server.ts |       0 |        0 |       0 |       0 | 3-36              
  ...age.server.ts |       0 |        0 |       0 |       0 | 5-175             
  theme.server.ts  |       0 |        0 |       0 |       0 | 3-17              
  timing.server.ts |       0 |        0 |       0 |       0 | 14-117            
  toast.server.ts  |       0 |        0 |       0 |       0 | 6-54              
  totp.server.ts   |       0 |        0 |       0 |       0 |                   
  ...validation.ts |       0 |        0 |       0 |       0 | 3-84              
  user.ts          |       0 |        0 |       0 |       0 | 7-69              
  ...ion.server.ts |       0 |      100 |     100 |       0 | 3                 
 ...tils/providers |       0 |        0 |       0 |       0 |                   
  constants.ts     |       0 |      100 |     100 |       0 | 1-3               
  github.server.ts |       0 |        0 |       0 |       0 | 11-152            
  provider.ts      |       0 |      100 |       0 |       0 | 25-28             
-------------------|---------|----------|---------|---------|-------------------

⎯⎯⎯⎯⎯⎯ Unhandled Error ⎯⎯⎯⎯⎯⎯⎯
ExecaError: Command failed with exit code 1: npx prisma migrate reset --force --skip-seed --skip-generate
 ❯ getFinalError node_modules/execa/lib/return/final-error.js:6:9
 ❯ makeError node_modules/execa/lib/return/result.js:108:16
 ❯ getAsyncResult node_modules/execa/lib/methods/main-async.js:168:4
 ❯ handlePromise node_modules/execa/lib/methods/main-async.js:151:17
 ❯ processTicksAndRejections node:internal/process/task_queues:105:5
 ❯ Object.setup tests/setup/global-setup.ts:28:2
     26|  }
     27| 
     28|  await execaCommand(
       |  ^
     29|   'npx prisma migrate reset --force --skip-seed --skip-generate',
     30|   {
 ❯ TestProject._initializeGlobalSetup node_modules/vitest/dist/chunks/cli-api.Cx2DW4Bc.js:10030:21
 ❯ Vitest.initializeGlobalSetup node_modules/vitest/dist/chunks/cli-api.Cx2DW4Bc.js:12730:35
 ❯ node_modules/vitest/dist/chunks/cli-api.Cx2DW4Bc.js:12593:6
 ❯ node_modules/vitest/dist/chunks/cli-api.Cx2DW4Bc.js:12622:11

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { shortMessage: 'Command failed with exit code 1: npx prisma migrate reset --force --skip-seed --skip-generate', command: 'npx prisma migrate reset --force --skip-seed --skip-generate', escapedCommand: 'npx prisma migrate reset --force --skip-seed --skip-generate', cwd: '/home/runner/work/epic-stack/epic-stack', durationMs: 1049.879893, failed: true, timedOut: false, isCanceled: false, isGracefullyCanceled: false, isTerminated: false, isMaxBuffer: false, isForcefullyTerminated: false, exitCode: 1, stdio: [ undefined, undefined, undefined ], ipcOutput: [], pipedFrom: [] }



Error: Process completed with exit code 1.
Run npm run typecheck --if-present

> typecheck
> react-router typegen && tsc

Error: app/routes/_auth/auth.$provider/callback.test.ts(29,32): error TS2345: Argument of type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' is not assignable to parameter of type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
  Property 'unstable_pattern' is missing in type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' but required in type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
Error: app/routes/_auth/auth.$provider/callback.test.ts(45,32): error TS2345: Argument of type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' is not assignable to parameter of type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
  Property 'unstable_pattern' is missing in type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' but required in type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
Error: app/routes/_auth/auth.$provider/callback.test.ts(68,32): error TS2345: Argument of type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' is not assignable to parameter of type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
  Property 'unstable_pattern' is missing in type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' but required in type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
Error: app/routes/_auth/auth.$provider/callback.test.ts(108,32): error TS2345: Argument of type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' is not assignable to parameter of type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
  Property 'unstable_pattern' is missing in type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' but required in type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
Error: app/routes/_auth/auth.$provider/callback.test.ts(127,32): error TS2345: Argument of type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' is not assignable to parameter of type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
  Property 'unstable_pattern' is missing in type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' but required in type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
Error: app/routes/_auth/auth.$provider/callback.test.ts(175,32): error TS2345: Argument of type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' is not assignable to parameter of type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
  Property 'unstable_pattern' is missing in type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' but required in type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
Error: app/routes/_auth/auth.$provider/callback.test.ts(202,32): error TS2345: Argument of type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' is not assignable to parameter of type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
  Property 'unstable_pattern' is missing in type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' but required in type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
Error: app/routes/_auth/auth.$provider/callback.test.ts(230,32): error TS2345: Argument of type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' is not assignable to parameter of type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
  Property 'unstable_pattern' is missing in type '{ request: Request; params: { provider: string; }; context: AppLoadContext; }' but required in type 'CreateServerLoaderArgs<Info & { module: typeof import("/home/runner/work/epic-stack/epic-stack/app/routes/_auth/auth.$provider/callback"); matches: Matches; }>'.
Error: app/routes/settings/profile/two-factor/verify.tsx(75,29): error TS2345: Argument of type '(data: { intent: "cancel"; } | { intent: "verify"; code: string; }, ctx: $RefinementCtx<{ intent: "cancel"; } | { intent: "verify"; code: string; }>) => Promise<null | undefined>' is not assignable to parameter of type '(arg: { intent: "cancel"; } | { intent: "verify"; code: string; }, ctx: $RefinementCtx<{ intent: "cancel"; } | { intent: "verify"; code: string; }>) => void | Promise<void>'.
  Type 'Promise<null | undefined>' is not assignable to type 'void | Promise<void>'.
    Type 'Promise<null | undefined>' is not assignable to type 'Promise<void>'.
      Type 'null | undefined' is not assignable to type 'void'.
        Type 'null' is not assignable to type 'void'.
Error: app/utils/storage.server.ts(17,44): error TS2339: Property 'stream' does not exist on type 'never'.
Error: server/index.ts(106,40): error TS2345: Argument of type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' is not assignable to parameter of type 'string'.
Error: Process completed with exit code 2.

Please figure out what's wrong. Feel free to look up docs for relevant libraries if needed.

@cursor
Copy link

cursor bot commented Jan 22, 2026

Taking a look!

Open in Cursor Open in Web

cursoragent and others added 6 commits January 22, 2026 00:37
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
@kentcdodds
Copy link
Member Author

@cursor, the tests failed in CI again. Please make certain they're working before pushing:

Run npm run test -- --coverage

> test
> vitest --coverage

🖼️  Generated SVG spritesheet in app/components/ui/icons
TS Generated icon types in app/components/ui/icons/types.ts

 RUN  v4.0.17 /home/runner/work/epic-stack/epic-stack
      Coverage enabled with v8

(node:2367) ExperimentalWarning: SQLite is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
Loaded Prisma config from prisma.config.ts.

Prisma schema loaded from prisma/schema.prisma.
Datasource "db": SQLite database "base.db" at "file:/home/runner/work/epic-stack/epic-stack/tests/prisma/base.db"

SQLite database base.db created at file:/home/runner/work/epic-stack/epic-stack/tests/prisma/base.db

1 migration found in prisma/migrations

Applying migration `20250221233640_init`

The following migration(s) have been applied:

migrations/
  └─ 20250221233640_init/
    └─ migration.sql

All migrations have been successfully applied.
(node:2442) ExperimentalWarning: SQLite is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
(node:2443) ExperimentalWarning: SQLite is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
 ❯ app/routes/users/$username/index.test.tsx (0 test)
 ❯ app/utils/misc.use-double-check.test.tsx (0 test)
(node:2479) ExperimentalWarning: SQLite is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
 ✓ app/utils/auth.server.test.ts (5 tests) 1071ms
     ✓ checkIsCommonPassword times out after 1 second  1002ms
(node:2486) ExperimentalWarning: SQLite is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
 ✓ app/utils/headers.server.test.ts (3 tests) 13ms
 ✓ app/routes/_auth/auth.$provider/callback.test.ts (8 tests) 385ms
 ✓ app/utils/misc.error-message.test.ts (3 tests) 10ms

⎯⎯⎯⎯⎯⎯ Failed Suites 2 ⎯⎯⎯⎯⎯⎯⎯

 FAIL  app/utils/misc.use-double-check.test.tsx [ app/utils/misc.use-double-check.test.tsx ]
 FAIL  app/routes/users/$username/index.test.tsx [ app/routes/users/$username/index.test.tsx ]
Error: Cannot bundle built-in module "node:sqlite" imported from "app/utils/cache.server.ts". Consider disabling environments.client.noExternal or remove the built-in dependency.
  Plugin: vite:import-analysis
  File: /home/runner/work/epic-stack/epic-stack/app/utils/cache.server.ts:3:29
  1  |  import fs from "node:fs";
  2  |  import path from "node:path";
  3  |  import { DatabaseSync } from "node:sqlite";
     |                                ^
  4  |  import {
  5  |    cachified as baseCachified,
 ❯ ResolveIdContext._formatLog node_modules/vite/dist/node/chunks/config.js:28999:43
 ❯ ResolveIdContext.error node_modules/vite/dist/node/chunks/config.js:28996:14
 ❯ ResolveIdContext.handler node_modules/vite/dist/node/chunks/config.js:32578:13
 ❯ EnvironmentPluginContainer.resolveId node_modules/vite/dist/node/chunks/config.js:28717:56
 ❯ TransformPluginContext.resolve node_modules/vite/dist/node/chunks/config.js:28929:13
 ❯ normalizeUrl node_modules/vite/dist/node/chunks/config.js:27111:22
 ❯ node_modules/vite/dist/node/chunks/config.js:27177:32
 ❯ TransformPluginContext.transform node_modules/vite/dist/node/chunks/config.js:27145:4

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯


 Test Files  2 failed | 4 passed (6)
      Tests  19 passed (19)
   Start at  00:44:26
   Duration  6.05s (transform 711ms, setup 4.97s, import 1.71s, tests 1.48s, environment 1.40s)

Error: Process completed with exit code 1.

@cursor
Copy link

cursor bot commented Jan 22, 2026

Summary

  • Addressed the CI node:sqlite bundling failure by lazy-loading node:sqlite in cache.server.ts, which unblocks jsdom bundling for the two failing suites.【F:app/utils/cache.server.ts†L1-L58】
  • Updated the Vitest DB setup to avoid Prisma 7 deprecated flags and to rebuild the base DB via migrate deploy when schema changes, preventing empty DBs in tests.【F:tests/setup/global-setup.ts†L12-L36】

Testing

  • npm run typecheck (re-ran to confirm TypeScript is clean before pushing).【F:/opt/cursor/artifacts/typecheck.log†L1-L4】
  • npm run test -- --coverage (re-ran to confirm the previously failing suites now pass; requires .env like CI provides).【F:/opt/cursor/artifacts/vitest-coverage.log†L1-L20】

Notes:

  • The CI error you pasted is specifically from the eager node:sqlite import at module load. That is now resolved by lazy-loading node:sqlite inside createDatabase, so Vite’s test bundler no longer trips over it.【F:app/utils/cache.server.ts†L1-L58】
  • I verified locally that the full vitest --coverage run passes end-to-end and logged the output for review.【F:/opt/cursor/artifacts/vitest-coverage.log†L1-L20】

Manual UI testing not run because this change is test/runtime infrastructure only and does not touch UI behavior.

Open in Cursor Open in Web

cursoragent and others added 2 commits January 22, 2026 00:54
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
@kentcdodds kentcdodds marked this pull request as ready for review January 22, 2026 03:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants