Skip to content

Commit e59e917

Browse files
authoredJun 13, 2024··
refactor: add ctx and metadata props to action callbacks (#167)
This PR adds `ctx` and `metadata` props to `onSuccess`, `onError` and `onSettled` action callbacks.
1 parent 1c32f6a commit e59e917

File tree

6 files changed

+113
-41
lines changed

6 files changed

+113
-41
lines changed
 

‎packages/next-safe-action/src/__tests__/action-callbacks.test.ts

+43-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,17 @@ import { test } from "node:test";
55
import { z } from "zod";
66
import { DEFAULT_SERVER_ERROR_MESSAGE, createSafeActionClient, returnValidationErrors } from "..";
77

8-
const ac = createSafeActionClient();
8+
const ac = createSafeActionClient({
9+
defineMetadataSchema() {
10+
return z.object({
11+
actionName: z.string(),
12+
});
13+
},
14+
})
15+
.use(async ({ next }) => {
16+
return next({ ctx: { foo: "bar" } });
17+
})
18+
.metadata({ actionName: "test" });
919

1020
test("action with no input schema and no server errors calls `onSuccess` and `onSettled` callbacks", async () => {
1121
let executed = 0;
@@ -45,12 +55,14 @@ test("action with input schemas and no errors calls `onSuccess` and `onSettled`
4555
};
4656
},
4757
{
48-
onSuccess: ({ clientInput, bindArgsClientInputs, parsedInput, bindArgsParsedInputs, data }) => {
58+
onSuccess: ({ clientInput, bindArgsClientInputs, parsedInput, bindArgsParsedInputs, data, metadata, ctx }) => {
4959
executed++;
5060

5161
assert.deepStrictEqual(
52-
{ clientInput, bindArgsClientInputs, parsedInput, bindArgsParsedInputs, data },
62+
{ clientInput, bindArgsClientInputs, parsedInput, bindArgsParsedInputs, data, metadata, ctx },
5363
{
64+
metadata: { actionName: "test" },
65+
ctx: { foo: "bar" },
5466
clientInput: inputs[2],
5567
bindArgsClientInputs: inputs.slice(0, 2),
5668
parsedInput: inputs[2],
@@ -64,12 +76,14 @@ test("action with input schemas and no errors calls `onSuccess` and `onSettled`
6476
onError: () => {
6577
executed++; // should not be called
6678
},
67-
onSettled: ({ clientInput, bindArgsClientInputs, result }) => {
79+
onSettled: ({ clientInput, bindArgsClientInputs, result, metadata, ctx }) => {
6880
executed++;
6981

7082
assert.deepStrictEqual(
71-
{ clientInput, bindArgsClientInputs, result },
83+
{ clientInput, bindArgsClientInputs, result, metadata, ctx },
7284
{
85+
metadata: { actionName: "test" },
86+
ctx: { foo: "bar" },
7387
clientInput: inputs[2],
7488
bindArgsClientInputs: inputs.slice(0, 2),
7589
result: {
@@ -102,12 +116,14 @@ test("action with input schemas and server error calls `onError` and `onSettled`
102116
onSuccess: () => {
103117
executed++; // should not be called
104118
},
105-
onError({ error, clientInput, bindArgsClientInputs }) {
119+
onError({ error, clientInput, bindArgsClientInputs, metadata, ctx }) {
106120
executed++;
107121

108122
assert.deepStrictEqual(
109-
{ error, clientInput, bindArgsClientInputs },
123+
{ error, clientInput, bindArgsClientInputs, metadata, ctx },
110124
{
125+
metadata: { actionName: "test" },
126+
ctx: { foo: "bar" },
111127
error: {
112128
serverError: DEFAULT_SERVER_ERROR_MESSAGE,
113129
},
@@ -116,12 +132,14 @@ test("action with input schemas and server error calls `onError` and `onSettled`
116132
}
117133
);
118134
},
119-
onSettled({ clientInput, bindArgsClientInputs, result }) {
135+
onSettled({ clientInput, bindArgsClientInputs, result, metadata, ctx }) {
120136
executed++;
121137

122138
assert.deepStrictEqual(
123-
{ result, clientInput, bindArgsClientInputs },
139+
{ result, clientInput, bindArgsClientInputs, metadata, ctx },
124140
{
141+
metadata: { actionName: "test" },
142+
ctx: { foo: "bar" },
125143
result: {
126144
serverError: DEFAULT_SERVER_ERROR_MESSAGE,
127145
},
@@ -154,12 +172,14 @@ test("action with validation errors calls `onError` and `onSettled` callbacks wi
154172
onSuccess: () => {
155173
executed++; // should not be called
156174
},
157-
onError({ error, clientInput, bindArgsClientInputs }) {
175+
onError({ error, clientInput, bindArgsClientInputs, metadata, ctx }) {
158176
executed++;
159177

160178
assert.deepStrictEqual(
161-
{ error, clientInput, bindArgsClientInputs },
179+
{ error, clientInput, bindArgsClientInputs, metadata, ctx },
162180
{
181+
metadata: { actionName: "test" },
182+
ctx: { foo: "bar" },
163183
error: {
164184
validationErrors: {
165185
username: {
@@ -180,12 +200,14 @@ test("action with validation errors calls `onError` and `onSettled` callbacks wi
180200
}
181201
);
182202
},
183-
onSettled({ clientInput, bindArgsClientInputs, result }) {
203+
onSettled({ clientInput, bindArgsClientInputs, result, metadata, ctx }) {
184204
executed++;
185205

186206
assert.deepStrictEqual(
187-
{ result, clientInput, bindArgsClientInputs },
207+
{ result, clientInput, bindArgsClientInputs, metadata, ctx },
188208
{
209+
metadata: { actionName: "test" },
210+
ctx: { foo: "bar" },
189211
result: {
190212
validationErrors: {
191213
username: {
@@ -231,12 +253,14 @@ test("action with server validation error calls `onError` and `onSettled` callba
231253
onSuccess: () => {
232254
executed++; // should not be called
233255
},
234-
onError({ error, clientInput, bindArgsClientInputs }) {
256+
onError({ error, clientInput, bindArgsClientInputs, metadata, ctx }) {
235257
executed++;
236258

237259
assert.deepStrictEqual(
238-
{ error, clientInput, bindArgsClientInputs },
260+
{ error, clientInput, bindArgsClientInputs, metadata, ctx },
239261
{
262+
metadata: { actionName: "test" },
263+
ctx: { foo: "bar" },
240264
error: {
241265
validationErrors: {
242266
username: {
@@ -249,12 +273,14 @@ test("action with server validation error calls `onError` and `onSettled` callba
249273
}
250274
);
251275
},
252-
onSettled({ clientInput, bindArgsClientInputs, result }) {
276+
onSettled({ clientInput, bindArgsClientInputs, result, metadata, ctx }) {
253277
executed++;
254278

255279
assert.deepStrictEqual(
256-
{ result, clientInput, bindArgsClientInputs },
280+
{ result, clientInput, bindArgsClientInputs, metadata, ctx },
257281
{
282+
metadata: { actionName: "test" },
283+
ctx: { foo: "bar" },
258284
result: {
259285
validationErrors: {
260286
username: {

‎packages/next-safe-action/src/__tests__/typeschema/action-callbacks.test.ts

+43-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,17 @@ import { test } from "node:test";
55
import { z } from "zod";
66
import { DEFAULT_SERVER_ERROR_MESSAGE, createSafeActionClient, returnValidationErrors } from "../../typeschema";
77

8-
const ac = createSafeActionClient();
8+
const ac = createSafeActionClient({
9+
defineMetadataSchema() {
10+
return z.object({
11+
actionName: z.string(),
12+
});
13+
},
14+
})
15+
.use(async ({ next }) => {
16+
return next({ ctx: { foo: "bar" } });
17+
})
18+
.metadata({ actionName: "test" });
919

1020
test("typeschema - action with no input schema and no server errors calls `onSuccess` and `onSettled` callbacks", async () => {
1121
let executed = 0;
@@ -45,12 +55,14 @@ test("typeschema - action with input schemas and no errors calls `onSuccess` and
4555
};
4656
},
4757
{
48-
onSuccess: ({ clientInput, bindArgsClientInputs, parsedInput, bindArgsParsedInputs, data }) => {
58+
onSuccess: ({ clientInput, bindArgsClientInputs, parsedInput, bindArgsParsedInputs, data, metadata, ctx }) => {
4959
executed++;
5060

5161
assert.deepStrictEqual(
52-
{ clientInput, bindArgsClientInputs, parsedInput, bindArgsParsedInputs, data },
62+
{ clientInput, bindArgsClientInputs, parsedInput, bindArgsParsedInputs, data, metadata, ctx },
5363
{
64+
metadata: { actionName: "test" },
65+
ctx: { foo: "bar" },
5466
clientInput: inputs[2],
5567
bindArgsClientInputs: inputs.slice(0, 2),
5668
parsedInput: inputs[2],
@@ -64,12 +76,14 @@ test("typeschema - action with input schemas and no errors calls `onSuccess` and
6476
onError: () => {
6577
executed++; // should not be called
6678
},
67-
onSettled: ({ clientInput, bindArgsClientInputs, result }) => {
79+
onSettled: ({ clientInput, bindArgsClientInputs, result, metadata, ctx }) => {
6880
executed++;
6981

7082
assert.deepStrictEqual(
71-
{ clientInput, bindArgsClientInputs, result },
83+
{ clientInput, bindArgsClientInputs, result, metadata, ctx },
7284
{
85+
metadata: { actionName: "test" },
86+
ctx: { foo: "bar" },
7387
clientInput: inputs[2],
7488
bindArgsClientInputs: inputs.slice(0, 2),
7589
result: {
@@ -102,12 +116,14 @@ test("typeschema - action with input schemas and server error calls `onError` an
102116
onSuccess: () => {
103117
executed++; // should not be called
104118
},
105-
onError({ error, clientInput, bindArgsClientInputs }) {
119+
onError({ error, clientInput, bindArgsClientInputs, metadata, ctx }) {
106120
executed++;
107121

108122
assert.deepStrictEqual(
109-
{ error, clientInput, bindArgsClientInputs },
123+
{ error, clientInput, bindArgsClientInputs, metadata, ctx },
110124
{
125+
metadata: { actionName: "test" },
126+
ctx: { foo: "bar" },
111127
error: {
112128
serverError: DEFAULT_SERVER_ERROR_MESSAGE,
113129
},
@@ -116,12 +132,14 @@ test("typeschema - action with input schemas and server error calls `onError` an
116132
}
117133
);
118134
},
119-
onSettled({ clientInput, bindArgsClientInputs, result }) {
135+
onSettled({ clientInput, bindArgsClientInputs, result, metadata, ctx }) {
120136
executed++;
121137

122138
assert.deepStrictEqual(
123-
{ result, clientInput, bindArgsClientInputs },
139+
{ result, clientInput, bindArgsClientInputs, metadata, ctx },
124140
{
141+
metadata: { actionName: "test" },
142+
ctx: { foo: "bar" },
125143
result: {
126144
serverError: DEFAULT_SERVER_ERROR_MESSAGE,
127145
},
@@ -154,12 +172,14 @@ test("typeschema - action with validation errors calls `onError` and `onSettled`
154172
onSuccess: () => {
155173
executed++; // should not be called
156174
},
157-
onError({ error, clientInput, bindArgsClientInputs }) {
175+
onError({ error, clientInput, bindArgsClientInputs, metadata, ctx }) {
158176
executed++;
159177

160178
assert.deepStrictEqual(
161-
{ error, clientInput, bindArgsClientInputs },
179+
{ error, clientInput, bindArgsClientInputs, metadata, ctx },
162180
{
181+
metadata: { actionName: "test" },
182+
ctx: { foo: "bar" },
163183
error: {
164184
validationErrors: {
165185
username: {
@@ -180,12 +200,14 @@ test("typeschema - action with validation errors calls `onError` and `onSettled`
180200
}
181201
);
182202
},
183-
onSettled({ clientInput, bindArgsClientInputs, result }) {
203+
onSettled({ clientInput, bindArgsClientInputs, result, metadata, ctx }) {
184204
executed++;
185205

186206
assert.deepStrictEqual(
187-
{ result, clientInput, bindArgsClientInputs },
207+
{ result, clientInput, bindArgsClientInputs, metadata, ctx },
188208
{
209+
metadata: { actionName: "test" },
210+
ctx: { foo: "bar" },
189211
result: {
190212
validationErrors: {
191213
username: {
@@ -231,12 +253,14 @@ test("typeschema - action with server validation error calls `onError` and `onSe
231253
onSuccess: () => {
232254
executed++; // should not be called
233255
},
234-
onError({ error, clientInput, bindArgsClientInputs }) {
256+
onError({ error, clientInput, bindArgsClientInputs, metadata, ctx }) {
235257
executed++;
236258

237259
assert.deepStrictEqual(
238-
{ error, clientInput, bindArgsClientInputs },
260+
{ error, clientInput, bindArgsClientInputs, metadata, ctx },
239261
{
262+
metadata: { actionName: "test" },
263+
ctx: { foo: "bar" },
240264
error: {
241265
validationErrors: {
242266
username: {
@@ -249,12 +273,14 @@ test("typeschema - action with server validation error calls `onError` and `onSe
249273
}
250274
);
251275
},
252-
onSettled({ clientInput, bindArgsClientInputs, result }) {
276+
onSettled({ clientInput, bindArgsClientInputs, result, metadata, ctx }) {
253277
executed++;
254278

255279
assert.deepStrictEqual(
256-
{ result, clientInput, bindArgsClientInputs },
280+
{ result, clientInput, bindArgsClientInputs, metadata, ctx },
257281
{
282+
metadata: { actionName: "test" },
283+
ctx: { foo: "bar" },
258284
result: {
259285
validationErrors: {
260286
username: {

‎packages/next-safe-action/src/action-builder.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ export function actionBuilder<
5252
function buildAction({ withState }: { withState: false }): {
5353
action: <Data>(
5454
serverCodeFn: ServerCodeFn<MD, Ctx, S, BAS, Data>,
55-
cb?: SafeActionCallbacks<ServerError, S, BAS, CVE, CBAVE, Data>
55+
cb?: SafeActionCallbacks<ServerError, MD, Ctx, S, BAS, CVE, CBAVE, Data>
5656
) => SafeActionFn<ServerError, S, BAS, CVE, CBAVE, Data>;
5757
};
5858
function buildAction({ withState }: { withState: true }): {
5959
action: <Data>(
6060
serverCodeFn: StateServerCodeFn<ServerError, MD, Ctx, S, BAS, CVE, CBAVE, Data>,
61-
cb?: SafeActionCallbacks<ServerError, S, BAS, CVE, CBAVE, Data>
61+
cb?: SafeActionCallbacks<ServerError, MD, Ctx, S, BAS, CVE, CBAVE, Data>
6262
) => SafeStateActionFn<ServerError, S, BAS, CVE, CBAVE, Data>;
6363
};
6464
function buildAction({ withState }: { withState: boolean }) {
@@ -67,7 +67,7 @@ export function actionBuilder<
6767
serverCodeFn:
6868
| ServerCodeFn<MD, Ctx, S, BAS, Data>
6969
| StateServerCodeFn<ServerError, MD, Ctx, S, BAS, CVE, CBAVE, Data>,
70-
cb?: SafeActionCallbacks<ServerError, S, BAS, CVE, CBAVE, Data>
70+
cb?: SafeActionCallbacks<ServerError, MD, Ctx, S, BAS, CVE, CBAVE, Data>
7171
) => {
7272
return async (...clientInputs: unknown[]) => {
7373
let prevCtx: unknown = undefined;
@@ -252,6 +252,8 @@ export function actionBuilder<
252252
await Promise.resolve(
253253
cb?.onSuccess?.({
254254
data: undefined,
255+
metadata: args.metadata,
256+
ctx: prevCtx as Ctx,
255257
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
256258
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
257259
parsedInput: parsedInputDatas.at(-1) as S extends Schema ? Infer<S> : undefined,
@@ -263,6 +265,8 @@ export function actionBuilder<
263265

264266
await Promise.resolve(
265267
cb?.onSettled?.({
268+
metadata: args.metadata,
269+
ctx: prevCtx as Ctx,
266270
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
267271
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
268272
result: {},
@@ -295,6 +299,8 @@ export function actionBuilder<
295299

296300
await Promise.resolve(
297301
cb?.onSuccess?.({
302+
metadata: args.metadata,
303+
ctx: prevCtx as Ctx,
298304
data: actionResult.data as Data,
299305
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
300306
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
@@ -307,6 +313,8 @@ export function actionBuilder<
307313
} else {
308314
await Promise.resolve(
309315
cb?.onError?.({
316+
metadata: args.metadata,
317+
ctx: prevCtx as Ctx,
310318
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
311319
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
312320
error: actionResult,
@@ -317,6 +325,8 @@ export function actionBuilder<
317325
// onSettled, if provided, is always executed.
318326
await Promise.resolve(
319327
cb?.onSettled?.({
328+
metadata: args.metadata,
329+
ctx: prevCtx as Ctx,
320330
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
321331
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
322332
result: actionResult,

‎packages/next-safe-action/src/index.types.ts

+8
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ export type StateServerCodeFn<
129129
*/
130130
export type SafeActionCallbacks<
131131
ServerError,
132+
MD,
133+
Ctx,
132134
S extends Schema | undefined,
133135
BAS extends readonly Schema[],
134136
CVE,
@@ -137,6 +139,8 @@ export type SafeActionCallbacks<
137139
> = {
138140
onSuccess?: (args: {
139141
data?: Data;
142+
metadata: MD;
143+
ctx?: Ctx;
140144
clientInput: S extends Schema ? InferIn<S> : undefined;
141145
bindArgsClientInputs: InferInArray<BAS>;
142146
parsedInput: S extends Schema ? Infer<S> : undefined;
@@ -146,11 +150,15 @@ export type SafeActionCallbacks<
146150
}) => MaybePromise<void>;
147151
onError?: (args: {
148152
error: Prettify<Omit<SafeActionResult<ServerError, S, BAS, CVE, CBAVE, Data>, "data">>;
153+
metadata: MD;
154+
ctx?: Ctx;
149155
clientInput: S extends Schema ? InferIn<S> : undefined;
150156
bindArgsClientInputs: InferInArray<BAS>;
151157
}) => MaybePromise<void>;
152158
onSettled?: (args: {
153159
result: Prettify<SafeActionResult<ServerError, S, BAS, CVE, CBAVE, Data>>;
160+
metadata: MD;
161+
ctx?: Ctx;
154162
clientInput: S extends Schema ? InferIn<S> : undefined;
155163
bindArgsClientInputs: InferInArray<BAS>;
156164
hasRedirected: boolean;

‎packages/next-safe-action/src/safe-action-client.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export class SafeActionClient<
199199
*/
200200
action<Data>(
201201
serverCodeFn: ServerCodeFn<MD, Ctx, S, BAS, Data>,
202-
cb?: SafeActionCallbacks<ServerError, S, BAS, CVE, CBAVE, Data>
202+
cb?: SafeActionCallbacks<ServerError, MD, Ctx, S, BAS, CVE, CBAVE, Data>
203203
) {
204204
return actionBuilder({
205205
validationStrategy: this.#validationStrategy,
@@ -226,7 +226,7 @@ export class SafeActionClient<
226226
*/
227227
stateAction<Data>(
228228
serverCodeFn: StateServerCodeFn<ServerError, MD, Ctx, S, BAS, CVE, CBAVE, Data>,
229-
cb?: SafeActionCallbacks<ServerError, S, BAS, CVE, CBAVE, Data>
229+
cb?: SafeActionCallbacks<ServerError, MD, Ctx, S, BAS, CVE, CBAVE, Data>
230230
) {
231231
return actionBuilder({
232232
validationStrategy: this.#validationStrategy,

‎website/docs/execution/action-callbacks.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,17 @@ const action = actionClient
1818
}, {
1919
onSuccess: ({
2020
data,
21+
ctx,
22+
metadata,
2123
clientInput,
2224
bindArgsClientInputs,
2325
parsedInput,
2426
bindArgsParsedInputs,
2527
hasRedirected,
2628
hasNotFound,
2729
}) => {},
28-
onError: ({ error, clientInput, bindArgsClientInputs }) => {},
29-
onSettled: ({ result, clientInput, bindArgsClientInputs }) => {},
30+
onError: ({ error, ctx, metadata, clientInput, bindArgsClientInputs }) => {},
31+
onSettled: ({ result, ctx, metadata, clientInput, bindArgsClientInputs }) => {},
3032
});
3133
```
3234

0 commit comments

Comments
 (0)
Please sign in to comment.