74 lines
2.0 KiB
TypeScript
74 lines
2.0 KiB
TypeScript
import { BSON } from "https://esm.sh/bson@6.2.0";
|
|
|
|
// BSON doesn't support top-level primitives, so we need to wrap them in an object
|
|
const topLevelValueKey = "$_tl";
|
|
|
|
// BSON doesn't support undefined, so we need to encode it as a "magic" string
|
|
const undefinedPlaceHolder = "$_undefined_$";
|
|
|
|
/**
|
|
* BSON encoder, but also supporting "edge cases" like encoding strings, numbers, etc.
|
|
* @param obj
|
|
* @returns
|
|
*/
|
|
export function encodeBSON(obj: any): Uint8Array {
|
|
if (
|
|
obj === undefined || obj === null ||
|
|
!(typeof obj === "object" && obj.constructor === Object)
|
|
) {
|
|
obj = { [topLevelValueKey]: obj };
|
|
}
|
|
obj = traverseAndRewriteJSON(obj, (val) => {
|
|
if (val === undefined) {
|
|
return undefinedPlaceHolder;
|
|
}
|
|
return val;
|
|
});
|
|
return BSON.serialize(obj);
|
|
}
|
|
|
|
export function decodeBSON(data: Uint8Array): any {
|
|
let result = BSON.deserialize(data);
|
|
// For whatever reason the BSON library doesn't unwrap binary blobs automatically
|
|
result = traverseAndRewriteJSON(result, (val) => {
|
|
if (typeof val?.value === "function") {
|
|
return val.value();
|
|
} else if (val === undefinedPlaceHolder) {
|
|
return undefined;
|
|
}
|
|
return val;
|
|
});
|
|
if (Object.hasOwn(result, topLevelValueKey)) {
|
|
return result[topLevelValueKey];
|
|
} else {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Traverses and rewrites an object recursively.
|
|
*
|
|
* @param obj - The object to traverse and rewrite.
|
|
* @param rewrite - The function to apply for rewriting each value.
|
|
* @returns The rewritten object.
|
|
*/
|
|
export function traverseAndRewriteJSON(
|
|
obj: any,
|
|
rewrite: (val: any) => any,
|
|
): any {
|
|
// Apply rewrite to object as a whole
|
|
obj = rewrite(obj);
|
|
// Recurse down if this is an array or a "plain object"
|
|
if (
|
|
obj && (Array.isArray(obj) ||
|
|
(typeof obj === "object" && obj.constructor === Object))
|
|
) {
|
|
const keys = Object.keys(obj);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i];
|
|
obj[key] = traverseAndRewriteJSON(obj[key], rewrite);
|
|
}
|
|
}
|
|
return obj;
|
|
}
|