Skip to content

Commit 6d47bfe

Browse files
committed
feat: Add Standard Schema validator to qwik-city
1 parent aa595d4 commit 6d47bfe

File tree

14 files changed

+305
-48
lines changed

14 files changed

+305
-48
lines changed

Diff for: package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
"@node-rs/helper": "1.6.0",
125125
"@octokit/action": "6.1.0",
126126
"@playwright/test": "1.50.1",
127+
"@standard-schema/spec": "1.0.0",
127128
"@types/brotli": "1.3.4",
128129
"@types/bun": "1.1.6",
129130
"@types/cross-spawn": "6.0.6",
@@ -181,7 +182,7 @@
181182
"vitest": "2.0.5",
182183
"watchlist": "0.3.1",
183184
"which-pm-runs": "1.1.0",
184-
"zod": "3.22.4"
185+
"zod": "3.24.2"
185186
},
186187
"engines": {
187188
"node": ">=16.8.0 <18.0.0 || >=18.11",

Diff for: packages/docs/src/routes/api/qwik-city/api.json

+31-3
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@
292292
}
293293
],
294294
"kind": "TypeAlias",
295-
"content": "```typescript\nexport type GetValidatorInputType<VALIDATOR extends TypedDataValidator> = VALIDATOR extends ValibotDataValidator<infer TYPE> ? v.InferInput<TYPE> : VALIDATOR extends ZodDataValidator<infer TYPE> ? z.input<TYPE> : never;\n```\n**References:** [TypedDataValidator](#typeddatavalidator)",
295+
"content": "```typescript\nexport type GetValidatorInputType<VALIDATOR extends TypedDataValidator> = VALIDATOR extends StandardSchemaDataValidator<infer TYPE> ? StandardSchemaV1.InferInput<TYPE> : VALIDATOR extends ValibotDataValidator<infer TYPE> ? v.InferInput<TYPE> : VALIDATOR extends ZodDataValidator<infer TYPE> ? z.input<TYPE> : never;\n```\n**References:** [TypedDataValidator](#typeddatavalidator)",
296296
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/types.ts",
297297
"mdFile": "qwik-city.getvalidatorinputtype.md"
298298
},
@@ -306,7 +306,7 @@
306306
}
307307
],
308308
"kind": "TypeAlias",
309-
"content": "```typescript\nexport type GetValidatorOutputType<VALIDATOR extends TypedDataValidator> = VALIDATOR extends ValibotDataValidator<infer TYPE> ? v.InferOutput<TYPE> : VALIDATOR extends ZodDataValidator<infer TYPE> ? z.output<TYPE> : never;\n```\n**References:** [TypedDataValidator](#typeddatavalidator)",
309+
"content": "```typescript\nexport type GetValidatorOutputType<VALIDATOR extends TypedDataValidator> = VALIDATOR extends StandardSchemaDataValidator<infer TYPE> ? StandardSchemaV1.InferOutput<TYPE> : VALIDATOR extends ValibotDataValidator<infer TYPE> ? v.InferOutput<TYPE> : VALIDATOR extends ZodDataValidator<infer TYPE> ? z.output<TYPE> : never;\n```\n**References:** [TypedDataValidator](#typeddatavalidator)",
310310
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/types.ts",
311311
"mdFile": "qwik-city.getvalidatoroutputtype.md"
312312
},
@@ -716,6 +716,20 @@
716716
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/router-outlet-component.tsx",
717717
"mdFile": "qwik-city.routeroutlet.md"
718718
},
719+
{
720+
"name": "schema$",
721+
"id": "schema_",
722+
"hierarchy": [
723+
{
724+
"name": "schema$",
725+
"id": "schema_"
726+
}
727+
],
728+
"kind": "Variable",
729+
"content": "```typescript\nschema$: StandardSchemaConstructor\n```",
730+
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/server-functions.ts",
731+
"mdFile": "qwik-city.schema_.md"
732+
},
719733
{
720734
"name": "server$",
721735
"id": "server_",
@@ -786,6 +800,20 @@
786800
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/sw-component.tsx",
787801
"mdFile": "qwik-city.serviceworkerregister.md"
788802
},
803+
{
804+
"name": "standardSchemaQrl",
805+
"id": "standardschemaqrl",
806+
"hierarchy": [
807+
{
808+
"name": "standardSchemaQrl",
809+
"id": "standardschemaqrl"
810+
}
811+
],
812+
"kind": "Variable",
813+
"content": "```typescript\nstandardSchemaQrl: StandardSchemaConstructorQRL\n```",
814+
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/server-functions.ts",
815+
"mdFile": "qwik-city.standardschemaqrl.md"
816+
},
789817
{
790818
"name": "StaticGenerate",
791819
"id": "staticgenerate",
@@ -838,7 +866,7 @@
838866
}
839867
],
840868
"kind": "TypeAlias",
841-
"content": "```typescript\nexport type TypedDataValidator = ValibotDataValidator | ZodDataValidator;\n```",
869+
"content": "```typescript\nexport type TypedDataValidator = StandardSchemaDataValidator | ValibotDataValidator | ZodDataValidator;\n```",
842870
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/types.ts",
843871
"mdFile": "qwik-city.typeddatavalidator.md"
844872
},

