code:
'use strict';

const Assert = require('@hapi/hoek/lib/assert');
const Domain = require('@sideway/address/lib/domain');
const Email = require('@sideway/address/lib/email');
const Ip = require('@sideway/address/lib/ip');
const EscapeRegex = require('@hapi/hoek/lib/escapeRegex');
const Tlds = require('@sideway/address/lib/tlds');
const Uri = require('@sideway/address/lib/uri');

const Any = require('./any');
const Common = require('../common');

const internals = {
    tlds: Tlds instanceof Set ? { tlds: { allow: Tlds, deny: null } } : false,              // $lab:coverage:ignore$
    base64Regex: {
        // paddingRequired
        true: {
            // urlSafe
            true: /^(?:[\w\-]{2}[\w\-]{2})*(?:[\w\-]{2}==|[\w\-]{3}=)?$/,
            false: /^(?:[A-Za-z0-9+\/]{2}[A-Za-z0-9+\/]{2})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
        },
        false: {
            true: /^(?:[\w\-]{2}[\w\-]{2})*(?:[\w\-]{2}(==)?|[\w\-]{3}=?)?$/,
            false: /^(?:[A-Za-z0-9+\/]{2}[A-Za-z0-9+\/]{2})*(?:[A-Za-z0-9+\/]{2}(==)?|[A-Za-z0-9+\/]{3}=?)?$/
        }
    },
    dataUriRegex: /^data:[\w+.-]+\/[\w+.-]+;((charset=[\w-]+|base64),)?(.*)$/,
    hexRegex: {
        withPrefix: /^0x[0-9a-f]+$/i,
        withOptionalPrefix: /^(?:0x)?[0-9a-f]+$/i,
        withoutPrefix: /^[0-9a-f]+$/i
    },
    ipRegex: Ip.regex({ cidr: 'forbidden' }).regex,
    isoDurationRegex: /^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/,

    guidBrackets: {
        '{': '}', '[': ']', '(': ')', '': ''
    },
    guidVersions: {
        uuidv1: '1',
        uuidv2: '2',
        uuidv3: '3',
        uuidv4: '4',
        uuidv5: '5',
        uuidv6: '6',
        uuidv7: '7',
        uuidv8: '8'
    },
    guidSeparators: new Set([undefined, true, false, '-', ':']),

    normalizationForms: ['NFC', 'NFD', 'NFKC', 'NFKD']
};

