src/vs/base/common/validation.ts

396 LOC · 253 covered · 143 uncovered · 71 ranges · 463 concepts · 15 introducers · 257 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- validation.ts ×43
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { mapFilter } from './arrays.js';
7 > import { IJSONSchema } from './jsonSchema.js';
8 >
9 > export interface IValidator<T> {
10 > validate(content: unknown): { content: T; error: undefined } | { content: undefined; error: ValidationError };
11 >
12 > getJSONSchema(): IJSONSchema;
13 > }
14 >
15 > export abstract class ValidatorBase<T> implements IValidator<T> {
16 > abstract validate(content: unknown): { content: T; error: undefined } | { content: undefined; error: ValidationError };
17 >
18 > abstract getJSONSchema(): IJSONSchema;
19 >
20 > validateOrThrow(content: unknown): T {
21 const result = this.validate(content);
22 if (result.error) {
23 throw new Error(result.error.message);
24 }
25 return result.content;
26 }
28 >
29 > export type ValidatorType<T> = T extends IValidator<infer U> ? U : never;
30 >
31 > export interface ValidationError {
32 > message: string;
33 > }
34 >
35 > type TypeOfMap = {
36 > string: string;
37 > number: number;
38 > boolean: boolean;
39 > object: object;
40 > null: null;
41 > };
42 >
43 > class TypeofValidator<TKey extends keyof TypeOfMap> extends ValidatorBase<TypeOfMap[TKey]> {
44 > constructor(private readonly type: TKey) {
45 > super();
46 > }
47 >
48 > validate(content: unknown): { content: TypeOfMap[TKey]; error: undefined } | { content: undefined; error: ValidationError } {
49 > if (typeof content !== this.type) { validation.ts ×2
50 > return { content: undefined, error: { message: `Expected ${this.type}, but got ${typeof content}` } }; validation.ts ×1
51 > }
53 > return { content: content as TypeOfMap[TKey], error: undefined };
54 > }
56 > getJSONSchema(): IJSONSchema {
57 return { type: this.type };
58 }
60 >
61 > const vStringValidator = new TypeofValidator('string');
62 > export function vString(): ValidatorBase<string> { return vStringValidator; }
63 >
64 > const vNumberValidator = new TypeofValidator('number');
65 > export function vNumber(): ValidatorBase<number> { return vNumberValidator; }
66 >
67 > const vBooleanValidator = new TypeofValidator('boolean');
68 > export function vBoolean(): ValidatorBase<boolean> { return vBooleanValidator; }
69 >
70 > const vObjAnyValidator = new TypeofValidator('object');
71 > export function vObjAny(): ValidatorBase<object> { return vObjAnyValidator; }
72 >
73 >
74 > class UncheckedValidator<T> extends ValidatorBase<T> {
75 > validate(content: unknown): { content: T; error: undefined } {
76 > return { content: content as T, error: undefined }; validation.ts ×1
77 > }
79 > getJSONSchema(): IJSONSchema {
80 return {};
81 }
83 >
84 > export function vUnchecked<T>(): ValidatorBase<T> {
85 > return new UncheckedValidator<T>(); claudeElicitation.ts ×7
86 > }
88 > class UndefinedValidator extends ValidatorBase<undefined> {
89 > validate(content: unknown): { content: undefined; error: undefined } | { content: undefined; error: ValidationError } {
90 if (content !== undefined) {
91 return { content: undefined, error: { message: `Expected undefined, but got ${typeof content}` } };
92 }
93
94 return { content: undefined, error: undefined };
95 }
97 > getJSONSchema(): IJSONSchema {
98 return {};
99 }
101 >
102 > export function vUndefined(): ValidatorBase<undefined> {
103 return new UndefinedValidator();
104 }
106 > export function vUnknown(): ValidatorBase<unknown> {
107 > return vUnchecked(); claudeElicitation.ts ×7
108 > }
110 > export type ObjectProperties = Record<string, unknown>;
111 >
112 > export class Optional<T extends IValidator<unknown>> {
113 > constructor(public readonly validator: T) { }
114 > }
115 >
116 > export function vOptionalProp<T>(validator: IValidator<T>): Optional<IValidator<T>> {
117 > return new Optional(validator); validation.ts ×1
118 > }
120 > type ExtractOptionalKeys<T> = {
121 > [K in keyof T]: T[K] extends Optional<IValidator<unknown>> ? K : never;
122 > }[keyof T];
123 >
124 > type ExtractRequiredKeys<T> = {
125 > [K in keyof T]: T[K] extends Optional<IValidator<unknown>> ? never : K;
126 > }[keyof T];
127 >
128 > export type vObjType<T extends Record<string, IValidator<unknown> | Optional<IValidator<unknown>>>> = {
129 > [K in ExtractRequiredKeys<T>]: T[K] extends IValidator<infer U> ? U : never;
130 > } & {
131 > [K in ExtractOptionalKeys<T>]?: T[K] extends Optional<IValidator<infer U>> ? U : never;
132 > };
133 >
134 > class ObjValidator<T extends Record<string, IValidator<unknown> | Optional<IValidator<unknown>>>> extends ValidatorBase<vObjType<T>> {
135 > constructor(private readonly properties: T) {
136 > super(); validation.ts ×4
137 > }
139 > validate(content: unknown): { content: vObjType<T>; error: undefined } | { content: undefined; error: ValidationError } {
140 > if (typeof content !== 'object' || content === null) { validation.ts ×5
141 return { content: undefined, error: { message: 'Expected object' } };
142 }
144 > // eslint-disable-next-line local/code-no-dangerous-type-assertions
145 > const result: vObjType<T> = {} as vObjType<T>;
146 >
147 > for (const key in this.properties) {
148 > const prop = this.properties[key];
149 > // eslint-disable-next-line local/code-no-any-casts, @typescript-eslint/no-explicit-any
150 > const fieldValue = (content as any)[key];
151 >
152 > const isOptional = prop instanceof Optional;
153 > const validator: IValidator<unknown> = isOptional ? prop.validator : prop;
154 >
155 > if (isOptional && fieldValue === undefined) {
156 > // Optional field not provided, skip validation claudeElicitation.ts ×2
157 > continue;
158 > }
160 > const { content: value, error } = validator.validate(fieldValue);
161 > if (error) {
162 > return { content: undefined, error: { message: `Error in property '${key}': ${error.message}` } }; validation.ts ×2
163 > }
165 > // eslint-disable-next-line local/code-no-any-casts, @typescript-eslint/no-explicit-any
166 > (result as any)[key] = value;
167 > }
169 > return { content: result, error: undefined };
172 > getJSONSchema(): IJSONSchema {
173 const requiredFields: string[] = [];
174 const schemaProperties: Record<string, IJSONSchema> = {};
175
176 for (const [key, prop] of Object.entries(this.properties)) {
177 const isOptional = prop instanceof Optional;
178 const validator: IValidator<unknown> = isOptional ? prop.validator : prop;
179 schemaProperties[key] = validator.getJSONSchema();
180 if (!isOptional) {
181 requiredFields.push(key);
182 }
183 }
184
185 const schema: IJSONSchema = {
186 type: 'object',
187 properties: schemaProperties,
188 ...(requiredFields.length > 0 ? { required: requiredFields } : {})
189 };
190
191 return schema;
192 }
194 >
195 > export function vObj<T extends Record<string, IValidator<unknown> | Optional<IValidator<unknown>>>>(properties: T): ValidatorBase<vObjType<T>> {
196 > return new ObjValidator(properties); validation.ts ×4
197 > }
199 > class ArrayValidator<T> extends ValidatorBase<T[]> {
200 > constructor(private readonly validator: IValidator<T>) {
201 > super(); validation.ts ×4
202 > }
204 > validate(content: unknown): { content: T[]; error: undefined } | { content: undefined; error: ValidationError } {
205 > if (!Array.isArray(content)) { validation.ts ×2
206 > return { content: undefined, error: { message: 'Expected array' } }; validation.ts ×2
207 > }
209 > const result: T[] = [];
210 > for (let i = 0; i < content.length; i++) {
211 > const { content: value, error } = this.validator.validate(content[i]);
212 > if (error) {
213 return { content: undefined, error: { message: `Error in element ${i}: ${error.message}` } };
214 }
216 > result.push(value);
217 > }
218 >
219 > return { content: result, error: undefined };
222 > getJSONSchema(): IJSONSchema {
223 return {
224 type: 'array',
225 items: this.validator.getJSONSchema(),
226 };
227 }
229 >
230 > export function vArray<T>(validator: IValidator<T>): ValidatorBase<T[]> {
231 > return new ArrayValidator(validator); validation.ts ×4
232 > }
234 > type vTupleType<T extends IValidator<unknown>[]> = { [K in keyof T]: ValidatorType<T[K]> };
235 >
236 > class TupleValidator<T extends IValidator<unknown>[]> extends ValidatorBase<vTupleType<T>> {
237 > constructor(private readonly validators: T) {
238 super();
239 }
241 > validate(content: unknown): { content: vTupleType<T>; error: undefined } | { content: undefined; error: ValidationError } {
242 if (!Array.isArray(content)) {
243 return { content: undefined, error: { message: 'Expected array' } };
244 }
245
246 if (content.length !== this.validators.length) {
247 return { content: undefined, error: { message: `Expected tuple of length ${this.validators.length}, but got ${content.length}` } };
248 }
249
250 const result = [] as vTupleType<T>;
251 for (let i = 0; i < this.validators.length; i++) {
252 const validator = this.validators[i];
253 const { content: value, error } = validator.validate(content[i]);
254 if (error) {
255 return { content: undefined, error: { message: `Error in element ${i}: ${error.message}` } };
256 }
257 result.push(value);
258 }
259
260 return { content: result, error: undefined };
261 }
263 > getJSONSchema(): IJSONSchema {
264 return {
265 type: 'array',
266 items: this.validators.map(validator => validator.getJSONSchema()),
267 };
268 }
270 >
271 > export function vTuple<T extends IValidator<unknown>[]>(...validators: T): ValidatorBase<vTupleType<T>> {
272 return new TupleValidator(validators);
273 }
275 > class UnionValidator<T extends IValidator<unknown>[]> extends ValidatorBase<ValidatorType<T[number]>> {
276 > constructor(private readonly validators: T) {
277 super();
278 }
280 > validate(content: unknown): { content: ValidatorType<T[number]>; error: undefined } | { content: undefined; error: ValidationError } {
281 let lastError: ValidationError | undefined;
282 for (const validator of this.validators) {
283 const { content: value, error } = validator.validate(content);
284 if (!error) {
285 // eslint-disable-next-line local/code-no-any-casts, @typescript-eslint/no-explicit-any
286 return { content: value as any, error: undefined };
287 }
288
289 lastError = error;
290 }
291
292 return { content: undefined, error: lastError! };
293 }
295 > getJSONSchema(): IJSONSchema {
296 return {
297 oneOf: mapFilter(this.validators, validator => {
298 if (validator instanceof UndefinedValidator) {
299 return undefined;
300 }
301 return validator.getJSONSchema();
302 }),
303 };
304 }
306 >
307 > export function vUnion<T extends IValidator<unknown>[]>(...validators: T): ValidatorBase<ValidatorType<T[number]>> {
308 return new UnionValidator(validators);
309 }
311 > class EnumValidator<T extends string[]> extends ValidatorBase<T[number]> {
312 > constructor(private readonly values: T) {
313 super();
314 }
316 > validate(content: unknown): { content: T[number]; error: undefined } | { content: undefined; error: ValidationError } {
317 if (this.values.indexOf(content as string) === -1) {
318 return { content: undefined, error: { message: `Expected one of: ${this.values.join(', ')}` } };
319 }
320
321 return { content: content as T[number], error: undefined };
322 }
324 > getJSONSchema(): IJSONSchema {
325 return {
326 enum: this.values,
327 };
328 }
330 >
331 > export function vEnum<T extends string[]>(...values: T): ValidatorBase<T[number]> {
332 return new EnumValidator(values);
333 }
335 > class LiteralValidator<T extends string> extends ValidatorBase<T> {
336 > constructor(private readonly value: T) {
338 > }
340 > validate(content: unknown): { content: T; error: undefined } | { content: undefined; error: ValidationError } {
341 > if (content !== this.value) { localAgentHostMetadata.ts ×4
342 return { content: undefined, error: { message: `Expected: ${this.value}` } };
343 }
345 > return { content: content as T, error: undefined };
346 > }
348 > getJSONSchema(): IJSONSchema {
349 return {
350 const: this.value,
351 };
352 }
354 >
355 > export function vLiteral<T extends string>(value: T): ValidatorBase<T> {
356 > return new LiteralValidator(value); localAgentHostMetadata.ts ×14
357 > }
359 > class LazyValidator<T> extends ValidatorBase<T> {
360 > constructor(private readonly fn: () => IValidator<T>) {
361 super();
362 }
364 > validate(content: unknown): { content: T; error: undefined } | { content: undefined; error: ValidationError } {
365 return this.fn().validate(content);
366 }
368 > getJSONSchema(): IJSONSchema {
369 return this.fn().getJSONSchema();
370 }
372 >
373 > export function vLazy<T>(fn: () => IValidator<T>): ValidatorBase<T> {
374 return new LazyValidator(fn);
375 }
377 > class UseRefSchemaValidator<T> extends ValidatorBase<T> {
378 > constructor(
379 private readonly _ref: string,
380 private readonly _validator: IValidator<T>
381 ) {
382 super();
383 }
385 > validate(content: unknown): { content: T; error: undefined } | { content: undefined; error: ValidationError } {
386 return this._validator.validate(content);
387 }
389 > getJSONSchema(): IJSONSchema {
390 return { $ref: this._ref };
391 }
393 >
394 > export function vWithJsonSchemaRef<T>(ref: string, validator: IValidator<T>): ValidatorBase<T> {
395 return new UseRefSchemaValidator(ref, validator);
396 }