Diff for: packages/docs/src/routes/api/qwik-city/index.md

+34-11
Original file line numberDiff line numberDiff line change
@@ -1388,11 +1388,13 @@ T
13881388
13891389
```typescript
13901390
export type GetValidatorInputType<VALIDATOR extends TypedDataValidator> =
1391-
VALIDATOR extends ValibotDataValidator<infer TYPE>
1392-
? v.InferInput<TYPE>
1393-
: VALIDATOR extends ZodDataValidator<infer TYPE>
1394-
? z.input<TYPE>
1395-
: never;
1391+
VALIDATOR extends StandardSchemaDataValidator<infer TYPE>
1392+
? StandardSchemaV1.InferInput<TYPE>
1393+
: VALIDATOR extends ValibotDataValidator<infer TYPE>
1394+
? v.InferInput<TYPE>
1395+
: VALIDATOR extends ZodDataValidator<infer TYPE>
1396+
? z.input<TYPE>
1397+
: never;
13961398
```
13971399
13981400
**References:** [TypedDataValidator](#typeddatavalidator)
@@ -1403,11 +1405,13 @@ export type GetValidatorInputType<VALIDATOR extends TypedDataValidator> =
14031405
14041406
```typescript
14051407
export type GetValidatorOutputType<VALIDATOR extends TypedDataValidator> =
1406-
VALIDATOR extends ValibotDataValidator<infer TYPE>
1407-
? v.InferOutput<TYPE>
1408-
: VALIDATOR extends ZodDataValidator<infer TYPE>
1409-
? z.output<TYPE>
1410-
: never;
1408+
VALIDATOR extends StandardSchemaDataValidator<infer TYPE>
1409+
? StandardSchemaV1.InferOutput<TYPE>
1410+
: VALIDATOR extends ValibotDataValidator<infer TYPE>
1411+
? v.InferOutput<TYPE>
1412+
: VALIDATOR extends ZodDataValidator<infer TYPE>
1413+
? z.output<TYPE>
1414+
: never;
14111415
```
14121416
14131417
**References:** [TypedDataValidator](#typeddatavalidator)
@@ -2173,6 +2177,14 @@ RouterOutlet: import("@builder.io/qwik").Component<unknown>;
21732177
21742178
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/router-outlet-component.tsx)
21752179
2180+
## schema$
2181+
2182+
```typescript
2183+
schema$: StandardSchemaConstructor;
2184+
```
2185+
2186+
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/server-functions.ts)
2187+
21762188
## server$
21772189
21782190
```typescript
@@ -2306,6 +2318,14 @@ JSXOutput
23062318
23072319
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/sw-component.tsx)
23082320
2321+
## standardSchemaQrl
2322+
2323+
```typescript
2324+
standardSchemaQrl: StandardSchemaConstructorQRL;
2325+
```
2326+
2327+
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/server-functions.ts)
2328+
23092329
## StaticGenerate
23102330
23112331
```typescript
@@ -2373,7 +2393,10 @@ export type StrictUnion<T> = Prettify<StrictUnionHelper<T, T>>;
23732393
## TypedDataValidator
23742394
23752395
```typescript
2376-
export type TypedDataValidator = ValibotDataValidator | ZodDataValidator;
2396+
export type TypedDataValidator =
2397+
| StandardSchemaDataValidator
2398+
| ValibotDataValidator
2399+
| ZodDataValidator;
23772400
```
23782401
23792402
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/types.ts)

Diff for: packages/docs/src/routes/api/qwik/api.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,7 @@
17741774
}
17751775
],
17761776
"kind": "Function",
1777-
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\nJSXNode&lt;'script'&gt;",
1777+
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\n[JSXNode](#jsxnode)<!-- -->&lt;'script'&gt;",
17781778
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts",
17791779
"mdFile": "qwik.prefetchserviceworker.md"
17801780
},

