openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
62 lines (61 loc) • 2.62 kB
JavaScript
//#region packages/normalization-core/src/stable-stringify.ts
const preserveString = (value) => value;
/** Deterministically stringifies values, optionally normalizing strings before key ordering. */
function stableStringify(value, normalizeString = preserveString) {
return stringifyStableValue(value, /* @__PURE__ */ new WeakSet(), normalizeString);
}
function stringifyStableValue(value, stack, normalizeString) {
if (value === null || value === void 0) return String(value);
if (typeof value === "number" && !Number.isFinite(value)) return JSON.stringify(String(value));
if (typeof value === "bigint") return JSON.stringify(value.toString());
if (typeof value === "string") return JSON.stringify(normalizeString(value));
if (typeof value !== "object") return JSON.stringify(value) ?? "null";
if (stack.has(value)) return JSON.stringify("[Circular]");
stack.add(value);
try {
return stringifyObjectValue(value, stack, normalizeString);
} finally {
stack.delete(value);
}
}
function stringifyObjectValue(value, stack, normalizeString) {
if (value instanceof Error) return stringifyStableValue({
name: value.name,
message: value.message,
stack: value.stack
}, stack, normalizeString);
if (value instanceof Uint8Array) return stringifyStableValue({
type: "Uint8Array",
data: encodeBase64(value)
}, stack, normalizeString);
if (Array.isArray(value)) {
const serializedEntries = [];
for (const entry of value) serializedEntries.push(stringifyStableValue(entry, stack, normalizeString));
return `[${serializedEntries.join(",")}]`;
}
const record = value;
if (normalizeString === preserveString) {
const fields = [];
for (const key of Object.keys(record).toSorted()) fields.push(`${JSON.stringify(key)}:${stringifyStableValue(record[key], stack, normalizeString)}`);
return `{${fields.join(",")}}`;
}
const entries = Object.keys(record).map((key) => ({
key,
normalizedKey: normalizeString(key)
})).toSorted((left, right) => {
return compareStableStrings(left.normalizedKey, right.normalizedKey) || compareStableStrings(left.key, right.key);
});
const serializedFields = [];
for (const { key, normalizedKey } of entries) serializedFields.push(`${JSON.stringify(normalizedKey)}:${stringifyStableValue(record[key], stack, normalizeString)}`);
return `{${serializedFields.join(",")}}`;
}
function encodeBase64(value) {
let binary = "";
for (const byte of value) binary += String.fromCharCode(byte);
return btoa(binary);
}
function compareStableStrings(left, right) {
return left < right ? -1 : left > right ? 1 : 0;
}
//#endregion
export { stableStringify as t };