diff --git a/.gitattributes b/.gitattributes
index 7415f78..e84f95f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -20,6 +20,7 @@ packages/all/src/__tests__/tsconfig.json linguist-generated
packages/all/src/__tests__/typebox.test.ts linguist-generated
packages/all/src/__tests__/valibot.test.ts linguist-generated
packages/all/src/__tests__/valita.test.ts linguist-generated
+packages/all/src/__tests__/vine.test.ts linguist-generated
packages/all/src/__tests__/yup.test.ts linguist-generated
packages/all/src/__tests__/zod.test.ts linguist-generated
packages/all/src/index.ts linguist-generated
@@ -79,6 +80,7 @@ packages/main/src/__tests__/tsconfig.json linguist-generated
packages/main/src/__tests__/typebox.test.ts linguist-generated
packages/main/src/__tests__/valibot.test.ts linguist-generated
packages/main/src/__tests__/valita.test.ts linguist-generated
+packages/main/src/__tests__/vine.test.ts linguist-generated
packages/main/src/__tests__/yup.test.ts linguist-generated
packages/main/src/__tests__/zod.test.ts linguist-generated
packages/main/src/adapters.ts linguist-generated
@@ -114,6 +116,10 @@ packages/valita/README.md linguist-generated
packages/valita/src/__tests__/tsconfig.json linguist-generated
packages/valita/src/index.ts linguist-generated
packages/valita/tsconfig.json linguist-generated
+packages/vine/README.md linguist-generated
+packages/vine/src/__tests__/tsconfig.json linguist-generated
+packages/vine/src/index.ts linguist-generated
+packages/vine/tsconfig.json linguist-generated
packages/yup/README.md linguist-generated
packages/yup/src/__tests__/tsconfig.json linguist-generated
packages/yup/src/index.ts linguist-generated
diff --git a/README.md b/README.md
index bcd5998..839669f 100644
--- a/README.md
+++ b/README.md
@@ -234,6 +234,16 @@ We value flexibility, which is why there are multiple ways of using TypeSchema:
@typeschema/fastest-validator |
|
+
+ vine |
+ |
+ ✅ |
+ ✅ |
+ ✅ |
+ 🧐 |
+ @typeschema/vine |
+ |
+
suretype |
|
diff --git a/packages/all/package.json b/packages/all/package.json
index bac758b..2f34e81 100644
--- a/packages/all/package.json
+++ b/packages/all/package.json
@@ -29,6 +29,7 @@
"typebox",
"valibot",
"valita",
+ "vine",
"yup",
"zod"
],
@@ -91,6 +92,7 @@
"@typeschema/typebox": "workspace:*",
"@typeschema/valibot": "workspace:*",
"@typeschema/valita": "workspace:*",
+ "@typeschema/vine": "workspace:*",
"@typeschema/yup": "workspace:*",
"@typeschema/zod": "workspace:*"
},
@@ -118,6 +120,7 @@
"@gcornut/valibot-json-schema": "^0.0.25",
"valibot": "^0.30.0",
"@badrap/valita": "^0.3.6",
+ "@vinejs/vine": "^2.0.0",
"@sodaru/yup-to-json-schema": "^2.0.1",
"yup": "^1.4.0",
"zod": "^3.22.4",
diff --git a/packages/all/src/__tests__/vine.test.ts b/packages/all/src/__tests__/vine.test.ts
new file mode 100644
index 0000000..0bfa1c5
--- /dev/null
+++ b/packages/all/src/__tests__/vine.test.ts
@@ -0,0 +1,82 @@
+/**
+ * This file is generated. Do not modify it manually!
+ */
+
+import type {Infer, InferIn} from '..';
+
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+import {expectTypeOf} from 'expect-type';
+import {describe, expect, test} from 'vitest';
+
+import {assert, validate, wrap} from '..';
+
+describe('vine', () => {
+ const schema = vine.object({
+ age: vine.number(),
+ createdAt: vine.date({formats: {utc: true}}),
+ email: vine.string().email(),
+ id: vine.string(),
+ name: vine.string(),
+ updatedAt: vine.date({formats: {utc: true}}),
+ });
+
+ const data = {
+ age: 123 as string | number,
+ createdAt: '2021-01-01T00:00:00.000Z' as string | number,
+ email: 'john.doe@test.com',
+ id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+ name: 'John Doe',
+ updatedAt: '2021-01-01T00:00:00.000Z' as string | number,
+ };
+ const outputData = {
+ age: 123,
+ createdAt: new Date('2021-01-01T00:00:00.000Z'),
+ email: 'john.doe@test.com',
+ id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+ name: 'John Doe',
+ updatedAt: new Date('2021-01-01T00:00:00.000Z'),
+ };
+ const badData = {
+ age: '123a',
+ createdAt: '2021-01-01T00:00:00.000Z',
+ email: 'john.doe@test.com',
+ id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+ name: 'John Doe',
+ updatedAt: '2021-01-01T00:00:00.000Z',
+ };
+
+ test('infer', () => {
+ expectTypeOf>().toEqualTypeOf(outputData);
+ expectTypeOf>().toEqualTypeOf(data);
+ });
+
+ test('validate', async () => {
+ expect(await validate(schema, data)).toStrictEqual({
+ data: outputData,
+ success: true,
+ });
+ expect(await validate(schema, badData)).toStrictEqual({
+ issues: [{message: 'The age field must be a number', path: ['age']}],
+ success: false,
+ });
+ });
+
+ test('assert', async () => {
+ expect(await assert(schema, data)).toStrictEqual(outputData);
+ await expect(assert(schema, badData)).rejects.toThrow();
+ });
+
+ test('wrap', async () => {
+ const tRPC = initTRPC.create();
+ const router = tRPC.router({
+ hello: tRPC.procedure.input(wrap(schema)).query(({input}) => {
+ expectTypeOf().toEqualTypeOf(outputData);
+ return input;
+ }),
+ });
+ const createCaller = tRPC.createCallerFactory(router);
+ const caller = createCaller({});
+ expect(await caller.hello(data)).toStrictEqual(outputData);
+ });
+});
diff --git a/packages/main/package.json b/packages/main/package.json
index a584ad1..2d473fc 100644
--- a/packages/main/package.json
+++ b/packages/main/package.json
@@ -29,6 +29,7 @@
"typebox",
"valibot",
"valita",
+ "vine",
"yup",
"zod"
],
@@ -115,6 +116,8 @@
"valibot": "^0.30.0",
"@typeschema/valita": "workspace:*",
"@badrap/valita": "^0.3.6",
+ "@typeschema/vine": "workspace:*",
+ "@vinejs/vine": "^2.0.0",
"@typeschema/yup": "workspace:*",
"@sodaru/yup-to-json-schema": "^2.0.1",
"yup": "^1.4.0",
@@ -139,6 +142,7 @@
"@typeschema/typebox": "workspace:*",
"@typeschema/valibot": "workspace:*",
"@typeschema/valita": "workspace:*",
+ "@typeschema/vine": "workspace:*",
"@typeschema/yup": "workspace:*",
"@typeschema/zod": "workspace:*"
},
@@ -191,6 +195,9 @@
"@typeschema/valita": {
"optional": true
},
+ "@typeschema/vine": {
+ "optional": true
+ },
"@typeschema/yup": {
"optional": true
},
diff --git a/packages/main/src/__tests__/vine.test.ts b/packages/main/src/__tests__/vine.test.ts
new file mode 100644
index 0000000..0bfa1c5
--- /dev/null
+++ b/packages/main/src/__tests__/vine.test.ts
@@ -0,0 +1,82 @@
+/**
+ * This file is generated. Do not modify it manually!
+ */
+
+import type {Infer, InferIn} from '..';
+
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+import {expectTypeOf} from 'expect-type';
+import {describe, expect, test} from 'vitest';
+
+import {assert, validate, wrap} from '..';
+
+describe('vine', () => {
+ const schema = vine.object({
+ age: vine.number(),
+ createdAt: vine.date({formats: {utc: true}}),
+ email: vine.string().email(),
+ id: vine.string(),
+ name: vine.string(),
+ updatedAt: vine.date({formats: {utc: true}}),
+ });
+
+ const data = {
+ age: 123 as string | number,
+ createdAt: '2021-01-01T00:00:00.000Z' as string | number,
+ email: 'john.doe@test.com',
+ id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+ name: 'John Doe',
+ updatedAt: '2021-01-01T00:00:00.000Z' as string | number,
+ };
+ const outputData = {
+ age: 123,
+ createdAt: new Date('2021-01-01T00:00:00.000Z'),
+ email: 'john.doe@test.com',
+ id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+ name: 'John Doe',
+ updatedAt: new Date('2021-01-01T00:00:00.000Z'),
+ };
+ const badData = {
+ age: '123a',
+ createdAt: '2021-01-01T00:00:00.000Z',
+ email: 'john.doe@test.com',
+ id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+ name: 'John Doe',
+ updatedAt: '2021-01-01T00:00:00.000Z',
+ };
+
+ test('infer', () => {
+ expectTypeOf>().toEqualTypeOf(outputData);
+ expectTypeOf>().toEqualTypeOf(data);
+ });
+
+ test('validate', async () => {
+ expect(await validate(schema, data)).toStrictEqual({
+ data: outputData,
+ success: true,
+ });
+ expect(await validate(schema, badData)).toStrictEqual({
+ issues: [{message: 'The age field must be a number', path: ['age']}],
+ success: false,
+ });
+ });
+
+ test('assert', async () => {
+ expect(await assert(schema, data)).toStrictEqual(outputData);
+ await expect(assert(schema, badData)).rejects.toThrow();
+ });
+
+ test('wrap', async () => {
+ const tRPC = initTRPC.create();
+ const router = tRPC.router({
+ hello: tRPC.procedure.input(wrap(schema)).query(({input}) => {
+ expectTypeOf().toEqualTypeOf(outputData);
+ return input;
+ }),
+ });
+ const createCaller = tRPC.createCallerFactory(router);
+ const caller = createCaller({});
+ expect(await caller.hello(data)).toStrictEqual(outputData);
+ });
+});
diff --git a/packages/main/src/adapters.ts b/packages/main/src/adapters.ts
index 972067b..33b6ba5 100644
--- a/packages/main/src/adapters.ts
+++ b/packages/main/src/adapters.ts
@@ -18,6 +18,7 @@ import type {AdapterResolver as SuretypeResolver} from '@typeschema/suretype';
import type {AdapterResolver as TypeboxResolver} from '@typeschema/typebox';
import type {AdapterResolver as ValibotResolver} from '@typeschema/valibot';
import type {AdapterResolver as ValitaResolver} from '@typeschema/valita';
+import type {AdapterResolver as VineResolver} from '@typeschema/vine';
import type {AdapterResolver as YupResolver} from '@typeschema/yup';
import type {AdapterResolver as ZodResolver} from '@typeschema/zod';
@@ -38,6 +39,7 @@ export type AdapterResolvers = {
typebox: TypeboxResolver;
valibot: ValibotResolver;
valita: ValitaResolver;
+ vine: VineResolver;
yup: YupResolver;
zod: ZodResolver;
};
diff --git a/packages/main/src/selector.ts b/packages/main/src/selector.ts
index 8a9a856..09a1d21 100644
--- a/packages/main/src/selector.ts
+++ b/packages/main/src/selector.ts
@@ -92,6 +92,7 @@ export type Select =
: TSchema extends {kind: unknown} ? 'deepkit'
: TSchema extends {addValidator: unknown} ? 'ow'
: TSchema extends {toTerminals: unknown} ? 'valita'
+ : TSchema extends {bail: unknown} ? 'vine'
: IsJSONSchema extends true ? 'json'
: 'fastestValidator'
: never;
@@ -129,6 +130,7 @@ export const select: <
if ('kind' in schema) return is.deepkit(notJSON(schema));
if ('addValidator' in schema) return is.ow(notJSON(schema));
if ('toTerminals' in schema) return is.valita(notJSON(schema));
+ if ('bail' in schema) return is.vine(notJSON(schema));
if (isJSONSchema(schema)) return is.json(schema);
return is.fastestValidator(schema);
}
diff --git a/packages/main/src/serialization.ts b/packages/main/src/serialization.ts
index 5aec484..a232a99 100644
--- a/packages/main/src/serialization.ts
+++ b/packages/main/src/serialization.ts
@@ -69,6 +69,7 @@ export const serializationAdapter: SerializationAdapter = selec
typebox: async schema => (await importTypeboxSerializationAdapter())(schema),
valibot: async schema => (await importValibotSerializationAdapter())(schema),
valita: unsupportedAdapter('@typeschema/valita'),
+ vine: unsupportedAdapter('@typeschema/vine'),
yup: async schema => (await importYupSerializationAdapter())(schema),
zod: async schema => (await importZodSerializationAdapter())(schema),
});
diff --git a/packages/main/src/validation.ts b/packages/main/src/validation.ts
index 9ce525a..8c301ac 100644
--- a/packages/main/src/validation.ts
+++ b/packages/main/src/validation.ts
@@ -92,6 +92,11 @@ const importValitaValidationAdapter = memoize(async () => {
return validationAdapter;
});
+const importVineValidationAdapter = memoize(async () => {
+ const {validationAdapter} = await import('@typeschema/vine');
+ return validationAdapter;
+});
+
const importYupValidationAdapter = memoize(async () => {
const {validationAdapter} = await import('@typeschema/yup');
return validationAdapter;
@@ -119,6 +124,7 @@ export const validationAdapter: ValidationAdapter = select({
typebox: async schema => (await importTypeboxValidationAdapter())(schema),
valibot: async schema => (await importValibotValidationAdapter())(schema),
valita: async schema => (await importValitaValidationAdapter())(schema),
+ vine: async schema => (await importVineValidationAdapter())(schema),
yup: async schema => (await importYupValidationAdapter())(schema),
zod: async schema => (await importZodValidationAdapter())(schema),
});
diff --git a/packages/vine/README.md b/packages/vine/README.md
new file mode 100644
index 0000000..db42a5a
--- /dev/null
+++ b/packages/vine/README.md
@@ -0,0 +1,49 @@
+
+
+
+@typeschema/vine
+
+
+
+
+
+
+
+ Reusable adapter for VineJS schemas
+
+ https://typeschema.com ✨
+
+
+```ts
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+
+import {wrap} from '@typeschema/vine';
+
+const schema = vine.object({name: vine.string()});
+
+const t = initTRPC.create();
+const appRouter = t.router({
+ hello: t.procedure
+ .input(wrap(schema))
+ .query(({input}) => `Hello, ${input.name}!`),
+ // ^? {name: string}
+});
+
+```
+
+Use it directly or through [`@typeschema/main`](https://github.com/decs/typeschema/tree/main/packages/main)
+
+## Dependencies
+- [`@vinejs/vine`](https://www.npmjs.com/package/@vinejs/vine): Required for inference and validation (`^2.0.0`)
+
+## API
+
+### Inference
+- `Infer`: Extracts the output type of a schema
+- `InferIn`: Extracts the input type of a schema
+
+### Validation
+- `wrap(schema)`: Returns the wrapped schema with access to its operations
+- `validate(schema, data)`: Returns the validated data or a list of validation issues
+- `assert(schema, data)`: Returns the validated data or throws an `AggregateError`
diff --git a/packages/vine/package.json b/packages/vine/package.json
new file mode 100644
index 0000000..8ff3679
--- /dev/null
+++ b/packages/vine/package.json
@@ -0,0 +1,77 @@
+{
+ "//": "This file is partially generated. Only some fields can be modified manually!",
+ "name": "@typeschema/vine",
+ "//version": "This field is manually maintained.",
+ "version": "0.0.0",
+ "//description": "This field is manually maintained.",
+ "description": "Reusable adapter for VineJS schemas",
+ "keywords": [
+ "typescript",
+ "type",
+ "schema",
+ "adapter",
+ "validation",
+ "inference",
+ "assert"
+ ],
+ "homepage": "https://typeschema.com",
+ "license": "MIT",
+ "author": {
+ "name": "André Costa",
+ "email": "andrefonsecacosta@gmail.com"
+ },
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org/"
+ },
+ "files": [
+ "/dist"
+ ],
+ "main": "dist/index.js",
+ "module": "dist/index.mjs",
+ "types": "dist/index.d.ts",
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/index.d.mts",
+ "default": "./dist/index.mjs"
+ },
+ "require": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ }
+ },
+ "sideEffects": false,
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/decs/typeschema.git"
+ },
+ "scripts": {
+ "build": "tsup --config ../../tsup.config.ts",
+ "lint": "eslint src --fix",
+ "lint:package": "publint && attw --pack",
+ "test": "vitest --config ../../vitest.config.ts",
+ "upgrade:deps": "ncu -u --dep=dev,peer --reject ow"
+ },
+ "dependencies": {
+ "@typeschema/core": "workspace:*"
+ },
+ "//devDependencies": "This field is manually maintained.",
+ "devDependencies": {
+ "@vinejs/vine": "^2.0.0"
+ },
+ "//peerDependencies": {
+ "//": "This field is manually maintained.",
+ "@vinejs/vine": "Required for inference and validation"
+ },
+ "peerDependencies": {
+ "@vinejs/vine": "^2.0.0"
+ },
+ "//peerDependenciesMeta": "This field is manually maintained.",
+ "peerDependenciesMeta": {
+ "@vinejs/vine": {
+ "optional": true
+ }
+ }
+}
diff --git a/packages/vine/src/__tests__/example.ts b/packages/vine/src/__tests__/example.ts
new file mode 100644
index 0000000..14882d6
--- /dev/null
+++ b/packages/vine/src/__tests__/example.ts
@@ -0,0 +1,14 @@
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+
+import {wrap} from '..';
+
+const schema = vine.object({name: vine.string()});
+
+const t = initTRPC.create();
+const appRouter = t.router({
+ hello: t.procedure
+ .input(wrap(schema))
+ .query(({input}) => `Hello, ${input.name}!`),
+ // ^? {name: string}
+});
diff --git a/packages/vine/src/__tests__/tsconfig.json b/packages/vine/src/__tests__/tsconfig.json
new file mode 100644
index 0000000..6a86fc8
--- /dev/null
+++ b/packages/vine/src/__tests__/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "//": "This file is generated. Do not modify it manually!",
+ "extends": "../../../../tsconfig.test.json",
+ "include": ["*.ts"]
+}
diff --git a/packages/vine/src/__tests__/vine.test.ts b/packages/vine/src/__tests__/vine.test.ts
new file mode 100644
index 0000000..d666c1b
--- /dev/null
+++ b/packages/vine/src/__tests__/vine.test.ts
@@ -0,0 +1,78 @@
+import type {Infer, InferIn} from '..';
+
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+import {expectTypeOf} from 'expect-type';
+import {describe, expect, test} from 'vitest';
+
+import {assert, validate, wrap} from '..';
+
+describe('vine', () => {
+ const schema = vine.object({
+ age: vine.number(),
+ createdAt: vine.date({formats: {utc: true}}),
+ email: vine.string().email(),
+ id: vine.string(),
+ name: vine.string(),
+ updatedAt: vine.date({formats: {utc: true}}),
+ });
+
+ const data = {
+ age: 123 as string | number,
+ createdAt: '2021-01-01T00:00:00.000Z' as string | number,
+ email: 'john.doe@test.com',
+ id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+ name: 'John Doe',
+ updatedAt: '2021-01-01T00:00:00.000Z' as string | number,
+ };
+ const outputData = {
+ age: 123,
+ createdAt: new Date('2021-01-01T00:00:00.000Z'),
+ email: 'john.doe@test.com',
+ id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+ name: 'John Doe',
+ updatedAt: new Date('2021-01-01T00:00:00.000Z'),
+ };
+ const badData = {
+ age: '123a',
+ createdAt: '2021-01-01T00:00:00.000Z',
+ email: 'john.doe@test.com',
+ id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+ name: 'John Doe',
+ updatedAt: '2021-01-01T00:00:00.000Z',
+ };
+
+ test('infer', () => {
+ expectTypeOf>().toEqualTypeOf(outputData);
+ expectTypeOf>().toEqualTypeOf(data);
+ });
+
+ test('validate', async () => {
+ expect(await validate(schema, data)).toStrictEqual({
+ data: outputData,
+ success: true,
+ });
+ expect(await validate(schema, badData)).toStrictEqual({
+ issues: [{message: 'The age field must be a number', path: ['age']}],
+ success: false,
+ });
+ });
+
+ test('assert', async () => {
+ expect(await assert(schema, data)).toStrictEqual(outputData);
+ await expect(assert(schema, badData)).rejects.toThrow();
+ });
+
+ test('wrap', async () => {
+ const tRPC = initTRPC.create();
+ const router = tRPC.router({
+ hello: tRPC.procedure.input(wrap(schema)).query(({input}) => {
+ expectTypeOf().toEqualTypeOf(outputData);
+ return input;
+ }),
+ });
+ const createCaller = tRPC.createCallerFactory(router);
+ const caller = createCaller({});
+ expect(await caller.hello(data)).toStrictEqual(outputData);
+ });
+});
diff --git a/packages/vine/src/index.ts b/packages/vine/src/index.ts
new file mode 100644
index 0000000..c5a1586
--- /dev/null
+++ b/packages/vine/src/index.ts
@@ -0,0 +1,37 @@
+/**
+ * This file is generated. Do not modify it manually!
+ */
+
+import type {
+ InputFrom,
+ OutputFrom,
+ SchemaFrom,
+ UnknownIfNever,
+} from '@typeschema/core';
+
+import {
+ createAssert,
+ createValidate,
+ createWrap,
+} from '@typeschema/core';
+
+import {AdapterResolver} from './resolver';
+import {validationAdapter} from './validation';
+
+export type Schema = SchemaFrom;
+export type Infer = UnknownIfNever<
+ OutputFrom
+>;
+export type InferIn = UnknownIfNever<
+ InputFrom
+>;
+
+export const validate = createValidate(validationAdapter);
+export const assert = createAssert(validate);
+export const wrap = createWrap(assert, validate);
+
+
+export {
+ AdapterResolver,
+ validationAdapter,
+};
diff --git a/packages/vine/src/resolver.ts b/packages/vine/src/resolver.ts
new file mode 100644
index 0000000..5c8679a
--- /dev/null
+++ b/packages/vine/src/resolver.ts
@@ -0,0 +1,12 @@
+import type {IfDefined, Resolver} from '@typeschema/core';
+import type {BaseType} from '@vinejs/vine';
+import type {Infer, InferInput} from '@vinejs/vine/types';
+
+export interface AdapterResolver extends Resolver {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ base: IfDefined, '@vinejs/vine'>;
+ input: this['schema'] extends this['base']
+ ? InferInput
+ : never;
+ output: this['schema'] extends this['base'] ? Infer : never;
+}
diff --git a/packages/vine/src/validation.ts b/packages/vine/src/validation.ts
new file mode 100644
index 0000000..77651ff
--- /dev/null
+++ b/packages/vine/src/validation.ts
@@ -0,0 +1,37 @@
+import type {AdapterResolver} from './resolver';
+import type {ValidationAdapter} from '@typeschema/core';
+
+import {memoize} from '@typeschema/core';
+
+const importValidationModule = memoize(async () => {
+ const {errors, Vine} = await import('@vinejs/vine');
+ return {errors, vine: new Vine()};
+});
+
+export const validationAdapter: ValidationAdapter<
+ AdapterResolver
+> = async schema => {
+ const {errors, vine} = await importValidationModule();
+ const validator = vine.compile(schema);
+ return async data => {
+ try {
+ return {
+ data: await validator.validate(data),
+ success: true,
+ };
+ } catch (error) {
+ if (error instanceof errors.E_VALIDATION_ERROR) {
+ return {
+ issues: error.messages.map(
+ ({message, field}: {message: string; field: string}) => ({
+ message,
+ path: field != null ? field.split('.') : undefined,
+ }),
+ ),
+ success: false,
+ };
+ }
+ throw error;
+ }
+ };
+};
diff --git a/packages/vine/tsconfig.json b/packages/vine/tsconfig.json
new file mode 100644
index 0000000..caf2717
--- /dev/null
+++ b/packages/vine/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "//": "This file is generated. Do not modify it manually!",
+ "extends": "../../tsconfig.json",
+ "include": ["src"]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f4dfd5b..4f8ca99 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -281,6 +281,9 @@ importers:
'@typeschema/valita':
specifier: workspace:*
version: link:../valita
+ '@typeschema/vine':
+ specifier: workspace:*
+ version: link:../vine
'@typeschema/yup':
specifier: workspace:*
version: link:../yup
@@ -309,6 +312,9 @@ importers:
'@sodaru/yup-to-json-schema':
specifier: ^2.0.1
version: 2.0.1
+ '@vinejs/vine':
+ specifier: ^2.0.0
+ version: 2.0.0
ajv:
specifier: ^8.12.0
version: 8.12.0
@@ -559,12 +565,18 @@ importers:
'@typeschema/valita':
specifier: workspace:*
version: link:../valita
+ '@typeschema/vine':
+ specifier: workspace:*
+ version: link:../vine
'@typeschema/yup':
specifier: workspace:*
version: link:../yup
'@typeschema/zod':
specifier: workspace:*
version: link:../zod
+ '@vinejs/vine':
+ specifier: ^2.0.0
+ version: 2.0.0
ajv:
specifier: ^8.12.0
version: 8.12.0
@@ -699,6 +711,16 @@ importers:
specifier: ^0.3.6
version: 0.3.6
+ packages/vine:
+ dependencies:
+ '@typeschema/core':
+ specifier: workspace:*
+ version: link:../core
+ devDependencies:
+ '@vinejs/vine':
+ specifier: ^2.0.0
+ version: 2.0.0
+
packages/yup:
dependencies:
'@typeschema/core':
@@ -3486,6 +3508,11 @@ packages:
write-yaml-file: 5.0.0
dev: true
+ /@poppinss/macroable@1.0.2:
+ resolution: {integrity: sha512-xhhEcEvhQC8mP5oOr5hbE4CmUgmw/IPV1jhpGg2xSkzoFrt9i8YVqBQt9744EFesi5F7pBheWozg63RUBM/5JA==}
+ engines: {node: '>=18.16.0'}
+ dev: true
+
/@rollup/plugin-node-resolve@15.2.3(rollup@4.12.0):
resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==}
engines: {node: '>=14.0.0'}
@@ -4366,6 +4393,25 @@ packages:
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
dev: true
+ /@vinejs/compiler@2.5.0:
+ resolution: {integrity: sha512-hg4ekaB5Y2zh+IWzBiC/WCDWrIfpVnKu/ubUvelKlidc/VbulsexoFRw5kJGHZenPVI5YzNnDeTdYSALkTV7jQ==}
+ engines: {node: '>=18.0.0'}
+ dev: true
+
+ /@vinejs/vine@2.0.0:
+ resolution: {integrity: sha512-NqgT4B2uo4mMsGI8LJdpuXNnan7F3xm10+kHaXpqI0PCYpn7+Xiic6av586mmj747/qZ3iR8o4C9cL54WU1fWw==}
+ engines: {node: '>=18.16.0'}
+ dependencies:
+ '@poppinss/macroable': 1.0.2
+ '@types/validator': 13.11.9
+ '@vinejs/compiler': 2.5.0
+ camelcase: 8.0.0
+ dayjs: 1.11.10
+ dlv: 1.1.3
+ normalize-url: 8.0.1
+ validator: 13.11.0
+ dev: true
+
/@vitest/expect@1.3.1:
resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
dependencies:
@@ -5255,6 +5301,11 @@ packages:
engines: {node: '>=14.16'}
dev: true
+ /camelcase@8.0.0:
+ resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
+ engines: {node: '>=16'}
+ dev: true
+
/can-write-to-dir@1.1.1:
resolution: {integrity: sha512-eOgiEWqjppB+3DN/5E82EQ8dTINus8d9GXMCbEsUnp2hcUIcXmBvzWmD3tXMk3CuBK0v+ddK9qw0EAF+JVRMjQ==}
engines: {node: '>=10.13'}
@@ -5749,6 +5800,10 @@ packages:
engines: {node: '>= 14'}
dev: true
+ /dayjs@1.11.10:
+ resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
+ dev: true
+
/debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
@@ -5908,6 +5963,10 @@ packages:
path-type: 4.0.0
dev: true
+ /dlv@1.1.3:
+ resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+ dev: true
+
/doctrine@2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
@@ -9047,6 +9106,11 @@ packages:
engines: {node: '>=14.16'}
dev: true
+ /normalize-url@8.0.1:
+ resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
+ engines: {node: '>=14.16'}
+ dev: true
+
/npm-bundled@2.0.1:
resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@@ -12382,6 +12446,7 @@ packages:
'@typeschema/typebox': link:packages/typebox
'@typeschema/valibot': link:packages/valibot
'@typeschema/valita': link:packages/valita
+ '@typeschema/vine': link:packages/vine
'@typeschema/yup': link:packages/yup
'@typeschema/zod': link:packages/zod
dev: false
@@ -12407,6 +12472,7 @@ packages:
'@typeschema/typebox': workspace:*
'@typeschema/valibot': workspace:*
'@typeschema/valita': workspace:*
+ '@typeschema/vine': workspace:*
'@typeschema/yup': workspace:*
'@typeschema/zod': workspace:*
peerDependenciesMeta:
@@ -12442,6 +12508,8 @@ packages:
optional: true
'@typeschema/valita':
optional: true
+ '@typeschema/vine':
+ optional: true
'@typeschema/yup':
optional: true
'@typeschema/zod':
diff --git a/tsconfig.test.json b/tsconfig.test.json
index 092fbde..1c82ee8 100644
--- a/tsconfig.test.json
+++ b/tsconfig.test.json
@@ -1,7 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
- "moduleResolution": "Node",
+ "moduleResolution": "Bundler",
"rootDir": "..",
"plugins": [{"transform": "typia/lib/transform"}],
"experimentalDecorators": true,
diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts
index abdada0..163e5fc 100644
--- a/turbo/generators/config.ts
+++ b/turbo/generators/config.ts
@@ -304,6 +304,12 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void {
name: 'fastest-validator',
url: 'https://github.com/icebob/fastest-validator',
},
+ {
+ adapter: adapters.find(adapter => adapter.name === 'vine'),
+ github: 'vinejs/vine',
+ name: 'vine',
+ url: 'https://vinejs.dev',
+ },
{
adapter: adapters.find(adapter => adapter.name === 'suretype'),
github: 'grantila/suretype',