- 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
104 lines
4.1 KiB
JavaScript
104 lines
4.1 KiB
JavaScript
import { isKeyOf, throwParseError } from "@ark/util";
|
|
import { InternalPrimitiveConstraint } from "../constraint.js";
|
|
export class BaseRange extends InternalPrimitiveConstraint {
|
|
boundOperandKind = operandKindsByBoundKind[this.kind];
|
|
compiledActual = this.boundOperandKind === "value" ? `data`
|
|
: this.boundOperandKind === "length" ? `data.length`
|
|
: `data.valueOf()`;
|
|
comparator = compileComparator(this.kind, this.exclusive);
|
|
numericLimit = this.rule.valueOf();
|
|
expression = `${this.comparator} ${this.rule}`;
|
|
compiledCondition = `${this.compiledActual} ${this.comparator} ${this.numericLimit}`;
|
|
compiledNegation = `${this.compiledActual} ${negatedComparators[this.comparator]} ${this.numericLimit}`;
|
|
// we need to compute stringLimit before errorContext, which references it
|
|
// transitively through description for date bounds
|
|
stringLimit = this.boundOperandKind === "date" ?
|
|
dateLimitToString(this.numericLimit)
|
|
: `${this.numericLimit}`;
|
|
limitKind = this.comparator["0"] === "<" ? "upper" : "lower";
|
|
isStricterThan(r) {
|
|
const thisLimitIsStricter = this.limitKind === "upper" ?
|
|
this.numericLimit < r.numericLimit
|
|
: this.numericLimit > r.numericLimit;
|
|
return (thisLimitIsStricter ||
|
|
(this.numericLimit === r.numericLimit &&
|
|
this.exclusive === true &&
|
|
!r.exclusive));
|
|
}
|
|
overlapsRange(r) {
|
|
if (this.isStricterThan(r))
|
|
return false;
|
|
if (this.numericLimit === r.numericLimit && (this.exclusive || r.exclusive))
|
|
return false;
|
|
return true;
|
|
}
|
|
overlapIsUnit(r) {
|
|
return (this.numericLimit === r.numericLimit && !this.exclusive && !r.exclusive);
|
|
}
|
|
}
|
|
const negatedComparators = {
|
|
"<": ">=",
|
|
"<=": ">",
|
|
">": "<=",
|
|
">=": "<"
|
|
};
|
|
export const boundKindPairsByLower = {
|
|
min: "max",
|
|
minLength: "maxLength",
|
|
after: "before"
|
|
};
|
|
export const parseExclusiveKey = {
|
|
// omit key with value false since it is the default
|
|
parse: (flag) => flag || undefined
|
|
};
|
|
export const createLengthSchemaNormalizer = (kind) => (schema) => {
|
|
if (typeof schema === "number")
|
|
return { rule: schema };
|
|
const { exclusive, ...normalized } = schema;
|
|
return exclusive ?
|
|
{
|
|
...normalized,
|
|
rule: kind === "minLength" ? normalized.rule + 1 : normalized.rule - 1
|
|
}
|
|
: normalized;
|
|
};
|
|
export const createDateSchemaNormalizer = (kind) => (schema) => {
|
|
if (typeof schema === "number" ||
|
|
typeof schema === "string" ||
|
|
schema instanceof Date)
|
|
return { rule: schema };
|
|
const { exclusive, ...normalized } = schema;
|
|
if (!exclusive)
|
|
return normalized;
|
|
const numericLimit = typeof normalized.rule === "number" ? normalized.rule
|
|
: typeof normalized.rule === "string" ?
|
|
new Date(normalized.rule).valueOf()
|
|
: normalized.rule.valueOf();
|
|
return exclusive ?
|
|
{
|
|
...normalized,
|
|
rule: kind === "after" ? numericLimit + 1 : numericLimit - 1
|
|
}
|
|
: normalized;
|
|
};
|
|
export const parseDateLimit = (limit) => typeof limit === "string" || typeof limit === "number" ?
|
|
new Date(limit)
|
|
: limit;
|
|
export const writeInvalidLengthBoundMessage = (kind, limit) => `${kind} bound must be a positive integer (was ${limit})`;
|
|
export const createLengthRuleParser = (kind) => (limit) => {
|
|
if (!Number.isInteger(limit) || limit < 0)
|
|
throwParseError(writeInvalidLengthBoundMessage(kind, limit));
|
|
return limit;
|
|
};
|
|
const operandKindsByBoundKind = {
|
|
min: "value",
|
|
max: "value",
|
|
minLength: "length",
|
|
maxLength: "length",
|
|
after: "date",
|
|
before: "date"
|
|
};
|
|
export const compileComparator = (kind, exclusive) => `${isKeyOf(kind, boundKindPairsByLower) ? ">" : "<"}${exclusive ? "" : "="}`;
|
|
export const dateLimitToString = (limit) => typeof limit === "string" ? limit : new Date(limit).toLocaleString();
|
|
export const writeUnboundableMessage = (root) => `Bounded expression ${root} must be exactly one of number, string, Array, or Date`;
|