Files
pikasTech-unidesk/scripts/src/preview.ts
T
2026-05-05 07:57:29 +00:00

85 lines
2.7 KiB
TypeScript

export interface PreviewOptions {
maxDepth?: number;
maxArrayItems?: number;
maxObjectKeys?: number;
maxStringLength?: number;
}
const DEFAULT_PREVIEW_OPTIONS: Required<PreviewOptions> = {
maxDepth: 4,
maxArrayItems: 6,
maxObjectKeys: 32,
maxStringLength: 1000,
};
function optionsWithDefaults(options: PreviewOptions = {}): Required<PreviewOptions> {
return {
maxDepth: options.maxDepth ?? DEFAULT_PREVIEW_OPTIONS.maxDepth,
maxArrayItems: options.maxArrayItems ?? DEFAULT_PREVIEW_OPTIONS.maxArrayItems,
maxObjectKeys: options.maxObjectKeys ?? DEFAULT_PREVIEW_OPTIONS.maxObjectKeys,
maxStringLength: options.maxStringLength ?? DEFAULT_PREVIEW_OPTIONS.maxStringLength,
};
}
export function jsonByteLength(value: unknown): number {
try {
return Buffer.byteLength(JSON.stringify(value), "utf8");
} catch {
return Buffer.byteLength(String(value), "utf8");
}
}
export function previewJson(value: unknown, options: PreviewOptions = {}, depth = 0): unknown {
const limits = optionsWithDefaults(options);
if (value === null || typeof value === "number" || typeof value === "boolean") return value;
if (typeof value === "string") {
if (value.length <= limits.maxStringLength) return value;
return {
_previewType: "string",
length: value.length,
text: value.slice(0, limits.maxStringLength),
truncated: true,
};
}
if (typeof value !== "object") return String(value);
if (depth >= limits.maxDepth) {
return {
_previewType: Array.isArray(value) ? "array" : "object",
truncated: true,
...(Array.isArray(value) ? { length: value.length } : { keys: Object.keys(value as Record<string, unknown>).length }),
};
}
if (Array.isArray(value)) {
return {
_previewType: "array",
length: value.length,
items: value.slice(0, limits.maxArrayItems).map((item) => previewJson(item, limits, depth + 1)),
truncatedItems: Math.max(0, value.length - limits.maxArrayItems),
};
}
const record = value as Record<string, unknown>;
const entries = Object.entries(record);
const preview: Record<string, unknown> = {};
for (const [key, item] of entries.slice(0, limits.maxObjectKeys)) {
preview[key] = previewJson(item, limits, depth + 1);
}
if (entries.length > limits.maxObjectKeys) {
preview._preview = {
truncatedKeys: entries.length - limits.maxObjectKeys,
totalKeys: entries.length,
};
}
return preview;
}
export function boundedJsonDetail(value: unknown, maxBytes: number, options: PreviewOptions = {}): unknown {
const originalBytes = jsonByteLength(value);
if (originalBytes <= maxBytes) return value;
return {
_truncated: true,
originalBytes,
maxBytes,
preview: previewJson(value, options),
};
}