Skip to content

Commit

Permalink
feat: require Next.js 15.1 and React 19 (#305)
Browse files Browse the repository at this point in the history
7.10.0 release:
- updates and fixes the internal framework error handling detection, for
Next.js errors that have to be rethrown
- requires action callbacks and validation error shaper functions to be
async (see
[this](vercel/next.js#72336 (comment))).

The minimum required Next.js version will be stable 15.1 and minimum
React version will be 19. Older framework versions will not work with
next-safe-action >= 7.10.0, and vice versa.

## Related issue(s) or discussion(s)

- #288 
- #299
  • Loading branch information
TheEdoRan authored Dec 11, 2024
1 parent 0e160c1 commit 86f00f4
Show file tree
Hide file tree
Showing 22 changed files with 698 additions and 2,425 deletions.
6 changes: 5 additions & 1 deletion apps/playground/next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
experimental: {
authInterrupts: true,
},
};

module.exports = nextConfig;
12 changes: 6 additions & 6 deletions apps/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@
"dependencies": {
"@hookform/resolvers": "^3.9.1",
"lucide-react": "^0.460.0",
"next": "15.0.3",
"next": "15.1.0",
"next-safe-action": "workspace:*",
"react": "19.0.0-rc-69d4b800-20241021",
"react-dom": "19.0.0-rc-69d4b800-20241021",
"react": "^19",
"react-dom": "^19",
"react-hook-form": "^7.53.2",
"zod": "^3.23.8",
"zod-form-data": "^2.0.2"
},
"devDependencies": {
"@types/node": "^22",
"@types/react": "^18.3.12",
"@types/react-dom": "18.3.1",
"@types/react": "^19",
"@types/react-dom": "^19",
"autoprefixer": "10.4.20",
"eslint": "^8.57.0",
"eslint-config-next": "15.0.3",
"eslint-config-next": "15.0.4-canary.45",
"postcss": "8.4.49",
"tailwindcss": "3.4.15",
"typescript": "^5.6.3"
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"cz-conventional-changelog": "^3.3.0",
"husky": "^9.1.7",
"is-ci": "^3.0.1",
"turbo": "^2.3.0"
"turbo": "^2.3.3"
},
"packageManager": "pnpm@9.14.4+sha512.c8180b3fbe4e4bca02c94234717896b5529740a6cbadf19fa78254270403ea2f27d4e1d46a08a0f56c89b63dc8ebfd3ee53326da720273794e6200fcf0d184ab"
"packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c"
}
4 changes: 4 additions & 0 deletions packages/next-safe-action/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ module.exports = defineConfig({
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-redundant-type-constituents": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unsafe-function-type": "off",
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/prefer-promise-reject-errors": "off",
"@typescript-eslint/only-throw-error": "off",
"@typescript-eslint/ban-types": "off",
"react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/require-await": "off",
Expand Down
16 changes: 8 additions & 8 deletions packages/next-safe-action/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,17 @@
"@eslint/js": "^9.15.0",
"@sinclair/typebox": "^0.34.4",
"@types/node": "^22",
"@types/react": "^18.3.12",
"@types/react-dom": "18.3.1",
"@types/react": "^19",
"@types/react-dom": "^19",
"deepmerge-ts": "^7.1.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^2.1.0",
"eslint-plugin-react-hooks": "^5.0.0",
"next": "15.0.3",
"next": "15.1.0",
"prettier": "^3.3.3",
"react": "18.3.1",
"react-dom": "18.3.1",
"react": "^19",
"react-dom": "^19",
"semantic-release": "^23",
"tsup": "^8.3.5",
"tsx": "^4.19.2",
Expand All @@ -91,9 +91,9 @@
},
"peerDependencies": {
"@sinclair/typebox": ">= 0.33.3",
"next": ">= 14.0.0",
"react": ">= 18.2.0",
"react-dom": ">= 18.2.0",
"next": ">= 15.1.0",
"react": ">= 19.0.0",
"react-dom": ">= 19.0.0",
"valibot": ">= 0.36.0",
"yup": ">= 1.0.0",
"zod": ">= 3.0.0"
Expand Down
38 changes: 23 additions & 15 deletions packages/next-safe-action/src/__tests__/action-callbacks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ test("action with no input schema and no server errors calls `onSuccess` and `on
return;
},
{
onSuccess: () => {
onSuccess: async () => {
executed++;
},
onError: () => {
onError: async () => {
executed++; // should not be called
},
onSettled: () => {
onSettled: async () => {
executed++;
},
}
Expand All @@ -57,7 +57,15 @@ test("action with input schemas and no errors calls `onSuccess` and `onSettled`
};
},
{
onSuccess: ({ clientInput, bindArgsClientInputs, parsedInput, bindArgsParsedInputs, data, metadata, ctx }) => {
onSuccess: async ({
clientInput,
bindArgsClientInputs,
parsedInput,
bindArgsParsedInputs,
data,
metadata,
ctx,
}) => {
executed++;

assert.deepStrictEqual(
Expand All @@ -75,10 +83,10 @@ test("action with input schemas and no errors calls `onSuccess` and `onSettled`
}
);
},
onError: () => {
onError: async () => {
executed++; // should not be called
},
onSettled: ({ clientInput, bindArgsClientInputs, result, metadata, ctx }) => {
onSettled: async ({ clientInput, bindArgsClientInputs, result, metadata, ctx }) => {
executed++;

assert.deepStrictEqual(
Expand Down Expand Up @@ -115,10 +123,10 @@ test("action with input schemas and server error calls `onError` and `onSettled`
throw new Error("Server error");
},
{
onSuccess: () => {
onSuccess: async () => {
executed++; // should not be called
},
onError({ error, clientInput, bindArgsClientInputs, metadata, ctx }) {
onError: async ({ error, clientInput, bindArgsClientInputs, metadata, ctx }) => {
executed++;

assert.deepStrictEqual(
Expand All @@ -134,7 +142,7 @@ test("action with input schemas and server error calls `onError` and `onSettled`
}
);
},
onSettled({ clientInput, bindArgsClientInputs, result, metadata, ctx }) {
onSettled: async ({ clientInput, bindArgsClientInputs, result, metadata, ctx }) => {
executed++;

assert.deepStrictEqual(
Expand Down Expand Up @@ -171,10 +179,10 @@ test("action with validation errors calls `onError` and `onSettled` callbacks wi
};
},
{
onSuccess: () => {
onSuccess: async () => {
executed++; // should not be called
},
onError({ error, clientInput, bindArgsClientInputs, metadata, ctx }) {
onError: async ({ error, clientInput, bindArgsClientInputs, metadata, ctx }) => {
executed++;

assert.deepStrictEqual(
Expand Down Expand Up @@ -202,7 +210,7 @@ test("action with validation errors calls `onError` and `onSettled` callbacks wi
}
);
},
onSettled({ clientInput, bindArgsClientInputs, result, metadata, ctx }) {
onSettled: async ({ clientInput, bindArgsClientInputs, result, metadata, ctx }) => {
executed++;

assert.deepStrictEqual(
Expand Down Expand Up @@ -252,10 +260,10 @@ test("action with server validation error calls `onError` and `onSettled` callba
});
},
{
onSuccess: () => {
onSuccess: async () => {
executed++; // should not be called
},
onError({ error, clientInput, bindArgsClientInputs, metadata, ctx }) {
onError: async ({ error, clientInput, bindArgsClientInputs, metadata, ctx }) => {
executed++;

assert.deepStrictEqual(
Expand All @@ -275,7 +283,7 @@ test("action with server validation error calls `onError` and `onSettled` callba
}
);
},
onSettled({ clientInput, bindArgsClientInputs, result, metadata, ctx }) {
onSettled: async ({ clientInput, bindArgsClientInputs, result, metadata, ctx }) => {
executed++;

assert.deepStrictEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ test("action with invalid bind args input gives back an object with correct `bin
];

const action = dac
.bindArgsSchemas(bindArgsSchemas, { handleBindArgsValidationErrorsShape: flattenBindArgsValidationErrors })
.bindArgsSchemas(bindArgsSchemas, {
handleBindArgsValidationErrorsShape: async (ve) => flattenBindArgsValidationErrors(ve),
})
.action(async () => {
return {
ok: true,
Expand Down Expand Up @@ -138,7 +140,9 @@ test("action with invalid bind args input gives back an object with correct `bin
];

const action = foac
.bindArgsSchemas(bindArgsSchemas, { handleBindArgsValidationErrorsShape: flattenBindArgsValidationErrors })
.bindArgsSchemas(bindArgsSchemas, {
handleBindArgsValidationErrorsShape: async (ve) => flattenBindArgsValidationErrors(ve),
})
.action(async () => {
return {
ok: true,
Expand Down Expand Up @@ -225,7 +229,9 @@ test("action with invalid bind args input gives back an object with correct `bin
];

const action = flac
.bindArgsSchemas(bindArgsSchemas, { handleBindArgsValidationErrorsShape: formatBindArgsValidationErrors })
.bindArgsSchemas(bindArgsSchemas, {
handleBindArgsValidationErrorsShape: async (ve) => formatBindArgsValidationErrors(ve),
})
.action(async () => {
return {
ok: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ test("action with invalid bind args input and invalid main input gives back an o

const action = dac
.schema(schema)
.bindArgsSchemas(bindArgsSchemas, { handleBindArgsValidationErrorsShape: flattenBindArgsValidationErrors })
.bindArgsSchemas(bindArgsSchemas, {
handleBindArgsValidationErrorsShape: async (ve) => flattenBindArgsValidationErrors(ve),
})
.action(async () => {
return {
ok: true,
Expand Down Expand Up @@ -131,7 +133,7 @@ test("action with invalid bind args input and invalid main input gives back an o
];

const action = foac
.schema(schema, { handleValidationErrorsShape: flattenValidationErrors })
.schema(schema, { handleValidationErrorsShape: async (ve) => flattenValidationErrors(ve) })
.bindArgsSchemas(bindArgsSchemas)
.action(async () => {
return {
Expand Down Expand Up @@ -179,7 +181,9 @@ test("action with invalid bind args input and valid main input gives back an obj

const action = foac
.schema(schema)
.bindArgsSchemas(bindArgsSchemas, { handleBindArgsValidationErrorsShape: flattenBindArgsValidationErrors })
.bindArgsSchemas(bindArgsSchemas, {
handleBindArgsValidationErrorsShape: async (ve) => flattenBindArgsValidationErrors(ve),
})
.action(async () => {
return {
ok: true,
Expand Down Expand Up @@ -287,8 +291,10 @@ test("action with invalid bind args input, invalid main input and root level sch
];

const action = flac
.schema(schema, { handleValidationErrorsShape: formatValidationErrors })
.bindArgsSchemas(bindArgsSchemas, { handleBindArgsValidationErrorsShape: formatBindArgsValidationErrors })
.schema(schema, { handleValidationErrorsShape: async (ve) => formatValidationErrors(ve) })
.bindArgsSchemas(bindArgsSchemas, {
handleBindArgsValidationErrorsShape: async (ve) => formatBindArgsValidationErrors(ve),
})
.action(async () => {
return {
ok: true,
Expand Down
4 changes: 2 additions & 2 deletions packages/next-safe-action/src/__tests__/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,10 @@ test("overridden formatted validation errors in execution result from middleware
z.object({
username: z.string().max(3),
}),
{ handleValidationErrorsShape: formatValidationErrors }
{ handleValidationErrorsShape: async (ve) => formatValidationErrors(ve) }
)
.bindArgsSchemas([z.object({ age: z.number().positive() })], {
handleBindArgsValidationErrorsShape: formatBindArgsValidationErrors,
handleBindArgsValidationErrorsShape: async (ve) => formatBindArgsValidationErrors(ve),
})
.use(async ({ next }) => {
// Await action execution.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ test("action with invalid input gives back an object with correct `validationErr

const action = dac
.schema(schema, {
handleValidationErrorsShape: flattenValidationErrors,
handleValidationErrorsShape: async (ve) => flattenValidationErrors(ve),
})
.action(async () => {
return {
Expand Down Expand Up @@ -318,7 +318,7 @@ test("action with invalid input gives back an object with correct `validationErr

const action = foac
.schema(schema, {
handleValidationErrorsShape: flattenValidationErrors,
handleValidationErrorsShape: async (ve) => flattenValidationErrors(ve),
})
.action(async () => {
return {
Expand Down Expand Up @@ -452,7 +452,7 @@ test("action with invalid input gives back an object with correct `validationErr

const action = flac
.schema(schema, {
handleValidationErrorsShape: formatValidationErrors,
handleValidationErrorsShape: async (ve) => formatValidationErrors(ve),
})
.action(async () => {
return {
Expand Down
Loading

0 comments on commit 86f00f4

Please sign in to comment.