module.exports = Any.extend({

    type: 'string',

    flags: {

        insensitive: { default: false },
        truncate: { default: false }
    },

    terms: {

        replacements: { init: null }
    },

    coerce: {
        from: 'string',
        method(value, { schema, state, prefs }) {

            const normalize = schema.$_getRule('normalize');
            if (normalize) {
                value = value.normalize(normalize.args.form);
            }

            const casing = schema.$_getRule('case');
            if (casing) {
                value = casing.args.direction === 'upper' ? value.toLocaleUpperCase() : value.toLocaleLowerCase();
            }

            const trim = schema.$_getRule('trim');
            if (trim &&
                trim.args.enabled) {

                value = value.trim();
            }

            if (schema.$_terms.replacements) {
                for (const replacement of schema.$_terms.replacements) {
                    value = value.replace(replacement.pattern, replacement.replacement);
                }
            }

            const hex = schema.$_getRule('hex');
            if (hex &&
                hex.args.options.byteAligned &&
                value.length % 2 !== 0) {

                value = `0${value}`;
            }

            if (schema.$_getRule('isoDate')) {
                const iso = internals.isoDate(value);
                if (iso) {
                    value = iso;
                }
            }

            if (schema._flags.truncate) {
                const rule = schema.$_getRule('max');
                if (rule) {
                    let limit = rule.args.limit;
                    if (Common.isResolvable(limit)) {
                        limit = limit.resolve(value, state, prefs);
                        if (!Common.limit(limit)) {
                            return { value, errors: schema.$_createError('any.ref', limit, { ref: rule.args.limit, arg: 'limit', reason: 'must be a positive integer' }, state, prefs) };
                        }
                    }

                    value = value.slice(0, limit);
                }
            }

            return { value };
        }
    },

    validate(value, { schema, error }) {

        if (typeof value !== 'string') {
            return { value, errors: error('string.base') };
        }

        if (value === '') {
            const min = schema.$_getRule('min');
            if (min &&
                min.args.limit === 0) {

                return;
            }

            return { value, errors: error('string.empty') };
        }
    },

    rules: {

        alphanum: {
            method() {

                return this.$_addRule('alphanum');
            },
            validate(value, helpers) {

                if (/^[a-zA-Z0-9]+$/.test(value)) {
                    return value;
                }

                return helpers.error('string.alphanum');
            }
        },

        base64: {
            method(options = {}) {

                Common.assertOptions(options, ['paddingRequired', 'urlSafe']);

                options = { urlSafe: false, paddingRequired: true, ...options };
                Assert(typeof options.paddingRequired === 'boolean', 'paddingRequired must be boolean');
                Assert(typeof options.urlSafe === 'boolean', 'urlSafe must be boolean');

                return this.$_addRule({ name: 'base64', args: { options } });
            },
            validate(value, helpers, { options }) {

                const regex = internals.base64Regex[options.paddingRequired][options.urlSafe];
                if (regex.test(value)) {
                    return value;
                }

                return helpers.error('string.base64');
            }
        },

        case: {
            method(direction) {

                Assert(['lower', 'upper'].includes(direction), 'Invalid case:', direction);

                return this.$_addRule({ name: 'case', args: { direction } });
            },
            validate(value, helpers, { direction }) {

                if (direction === 'lower' && value === value.toLocaleLowerCase() ||
                    direction === 'upper' && value === value.toLocaleUpperCase()) {

                    return value;
                }

                return helpers.error(`string.${direction}case`);
            },
            convert: true
        },

        creditCard: {
            method() {

                return this.$_addRule('creditCard');
            },
            validate(value, helpers) {

                let i = value.length;
                let sum = 0;
                let mul = 1;

                while (i--) {
                    const char = value.charAt(i) * mul;
                    sum = sum + (char - (char > 9) * 9);
                    mul = mul ^ 3;
                }

                if (sum > 0 &&
                    sum % 10 === 0) {

                    return value;
                }

                return helpers.error('string.creditCard');
            }
        },

        dataUri: {
            method(options = {}) {

                Common.assertOptions(options, ['paddingRequired']);

                options = { paddingRequired: true, ...options };
                Assert(typeof options.paddingRequired === 'boolean', 'paddingRequired must be boolean');

                return this.$_addRule({ name: 'dataUri', args: { options } });
            },
            validate(value, helpers, { options }) {

                const matches = value.match(internals.dataUriRegex);

                if (matches) {
                    if (!matches[2]) {
                        return value;
                    }

                    if (matches[2] !== 'base64') {
                        return value;
                    }

                    const base64regex = internals.base64Regex[options.paddingRequired].false;
                    if (base64regex.test(matches[3])) {
                        return value;
                    }
                }

                return helpers.error('string.dataUri');
            }
        },

        domain: {
            method(options) {

                if (options) {
                    Common.assertOptions(options, ['allowFullyQualified', 'allowUnicode', 'maxDomainSegments', 'minDomainSegments', 'tlds']);
                }

                const address = internals.addressOptions(options);
                return this.$_addRule({ name: 'domain', args: { options }, address });
            },
            validate(value, helpers, args, { address }) {

                if (Domain.isValid(value, address)) {
                    return value;
                }

                return helpers.error('string.domain');
            }
        },

        email: {
            method(options = {}) {

                Common.assertOptions(options, ['allowFullyQualified', 'allowUnicode', 'ignoreLength', 'maxDomainSegments', 'minDomainSegments', 'multiple', 'separator', 'tlds']);
                Assert(options.multiple === undefined || typeof options.multiple === 'boolean', 'multiple option must be an boolean');

                const address = internals.addressOptions(options);
                const regex = new RegExp(`\\s*[${options.separator ? EscapeRegex(options.separator) : ','}]\\s*`);

                return this.$_addRule({ name: 'email', args: { options }, regex, address });
            },
            validate(value, helpers, { options }, { regex, address }) {

                const emails = options.multiple ? value.split(regex) : [value];
                const invalids = [];
                for (const email of emails) {
                    if (!Email.isValid(email, address)) {
                        invalids.push(email);
                    }
                }

                if (!invalids.length) {
                    return value;
                }

                return helpers.error('string.email', { value, invalids });
            }
        },

        guid: {
            alias: 'uuid',
            method(options = {}) {

                Common.assertOptions(options, ['version', 'separator']);

                let versionNumbers = '';

                if (options.version) {
                    const versions = [].concat(options.version);

                    Assert(versions.length >= 1, 'version must have at least 1 valid version specified');
                    const set = new Set();

                    for (let i = 0; i < versions.length; ++i) {
                        const version = versions[i];
                        Assert(typeof version === 'string', 'version at position ' + i + ' must be a string');
                        const versionNumber = internals.guidVersions[version.toLowerCase()];
                        Assert(versionNumber, 'version at position ' + i + ' must be one of ' + Object.keys(internals.guidVersions).join(', '));
                        Assert(!set.has(versionNumber), 'version at position ' + i + ' must not be a duplicate');

                        versionNumbers += versionNumber;
                        set.add(versionNumber);
                    }
                }

                Assert(internals.guidSeparators.has(options.separator), 'separator must be one of true, false, "-", or ":"');
                const separator = options.separator === undefined ? '[:-]?' :
                    options.separator === true ? '[:-]' :
                        options.separator === false ? '[]?' : `\\${options.separator}`;

                const regex = new RegExp(`^([\\[{\\(]?)[0-9A-F]{8}(${separator})[0-9A-F]{4}\\2?[${versionNumbers || '0-9A-F'}][0-9A-F]{3}\\2?[${versionNumbers ? '89AB' : '0-9A-F'}][0-9A-F]{3}\\2?[0-9A-F]{12}([\\]}\\)]?)$`, 'i');

                return this.$_addRule({ name: 'guid', args: { options }, regex });
            },
            validate(value, helpers, args, { regex }) {

                const results = regex.exec(value);

                if (!results) {
                    return helpers.error('string.guid');
                }

                // Matching braces

                if (internals.guidBrackets[results[1]] !== results[results.length - 1]) {
                    return helpers.error('string.guid');
                }

                return value;
            }
        },

        hex: {
            method(options = {}) {

                Common.assertOptions(options, ['byteAligned', 'prefix']);

                options = { byteAligned: false, prefix: false, ...options };
                Assert(typeof options.byteAligned === 'boolean', 'byteAligned must be boolean');
                Assert(typeof options.prefix === 'boolean' || options.prefix === 'optional', 'prefix must be boolean or "optional"');

                return this.$_addRule({ name: 'hex', args: { options } });
            },
            validate(value, helpers, { options }) {

                const re = options.prefix === 'optional' ?
                    internals.hexRegex.withOptionalPrefix :
                    options.prefix === true ?
                        internals.hexRegex.withPrefix :
                        internals.hexRegex.withoutPrefix;
                if (!re.test(value)) {
                    return helpers.error('string.hex');
                }

                if (options.byteAligned &&
                    value.length % 2 !== 0) {

                    return helpers.error('string.hexAlign');
                }

                return value;
            }
        },

        hostname: {
            method() {

                return this.$_addRule('hostname');
            },
            validate(value, helpers) {

                if (Domain.isValid(value, { minDomainSegments: 1 }) ||
                    internals.ipRegex.test(value)) {

                    return value;
                }

                return helpers.error('string.hostname');
            }
        },

        insensitive: {
            method() {

                return this.$_setFlag('insensitive', true);
            }
        },

        ip: {
            method(options = {}) {

                Common.assertOptions(options, ['cidr', 'version']);

                const { cidr, versions, regex } = Ip.regex(options);
                const version = options.version ? versions : undefined;
                return this.$_addRule({ name: 'ip', args: { options: { cidr, version } }, regex });
            },
            validate(value, helpers, { options }, { regex }) {

                if (regex.test(value)) {
                    return value;
                }

                if (options.version) {
                    return helpers.error('string.ipVersion', { value, cidr: options.cidr, version: options.version });
                }

                return helpers.error('string.ip', { value, cidr: options.cidr });
            }
        },

        isoDate: {
            method() {

                return this.$_addRule('isoDate');
            },
            validate(value, { error }) {

                if (internals.isoDate(value)) {
                    return value;
                }

                return error('string.isoDate');
            }
        },

        isoDuration: {
            method() {

                return this.$_addRule('isoDuration');
            },
            validate(value, helpers) {

                if (internals.isoDurationRegex.test(value)) {
                    return value;
                }

                return helpers.error('string.isoDuration');
            }
        },

        length: {
            method(limit, encoding) {

                return internals.length(this, 'length', limit, '=', encoding);
            },
            validate(value, helpers, { limit, encoding }, { name, operator, args }) {

                const length = encoding ? Buffer && Buffer.byteLength(value, encoding) : value.length;      // $lab:coverage:ignore$
                if (Common.compare(length, limit, operator)) {
                    return value;
                }

                return helpers.error('string.' + name, { limit: args.limit, value, encoding });
            },
            args: [
                {
                    name: 'limit',
                    ref: true,
                    assert: Common.limit,
                    message: 'must be a positive integer'
                },
                'encoding'
            ]
        },

        lowercase: {
            method() {

                return this.case('lower');
            }
        },

        max: {
            method(limit, encoding) {

                return internals.length(this, 'max', limit, '<=', encoding);
            },
            args: ['limit', 'encoding']
        },

        min: {
            method(limit, encoding) {

                return internals.length(this, 'min', limit, '>=', encoding);
            },
            args: ['limit', 'encoding']
        },

        normalize: {
            method(form = 'NFC') {

                Assert(internals.normalizationForms.includes(form), 'normalization form must be one of ' + internals.normalizationForms.join(', '));

                return this.$_addRule({ name: 'normalize', args: { form } });
            },
            validate(value, { error }, { form }) {

                if (value === value.normalize(form)) {
                    return value;
                }

                return error('string.normalize', { value, form });
            },
            convert: true
        },

        pattern: {
            alias: 'regex',
            method(regex, options = {}) {

                Assert(regex instanceof RegExp, 'regex must be a RegExp');
                Assert(!regex.flags.includes('g') && !regex.flags.includes('y'), 'regex should not use global or sticky mode');

                if (typeof options === 'string') {
                    options = { name: options };
                }

                Common.assertOptions(options, ['invert', 'name']);

                const errorCode = ['string.pattern', options.invert ? '.invert' : '', options.name ? '.name' : '.base'].join('');
                return this.$_addRule({ name: 'pattern', args: { regex, options }, errorCode });
            },
            validate(value, helpers, { regex, options }, { errorCode }) {

                const patternMatch = regex.test(value);

                if (patternMatch ^ options.invert) {
                    return value;
                }

                return helpers.error(errorCode, { name: options.name, regex, value });
            },
            args: ['regex', 'options'],
            multi: true
        },

        replace: {
            method(pattern, replacement) {

                if (typeof pattern === 'string') {
                    pattern = new RegExp(EscapeRegex(pattern), 'g');
                }

                Assert(pattern instanceof RegExp, 'pattern must be a RegExp');
                Assert(typeof replacement === 'string', 'replacement must be a String');

                const obj = this.clone();

                if (!obj.$_terms.replacements) {
                    obj.$_terms.replacements = [];
                }

                obj.$_terms.replacements.push({ pattern, replacement });
                return obj;
            }
        },

        token: {
            method() {

                return this.$_addRule('token');
            },
            validate(value, helpers) {

                if (/^\w+$/.test(value)) {
                    return value;
                }

                return helpers.error('string.token');
            }
        },

        trim: {
            method(enabled = true) {

                Assert(typeof enabled === 'boolean', 'enabled must be a boolean');

                return this.$_addRule({ name: 'trim', args: { enabled } });
            },
            validate(value, helpers, { enabled }) {

                if (!enabled ||
                    value === value.trim()) {

                    return value;
                }

                return helpers.error('string.trim');
            },
            convert: true
        },

        truncate: {
            method(enabled = true) {

                Assert(typeof enabled === 'boolean', 'enabled must be a boolean');

                return this.$_setFlag('truncate', enabled);
            }
        },

        uppercase: {
            method() {

                return this.case('upper');
            }
        },

        uri: {
            method(options = {}) {

                Common.assertOptions(options, ['allowRelative', 'allowQuerySquareBrackets', 'domain', 'relativeOnly', 'scheme', 'encodeUri']);

                if (options.domain) {
                    Common.assertOptions(options.domain, ['allowFullyQualified', 'allowUnicode', 'maxDomainSegments', 'minDomainSegments', 'tlds']);
                }

                const { regex, scheme } = Uri.regex(options);
                const domain = options.domain ? internals.addressOptions(options.domain) : null;
                return this.$_addRule({ name: 'uri', args: { options }, regex, domain, scheme });
            },
            validate(value, helpers, { options }, { regex, domain, scheme }) {

                if (['http:/', 'https:/'].includes(value)) {            // scheme:/ is technically valid but makes no sense
                    return helpers.error('string.uri');
                }

                let match = regex.exec(value);

                if (!match && helpers.prefs.convert && options.encodeUri) {
                    const encoded = encodeURI(value);
                    match = regex.exec(encoded);
                    if (match) {
                        value = encoded;
                    }
                }

                if (match) {
                    const matched = match[1] || match[2];
                    if (domain &&
                        (!options.allowRelative || matched) &&
                        !Domain.isValid(matched, domain)) {

                        return helpers.error('string.domain', { value: matched });
                    }

                    return value;
                }

                if (options.relativeOnly) {
                    return helpers.error('string.uriRelativeOnly');
                }

                if (options.scheme) {
                    return helpers.error('string.uriCustomScheme', { scheme, value });
                }

                return helpers.error('string.uri');
            }
        }
    },

    manifest: {

        build(obj, desc) {

            if (desc.replacements) {
                for (const { pattern, replacement } of desc.replacements) {
                    obj = obj.replace(pattern, replacement);
                }
            }

            return obj;
        }
    },

    messages: {
        'string.alphanum': '{{#label}} must only contain alpha-numeric characters',
        'string.base': '{{#label}} must be a string',
        'string.base64': '{{#label}} must be a valid base64 string',
        'string.creditCard': '{{#label}} must be a credit card',
        'string.dataUri': '{{#label}} must be a valid dataUri string',
        'string.domain': '{{#label}} must contain a valid domain name',
        'string.email': '{{#label}} must be a valid email',
        'string.empty': '{{#label}} is not allowed to be empty',
        'string.guid': '{{#label}} must be a valid GUID',
        'string.hex': '{{#label}} must only contain hexadecimal characters',
        'string.hexAlign': '{{#label}} hex decoded representation must be byte aligned',
        'string.hostname': '{{#label}} must be a valid hostname',
        'string.ip': '{{#label}} must be a valid ip address with a {{#cidr}} CIDR',
        'string.ipVersion': '{{#label}} must be a valid ip address of one of the following versions {{#version}} with a {{#cidr}} CIDR',
        'string.isoDate': '{{#label}} must be in iso format',
        'string.isoDuration': '{{#label}} must be a valid ISO 8601 duration',
        'string.length': '{{#label}} length must be {{#limit}} characters long',
        'string.lowercase': '{{#label}} must only contain lowercase characters',
        'string.max': '{{#label}} length must be less than or equal to {{#limit}} characters long',
        'string.min': '{{#label}} length must be at least {{#limit}} characters long',
        'string.normalize': '{{#label}} must be unicode normalized in the {{#form}} form',
        'string.token': '{{#label}} must only contain alpha-numeric and underscore characters',
        'string.pattern.base': '{{#label}} with value {:[.]} fails to match the required pattern: {{#regex}}',
        'string.pattern.name': '{{#label}} with value {:[.]} fails to match the {{#name}} pattern',
        'string.pattern.invert.base': '{{#label}} with value {:[.]} matches the inverted pattern: {{#regex}}',
        'string.pattern.invert.name': '{{#label}} with value {:[.]} matches the inverted {{#name}} pattern',
        'string.trim': '{{#label}} must not have leading or trailing whitespace',
        'string.uri': '{{#label}} must be a valid uri',
        'string.uriCustomScheme': '{{#label}} must be a valid uri with a scheme matching the {{#scheme}} pattern',
        'string.uriRelativeOnly': '{{#label}} must be a valid relative uri',
        'string.uppercase': '{{#label}} must only contain uppercase characters'
    }
});

// Helpers

internals.addressOptions = function (options) {

    if (!options) {
        return internals.tlds || options;      // $lab:coverage:ignore$
    }

    // minDomainSegments

    Assert(options.minDomainSegments === undefined ||
        Number.isSafeInteger(options.minDomainSegments) && options.minDomainSegments > 0, 'minDomainSegments must be a positive integer');

    // maxDomainSegments

    Assert(options.maxDomainSegments === undefined ||
        Number.isSafeInteger(options.maxDomainSegments) && options.maxDomainSegments > 0, 'maxDomainSegments must be a positive integer');

    // tlds

    if (options.tlds === false) {
        return options;
    }

    if (options.tlds === true ||
        options.tlds === undefined) {

        Assert(internals.tlds, 'Built-in TLD list disabled');
        return Object.assign({}, options, internals.tlds);
    }

    Assert(typeof options.tlds === 'object', 'tlds must be true, false, or an object');

    const deny = options.tlds.deny;
    if (deny) {
        if (Array.isArray(deny)) {
            options = Object.assign({}, options, { tlds: { deny: new Set(deny) } });
        }

        Assert(options.tlds.deny instanceof Set, 'tlds.deny must be an array, Set, or boolean');
        Assert(!options.tlds.allow, 'Cannot specify both tlds.allow and tlds.deny lists');
        internals.validateTlds(options.tlds.deny, 'tlds.deny');
        return options;
    }

    const allow = options.tlds.allow;
    if (!allow) {
        return options;
    }

    if (allow === true) {
        Assert(internals.tlds, 'Built-in TLD list disabled');
        return Object.assign({}, options, internals.tlds);
    }

    if (Array.isArray(allow)) {
        options = Object.assign({}, options, { tlds: { allow: new Set(allow) } });
    }

    Assert(options.tlds.allow instanceof Set, 'tlds.allow must be an array, Set, or boolean');
    internals.validateTlds(options.tlds.allow, 'tlds.allow');
    return options;
};

