@phnq/message
Version:
Asynchronous, incremental messaging client and server
59 lines (58 loc) • 2.04 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.deserialize = exports.serialize = exports.deannotate = exports.annotate = void 0;
/**
* Although it may be tempting to no-op this and let `JSON.stringify` serialize
* dates to ISO 8601 strings, this is not a good idea. The reason is that a
* string that happens to be an ISO 8601 date string when serialized will be
* deserialized as a Date object. This could have implications during message
* signing and verification.
*
* {
* str: "2025-03-22T11:53:26.424"
* }
*
* will have the `str` deserialized as a Date object. But when verifying the message,
* which involves using `JSON.stringify`, it will be serialized as:
* {
* str: "2025-03-22T11:53:26.424Z"
* }
*
* The additional `Z` at the end of the string will cause the hash to be different
* and the verification to fail.
*/
const annotate = (val) => {
if (val instanceof Array) {
const arr = val;
return arr.map(exports.annotate);
}
if (val instanceof Date) {
const date = val;
return `${date.toISOString()}@@@D`;
}
if (val && typeof val === 'object') {
return Object.fromEntries(Object.entries(val).map(([k, v]) => [k, (0, exports.annotate)(v)]));
}
return val;
};
exports.annotate = annotate;
const DATE_RE = /^(.+)@@@D$/;
const deannotate = (val) => {
if (val instanceof Array) {
const arr = val;
return arr.map(exports.deannotate);
}
const dateM = typeof val === 'string' ? DATE_RE.exec(val) : undefined;
if (dateM) {
return new Date(dateM[1]);
}
if (val && typeof val === 'object') {
return Object.fromEntries(Object.entries(val).map(([k, v]) => [k, (0, exports.deannotate)(v)]));
}
return val;
};
exports.deannotate = deannotate;
const serialize = (val) => JSON.stringify((0, exports.annotate)(val));
exports.serialize = serialize;
const deserialize = (str) => (0, exports.deannotate)(JSON.parse(str));
exports.deserialize = deserialize;
;