- 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
86 lines
3.4 KiB
JavaScript
86 lines
3.4 KiB
JavaScript
import { append, printable, throwParseError, unset } from "@ark/util";
|
|
import { BaseConstraint } from "../constraint.js";
|
|
import { flatRef } from "../node.js";
|
|
import { compileSerializedValue } from "../shared/compile.js";
|
|
import { Disjoint } from "../shared/disjoint.js";
|
|
import { intersectOrPipeNodes } from "../shared/intersections.js";
|
|
import { $ark } from "../shared/registry.js";
|
|
import { traverseKey } from "../shared/traversal.js";
|
|
export const intersectProps = (l, r, ctx) => {
|
|
if (l.key !== r.key)
|
|
return null;
|
|
const key = l.key;
|
|
let value = intersectOrPipeNodes(l.value, r.value, ctx);
|
|
const kind = l.required || r.required ? "required" : "optional";
|
|
if (value instanceof Disjoint) {
|
|
if (kind === "optional")
|
|
value = $ark.intrinsic.never.internal;
|
|
else {
|
|
// if either operand was optional, the Disjoint has to be treated as optional
|
|
return value.withPrefixKey(l.key, l.required && r.required ? "required" : "optional");
|
|
}
|
|
}
|
|
if (kind === "required") {
|
|
return ctx.$.node("required", {
|
|
key,
|
|
value
|
|
});
|
|
}
|
|
const defaultIntersection = l.hasDefault() ?
|
|
r.hasDefault() ?
|
|
l.default === r.default ?
|
|
l.default
|
|
: throwParseError(writeDefaultIntersectionMessage(l.default, r.default))
|
|
: l.default
|
|
: r.hasDefault() ? r.default
|
|
: unset;
|
|
return ctx.$.node("optional", {
|
|
key,
|
|
value,
|
|
// unset is stripped during parsing
|
|
default: defaultIntersection
|
|
});
|
|
};
|
|
export class BaseProp extends BaseConstraint {
|
|
required = this.kind === "required";
|
|
optional = this.kind === "optional";
|
|
impliedBasis = $ark.intrinsic.object.internal;
|
|
serializedKey = compileSerializedValue(this.key);
|
|
compiledKey = typeof this.key === "string" ? this.key : this.serializedKey;
|
|
flatRefs = append(this.value.flatRefs.map(ref => flatRef([this.key, ...ref.path], ref.node)), flatRef([this.key], this.value));
|
|
_transform(mapper, ctx) {
|
|
ctx.path.push(this.key);
|
|
const result = super._transform(mapper, ctx);
|
|
ctx.path.pop();
|
|
return result;
|
|
}
|
|
hasDefault() {
|
|
return "default" in this.inner;
|
|
}
|
|
traverseAllows = (data, ctx) => {
|
|
if (this.key in data) {
|
|
// ctx will be undefined if this node isn't context-dependent
|
|
return traverseKey(this.key, () => this.value.traverseAllows(data[this.key], ctx), ctx);
|
|
}
|
|
return this.optional;
|
|
};
|
|
traverseApply = (data, ctx) => {
|
|
if (this.key in data) {
|
|
traverseKey(this.key, () => this.value.traverseApply(data[this.key], ctx), ctx);
|
|
}
|
|
else if (this.hasKind("required"))
|
|
ctx.errorFromNodeContext(this.errorContext);
|
|
};
|
|
compile(js) {
|
|
js.if(`${this.serializedKey} in data`, () => js.traverseKey(this.serializedKey, `data${js.prop(this.key)}`, this.value));
|
|
if (this.hasKind("required")) {
|
|
js.else(() => js.traversalKind === "Apply" ?
|
|
js.line(`ctx.errorFromNodeContext(${this.compiledErrorContext})`)
|
|
: js.return(false));
|
|
}
|
|
if (js.traversalKind === "Allows")
|
|
js.return(true);
|
|
}
|
|
}
|
|
export const writeDefaultIntersectionMessage = (lValue, rValue) => `Invalid intersection of default values ${printable(lValue)} & ${printable(rValue)}`;
|