internals.validateTlds = function (set, source) {

    for (const tld of set) {
        Assert(Domain.isValid(tld, { minDomainSegments: 1, maxDomainSegments: 1 }), `${source} must contain valid top level domain names`);
    }
};

internals.isoDate = function (value) {

    if (!Common.isIsoDate(value)) {
        return null;
    }

    if (/.*T.*[+-]\d\d$/.test(value)) {             // Add missing trailing zeros to timeshift
        value += '00';
    }

    const date = new Date(value);
    if (isNaN(date.getTime())) {
        return null;
    }

    return date.toISOString();
};

internals.length = function (schema, name, limit, operator, encoding) {

    Assert(!encoding || Buffer && Buffer.isEncoding(encoding), 'Invalid encoding:', encoding);      // $lab:coverage:ignore$

    return schema.$_addRule({ name, method: 'length', args: { limit, encoding }, operator });
};


code:
'use strict';

const Assert = require('@hapi/hoek/lib/assert');

const Any = require('./any');

const internals = {};

internals.Map = class extends Map {

    slice() {

        return new internals.Map(this);
    }
};

module.exports = Any.extend({

    type: 'symbol',

    terms: {

        map: { init: new internals.Map() }
    },

    coerce: {
        method(value, { schema, error }) {

            const lookup = schema.$_terms.map.get(value);
            if (lookup) {
                value = lookup;
            }

            if (!schema._flags.only ||
                typeof value === 'symbol') {

                return { value };
            }

            return { value, errors: error('symbol.map', { map: schema.$_terms.map }) };
        }
    },

    validate(value, { error }) {

        if (typeof value !== 'symbol') {
            return { value, errors: error('symbol.base') };
        }
    },

    rules: {
        map: {
            method(iterable) {

                if (iterable &&
                    !iterable[Symbol.iterator] &&
                    typeof iterable === 'object') {

                    iterable = Object.entries(iterable);
                }

                Assert(iterable && iterable[Symbol.iterator], 'Iterable must be an iterable or object');

                const obj = this.clone();

                const symbols = [];
                for (const entry of iterable) {
                    Assert(entry && entry[Symbol.iterator], 'Entry must be an iterable');
                    const [key, value] = entry;

                    Assert(typeof key !== 'object' && typeof key !== 'function' && typeof key !== 'symbol', 'Key must not be of type object, function, or Symbol');
                    Assert(typeof value === 'symbol', 'Value must be a Symbol');

                    obj.$_terms.map.set(key, value);
                    symbols.push(value);
                }

                return obj.valid(...symbols);
            }
        }
    },

    manifest: {

        build(obj, desc) {

            if (desc.map) {
                obj = obj.map(desc.map);
            }

            return obj;
        }
    },

    messages: {
        'symbol.base': '{{#label}} must be a symbol',
        'symbol.map': '{{#label}} must be one of {{#map}}'
    }
});

license: mit


code:
'use strict';

const Assert = require('@hapi/hoek/lib/assert');
const Clone = require('@hapi/hoek/lib/clone');
const Ignore = require('@hapi/hoek/lib/ignore');
const Reach = require('@hapi/hoek/lib/reach');

const Common = require('./common');
const Errors = require('./errors');
const State = require('./state');

const internals = {
    result: Symbol('result')
};

exports.entry = function (value, schema, prefs) {

    let settings = Common.defaults;
    if (prefs) {
        Assert(prefs.warnings === undefined, 'Cannot override warnings preference in synchronous validation');
        Assert(prefs.artifacts === undefined, 'Cannot override artifacts preference in synchronous validation');
        settings = Common.preferences(Common.defaults, prefs);
    }

    const result = internals.entry(value, schema, settings);
    Assert(!result.mainstay.externals.length, 'Schema with external rules must use validateAsync()');
    const outcome = { value: result.value };

    if (result.error) {
        outcome.error = result.error;
    }

    if (result.mainstay.warnings.length) {
        outcome.warning = Errors.details(result.mainstay.warnings);
    }

    if (result.mainstay.debug) {
        outcome.debug = result.mainstay.debug;
    }

    if (result.mainstay.artifacts) {
        outcome.artifacts = result.mainstay.artifacts;
    }

    return outcome;
};

exports.entryAsync = async function (value, schema, prefs) {

    let settings = Common.defaults;
    if (prefs) {
        settings = Common.preferences(Common.defaults, prefs);
    }

    const result = internals.entry(value, schema, settings);
    const mainstay = result.mainstay;
    if (result.error) {
        if (mainstay.debug) {
            result.error.debug = mainstay.debug;
        }

        throw result.error;
    }

    if (mainstay.externals.length) {
        let root = result.value;
        const errors = [];
        for (const external of mainstay.externals) {
            const path = external.state.path;
            const linked = external.schema.type === 'link' ? mainstay.links.get(external.schema) : null;
            let node = root;
            let key;
            let parent;

            const ancestors = path.length ? [root] : [];
            const original = path.length ? Reach(value, path) : value;

            if (path.length) {
                key = path[path.length - 1];

                let current = root;
                for (const segment of path.slice(0, -1)) {
                    current = current[segment];
                    ancestors.unshift(current);
                }

                parent = ancestors[0];
                node = parent[key];
            }

            try {
                const createError = (code, local) => (linked || external.schema).$_createError(code, node, local, external.state, settings);
                const output = await external.method(node, {
                    schema: external.schema,
                    linked,
                    state: external.state,
                    prefs,
                    original,
                    error: createError,
                    errorsArray: internals.errorsArray,
                    warn: (code, local) => mainstay.warnings.push((linked || external.schema).$_createError(code, node, local, external.state, settings)),
                    message: (messages, local) => (linked || external.schema).$_createError('external', node, local, external.state, settings, { messages })
                });

                if (output === undefined ||
                    output === node) {

                    continue;
                }

                if (output instanceof Errors.Report) {
                    mainstay.tracer.log(external.schema, external.state, 'rule', 'external', 'error');
                    errors.push(output);

                    if (settings.abortEarly) {
                        break;
                    }

                    continue;
                }

                if (Array.isArray(output) &&
                    output[Common.symbols.errors]) {
                    mainstay.tracer.log(external.schema, external.state, 'rule', 'external', 'error');
                    errors.push(...output);

                    if (settings.abortEarly) {
                        break;
                    }

                    continue;
                }

                if (parent) {
                    mainstay.tracer.value(external.state, 'rule', node, output, 'external');
                    parent[key] = output;
                }
                else {
                    mainstay.tracer.value(external.state, 'rule', root, output, 'external');
                    root = output;
                }
            }
            catch (err) {
                if (settings.errors.label) {
                    err.message += ` (${(external.label)})`;       // Change message to include path
                }

                throw err;
            }
        }

        result.value = root;

        if (errors.length) {
            result.error = Errors.process(errors, value, settings);

            if (mainstay.debug) {
                result.error.debug = mainstay.debug;
            }

            throw result.error;
        }
    }

    if (!settings.warnings &&
        !settings.debug &&
        !settings.artifacts) {

        return result.value;
    }

    const outcome = { value: result.value };
    if (mainstay.warnings.length) {
        outcome.warning = Errors.details(mainstay.warnings);
    }

    if (mainstay.debug) {
        outcome.debug = mainstay.debug;
    }

    if (mainstay.artifacts) {
        outcome.artifacts = mainstay.artifacts;
    }

    return outcome;
};

internals.Mainstay = class {

    constructor(tracer, debug, links) {

        this.externals = [];
        this.warnings = [];
        this.tracer = tracer;
        this.debug = debug;
        this.links = links;
        this.shadow = null;
        this.artifacts = null;

        this._snapshots = [];
    }

    snapshot() {

        this._snapshots.push({
            externals: this.externals.slice(),
            warnings: this.warnings.slice()
        });
    }

    restore() {

        const snapshot = this._snapshots.pop();
        this.externals = snapshot.externals;
        this.warnings = snapshot.warnings;
    }

    commit() {

        this._snapshots.pop();
    }
};

internals.entry = function (value, schema, prefs) {

    // Prepare state

    const { tracer, cleanup } = internals.tracer(schema, prefs);
    const debug = prefs.debug ? [] : null;
    const links = schema._ids._schemaChain ? new Map() : null;
    const mainstay = new internals.Mainstay(tracer, debug, links);
    const schemas = schema._ids._schemaChain ? [{ schema }] : null;
    const state = new State([], [], { mainstay, schemas });

    // Validate value

    const result = exports.validate(value, schema, state, prefs);

    // Process value and errors

    if (cleanup) {
        schema.$_root.untrace();
    }

    const error = Errors.process(result.errors, value, prefs);
    return { value: result.value, error, mainstay };
};

internals.tracer = function (schema, prefs) {

    if (schema.$_root._tracer) {
        return { tracer: schema.$_root._tracer._register(schema) };
    }

    if (prefs.debug) {
        Assert(schema.$_root.trace, 'Debug mode not supported');
        return { tracer: schema.$_root.trace()._register(schema), cleanup: true };
    }

    return { tracer: internals.ignore };
};

