Skip to content

Commit

Permalink
feat(middleware): extend context by default when defining middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
TheEdoRan committed Aug 11, 2024
1 parent ed3bd85 commit 53489fe
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 35 deletions.
4 changes: 2 additions & 2 deletions packages/next-safe-action/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,13 @@
"zod": "^3.23.6"
},
"peerDependencies": {
"@sinclair/typebox": ">= 0.33.3",
"next": ">= 14.0.0",
"react": ">= 18.2.0",
"react-dom": ">= 18.2.0",
"valibot": ">= 0.36.0",
"yup": ">= 1.0.0",
"zod": ">= 3.0.0",
"@sinclair/typebox": ">= 0.33.3"
"zod": ">= 3.0.0"
},
"peerDependenciesMeta": {
"zod": {
Expand Down
29 changes: 15 additions & 14 deletions packages/next-safe-action/src/action-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
ServerCodeFn,
StateServerCodeFn,
} from "./index.types";
import { ActionMetadataError, DEFAULT_SERVER_ERROR_MESSAGE, isError } from "./utils";
import { ActionMetadataError, deepMerge, DEFAULT_SERVER_ERROR_MESSAGE, isError } from "./utils";
import { ActionServerValidationError, ActionValidationError, buildValidationErrors } from "./validation-errors";
import type {
BindArgsValidationErrors,
Expand Down Expand Up @@ -71,7 +71,7 @@ export function actionBuilder<
utils?: SafeActionUtils<ServerError, MD, Ctx, S, BAS, CVE, CBAVE, Data>
) => {
return async (...clientInputs: unknown[]) => {
let prevCtx: object = {};
let currentCtx: object = {};
const middlewareResult: MiddlewareResult<ServerError, object> = { success: false };
type PrevResult = SafeActionResult<ServerError, S, BAS, CVE, CBAVE, Data> | undefined;
let prevResult: PrevResult | undefined = undefined;
Expand Down Expand Up @@ -99,7 +99,7 @@ export function actionBuilder<
}

const middlewareFn = args.middlewareFns[idx];
middlewareResult.ctx = prevCtx;
middlewareResult.ctx = currentCtx;

try {
if (idx === 0) {
Expand All @@ -118,10 +118,11 @@ export function actionBuilder<
await middlewareFn({
clientInput: clientInputs.at(-1), // pass raw client input
bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [],
ctx: prevCtx,
ctx: currentCtx,
metadata: args.metadata,
next: async ({ ctx }) => {
prevCtx = ctx;
next: async (nextOpts) => {
currentCtx = deepMerge(currentCtx, nextOpts?.ctx ?? {});
// currentCtx = { ...cloneDeep(currentCtx), ...(nextOpts?.ctx ?? {}) };
await executeMiddlewareStack(idx + 1);
return middlewareResult;
},
Expand Down Expand Up @@ -196,7 +197,7 @@ export function actionBuilder<
scfArgs[0] = {
parsedInput: parsedInputDatas.at(-1) as S extends Schema ? Infer<S> : undefined,
bindArgsParsedInputs: parsedInputDatas.slice(0, -1) as InferArray<BAS>,
ctx: prevCtx as Ctx,
ctx: currentCtx as Ctx,
metadata: args.metadata,
};

Expand Down Expand Up @@ -234,7 +235,7 @@ export function actionBuilder<
args.handleReturnedServerError(error, {
clientInput: clientInputs.at(-1), // pass raw client input
bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [],
ctx: prevCtx,
ctx: currentCtx,
metadata: args.metadata as MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined,
})
);
Expand All @@ -246,7 +247,7 @@ export function actionBuilder<
returnedError,
clientInput: clientInputs.at(-1), // pass raw client input
bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [],
ctx: prevCtx,
ctx: currentCtx,
metadata: args.metadata as MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined,
})
);
Expand All @@ -263,7 +264,7 @@ export function actionBuilder<
utils?.onSuccess?.({
data: undefined,
metadata: args.metadata,
ctx: prevCtx as Ctx,
ctx: currentCtx as Ctx,
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
parsedInput: parsedInputDatas.at(-1) as S extends Schema ? Infer<S> : undefined,
Expand All @@ -276,7 +277,7 @@ export function actionBuilder<
await Promise.resolve(
utils?.onSettled?.({
metadata: args.metadata,
ctx: prevCtx as Ctx,
ctx: currentCtx as Ctx,
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
result: {},
Expand Down Expand Up @@ -324,7 +325,7 @@ export function actionBuilder<
await Promise.resolve(
utils?.onSuccess?.({
metadata: args.metadata,
ctx: prevCtx as Ctx,
ctx: currentCtx as Ctx,
data: actionResult.data as Data,
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
Expand All @@ -338,7 +339,7 @@ export function actionBuilder<
await Promise.resolve(
utils?.onError?.({
metadata: args.metadata,
ctx: prevCtx as Ctx,
ctx: currentCtx as Ctx,
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
error: actionResult,
Expand All @@ -350,7 +351,7 @@ export function actionBuilder<
await Promise.resolve(
utils?.onSettled?.({
metadata: args.metadata,
ctx: prevCtx as Ctx,
ctx: currentCtx as Ctx,
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
result: actionResult,
Expand Down
14 changes: 7 additions & 7 deletions packages/next-safe-action/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ export type MiddlewareFn<ServerError, MD, Ctx extends object, NextCtx extends ob
(opts: {
clientInput: unknown;
bindArgsClientInputs: unknown[];
ctx: Ctx;
ctx: Prettify<Ctx>;
metadata: MD;
next: {
<NC extends object>(opts: { ctx: NC }): Promise<MiddlewareResult<ServerError, NC>>;
<NC extends object = {}>(opts?: { ctx?: NC }): Promise<MiddlewareResult<ServerError, NC>>;
};
}): Promise<MiddlewareResult<ServerError, NextCtx>>;
};
Expand All @@ -132,7 +132,7 @@ export type ServerCodeFn<
> = (args: {
parsedInput: S extends Schema ? Infer<S> : undefined;
bindArgsParsedInputs: InferArray<BAS>;
ctx: Ctx;
ctx: Prettify<Ctx>;
metadata: MD;
}) => Promise<Data>;

Expand All @@ -152,7 +152,7 @@ export type StateServerCodeFn<
args: {
parsedInput: S extends Schema ? Infer<S> : undefined;
bindArgsParsedInputs: InferArray<BAS>;
ctx: Ctx;
ctx: Prettify<Ctx>;
metadata: MD;
},
utils: { prevResult: Prettify<SafeActionResult<ServerError, S, BAS, CVE, CBAVE, Data>> }
Expand All @@ -176,7 +176,7 @@ export type SafeActionUtils<
onSuccess?: (args: {
data?: Data;
metadata: MD;
ctx?: Ctx;
ctx?: Prettify<Ctx>;
clientInput: S extends Schema ? InferIn<S> : undefined;
bindArgsClientInputs: InferInArray<BAS>;
parsedInput: S extends Schema ? Infer<S> : undefined;
Expand All @@ -187,14 +187,14 @@ export type SafeActionUtils<
onError?: (args: {
error: Prettify<Omit<SafeActionResult<ServerError, S, BAS, CVE, CBAVE, Data>, "data">>;
metadata: MD;
ctx?: Ctx;
ctx?: Prettify<Ctx>;
clientInput: S extends Schema ? InferIn<S> : undefined;
bindArgsClientInputs: InferInArray<BAS>;
}) => MaybePromise<void>;
onSettled?: (args: {
result: Prettify<SafeActionResult<ServerError, S, BAS, CVE, CBAVE, Data>>;
metadata: MD;
ctx?: Ctx;
ctx?: Prettify<Ctx>;
clientInput: S extends Schema ? InferIn<S> : undefined;
bindArgsClientInputs: InferInArray<BAS>;
hasRedirected: boolean;
Expand Down
4 changes: 2 additions & 2 deletions packages/next-safe-action/src/safe-action-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class SafeActionClient<
*
* {@link https://next-safe-action.dev/docs/safe-action-client/instance-methods#use See docs for more information}
*/
use<NextCtx extends object>(middlewareFn: MiddlewareFn<ServerError, MD, Ctx, NextCtx>) {
use<NextCtx extends object>(middlewareFn: MiddlewareFn<ServerError, MD, Ctx, Ctx & NextCtx>) {
return new SafeActionClient({
middlewareFns: [...this.#middlewareFns, middlewareFn],
handleReturnedServerError: this.#handleReturnedServerError,
Expand All @@ -99,7 +99,7 @@ export class SafeActionClient<
validationAdapter: this.#validationAdapter,
handleValidationErrorsShape: this.#handleValidationErrorsShape,
handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape,
ctxType: {} as NextCtx,
ctxType: {} as Ctx & NextCtx,
defaultValidationErrorsShape: this.#defaultValidationErrorsShape,
throwValidationErrors: this.#throwValidationErrors,
});
Expand Down
16 changes: 16 additions & 0 deletions packages/next-safe-action/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ export const DEFAULT_SERVER_ERROR_MESSAGE = "Something went wrong while executin

export const isError = (error: unknown): error is Error => error instanceof Error;

export const deepMerge = (obj1: object, obj2: object) => {
for (const key of Object.keys(obj2)) {
const k = key as keyof typeof obj2;
// eslint-disable-next-line
if (typeof obj2[k] === "object" && Object.hasOwn(obj1, k)) {
// @ts-expect-error
if (!obj1[k]) obj1[k] = {};
deepMerge(obj1[k], obj2[k]);
} else {
obj1[k] = obj2[k];
}
}

return obj1;
};

/**
* This error is thrown when an action's metadata input is invalid, i.e. when there's a mismatch between the
* type of the metadata schema returned from `defineMetadataSchema` and the actual input.
Expand Down
2 changes: 1 addition & 1 deletion packages/next-safe-action/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"target": "ESNext",
"module": "CommonJS",
"lib": ["ES2021.String"],
"lib": ["ES2022"],
"skipLibCheck": true,
"sourceMap": true,
"outDir": "./dist",
Expand Down
18 changes: 9 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 53489fe

Please sign in to comment.