@sugardarius/anzen 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,196 @@
1
- import { A as AuthContext, T as TSegmentsDict, a as TSearchParamsDict, b as TBodySchema, c as TFormDataDict, C as CreateSafeRouteHandlerOptions, S as SafeRouteHandler, d as CreateSafeRouteHandlerReturnType } from './types-LPIIICMI.cjs';
2
- export { f as AuthFunction, g as AuthFunctionParams, e as Awaitable, B as BaseOptions, O as OnValidationErrorResponse, P as ProvidedRouteContext, h as SafeRouteHandlerContext } from './types-LPIIICMI.cjs';
1
+ import { A as AuthContext, S as StandardSchemaDictionary, a as StandardSchemaV1, b as Awaitable, U as UnwrapReadonlyObject, E as EmptyObjectType } from './standard-schema-BHEkWkzf.cjs';
2
+
3
+ type TSegmentsDict = StandardSchemaDictionary;
4
+ type TSearchParamsDict = StandardSchemaDictionary;
5
+ type TBodySchema = StandardSchemaV1;
6
+ type TFormDataDict = StandardSchemaDictionary;
7
+ type RouteHandlerAuthFunctionParams<TSegments extends TSegmentsDict | undefined, TSearchParams extends TSearchParamsDict | undefined, TBody extends TBodySchema | undefined, TFormData extends TFormDataDict | undefined> = {
8
+ /**
9
+ * ID for the route handler.
10
+ */
11
+ readonly id: string;
12
+ /**
13
+ * Parsed request url
14
+ */
15
+ readonly url: URL;
16
+ /**
17
+ * Original request
18
+ *
19
+ * Cloned from the incoming request to avoid side effects
20
+ * and to make it consumable in the `authorize` function.
21
+ * Due to `NextRequest` limitations as the req is cloned it's always a `Request`
22
+ */
23
+ req: Request;
24
+ } & (TSegments extends TSegmentsDict ? {
25
+ /**
26
+ * Validated route dynamic segments
27
+ */
28
+ readonly segments: UnwrapReadonlyObject<StandardSchemaDictionary.InferOutput<TSegments>>;
29
+ } : EmptyObjectType) & (TSearchParams extends TSearchParamsDict ? {
30
+ /**
31
+ * Validated search params
32
+ */
33
+ readonly searchParams: UnwrapReadonlyObject<StandardSchemaDictionary.InferOutput<TSearchParams>>;
34
+ } : EmptyObjectType) & (TBody extends TBodySchema ? {
35
+ /**
36
+ * Validated request body
37
+ */
38
+ readonly body: StandardSchemaV1.InferOutput<TBody>;
39
+ } : EmptyObjectType) & (TFormData extends TFormDataDict ? {
40
+ /**
41
+ * Validated form data
42
+ */
43
+ readonly formData: UnwrapReadonlyObject<StandardSchemaDictionary.InferOutput<TFormData>>;
44
+ } : EmptyObjectType);
45
+ type RouteHandlerAuthFunction<AC extends AuthContext | undefined, TSegments extends TSegmentsDict | undefined, TSearchParams extends TSearchParamsDict | undefined, TBody extends TBodySchema | undefined, TFormData extends TFormDataDict | undefined> = (params: RouteHandlerAuthFunctionParams<TSegments, TSearchParams, TBody, TFormData>) => Awaitable<AC | Response | never>;
46
+ type OnValidationErrorResponse = (issues: readonly StandardSchemaV1.Issue[]) => Awaitable<Response>;
47
+ type CreateSafeRouteHandlerOptions<AC extends AuthContext | undefined, TSegments extends TSegmentsDict | undefined, TSearchParams extends TSearchParamsDict | undefined, TBody extends TBodySchema | undefined, TFormData extends TFormDataDict | undefined> = {
48
+ /**
49
+ * ID for the route handler.
50
+ * Used when logging in development or when `debug` is enabled.
51
+ *
52
+ * You can also use it to add extra logging or monitoring.
53
+ */
54
+ id?: string;
55
+ /**
56
+ * Callback triggered when the request fails.
57
+ * By default it returns a simple `500` response and the error is logged into the console.
58
+ *
59
+ * Use it if your handler use custom errors and
60
+ * you want to manage them properly by returning a proper response.
61
+ */
62
+ onErrorResponse?: (err: unknown) => Awaitable<Response>;
63
+ /**
64
+ * Use this options to enable debug mode.
65
+ * It will add logs in the handler to help you debug the request.
66
+ *
67
+ * By default it's set to `false` for production builds.
68
+ * In development builds, it will be `true` if `NODE_ENV` is not set to `production`.
69
+ */
70
+ debug?: boolean;
71
+ /**
72
+ * Dynamic route segments used for the route handler path.
73
+ * By design it will handler if the segments are a `Promise` or not.
74
+ *
75
+ * Please note the expected input is a `StandardSchemaDictionary`.
76
+ */
77
+ segments?: TSegments;
78
+ /**
79
+ * Callback triggered when dynamic segments validations returned issues.
80
+ * By default it returns a simple `400` response and issues are logged into the console.
81
+ */
82
+ onSegmentsValidationErrorResponse?: OnValidationErrorResponse;
83
+ /**
84
+ * Search params used in the route.
85
+ *
86
+ * Please note the expected input is a `StandardSchemaDictionary`.
87
+ */
88
+ searchParams?: TSearchParams;
89
+ /**
90
+ * Callback triggered when search params validations returned issues.
91
+ * By default it returns a simple `400` response and issues are logged into the console.
92
+ */
93
+ onSearchParamsValidationErrorResponse?: OnValidationErrorResponse;
94
+ /**
95
+ * Request body.
96
+ *
97
+ * Returns a `405` response if the request method is not `POST`, 'PUT' or 'PATCH'.
98
+ * Returns a `415`response if the request does not explicitly set the `Content-Type` to `application/json`.
99
+ *
100
+ * IMPORTANT: The body is parsed as JSON, so it must be a valid JSON object!
101
+ * IMPORTANT: Body shouldn't be used with `formData` at the same time. They are exclusive.
102
+ * Why making the distinction? `formData` is used as a `StandardSchemaDictionary` whereas `body` is used as a `StandardSchemaV1`.
103
+ */
104
+ body?: TBody;
105
+ /**
106
+ * Callback triggered when body validation returned issues.
107
+ * By default it returns a simple `400` response and issues are logged into the console.
108
+ */
109
+ onBodyValidationErrorResponse?: OnValidationErrorResponse;
110
+ /**
111
+ * Request form data.
112
+ *
113
+ * Returns a `405` response if the request method is not `POST`, 'PUT' or 'PATCH'.
114
+ * Returns a `415`response if the request does not explicitly set the `Content-Type` to `multipart/form-data`
115
+ * or to `application/x-www-form-urlencoded`.
116
+ *
117
+ * IMPORTANT: formData shouldn't be used with `body` at the same time. They are exclusive.
118
+ * Why making the distinction? `formData` is used as a `StandardSchemaDictionary` whereas `body` is used as a `StandardSchemaV1`.
119
+ */
120
+ formData?: TFormData;
121
+ /**
122
+ * Callback triggered when form data validation returned issues.
123
+ * By default it returns a simple `400` response and issues are logged into the console.
124
+ */
125
+ onFormDataValidationErrorResponse?: OnValidationErrorResponse;
126
+ /**
127
+ * Function to use to authorize the request.
128
+ * By default it always authorize the request.
129
+ *
130
+ * When returning a response, it will be used as the response for the request.
131
+ * Return a response when the request is not authorized.
132
+ */
133
+ authorize?: RouteHandlerAuthFunction<AC, TSegments, TSearchParams, TBody, TFormData>;
134
+ };
135
+ type ProvidedRouteContext = {
136
+ /**
137
+ * Route dynamic segments as params
138
+ */
139
+ params: Awaitable<any> | undefined;
140
+ };
141
+ type CreateSafeRouteHandlerReturnType<TReq extends Request = Request> = (
142
+ /**
143
+ * Original request
144
+ */
145
+ req: TReq,
146
+ /**
147
+ * Provided context added by Next.js itself
148
+ */
149
+ providedContext: ProvidedRouteContext) => Promise<Response | never>;
150
+ type SafeRouteHandlerContext<AC extends AuthContext | undefined, TSegments extends TSegmentsDict | undefined, TSearchParams extends TSearchParamsDict | undefined, TBody extends TBodySchema | undefined, TFormData extends TFormDataDict | undefined> = {
151
+ /**
152
+ * Route handler ID
153
+ */
154
+ readonly id: string;
155
+ /**
156
+ * Parsed request url
157
+ */
158
+ readonly url: URL;
159
+ } & (AC extends AuthContext ? {
160
+ /**
161
+ * Auth context
162
+ */
163
+ readonly auth: AC;
164
+ } : EmptyObjectType) & (TSegments extends TSegmentsDict ? {
165
+ /**
166
+ * Validated route dynamic segments
167
+ */
168
+ readonly segments: UnwrapReadonlyObject<StandardSchemaDictionary.InferOutput<TSegments>>;
169
+ } : EmptyObjectType) & (TSearchParams extends TSearchParamsDict ? {
170
+ /**
171
+ * Validated search params
172
+ */
173
+ readonly searchParams: UnwrapReadonlyObject<StandardSchemaDictionary.InferOutput<TSearchParams>>;
174
+ } : EmptyObjectType) & (TBody extends TBodySchema ? {
175
+ /**
176
+ * Validated request body
177
+ */
178
+ readonly body: StandardSchemaV1.InferOutput<TBody>;
179
+ } : EmptyObjectType) & (TFormData extends TFormDataDict ? {
180
+ /**
181
+ * Validated form data
182
+ */
183
+ readonly formData: UnwrapReadonlyObject<StandardSchemaDictionary.InferOutput<TFormData>>;
184
+ } : EmptyObjectType);
185
+ type SafeRouteHandler<AC extends AuthContext | undefined, TSegments extends TSegmentsDict | undefined, TSearchParams extends TSearchParamsDict | undefined, TBody extends TBodySchema | undefined, TFormData extends TFormDataDict | undefined, TReq extends Request = Request> = (
186
+ /**
187
+ * Safe route handler context
188
+ */
189
+ ctx: SafeRouteHandlerContext<AC, TSegments, TSearchParams, TBody, TFormData>,
190
+ /**
191
+ * Original request
192
+ */
193
+ req: TReq) => Promise<Response | never>;
3
194
 
