Files
headroom/frontend/node_modules/@ark/util/out/serialize.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

151 lines
5.5 KiB
JavaScript

import { domainOf } from "./domain.js";
import { serializePrimitive } from "./primitive.js";
import { stringAndSymbolicEntriesOf } from "./records.js";
import { isDotAccessible, register } from "./registry.js";
export const snapshot = (data, opts = {}) => _serialize(data, {
onUndefined: `$ark.undefined`,
onBigInt: n => `$ark.bigint-${n}`,
...opts
}, []);
export const print = (data, opts) => console.log(printable(data, opts));
export const printable = (data, opts) => {
switch (domainOf(data)) {
case "object":
const o = data;
const ctorName = o.constructor?.name ?? "Object";
return (ctorName === "Object" || ctorName === "Array" ?
opts?.quoteKeys === false ?
stringifyUnquoted(o, opts?.indent ?? 0, "")
: JSON.stringify(_serialize(o, printableOpts, []), null, opts?.indent)
: stringifyUnquoted(o, opts?.indent ?? 0, ""));
case "symbol":
return printableOpts.onSymbol(data);
default:
return serializePrimitive(data);
}
};
const stringifyUnquoted = (value, indent, currentIndent) => {
if (typeof value === "function")
return printableOpts.onFunction(value);
if (typeof value !== "object" || value === null)
return serializePrimitive(value);
const nextIndent = currentIndent + " ".repeat(indent);
if (Array.isArray(value)) {
if (value.length === 0)
return "[]";
const items = value
.map(item => stringifyUnquoted(item, indent, nextIndent))
.join(",\n" + nextIndent);
return indent ? `[\n${nextIndent}${items}\n${currentIndent}]` : `[${items}]`;
}
const ctorName = value.constructor?.name ?? "Object";
if (ctorName === "Object") {
const keyValues = stringAndSymbolicEntriesOf(value).map(([key, val]) => {
const stringifiedKey = typeof key === "symbol" ? printableOpts.onSymbol(key)
: isDotAccessible(key) ? key
: JSON.stringify(key);
const stringifiedValue = stringifyUnquoted(val, indent, nextIndent);
return `${nextIndent}${stringifiedKey}: ${stringifiedValue}`;
});
if (keyValues.length === 0)
return "{}";
return indent ?
`{\n${keyValues.join(",\n")}\n${currentIndent}}`
: `{${keyValues.join(", ")}}`;
}
if (value instanceof Date)
return describeCollapsibleDate(value);
if ("expression" in value && typeof value.expression === "string")
return value.expression;
return ctorName;
};
const printableOpts = {
onCycle: () => "(cycle)",
onSymbol: v => `Symbol(${register(v)})`,
onFunction: v => `Function(${register(v)})`
};
const _serialize = (data, opts, seen) => {
switch (domainOf(data)) {
case "object": {
const o = data;
if ("toJSON" in o && typeof o.toJSON === "function")
return o.toJSON();
if (typeof o === "function")
return printableOpts.onFunction(o);
if (seen.includes(o))
return "(cycle)";
const nextSeen = [...seen, o];
if (Array.isArray(o))
return o.map(item => _serialize(item, opts, nextSeen));
if (o instanceof Date)
return o.toDateString();
const result = {};
for (const k in o)
result[k] = _serialize(o[k], opts, nextSeen);
for (const s of Object.getOwnPropertySymbols(o)) {
result[opts.onSymbol?.(s) ?? s.toString()] = _serialize(o[s], opts, nextSeen);
}
return result;
}
case "symbol":
return printableOpts.onSymbol(data);
case "bigint":
return opts.onBigInt?.(data) ?? `${data}n`;
case "undefined":
return opts.onUndefined ?? "undefined";
case "string":
return data.replace(/\\/g, "\\\\");
default:
return data;
}
};
/**
* Converts a Date instance to a human-readable description relative to its precision
*/
export const describeCollapsibleDate = (date) => {
const year = date.getFullYear();
const month = date.getMonth();
const dayOfMonth = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
const milliseconds = date.getMilliseconds();
if (month === 0 &&
dayOfMonth === 1 &&
hours === 0 &&
minutes === 0 &&
seconds === 0 &&
milliseconds === 0)
return `${year}`;
const datePortion = `${months[month]} ${dayOfMonth}, ${year}`;
if (hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 0)
return datePortion;
let timePortion = date.toLocaleTimeString();
const suffix = timePortion.endsWith(" AM") || timePortion.endsWith(" PM") ?
timePortion.slice(-3)
: "";
if (suffix)
timePortion = timePortion.slice(0, -suffix.length);
if (milliseconds)
timePortion += `.${pad(milliseconds, 3)}`;
else if (timeWithUnnecessarySeconds.test(timePortion))
timePortion = timePortion.slice(0, -3);
return `${timePortion + suffix}, ${datePortion}`;
};
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
];
const timeWithUnnecessarySeconds = /:\d\d:00$/;
const pad = (value, length) => String(value).padStart(length, "0");