Skip to content

Facilitate extension of serialization of values in messages #3065

Open
@GerkinDev

Description

Runtime

NodeJS

Runtime version

22

Module version

17.13.3

Used with

Standalone

Any other relevant information

No response

What problem are you trying to solve?

The default serialization of values using template {#value} does not match our expected outcomes. I was looking for a way to improve it (by relying on require('node:utils').inspect.

  • I've tried using expressions only to switch formatting depending on value (I want to quote strings, but not int, and do this deeply within objects/array). For object, it resulted in [object Object]
  • I've tried to declare global functions I could use in expression without passing it explicitly everytime.
  • I ended up doing Joi.expression('{#label} expected to be ..., have {inspectValue(#value)}', { inspectValue(value) { return inspect(value) }}) for every overridden message. It works, but requires a lot of boilerplate code for something I'd like to make available for the whole app.

Do you have a new or modified API suggestion to solve the problem?

  • Add a method to declare template functions available for every expressions
  • Or add a way to customize Template's internal.stringify:

    joi/lib/template.js

    Lines 330 to 385 in 239ec33

    internals.stringify = function (value, original, state, prefs, local, options = {}) {
    const type = typeof value;
    const wrap = prefs && prefs.errors && prefs.errors.wrap || {};
    let skipWrap = false;
    if (Ref.isRef(value) &&
    value.render) {
    skipWrap = value.in;
    value = value.resolve(original, state, prefs, local, { in: value.in, ...options });
    }
    if (value === null) {
    return 'null';
    }
    if (type === 'string') {
    return internals.wrap(value, options.arrayItems && wrap.string);
    }
    if (type === 'number' ||
    type === 'function' ||
    type === 'symbol') {
    return value.toString();
    }
    if (type !== 'object') {
    return JSON.stringify(value);
    }
    if (value instanceof Date) {
    return internals.Template.date(value, prefs);
    }
    if (value instanceof Map) {
    const pairs = [];
    for (const [key, sym] of value.entries()) {
    pairs.push(`${key.toString()} -> ${sym.toString()}`);
    }
    value = pairs;
    }
    if (!Array.isArray(value)) {
    return value.toString();
    }
    const values = [];
    for (const item of value) {
    values.push(internals.stringify(item, original, state, prefs, local, { arrayItems: true, ...options }));
    }
    return internals.wrap(values.join(', '), !skipWrap && wrap.array);
    };

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    featureNew functionality or improvement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions