Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: TheEdoRan/next-safe-action
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v7.7.1
Choose a base ref
...
head repository: TheEdoRan/next-safe-action
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v7.8.0
Choose a head ref
  • 10 commits
  • 43 files changed
  • 1 contributor

Commits on Aug 21, 2024

  1. chore: update logo (#241)

    Update library logo.
    TheEdoRan authored Aug 21, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    AkihiroSuda Akihiro Suda
    Copy the full SHA
    1a34b47 View commit details

Commits on Aug 24, 2024

  1. chore: update pnpm to 9.8.0

    TheEdoRan committed Aug 24, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    AkihiroSuda Akihiro Suda
    Copy the full SHA
    ccacfe0 View commit details
  2. chore(website): remove null ctx from examples

    TheEdoRan committed Aug 24, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    AkihiroSuda Akihiro Suda
    Copy the full SHA
    0b641ac View commit details

Commits on Aug 28, 2024

  1. chore: update pnpm to 9.9.0

    TheEdoRan committed Aug 28, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    AkihiroSuda Akihiro Suda
    Copy the full SHA
    47ff660 View commit details
  2. chore(website): update middleware page

    TheEdoRan committed Aug 28, 2024
    Copy the full SHA
    cf476f2 View commit details
  3. Copy the full SHA
    0dec8b6 View commit details
  4. chore(website): update landing page

    TheEdoRan committed Aug 28, 2024
    Copy the full SHA
    52c1947 View commit details
  5. chore(website): improve docs

    TheEdoRan committed Aug 28, 2024
    Copy the full SHA
    39c0525 View commit details

Commits on Aug 29, 2024

  1. chore(github): add Node.js version field to issue template

    TheEdoRan committed Aug 29, 2024
    Copy the full SHA
    84e15e3 View commit details
  2. feat: support output data validation (#250)

    This PR adds the `outputSchema` method to allow for optional validation
    of the action's return value.
    
    re #245
    TheEdoRan authored Aug 29, 2024
    Copy the full SHA
    81cd392 View commit details
Showing with 354 additions and 310 deletions.
  1. +2 −1 .eslintignore
  2. +6 −0 .github/ISSUE_TEMPLATE/1_bug_report.yml
  3. +1 −1 README.md
  4. BIN assets/logo.png
  5. +2 −2 package.json
  6. +1 −1 packages/next-safe-action/README.md
  7. +15 −9 packages/next-safe-action/src/__tests__/happy-path.test.ts
  8. +61 −1 packages/next-safe-action/src/__tests__/validation-errors.test.ts
  9. +55 −39 packages/next-safe-action/src/action-builder.ts
  10. +5 −2 packages/next-safe-action/src/index.ts
  11. +67 −32 packages/next-safe-action/src/safe-action-client.ts
  12. +0 −11 packages/next-safe-action/src/utils.ts
  13. +31 −2 packages/next-safe-action/src/validation-errors.ts
  14. +1 −6 packages/next-safe-action/src/validation-errors.types.ts
  15. +29 −29 pnpm-lock.yaml
  16. +1 −1 website/docs/contributing.md
  17. +1 −1 website/docs/execution/index.md
  18. +1 −1 website/docs/getting-started.md
  19. +1 −1 website/docs/integrations/index.md
  20. +0 −27 website/docs/introduction.md
  21. +1 −1 website/docs/migrations/index.md
  22. +1 −1 website/docs/recipes/index.md
  23. +1 −1 website/docs/safe-action-client/extend-a-client.md
  24. +1 −1 website/docs/safe-action-client/index.md
  25. +6 −6 website/docs/safe-action-client/initialization-options.md
  26. +9 −1 website/docs/safe-action-client/instance-methods.md
  27. +7 −46 website/docs/safe-action-client/middleware.md
  28. +3 −4 website/docs/troubleshooting.md
  29. +2 −2 website/docs/types/index.md
  30. +6 −1 website/docusaurus.config.js
  31. +3 −3 website/src/components/landing/features.tsx
  32. +3 −8 website/src/components/landing/getting-started.tsx
  33. +16 −8 website/src/components/landing/hero.tsx
  34. +0 −40 website/src/components/landing/stars-button.tsx
  35. +5 −1 website/src/css/custom.css
  36. BIN website/static/img/favicon-16x16.png
  37. BIN website/static/img/favicon-32x32.png
  38. BIN website/static/img/favicon.ico
  39. +5 −0 website/static/img/logo-dark-mode.svg
  40. +5 −0 website/static/img/logo-light-mode.svg
  41. BIN website/static/img/logo.png
  42. +0 −19 website/static/img/logo.svg
  43. BIN website/static/img/social-card.png
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
**/*.js
**/*.js
/website
6 changes: 6 additions & 0 deletions .github/ISSUE_TEMPLATE/1_bug_report.yml
Original file line number Diff line number Diff line change
@@ -71,6 +71,12 @@ body:
placeholder: 14.x.x
validations:
required: true
- type: input
attributes:
label: Node.js version
placeholder: 20.x.x
validations:
required: true
- type: textarea
attributes:
label: Additional context
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ https://github.com/TheEdoRan/next-safe-action/assets/1337629/664eb3ee-92f3-4d4a-
- ✅ End-to-end type safety
- ✅ Form Actions support
- ✅ Powerful middleware system
- ✅ Input validation using multiple validation libraries
- ✅ Input/output validation using multiple validation libraries
- ✅ Advanced server error handling
- ✅ Optimistic updates

Binary file modified assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@
"cz-conventional-changelog": "^3.3.0",
"husky": "^9.0.11",
"is-ci": "^3.0.1",
"turbo": "^2.0.14"
"turbo": "^2.1.0"
},
"packageManager": "pnpm@9.7.1+sha512.faf344af2d6ca65c4c5c8c2224ea77a81a5e8859cbc4e06b1511ddce2f0151512431dd19e6aff31f2c6a8f5f2aced9bd2273e1fed7dd4de1868984059d2c4247"
"packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1"
}
2 changes: 1 addition & 1 deletion packages/next-safe-action/README.md
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ https://github.com/TheEdoRan/next-safe-action/assets/1337629/664eb3ee-92f3-4d4a-
- ✅ End-to-end type safety
- ✅ Form Actions support
- ✅ Powerful middleware system
- ✅ Input validation using multiple validation libraries
- ✅ Input/output validation using multiple validation libraries
- ✅ Advanced server error handling
- ✅ Optimistic updates