exports.validate = function (value, schema, state, prefs, overrides = {}) {

    if (schema.$_terms.whens) {
        schema = schema._generate(value, state, prefs).schema;
    }

    // Setup state and settings

    if (schema._preferences) {
        prefs = internals.prefs(schema, prefs);
    }

    // Cache

    if (schema._cache &&
        prefs.cache) {

        const result = schema._cache.get(value);
        state.mainstay.tracer.debug(state, 'validate', 'cached', !!result);
        if (result) {
            return result;
        }
    }

    // Helpers

    const createError = (code, local, localState) => schema.$_createError(code, value, local, localState || state, prefs);
    const helpers = {
        original: value,
        prefs,
        schema,
        state,
        error: createError,
        errorsArray: internals.errorsArray,
        warn: (code, local, localState) => state.mainstay.warnings.push(createError(code, local, localState)),
        message: (messages, local) => schema.$_createError('custom', value, local, state, prefs, { messages })
    };

    // Prepare

    state.mainstay.tracer.entry(schema, state);

    const def = schema._definition;
    if (def.prepare &&
        value !== undefined &&
        prefs.convert) {

        const prepared = def.prepare(value, helpers);
        if (prepared) {
            state.mainstay.tracer.value(state, 'prepare', value, prepared.value);
            if (prepared.errors) {
                return internals.finalize(prepared.value, [].concat(prepared.errors), helpers);         // Prepare error always aborts early
            }

            value = prepared.value;
        }
    }

    // Type coercion

    if (def.coerce &&
        value !== undefined &&
        prefs.convert &&
        (!def.coerce.from || def.coerce.from.includes(typeof value))) {

        const coerced = def.coerce.method(value, helpers);
        if (coerced) {
            state.mainstay.tracer.value(state, 'coerced', value, coerced.value);
            if (coerced.errors) {
                return internals.finalize(coerced.value, [].concat(coerced.errors), helpers);           // Coerce error always aborts early
            }

            value = coerced.value;
        }
    }

    // Empty value

    const empty = schema._flags.empty;
    if (empty &&
        empty.$_match(internals.trim(value, schema), state.nest(empty), Common.defaults)) {

        state.mainstay.tracer.value(state, 'empty', value, undefined);
        value = undefined;
    }

    // Presence requirements (required, optional, forbidden)

    const presence = overrides.presence || schema._flags.presence || (schema._flags._endedSwitch ? null : prefs.presence);
    if (value === undefined) {
        if (presence === 'forbidden') {
            return internals.finalize(value, null, helpers);
        }

        if (presence === 'required') {
            return internals.finalize(value, [schema.$_createError('any.required', value, null, state, prefs)], helpers);
        }

        if (presence === 'optional') {
            if (schema._flags.default !== Common.symbols.deepDefault) {
                return internals.finalize(value, null, helpers);
            }

            state.mainstay.tracer.value(state, 'default', value, {});
            value = {};
        }
    }
    else if (presence === 'forbidden') {
        return internals.finalize(value, [schema.$_createError('any.unknown', value, null, state, prefs)], helpers);
    }

    // Allowed values

    const errors = [];

    if (schema._valids) {
        const match = schema._valids.get(value, state, prefs, schema._flags.insensitive);
        if (match) {
            if (prefs.convert) {
                state.mainstay.tracer.value(state, 'valids', value, match.value);
                value = match.value;
            }

            state.mainstay.tracer.filter(schema, state, 'valid', match);
            return internals.finalize(value, null, helpers);
        }

        if (schema._flags.only) {
            const report = schema.$_createError('any.only', value, { valids: schema._valids.values({ display: true }) }, state, prefs);
            if (prefs.abortEarly) {
                return internals.finalize(value, [report], helpers);
            }

            errors.push(report);
        }
    }

    // Denied values

    if (schema._invalids) {
        const match = schema._invalids.get(value, state, prefs, schema._flags.insensitive);
        if (match) {
            state.mainstay.tracer.filter(schema, state, 'invalid', match);
            const report = schema.$_createError('any.invalid', value, { invalids: schema._invalids.values({ display: true }) }, state, prefs);
            if (prefs.abortEarly) {
                return internals.finalize(value, [report], helpers);
            }

            errors.push(report);
        }
    }

    // Base type

    if (def.validate) {
        const base = def.validate(value, helpers);
        if (base) {
            state.mainstay.tracer.value(state, 'base', value, base.value);
            value = base.value;

            if (base.errors) {
                if (!Array.isArray(base.errors)) {
                    errors.push(base.errors);
                    return internals.finalize(value, errors, helpers);          // Base error always aborts early
                }

                if (base.errors.length) {
                    errors.push(...base.errors);
                    return internals.finalize(value, errors, helpers);          // Base error always aborts early
                }
            }
        }
    }

    // Validate tests

    if (!schema._rules.length) {
        return internals.finalize(value, errors, helpers);
    }

    return internals.rules(value, errors, helpers);
};

internals.rules = function (value, errors, helpers) {

    const { schema, state, prefs } = helpers;

    for (const rule of schema._rules) {
        const definition = schema._definition.rules[rule.method];

        // Skip rules that are also applied in coerce step

        if (definition.convert &&
            prefs.convert) {

            state.mainstay.tracer.log(schema, state, 'rule', rule.name, 'full');
            continue;
        }

        // Resolve references

        let ret;
        let args = rule.args;
        if (rule._resolve.length) {
            args = Object.assign({}, args);                                     // Shallow copy
            for (const key of rule._resolve) {
                const resolver = definition.argsByName.get(key);

                const resolved = args[key].resolve(value, state, prefs);
                const normalized = resolver.normalize ? resolver.normalize(resolved) : resolved;

                const invalid = Common.validateArg(normalized, null, resolver);
                if (invalid) {
                    ret = schema.$_createError('any.ref', resolved, { arg: key, ref: args[key], reason: invalid }, state, prefs);
                    break;
                }

                args[key] = normalized;
            }
        }

        // Test rule

        ret = ret || definition.validate(value, helpers, args, rule);           // Use ret if already set to reference error

        const result = internals.rule(ret, rule);
        if (result.errors) {
            state.mainstay.tracer.log(schema, state, 'rule', rule.name, 'error');

            if (rule.warn) {
                state.mainstay.warnings.push(...result.errors);
                continue;
            }

            if (prefs.abortEarly) {
                return internals.finalize(value, result.errors, helpers);
            }

            errors.push(...result.errors);
        }
        else {
            state.mainstay.tracer.log(schema, state, 'rule', rule.name, 'pass');
            state.mainstay.tracer.value(state, 'rule', value, result.value, rule.name);
            value = result.value;
        }
    }

    return internals.finalize(value, errors, helpers);
};

internals.rule = function (ret, rule) {

    if (ret instanceof Errors.Report) {
        internals.error(ret, rule);
        return { errors: [ret], value: null };
    }

    if (Array.isArray(ret) &&
        ret[Common.symbols.errors]) {

        ret.forEach((report) => internals.error(report, rule));
        return { errors: ret, value: null };
    }

    return { errors: null, value: ret };
};

internals.error = function (report, rule) {

    if (rule.message) {
        report._setTemplate(rule.message);
    }

    return report;
};

internals.finalize = function (value, errors, helpers) {

    errors = errors || [];
    const { schema, state, prefs } = helpers;

    // Failover value

    if (errors.length) {
        const failover = internals.default('failover', undefined, errors, helpers);
        if (failover !== undefined) {
            state.mainstay.tracer.value(state, 'failover', value, failover);
            value = failover;
            errors = [];
        }
    }

    // Error override

    if (errors.length &&
        schema._flags.error) {

        if (typeof schema._flags.error === 'function') {
            errors = schema._flags.error(errors);
            if (!Array.isArray(errors)) {
                errors = [errors];
            }

            for (const error of errors) {
                Assert(error instanceof Error || error instanceof Errors.Report, 'error() must return an Error object');
            }
        }
        else {
            errors = [schema._flags.error];
        }
    }

    // Default

    if (value === undefined) {
        const defaulted = internals.default('default', value, errors, helpers);
        state.mainstay.tracer.value(state, 'default', value, defaulted);
        value = defaulted;
    }

    // Cast

    if (schema._flags.cast &&
        value !== undefined) {

        const caster = schema._definition.cast[schema._flags.cast];
        if (caster.from(value)) {
            const casted = caster.to(value, helpers);
            state.mainstay.tracer.value(state, 'cast', value, casted, schema._flags.cast);
            value = casted;
        }
    }

    // Externals

    if (schema.$_terms.externals &&
        prefs.externals &&
        prefs._externals !== false) {                       // Disabled for matching

        for (const { method } of schema.$_terms.externals) {
            state.mainstay.externals.push({ method, schema, state, label: Errors.label(schema._flags, state, prefs) });
        }
    }

    // Result

    const result = { value, errors: errors.length ? errors : null };

    if (schema._flags.result) {
        result.value = schema._flags.result === 'strip' ? undefined : /* raw */ helpers.original;
        state.mainstay.tracer.value(state, schema._flags.result, value, result.value);
        state.shadow(value, schema._flags.result);
    }

    // Cache

    if (schema._cache &&
        prefs.cache !== false &&
        !schema._refs.length) {

        schema._cache.set(helpers.original, result);
    }

    // Artifacts

    if (value !== undefined &&
        !result.errors &&
        schema._flags.artifact !== undefined) {

        state.mainstay.artifacts = state.mainstay.artifacts || new Map();
        if (!state.mainstay.artifacts.has(schema._flags.artifact)) {
            state.mainstay.artifacts.set(schema._flags.artifact, []);
        }

        state.mainstay.artifacts.get(schema._flags.artifact).push(state.path);
    }

    return result;
};

internals.prefs = function (schema, prefs) {

    const isDefaultOptions = prefs === Common.defaults;
    if (isDefaultOptions &&
        schema._preferences[Common.symbols.prefs]) {

        return schema._preferences[Common.symbols.prefs];
    }

    prefs = Common.preferences(prefs, schema._preferences);
    if (isDefaultOptions) {
        schema._preferences[Common.symbols.prefs] = prefs;
    }

    return prefs;
};

internals.default = function (flag, value, errors, helpers) {

    const { schema, state, prefs } = helpers;
    const source = schema._flags[flag];
    if (prefs.noDefaults ||
        source === undefined) {

        return value;
    }

    state.mainstay.tracer.log(schema, state, 'rule', flag, 'full');

    if (!source) {
        return source;
    }

    if (typeof source === 'function') {
        const args = source.length ? [Clone(state.ancestors[0]), helpers] : [];

        try {
            return source(...args);
        }
        catch (err) {
            errors.push(schema.$_createError(`any.${flag}`, null, { error: err }, state, prefs));
            return;
        }
    }

    if (typeof source !== 'object') {
        return source;
    }

    if (source[Common.symbols.literal]) {
        return source.literal;
    }

    if (Common.isResolvable(source)) {
        return source.resolve(value, state, prefs);
    }

    return Clone(source);
};

internals.trim = function (value, schema) {

    if (typeof value !== 'string') {
        return value;
    }

    const trim = schema.$_getRule('trim');
    if (!trim ||
        !trim.args.enabled) {

        return value;
    }

    return value.trim();
};

internals.ignore = {
    active: false,
    debug: Ignore,
    entry: Ignore,
    filter: Ignore,
    log: Ignore,
    resolve: Ignore,
    value: Ignore
};

internals.errorsArray = function () {

    const errors = [];
    errors[Common.symbols.errors] = true;
    return errors;
};


code:
'use strict';

const Assert = require('@hapi/hoek/lib/assert');
const DeepEqual = require('@hapi/hoek/lib/deepEqual');

const Common = require('./common');

const internals = {};

