Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */

import { z } from 'zod';
import { z } from 'zod/v4';

import { $BaseEnv, AppFactory, AuthModule, CryptoService } from '../src/index.js';
import { AppController } from './app.controller.js';
Expand Down
2 changes: 1 addition & 1 deletion example/cats/schemas/cat.schema.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';

export type Cat = z.infer<typeof $Cat>;
export const $Cat = z.object({
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"react-dom": "^19.1.0",
"reflect-metadata": "~0.1.13",
"rxjs": "^7.8.2",
"zod": "^3.22.6"
"zod": "^3.25.x"
},
"peerDependenciesMeta": {
"react": {
Expand All @@ -86,7 +86,7 @@
"dependencies": {
"@casl/ability": "^6.7.3",
"@casl/prisma": "^1.5.1",
"@douglasneuroinformatics/libjs": "^2.6.0",
"@douglasneuroinformatics/libjs": "^3.0.1",
"@nestjs/jwt": "^11.0.0",
"@nestjs/passport": "^11.0.5",
"@nestjs/swagger": "^11.0.6",
Expand Down
32 changes: 16 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/app/app.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { MiddlewareConsumer, Provider, Type } from '@nestjs/common';
import { APP_FILTER, APP_GUARD, APP_PIPE } from '@nestjs/core';
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import type { Simplify } from 'type-fest';
import type { z } from 'zod';
import type { z } from 'zod/v4';

import { GlobalExceptionFilter } from '../filters/global-exception.filter.js';
import { JSX_OPTIONS_TOKEN } from '../interceptors/render.interceptor.js';
Expand Down Expand Up @@ -30,7 +30,7 @@ export type CreateAppOptions<
configureMiddleware?: (consumer: MiddlewareConsumer) => void;
controllers?: Type<any>[];
envSchema: TEnvSchema;
imports?: (ConditionalImport<z.TypeOf<TEnvSchema>> | ImportedModule)[];
imports?: (ConditionalImport<z.output<TEnvSchema>> | ImportedModule)[];
jsx?: JSXOptions;
prisma: PrismaModuleOptions<TPrismaGlobalOmitConfig>;
providers?: Provider[];
Expand All @@ -44,7 +44,7 @@ export class AppFactory {
version,
...options
}: CreateAppOptions<TEnvSchema, TPrismaGlobalOmitConfig>): AppContainer<
z.TypeOf<TEnvSchema>,
z.output<TEnvSchema>,
TPrismaGlobalOmitConfig
> {
const envConfig = parseEnv(envSchema);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { z } from 'zod';
import { z } from 'zod/v4';

import { VALIDATION_SCHEMA_METADATA_KEY } from '../../utils/validation.utils.js';
import { ValidationSchema } from '../validation-schema.decorator.js';
Expand All @@ -18,7 +18,7 @@ describe('ValidationSchema', () => {
class Test {
foo: null;
}
const $Schema = Reflect.getMetadata(VALIDATION_SCHEMA_METADATA_KEY, Test) as z.AnyZodObject;
const $Schema = Reflect.getMetadata(VALIDATION_SCHEMA_METADATA_KEY, Test) as z.ZodObject;
expect($Schema).toBeInstanceOf(z.ZodType);
expect($Schema.shape).toMatchObject({
foo: expect.any(z.ZodNull)
Expand Down
10 changes: 6 additions & 4 deletions src/decorators/validation-schema.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { Class } from 'type-fest';
import { z } from 'zod';
import type { Class, OmitIndexSignature } from 'type-fest';
import { z } from 'zod/v4';

import { applyValidationSchema } from '../utils/validation.utils.js';

export function ValidationSchema<T extends z.ZodType<{ [key: string]: any }>>(
schema: T
): (target: Class<z.TypeOf<T>>) => void;
): (target: Class<OmitIndexSignature<T['_output']>>) => void;

export function ValidationSchema<T extends z.ZodRawShape>(shape: T): (target: Class<z.TypeOf<z.ZodObject<T>>>) => void;
export function ValidationSchema<T extends z.ZodRawShape>(
shape: T
): (target: Class<OmitIndexSignature<z.ZodObject<T>['_output']>>) => void;

/**
* Decorator to define the Zod validation schema for DTO classes
Expand Down
8 changes: 4 additions & 4 deletions src/meta/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as path from 'node:path';
import { RuntimeException } from '@douglasneuroinformatics/libjs';
import { fromAsyncThrowable, ok } from 'neverthrow';
import type { ResultAsync } from 'neverthrow';
import { z } from 'zod';
import { z } from 'zod/v4';

import { AbstractAppContainer } from '../app/app.base.js';
import { importDefault } from './import.js';
Expand All @@ -16,13 +16,13 @@ const $EntryFunction = z.custom<(...args: any[]) => any>((arg) => typeof arg ===

const $UserConfigOptions: z.ZodType<UserConfigOptions> = z.object({
build: z.object({
esbuildOptions: z.record(z.any()).optional(),
esbuildOptions: z.record(z.string(), z.any()).optional(),
mode: z.enum(['module', 'server']).optional(),
onComplete: z.function().returns(z.any()).optional(),
onComplete: z.custom<(...args: any[]) => any>((data) => typeof data === 'function', 'must be function').optional(),
outfile: z.string().min(1)
}),
entry: $EntryFunction,
globals: z.record(z.any()).optional()
globals: z.record(z.string(), z.any()).optional()
});

type UserConfigWithBaseDir = UserConfigOptions & {
Expand Down
11 changes: 7 additions & 4 deletions src/mixins/__tests__/data-transfer-object.mixin.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { describe, expect, expectTypeOf, it } from 'vitest';
import { z } from 'zod';
import { z } from 'zod/v4';

import { getValidationSchema } from '../../utils/validation.utils.js';
import { DataTransferObject } from '../data-transfer-object.mixin.js';

describe('DataTransferObject', () => {
it('should create a class with the expected metadata', () => {
const Cat = DataTransferObject({
const $Cat = z.object({
age: z.union([z.string(), z.number()]).transform(Number),
breed: z.string()
});
expect(getValidationSchema(Cat)).toBeInstanceOf(z.ZodType);
expectTypeOf(Cat.prototype).toMatchTypeOf<{
const Cat1 = DataTransferObject($Cat);
expect(getValidationSchema(Cat1)).toBeInstanceOf(z.ZodType);
const Cat2 = DataTransferObject($Cat.shape);
expect(getValidationSchema(Cat2)).toBeInstanceOf(z.ZodType);
expectTypeOf(Cat1.prototype).toMatchTypeOf<{
age: number;
breed: string;
}>();
Expand Down
8 changes: 4 additions & 4 deletions src/mixins/data-transfer-object.mixin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Class } from 'type-fest';
import { z } from 'zod';
import { z } from 'zod/v4';

import { applyValidationSchema } from '../utils/validation.utils.js';

Expand All @@ -8,21 +8,21 @@ import { applyValidationSchema } from '../utils/validation.utils.js';
* @param schema - Zod object schema for the DTO.
* @returns A DTO class with validation.
*/
export function DataTransferObject<T extends z.ZodType<{ [key: string]: any }>>(schema: T): Class<z.TypeOf<T>>;
export function DataTransferObject<T extends z.ZodType<{ [key: string]: any }>>(schema: T): Class<z.output<T>>;
/**
* Creates a Data Transfer Object (DTO) class with a Zod schema for validation.
* @param shape - Zod raw shape for the DTO.
* @returns A DTO class with validation.
*/
export function DataTransferObject<T extends z.ZodRawShape>(shape: T): Class<z.TypeOf<z.ZodObject<T>>>;
export function DataTransferObject<T extends z.ZodRawShape>(shape: T): Class<z.output<z.ZodObject<T>>>;
/**
* Creates a Data Transfer Object (DTO) class with a Zod schema for validation.
* @param shapeOrSchema - Zod raw shape or schema for the DTO.
* @returns A DTO class with validation.
*/
export function DataTransferObject(shapeOrSchema: z.ZodRawShape | z.ZodType<{ [key: string]: any }>): unknown {
const schema = shapeOrSchema instanceof z.ZodType ? shapeOrSchema : z.object(shapeOrSchema);
const Target = class {} as Class<z.TypeOf<typeof schema>>;
const Target = class {} as Class<z.output<typeof schema>>;
applyValidationSchema(Target, schema);
return Target;
}
2 changes: 1 addition & 1 deletion src/modules/auth/__tests__/auth.module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Test } from '@nestjs/testing';
import request from 'supertest';
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
import type { Mock } from 'vitest';
import { z } from 'zod';
import { z } from 'zod/v4';

import { RouteAccess } from '../../../decorators/route-access.decorator.js';
import { ConfigModule } from '../../config/config.module.js';
Expand Down
12 changes: 6 additions & 6 deletions src/modules/auth/auth.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { Prisma } from '@prisma/client';
import type { DefaultSelection } from '@prisma/client/runtime/library';
import type { IfNever } from 'type-fest';
import type { z } from 'zod';
import type { z } from 'zod/v4';

import { defineToken } from '../../utils/token.utils.js';

Expand All @@ -20,7 +20,7 @@
};

type RuntimeAppSubjects = Subjects<{
[K in keyof Prisma.TypeMap['model']]: DefaultSelection<Prisma.TypeMap['model'][K]['payload']>;

Check failure on line 23 in src/modules/auth/auth.config.ts

View workflow job for this annotation

GitHub Actions / ci

Namespace '"/home/runner/work/libnest/libnest/node_modules/.pnpm/@prisma+client@6.6.0_prisma@6.6.0_typescript@5.6.3__typescript@5.6.3/node_modules/.prisma/client/default".Prisma' has no exported member 'TypeMap'.

Check failure on line 23 in src/modules/auth/auth.config.ts

View workflow job for this annotation

GitHub Actions / ci

Namespace '"/home/runner/work/libnest/libnest/node_modules/.pnpm/@prisma+client@6.6.0_prisma@6.6.0_typescript@5.6.3__typescript@5.6.3/node_modules/.prisma/client/default".Prisma' has no exported member 'TypeMap'.
}>;

// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
Expand Down Expand Up @@ -73,20 +73,20 @@

type AuthModuleOptions<
TLoginCredentialsSchema extends BaseLoginCredentialsSchema = BaseLoginCredentialsSchema,
TPayloadSchema extends z.ZodType<{ [key: string]: unknown }> = z.ZodType<{ [key: string]: unknown }>,
TMetadataSchema extends z.ZodTypeAny = z.ZodNever
TPayloadSchema extends z.ZodType<{ [key: string]: any }> = z.ZodType<{ [key: string]: any }>,
TMetadataSchema extends z.ZodType = z.ZodNever
> = {
defineAbility: (
ability: AbilityBuilder<AppAbility>,
tokenPayload: z.TypeOf<TPayloadSchema>,
metadata: z.TypeOf<TMetadataSchema>
tokenPayload: z.output<TPayloadSchema>,
metadata: z.output<TMetadataSchema>
) => any;
schemas: {
loginCredentials: TLoginCredentialsSchema;
metadata?: TMetadataSchema;
tokenPayload: TPayloadSchema;
};
userQuery: UserQuery<z.TypeOf<TLoginCredentialsSchema>, z.TypeOf<TPayloadSchema>, z.TypeOf<TMetadataSchema>>;
userQuery: UserQuery<z.output<TLoginCredentialsSchema>, z.output<TPayloadSchema>, z.output<TMetadataSchema>>;
};

export const { ConfigurableModuleClass: ConfigurableAuthModule, MODULE_OPTIONS_TOKEN: AUTH_MODULE_OPTIONS_TOKEN } =
Expand Down
10 changes: 5 additions & 5 deletions src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Inject, Module } from '@nestjs/common';
import type { ConfigurableModuleAsyncOptions, DynamicModule, OnModuleInit } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { JwtModule } from '@nestjs/jwt';
import { z } from 'zod';
import { z } from 'zod/v4';

import { applyValidationSchema } from '../../utils/validation.utils.js';
import { ConfigService } from '../config/config.service.js';
Expand Down Expand Up @@ -65,15 +65,15 @@ export class AuthModule extends ConfigurableAuthModule implements OnModuleInit {

static forRoot<
TLoginCredentialsSchema extends BaseLoginCredentialsSchema,
TPayloadSchema extends z.ZodType<{ [key: string]: unknown }>,
TMetadataSchema extends z.ZodTypeAny = z.ZodNever
TPayloadSchema extends z.ZodType<{ [key: string]: any }>,
TMetadataSchema extends z.ZodType = z.ZodNever
>(options: AuthModuleOptions<TLoginCredentialsSchema, TPayloadSchema, TMetadataSchema>): DynamicModule {
return super.forRoot(options);
}
static forRootAsync<
TLoginCredentialsSchema extends BaseLoginCredentialsSchema,
TPayloadSchema extends z.ZodType<{ [key: string]: unknown }>,
TMetadataSchema extends z.ZodTypeAny = z.ZodNever
TPayloadSchema extends z.ZodType<{ [key: string]: any }>,
TMetadataSchema extends z.ZodType = z.ZodNever
>(
options: ConfigurableModuleAsyncOptions<
AuthModuleOptions<TLoginCredentialsSchema, TPayloadSchema, TMetadataSchema>,
Expand Down
2 changes: 1 addition & 1 deletion src/pipes/__tests__/parse-schema.pipe.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BadRequestException } from '@nestjs/common';
import { describe, expect, it } from 'vitest';
import { z } from 'zod';
import { z } from 'zod/v4';

import { ParseSchemaPipe } from '../parse-schema.pipe.js';

Expand Down
4 changes: 2 additions & 2 deletions src/pipes/__tests__/validation.pipe.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BadRequestException } from '@nestjs/common';
import { describe, expect, it } from 'vitest';
import { z } from 'zod';
import { z } from 'zod/v4';

import { ValidationSchema } from '../../decorators/validation-schema.decorator.js';
import { DataTransferObject } from '../../mixins/data-transfer-object.mixin.js';
Expand Down Expand Up @@ -39,7 +39,7 @@ describe('ValidationPipe', () => {
class Test {
isTest: boolean;
}
applyValidationSchema(Test, {} as z.ZodTypeAny);
applyValidationSchema(Test, {} as any);
await expect(validationPipe.transform({}, { metatype: Test, type: 'body' })).rejects.toThrow(
"Schema for 'Test' must be instance of ZodType"
);
Expand Down
4 changes: 2 additions & 2 deletions src/pipes/parse-schema.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import type { PipeTransform } from '@nestjs/common';
import type { z } from 'zod';
import type { z } from 'zod/v4';

@Injectable()
export class ParseSchemaPipe<T> implements PipeTransform<T> {
isOptional: boolean;
schema: Zod.ZodType<T>;
schema: z.ZodType<T>;

constructor(options: { isOptional?: boolean; schema: z.ZodType<T> }) {
this.isOptional = options.isOptional ?? false;
Expand Down
4 changes: 2 additions & 2 deletions src/schemas/env.schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { $BooleanLike, $NumberLike, $UrlLike } from '@douglasneuroinformatics/libjs';
import { z } from 'zod';
import { z } from 'zod/v4';

import type { UserConfig } from '../user-config.js';

Expand Down Expand Up @@ -37,7 +37,7 @@ export const $BaseEnv = z.object({
*
* @see {@link $BaseEnv}
*/
export type BaseEnv = z.infer<typeof $BaseEnv>;
export type BaseEnv = z.output<typeof $BaseEnv>;

export type NodeEnv = BaseEnv['NODE_ENV'];

Expand Down
Loading
Loading