Files
headroom/frontend/node_modules/@ark/schema/out/parse.js
Santhosh Janardhanan de2d83092e feat: Reinitialize frontend with SvelteKit and TypeScript
- 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
2026-02-17 16:19:59 -05:00

216 lines
7.9 KiB
JavaScript

import { domainDescriptions, entriesOf, flatMorph, hasDomain, isArray, isEmptyObject, printable, throwInternalError, throwParseError, unset } from "@ark/util";
import { nodeClassesByKind, nodeImplementationsByKind } from "./kinds.js";
import { Disjoint } from "./shared/disjoint.js";
import { constraintKeys, defaultValueSerializer, isNodeKind, precedenceOfKind } from "./shared/implement.js";
import { $ark } from "./shared/registry.js";
import { hasArkKind, isNode } from "./shared/utils.js";
export const schemaKindOf = (schema, allowedKinds) => {
const kind = discriminateRootKind(schema);
if (allowedKinds && !allowedKinds.includes(kind)) {
return throwParseError(`Root of kind ${kind} should be one of ${allowedKinds}`);
}
return kind;
};
const discriminateRootKind = (schema) => {
if (hasArkKind(schema, "root"))
return schema.kind;
if (typeof schema === "string") {
return (schema[0] === "$" ? "alias"
: schema in domainDescriptions ? "domain"
: "proto");
}
if (typeof schema === "function")
return "proto";
// throw at end of function
if (typeof schema !== "object" || schema === null)
return throwParseError(writeInvalidSchemaMessage(schema));
if ("morphs" in schema)
return "morph";
if ("branches" in schema || isArray(schema))
return "union";
if ("unit" in schema)
return "unit";
if ("reference" in schema)
return "alias";
const schemaKeys = Object.keys(schema);
if (schemaKeys.length === 0 || schemaKeys.some(k => k in constraintKeys))
return "intersection";
if ("proto" in schema)
return "proto";
if ("domain" in schema)
return "domain";
return throwParseError(writeInvalidSchemaMessage(schema));
};
export const writeInvalidSchemaMessage = (schema) => `${printable(schema)} is not a valid type schema`;
const nodeCountsByPrefix = {};
const serializeListableChild = (listableNode) => isArray(listableNode) ?
listableNode.map(node => node.collapsibleJson)
: listableNode.collapsibleJson;
export const nodesByRegisteredId = {};
$ark.nodesByRegisteredId = nodesByRegisteredId;
export const registerNodeId = (prefix) => {
nodeCountsByPrefix[prefix] ??= 0;
return `${prefix}${++nodeCountsByPrefix[prefix]}`;
};
export const parseNode = (ctx) => {
const impl = nodeImplementationsByKind[ctx.kind];
const configuredSchema = impl.applyConfig?.(ctx.def, ctx.$.resolvedConfig) ?? ctx.def;
const inner = {};
const { meta: metaSchema, ...innerSchema } = configuredSchema;
const meta = metaSchema === undefined ? {}
: typeof metaSchema === "string" ? { description: metaSchema }
: metaSchema;
// ensure node entries are parsed in order of precedence, with non-children
// parsed first
const innerSchemaEntries = entriesOf(innerSchema)
.sort(([lKey], [rKey]) => isNodeKind(lKey) ?
isNodeKind(rKey) ? precedenceOfKind(lKey) - precedenceOfKind(rKey)
: 1
: isNodeKind(rKey) ? -1
: lKey < rKey ? -1
: 1)
.filter(([k, v]) => {
// move meta. prefixed props to meta, overwriting existing nested
// props of the same name if they exist
if (k.startsWith("meta.")) {
const metaKey = k.slice(5);
meta[metaKey] = v;
return false;
}
return true;
});
for (const entry of innerSchemaEntries) {
const k = entry[0];
const keyImpl = impl.keys[k];
if (!keyImpl)
return throwParseError(`Key ${k} is not valid on ${ctx.kind} schema`);
const v = keyImpl.parse ? keyImpl.parse(entry[1], ctx) : entry[1];
if (v !== unset && (v !== undefined || keyImpl.preserveUndefined))
inner[k] = v;
}
if (impl.reduce && !ctx.prereduced) {
const reduced = impl.reduce(inner, ctx.$);
if (reduced) {
if (reduced instanceof Disjoint)
return reduced.throw();
// we can't cache this reduction for now in case the reduction involved
// impliedSiblings
return withMeta(reduced, meta);
}
}
const node = createNode({
id: ctx.id,
kind: ctx.kind,
inner,
meta,
$: ctx.$
});
return node;
};
export const createNode = ({ id, kind, inner, meta, $, ignoreCache }) => {
const impl = nodeImplementationsByKind[kind];
const innerEntries = entriesOf(inner);
const children = [];
let innerJson = {};
for (const [k, v] of innerEntries) {
const keyImpl = impl.keys[k];
const serialize = keyImpl.serialize ??
(keyImpl.child ? serializeListableChild : defaultValueSerializer);
innerJson[k] = serialize(v);
if (keyImpl.child === true) {
const listableNode = v;
if (isArray(listableNode))
children.push(...listableNode);
else
children.push(listableNode);
}
else if (typeof keyImpl.child === "function")
children.push(...keyImpl.child(v));
}
if (impl.finalizeInnerJson)
innerJson = impl.finalizeInnerJson(innerJson);
let json = { ...innerJson };
let metaJson = {};
if (!isEmptyObject(meta)) {
metaJson = flatMorph(meta, (k, v) => [
k,
k === "examples" ? v : defaultValueSerializer(v)
]);
json.meta = possiblyCollapse(metaJson, "description", true);
}
innerJson = possiblyCollapse(innerJson, impl.collapsibleKey, false);
const innerHash = JSON.stringify({ kind, ...innerJson });
json = possiblyCollapse(json, impl.collapsibleKey, false);
const collapsibleJson = possiblyCollapse(json, impl.collapsibleKey, true);
const hash = JSON.stringify({ kind, ...json });
// we have to wait until after reduction to return a cached entry,
// since reduction can add impliedSiblings
if ($.nodesByHash[hash] && !ignoreCache)
return $.nodesByHash[hash];
const attachments = {
id,
kind,
impl,
inner,
innerEntries,
innerJson,
innerHash,
meta,
metaJson,
json,
hash,
collapsibleJson: collapsibleJson,
children
};
if (kind !== "intersection") {
for (const k in inner)
if (k !== "in" && k !== "out")
attachments[k] = inner[k];
}
const node = new nodeClassesByKind[kind](attachments, $);
return ($.nodesByHash[hash] = node);
};
export const withId = (node, id) => {
if (node.id === id)
return node;
if (isNode(nodesByRegisteredId[id]))
throwInternalError(`Unexpected attempt to overwrite node id ${id}`);
// have to ignore cache to force creation of new potentially cyclic id
return createNode({
id,
kind: node.kind,
inner: node.inner,
meta: node.meta,
$: node.$,
ignoreCache: true
});
};
export const withMeta = (node, meta, id) => {
if (id && isNode(nodesByRegisteredId[id]))
throwInternalError(`Unexpected attempt to overwrite node id ${id}`);
return createNode({
id: id ?? registerNodeId(meta.alias ?? node.kind),
kind: node.kind,
inner: node.inner,
meta,
$: node.$
});
};
const possiblyCollapse = (json, toKey, allowPrimitive) => {
const collapsibleKeys = Object.keys(json);
if (collapsibleKeys.length === 1 && collapsibleKeys[0] === toKey) {
const collapsed = json[toKey];
if (allowPrimitive)
return collapsed;
if (
// if the collapsed value is still an object
hasDomain(collapsed, "object") &&
// and the JSON did not include any implied keys
(Object.keys(collapsed).length === 1 || Array.isArray(collapsed))) {
// we can replace it with its collapsed value
return collapsed;
}
}
return json;
};