4
195
  /**
5
196
  * Creates a safe route handler with data validation and error handling
@@ -13,7 +204,7 @@ export { f as AuthFunction, g as AuthFunctionParams, e as Awaitable, B as BaseOp
13
204
  * @example
14
205
  * ```ts
15
206
  * import { string } from 'decoders'
16
- *import { createSafeRouteHandler } from '@sugardarius/anzen'
207
+ * import { createSafeRouteHandler } from '@sugardarius/anzen'
17
208
  * import { auth } from '~/lib/auth'
18
209
  *
19
210
  * export const GET = createSafeRouteHandler(
@@ -39,4 +230,263 @@ export { f as AuthFunction, g as AuthFunctionParams, e as Awaitable, B as BaseOp
39
230
  */
40
231
  declare function createSafeRouteHandler<AC extends AuthContext | undefined = undefined, TRouteDynamicSegments extends TSegmentsDict | undefined = undefined, TSearchParams extends TSearchParamsDict | undefined = undefined, TBody extends TBodySchema | undefined = undefined, TFormData extends TFormDataDict | undefined = undefined, TReq extends Request = Request>(options: CreateSafeRouteHandlerOptions<AC, TRouteDynamicSegments, TSearchParams, TBody, TFormData>, handlerFn: SafeRouteHandler<AC, TRouteDynamicSegments, TSearchParams, TBody, TFormData, TReq>): CreateSafeRouteHandlerReturnType<TReq>;