Diff for: packages/docs/src/routes/api/qwik/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3651,7 +3651,7 @@ opts
36513651
</tbody></table>
36523652
**Returns:**
36533653
3654-
JSXNode&lt;'script'&gt;
3654+
[JSXNode](#jsxnode)&lt;'script'&gt;
36553655
36563656
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts)
36573657

Diff for: packages/insights/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"vite": "5.3.5",
4040
"vite-tsconfig-paths": "4.3.2",
4141
"vitest": "2.0.5",
42-
"zod": "3.22.4"
42+
"zod": "3.24.2"
4343
},
4444
"engines": {
4545
"node": ">=16.8.0 <18.0.0 || >=18.11"

Diff for: packages/qwik-city/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"vfile": "6.0.2",
1414
"vite": "^5",
1515
"vite-imagetools": "^7",
16-
"zod": "3.22.4"
16+
"zod": "3.24.2"
1717
},
1818
"devDependencies": {
1919
"@azure/functions": "3.5.1",

Diff for: packages/qwik-city/src/runtime/src/api.md

+15-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { RequestEventCommon } from '@builder.io/qwik-city/middleware/request-han
2323
import { RequestEventLoader } from '@builder.io/qwik-city/middleware/request-handler';
2424
import { RequestHandler } from '@builder.io/qwik-city/middleware/request-handler';
2525
import type { ResolveSyncValue } from '@builder.io/qwik-city/middleware/request-handler';
26+
import type { StandardSchemaV1 } from '@standard-schema/spec';
2627
import type * as v from 'valibot';
2728
import type { ValueOrPromise } from '@builder.io/qwik';
2829
import { z } from 'zod';
@@ -240,14 +241,15 @@ export interface FormSubmitSuccessDetail<T> {
240241
value: T;
241242
}
242243

244+
// Warning: (ae-forgotten-export) The symbol "StandardSchemaDataValidator" needs to be exported by the entry point index.d.ts
243245
// Warning: (ae-forgotten-export) The symbol "ValibotDataValidator" needs to be exported by the entry point index.d.ts
244246
// Warning: (ae-forgotten-export) The symbol "ZodDataValidator" needs to be exported by the entry point index.d.ts
245247
//
246248
// @public (undocumented)
247-
export type GetValidatorInputType<VALIDATOR extends TypedDataValidator> = VALIDATOR extends ValibotDataValidator<infer TYPE> ? v.InferInput<TYPE> : VALIDATOR extends ZodDataValidator<infer TYPE> ? z_2.input<TYPE> : never;
249+
export type GetValidatorInputType<VALIDATOR extends TypedDataValidator> = VALIDATOR extends StandardSchemaDataValidator<infer TYPE> ? StandardSchemaV1.InferInput<TYPE> : VALIDATOR extends ValibotDataValidator<infer TYPE> ? v.InferInput<TYPE> : VALIDATOR extends ZodDataValidator<infer TYPE> ? z_2.input<TYPE> : never;
248250

249251
// @public (undocumented)
250-
export type GetValidatorOutputType<VALIDATOR extends TypedDataValidator> = VALIDATOR extends ValibotDataValidator<infer TYPE> ? v.InferOutput<TYPE> : VALIDATOR extends ZodDataValidator<infer TYPE> ? z_2.output<TYPE> : never;
252+
export type GetValidatorOutputType<VALIDATOR extends TypedDataValidator> = VALIDATOR extends StandardSchemaDataValidator<infer TYPE> ? StandardSchemaV1.InferOutput<TYPE> : VALIDATOR extends ValibotDataValidator<infer TYPE> ? v.InferOutput<TYPE> : VALIDATOR extends ZodDataValidator<infer TYPE> ? z_2.output<TYPE> : never;
251253

252254
// @public (undocumented)
253255
export type GetValidatorType<VALIDATOR extends TypedDataValidator> = GetValidatorOutputType<VALIDATOR>;
@@ -429,6 +431,11 @@ export type RouteNavigate = QRL<(path?: string | number | URL, options?: {
429431
// @public (undocumented)
430432
export const RouterOutlet: Component<unknown>;
431433

434+
// Warning: (ae-forgotten-export) The symbol "StandardSchemaConstructor" needs to be exported by the entry point index.d.ts
435+
//
436+
// @public (undocumented)
437+
export const schema$: StandardSchemaConstructor;
438+
432439
// Warning: (ae-forgotten-export) The symbol "ServerConfig" needs to be exported by the entry point index.d.ts
433440
//
434441
// @public (undocumented)
@@ -451,6 +458,11 @@ export const ServiceWorkerRegister: (props: {
451458
nonce?: string;
452459
}) => JSXOutput;
453460

461+
// Warning: (ae-forgotten-export) The symbol "StandardSchemaConstructorQRL" needs to be exported by the entry point index.d.ts
462+
//
463+
// @public (undocumented)
464+
export const standardSchemaQrl: StandardSchemaConstructorQRL;
465+
454466
// @public (undocumented)
455467
export interface StaticGenerate {
456468
// (undocumented)
@@ -469,7 +481,7 @@ export type StaticGenerateHandler = ({ env, }: {
469481
export type StrictUnion<T> = Prettify<StrictUnionHelper<T, T>>;
470482

471483
// @public (undocumented)
472-
export type TypedDataValidator = ValibotDataValidator | ZodDataValidator;
484+
export type TypedDataValidator = StandardSchemaDataValidator | ValibotDataValidator | ZodDataValidator;
473485

474486
// Warning: (ae-forgotten-export) The symbol "ContentState" needs to be exported by the entry point index.d.ts
475487
//

Diff for: packages/qwik-city/src/runtime/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export { routeAction$, routeActionQrl } from './server-functions';
6161
export { globalAction$, globalActionQrl } from './server-functions';
6262
export { routeLoader$, routeLoaderQrl } from './server-functions';
6363
export { server$, serverQrl } from './server-functions';
64+
export { schema$, standardSchemaQrl } from './server-functions';
6465
export { valibot$, valibotQrl } from './server-functions';
6566
export { zod$, zodQrl } from './server-functions';
6667
export { validator$, validatorQrl } from './server-functions';

Diff for: packages/qwik-city/src/runtime/src/server-functions.ts

+59
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
type ValueOrPromise,
1414
} from '@builder.io/qwik';
1515

16+
import type { StandardSchemaV1 } from '@standard-schema/spec';
1617
import * as v from 'valibot';
1718
import { z } from 'zod';
1819
import type { RequestEventLoader } from '../../middleware/request-handler/types';
@@ -47,6 +48,9 @@ import type {
4748
ZodConstructor,
4849
ZodConstructorQRL,
4950
ZodDataValidator,
51+
StandardSchemaDataValidator,
52+
StandardSchemaConstructorQRL,
53+
StandardSchemaConstructor,
5054
} from './types';
5155
import { useAction, useLocation, useQwikCityEnv } from './use-functions';
5256

@@ -253,6 +257,61 @@ const flattenValibotIssues = (issues: v.GenericIssue[]) => {
253257
}, {});
254258
};
255259

260+
/** @public */
261+
export const standardSchemaQrl: StandardSchemaConstructorQRL = (
262+
qrl: QRL<StandardSchemaV1 | ((ev: RequestEvent) => StandardSchemaV1)>
263+
): StandardSchemaDataValidator => {
264+
if (isServer) {
265+
return {
266+
__brand: 'standard-schema',
267+
async validate(ev, inputData) {
268+
const schema: StandardSchemaV1 = await qrl
269+
.resolve()
270+
.then((obj) => ('~standard' in obj ? obj : obj(ev)));
271+
const data = inputData ?? (await ev.parseBody());
272+
const result = await schema['~standard'].validate(data);
273+
if (!result.issues) {
274+
return {
275+
success: true,
276+
data: result.value,
277+
};
278+
} else {
279+
if (isDev) {
280+
console.error('ERROR: Standard Schema validation failed', result.issues);
281+
}
282+
const formErrors: string[] = [];
283+
const fieldErrors: Partial<Record<string, string[]>> = {};
284+
for (const issue of result.issues) {
285+
const dotPath = issue.path
286+
?.map((item) => (typeof item === 'object' ? item.key : item))
287+
.join('.');
288+
if (dotPath) {
289+
const sub = fieldErrors[dotPath];
290+
if (sub) {
291+
sub.push(issue.message);
292+
} else {
293+
fieldErrors[dotPath] = [issue.message];
294+
}
295+
} else {
296+
formErrors.push(issue.message);
297+
}
298+
}
299+
return {
300+
success: false,
301+
status: 400,
302+
error: { formErrors, fieldErrors },
303+
};
304+
}
305+
},
306+
};
307+
}
308+
return undefined as never;
309+
};
310+
311+
/** @public */
312+
export const schema$: StandardSchemaConstructor =
313+
/*#__PURE__*/ implicit$FirstArg(standardSchemaQrl);
314+
256315
/** @alpha */
257316
export const valibotQrl: ValibotConstructorQRL = (
258317
qrl: QRL<

0 commit comments

Comments
 (0)