- Delete old Vite+Svelte frontend - Initialize new SvelteKit project with TypeScript - Configure Tailwind CSS v4 + DaisyUI - Implement JWT authentication with auto-refresh - Create login page with form validation (Zod) - Add protected route guards - Update Docker configuration for single-stage build - Add E2E tests with Playwright (6/11 passing) - Fix Svelte 5 reactivity with $state() runes Known issues: - 5 E2E tests failing (timing/async issues) - Token refresh implementation needs debugging - Validation error display timing
103 lines
3.7 KiB
JavaScript
103 lines
3.7 KiB
JavaScript
import { schemaInfo } from './schemaInfo.js';
|
|
import { merge as deepMerge } from 'ts-deepmerge';
|
|
export function constraints(schema) {
|
|
return _constraints(schemaInfo(schema, false, []), []);
|
|
}
|
|
function merge(...constraints) {
|
|
const filtered = constraints.filter((c) => !!c);
|
|
if (!filtered.length)
|
|
return undefined;
|
|
if (filtered.length == 1)
|
|
return filtered[0];
|
|
return deepMerge(...filtered);
|
|
}
|
|
function _constraints(info, path) {
|
|
if (!info)
|
|
return undefined;
|
|
let output = undefined;
|
|
// Union
|
|
if (info.union && info.union.length) {
|
|
const infos = info.union.map((s) => schemaInfo(s, info.isOptional, path));
|
|
const merged = infos.map((i) => _constraints(i, path));
|
|
output = merge(output, ...merged);
|
|
// Delete required if any part of the union is optional
|
|
if (output &&
|
|
(info.isNullable || info.isOptional || infos.some((i) => i?.isNullable || i?.isOptional))) {
|
|
delete output.required;
|
|
}
|
|
}
|
|
// Arrays
|
|
if (info.array) {
|
|
output = merge(output, ...info.array.map((i) => _constraints(schemaInfo(i, info.isOptional, path), path)));
|
|
}
|
|
// Objects
|
|
if (info.properties) {
|
|
const obj = {};
|
|
for (const [key, prop] of Object.entries(info.properties)) {
|
|
const propInfo = schemaInfo(prop, !info.required?.includes(key) || prop.default !== undefined, [key]);
|
|
const propConstraint = _constraints(propInfo, [...path, key]);
|
|
if (typeof propConstraint === 'object' && Object.values(propConstraint).length > 0) {
|
|
obj[key] = propConstraint;
|
|
}
|
|
}
|
|
output = merge(output, obj);
|
|
}
|
|
return output ?? constraint(info);
|
|
}
|
|
function constraint(info) {
|
|
const output = {};
|
|
const schema = info.schema;
|
|
const type = schema.type;
|
|
const format = schema.format;
|
|
// Must be before type check
|
|
if (type == 'integer' &&
|
|
format == 'unix-time' //||
|
|
//format == 'date-time' ||
|
|
//format == 'date' ||
|
|
//format == 'time'
|
|
) {
|
|
const date = schema;
|
|
if (date.minimum !== undefined)
|
|
output.min = new Date(date.minimum).toISOString();
|
|
if (date.maximum !== undefined)
|
|
output.max = new Date(date.maximum).toISOString();
|
|
}
|
|
else if (type == 'string') {
|
|
const str = schema;
|
|
const patterns = [
|
|
str.pattern,
|
|
...(str.allOf ? str.allOf.map((s) => (typeof s == 'boolean' ? undefined : s.pattern)) : [])
|
|
].filter((s) => s !== undefined);
|
|
if (patterns.length > 0)
|
|
output.pattern = patterns[0];
|
|
if (str.minLength !== undefined)
|
|
output.minlength = str.minLength;
|
|
if (str.maxLength !== undefined)
|
|
output.maxlength = str.maxLength;
|
|
}
|
|
else if (type == 'number' || type == 'integer') {
|
|
const num = schema;
|
|
if (num.minimum !== undefined)
|
|
output.min = num.minimum;
|
|
else if (num.exclusiveMinimum !== undefined)
|
|
output.min = num.exclusiveMinimum + (type == 'integer' ? 1 : Number.MIN_VALUE);
|
|
if (num.maximum !== undefined)
|
|
output.max = num.maximum;
|
|
else if (num.exclusiveMaximum !== undefined)
|
|
output.max = num.exclusiveMaximum - (type == 'integer' ? 1 : Number.MIN_VALUE);
|
|
if (num.multipleOf !== undefined)
|
|
output.step = num.multipleOf;
|
|
}
|
|
else if (type == 'array') {
|
|
const arr = schema;
|
|
if (arr.minItems !== undefined)
|
|
output.min = arr.minItems;
|
|
if (arr.maxItems !== undefined)
|
|
output.max = arr.maxItems;
|
|
}
|
|
if (!info.isNullable && !info.isOptional) {
|
|
output.required = true;
|
|
}
|
|
return Object.keys(output).length > 0 ? output : undefined;
|
|
}
|