41
232
 
42
- export { AuthContext, CreateSafeRouteHandlerOptions, CreateSafeRouteHandlerReturnType, SafeRouteHandler, TBodySchema, TFormDataDict, TSearchParamsDict, TSegmentsDict, createSafeRouteHandler };
233
+ type TInputSchema = StandardSchemaV1;
234
+ type ServerActionErrorContext = Record<string, unknown>;
235
+ /**
236
+ * Generic server error.
237
+ * Triggered when server action handler throws an unexpected error.
238
+ *
239
+ * Context `ctx` is used to store the error context.
240
+ * It can be customized by using the `onError` option when creating the server action.
241
+ */
242
+ type ServerError = {
243
+ readonly code: 'SERVER_ERROR';
244
+ readonly ctx: ServerActionErrorContext;
245
+ };
246
+ /**
247
+ * Unauthorized error.
248
+ * Triggered when server action is not authorized by the `authorize` function
249
+ * 👉🏻 When `authorize` function throws an error.
250
+ *
251
+ *
252
+ * Context `ctx` is used to store the error context.
253
+ * It can be customized by using the `onError` option when creating the server action.
254
+ */
255
+ type UnauthorizedError = {
256
+ readonly code: 'UNAUTHORIZED_ERROR';
257
+ readonly ctx: ServerActionErrorContext;
258
+ };
259
+ /**
260
+ * Validation error.
261
+ * Triggered when server action input validation returns issues.
262
+ *
263
+ * Context `ctx` is used to store the error context.
264
+ * It can be customized by using the `onInputValidationError` option when creating the server action.
265
+ *
266
+ * By default this error will return the issues `StandardSchemaV1.Issue[]` in the context when
267
+ * no context customization is provided.
268
+ */
269
+ type ValidationError = {
270
+ readonly code: 'VALIDATION_ERROR';
271
+ readonly ctx: ServerActionErrorContext;
272
+ };
273
+ /**
274
+ * Tagged error.
275
+ * It represents an error expected to be used in the server action
276
+ * defined by developers themselves.
277
+ */
278
+ type TaggedError = {
279
+ readonly code: string;
280
+ readonly ctx: ServerActionErrorContext;
281
+ };
282
+ type SafeServerActionError = ValidationError | UnauthorizedError | ServerError | TaggedError;
283
+ type SafeServerActionResultSuccess<TOutput> = {
284
+ readonly success: true;
285
+ readonly output: TOutput;
286
+ readonly error?: never;
287
+ };
288
+ type SafeServerActionResultError<TError> = {
289
+ readonly success: false;
290
+ readonly output?: never;
291
+ readonly error: TError;
292
+ };
293
+ type ServerActionAuthFunctionParams<TInput extends TInputSchema | undefined> = {
294
+ /**
295
+ * Server action ID
296
+ */
297
+ readonly id: string;
298
+ } & (TInput extends TInputSchema ? {
299
+ /**
300
+ * Validated input
301
+ */
302
+ readonly input: UnwrapReadonlyObject<StandardSchemaV1.InferOutput<TInput>>;
303
+ } : EmptyObjectType);
304
+ type ServerActionAuthFunction<AC extends AuthContext | undefined, TInput extends TInputSchema | undefined> = (
305
+ /**
306
+ * Auth function parameters
307
+ * Contains the server action ID and the validated input.
308
+ *
309
+ * If the input is not provided, the property do not exists.
310
+ *
311
+ * @example
312
+ * ```ts
313
+ * authorize: async ({ id, input }) => {
314
+ * const auth = await getAuth()
315
+ * if (!auth) {
316
+ * throw new UnauthenticatedError()
317
+ * }
318
+ *
319
+ * const hasAccess = await checkAccess({
320
+ * userId: auth.user.id,
321
+ * resourceId: input.resourceId,
322
+ * })
323
+ *
324
+ * if (!hasAccess) {
325
+ * throw new ForbiddenError()
326
+ * }
327
+ *
328
+ * return { user: auth.user }
329
+ * }
330
+ * ```
331
+ */
332
+ params: ServerActionAuthFunctionParams<TInput>) => Awaitable<AC | never>;
333
+ type OnError = (err: unknown) => Awaitable<ServerActionErrorContext>;
334
+ type OnInputValidationError = (issues: readonly StandardSchemaV1.Issue[]) => Awaitable<ServerActionErrorContext>;
335
+ type CreateSafeServerActionOptions<TInput extends TInputSchema | undefined, AC extends AuthContext | undefined> = {
336
+ /**
337
+ * ID for the server action.
338
+ * Used when logging in development or when `debug` is enabled.
339
+ *
340
+ * You can also use it to add extra logging or monitoring.
341
+ */
342
+ id?: string;
343
+ /**
344
+ * Use this options to enable debug mode.
345
+ * It will add logs in the handler to help you debug server action.
346
+ *
347
+ * By default it's set to `false` for production builds.
348
+ * In development builds, it will be `true` if `NODE_ENV` is not set to `production`.
349
+ */
350
+ debug?: boolean;
351
+ /**
352
+ * Callback triggered when the server action throws an unhandled error.
353
+ * By default it will return an error context object and the error is logged into the console.
354
+ *
355
+ * Use it if you want to manage unexpected errors properly
356
+ * to log or trace and define custom error contexts objects.
357
+ *
358
+ * ⚠️ By design, this callback isn't mean to be used to manage navigation behaviors like using `notFound` or `redirect`,
359
+ * or with `throw` statements as it's best to handle them in the UI. ⚠️
360
+ *
361
+ * @example
362
+ * ```ts
363
+ * // ✅ Valid use case
364
+ * onError: async (err: unknown) => {
365
+ * log.error(`🛑 Unexpected error in server action '${id}'`, err)
366
+ * if (err instanceof NotFoundError) {
367
+ * return {
368
+ * message: 'Resource not found',
369
+ * }
370
+ * }
371
+ *
372
+ * return {
373
+ * message: 'An unexpected error occurred',
374
+ * err: JSON.stringify(err),
375
+ * }
376
+ * }
377
+ *
378
+ * // ❌ Invalid use case
379
+ * onError: async (err: unknown) => {
380
+ * throw err
381
+ * // or redirect('/')
382
+ * }
383
+ */
384
+ onError?: OnError;
385
+ /**
386
+ * Server action input schema used to validate the input
387
+ * when calling the server action.
388
+ *
389
+ * Please note the expected input is a `StandardSchemaV1`.
390
+ */
391
+ input?: TInput;
392
+ /**
393
+ * Callback triggered when input validation returned issues
394
+ * By default it will return an error context object and the error is logged into the console.
395
+ *
396
+ * Use it if you want to manage input validation errors properly
397
+ * to log or trace and define custom error contexts objects.
398
+ *
399
+ * ⚠️ By design, this callback isn't mean to be used to manage navigation behaviors like using `notFound` or `redirect`,
400
+ * or with `throw` statements as it's best to handle them in the UI. ⚠️
401
+ *
402
+ * @example
403
+ * ```ts
404
+ * // ✅ Valid use case
405
+ * onInputValidationError: async (issues: readonly StandardSchemaV1.Issue[]) => {
406
+ * log.error(`🛑 Invalid input for server action '${id}'`, issues)
407
+ * return {
408
+ * message: 'Invalid input',
409
+ * issues,
410
+ * }
411
+ * }
412
+ *
413
+ * // ❌ Invalid use case
414
+ * onInputValidationError: async (issues: readonly StandardSchemaV1.Issue[]) => {
415
+ * throw new Error('Invalid input')
416
+ * }
417
+ * ```
418
+ */
419
+ onInputValidationError?: OnInputValidationError;
420
+ /**
421
+ * Function to use to authorize the server action.
422
+ * By default it always authorize the server action.
423
+ *
424
+ * Returns an unauthorized error when the server action is not authorized
425
+ * or never when `redirect`, `notFound`, `forbidden` or `unauthorized` are thrown.
426
+ */
427
+ authorize?: ServerActionAuthFunction<AC, TInput>;
428
+ };
429
+ type SafeServerActionResult<TOutput, TError> = SafeServerActionResultSuccess<TOutput> | SafeServerActionResultError<TError> | never;
430
+ type InferServerActionProvidedInput<TInput extends TInputSchema | undefined> = TInput extends TInputSchema ? FormData | StandardSchemaV1.InferOutput<TInput> : undefined;
431
+ type CreateSafeServerActionReturnType<TInput extends TInputSchema | undefined, TOutput, TError> = [TInput] extends [TInputSchema] ? (providedInput: InferServerActionProvidedInput<TInput>) => Promise<SafeServerActionResult<TOutput, TError>> : (providedInput?: InferServerActionProvidedInput<TInput>) => Promise<SafeServerActionResult<TOutput, TError>>;
432
+ type SafeServerActionContext<TInput extends TInputSchema | undefined, AC extends AuthContext | undefined> = {
433
+ /**
434
+ * Server action ID
435
+ */
436
+ readonly id: string;
437
+ /**
438
+ * Tag error function
439
+ * Throws a developer defined tagged error.
440
+ * @example
441
+ * ```ts
442
+ * // Server
443
+ * export const myAction = createSafeServerAction({
444
+ * id: 'my action,
445
+ * }, async ({ tagErr }) => {
446
+ * tagErr('CONFLICT', {
447
+ * message: 'resource already exists',
448
+ * })
449
+ * })
450
+ *
451
+ * // Client
452
+ * const result = await myAction()
453
+ * if (result.success === false) {
454
+ * if (result.error.code === 'CONFLICT') {
455
+ * return <span>{result.error.ctx.message}</span>
456
+ * }
457
+ * }
458
+ * ```
459
+ */
460
+ readonly tagErr: (code: string, ctx: ServerActionErrorContext) => never;
461
+ } & (AC extends AuthContext ? {
462
+ /**
463
+ * Auth context
464
+ */
465
+ readonly auth: AC;
466
+ } : EmptyObjectType) & (TInput extends TInputSchema ? {
467
+ /**
468
+ * Validated input
469
+ */
470
+ readonly input: UnwrapReadonlyObject<StandardSchemaV1.InferOutput<TInput>>;
471
+ } : EmptyObjectType);
472
+ type SafeServerActionHandler<TOutput, TInput extends TInputSchema | undefined, AC extends AuthContext | undefined> = (
473
+ /**
474
+ * Safe server action context
475
+ */
476
+ ctx: SafeServerActionContext<TInput, AC>) => Promise<TOutput | never>;
477
+
478
+ /**
479
+ * Overload for server actions with no input.
480
+ * Used when calling server actions with no input schema provided,
481
+ * making the DX for developers easier and nicer. It avoids to call
482
+ * a server action with `undefined` as input.
483
+ */
484
+ declare function createSafeServerAction<TOutput, AC extends AuthContext | undefined = undefined>(options: CreateSafeServerActionOptions<undefined, AC>, handler: SafeServerActionHandler<TOutput, undefined, AC>): CreateSafeServerActionReturnType<undefined, TOutput, SafeServerActionError>;
485
+ /**
486
+ * Overload for server actions with input.
487
+ */
488
+ declare function createSafeServerAction<TOutput, TInput extends TInputSchema, AC extends AuthContext | undefined = undefined>(options: CreateSafeServerActionOptions<TInput, AC> & {
489
+ input: TInput;
490
+ }, handler: SafeServerActionHandler<TOutput, TInput, AC>): CreateSafeServerActionReturnType<TInput, TOutput, SafeServerActionError>;
491
+
492
+ export { AuthContext, Awaitable, type CreateSafeRouteHandlerOptions, type CreateSafeRouteHandlerReturnType, type CreateSafeServerActionOptions, type CreateSafeServerActionReturnType, type InferServerActionProvidedInput, type OnValidationErrorResponse, type ProvidedRouteContext, type RouteHandlerAuthFunction, type RouteHandlerAuthFunctionParams, type SafeRouteHandler, type SafeRouteHandlerContext, type SafeServerActionContext, type SafeServerActionError, type SafeServerActionHandler, type SafeServerActionResult, type SafeServerActionResultError, type SafeServerActionResultSuccess, type ServerActionAuthFunction, type ServerActionAuthFunctionParams, type ServerActionErrorContext, type ServerError, type TBodySchema, type TFormDataDict, type TSearchParamsDict, type TSegmentsDict, type UnauthorizedError, type ValidationError, createSafeRouteHandler, createSafeServerAction };