module.exports = internals.Values = class {

    constructor(values, refs) {

        this._values = new Set(values);
        this._refs = new Set(refs);
        this._lowercase = internals.lowercases(values);

        this._override = false;
    }

    get length() {

        return this._values.size + this._refs.size;
    }

    add(value, refs) {

        // Reference

        if (Common.isResolvable(value)) {
            if (!this._refs.has(value)) {
                this._refs.add(value);

                if (refs) {                     // Skipped in a merge
                    refs.register(value);
                }
            }

            return;
        }

        // Value

        if (!this.has(value, null, null, false)) {
            this._values.add(value);

            if (typeof value === 'string') {
                this._lowercase.set(value.toLowerCase(), value);
            }
        }
    }

    static merge(target, source, remove) {

        target = target || new internals.Values();

        if (source) {
            if (source._override) {
                return source.clone();
            }

            for (const item of [...source._values, ...source._refs]) {
                target.add(item);
            }
        }

        if (remove) {
            for (const item of [...remove._values, ...remove._refs]) {
                target.remove(item);
            }
        }

        return target.length ? target : null;
    }

    remove(value) {

        // Reference

        if (Common.isResolvable(value)) {
            this._refs.delete(value);
            return;
        }

        // Value

        this._values.delete(value);

        if (typeof value === 'string') {
            this._lowercase.delete(value.toLowerCase());
        }
    }

    has(value, state, prefs, insensitive) {

        return !!this.get(value, state, prefs, insensitive);
    }

    get(value, state, prefs, insensitive) {

        if (!this.length) {
            return false;
        }

        // Simple match

        if (this._values.has(value)) {
            return { value };
        }

        // Case insensitive string match

        if (typeof value === 'string' &&
            value &&
            insensitive) {

            const found = this._lowercase.get(value.toLowerCase());
            if (found) {
                return { value: found };
            }
        }

        if (!this._refs.size &&
            typeof value !== 'object') {

            return false;
        }

        // Objects

        if (typeof value === 'object') {
            for (const item of this._values) {
                if (DeepEqual(item, value)) {
                    return { value: item };
                }
            }
        }

        // References

        if (state) {
            for (const ref of this._refs) {
                const resolved = ref.resolve(value, state, prefs, null, { in: true });
                if (resolved === undefined) {
                    continue;
                }

                const items = !ref.in || typeof resolved !== 'object'
                    ? [resolved]
                    : Array.isArray(resolved) ? resolved : Object.keys(resolved);

                for (const item of items) {
                    if (typeof item !== typeof value) {
                        continue;
                    }

                    if (insensitive &&
                        value &&
                        typeof value === 'string') {

                        if (item.toLowerCase() === value.toLowerCase()) {
                            return { value: item, ref };
                        }
                    }
                    else {
                        if (DeepEqual(item, value)) {
                            return { value: item, ref };
                        }
                    }
                }
            }
        }

        return false;
    }

    override() {

        this._override = true;
    }

    values(options) {

        if (options &&
            options.display) {

            const values = [];

            for (const item of [...this._values, ...this._refs]) {
                if (item !== undefined) {
                    values.push(item);
                }
            }

            return values;
        }

        return Array.from([...this._values, ...this._refs]);
    }

    clone() {

        const set = new internals.Values(this._values, this._refs);
        set._override = this._override;
        return set;
    }

    concat(source) {

        Assert(!source._override, 'Cannot concat override set of values');

        const set = new internals.Values([...this._values, ...source._values], [...this._refs, ...source._refs]);
        set._override = this._override;
        return set;
    }

    describe() {

        const normalized = [];

        if (this._override) {
            normalized.push({ override: true });
        }

        for (const value of this._values.values()) {
            normalized.push(value && typeof value === 'object' ? { value } : value);
        }

        for (const value of this._refs.values()) {
            normalized.push(value.describe());
        }

        return normalized;
    }
};

internals.Values.prototype[Common.symbols.values] = true;

// Aliases

internals.Values.prototype.slice = internals.Values.prototype.clone;

// Helpers

internals.lowercases = function (from) {

    const map = new Map();

    if (from) {
        for (const value of from) {
            if (typeof value === 'string') {
                map.set(value.toLowerCase(), value);
            }
        }
    }

    return map;
};


joi

description: Object schema validation