24 changes: 15 additions & 9 deletions packages/next-safe-action/src/__tests__/happy-path.test.ts
Original file line number Diff line number Diff line change
@@ -38,14 +38,17 @@ test("action with no input schema and return data gives back an object with corr
assert.deepStrictEqual(actualResult, expectedResult);
});

test("action with input schema and return data gives back an object with correct `data`", async () => {
test("action with input, output schema and return data gives back an object with correct `data`", async () => {
const userId = "ed6f5b84-6bca-4d01-9a51-c3d0c49a7996";

const action = ac.schema(z.object({ userId: z.string().uuid() })).action(async ({ parsedInput }) => {
return {
userId: parsedInput.userId,
};
});
const action = ac
.schema(z.object({ userId: z.string().uuid() }))
.outputSchema(z.object({ userId: z.string() }))
.action(async ({ parsedInput }) => {
return {
userId: parsedInput.userId,
};
});

const actualResult = await action({ userId });

@@ -80,13 +83,14 @@ test("action with input schema passed via async function and return data gives b
assert.deepStrictEqual(actualResult, expectedResult);
});

test("action with input schema extended via async function and return data gives back an object with correct `data`", async () => {
test("action with input schema extended via async function, ouput schema and return data gives back an object with correct `data`", async () => {
const userId = "ed6f5b84-6bca-4d01-9a51-c3d0c49a7996";
const password = "password";

const action = ac
.schema(z.object({ password: z.string() }))
.schema(async (prevSchema) => prevSchema.extend({ userId: z.string().uuid() }))
.outputSchema(z.object({ userId: z.string(), password: z.string() }))
.action(async ({ parsedInput }) => {
return {
userId: parsedInput.userId,
@@ -106,12 +110,13 @@ test("action with input schema extended via async function and return data gives
assert.deepStrictEqual(actualResult, expectedResult);
});

test("action with no input schema, bind args input schemas and return data gives back an object with correct `data`", async () => {
test("action with no input schema, with bind args input schemas, output schema and return data gives back an object with correct `data`", async () => {
const username = "johndoe";
const age = 30;

const action = ac
.bindArgsSchemas<[username: z.ZodString, age: z.ZodNumber]>([z.string(), z.number()])
.outputSchema(z.object({ username: z.string(), age: z.number() }))
.action(async ({ bindArgsParsedInputs: [username, age] }) => {
return {
username,
@@ -131,14 +136,15 @@ test("action with no input schema, bind args input schemas and return data gives
assert.deepStrictEqual(actualResult, expectedResult);
});

test("action with input schema, bind args input schemas and return data gives back an object with correct `data`", async () => {
test("action with input schema, bind args input schemas, output schema and return data gives back an object with correct `data`", async () => {
const userId = "ed6f5b84-6bca-4d01-9a51-c3d0c49a7996";
const username = "johndoe";
const age = 30;

const action = ac
.schema(z.object({ userId: z.string().uuid() }))
.bindArgsSchemas<[username: z.ZodString, age: z.ZodNumber]>([z.string(), z.number()])
.outputSchema(z.object({ userId: z.string(), username: z.string(), age: z.number() }))
.action(async ({ parsedInput, bindArgsParsedInputs: [username, age] }) => {
return {
userId: parsedInput.userId,
Original file line number Diff line number Diff line change
@@ -3,8 +3,16 @@
import assert from "node:assert";
import { test } from "node:test";
import { z } from "zod";
import { createSafeActionClient, flattenValidationErrors, formatValidationErrors, returnValidationErrors } from "..";
import type { ValidationErrors } from "..";
import {
createSafeActionClient,
DEFAULT_SERVER_ERROR_MESSAGE,
flattenValidationErrors,
formatValidationErrors,
returnValidationErrors,
} from "..";
import { zodAdapter } from "../adapters/zod";
import { ActionOutputDataValidationError } from "../validation-errors";

// Default client tests.

@@ -144,6 +152,58 @@ test("action with invalid input gives back an object with correct `validationErr
assert.deepStrictEqual(actualResult, expectedResult);
});

test("action with invalid output data returns the default `serverError`", async () => {
const action = dac.outputSchema(z.object({ result: z.string().min(3) })).action(async () => {
return {
result: "ok",
};
});

const actualResult = await action();

const expectedResult = {
serverError: DEFAULT_SERVER_ERROR_MESSAGE,
};

assert.deepStrictEqual(actualResult, expectedResult);
});

test("action with invalid output data throws an error of the correct type", async () => {
const tac = createSafeActionClient({
validationAdapter: zodAdapter(),
handleReturnedServerError: (e) => {
throw e;
},
});

const outputSchema = z.object({ result: z.string().min(3) });

const action = tac.outputSchema(outputSchema).action(async () => {
return {
result: "ok",
};
});

const expectedResult = {
serverError: "String must contain at least 3 character(s)",
};

const actualResult = {
serverError: "",
};

try {
await action();
} catch (e) {
if (e instanceof ActionOutputDataValidationError) {
actualResult.serverError =
(e.validationErrors as ValidationErrors<typeof outputSchema>).result?._errors?.[0] ?? "";
}
}

assert.deepStrictEqual(actualResult, expectedResult);
});

// Formatted shape tests (same as default).

const foac = createSafeActionClient({
Loading