@hyperspan/framework 1.0.6 → 1.0.7
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/package.json +1 -1
- package/src/actions.test.ts +1 -1
- package/src/actions.ts +7 -3
- package/src/middleware.ts +12 -15
- package/src/types.ts +17 -7
package/package.json
CHANGED
package/src/actions.test.ts
CHANGED
|
@@ -256,7 +256,7 @@ describe('createAction', () => {
|
|
|
256
256
|
|
|
257
257
|
const responseText = await response.text();
|
|
258
258
|
// Should render the custom error handler
|
|
259
|
-
expect(responseText).toContain('
|
|
259
|
+
expect(responseText).toContain('Invalid email address');
|
|
260
260
|
expect(responseText).toContain('Data: {"email":"not-an-email"}');
|
|
261
261
|
// Should NOT contain the success message from post handler
|
|
262
262
|
expect(responseText).not.toContain('Hello,');
|
package/src/actions.ts
CHANGED
|
@@ -84,8 +84,8 @@ export function createAction<T extends z.ZodObject<any, any>>(params: { name: st
|
|
|
84
84
|
* Form to render
|
|
85
85
|
* This will be wrapped in a <hs-action> web component and submitted via fetch()
|
|
86
86
|
*/
|
|
87
|
-
form(
|
|
88
|
-
api._form =
|
|
87
|
+
form(_formFn) {
|
|
88
|
+
api._form = _formFn;
|
|
89
89
|
return api;
|
|
90
90
|
},
|
|
91
91
|
/**
|
|
@@ -101,7 +101,7 @@ export function createAction<T extends z.ZodObject<any, any>>(params: { name: st
|
|
|
101
101
|
/**
|
|
102
102
|
* Get form renderer method
|
|
103
103
|
*/
|
|
104
|
-
render(c
|
|
104
|
+
render(c, props) {
|
|
105
105
|
const formContent = api._form ? api._form(c, props || {}) : null;
|
|
106
106
|
return formContent ? html`<hs-action url="${this._path()}">${formContent}</hs-action>${actionsClientJS.renderScriptTag()}` : null;
|
|
107
107
|
},
|
|
@@ -109,6 +109,10 @@ export function createAction<T extends z.ZodObject<any, any>>(params: { name: st
|
|
|
109
109
|
_errorHandler = handler;
|
|
110
110
|
return api;
|
|
111
111
|
},
|
|
112
|
+
use(middleware: HS.MiddlewareFunction) {
|
|
113
|
+
route.use(middleware);
|
|
114
|
+
return api;
|
|
115
|
+
},
|
|
112
116
|
middleware(middleware: Array<HS.MiddlewareFunction>) {
|
|
113
117
|
route.middleware(middleware);
|
|
114
118
|
return api;
|
package/src/middleware.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formDataToJSON } from './utils';
|
|
2
|
-
import { z, flattenError } from 'zod/v4';
|
|
2
|
+
import { z, flattenError, prettifyError } from 'zod/v4';
|
|
3
3
|
|
|
4
4
|
import type { ZodAny, ZodObject, ZodError } from 'zod/v4';
|
|
5
5
|
import type { Hyperspan as HS } from './types';
|
|
@@ -26,12 +26,15 @@ function inferValidationType(headers: Headers): TValidationType {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export class ZodValidationError extends Error {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
public fieldErrors;
|
|
30
|
+
public formErrors: unknown[];
|
|
31
|
+
constructor(error: ZodError, schema: ZodObject | ZodAny) {
|
|
32
|
+
const message = prettifyError(error);
|
|
33
|
+
const flattened = flattenError<z.infer<typeof schema>>(error);
|
|
34
|
+
super(message);
|
|
31
35
|
this.name = 'ZodValidationError';
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Object.assign(this, flattened);
|
|
36
|
+
this.fieldErrors = flattened.fieldErrors;
|
|
37
|
+
this.formErrors = flattened.formErrors;
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -44,8 +47,8 @@ export function validateQuery(schema: ZodObject | ZodAny): HS.MiddlewareFunction
|
|
|
44
47
|
context.vars.query = validated.data as z.infer<typeof schema>;
|
|
45
48
|
|
|
46
49
|
if (!validated.success) {
|
|
47
|
-
const err =
|
|
48
|
-
|
|
50
|
+
const err = new ZodValidationError(validated.error, schema);
|
|
51
|
+
throw new HTTPResponseException(err, { status: 400 });
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
return next();
|
|
@@ -72,20 +75,14 @@ export function validateBody(schema: ZodObject | ZodAny, type?: TValidationType)
|
|
|
72
75
|
const validated = schema.safeParse(body);
|
|
73
76
|
|
|
74
77
|
if (!validated.success) {
|
|
75
|
-
const err =
|
|
78
|
+
const err = new ZodValidationError(validated.error, schema);
|
|
76
79
|
throw new HTTPResponseException(err, { status: 400 });
|
|
77
|
-
//return context.res.error(err, { status: 400 });
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
return next();
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
export function formatZodError(error: ZodError): ZodValidationError {
|
|
85
|
-
const zodError = flattenError(error);
|
|
86
|
-
return new ZodValidationError(zodError);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
86
|
/**
|
|
90
87
|
* Type guard to check if a handler is a middleware function
|
|
91
88
|
* Middleware functions have 2 parameters (context, next)
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HSHtml } from '@hyperspan/html';
|
|
2
|
+
import { ZodValidationError } from './middleware';
|
|
2
3
|
import * as z from 'zod/v4';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -156,20 +157,29 @@ export namespace Hyperspan {
|
|
|
156
157
|
/**
|
|
157
158
|
* Action = Form + route handler
|
|
158
159
|
*/
|
|
159
|
-
|
|
160
|
-
export type
|
|
160
|
+
// Form renderer
|
|
161
|
+
export type ActionFormResponse = HSHtml | void | null | Promise<HSHtml | void | null>;
|
|
162
|
+
export type ActionFormProps<T extends z.ZodTypeAny> = { data?: Partial<z.infer<T>>; error?: ZodValidationError };
|
|
163
|
+
export type ActionForm<T extends z.ZodTypeAny> = (
|
|
164
|
+
c: Context, props: ActionFormProps<T>
|
|
165
|
+
) => ActionFormResponse;
|
|
166
|
+
// Form handler
|
|
167
|
+
export type ActionFormHandlerResponse = ActionFormResponse | Response | Promise<Response>;
|
|
168
|
+
export type ActionFormHandlerProps<T extends z.ZodTypeAny> = { data: z.infer<T>; error?: ZodValidationError | Error };
|
|
161
169
|
export type ActionFormHandler<T extends z.ZodTypeAny> = (
|
|
162
|
-
c: Context, props:
|
|
163
|
-
) =>
|
|
170
|
+
c: Context, props: ActionFormHandlerProps<T>
|
|
171
|
+
) => ActionFormHandlerResponse;
|
|
172
|
+
// Action API
|
|
164
173
|
export interface Action<T extends z.ZodTypeAny> {
|
|
165
174
|
_kind: 'hsAction';
|
|
166
175
|
_config: Partial<Hyperspan.RouteConfig>;
|
|
167
176
|
_path(): string;
|
|
168
|
-
_form: null |
|
|
169
|
-
form(form:
|
|
170
|
-
render: (c: Context, props?:
|
|
177
|
+
_form: null | ActionForm<T>;
|
|
178
|
+
form(form: ActionForm<T>): Action<T>;
|
|
179
|
+
render: (c: Context, props?: ActionFormProps<T>) => ActionFormResponse;
|
|
171
180
|
post: (handler: ActionFormHandler<T>) => Action<T>;
|
|
172
181
|
errorHandler: (handler: ActionFormHandler<T>) => Action<T>;
|
|
182
|
+
use: (middleware: Hyperspan.MiddlewareFunction) => Action<T>;
|
|
173
183
|
middleware: (middleware: Array<Hyperspan.MiddlewareFunction>) => Action<T>;
|
|
174
184
|
fetch: (request: Request) => Promise<Response>;
|
|
175
185
|
}
|