- 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
146 lines
5.0 KiB
JavaScript
146 lines
5.0 KiB
JavaScript
import { CastableBase, DynamicFunction, hasDomain, isDotAccessible, serializePrimitive } from "@ark/util";
|
|
import { registeredReference } from "./registry.js";
|
|
export class CompiledFunction extends CastableBase {
|
|
argNames;
|
|
body = "";
|
|
constructor(...args) {
|
|
super();
|
|
this.argNames = args;
|
|
for (const arg of args) {
|
|
if (arg in this) {
|
|
throw new Error(`Arg name '${arg}' would overwrite an existing property on FunctionBody`);
|
|
}
|
|
;
|
|
this[arg] = arg;
|
|
}
|
|
}
|
|
indentation = 0;
|
|
indent() {
|
|
this.indentation += 4;
|
|
return this;
|
|
}
|
|
dedent() {
|
|
this.indentation -= 4;
|
|
return this;
|
|
}
|
|
prop(key, optional = false) {
|
|
return compileLiteralPropAccess(key, optional);
|
|
}
|
|
index(key, optional = false) {
|
|
return indexPropAccess(`${key}`, optional);
|
|
}
|
|
line(statement) {
|
|
;
|
|
this.body += `${" ".repeat(this.indentation)}${statement}\n`;
|
|
return this;
|
|
}
|
|
const(identifier, expression) {
|
|
this.line(`const ${identifier} = ${expression}`);
|
|
return this;
|
|
}
|
|
let(identifier, expression) {
|
|
return this.line(`let ${identifier} = ${expression}`);
|
|
}
|
|
set(identifier, expression) {
|
|
return this.line(`${identifier} = ${expression}`);
|
|
}
|
|
if(condition, then) {
|
|
return this.block(`if (${condition})`, then);
|
|
}
|
|
elseIf(condition, then) {
|
|
return this.block(`else if (${condition})`, then);
|
|
}
|
|
else(then) {
|
|
return this.block("else", then);
|
|
}
|
|
/** Current index is "i" */
|
|
for(until, body, initialValue = 0) {
|
|
return this.block(`for (let i = ${initialValue}; ${until}; i++)`, body);
|
|
}
|
|
/** Current key is "k" */
|
|
forIn(object, body) {
|
|
return this.block(`for (const k in ${object})`, body);
|
|
}
|
|
block(prefix, contents, suffix = "") {
|
|
this.line(`${prefix} {`);
|
|
this.indent();
|
|
contents(this);
|
|
this.dedent();
|
|
return this.line(`}${suffix}`);
|
|
}
|
|
return(expression = "") {
|
|
return this.line(`return ${expression}`);
|
|
}
|
|
write(name = "anonymous", indent = 0) {
|
|
return `${name}(${this.argNames.join(", ")}) { ${indent ?
|
|
this.body
|
|
.split("\n")
|
|
.map(l => " ".repeat(indent) + `${l}`)
|
|
.join("\n")
|
|
: this.body} }`;
|
|
}
|
|
compile() {
|
|
return new DynamicFunction(...this.argNames, this.body);
|
|
}
|
|
}
|
|
export const compileSerializedValue = (value) => hasDomain(value, "object") || typeof value === "symbol" ?
|
|
registeredReference(value)
|
|
: serializePrimitive(value);
|
|
export const compileLiteralPropAccess = (key, optional = false) => {
|
|
if (typeof key === "string" && isDotAccessible(key))
|
|
return `${optional ? "?" : ""}.${key}`;
|
|
return indexPropAccess(serializeLiteralKey(key), optional);
|
|
};
|
|
export const serializeLiteralKey = (key) => typeof key === "symbol" ? registeredReference(key) : JSON.stringify(key);
|
|
export const indexPropAccess = (key, optional = false) => `${optional ? "?." : ""}[${key}]`;
|
|
export class NodeCompiler extends CompiledFunction {
|
|
traversalKind;
|
|
optimistic;
|
|
constructor(ctx) {
|
|
super("data", "ctx");
|
|
this.traversalKind = ctx.kind;
|
|
this.optimistic = ctx.optimistic === true;
|
|
}
|
|
invoke(node, opts) {
|
|
const arg = opts?.arg ?? this.data;
|
|
const requiresContext = typeof node === "string" ? true : this.requiresContextFor(node);
|
|
const id = typeof node === "string" ? node : node.id;
|
|
if (requiresContext)
|
|
return `${this.referenceToId(id, opts)}(${arg}, ${this.ctx})`;
|
|
return `${this.referenceToId(id, opts)}(${arg})`;
|
|
}
|
|
referenceToId(id, opts) {
|
|
const invokedKind = opts?.kind ?? this.traversalKind;
|
|
const base = `this.${id}${invokedKind}`;
|
|
return opts?.bind ? `${base}.bind(${opts?.bind})` : base;
|
|
}
|
|
requiresContextFor(node) {
|
|
return this.traversalKind === "Apply" || node.allowsRequiresContext;
|
|
}
|
|
initializeErrorCount() {
|
|
return this.const("errorCount", "ctx.currentErrorCount");
|
|
}
|
|
returnIfFail() {
|
|
return this.if("ctx.currentErrorCount > errorCount", () => this.return());
|
|
}
|
|
returnIfFailFast() {
|
|
return this.if("ctx.failFast && ctx.currentErrorCount > errorCount", () => this.return());
|
|
}
|
|
traverseKey(keyExpression, accessExpression, node) {
|
|
const requiresContext = this.requiresContextFor(node);
|
|
if (requiresContext)
|
|
this.line(`${this.ctx}.path.push(${keyExpression})`);
|
|
this.check(node, {
|
|
arg: accessExpression
|
|
});
|
|
if (requiresContext)
|
|
this.line(`${this.ctx}.path.pop()`);
|
|
return this;
|
|
}
|
|
check(node, opts) {
|
|
return this.traversalKind === "Allows" ?
|
|
this.if(`!${this.invoke(node, opts)}`, () => this.return(false))
|
|
: this.line(this.invoke(node, opts));
|
|
}
|
|
}
|