Generated from services/crossfire/node/packages/contracts/src/schema/validation.ts at
52bf0279Definitions#
These TypeScript definitions document server validation. check.object rejects unknown fields. Custom functions and refinements specify additional checks.
Imported definitions#
Parser#
export type Parser<T> = (input: unknown) => Result<T>;Related definitions:
Parsed#
export type Parsed<Check> = Check extends Parser<infer Value> ? Value : never;Related definitions:
refine#
export function refine<Value>(parse: Parser<Value>, accepts: (value: Value) => boolean, message: string): Parser<Value> {
return input => { const parsed = parse(input); return !parsed.ok || accepts(parsed.value) ? parsed : failure('invalid', message); };
}Related definitions:
text#
export function text(maximum = 255, minimum = 1): Parser<string> {
return input => typeof input === 'string' && input.length >= minimum && input.length <= maximum && input.isWellFormed() && !/[\u0000-\u001f\u007f]/u.test(input)
? success(input) : failure('invalid', 'Expected text of ' + minimum + '–' + maximum + ' characters');
}Related definitions:
integer#
export function integer(minimum = 0, maximum = Number.MAX_SAFE_INTEGER): Parser<number> {
return input => typeof input === 'number' && Number.isSafeInteger(input) && input >= minimum && input <= maximum ? success(input) : failure('invalid', 'Expected a bounded integer');
}Related definitions:
boolean#
boolean: Parser<boolean> = input => typeof input === 'boolean' ? success(input) : failure('invalid', 'Expected a boolean')Related definitions:
choice#
export function choice<const Values extends readonly string[]>(...values: Values): Parser<Values[number]> {
return input => typeof input === 'string' && values.includes(input) ? success(input) : failure('invalid', 'Choose one of: ' + values.join(', '));
}Related definitions:
nullable#
export function nullable<Value>(parse: Parser<Value>): Parser<Value | null> { return input => input === null ? success(null) : parse(input); }Related definitions:
optional#
export function optional<Value>(parse: Parser<Value>): Parser<Value | undefined> { return input => input === undefined ? success(undefined) : parse(input); }Related definitions:
list#
export function list<Value>(parse: Parser<Value>, maximum = 100, minimum = 0): Parser<Value[]> {
return input => {
if (!Array.isArray(input) || input.length < minimum || input.length > maximum)
return failure('invalid', 'Expected a bounded list');
const values: Value[] = [];
for (const item of input) {
const result = parse(item);
if (!result.ok)
return result;
values.push(result.value);
}
return success(values);
};
}Related definitions:
Shape#
type Shape = Record<string, Parser<unknown>>;Related definitions:
OptionalKeys#
type OptionalKeys<Fields extends Shape> = {
[Key in keyof Fields]: undefined extends Parsed<Fields[Key]> ? Key : never;
}[keyof Fields];Related definitions:
ObjectValue#
type ObjectValue<Fields extends Shape> = {
[Key in Exclude<keyof Fields, OptionalKeys<Fields>>]: Parsed<Fields[Key]>;
} & {
[Key in OptionalKeys<Fields>]?: Exclude<Parsed<Fields[Key]>, undefined>;
};Related definitions:
object#
export function object<const Fields extends Shape>(fields: Fields): Parser<ObjectValue<Fields>> {
return input => {
if (!input || typeof input !== 'object' || Array.isArray(input))
return failure('invalid', 'Expected an object');
const record = input as Record<string, unknown>;
if (Object.keys(record).some(key => !Object.hasOwn(fields, key)))
return failure('invalid', 'Unknown request fields');
const output: Record<string, unknown> = {};
for (const [key, parse] of Object.entries(fields)) {
const parsed = parse(record[key]);
if (!parsed.ok)
return { ok: false, error: { code: parsed.error.code, message: key + ': ' + parsed.error.message, fieldErrors: { [key]: parsed.error.message } } };
if (parsed.value !== undefined)
output[key] = parsed.value;
}
return success(output as ObjectValue<Fields>);
};
}Related definitions:
identifier#
identifier: Parser<string> = input => typeof input === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/u.test(input)
? success(input) : failure('invalid', 'Expected an opaque UUID')Related definitions:
name#
name: Parser<string> = input => typeof input === 'string' && /^[a-z][a-z0-9-]{0,39}$/u.test(input) ? success(input) : failure('invalid', 'Expected a lowercase name of 1–40 characters')Related definitions:
matrixUserId#
matrixUserId: Parser<string> = input => typeof input === 'string' && input.length <= 255 && input.isWellFormed() && /^@[^:\s]+:[^\s]+$/u.test(input) ? success(input) : failure('invalid', 'Expected a Matrix user ID')Related definitions:
roomId#
roomId: Parser<string> = input => typeof input === 'string' && input.length <= 255 && input.isWellFormed() && /^!\S+$/u.test(input) ? success(input) : failure('invalid', 'Expected an immutable Matrix room ID')Related definitions:
eventId#
eventId: Parser<string> = input => typeof input === 'string' && input.length <= 255 && input.isWellFormed() && /^\$\S+$/u.test(input) ? success(input) : failure('invalid', 'Expected a Matrix event ID')Related definitions:
secret#
secret: Parser<string> = input => typeof input === 'string' && /^[A-Za-z0-9_-]{43}$/u.test(input) ? success(input) : failure('invalid', 'Expected a private 32-byte claim')Related definitions:
empty#
empty = object({})Related definitions:
status#
status = choice('active', 'suspended', 'revoked')Related definitions:
wakeMode#
wakeMode = choice('selected', 'humans', 'everyone')Related definitions:
plainText#
export function plainText(maximumBytes: number): Parser<string> {
return input => typeof input === 'string' && input.isWellFormed() && new TextEncoder().encode(input).length <= maximumBytes && !/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(input)
? success(input) : failure('invalid', 'Expected plain text up to ' + maximumBytes + ' UTF-8 bytes');
}Related definitions:
multiline#
multiline = plainText(8192)Related definitions: