From 8fb29093fce3e4afe285a10de877a0a352bb1296 Mon Sep 17 00:00:00 2001 From: Vinicius Dacal Date: Sat, 7 Feb 2026 00:44:28 -0300 Subject: [PATCH] chore: enable Biome formatter and apply formatting fixes Enable the Biome formatter with project conventions (2-space indent, single quotes, semicolons, trailing commas, 100-char line width). Applied auto-formatting across all packages. Co-Authored-By: Claude Opus 4.6 --- biome.json | 4 +- .../src/app/__tests__/app-builder.test.ts | 12 +++- packages/core/src/app/app-runner.ts | 5 +- packages/core/src/di/boot-executor.ts | 9 +-- .../src/env/__tests__/env-validator.test.ts | 10 +-- .../src/module/__tests__/router-def.test.ts | 7 +- packages/core/src/module/module-def.ts | 4 +- packages/core/src/module/service.ts | 16 ++--- packages/core/src/router/trie.ts | 11 ++-- packages/core/src/server/cors.ts | 6 +- packages/core/src/server/response-utils.ts | 6 +- packages/core/src/types/boot-sequence.ts | 6 +- packages/core/src/types/http.ts | 22 +++++-- packages/core/src/types/module.ts | 10 +-- .../integration/complex-compositions.test.ts | 8 ++- .../integration/named-schemas.test.ts | 20 +++--- .../integration/recursive-schemas.test.ts | 20 +++--- packages/schema/src/core/schema.ts | 18 +++--- packages/schema/src/core/types.ts | 4 +- .../src/effects/__tests__/readonly.test.ts | 8 ++- packages/schema/src/index.ts | 45 +++++++++---- .../__tests__/json-schema.test.ts | 8 ++- .../__tests__/openapi-output.test.ts | 10 ++- .../src/refinements/__tests__/refine.test.ts | 5 +- .../src/schemas/__tests__/custom.test.ts | 5 +- .../__tests__/discriminated-union.test.ts | 12 +++- .../schema/src/schemas/__tests__/map.test.ts | 5 +- .../src/schemas/__tests__/object.test.ts | 40 +++++++++--- .../src/schemas/__tests__/special.test.ts | 9 ++- .../src/schemas/__tests__/string.test.ts | 5 +- packages/schema/src/schemas/array.ts | 25 ++++++-- packages/schema/src/schemas/bigint.ts | 5 +- packages/schema/src/schemas/boolean.ts | 5 +- packages/schema/src/schemas/date.ts | 15 ++++- .../schema/src/schemas/discriminated-union.ts | 4 +- packages/schema/src/schemas/enum.ts | 7 +- .../schemas/formats/__tests__/base64.test.ts | 5 +- .../schemas/formats/__tests__/ipv6.test.ts | 4 +- .../src/schemas/formats/__tests__/jwt.test.ts | 3 +- .../schemas/formats/__tests__/uuid.test.ts | 8 ++- packages/schema/src/schemas/formats/email.ts | 3 +- .../schema/src/schemas/formats/hostname.ts | 3 +- packages/schema/src/schemas/formats/ipv4.ts | 2 +- packages/schema/src/schemas/formats/ipv6.ts | 3 +- packages/schema/src/schemas/formats/iso.ts | 2 +- packages/schema/src/schemas/instanceof.ts | 9 ++- packages/schema/src/schemas/intersection.ts | 15 +++-- packages/schema/src/schemas/map.ts | 5 +- packages/schema/src/schemas/number.ts | 30 +++++++-- packages/schema/src/schemas/object.ts | 23 +++++-- packages/schema/src/schemas/set.ts | 20 ++++-- packages/schema/src/schemas/special.ts | 15 ++++- packages/schema/src/schemas/string.ts | 40 +++++++++--- packages/schema/src/schemas/symbol.ts | 5 +- packages/schema/src/schemas/tuple.ts | 21 ++++-- packages/schema/src/schemas/union.ts | 2 +- .../transforms/__tests__/preprocess.test.ts | 4 +- .../utils/__tests__/type-inference.test.ts | 8 ++- .../testing/src/__tests__/test-app.test.ts | 64 ++++++++++++++----- .../src/__tests__/test-service.test.ts | 9 ++- packages/testing/src/test-app.ts | 26 ++++++-- packages/testing/src/test-service.ts | 5 +- 62 files changed, 532 insertions(+), 213 deletions(-) diff --git a/biome.json b/biome.json index f58248067..b0a0044b6 100644 --- a/biome.json +++ b/biome.json @@ -6,10 +6,10 @@ "useIgnoreFile": true }, "formatter": { - "enabled": false, + "enabled": true, "indentStyle": "space", "indentWidth": 2, - "lineWidth": 120 + "lineWidth": 100 }, "linter": { "enabled": false, diff --git a/packages/core/src/app/__tests__/app-builder.test.ts b/packages/core/src/app/__tests__/app-builder.test.ts index 9437f032a..8f3266589 100644 --- a/packages/core/src/app/__tests__/app-builder.test.ts +++ b/packages/core/src/app/__tests__/app-builder.test.ts @@ -15,7 +15,13 @@ function createTestModule(name: string, prefix: string, routes: TestRoute[]) { const moduleDef = createModuleDef({ name }); const router = moduleDef.router({ prefix }); for (const route of routes) { - const method = route.method.toLowerCase() as 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head'; + const method = route.method.toLowerCase() as + | 'get' + | 'post' + | 'put' + | 'patch' + | 'delete' + | 'head'; router[method](route.path, { handler: route.handler }); } return createModule(moduleDef, { services: [], routers: [router], exports: [] }); @@ -199,7 +205,9 @@ describe('createApp', () => { ]); const app = createApp({}).register(mod); - const res = await app.handler(new Request('http://localhost/users/42/activate', { method: 'POST' })); + const res = await app.handler( + new Request('http://localhost/users/42/activate', { method: 'POST' }), + ); expect(res.status).toBe(204); }); diff --git a/packages/core/src/app/app-runner.ts b/packages/core/src/app/app-runner.ts index ceb14db6b..c0631b49e 100644 --- a/packages/core/src/app/app-runner.ts +++ b/packages/core/src/app/app-runner.ts @@ -150,9 +150,8 @@ export function buildHandler( }); const result = await entry.handler(ctx); - const response = result === undefined - ? new Response(null, { status: 204 }) - : createJsonResponse(result); + const response = + result === undefined ? new Response(null, { status: 204 }) : createJsonResponse(result); if (config.cors) { return applyCorsHeaders(config.cors, request, response); diff --git a/packages/core/src/di/boot-executor.ts b/packages/core/src/di/boot-executor.ts index 9e72a1bf8..1dce3bc7c 100644 --- a/packages/core/src/di/boot-executor.ts +++ b/packages/core/src/di/boot-executor.ts @@ -1,7 +1,4 @@ -import type { - BootSequence, - ServiceBootInstruction, -} from '../types/boot-sequence'; +import type { BootSequence, ServiceBootInstruction } from '../types/boot-sequence'; import { makeImmutable } from '../immutability'; interface ServiceEntry { @@ -37,9 +34,7 @@ export class BootExecutor { this.services.set(instr.id, { methods: instr.factory.methods(deps, state), - onDestroy: instr.factory.onDestroy - ? () => instr.factory.onDestroy!(deps, state) - : undefined, + onDestroy: instr.factory.onDestroy ? () => instr.factory.onDestroy!(deps, state) : undefined, }); } diff --git a/packages/core/src/env/__tests__/env-validator.test.ts b/packages/core/src/env/__tests__/env-validator.test.ts index c8f742220..bdcdcc2de 100644 --- a/packages/core/src/env/__tests__/env-validator.test.ts +++ b/packages/core/src/env/__tests__/env-validator.test.ts @@ -73,10 +73,12 @@ describe('createEnv', () => { const env = createEnv({ schema: s.object({ - DB: s.object({ - HOST: s.string(), - PORT: s.string(), - }).default({ HOST: 'localhost', PORT: '5432' }), + DB: s + .object({ + HOST: s.string(), + PORT: s.string(), + }) + .default({ HOST: 'localhost', PORT: '5432' }), }), }); diff --git a/packages/core/src/module/__tests__/router-def.test.ts b/packages/core/src/module/__tests__/router-def.test.ts index 3fcb88dc0..cef3c3525 100644 --- a/packages/core/src/module/__tests__/router-def.test.ts +++ b/packages/core/src/module/__tests__/router-def.test.ts @@ -64,7 +64,12 @@ describe('moduleDef.router', () => { expect(router.routes).toHaveLength(6); expect(router.routes.map((r) => r.method)).toEqual([ - 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', + 'GET', + 'POST', + 'PUT', + 'PATCH', + 'DELETE', + 'HEAD', ]); }); diff --git a/packages/core/src/module/module-def.ts b/packages/core/src/module/module-def.ts index e3fdbf41f..9bd44ef73 100644 --- a/packages/core/src/module/module-def.ts +++ b/packages/core/src/module/module-def.ts @@ -7,7 +7,9 @@ export interface NamedModuleDef< TImports extends Record = Record, TOptions extends Record = Record, > extends ModuleDef { - service: (config: ServiceDef) => NamedServiceDef; + service: ( + config: ServiceDef, + ) => NamedServiceDef; router: (config: RouterDef) => NamedRouterDef; } diff --git a/packages/core/src/module/service.ts b/packages/core/src/module/service.ts index e26f73777..7308d8cb4 100644 --- a/packages/core/src/module/service.ts +++ b/packages/core/src/module/service.ts @@ -1,19 +1,15 @@ import type { ServiceDef } from '../types/module'; import { deepFreeze } from '../immutability'; -export interface NamedServiceDef< - TDeps = any, - TState = any, - TMethods = any, -> extends ServiceDef { +export interface NamedServiceDef + extends ServiceDef { moduleName: string; } -export function createServiceDef< - TDeps = any, - TState = any, - TMethods = any, ->(moduleName: string, config: ServiceDef): NamedServiceDef { +export function createServiceDef( + moduleName: string, + config: ServiceDef, +): NamedServiceDef { return deepFreeze({ ...config, moduleName, diff --git a/packages/core/src/router/trie.ts b/packages/core/src/router/trie.ts index 6f602e583..0d94216eb 100644 --- a/packages/core/src/router/trie.ts +++ b/packages/core/src/router/trie.ts @@ -118,13 +118,10 @@ export class Trie { } if (node.paramChild) { - const result = this.matchNode( - node.paramChild.node, - segments, - index + 1, - method, - { ...params, [node.paramChild.name]: segment }, - ); + const result = this.matchNode(node.paramChild.node, segments, index + 1, method, { + ...params, + [node.paramChild.name]: segment, + }); if (result) return result; } diff --git a/packages/core/src/server/cors.ts b/packages/core/src/server/cors.ts index 3ee44e9c1..9ff1738f1 100644 --- a/packages/core/src/server/cors.ts +++ b/packages/core/src/server/cors.ts @@ -44,7 +44,11 @@ export function handleCors(config: CorsConfig, request: Request): Response | nul return null; } -export function applyCorsHeaders(config: CorsConfig, request: Request, response: Response): Response { +export function applyCorsHeaders( + config: CorsConfig, + request: Request, + response: Response, +): Response { const requestOrigin = request.headers.get('origin'); const origin = resolveOrigin(config, requestOrigin); diff --git a/packages/core/src/server/response-utils.ts b/packages/core/src/server/response-utils.ts index a75b96a7f..3fbb30067 100644 --- a/packages/core/src/server/response-utils.ts +++ b/packages/core/src/server/response-utils.ts @@ -1,6 +1,10 @@ import { VertzException } from '../exceptions'; -export function createJsonResponse(data: unknown, status = 200, headers?: Record): Response { +export function createJsonResponse( + data: unknown, + status = 200, + headers?: Record, +): Response { return new Response(JSON.stringify(data), { status, headers: { diff --git a/packages/core/src/types/boot-sequence.ts b/packages/core/src/types/boot-sequence.ts index de936359a..239d27273 100644 --- a/packages/core/src/types/boot-sequence.ts +++ b/packages/core/src/types/boot-sequence.ts @@ -1,6 +1,10 @@ import type { ServiceDef } from './module'; -export type ServiceFactory = ServiceDef; +export type ServiceFactory = ServiceDef< + TDeps, + TState, + TMethods +>; export interface ServiceBootInstruction { type: 'service'; diff --git a/packages/core/src/types/http.ts b/packages/core/src/types/http.ts index 16bf35d91..87a542d41 100644 --- a/packages/core/src/types/http.ts +++ b/packages/core/src/types/http.ts @@ -1,7 +1,21 @@ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'; export type HttpStatusCode = - | 200 | 201 | 204 - | 301 | 302 | 304 - | 400 | 401 | 403 | 404 | 405 | 409 | 422 | 429 - | 500 | 502 | 503 | 504; + | 200 + | 201 + | 204 + | 301 + | 302 + | 304 + | 400 + | 401 + | 403 + | 404 + | 405 + | 409 + | 422 + | 429 + | 500 + | 502 + | 503 + | 504; diff --git a/packages/core/src/types/module.ts b/packages/core/src/types/module.ts index cb61e2ad8..3bdf56c7e 100644 --- a/packages/core/src/types/module.ts +++ b/packages/core/src/types/module.ts @@ -9,11 +9,7 @@ export interface ModuleDef< options?: Schema; } -export interface ServiceDef< - TDeps = any, - TState = any, - TMethods = any, -> { +export interface ServiceDef { inject?: Record; onInit?: (deps: TDeps) => Promise | TState; methods: (deps: TDeps, state: TState) => TMethods; @@ -25,9 +21,7 @@ export interface RouterDef { inject?: Record; } -export interface Module< - TDef extends ModuleDef = ModuleDef, -> { +export interface Module { definition: TDef; services: ServiceDef[]; routers: RouterDef[]; diff --git a/packages/schema/src/__tests__/integration/complex-compositions.test.ts b/packages/schema/src/__tests__/integration/complex-compositions.test.ts index 2431bd6b1..ee3dabf04 100644 --- a/packages/schema/src/__tests__/integration/complex-compositions.test.ts +++ b/packages/schema/src/__tests__/integration/complex-compositions.test.ts @@ -14,7 +14,10 @@ describe('Integration: Complex Compositions', () => { expect(nameOnly.parse({ name: 'John' })).toEqual({ name: 'John' }); const extended = nameOnly.extend({ role: s.string() }); - expect(extended.parse({ name: 'John', role: 'admin' })).toEqual({ name: 'John', role: 'admin' }); + expect(extended.parse({ name: 'John', role: 'admin' })).toEqual({ + name: 'John', + role: 'admin', + }); const partial = extended.partial(); expect(partial.parse({})).toEqual({}); @@ -44,7 +47,8 @@ describe('Integration: Complex Compositions', () => { }); it('transform pipeline: string → parse → number → validate', () => { - const stringToNumber = s.string() + const stringToNumber = s + .string() .transform((v) => parseInt(v, 10)) .pipe(s.number().int().gte(0)); diff --git a/packages/schema/src/__tests__/integration/named-schemas.test.ts b/packages/schema/src/__tests__/integration/named-schemas.test.ts index f12e516f0..c5719b39a 100644 --- a/packages/schema/src/__tests__/integration/named-schemas.test.ts +++ b/packages/schema/src/__tests__/integration/named-schemas.test.ts @@ -11,15 +11,19 @@ describe('Integration: Named Schemas', () => { }); it('named object with named nested schemas', () => { - const addressSchema = s.object({ - street: s.string(), - city: s.string(), - }).id('Address'); + const addressSchema = s + .object({ + street: s.string(), + city: s.string(), + }) + .id('Address'); - const userSchema = s.object({ - name: s.string(), - address: addressSchema, - }).id('UserWithAddress'); + const userSchema = s + .object({ + name: s.string(), + address: addressSchema, + }) + .id('UserWithAddress'); const jsonSchema = userSchema.toJSONSchema(); expect(jsonSchema.$defs!['UserWithAddress']).toBeDefined(); diff --git a/packages/schema/src/__tests__/integration/recursive-schemas.test.ts b/packages/schema/src/__tests__/integration/recursive-schemas.test.ts index 4e995393e..964d52cbc 100644 --- a/packages/schema/src/__tests__/integration/recursive-schemas.test.ts +++ b/packages/schema/src/__tests__/integration/recursive-schemas.test.ts @@ -4,10 +4,12 @@ import type { Infer } from '../..'; describe('Integration: Recursive Schemas', () => { it('tree node with s.lazy() and .id()', () => { - const treeSchema = s.object({ - value: s.string(), - children: s.lazy(() => treeSchema).nullable(), - }).id('TreeNodeIntegration'); + const treeSchema = s + .object({ + value: s.string(), + children: s.lazy(() => treeSchema).nullable(), + }) + .id('TreeNodeIntegration'); type TreeNode = Infer; @@ -48,10 +50,12 @@ describe('Integration: Recursive Schemas', () => { }); it('JSON Schema output with $ref for recursive schema', () => { - const nodeSchema = s.object({ - name: s.string(), - children: s.array(s.lazy(() => nodeSchema)), - }).id('RecursiveNode'); + const nodeSchema = s + .object({ + name: s.string(), + children: s.array(s.lazy(() => nodeSchema)), + }) + .id('RecursiveNode'); const jsonSchema = nodeSchema.toJSONSchema(); expect(jsonSchema.$ref).toBe('#/$defs/RecursiveNode'); diff --git a/packages/schema/src/core/schema.ts b/packages/schema/src/core/schema.ts index 99641f29e..cba7cf270 100644 --- a/packages/schema/src/core/schema.ts +++ b/packages/schema/src/core/schema.ts @@ -256,9 +256,7 @@ export class DefaultSchema extends Schema { } private _resolveDefault(): I { - return typeof this._default === 'function' - ? (this._default as () => I)() - : this._default; + return typeof this._default === 'function' ? (this._default as () => I)() : this._default; } _clone(): DefaultSchema { @@ -311,10 +309,12 @@ export class RefinedSchema extends Schema { } _clone(): RefinedSchema { - return this._cloneBase(new RefinedSchema(this._inner, this._predicate, { - message: this._message, - path: this._path, - })); + return this._cloneBase( + new RefinedSchema(this._inner, this._predicate, { + message: this._message, + path: this._path, + }), + ); } } @@ -442,9 +442,7 @@ export class CatchSchema extends Schema { } private _resolveFallback(): O { - return typeof this._fallback === 'function' - ? (this._fallback as () => O)() - : this._fallback; + return typeof this._fallback === 'function' ? (this._fallback as () => O)() : this._fallback; } _clone(): CatchSchema { diff --git a/packages/schema/src/core/types.ts b/packages/schema/src/core/types.ts index 8a6e839df..2090c35a7 100644 --- a/packages/schema/src/core/types.ts +++ b/packages/schema/src/core/types.ts @@ -39,6 +39,4 @@ export interface SchemaMetadata { examples: unknown[]; } -export type SafeParseResult = - | { success: true; data: T } - | { success: false; error: ParseError }; +export type SafeParseResult = { success: true; data: T } | { success: false; error: ParseError }; diff --git a/packages/schema/src/effects/__tests__/readonly.test.ts b/packages/schema/src/effects/__tests__/readonly.test.ts index 37b28da7b..9755f86cb 100644 --- a/packages/schema/src/effects/__tests__/readonly.test.ts +++ b/packages/schema/src/effects/__tests__/readonly.test.ts @@ -20,7 +20,9 @@ describe('.readonly()', () => { name: new StringSchema(), }).readonly(); const result = schema.parse({ name: 'Alice' }); - expect(() => { (result as any).name = 'Bob'; }).toThrow(); + expect(() => { + (result as any).name = 'Bob'; + }).toThrow(); }); it('infers Readonly type', () => { @@ -36,7 +38,9 @@ describe('.readonly()', () => { const schema = new ArraySchema(new StringSchema()).readonly(); const result = schema.parse(['a', 'b']); expect(Object.isFrozen(result)).toBe(true); - expect(() => { (result as any).push('c'); }).toThrow(); + expect(() => { + (result as any).push('c'); + }).toThrow(); }); it('passes primitives through unchanged', () => { diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts index efbc5265e..56fbd20e1 100644 --- a/packages/schema/src/index.ts +++ b/packages/schema/src/index.ts @@ -34,7 +34,14 @@ export { BigIntSchema } from './schemas/bigint'; export { DateSchema } from './schemas/date'; export { NanSchema } from './schemas/nan'; export { SymbolSchema } from './schemas/symbol'; -export { AnySchema, UnknownSchema, NullSchema, UndefinedSchema, VoidSchema, NeverSchema } from './schemas/special'; +export { + AnySchema, + UnknownSchema, + NullSchema, + UndefinedSchema, + VoidSchema, + NeverSchema, +} from './schemas/special'; export { ObjectSchema } from './schemas/object'; export { ArraySchema } from './schemas/array'; export { TupleSchema } from './schemas/tuple'; @@ -92,7 +99,14 @@ import { BigIntSchema } from './schemas/bigint'; import { DateSchema } from './schemas/date'; import { NanSchema } from './schemas/nan'; import { SymbolSchema } from './schemas/symbol'; -import { AnySchema, UnknownSchema, NullSchema, UndefinedSchema, VoidSchema, NeverSchema } from './schemas/special'; +import { + AnySchema, + UnknownSchema, + NullSchema, + UndefinedSchema, + VoidSchema, + NeverSchema, +} from './schemas/special'; import { ObjectSchema } from './schemas/object'; import { ArraySchema } from './schemas/array'; import { TupleSchema } from './schemas/tuple'; @@ -155,20 +169,29 @@ export const s = { never: (): NeverSchema => new NeverSchema(), // Composites - object: >>(shape: T): ObjectSchema => new ObjectSchema(shape), + object: >>(shape: T): ObjectSchema => + new ObjectSchema(shape), array: (itemSchema: Schema): ArraySchema => new ArraySchema(itemSchema), tuple: []>(items: [...T]): TupleSchema => new TupleSchema(items), - enum: (values: T): EnumSchema => new EnumSchema(values), - literal: (value: T): LiteralSchema => new LiteralSchema(value), - union: []>(options: [...T]): UnionSchema => new UnionSchema(options), - discriminatedUnion: []>(discriminator: string, options: [...T]): DiscriminatedUnionSchema => - new DiscriminatedUnionSchema(discriminator, options), - intersection: (left: Schema, right: Schema): IntersectionSchema => new IntersectionSchema(left, right), + enum: (values: T): EnumSchema => + new EnumSchema(values), + literal: (value: T): LiteralSchema => + new LiteralSchema(value), + union: []>(options: [...T]): UnionSchema => + new UnionSchema(options), + discriminatedUnion: []>( + discriminator: string, + options: [...T], + ): DiscriminatedUnionSchema => new DiscriminatedUnionSchema(discriminator, options), + intersection: (left: Schema, right: Schema): IntersectionSchema => + new IntersectionSchema(left, right), record: (valueSchema: Schema): RecordSchema => new RecordSchema(valueSchema), - map: (keySchema: Schema, valueSchema: Schema): MapSchema => new MapSchema(keySchema, valueSchema), + map: (keySchema: Schema, valueSchema: Schema): MapSchema => + new MapSchema(keySchema, valueSchema), set: (valueSchema: Schema): SetSchema => new SetSchema(valueSchema), file: (): FileSchema => new FileSchema(), - custom: (check: (value: unknown) => boolean, message?: string): CustomSchema => new CustomSchema(check, message), + custom: (check: (value: unknown) => boolean, message?: string): CustomSchema => + new CustomSchema(check, message), instanceof: (cls: new (...args: any[]) => T): InstanceOfSchema => new InstanceOfSchema(cls), lazy: (getter: () => Schema): LazySchema => new LazySchema(getter), diff --git a/packages/schema/src/introspection/__tests__/json-schema.test.ts b/packages/schema/src/introspection/__tests__/json-schema.test.ts index 150e328ac..da07d6cc1 100644 --- a/packages/schema/src/introspection/__tests__/json-schema.test.ts +++ b/packages/schema/src/introspection/__tests__/json-schema.test.ts @@ -13,8 +13,12 @@ class TestSchema extends Schema { } return value; } - _schemaType(): SchemaType { return SchemaType.String; } - _toJSONSchema(): Record { return { type: 'string' }; } + _schemaType(): SchemaType { + return SchemaType.String; + } + _toJSONSchema(): Record { + return { type: 'string' }; + } _clone(): TestSchema { return this._cloneBase(new TestSchema()); } diff --git a/packages/schema/src/introspection/__tests__/openapi-output.test.ts b/packages/schema/src/introspection/__tests__/openapi-output.test.ts index cd39f301c..e8184075b 100644 --- a/packages/schema/src/introspection/__tests__/openapi-output.test.ts +++ b/packages/schema/src/introspection/__tests__/openapi-output.test.ts @@ -84,8 +84,14 @@ describe('OpenAPI v3.1 Output', () => { }); it('discriminated union produces oneOf with discriminator', () => { - const catSchema = new ObjectSchema({ type: new LiteralSchema('cat'), meow: new StringSchema() }); - const dogSchema = new ObjectSchema({ type: new LiteralSchema('dog'), bark: new StringSchema() }); + const catSchema = new ObjectSchema({ + type: new LiteralSchema('cat'), + meow: new StringSchema(), + }); + const dogSchema = new ObjectSchema({ + type: new LiteralSchema('dog'), + bark: new StringSchema(), + }); const schema = new DiscriminatedUnionSchema('type', [catSchema, dogSchema]); const jsonSchema = schema.toJSONSchema(); expect(jsonSchema.oneOf).toBeDefined(); diff --git a/packages/schema/src/refinements/__tests__/refine.test.ts b/packages/schema/src/refinements/__tests__/refine.test.ts index ae01ddd2a..d4fbcb70d 100644 --- a/packages/schema/src/refinements/__tests__/refine.test.ts +++ b/packages/schema/src/refinements/__tests__/refine.test.ts @@ -27,7 +27,10 @@ describe('.refine()', () => { }); it('uses custom path from params object', () => { - const schema = new StringSchema().refine((val) => val.length > 0, { message: 'Required', path: ['name'] }); + const schema = new StringSchema().refine((val) => val.length > 0, { + message: 'Required', + path: ['name'], + }); const result = schema.safeParse(''); expect(result.success).toBe(false); if (!result.success) { diff --git a/packages/schema/src/schemas/__tests__/custom.test.ts b/packages/schema/src/schemas/__tests__/custom.test.ts index fb18fb637..291b4d68c 100644 --- a/packages/schema/src/schemas/__tests__/custom.test.ts +++ b/packages/schema/src/schemas/__tests__/custom.test.ts @@ -8,7 +8,10 @@ describe('CustomSchema', () => { }); it('rejects when predicate returns false', () => { - const schema = new CustomSchema((v) => typeof v === 'number' && (v as number) > 0, 'Must be positive'); + const schema = new CustomSchema( + (v) => typeof v === 'number' && (v as number) > 0, + 'Must be positive', + ); const result = schema.safeParse(-1); expect(result.success).toBe(false); if (!result.success) { diff --git a/packages/schema/src/schemas/__tests__/discriminated-union.test.ts b/packages/schema/src/schemas/__tests__/discriminated-union.test.ts index 62704f7d9..8fb80325d 100644 --- a/packages/schema/src/schemas/__tests__/discriminated-union.test.ts +++ b/packages/schema/src/schemas/__tests__/discriminated-union.test.ts @@ -40,8 +40,16 @@ describe('DiscriminatedUnionSchema', () => { const schema = new DiscriminatedUnionSchema('type', [catSchema, dogSchema]); expect(schema.toJSONSchema()).toEqual({ oneOf: [ - { type: 'object', properties: { type: { const: 'cat' }, meow: { type: 'string' } }, required: ['type', 'meow'] }, - { type: 'object', properties: { type: { const: 'dog' }, bark: { type: 'number' } }, required: ['type', 'bark'] }, + { + type: 'object', + properties: { type: { const: 'cat' }, meow: { type: 'string' } }, + required: ['type', 'meow'], + }, + { + type: 'object', + properties: { type: { const: 'dog' }, bark: { type: 'number' } }, + required: ['type', 'bark'], + }, ], discriminator: { propertyName: 'type' }, }); diff --git a/packages/schema/src/schemas/__tests__/map.test.ts b/packages/schema/src/schemas/__tests__/map.test.ts index 3e8a796df..88ba2c2f2 100644 --- a/packages/schema/src/schemas/__tests__/map.test.ts +++ b/packages/schema/src/schemas/__tests__/map.test.ts @@ -6,7 +6,10 @@ import { NumberSchema } from '../number'; describe('MapSchema', () => { it('accepts Map instances with valid key/value types', () => { const schema = new MapSchema(new StringSchema(), new NumberSchema()); - const input = new Map([['a', 1], ['b', 2]]); + const input = new Map([ + ['a', 1], + ['b', 2], + ]); const result = schema.parse(input); expect(result).toBeInstanceOf(Map); expect(result.get('a')).toBe(1); diff --git a/packages/schema/src/schemas/__tests__/object.test.ts b/packages/schema/src/schemas/__tests__/object.test.ts index 1f8117aae..a79b1b772 100644 --- a/packages/schema/src/schemas/__tests__/object.test.ts +++ b/packages/schema/src/schemas/__tests__/object.test.ts @@ -32,7 +32,7 @@ describe('ObjectSchema', () => { const result = schema.safeParse({ name: 'Alice' }); expect(result.success).toBe(false); if (!result.success) { - const missingIssue = result.error.issues.find(i => i.code === ErrorCode.MissingProperty); + const missingIssue = result.error.issues.find((i) => i.code === ErrorCode.MissingProperty); expect(missingIssue).toBeDefined(); expect(missingIssue!.path).toEqual(['age']); } @@ -69,7 +69,7 @@ describe('ObjectSchema', () => { const result = schema.safeParse({ name: 'Alice', extra: 'bad' }); expect(result.success).toBe(false); if (!result.success) { - const issue = result.error.issues.find(i => i.code === ErrorCode.UnrecognizedKeys); + const issue = result.error.issues.find((i) => i.code === ErrorCode.UnrecognizedKeys); expect(issue).toBeDefined(); expect(issue!.message).toContain('extra'); } @@ -111,23 +111,41 @@ describe('ObjectSchema', () => { const a = new ObjectSchema({ name: new StringSchema(), age: new NumberSchema() }); const b = new ObjectSchema({ age: new StringSchema(), email: new StringSchema() }); const merged = a.merge(b); - expect(merged.parse({ name: 'Alice', age: '30', email: 'a@b.com' })).toEqual({ name: 'Alice', age: '30', email: 'a@b.com' }); + expect(merged.parse({ name: 'Alice', age: '30', email: 'a@b.com' })).toEqual({ + name: 'Alice', + age: '30', + email: 'a@b.com', + }); // age is now StringSchema from b, so number should fail const result = merged.safeParse({ name: 'Alice', age: 30, email: 'a@b.com' }); expect(result.success).toBe(false); }); it('.pick(keys) keeps only specified keys', () => { - const schema = new ObjectSchema({ name: new StringSchema(), age: new NumberSchema(), email: new StringSchema() }); + const schema = new ObjectSchema({ + name: new StringSchema(), + age: new NumberSchema(), + email: new StringSchema(), + }); const picked = schema.pick('name', 'email'); - expect(picked.parse({ name: 'Alice', email: 'a@b.com' })).toEqual({ name: 'Alice', email: 'a@b.com' }); + expect(picked.parse({ name: 'Alice', email: 'a@b.com' })).toEqual({ + name: 'Alice', + email: 'a@b.com', + }); expect(picked.keyof()).toEqual(['name', 'email']); }); it('.omit(keys) removes specified keys', () => { - const schema = new ObjectSchema({ name: new StringSchema(), age: new NumberSchema(), email: new StringSchema() }); + const schema = new ObjectSchema({ + name: new StringSchema(), + age: new NumberSchema(), + email: new StringSchema(), + }); const omitted = schema.omit('age'); - expect(omitted.parse({ name: 'Alice', email: 'a@b.com' })).toEqual({ name: 'Alice', email: 'a@b.com' }); + expect(omitted.parse({ name: 'Alice', email: 'a@b.com' })).toEqual({ + name: 'Alice', + email: 'a@b.com', + }); expect(omitted.keyof()).toEqual(['name', 'email']); }); @@ -148,7 +166,7 @@ describe('ObjectSchema', () => { expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues.length).toBe(2); - expect(result.error.issues.every(i => i.code === ErrorCode.MissingProperty)).toBe(true); + expect(result.error.issues.every((i) => i.code === ErrorCode.MissingProperty)).toBe(true); } expect(required.parse({ name: 'Alice', age: 30 })).toEqual({ name: 'Alice', age: 30 }); }); @@ -163,14 +181,16 @@ describe('ObjectSchema', () => { const result = required.safeParse({ name: 'Alice' }); expect(result.success).toBe(false); if (!result.success) { - const issue = result.error.issues.find(i => i.code === ErrorCode.MissingProperty); + const issue = result.error.issues.find((i) => i.code === ErrorCode.MissingProperty); expect(issue).toBeDefined(); expect(issue!.path).toEqual(['role']); } }); it('.catchall() overrides .strict() when chained', () => { - const schema = new ObjectSchema({ name: new StringSchema() }).strict().catchall(new NumberSchema()); + const schema = new ObjectSchema({ name: new StringSchema() }) + .strict() + .catchall(new NumberSchema()); // catchall should take precedence — validates unknown keys instead of rejecting expect(schema.parse({ name: 'Alice', score: 100 })).toEqual({ name: 'Alice', score: 100 }); }); diff --git a/packages/schema/src/schemas/__tests__/special.test.ts b/packages/schema/src/schemas/__tests__/special.test.ts index ba9eee6e6..0cdb8bfb8 100644 --- a/packages/schema/src/schemas/__tests__/special.test.ts +++ b/packages/schema/src/schemas/__tests__/special.test.ts @@ -1,5 +1,12 @@ import { describe, it, expect } from 'vitest'; -import { AnySchema, UnknownSchema, NullSchema, UndefinedSchema, VoidSchema, NeverSchema } from '../special'; +import { + AnySchema, + UnknownSchema, + NullSchema, + UndefinedSchema, + VoidSchema, + NeverSchema, +} from '../special'; import { ParseError } from '../../core/errors'; describe('Special schemas', () => { diff --git a/packages/schema/src/schemas/__tests__/string.test.ts b/packages/schema/src/schemas/__tests__/string.test.ts index 6870a4558..fda9e367e 100644 --- a/packages/schema/src/schemas/__tests__/string.test.ts +++ b/packages/schema/src/schemas/__tests__/string.test.ts @@ -111,7 +111,10 @@ describe('StringSchema', () => { }); it('.toJSONSchema() returns type with minLength, maxLength, pattern', () => { - const schema = new StringSchema().min(1).max(100).regex(/^[a-z]+$/); + const schema = new StringSchema() + .min(1) + .max(100) + .regex(/^[a-z]+$/); expect(schema.toJSONSchema()).toEqual({ type: 'string', minLength: 1, diff --git a/packages/schema/src/schemas/array.ts b/packages/schema/src/schemas/array.ts index 7c50bdaba..e7d4999e5 100644 --- a/packages/schema/src/schemas/array.ts +++ b/packages/schema/src/schemas/array.ts @@ -18,17 +18,29 @@ export class ArraySchema extends Schema { _parse(value: unknown, ctx: ParseContext): T[] { if (!Array.isArray(value)) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected array, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected array, received ' + typeof value, + }); return value as T[]; } if (this._min !== undefined && value.length < this._min) { - ctx.addIssue({ code: ErrorCode.TooSmall, message: `Array must contain at least ${this._min} element(s)` }); + ctx.addIssue({ + code: ErrorCode.TooSmall, + message: `Array must contain at least ${this._min} element(s)`, + }); } if (this._max !== undefined && value.length > this._max) { - ctx.addIssue({ code: ErrorCode.TooBig, message: `Array must contain at most ${this._max} element(s)` }); + ctx.addIssue({ + code: ErrorCode.TooBig, + message: `Array must contain at most ${this._max} element(s)`, + }); } if (this._length !== undefined && value.length !== this._length) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: `Array must contain exactly ${this._length} element(s)` }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: `Array must contain exactly ${this._length} element(s)`, + }); } const result: T[] = []; for (let i = 0; i < value.length; i++) { @@ -62,7 +74,10 @@ export class ArraySchema extends Schema { } _toJSONSchema(tracker: RefTracker): JSONSchemaObject { - const schema: JSONSchemaObject = { type: 'array', items: this._element._toJSONSchemaWithRefs(tracker) }; + const schema: JSONSchemaObject = { + type: 'array', + items: this._element._toJSONSchemaWithRefs(tracker), + }; if (this._min !== undefined) schema.minItems = this._min; if (this._max !== undefined) schema.maxItems = this._max; if (this._length !== undefined) { diff --git a/packages/schema/src/schemas/bigint.ts b/packages/schema/src/schemas/bigint.ts index 0cfdad440..94ca8520f 100644 --- a/packages/schema/src/schemas/bigint.ts +++ b/packages/schema/src/schemas/bigint.ts @@ -8,7 +8,10 @@ import type { JSONSchemaObject } from '../introspection/json-schema'; export class BigIntSchema extends Schema { _parse(value: unknown, ctx: ParseContext): bigint { if (typeof value !== 'bigint') { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected bigint, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected bigint, received ' + typeof value, + }); return value as bigint; } return value; diff --git a/packages/schema/src/schemas/boolean.ts b/packages/schema/src/schemas/boolean.ts index 672312040..aeb2bb8c7 100644 --- a/packages/schema/src/schemas/boolean.ts +++ b/packages/schema/src/schemas/boolean.ts @@ -8,7 +8,10 @@ import type { JSONSchemaObject } from '../introspection/json-schema'; export class BooleanSchema extends Schema { _parse(value: unknown, ctx: ParseContext): boolean { if (typeof value !== 'boolean') { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected boolean, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected boolean, received ' + typeof value, + }); return value as boolean; } return value; diff --git a/packages/schema/src/schemas/date.ts b/packages/schema/src/schemas/date.ts index 24ed6b743..d0119b500 100644 --- a/packages/schema/src/schemas/date.ts +++ b/packages/schema/src/schemas/date.ts @@ -13,7 +13,10 @@ export class DateSchema extends Schema { _parse(value: unknown, ctx: ParseContext): Date { if (!(value instanceof Date)) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected Date, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected Date, received ' + typeof value, + }); return value as Date; } if (Number.isNaN(value.getTime())) { @@ -21,10 +24,16 @@ export class DateSchema extends Schema { return value; } if (this._min !== undefined && value.getTime() < this._min.getTime()) { - ctx.addIssue({ code: ErrorCode.TooSmall, message: this._minMessage ?? `Date must be after ${this._min.toISOString()}` }); + ctx.addIssue({ + code: ErrorCode.TooSmall, + message: this._minMessage ?? `Date must be after ${this._min.toISOString()}`, + }); } if (this._max !== undefined && value.getTime() > this._max.getTime()) { - ctx.addIssue({ code: ErrorCode.TooBig, message: this._maxMessage ?? `Date must be before ${this._max.toISOString()}` }); + ctx.addIssue({ + code: ErrorCode.TooBig, + message: this._maxMessage ?? `Date must be before ${this._max.toISOString()}`, + }); } return value; } diff --git a/packages/schema/src/schemas/discriminated-union.ts b/packages/schema/src/schemas/discriminated-union.ts index 8fc4ce06a..d2e62b968 100644 --- a/packages/schema/src/schemas/discriminated-union.ts +++ b/packages/schema/src/schemas/discriminated-union.ts @@ -62,7 +62,7 @@ export class DiscriminatedUnionSchema extends Sc const matchedSchema = this._lookup.get(discriminatorValue); if (!matchedSchema) { - const expected = [...this._lookup.keys()].map(k => `'${k}'`).join(' | '); + const expected = [...this._lookup.keys()].map((k) => `'${k}'`).join(' | '); ctx.addIssue({ code: ErrorCode.InvalidUnion, message: `Invalid discriminator value. Expected ${expected}, received '${discriminatorValue}'`, @@ -79,7 +79,7 @@ export class DiscriminatedUnionSchema extends Sc _toJSONSchema(tracker: RefTracker): JSONSchemaObject { return { - oneOf: this._options.map(option => option._toJSONSchemaWithRefs(tracker)), + oneOf: this._options.map((option) => option._toJSONSchemaWithRefs(tracker)), discriminator: { propertyName: this._discriminator }, }; } diff --git a/packages/schema/src/schemas/enum.ts b/packages/schema/src/schemas/enum.ts index 40a7b4b20..65cbf3531 100644 --- a/packages/schema/src/schemas/enum.ts +++ b/packages/schema/src/schemas/enum.ts @@ -17,7 +17,7 @@ export class EnumSchema extends Schema if (!this._values.includes(value as string)) { ctx.addIssue({ code: ErrorCode.InvalidEnumValue, - message: `Invalid enum value. Expected ${this._values.map(v => `'${v}'`).join(' | ')}, received '${value}'`, + message: `Invalid enum value. Expected ${this._values.map((v) => `'${v}'`).join(' | ')}, received '${value}'`, }); } return value as T[number]; @@ -32,7 +32,10 @@ export class EnumSchema extends Schema } exclude(values: E[]): EnumSchema { - const remaining = this._values.filter(v => !(values as string[]).includes(v)) as unknown as [string, ...string[]]; + const remaining = this._values.filter((v) => !(values as string[]).includes(v)) as unknown as [ + string, + ...string[], + ]; const schema = new EnumSchema(remaining); return this._cloneBase(schema); } diff --git a/packages/schema/src/schemas/formats/__tests__/base64.test.ts b/packages/schema/src/schemas/formats/__tests__/base64.test.ts index 528f75aed..5260f67f2 100644 --- a/packages/schema/src/schemas/formats/__tests__/base64.test.ts +++ b/packages/schema/src/schemas/formats/__tests__/base64.test.ts @@ -16,6 +16,9 @@ describe('Base64Schema', () => { }); it('toJSONSchema includes contentEncoding', () => { - expect(new Base64Schema().toJSONSchema()).toEqual({ type: 'string', contentEncoding: 'base64' }); + expect(new Base64Schema().toJSONSchema()).toEqual({ + type: 'string', + contentEncoding: 'base64', + }); }); }); diff --git a/packages/schema/src/schemas/formats/__tests__/ipv6.test.ts b/packages/schema/src/schemas/formats/__tests__/ipv6.test.ts index c3288f56e..ff3797537 100644 --- a/packages/schema/src/schemas/formats/__tests__/ipv6.test.ts +++ b/packages/schema/src/schemas/formats/__tests__/ipv6.test.ts @@ -4,7 +4,9 @@ import { Ipv6Schema } from '../ipv6'; describe('Ipv6Schema', () => { it('accepts valid IPv6 addresses', () => { const schema = new Ipv6Schema(); - expect(schema.parse('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe('2001:0db8:85a3:0000:0000:8a2e:0370:7334'); + expect(schema.parse('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe( + '2001:0db8:85a3:0000:0000:8a2e:0370:7334', + ); expect(schema.parse('::1')).toBe('::1'); expect(schema.parse('fe80::1')).toBe('fe80::1'); }); diff --git a/packages/schema/src/schemas/formats/__tests__/jwt.test.ts b/packages/schema/src/schemas/formats/__tests__/jwt.test.ts index aca79a2f4..1b74725d0 100644 --- a/packages/schema/src/schemas/formats/__tests__/jwt.test.ts +++ b/packages/schema/src/schemas/formats/__tests__/jwt.test.ts @@ -4,7 +4,8 @@ import { JwtSchema } from '../jwt'; describe('JwtSchema', () => { it('accepts valid JWT format', () => { const schema = new JwtSchema(); - const token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U'; + const token = + 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U'; expect(schema.parse(token)).toBe(token); }); diff --git a/packages/schema/src/schemas/formats/__tests__/uuid.test.ts b/packages/schema/src/schemas/formats/__tests__/uuid.test.ts index eabd12ced..0d5f90939 100644 --- a/packages/schema/src/schemas/formats/__tests__/uuid.test.ts +++ b/packages/schema/src/schemas/formats/__tests__/uuid.test.ts @@ -4,8 +4,12 @@ import { UuidSchema } from '../uuid'; describe('UuidSchema', () => { it('accepts valid UUIDs', () => { const schema = new UuidSchema(); - expect(schema.parse('550e8400-e29b-41d4-a716-446655440000')).toBe('550e8400-e29b-41d4-a716-446655440000'); - expect(schema.parse('F47AC10B-58CC-4372-A567-0E02B2C3D479')).toBe('F47AC10B-58CC-4372-A567-0E02B2C3D479'); + expect(schema.parse('550e8400-e29b-41d4-a716-446655440000')).toBe( + '550e8400-e29b-41d4-a716-446655440000', + ); + expect(schema.parse('F47AC10B-58CC-4372-A567-0E02B2C3D479')).toBe( + 'F47AC10B-58CC-4372-A567-0E02B2C3D479', + ); }); it('rejects invalid UUIDs', () => { diff --git a/packages/schema/src/schemas/formats/email.ts b/packages/schema/src/schemas/formats/email.ts index 1c0fc2636..4faefd59f 100644 --- a/packages/schema/src/schemas/formats/email.ts +++ b/packages/schema/src/schemas/formats/email.ts @@ -1,6 +1,7 @@ import { FormatSchema } from './format-schema'; -const EMAIL_RE = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; +const EMAIL_RE = + /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; export class EmailSchema extends FormatSchema { protected _errorMessage = 'Invalid email'; diff --git a/packages/schema/src/schemas/formats/hostname.ts b/packages/schema/src/schemas/formats/hostname.ts index 03cc20a6b..d4e2c83ff 100644 --- a/packages/schema/src/schemas/formats/hostname.ts +++ b/packages/schema/src/schemas/formats/hostname.ts @@ -1,6 +1,7 @@ import { FormatSchema } from './format-schema'; -const HOSTNAME_RE = /^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; +const HOSTNAME_RE = + /^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; export class HostnameSchema extends FormatSchema { protected _errorMessage = 'Invalid hostname'; diff --git a/packages/schema/src/schemas/formats/ipv4.ts b/packages/schema/src/schemas/formats/ipv4.ts index 6e123ff31..427d540ae 100644 --- a/packages/schema/src/schemas/formats/ipv4.ts +++ b/packages/schema/src/schemas/formats/ipv4.ts @@ -8,7 +8,7 @@ export class Ipv4Schema extends FormatSchema { protected _validate(value: string): boolean { const match = IPV4_RE.exec(value); if (!match) return false; - return [match[1], match[2], match[3], match[4]].every(o => Number(o) <= 255); + return [match[1], match[2], match[3], match[4]].every((o) => Number(o) <= 255); } protected _jsonSchemaExtra(): Record { diff --git a/packages/schema/src/schemas/formats/ipv6.ts b/packages/schema/src/schemas/formats/ipv6.ts index dc3cf76f7..478495354 100644 --- a/packages/schema/src/schemas/formats/ipv6.ts +++ b/packages/schema/src/schemas/formats/ipv6.ts @@ -1,6 +1,7 @@ import { FormatSchema } from './format-schema'; -const IPV6_RE = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))$/; +const IPV6_RE = + /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))$/; export class Ipv6Schema extends FormatSchema { protected _errorMessage = 'Invalid IPv6 address'; diff --git a/packages/schema/src/schemas/formats/iso.ts b/packages/schema/src/schemas/formats/iso.ts index f71bd5c64..5130adbcd 100644 --- a/packages/schema/src/schemas/formats/iso.ts +++ b/packages/schema/src/schemas/formats/iso.ts @@ -12,7 +12,7 @@ function validateDateRange(value: string): boolean { } function validateTimeRange(value: string): boolean { - const [h, min, s] = value.split(':').map(v => Number(v.replace(/[^0-9.]/g, ''))); + const [h, min, s] = value.split(':').map((v) => Number(v.replace(/[^0-9.]/g, ''))); return h >= 0 && h <= 23 && min >= 0 && min <= 59 && s >= 0 && s <= 59; } diff --git a/packages/schema/src/schemas/instanceof.ts b/packages/schema/src/schemas/instanceof.ts index 0f893bbe4..a762cdbe3 100644 --- a/packages/schema/src/schemas/instanceof.ts +++ b/packages/schema/src/schemas/instanceof.ts @@ -6,7 +6,9 @@ import type { RefTracker } from '../introspection/json-schema'; import type { JSONSchemaObject } from '../introspection/json-schema'; export class InstanceOfSchema extends Schema { - private readonly _cls: new (...args: any[]) => T; + private readonly _cls: new ( + ...args: any[] + ) => T; constructor(cls: new (...args: any[]) => T) { super(); @@ -15,7 +17,10 @@ export class InstanceOfSchema extends Schema { _parse(value: unknown, ctx: ParseContext): T { if (!(value instanceof this._cls)) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: `Expected instance of ${this._cls.name}` }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: `Expected instance of ${this._cls.name}`, + }); return value as T; } return value; diff --git a/packages/schema/src/schemas/intersection.ts b/packages/schema/src/schemas/intersection.ts index 772012834..9ec8cabb9 100644 --- a/packages/schema/src/schemas/intersection.ts +++ b/packages/schema/src/schemas/intersection.ts @@ -4,10 +4,9 @@ import { ErrorCode } from '../core/errors'; import { SchemaType } from '../core/types'; import type { RefTracker, JSONSchemaObject } from '../introspection/json-schema'; -export class IntersectionSchema< - L extends Schema, - R extends Schema, -> extends Schema { +export class IntersectionSchema, R extends Schema> extends Schema< + L['_output'] & R['_output'] +> { private readonly _left: L; private readonly _right: R; @@ -29,8 +28,12 @@ export class IntersectionSchema< return value as any; } - if (typeof leftResult.data === 'object' && leftResult.data !== null && - typeof rightResult.data === 'object' && rightResult.data !== null) { + if ( + typeof leftResult.data === 'object' && + leftResult.data !== null && + typeof rightResult.data === 'object' && + rightResult.data !== null + ) { return { ...leftResult.data, ...rightResult.data }; } diff --git a/packages/schema/src/schemas/map.ts b/packages/schema/src/schemas/map.ts index 14c9906da..609b7f5fa 100644 --- a/packages/schema/src/schemas/map.ts +++ b/packages/schema/src/schemas/map.ts @@ -17,7 +17,10 @@ export class MapSchema extends Schema> { _parse(value: unknown, ctx: ParseContext): Map { if (!(value instanceof Map)) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected Map, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected Map, received ' + typeof value, + }); return value as Map; } const result = new Map(); diff --git a/packages/schema/src/schemas/number.ts b/packages/schema/src/schemas/number.ts index b5042f51e..dec0d1038 100644 --- a/packages/schema/src/schemas/number.ts +++ b/packages/schema/src/schemas/number.ts @@ -24,20 +24,35 @@ export class NumberSchema extends Schema { _parse(value: unknown, ctx: ParseContext): number { if (typeof value !== 'number' || Number.isNaN(value)) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected number, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected number, received ' + typeof value, + }); return value as number; } if (this._gte !== undefined && value < this._gte) { - ctx.addIssue({ code: ErrorCode.TooSmall, message: this._gteMessage ?? `Number must be greater than or equal to ${this._gte}` }); + ctx.addIssue({ + code: ErrorCode.TooSmall, + message: this._gteMessage ?? `Number must be greater than or equal to ${this._gte}`, + }); } if (this._gt !== undefined && value <= this._gt) { - ctx.addIssue({ code: ErrorCode.TooSmall, message: this._gtMessage ?? `Number must be greater than ${this._gt}` }); + ctx.addIssue({ + code: ErrorCode.TooSmall, + message: this._gtMessage ?? `Number must be greater than ${this._gt}`, + }); } if (this._lte !== undefined && value > this._lte) { - ctx.addIssue({ code: ErrorCode.TooBig, message: this._lteMessage ?? `Number must be less than or equal to ${this._lte}` }); + ctx.addIssue({ + code: ErrorCode.TooBig, + message: this._lteMessage ?? `Number must be less than or equal to ${this._lte}`, + }); } if (this._lt !== undefined && value >= this._lt) { - ctx.addIssue({ code: ErrorCode.TooBig, message: this._ltMessage ?? `Number must be less than ${this._lt}` }); + ctx.addIssue({ + code: ErrorCode.TooBig, + message: this._ltMessage ?? `Number must be less than ${this._lt}`, + }); } if (this._int && !Number.isInteger(value)) { ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected integer, received float' }); @@ -55,7 +70,10 @@ export class NumberSchema extends Schema { ctx.addIssue({ code: ErrorCode.TooBig, message: 'Number must be nonpositive' }); } if (this._multipleOf !== undefined && value % this._multipleOf !== 0) { - ctx.addIssue({ code: ErrorCode.NotMultipleOf, message: `Number must be a multiple of ${this._multipleOf}` }); + ctx.addIssue({ + code: ErrorCode.NotMultipleOf, + message: `Number must be a multiple of ${this._multipleOf}`, + }); } if (this._finite && !Number.isFinite(value)) { ctx.addIssue({ code: ErrorCode.NotFinite, message: 'Number must be finite' }); diff --git a/packages/schema/src/schemas/object.ts b/packages/schema/src/schemas/object.ts index 6c579cc98..d39767377 100644 --- a/packages/schema/src/schemas/object.ts +++ b/packages/schema/src/schemas/object.ts @@ -38,7 +38,10 @@ export class ObjectSchema extends Schema> _parse(value: unknown, ctx: ParseContext): InferShape { if (typeof value !== 'object' || value === null || Array.isArray(value)) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected object, received ' + receivedType(value) }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected object, received ' + receivedType(value), + }); return value as InferShape; } const obj = value as Record; @@ -47,7 +50,11 @@ export class ObjectSchema extends Schema> for (const key of shapeKeys) { if (!(key in obj) && !this._isOptionalKey(this._shape[key]!)) { - ctx.addIssue({ code: ErrorCode.MissingProperty, message: `Missing required property "${key}"`, path: [key] }); + ctx.addIssue({ + code: ErrorCode.MissingProperty, + message: `Missing required property "${key}"`, + path: [key], + }); continue; } ctx.pushPath(key); @@ -55,7 +62,7 @@ export class ObjectSchema extends Schema> ctx.popPath(); } - const unknownKeys = Object.keys(obj).filter(k => !shapeKeys.has(k)); + const unknownKeys = Object.keys(obj).filter((k) => !shapeKeys.has(k)); if (unknownKeys.length > 0) { if (this._catchall) { for (const key of unknownKeys) { @@ -66,7 +73,7 @@ export class ObjectSchema extends Schema> } else if (this._unknownKeys === 'strict') { ctx.addIssue({ code: ErrorCode.UnrecognizedKeys, - message: `Unrecognized key(s) in object: ${unknownKeys.map(k => `"${k}"`).join(', ')}`, + message: `Unrecognized key(s) in object: ${unknownKeys.map((k) => `"${k}"`).join(', ')}`, }); } else if (this._unknownKeys === 'passthrough') { for (const key of unknownKeys) { @@ -106,7 +113,13 @@ export class ObjectSchema extends Schema> return new ObjectSchema(picked as Pick); } - required(): ObjectSchema<{ [K in keyof S]: S[K] extends OptionalSchema ? Schema : S[K] extends DefaultSchema ? Schema : S[K] }> { + required(): ObjectSchema<{ + [K in keyof S]: S[K] extends OptionalSchema + ? Schema + : S[K] extends DefaultSchema + ? Schema + : S[K]; + }> { const requiredShape: Record> = {}; for (const key of Object.keys(this._shape)) { const schema = this._shape[key]!; diff --git a/packages/schema/src/schemas/set.ts b/packages/schema/src/schemas/set.ts index c68631315..f4432c061 100644 --- a/packages/schema/src/schemas/set.ts +++ b/packages/schema/src/schemas/set.ts @@ -18,7 +18,10 @@ export class SetSchema extends Schema> { _parse(value: unknown, ctx: ParseContext): Set { if (!(value instanceof Set)) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected Set, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected Set, received ' + typeof value, + }); return value as Set; } const result = new Set(); @@ -30,13 +33,22 @@ export class SetSchema extends Schema> { index++; } if (this._min !== undefined && result.size < this._min) { - ctx.addIssue({ code: ErrorCode.TooSmall, message: `Set must contain at least ${this._min} element(s)` }); + ctx.addIssue({ + code: ErrorCode.TooSmall, + message: `Set must contain at least ${this._min} element(s)`, + }); } if (this._max !== undefined && result.size > this._max) { - ctx.addIssue({ code: ErrorCode.TooBig, message: `Set must contain at most ${this._max} element(s)` }); + ctx.addIssue({ + code: ErrorCode.TooBig, + message: `Set must contain at most ${this._max} element(s)`, + }); } if (this._size !== undefined && result.size !== this._size) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: `Set must contain exactly ${this._size} element(s)` }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: `Set must contain exactly ${this._size} element(s)`, + }); } return result; } diff --git a/packages/schema/src/schemas/special.ts b/packages/schema/src/schemas/special.ts index a599d6ca0..04024ee72 100644 --- a/packages/schema/src/schemas/special.ts +++ b/packages/schema/src/schemas/special.ts @@ -44,7 +44,10 @@ export class UnknownSchema extends Schema { export class NullSchema extends Schema { _parse(value: unknown, ctx: ParseContext): null { if (value !== null) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected null, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected null, received ' + typeof value, + }); return value as null; } return value; @@ -66,7 +69,10 @@ export class NullSchema extends Schema { export class UndefinedSchema extends Schema { _parse(value: unknown, ctx: ParseContext): undefined { if (value !== undefined) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected undefined, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected undefined, received ' + typeof value, + }); return value as undefined; } return value; @@ -88,7 +94,10 @@ export class UndefinedSchema extends Schema { export class VoidSchema extends Schema { _parse(value: unknown, ctx: ParseContext): void { if (value !== undefined) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected void (undefined), received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected void (undefined), received ' + typeof value, + }); return value as void; } return value; diff --git a/packages/schema/src/schemas/string.ts b/packages/schema/src/schemas/string.ts index 1b15f5ae5..9bd871005 100644 --- a/packages/schema/src/schemas/string.ts +++ b/packages/schema/src/schemas/string.ts @@ -25,7 +25,10 @@ export class StringSchema extends Schema { _parse(value: unknown, ctx: ParseContext): string { if (typeof value !== 'string') { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected string, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected string, received ' + typeof value, + }); return value as string; } let v = value; @@ -42,25 +45,46 @@ export class StringSchema extends Schema { v = v.normalize(); } if (this._min !== undefined && v.length < this._min) { - ctx.addIssue({ code: ErrorCode.TooSmall, message: this._minMessage ?? `String must contain at least ${this._min} character(s)` }); + ctx.addIssue({ + code: ErrorCode.TooSmall, + message: this._minMessage ?? `String must contain at least ${this._min} character(s)`, + }); } if (this._max !== undefined && v.length > this._max) { - ctx.addIssue({ code: ErrorCode.TooBig, message: this._maxMessage ?? `String must contain at most ${this._max} character(s)` }); + ctx.addIssue({ + code: ErrorCode.TooBig, + message: this._maxMessage ?? `String must contain at most ${this._max} character(s)`, + }); } if (this._length !== undefined && v.length !== this._length) { - ctx.addIssue({ code: ErrorCode.InvalidString, message: this._lengthMessage ?? `String must be exactly ${this._length} character(s)` }); + ctx.addIssue({ + code: ErrorCode.InvalidString, + message: this._lengthMessage ?? `String must be exactly ${this._length} character(s)`, + }); } if (this._regex !== undefined && !this._regex.test(v)) { - ctx.addIssue({ code: ErrorCode.InvalidString, message: `Invalid: must match ${this._regex}` }); + ctx.addIssue({ + code: ErrorCode.InvalidString, + message: `Invalid: must match ${this._regex}`, + }); } if (this._startsWith !== undefined && !v.startsWith(this._startsWith)) { - ctx.addIssue({ code: ErrorCode.InvalidString, message: `Invalid input: must start with "${this._startsWith}"` }); + ctx.addIssue({ + code: ErrorCode.InvalidString, + message: `Invalid input: must start with "${this._startsWith}"`, + }); } if (this._endsWith !== undefined && !v.endsWith(this._endsWith)) { - ctx.addIssue({ code: ErrorCode.InvalidString, message: `Invalid input: must end with "${this._endsWith}"` }); + ctx.addIssue({ + code: ErrorCode.InvalidString, + message: `Invalid input: must end with "${this._endsWith}"`, + }); } if (this._includes !== undefined && !v.includes(this._includes)) { - ctx.addIssue({ code: ErrorCode.InvalidString, message: `Invalid input: must include "${this._includes}"` }); + ctx.addIssue({ + code: ErrorCode.InvalidString, + message: `Invalid input: must include "${this._includes}"`, + }); } if (this._uppercase && v !== v.toUpperCase()) { ctx.addIssue({ code: ErrorCode.InvalidString, message: 'Expected string to be uppercase' }); diff --git a/packages/schema/src/schemas/symbol.ts b/packages/schema/src/schemas/symbol.ts index d4d51cb5d..a7dc5317a 100644 --- a/packages/schema/src/schemas/symbol.ts +++ b/packages/schema/src/schemas/symbol.ts @@ -8,7 +8,10 @@ import type { JSONSchemaObject } from '../introspection/json-schema'; export class SymbolSchema extends Schema { _parse(value: unknown, ctx: ParseContext): symbol { if (typeof value !== 'symbol') { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected symbol, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected symbol, received ' + typeof value, + }); return value as symbol; } return value; diff --git a/packages/schema/src/schemas/tuple.ts b/packages/schema/src/schemas/tuple.ts index 4e22f64af..97e10c2d0 100644 --- a/packages/schema/src/schemas/tuple.ts +++ b/packages/schema/src/schemas/tuple.ts @@ -6,7 +6,9 @@ import type { RefTracker } from '../introspection/json-schema'; import type { JSONSchemaObject } from '../introspection/json-schema'; type TupleItems = [Schema, ...Schema[]]; -type InferTuple = { [K in keyof T]: T[K] extends Schema ? O : never }; +type InferTuple = { + [K in keyof T]: T[K] extends Schema ? O : never; +}; export class TupleSchema extends Schema> { private readonly _items: T; @@ -19,14 +21,23 @@ export class TupleSchema extends Schema> { _parse(value: unknown, ctx: ParseContext): InferTuple { if (!Array.isArray(value)) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: 'Expected array, received ' + typeof value }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: 'Expected array, received ' + typeof value, + }); return value as InferTuple; } if (!this._rest && value.length !== this._items.length) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: `Expected array of length ${this._items.length}, received ${value.length}` }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: `Expected array of length ${this._items.length}, received ${value.length}`, + }); } if (this._rest && value.length < this._items.length) { - ctx.addIssue({ code: ErrorCode.InvalidType, message: `Expected at least ${this._items.length} element(s), received ${value.length}` }); + ctx.addIssue({ + code: ErrorCode.InvalidType, + message: `Expected at least ${this._items.length} element(s), received ${value.length}`, + }); } const result: unknown[] = []; for (let i = 0; i < this._items.length; i++) { @@ -55,7 +66,7 @@ export class TupleSchema extends Schema> { } _toJSONSchema(tracker: RefTracker): JSONSchemaObject { - const prefixItems = this._items.map(item => item._toJSONSchemaWithRefs(tracker)); + const prefixItems = this._items.map((item) => item._toJSONSchemaWithRefs(tracker)); const schema: JSONSchemaObject = { type: 'array', prefixItems }; schema.items = this._rest ? this._rest._toJSONSchemaWithRefs(tracker) : false; return schema; diff --git a/packages/schema/src/schemas/union.ts b/packages/schema/src/schemas/union.ts index 65e2b6bcf..6f8f3be59 100644 --- a/packages/schema/src/schemas/union.ts +++ b/packages/schema/src/schemas/union.ts @@ -36,7 +36,7 @@ export class UnionSchema extends Schema> { _toJSONSchema(tracker: RefTracker): JSONSchemaObject { return { - anyOf: this._options.map(option => option._toJSONSchemaWithRefs(tracker)), + anyOf: this._options.map((option) => option._toJSONSchemaWithRefs(tracker)), }; } diff --git a/packages/schema/src/transforms/__tests__/preprocess.test.ts b/packages/schema/src/transforms/__tests__/preprocess.test.ts index 1c5592ead..8f32d5a94 100644 --- a/packages/schema/src/transforms/__tests__/preprocess.test.ts +++ b/packages/schema/src/transforms/__tests__/preprocess.test.ts @@ -15,7 +15,9 @@ describe('preprocess()', () => { }); it('safeParse catches exceptions thrown by preprocess function', () => { - const schema = preprocess(() => { throw new Error('Preprocess exploded'); }, new NumberSchema()); + const schema = preprocess(() => { + throw new Error('Preprocess exploded'); + }, new NumberSchema()); const result = schema.safeParse('hello'); expect(result.success).toBe(false); }); diff --git a/packages/schema/src/utils/__tests__/type-inference.test.ts b/packages/schema/src/utils/__tests__/type-inference.test.ts index 619771d65..a76b8a7f0 100644 --- a/packages/schema/src/utils/__tests__/type-inference.test.ts +++ b/packages/schema/src/utils/__tests__/type-inference.test.ts @@ -13,8 +13,12 @@ class TestStringSchema extends Schema { } return value; } - _schemaType(): SchemaType { return SchemaType.String; } - _toJSONSchema(): Record { return { type: 'string' }; } + _schemaType(): SchemaType { + return SchemaType.String; + } + _toJSONSchema(): Record { + return { type: 'string' }; + } _clone(): TestStringSchema { return this._cloneBase(new TestStringSchema()); } diff --git a/packages/testing/src/__tests__/test-app.test.ts b/packages/testing/src/__tests__/test-app.test.ts index 7ab881dd9..9035893b8 100644 --- a/packages/testing/src/__tests__/test-app.test.ts +++ b/packages/testing/src/__tests__/test-app.test.ts @@ -20,7 +20,13 @@ interface RouteInput { function addRoutes(router: NamedRouterDef, routes: RouteInput[]): void { for (const route of routes) { - const method = route.method.toLowerCase() as 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head'; + const method = route.method.toLowerCase() as + | 'get' + | 'post' + | 'put' + | 'patch' + | 'delete' + | 'head'; router[method](route.path, { handler: route.handler, response: route.response }); } } @@ -115,9 +121,7 @@ describe('createTestApp', () => { { method: 'GET', path: '/', handler: (ctx: any) => ({ dbUrl: ctx.env.DATABASE_URL }) }, ]); - const app = createTestApp() - .env({ DATABASE_URL: 'postgres://test' }) - .register(mod); + const app = createTestApp().env({ DATABASE_URL: 'postgres://test' }).register(mod); const res = await app.get('/api'); @@ -128,7 +132,9 @@ describe('createTestApp', () => { it('uses mocked middleware result instead of running real middleware', async () => { const authMiddleware = createMiddleware({ name: 'auth', - handler: () => { throw new Error('should not run'); }, + handler: () => { + throw new Error('should not run'); + }, }); const mod = createTestModule('test', '/api', [ @@ -148,7 +154,9 @@ describe('createTestApp', () => { it('supports per-request middleware override', async () => { const authMiddleware = createMiddleware({ name: 'auth', - handler: () => { throw new Error('should not run'); }, + handler: () => { + throw new Error('should not run'); + }, }); const mod = createTestModule('test', '/api', [ @@ -170,7 +178,11 @@ describe('createTestApp', () => { it('passes custom headers to handler via ctx', async () => { const mod = createTestModule('test', '/api', [ - { method: 'GET', path: '/', handler: (ctx: any) => ({ token: ctx.headers['authorization'] }) }, + { + method: 'GET', + path: '/', + handler: (ctx: any) => ({ token: ctx.headers['authorization'] }), + }, ]); const app = createTestApp().register(mod); @@ -182,7 +194,11 @@ describe('createTestApp', () => { it('executes a PUT request with body', async () => { const mod = createTestModule('test', '/users', [ - { method: 'PUT', path: '/:id', handler: (ctx: any) => ({ updated: ctx.params.id, name: ctx.body.name }) }, + { + method: 'PUT', + path: '/:id', + handler: (ctx: any) => ({ updated: ctx.params.id, name: ctx.body.name }), + }, ]); const app = createTestApp().register(mod); @@ -194,7 +210,11 @@ describe('createTestApp', () => { it('executes a PATCH request with body', async () => { const mod = createTestModule('test', '/users', [ - { method: 'PATCH', path: '/:id', handler: (ctx: any) => ({ patched: ctx.params.id, email: ctx.body.email }) }, + { + method: 'PATCH', + path: '/:id', + handler: (ctx: any) => ({ patched: ctx.params.id, email: ctx.body.email }), + }, ]); const app = createTestApp().register(mod); @@ -258,9 +278,7 @@ describe('createTestApp', () => { .mock(service, { greet: () => 'app-level' }) .register(mod); - const res = await app - .get('/api') - .mock(service, { greet: () => 'per-request' }); + const res = await app.get('/api').mock(service, { greet: () => 'per-request' }); expect(res.status).toBe(200); expect(res.body).toEqual({ message: 'per-request' }); @@ -287,7 +305,12 @@ describe('createTestApp', () => { it('passes when handler return matches response schema', async () => { const mod = createTestModule('test', '/api', [ - { method: 'GET', path: '/', response: s.object({ name: s.string() }), handler: () => ({ name: 'Alice' }) }, + { + method: 'GET', + path: '/', + response: s.object({ name: s.string() }), + handler: () => ({ name: 'Alice' }), + }, ]); const app = createTestApp().register(mod); @@ -311,7 +334,12 @@ describe('createTestApp', () => { it('throws when handler returns undefined but route has response schema', async () => { const mod = createTestModule('test', '/api', [ - { method: 'GET', path: '/', response: s.object({ name: s.string() }), handler: () => undefined }, + { + method: 'GET', + path: '/', + response: s.object({ name: s.string() }), + handler: () => undefined, + }, ]); const app = createTestApp().register(mod); @@ -321,12 +349,16 @@ describe('createTestApp', () => { it('throws when handler return does not match response schema', async () => { const mod = createTestModule('test', '/api', [ - { method: 'GET', path: '/', response: s.object({ name: s.string() }), handler: () => ({ name: 123 }) }, + { + method: 'GET', + path: '/', + response: s.object({ name: s.string() }), + handler: () => ({ name: 123 }), + }, ]); const app = createTestApp().register(mod); await expect(app.get('/api')).rejects.toThrow('Response validation failed'); }); - }); diff --git a/packages/testing/src/__tests__/test-service.test.ts b/packages/testing/src/__tests__/test-service.test.ts index 8b27ef20b..2f848ff4e 100644 --- a/packages/testing/src/__tests__/test-service.test.ts +++ b/packages/testing/src/__tests__/test-service.test.ts @@ -29,8 +29,9 @@ describe('createTestService', () => { }), }); - const methods = await createTestService(userService) - .mock(dbService, { query: (sql: string) => `mocked: ${sql}` }); + const methods = await createTestService(userService).mock(dbService, { + query: (sql: string) => `mocked: ${sql}`, + }); expect(methods.findById('42')).toBe("mocked: SELECT * FROM users WHERE id = '42'"); }); @@ -47,9 +48,7 @@ describe('createTestService', () => { }), }); - await expect(createTestService(userService)).rejects.toThrow( - /missing mock.*db/i, - ); + await expect(createTestService(userService)).rejects.toThrow(/missing mock.*db/i); }); it('awaits async onInit and passes state to methods', async () => { diff --git a/packages/testing/src/test-app.ts b/packages/testing/src/test-app.ts index 5f6677673..de28d93b6 100644 --- a/packages/testing/src/test-app.ts +++ b/packages/testing/src/test-app.ts @@ -27,8 +27,14 @@ export interface TestResponse { } export interface TestRequestBuilder extends PromiseLike { - mock(service: NamedServiceDef, impl: DeepPartial): TestRequestBuilder; - mockMiddleware, TProv extends Record>(middleware: NamedMiddlewareDef, result: TProv): TestRequestBuilder; + mock( + service: NamedServiceDef, + impl: DeepPartial, + ): TestRequestBuilder; + mockMiddleware, TProv extends Record>( + middleware: NamedMiddlewareDef, + result: TProv, + ): TestRequestBuilder; } interface RequestOptions { @@ -38,8 +44,14 @@ interface RequestOptions { export interface TestApp { register(module: NamedModule, options?: Record): TestApp; - mock(service: NamedServiceDef, impl: DeepPartial): TestApp; - mockMiddleware, TProv extends Record>(middleware: NamedMiddlewareDef, result: TProv): TestApp; + mock( + service: NamedServiceDef, + impl: DeepPartial, + ): TestApp; + mockMiddleware, TProv extends Record>( + middleware: NamedMiddlewareDef, + result: TProv, + ): TestApp; env(vars: Record): TestApp; get(path: string, options?: RequestOptions): TestRequestBuilder; post(path: string, options?: RequestOptions): TestRequestBuilder; @@ -214,7 +226,11 @@ export function createTestApp(): TestApp { }; } - function createRequestBuilder(method: string, path: string, options?: RequestOptions): TestRequestBuilder { + function createRequestBuilder( + method: string, + path: string, + options?: RequestOptions, + ): TestRequestBuilder { const perRequest: PerRequestMocks = { services: new Map(), middlewares: new Map, Record>(), diff --git a/packages/testing/src/test-service.ts b/packages/testing/src/test-service.ts index 7ceba3fdd..68b2df55c 100644 --- a/packages/testing/src/test-service.ts +++ b/packages/testing/src/test-service.ts @@ -3,7 +3,10 @@ import type { NamedServiceDef } from '@vertz/core'; import type { DeepPartial } from './types'; export interface TestServiceBuilder extends PromiseLike { - mock(service: NamedServiceDef, impl: DeepPartial): TestServiceBuilder; + mock( + service: NamedServiceDef, + impl: DeepPartial, + ): TestServiceBuilder; } export function createTestService(