Skip to content

Commit

Permalink
Merge pull request #5 from janglad/export-result
Browse files Browse the repository at this point in the history
Export Result, Ok, Err
  • Loading branch information
janglad authored Aug 11, 2024
2 parents ad23fc9 + 2b811ba commit 7d368b0
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 67 deletions.
5 changes: 5 additions & 0 deletions .changeset/quiet-berries-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"safe-fn": patch
---

Renames `Ok()` and `Err()` to `ok()` and `err()` to avoid overlap with types. Exports `ok()`, `err()`, `Ok`, `Err`, `Result` to be available to users.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,18 @@ export const createUser = SafeFn.new()
.action(async (input) => {
try {
const user = await db.user.insert(input.parsedInput);
return Ok(user);
return ok(user);
} catch (error) {
if (error instanceof DbError) {
if (error.code === "UNIQUE_CONSTRAINT") {
return Err({
return err({
code: "DUPLICATE_USER",
message: "User already exists",
});
}
}

return Err({
return err({
code: "INTERNAL_ERROR",
message: "Something went wrong",
});
Expand Down Expand Up @@ -86,22 +86,22 @@ const safeFn = SafeFn.new()
}),
)
.error((error) => {
return Err({
return err({
code: "CAUGHT_ERROR",
cause,
});
})
.action(async ({ parsedInput }) => {
const isProfane = await isProfaneName(parsedInput.firstName);
if (isProfane) {
return Err({
return err({
code: "PROFANE_NAME",
message: "Name is profane",
});
}

const fullName = `${parsedInput.firstName} ${parsedInput.lastName}`;
return Ok({ fullName });
return ok({ fullName });
});
```

Expand Down
6 changes: 6 additions & 0 deletions packages/safe-fn/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { type Err, type Ok, type Result, err, ok } from "./result";
import { SafeFn } from "./safe-fn";

import type {
Expand All @@ -23,14 +24,19 @@ export type {
AnySafeFn,
AnySafeFnThrownHandler,
BuilderSteps,
Err,
InferInputSchema,
InferOutputSchema,
InferRunArgs,
InferUnparsedInput,
Ok,
Result,
SafeFnActionFn,
SafeFnReturn,
SafeFnReturnData,
SafeFnReturnError,
SafeFnRunArgs,
TSafeFn,
err,
ok,
};
4 changes: 2 additions & 2 deletions packages/safe-fn/src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type Ok<TData> = {
};
export type InferOkData<T> = T extends Ok<infer TData> ? TData : never;

export const Ok = <const TData>(data: TData): Ok<TData> => ({
export const ok = <const TData>(data: TData): Ok<TData> => ({
success: true,
data,
error: undefined as never,
Expand All @@ -22,7 +22,7 @@ export type Err<TError> = {
export type InferErrError<T> = T extends Err<infer TError> ? TError : never;

export type AnyErr = Err<any>;
export const Err = <const TError>(error: TError): Err<TError> => ({
export const err = <const TError>(error: TError): Err<TError> => ({
success: false,
error,
data: undefined as never,
Expand Down
36 changes: 18 additions & 18 deletions packages/safe-fn/src/safe-fn.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expectTypeOf, test } from "vitest";
import { z } from "zod";
import { Err, Ok, type InferErrError, type Result } from "./result";
import { err, ok, type Err, type InferErrError, type Result } from "./result";
import { SafeFn } from "./safe-fn";
import type {
SafeFnDefaultThrowHandler,
Expand Down Expand Up @@ -265,7 +265,7 @@ describe("run", () => {
const inputSchema = z.string();
const safeFn = SafeFn.new()
.input(inputSchema)
.action((args) => Ok(args.parsedInput));
.action((args) => ok(args.parsedInput));

type RunInput = Parameters<typeof safeFn.run>[0];

Expand All @@ -281,7 +281,7 @@ describe("run", () => {
});
const safeFn = SafeFn.new()
.input(inputSchema)
.action((args) => Ok(args.parsedInput));
.action((args) => ok(args.parsedInput));

type RunInput = Parameters<typeof safeFn.run>[0];

Expand All @@ -299,7 +299,7 @@ describe("run", () => {
.transform(({ test }) => ({ test, newProperty: "test" }));
const safeFn = SafeFn.new()
.input(inputSchema)
.action((args) => Ok(args.parsedInput));
.action((args) => ok(args.parsedInput));

type RunInput = Parameters<typeof safeFn.run>[0];

Expand All @@ -320,7 +320,7 @@ describe("run", () => {
// >();
// });
test("should infer success return type from action when no output schema is provided", async () => {
const safeFn = SafeFn.new().action(() => Ok("data" as const));
const safeFn = SafeFn.new().action(() => ok("data" as const));

expectTypeOf(safeFn.run({})).resolves.toMatchTypeOf<
Result<"data", any>
Expand All @@ -331,7 +331,7 @@ describe("run", () => {
const outputSchema = z.string().transform((data) => data + "!");
const safeFn = SafeFn.new()
.output(outputSchema)
.action(() => Ok(""));
.action(() => ok(""));

const res = await safeFn.run({});
expectTypeOf(res).toMatchTypeOf<
Expand All @@ -342,7 +342,7 @@ describe("run", () => {

describe("error", () => {
test("should infer Err return as default when no error function is set", async () => {
const safeFn = SafeFn.new().action(() => Ok("data" as const));
const safeFn = SafeFn.new().action(() => ok("data" as const));

type Res = Awaited<ReturnType<typeof safeFn.run>>;
type InferredErrError = InferErrError<Res>;
Expand All @@ -352,7 +352,7 @@ describe("run", () => {
});

test("should infer Err return type from action when no error function is set", async () => {
const safeFn = SafeFn.new().action(() => Err("my error" as const));
const safeFn = SafeFn.new().action(() => err("my error" as const));
type Res = Awaited<ReturnType<typeof safeFn.run>>;
type InferredErrError = InferErrError<Res>;
expectTypeOf<InferredErrError>().toEqualTypeOf<
Expand All @@ -363,9 +363,9 @@ describe("run", () => {
test("should infer Err return type from action when error function is set", async () => {
const safeFn = SafeFn.new()
.action(() => {
return Err("error" as const);
return err("error" as const);
})
.error(() => Err("thrown" as const));
.error(() => err("thrown" as const));

type Res = Awaited<ReturnType<typeof safeFn.run>>;
type InferredErrError = InferErrError<Res>;
Expand Down Expand Up @@ -471,7 +471,7 @@ describe("internals", () => {

describe("error", () => {
test("should properly type the _uncaughtErrorHandler function", () => {
const safeFn = SafeFn.new().error((error) => Err("hello" as const));
const safeFn = SafeFn.new().error((error) => err("hello" as const));

type res = ReturnType<typeof safeFn._uncaughtErrorHandler>;
expectTypeOf(safeFn._uncaughtErrorHandler).toEqualTypeOf<
Expand All @@ -484,7 +484,7 @@ describe("parent", () => {
test("should properly type the _parent function", () => {
const safeFn1 = SafeFn.new()
.input(z.object({ name: z.string() }))
.action(() => Ok(""));
.action(() => ok(""));
const safeFn2 = SafeFn.new(safeFn1).input(z.object({ age: z.number() }));
expectTypeOf(safeFn2._parent).toEqualTypeOf(safeFn1);
});
Expand All @@ -499,7 +499,7 @@ describe("parent", () => {
const input2 = z.object({ age: z.number() });
const safeFn1 = SafeFn.new()
.input(input1)
.action(() => Ok(""));
.action(() => ok(""));
const safeFn2 = SafeFn.new(safeFn1).input(input2);

type S2ParsedInput = Parameters<
Expand All @@ -525,7 +525,7 @@ describe("parent", () => {

const safeFn1 = SafeFn.new()
.input(input1)
.action(() => Ok(""));
.action(() => ok(""));
const safeFn2 = SafeFn.new(safeFn1).input(input2);

type S2ParsedInput = Parameters<
Expand All @@ -539,7 +539,7 @@ describe("parent", () => {

test("should take parsedInput from child when parent has no input schema", () => {
const input = z.object({ name: z.string() });
const safeFn1 = SafeFn.new().action((args) => Ok(args.parsedInput));
const safeFn1 = SafeFn.new().action((args) => ok(args.parsedInput));
const safeFn2 = SafeFn.new(safeFn1).input(input);

type S2ParsedInput = Parameters<
Expand All @@ -553,7 +553,7 @@ describe("parent", () => {
const input = z.object({ name: z.string() });
const safeFn1 = SafeFn.new()
.input(input)
.action(() => Ok(""));
.action(() => ok(""));
const safeFn2 = SafeFn.new(safeFn1);

type S2ParsedInput = Parameters<
Expand All @@ -566,7 +566,7 @@ describe("parent", () => {

describe("ctx", () => {
test("should type ctx as unwrapped OK value from parent", () => {
const safeFn1 = SafeFn.new().action(() => Ok("ctx return" as const));
const safeFn1 = SafeFn.new().action(() => ok("ctx return" as const));
const safeFn2 = SafeFn.new(safeFn1);

type S2Ctx = Parameters<
Expand All @@ -576,7 +576,7 @@ describe("parent", () => {
});

test("should type ctx as empty object if parent never returns", () => {
const safeFn1 = SafeFn.new().action(() => Err("ctx return" as const));
const safeFn1 = SafeFn.new().action(() => err("ctx return" as const));
const safeFn2 = SafeFn.new(safeFn1);

type S2Ctx = Parameters<
Expand Down
Loading

0 comments on commit 7d368b0

Please sign in to comment.