files: lib/**/* dist/*

keywords: schema validation

prepublishOnly: cd browser && npm install && npm run build

test: lab -t 100 -a @hapi/code -L -Y

test-cov-html: lab -r html -o coverage.html -a @hapi/code


code:
function Message() {
    Object.defineProperties(
        this, {
            data: {
                enumerable: true,
                get: getData,
                set: setData
            },
            type: {
                enumerable: true,
                get: getType,
                set: setType
            },
            load:{
                enumerable:true,
                writable:false,
                value:parse
            },
            JSON: {
                enumerable: true,
                get: getJSON
            }
        }
    );

    var type = '';
    var data = {};

    function getType() {
        return type;
    }

    function getData() {
        return data;
    }

    function getJSON() {
        return JSON.stringify(
            {
                type: type,
                data: data
            }
        );
    }

    function setType(value) {
        type = value;
    }

    function setData(value) {
        data = value;
    }

    function parse(message){
        try{
            var message=JSON.parse(message);
            type=message.type;
            data=message.data;
        }catch(err){
            var badMessage=message;
            type='error',
            data={
                message:'Invalid JSON response format',
                err:err,
                response:badMessage
            }
        }
    }
}

module.exports=Message;


code:
# js-message
Normalized JS & JSON Message and event Protocol for node.js, vanilla.js (plain old javascript), react.js, websockets, rest api's, node-ipc, and any other protocol that might use a js object and or a JSON string.

js-message allows for seamless conversion of JSON messages and events to JS objects for a normalized implementation on the server and in the client without needing to concern yourself with JSON intermediaries and custom parsers.

Things are just easier when you normalize them.

npm js-message info :  [See npm trends and stats for js-message](http://npm-stat.com/charts.html?package=js-message&author=&from=&to=)
![js-message npm version](https://img.shields.io/npm/v/js-message.svg) ![supported node version for js-message](https://img.shields.io/node/v/js-message.svg) ![total npm downloads for js-message](https://img.shields.io/npm/dt/js-message.svg) ![monthly npm downloads for js-message](https://img.shields.io/npm/dm/js-message.svg) ![npm licence for js-message](https://img.shields.io/npm/l/js-message.svg)

` npm install --save js-message `

[![RIAEvangelist](https://avatars3.githubusercontent.com/u/369041?v=3&s=100)](https://github.com/RIAEvangelist)

GitHub info :
[![js-message GitHub Release](https://img.shields.io/github/release/RIAEvangelist/js-message.svg) ![GitHub license js-message license](https://img.shields.io/github/license/RIAEvangelist/js-message.svg) ![open issues for js-message on GitHub](https://img.shields.io/github/issues/RIAEvangelist/js-message.svg)](http://riaevangelist.github.io/js-message/)

[js-message site](http://riaevangelist.github.io/js-message/)

|method or key  |type   |mutable|description|
|---------------|-------|-------|-----------|
|type           |String |true   |the type of message|
|data           |Object |true   |the message data or payload|
|load           |func   |false  |load a message from JSON, this will return a message with the type of error if not valid JSON|
|JSON           |String |not by user|JSON representation of the message|

### Creating a Message Object

```javascript

    //commonjs
    var Message=require('js-message');
    //plain old javascript
    <script src='js-message-vanilla.js' />

    var myMessage=new Message;
    myMessage.type='message or event type';
    myMessage.data.something='something';
    myMessage.data.stuff=[1,2,3,4,5]

    console.log(myMessage.JSON);

```

### Creating a Message From JSON

```javascript

    //commonjs
    var Message=require('js-message');
    //plain old javascript
    <script src='js-message-vanilla.js' />

    //lets say we have the above example running on
    //a websocket server sending js-messages as JSON
    //
    //and lets say this is the client in the browser
    ws.on(
        'message',
        handleMessage
    );

    handleMessage(e){
        var message=new Message;
        message.load(e.data);

        console.log(message.type, message.data);
    }

```

### Sending a Message Object via WebSocket

```javascript

    //commonjs
    var Message=require('js-message');
    //plain old javascript
    <script src='js-message-vanilla.js' />

    //client example, but works the same on server too!
    var ws=new WebSocket('ws://myawesomeWS:8000');

    var myMessage=new Message;
    myMessage.type='setUsername';
    myMessage.data.username='sideshow bob';

    ws.send(myMessage.JSON);

```

---

This work is licenced via the [DBAD Public Licence](http://www.dbad-license.org/).


js-message

description: normalized JS Object and JSON message and event protocol for node.js, vanialla js, react.js, components, actions, stores and dispatchers

license: MIT

keywords: message normalize events js json protocol ipc node nodejs node.js react react.js reactjs websocket websockets web socket sockets ws flux reflux component components store stores action actions

moduleType: globals node


code:
(
    function(){
        function Message() {
            Object.defineProperties(
                this, {
                    data: {
                        enumerable: true,
                        get: getData,
                        set: setData
                    },
                    type: {
                        enumerable: true,
                        get: getType,
                        set: setType
                    },
                    load:{
                        enumarable:true,
                        writable:false,
                        value:parse
                    },
                    JSON: {
                        enumerable: true,
                        get: getJSON
                    }
                }
            );

            var type = '';
            var data = {};

            function getType() {
                return type;
            }

            function getData() {
                return data;
            }

            function getJSON() {
                return JSON.stringify(
                    {
                        type: type,
                        data: data
                    }
                );
            }

            function setType(value) {
                type = value;
            }

            function setData(value) {
                data = value;
            }

            function parse(message){
                try{
                    var message=JSON.parse(message);
                    type=message.type;
                    data=message.data;
                }catch(err){
                    var badMessage=message;
                    type='error',
                    data={
                        message:'Invalid JSON response format',
                        err:err,
                        response:badMessage
                    }
                }
            }
        }

        window.Message=Message;
    }
)();


code:
MIT License

Copyright (c) 2020 Brandon Nozaki Miller

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


test: echo "Error: no test specified" && exit 1

start: node devServer.js

type: git

author: Brandon Nozaki Miller


code:
### Version 4.0.0 (2018-01-28) ###

- Added: Support for ES2018. The only change needed was recognizing the `s`
  regex flag.
- Changed: _All_ tokens returned by the `matchToToken` function now have a
  `closed` property. It is set to `undefined` for the tokens where “closed”
  doesn’t make sense. This means that all tokens objects have the same shape,
  which might improve performance.

These are the breaking changes:

- `'/a/s'.match(jsTokens)` no longer returns `['/', 'a', '/', 's']`, but
  `['/a/s']`. (There are of course other variations of this.)
- Code that rely on some token objects not having the `closed` property could
  now behave differently.

### Version 3.0.2 (2017-06-28) ###

- No code changes. Just updates to the readme.

### Version 3.0.1 (2017-01-30) ###

- Fixed: ES2015 unicode escapes with more than 6 hex digits are now matched
  correctly.

### Version 3.0.0 (2017-01-11) ###

This release contains one breaking change, that should [improve performance in
V8][v8-perf]:

> So how can you, as a JavaScript developer, ensure that your RegExps are fast?
> If you are not interested in hooking into RegExp internals, make sure that
> neither the RegExp instance, nor its prototype is modified in order to get the
> best performance:
>
> ```js
> var re = /./g;
> re.exec('');  // Fast path.
> re.new_property = 'slow';
> ```

This module used to export a single regex, with `.matchToToken` bolted
on, just like in the above example. This release changes the exports of
the module to avoid this issue.

Before:

```js
import jsTokens from "js-tokens"
// or:
var jsTokens = require("js-tokens")
var matchToToken = jsTokens.matchToToken
```

After:

```js
import jsTokens, {matchToToken} from "js-tokens"
// or:
var jsTokens = require("js-tokens").default
var matchToToken = require("js-tokens").matchToToken
```

[v8-perf]: http://v8project.blogspot.se/2017/01/speeding-up-v8-regular-expressions.html

### Version 2.0.0 (2016-06-19) ###

- Added: Support for ES2016. In other words, support for the `**` exponentiation
  operator.

These are the breaking changes:

- `'**'.match(jsTokens)` no longer returns `['*', '*']`, but `['**']`.
- `'**='.match(jsTokens)` no longer returns `['*', '*=']`, but `['**=']`.

### Version 1.0.3 (2016-03-27) ###

- Improved: Made the regex ever so slightly smaller.
- Updated: The readme.

### Version 1.0.2 (2015-10-18) ###

- Improved: Limited npm package contents for a smaller download. Thanks to
  @zertosh!

### Version 1.0.1 (2015-06-20) ###

- Fixed: Declared an undeclared variable.

### Version 1.0.0 (2015-02-26) ###

- Changed: Merged the 'operator' and 'punctuation' types into 'punctuator'. That
  type is now equivalent to the Punctuator token in the ECMAScript
  specification. (Backwards-incompatible change.)
- Fixed: A `-` followed by a number is now correctly matched as a punctuator
  followed by a number. It used to be matched as just a number, but there is no
  such thing as negative number literals. (Possibly backwards-incompatible
  change.)

### Version 0.4.1 (2015-02-21) ###

- Added: Support for the regex `u` flag.

### Version 0.4.0 (2015-02-21) ###

- Improved: `jsTokens.matchToToken` performance.
- Added: Support for octal and binary number literals.
- Added: Support for template strings.

### Version 0.3.1 (2015-01-06) ###

- Fixed: Support for unicode spaces. They used to be allowed in names (which is
  very confusing), and some unicode newlines were wrongly allowed in strings and
  regexes.

### Version 0.3.0 (2014-12-19) ###

- Changed: The `jsTokens.names` array has been replaced with the
  `jsTokens.matchToToken` function. The capturing groups of `jsTokens` are no
  longer part of the public API; instead use said function. See this [gist] for
  an example. (Backwards-incompatible change.)
- Changed: The empty string is now considered an “invalid” token, instead an
  “empty” token (its own group). (Backwards-incompatible change.)
- Removed: component support. (Backwards-incompatible change.)

[gist]: https://gist.github.com/lydell/be49dbf80c382c473004

### Version 0.2.0 (2014-06-19) ###

- Changed: Match ES6 function arrows (`=>`) as an operator, instead of its own
  category (“functionArrow”), for simplicity. (Backwards-incompatible change.)
- Added: ES6 splats (`...`) are now matched as an operator (instead of three
  punctuations). (Backwards-incompatible change.)

### Version 0.1.0 (2014-03-08) ###

- Initial release.


code:
Overview [![Build Status](https://travis-ci.org/lydell/js-tokens.svg?branch=master)](https://travis-ci.org/lydell/js-tokens)
========

A regex that tokenizes JavaScript.

```js
var jsTokens = require("js-tokens").default

var jsString = "var foo=opts.foo;\n..."

jsString.match(jsTokens)
// ["var", " ", "foo", "=", "opts", ".", "foo", ";", "\n", ...]
```

Installation
============

`npm install js-tokens`

```js
import jsTokens from "js-tokens"
// or:
var jsTokens = require("js-tokens").default
```

Usage
=====

### `jsTokens` ###

A regex with the `g` flag that matches JavaScript tokens.

The regex _always_ matches, even invalid JavaScript and the empty string.

The next match is always directly after the previous.

### `var token = matchToToken(match)` ###

```js
import {matchToToken} from "js-tokens"
// or:
var matchToToken = require("js-tokens").matchToToken
```

Takes a `match` returned by `jsTokens.exec(string)`, and returns a `{type:
String, value: String}` object. The following types are available:

- string
- comment
- regex
- number
- name
- punctuator
- whitespace
- invalid

Multi-line comments and strings also have a `closed` property indicating if the
token was closed or not (see below).

Comments and strings both come in several flavors. To distinguish them, check if
the token starts with `//`, `/*`, `'`, `"` or `` ` ``.

Names are ECMAScript IdentifierNames, that is, including both identifiers and
keywords. You may use [is-keyword-js] to tell them apart.

Whitespace includes both line terminators and other whitespace.

[is-keyword-js]: https://github.com/crissdev/is-keyword-js

ECMAScript support
==================

The intention is to always support the latest ECMAScript version whose feature
set has been finalized.

If adding support for a newer version requires changes, a new version with a
major verion bump will be released.

Currently, ECMAScript 2018 is supported.

Invalid code handling
=====================

Unterminated strings are still matched as strings. JavaScript strings cannot
contain (unescaped) newlines, so unterminated strings simply end at the end of
the line. Unterminated template strings can contain unescaped newlines, though,
so they go on to the end of input.

Unterminated multi-line comments are also still matched as comments. They
simply go on to the end of the input.

Unterminated regex literals are likely matched as division and whatever is
inside the regex.

Invalid ASCII characters have their own capturing group.

Invalid non-ASCII characters are treated as names, to simplify the matching of
names (except unicode spaces which are treated as whitespace). Note: See also
the [ES2018](#es2018) section.

Regex literals may contain invalid regex syntax. They are still matched as
regex literals. They may also contain repeated regex flags, to keep the regex
simple.

Strings may contain invalid escape sequences.

Limitations
===========

Tokenizing JavaScript using regexes—in fact, _one single regex_—won’t be
perfect. But that’s not the point either.

You may compare jsTokens with [esprima] by using `esprima-compare.js`.
See `npm run esprima-compare`!

[esprima]: http://esprima.org/

### Template string interpolation ###

Template strings are matched as single tokens, from the starting `` ` `` to the
ending `` ` ``, including interpolations (whose tokens are not matched
individually).

Matching template string interpolations requires recursive balancing of `{` and
`}`—something that JavaScript regexes cannot do. Only one level of nesting is
supported.

### Division and regex literals collision ###

Consider this example:

```js
var g = 9.82
var number = bar / 2/g

var regex = / 2/g
```

A human can easily understand that in the `number` line we’re dealing with
division, and in the `regex` line we’re dealing with a regex literal. How come?
Because humans can look at the whole code to put the `/` characters in context.
A JavaScript regex cannot. It only sees forwards. (Well, ES2018 regexes can also
look backwards. See the [ES2018](#es2018) section).

When the `jsTokens` regex scans throught the above, it will see the following
at the end of both the `number` and `regex` rows:

```js
/ 2/g
```

It is then impossible to know if that is a regex literal, or part of an
expression dealing with division.

Here is a similar case:

```js
foo /= 2/g
foo(/= 2/g)
```

The first line divides the `foo` variable with `2/g`. The second line calls the
`foo` function with the regex literal `/= 2/g`. Again, since `jsTokens` only
sees forwards, it cannot tell the two cases apart.

There are some cases where we _can_ tell division and regex literals apart,
though.

First off, we have the simple cases where there’s only one slash in the line:

```js
var foo = 2/g
foo /= 2
```

Regex literals cannot contain newlines, so the above cases are correctly
identified as division. Things are only problematic when there are more than
one non-comment slash in a single line.

Secondly, not every character is a valid regex flag.

```js
var number = bar / 2/e
```

The above example is also correctly identified as division, because `e` is not a
valid regex flag. I initially wanted to future-proof by allowing `[a-zA-Z]*`
(any letter) as flags, but it is not worth it since it increases the amount of
ambigous cases. So only the standard `g`, `m`, `i`, `y` and `u` flags are
allowed. This means that the above example will be identified as division as
long as you don’t rename the `e` variable to some permutation of `gmiyus` 1 to 6
characters long.

Lastly, we can look _forward_ for information.

- If the token following what looks like a regex literal is not valid after a
  regex literal, but is valid in a division expression, then the regex literal
  is treated as division instead. For example, a flagless regex cannot be
  followed by a string, number or name, but all of those three can be the
  denominator of a division.
- Generally, if what looks like a regex literal is followed by an operator, the
  regex literal is treated as division instead. This is because regexes are
  seldomly used with operators (such as `+`, `*`, `&&` and `==`), but division
  could likely be part of such an expression.

Please consult the regex source and the test cases for precise information on
when regex or division is matched (should you need to know). In short, you
could sum it up as:

If the end of a statement looks like a regex literal (even if it isn’t), it
will be treated as one. Otherwise it should work as expected (if you write sane
code).

### ES2018 ###

ES2018 added some nice regex improvements to the language.

- [Unicode property escapes] should allow telling names and invalid non-ASCII
  characters apart without blowing up the regex size.
- [Lookbehind assertions] should allow matching telling division and regex
  literals apart in more cases.
- [Named capture groups] might simplify some things.

These things would be nice to do, but are not critical. They probably have to
wait until the oldest maintained Node.js LTS release supports those features.

[Unicode property escapes]: http://2ality.com/2017/07/regexp-unicode-property-escapes.html
[Lookbehind assertions]: http://2ality.com/2017/05/regexp-lookbehind-assertions.html
[Named capture groups]: http://2ality.com/2017/05/regexp-named-capture-groups.html

License
=======

[MIT](LICENSE).


js-tokens

author: Simon Lydell

description: A regex that tokenizes JavaScript.

keywords: JavaScript js token tokenize regex

repository: lydell/js-tokens

test: mocha --ui tdd

esprima-compare: node esprima-compare ./index.js everything.js/es5.js

build: node generate-index.js

dev: npm run build && npm test


code:
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [4.1.0] - 2021-04-15
### Added
- Types are now exported as `yaml.types.XXX`.
- Every type now has `options` property with original arguments kept as they were
  (see `yaml.types.int.options` as an example).

### Changed
- `Schema.extend()` now keeps old type order in case of conflicts
  (e.g. Schema.extend([ a, b, c ]).extend([ b, a, d ]) is now ordered as `abcd` instead of `cbad`).

## [4.0.0] - 2021-01-03
### Changed
- Check [migration guide](migrate_v3_to_v4.md) to see details for all breaking changes.
- Breaking: "unsafe" tags `!!js/function`, `!!js/regexp`, `!!js/undefined` are
  moved to [js-yaml-js-types](https://github.com/nodeca/js-yaml-js-types) package.
- Breaking: removed `safe*` functions. Use `load`, `loadAll`, `dump`
  instead which are all now safe by default.
- `yaml.DEFAULT_SAFE_SCHEMA` and `yaml.DEFAULT_FULL_SCHEMA` are removed, use
  `yaml.DEFAULT_SCHEMA` instead.
- `yaml.Schema.create(schema, tags)` is removed, use `schema.extend(tags)` instead.
- `!!binary` now always mapped to `Uint8Array` on load.
- Reduced nesting of `/lib` folder.
- Parse numbers according to YAML 1.2 instead of YAML 1.1 (`01234` is now decimal,
  `0o1234` is octal, `1:23` is parsed as string instead of base60).
- `dump()` no longer quotes `:`, `[`, `]`, `(`, `)` except when necessary, #470, #557.
- Line and column in exceptions are now formatted as `(X:Y)` instead of
  `at line X, column Y` (also present in compact format), #332.
- Code snippet created in exceptions now contains multiple lines with line numbers.
- `dump()` now serializes `undefined` as `null` in collections and removes keys with
  `undefined` in mappings, #571.
- `dump()` with `skipInvalid=true` now serializes invalid items in collections as null.
- Custom tags starting with `!` are now dumped as `!tag` instead of `!<!tag>`, #576.
- Custom tags starting with `tag:yaml.org,2002:` are now shorthanded using `!!`, #258.

### Added
- Added `.mjs` (es modules) support.
- Added `quotingType` and `forceQuotes` options for dumper to configure
  string literal style, #290, #529.
- Added `styles: { '!!null': 'empty' }` option for dumper
  (serializes `{ foo: null }` as "`foo: `"), #570.
- Added `replacer` option (similar to option in JSON.stringify), #339.
- Custom `Tag` can now handle all tags or multiple tags with the same prefix, #385.

### Fixed
- Astral characters are no longer encoded by `dump()`, #587.
- "duplicate mapping key" exception now points at the correct column, #452.
- Extra commas in flow collections (e.g. `[foo,,bar]`) now throw an exception
  instead of producing null, #321.
- `__proto__` key no longer overrides object prototype, #164.
- Removed `bower.json`.
- Tags are now url-decoded in `load()` and url-encoded in `dump()`
  (previously usage of custom non-ascii tags may have led to invalid YAML that can't be parsed).
- Anchors now work correctly with empty nodes, #301.
- Fix incorrect parsing of invalid block mapping syntax, #418.
- Throw an error if block sequence/mapping indent contains a tab, #80.

## [3.14.1] - 2020-12-07
### Security
- Fix possible code execution in (already unsafe) `.load()` (in &anchor).

## [3.14.0] - 2020-05-22
### Changed
- Support `safe/loadAll(input, options)` variant of call.
- CI: drop outdated nodejs versions.
- Dev deps bump.

### Fixed
- Quote `=` in plain scalars #519.
- Check the node type for `!<?>` tag in case user manually specifies it.
- Verify that there are no null-bytes in input.
- Fix wrong quote position when writing condensed flow, #526.

## [3.13.1] - 2019-04-05
### Security
- Fix possible code execution in (already unsafe) `.load()`, #480.

## [3.13.0] - 2019-03-20
### Security
- Security fix: `safeLoad()` can hang when arrays with nested refs
  used as key. Now throws exception for nested arrays. #475.

## [3.12.2] - 2019-02-26
### Fixed
- Fix `noArrayIndent` option for root level, #468.

## [3.12.1] - 2019-01-05
### Added
- Added `noArrayIndent` option, #432.

## [3.12.0] - 2018-06-02
### Changed
- Support arrow functions without a block statement, #421.

## [3.11.0] - 2018-03-05
### Added
- Add arrow functions suport for `!!js/function`.

### Fixed
- Fix dump in bin/octal/hex formats for negative integers, #399.

## [3.10.0] - 2017-09-10
### Fixed
- Fix `condenseFlow` output (quote keys for sure, instead of spaces), #371, #370.
- Dump astrals as codepoints instead of surrogate pair, #368.

## [3.9.1] - 2017-07-08
### Fixed
- Ensure stack is present for custom errors in node 7.+, #351.

## [3.9.0] - 2017-07-08
### Added
- Add `condenseFlow` option (to create pretty URL query params), #346.

### Fixed
- Support array return from safeLoadAll/loadAll, #350.

## [3.8.4] - 2017-05-08
### Fixed
- Dumper: prevent space after dash for arrays that wrap, #343.

## [3.8.3] - 2017-04-05
### Fixed
- Should not allow numbers to begin and end with underscore, #335.

## [3.8.2] - 2017-03-02
### Fixed
- Fix `!!float 123` (integers) parse, #333.
- Don't allow leading zeros in floats (except 0, 0.xxx).
- Allow positive exponent without sign in floats.

## [3.8.1] - 2017-02-07
### Changed
- Maintenance: update browserified build.

## [3.8.0] - 2017-02-07
### Fixed
- Fix reported position for `duplicated mapping key` errors.
  Now points to block start instead of block end.
  (#243, thanks to @shockey).

## [3.7.0] - 2016-11-12
### Added
- Support polymorphism for tags (#300, thanks to @monken).

### Fixed
- Fix parsing of quotes followed by newlines (#304, thanks to @dplepage).

## [3.6.1] - 2016-05-11
### Fixed
- Fix output cut on a pipe, #286.

## [3.6.0] - 2016-04-16
### Fixed
- Dumper rewrite, fix multiple bugs with trailing `\n`.
  Big thanks to @aepsilon!
- Loader: fix leading/trailing newlines in block scalars, @aepsilon.

## [3.5.5] - 2016-03-17
### Fixed
- Date parse fix: don't allow dates with on digit in month and day, #268.

## [3.5.4] - 2016-03-09
### Added
- `noCompatMode` for dumper, to disable quoting YAML 1.1 values.

## [3.5.3] - 2016-02-11
### Changed
- Maintenance release.

## [3.5.2] - 2016-01-11
### Changed
- Maintenance: missed comma in bower config.

## [3.5.1] - 2016-01-11
### Changed
- Removed `inherit` dependency, #239.
- Better browserify workaround for esprima load.
- Demo rewrite.

## [3.5.0] - 2016-01-10
### Fixed
- Dumper. Fold strings only, #217.
- Dumper. `norefs` option, to clone linked objects, #229.
- Loader. Throw a warning for duplicate keys, #166.
- Improved browserify support (mark `esprima` & `Buffer` excluded).

## [3.4.6] - 2015-11-26
### Changed
- Use standalone `inherit` to keep browserified files clear.

## [3.4.5] - 2015-11-23
### Added
- Added `lineWidth` option to dumper.

## [3.4.4] - 2015-11-21
### Fixed
- Fixed floats dump (missed dot for scientific format), #220.
- Allow non-printable characters inside quoted scalars, #192.

## [3.4.3] - 2015-10-10
### Changed
- Maintenance release - deps bump (esprima, argparse).

## [3.4.2] - 2015-09-09
### Fixed
- Fixed serialization of duplicated entries in sequences, #205.
  Thanks to @vogelsgesang.

## [3.4.1] - 2015-09-05
### Fixed
- Fixed stacktrace handling in generated errors, for browsers (FF/IE).

## [3.4.0] - 2015-08-23
### Changed
- Don't throw on warnings anymore. Use `onWarning` option to catch.
- Throw error on unknown tags (was warning before).
- Reworked internals of error class.

### Fixed
- Fixed multiline keys dump, #197. Thanks to @tcr.
- Fixed heading line breaks in some scalars (regression).

## [3.3.1] - 2015-05-13
### Added
- Added `.sortKeys` dumper option, thanks to @rjmunro.

### Fixed
- Fixed astral characters support, #191.

## [3.3.0] - 2015-04-26
### Changed
- Significantly improved long strings formatting in dumper, thanks to @isaacs.
- Strip BOM if exists.

## [3.2.7] - 2015-02-19
### Changed
- Maintenance release.
- Updated dependencies.
- HISTORY.md -> CHANGELOG.md

## [3.2.6] - 2015-02-07
### Fixed
- Fixed encoding of UTF-16 surrogate pairs. (e.g. "\U0001F431" CAT FACE).
- Fixed demo dates dump (#113, thanks to @Hypercubed).

## [3.2.5] - 2014-12-28
### Fixed
- Fixed resolving of all built-in types on empty nodes.
- Fixed invalid warning on empty lines within quoted scalars and flow collections.
- Fixed bug: Tag on an empty node didn't resolve in some cases.

## [3.2.4] - 2014-12-19
### Fixed
- Fixed resolving of !!null tag on an empty node.

## [3.2.3] - 2014-11-08
### Fixed
- Implemented dumping of objects with circular and cross references.
- Partially fixed aliasing of constructed objects. (see issue #141 for details)

## [3.2.2] - 2014-09-07
### Fixed
- Fixed infinite loop on unindented block scalars.
- Rewritten base64 encode/decode in binary type, to keep code licence clear.

## [3.2.1] - 2014-08-24
### Fixed
- Nothig new. Just fix npm publish error.

## [3.2.0] - 2014-08-24
### Added
- Added input piping support to CLI.

### Fixed
- Fixed typo, that could cause hand on initial indent (#139).

## [3.1.0] - 2014-07-07
### Changed
- 1.5x-2x speed boost.
- Removed deprecated `require('xxx.yml')` support.
- Significant code cleanup and refactoring.
- Internal API changed. If you used custom types - see updated examples.
  Others are not affected.
- Even if the input string has no trailing line break character,
  it will be parsed as if it has one.
- Added benchmark scripts.
- Moved bower files to /dist folder
- Bugfixes.

## [3.0.2] - 2014-02-27
### Fixed
- Fixed bug: "constructor" string parsed as `null`.

## [3.0.1] - 2013-12-22
### Fixed
- Fixed parsing of literal scalars. (issue #108)
- Prevented adding unnecessary spaces in object dumps. (issue #68)
- Fixed dumping of objects with very long (> 1024 in length) keys.

## [3.0.0] - 2013-12-16
### Changed
- Refactored code. Changed API for custom types.
- Removed output colors in CLI, dump json by default.
- Removed big dependencies from browser version (esprima, buffer). Load `esprima` manually, if `!!js/function` needed. `!!bin` now returns Array in browser
- AMD support.
- Don't quote dumped strings because of `-` & `?` (if not first char).
- __Deprecated__ loading yaml files via `require()`, as not recommended
  behaviour for node.

## [2.1.3] - 2013-10-16
### Fixed
- Fix wrong loading of empty block scalars.

## [2.1.2] - 2013-10-07
### Fixed
- Fix unwanted line breaks in folded scalars.

## [2.1.1] - 2013-10-02
### Fixed
- Dumper now respects deprecated booleans syntax from YAML 1.0/1.1
- Fixed reader bug in JSON-like sequences/mappings.

## [2.1.0] - 2013-06-05
### Added
- Add standard YAML schemas: Failsafe (`FAILSAFE_SCHEMA`),
  JSON (`JSON_SCHEMA`) and Core (`CORE_SCHEMA`).
- Add `skipInvalid` dumper option.

### Changed
- Rename `DEFAULT_SCHEMA` to `DEFAULT_FULL_SCHEMA`
  and `SAFE_SCHEMA` to `DEFAULT_SAFE_SCHEMA`.
- Use `safeLoad` for `require` extension.

### Fixed
- Bug fix: export `NIL` constant from the public interface.

## [2.0.5] - 2013-04-26
### Security
- Close security issue in !!js/function constructor.
  Big thanks to @nealpoole for security audit.

## [2.0.4] - 2013-04-08
### Changed
- Updated .npmignore to reduce package size

## [2.0.3] - 2013-02-26
### Fixed
- Fixed dumping of empty arrays ans objects. ([] and {} instead of null)

## [2.0.2] - 2013-02-15
### Fixed
- Fixed input validation: tabs are printable characters.

## [2.0.1] - 2013-02-09
### Fixed
- Fixed error, when options not passed to function cass

## [2.0.0] - 2013-02-09
### Changed
- Full rewrite. New architecture. Fast one-stage parsing.
- Changed custom types API.
- Added YAML dumper.

## [1.0.3] - 2012-11-05
### Fixed
- Fixed utf-8 files loading.

## [1.0.2] - 2012-08-02
### Fixed
- Pull out hand-written shims. Use ES5-Shims for old browsers support. See #44.
- Fix timstamps incorectly parsed in local time when no time part specified.

## [1.0.1] - 2012-07-07
### Fixed
- Fixes `TypeError: 'undefined' is not an object` under Safari. Thanks Phuong.
- Fix timestamps incorrectly parsed in local time. Thanks @caolan. Closes #46.

## [1.0.0] - 2012-07-01
### Changed
- `y`, `yes`, `n`, `no`, `on`, `off` are not converted to Booleans anymore.
  Fixes #42.
- `require(filename)` now returns a single document and throws an Error if
  file contains more than one document.
- CLI was merged back from js-yaml.bin

## [0.3.7] - 2012-02-28
### Fixed
- Fix export of `addConstructor()`. Closes #39.

## [0.3.6] - 2012-02-22
### Changed
- Removed AMD parts - too buggy to use. Need help to rewrite from scratch

### Fixed
- Removed YUI compressor warning (renamed `double` variable). Closes #40.

## [0.3.5] - 2012-01-10
### Fixed
- Workagound for .npmignore fuckup under windows. Thanks to airportyh.

## [0.3.4] - 2011-12-24
### Fixed
- Fixes str[] for oldIEs support.
- Adds better has change support for browserified demo.
- improves compact output of Error. Closes #33.

## [0.3.3] - 2011-12-20
### Added
- adds `compact` stringification of Errors.

### Changed
- jsyaml executable moved to separate module.

## [0.3.2] - 2011-12-16
### Added
- Added jsyaml executable.
- Added !!js/function support. Closes #12.

### Fixed
- Fixes ug with block style scalars. Closes #26.
- All sources are passing JSLint now.
- Fixes bug in Safari. Closes #28.
- Fixes bug in Opers. Closes #29.
- Improves browser support. Closes #20.

## [0.3.1] - 2011-11-18
### Added
- Added AMD support for browserified version.
- Added permalinks for online demo YAML snippets. Now we have YPaste service, lol.
- Added !!js/regexp and !!js/undefined types. Partially solves #12.

### Changed
- Wrapped browserified js-yaml into closure.

### Fixed
- Fixed the resolvement of non-specific tags. Closes #17.
- Fixed !!set mapping.
- Fixed month parse in dates. Closes #19.

## [0.3.0] - 2011-11-09
### Added
- Added browserified version. Closes #13.
- Added live demo of browserified version.
- Ported some of the PyYAML tests. See #14.

### Fixed
- Removed JS.Class dependency. Closes #3.
- Fixed timestamp bug when fraction was given.

## [0.2.2] - 2011-11-06
### Fixed
- Fixed crash on docs without ---. Closes #8.
- Fixed multiline string parse
- Fixed tests/comments for using array as key

## [0.2.1] - 2011-11-02
### Fixed
- Fixed short file read (<4k). Closes #9.

## [0.2.0] - 2011-11-02
### Changed
- First public release

[4.1.0]: https://github.com/nodeca/js-yaml/compare/4.0.0...4.1.0
[4.0.0]: https://github.com/nodeca/js-yaml/compare/3.14.0...4.0.0
[3.14.0]: https://github.com/nodeca/js-yaml/compare/3.13.1...3.14.0
[3.13.1]: https://github.com/nodeca/js-yaml/compare/3.13.0...3.13.1
[3.13.0]: https://github.com/nodeca/js-yaml/compare/3.12.2...3.13.0
[3.12.2]: https://github.com/nodeca/js-yaml/compare/3.12.1...3.12.2
[3.12.1]: https://github.com/nodeca/js-yaml/compare/3.12.0...3.12.1
[3.12.0]: https://github.com/nodeca/js-yaml/compare/3.11.0...3.12.0
[3.11.0]: https://github.com/nodeca/js-yaml/compare/3.10.0...3.11.0
[3.10.0]: https://github.com/nodeca/js-yaml/compare/3.9.1...3.10.0
[3.9.1]: https://github.com/nodeca/js-yaml/compare/3.9.0...3.9.1
[3.9.0]: https://github.com/nodeca/js-yaml/compare/3.8.4...3.9.0
[3.8.4]: https://github.com/nodeca/js-yaml/compare/3.8.3...3.8.4
[3.8.3]: https://github.com/nodeca/js-yaml/compare/3.8.2...3.8.3
[3.8.2]: https://github.com/nodeca/js-yaml/compare/3.8.1...3.8.2
[3.8.1]: https://github.com/nodeca/js-yaml/compare/3.8.0...3.8.1
[3.8.0]: https://github.com/nodeca/js-yaml/compare/3.7.0...3.8.0
[3.7.0]: https://github.com/nodeca/js-yaml/compare/3.6.1...3.7.0
[3.6.1]: https://github.com/nodeca/js-yaml/compare/3.6.0...3.6.1
[3.6.0]: https://github.com/nodeca/js-yaml/compare/3.5.5...3.6.0
[3.5.5]: https://github.com/nodeca/js-yaml/compare/3.5.4...3.5.5
[3.5.4]: https://github.com/nodeca/js-yaml/compare/3.5.3...3.5.4
[3.5.3]: https://github.com/nodeca/js-yaml/compare/3.5.2...3.5.3
[3.5.2]: https://github.com/nodeca/js-yaml/compare/3.5.1...3.5.2
[3.5.1]: https://github.com/nodeca/js-yaml/compare/3.5.0...3.5.1
[3.5.0]: https://github.com/nodeca/js-yaml/compare/3.4.6...3.5.0
[3.4.6]: https://github.com/nodeca/js-yaml/compare/3.4.5...3.4.6
[3.4.5]: https://github.com/nodeca/js-yaml/compare/3.4.4...3.4.5
[3.4.4]: https://github.com/nodeca/js-yaml/compare/3.4.3...3.4.4
[3.4.3]: https://github.com/nodeca/js-yaml/compare/3.4.2...3.4.3
[3.4.2]: https://github.com/nodeca/js-yaml/compare/3.4.1...3.4.2
[3.4.1]: https://github.com/nodeca/js-yaml/compare/3.4.0...3.4.1
[3.4.0]: https://github.com/nodeca/js-yaml/compare/3.3.1...3.4.0
[3.3.1]: https://github.com/nodeca/js-yaml/compare/3.3.0...3.3.1
[3.3.0]: https://github.com/nodeca/js-yaml/compare/3.2.7...3.3.0
[3.2.7]: https://github.com/nodeca/js-yaml/compare/3.2.6...3.2.7
[3.2.6]: https://github.com/nodeca/js-yaml/compare/3.2.5...3.2.6
[3.2.5]: https://github.com/nodeca/js-yaml/compare/3.2.4...3.2.5
[3.2.4]: https://github.com/nodeca/js-yaml/compare/3.2.3...3.2.4
[3.2.3]: https://github.com/nodeca/js-yaml/compare/3.2.2...3.2.3
[3.2.2]: https://github.com/nodeca/js-yaml/compare/3.2.1...3.2.2
[3.2.1]: https://github.com/nodeca/js-yaml/compare/3.2.0...3.2.1
[3.2.0]: https://github.com/nodeca/js-yaml/compare/3.1.0...3.2.0
[3.1.0]: https://github.com/nodeca/js-yaml/compare/3.0.2...3.1.0
[3.0.2]: https://github.com/nodeca/js-yaml/compare/3.0.1...3.0.2
[3.0.1]: https://github.com/nodeca/js-yaml/compare/3.0.0...3.0.1
[3.0.0]: https://github.com/nodeca/js-yaml/compare/2.1.3...3.0.0
[2.1.3]: https://github.com/nodeca/js-yaml/compare/2.1.2...2.1.3
[2.1.2]: https://github.com/nodeca/js-yaml/compare/2.1.1...2.1.2
[2.1.1]: https://github.com/nodeca/js-yaml/compare/2.1.0...2.1.1
[2.1.0]: https://github.com/nodeca/js-yaml/compare/2.0.5...2.1.0
[2.0.5]: https://github.com/nodeca/js-yaml/compare/2.0.4...2.0.5
[2.0.4]: https://github.com/nodeca/js-yaml/compare/2.0.3...2.0.4
[2.0.3]: https://github.com/nodeca/js-yaml/compare/2.0.2...2.0.3
[2.0.2]: https://github.com/nodeca/js-yaml/compare/2.0.1...2.0.2
[2.0.1]: https://github.com/nodeca/js-yaml/compare/2.0.0...2.0.1
[2.0.0]: https://github.com/nodeca/js-yaml/compare/1.0.3...2.0.0
[1.0.3]: https://github.com/nodeca/js-yaml/compare/1.0.2...1.0.3
[1.0.2]: https://github.com/nodeca/js-yaml/compare/1.0.1...1.0.2
[1.0.1]: https://github.com/nodeca/js-yaml/compare/1.0.0...1.0.1
[1.0.0]: https://github.com/nodeca/js-yaml/compare/0.3.7...1.0.0
[0.3.7]: https://github.com/nodeca/js-yaml/compare/0.3.6...0.3.7
[0.3.6]: https://github.com/nodeca/js-yaml/compare/0.3.5...0.3.6
[0.3.5]: https://github.com/nodeca/js-yaml/compare/0.3.4...0.3.5
[0.3.4]: https://github.com/nodeca/js-yaml/compare/0.3.3...0.3.4
[0.3.3]: https://github.com/nodeca/js-yaml/compare/0.3.2...0.3.3
[0.3.2]: https://github.com/nodeca/js-yaml/compare/0.3.1...0.3.2
[0.3.1]: https://github.com/nodeca/js-yaml/compare/0.3.0...0.3.1
[0.3.0]: https://github.com/nodeca/js-yaml/compare/0.2.2...0.3.0
[0.2.2]: https://github.com/nodeca/js-yaml/compare/0.2.1...0.2.2
[0.2.1]: https://github.com/nodeca/js-yaml/compare/0.2.0...0.2.1
[0.2.0]: https://github.com/nodeca/js-yaml/releases/tag/0.2.0