text: /**
   * Module dependencies
   */
  
  var assert = require('assert');
  var util = require('util');
  var _ = require('@sailshq/lodash');
  // var EA = require('encrypted-attr'); « this is required below for node compat.
  var flaverr = require('flaverr');
  var rttc = require('rttc');
  var eachRecordDeep = require('waterline-utils').eachRecordDeep;
  
  /**
   * Module constants
   */
  
  var WARNING_SUFFIXES = {
  
    MIGHT_BE_YOUR_FAULT:
    '\n'+
    '> You are seeing this warning because there are records in your database that don\'t\n'+
    '> match up with your models.  This is often the result of a model definition being\n'+
    '> changed without also migrating leftover data.  But it could also be because records\n'+
    '> were added or modified in your database from somewhere outside of Sails/Waterline\n'+
    '> (e.g. phpmyadmin, or another app).  In either case, to make this warning go away,\n'+
    '> you have a few options.  First of all, you could change your model definition so\n'+
    '> that it matches the existing records in your database.  Or you could update/destroy\n'+
    '> the old records in your database; either by hand, or using a migration script.\n'+
    '> \n'+
    (process.env.NODE_ENV !== 'production' ? '> (For example, to wipe all data, you might just use `migrate: drop`.)\n' : '')+
    '> \n'+
    '> More rarely, this warning could mean there is a bug in the adapter itself.  If you\n'+
    '> believe that is the case, then please contact the maintainer of this adapter by opening\n'+
    '> an issue, or visit http://sailsjs.com/support for help.\n',
  
    HARD_TO_SEE_HOW_THIS_COULD_BE_YOUR_FAULT:
    '\n'+
    '> This is usally caused by a bug in the adapter itself.  If you believe that\n'+
    '> might be the case here, then please contact the maintainer of this adapter by\n'+
    '> opening an issue, or visit http://sailsjs.com/support for help.\n'
  
  };
  
  
  /**
   * processAllRecords()
   *
   * Process potentially-populated records coming back from the adapter, AFTER they've already had
   * their keys transformed from column names back to attribute names and had populated data reintegrated.
   * To reiterate that: this function takes logical records, **NOT physical records**.
   *
   * `processAllRecords()` has 3 responsibilities:
   *
   * (1) Verify the integrity of the provided records, and any populated child records
   *     (Note: If present, child records only ever go 1 level deep in Waterline currently.)
   *      > At the moment, this serves primarily as a way to check for stale, unmigrated data that
   *      > might exist in the database, as well as any unexpected adapter compatibility problems.
   *      > For the full specification and expected behavior, see:
   *      > https://docs.google.com/spreadsheets/d/1whV739iW6O9SxRZLCIe2lpvuAUqm-ie7j7tn_Pjir3s/edit#gid=1927470769
   *
   * (2) Attach custom toJSON() functions to records, if the model says to do so.
   *
   * (3) Decrypt any data that was encrypted at rest.
   *
   * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   *
   * @param  {Array} records
   *         An array of records.  (These are logical records -- NOT physical records!!)
   *         (WARNING: This array and its deeply-nested contents might be mutated in-place!!!)
   *
   * @param {Ref?} meta
   *        The `meta` query key for the query.
   *
   * @param {String} modelIdentity
   *        The identity of the model these records came from (e.g. "pet" or "user")
   *        > Useful for looking up the Waterline model and accessing its attribute definitions.
   *
   * @param {Ref} orm
   *        The Waterline ORM instance.
   *        > Useful for accessing the model definitions.
   *
   * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   */
  module.exports = function processAllRecords(records, meta, modelIdentity, orm) {
    // console.time('processAllRecords');
  
  
    if (!_.isArray(records)) {
      throw new Error('Consistency violation: Expected `records` to be an array.  But instead, got: '+util.inspect(records,{depth:5})+'');
    }
  
    if (!_.isUndefined(meta) && !_.isObject(meta)) {
      throw new Error('Consistency violation: Expected `meta` to be a dictionary, or undefined.  But instead, got: '+util.inspect(meta,{depth:5})+'');
    }
  
    if (!_.isString(modelIdentity) || modelIdentity === '') {
      throw new Error('Consistency violation: Expected `modelIdentity` to be a non-empty string.  But instead, got: '+util.inspect(modelIdentity,{depth:5})+'');
    }
  
  
    // Determine whether to skip record verification below.
    // (we always do it unless the `skipRecordVerification` meta key is explicitly truthy,)
    var skippingRecordVerification = meta && meta.skipRecordVerification;
  
  
    // Iterate over each parent record and any nested arrays/dictionaries that
    // appear to be populated child records.
    eachRecordDeep(records, function _eachParentOrChildRecord(record, WLModel){
  
      // First, check the results to verify compliance with the adapter spec.,
      // as well as any issues related to stale data that might not have been
      // been migrated to keep up with the logical schema (`type`, etc. in
      // attribute definitions).
      if (!skippingRecordVerification) {
  
  
        //  ███╗   ██╗ ██████╗ ███╗   ██╗       █████╗ ████████╗████████╗██████╗ ██╗██████╗ ██╗   ██╗████████╗███████╗
        //  ████╗  ██║██╔═══██╗████╗  ██║      ██╔══██╗╚══██╔══╝╚══██╔══╝██╔══██╗██║██╔══██╗██║   ██║╚══██╔══╝██╔════╝
        //  ██╔██╗ ██║██║   ██║██╔██╗ ██║█████╗███████║   ██║      ██║   ██████╔╝██║██████╔╝██║   ██║   ██║   █████╗
        //  ██║╚██╗██║██║   ██║██║╚██╗██║╚════╝██╔══██║   ██║      ██║   ██╔══██╗██║██╔══██╗██║   ██║   ██║   ██╔══╝
        //  ██║ ╚████║╚██████╔╝██║ ╚████║      ██║  ██║   ██║      ██║   ██║  ██║██║██████╔╝╚██████╔╝   ██║   ███████╗
        //  ╚═╝  ╚═══╝ ╚═════╝ ╚═╝  ╚═══╝      ╚═╝  ╚═╝   ╚═╝      ╚═╝   ╚═╝  ╚═╝╚═╝╚═════╝  ╚═════╝    ╚═╝   ╚══════╝
        //
        //  ██╗  ██╗███████╗██╗   ██╗███████╗
        //  ██║ ██╔╝██╔════╝╚██╗ ██╔╝██╔════╝
        //  █████╔╝ █████╗   ╚████╔╝ ███████╗
        //  ██╔═██╗ ██╔══╝    ╚██╔╝  ╚════██║
        //  ██║  ██╗███████╗   ██║   ███████║
        //  ╚═╝  ╚═╝╚══════╝   ╚═╝   ╚══════╝
        //
        // If this model is defined as `schema: true`, then check the returned record
        // for any extraneous keys which do not correspond with declared attributes.
        // If any are found, then log a warning.
        if (WLModel.hasSchema) {
  
          var nonAttrKeys = _.difference(_.keys(record), _.keys(WLModel.attributes));
          if (nonAttrKeys > 0) {
  
            // Since this is `schema: true`, the adapter method should have
            // received an explicit `select` clause in the S3Q `criteria`
            // query key, and thus it should not have sent back any unrecognized
            // attributes (or in cases where there is no `criteria` query key, e.g.
            // a create(), the adapter should never send back extraneous properties
            // anyways, because Waterline core should have stripped any such extra
            // properties off on the way _in_ to the adapter).
            //
            // So if we made it here, we can safely assume that this is due
            // to an issue in the _adapter_ -- not some problem with unmigrated
            // data.
            console.warn('\n'+
              'Warning: A record in this result set has extraneous properties ('+nonAttrKeys+')\n'+
              'that, after adjusting for any custom columnNames, still do not correspond\n'+
              'any recognized attributes of this model (`'+WLModel.identity+'`).\n'+
              'Since this model is defined as `schema: true`, this behavior is unexpected.\n'+
              // ====================================================================================
              // Removed this for the sake of brevity-- could bring it back if deemed helpful.
              // ====================================================================================
              // 'This problem could be the result of an adapter method not properly observing\n'+
              // 'the `select` clause it receives in the incoming criteria (or otherwise sending\n'+
              // 'extra, unexpected properties on records that were left over from old data).\n'+
              // ====================================================================================
              WARNING_SUFFIXES.MIGHT_BE_YOUR_FAULT
            );
  
          }//</if>
  
        }//</if>
  
  
  
        //  ██╗  ██╗███████╗██╗   ██╗███████╗    ██╗    ██╗    ██╗    ██████╗ ██╗  ██╗███████╗
        //  ██║ ██╔╝██╔════╝╚██╗ ██╔╝██╔════╝    ██║    ██║   ██╔╝    ██╔══██╗██║  ██║██╔════╝
        //  █████╔╝ █████╗   ╚████╔╝ ███████╗    ██║ █╗ ██║  ██╔╝     ██████╔╝███████║███████╗
        //  ██╔═██╗ ██╔══╝    ╚██╔╝  ╚════██║    ██║███╗██║ ██╔╝      ██╔══██╗██╔══██║╚════██║
        //  ██║  ██╗███████╗   ██║   ███████║    ╚███╔███╔╝██╔╝       ██║  ██║██║  ██║███████║
        //  ╚═╝  ╚═╝╚══════╝   ╚═╝   ╚══════╝     ╚══╝╚══╝ ╚═╝        ╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝
        //
        //   ██████╗ ███████╗    ██╗   ██╗███╗   ██╗██████╗ ███████╗███████╗██╗███╗   ██╗███████╗██████╗
        //  ██╔═══██╗██╔════╝    ██║   ██║████╗  ██║██╔══██╗██╔════╝██╔════╝██║████╗  ██║██╔════╝██╔══██╗
        //  ██║   ██║█████╗      ██║   ██║██╔██╗ ██║██║  ██║█████╗  █████╗  ██║██╔██╗ ██║█████╗  ██║  ██║
        //  ██║   ██║██╔══╝      ██║   ██║██║╚██╗██║██║  ██║██╔══╝  ██╔══╝  ██║██║╚██╗██║██╔══╝  ██║  ██║
        //  ╚██████╔╝██║         ╚██████╔╝██║ ╚████║██████╔╝███████╗██║     ██║██║ ╚████║███████╗██████╔╝
        //   ╚═════╝ ╚═╝          ╚═════╝ ╚═╝  ╚═══╝╚═════╝ ╚══════╝╚═╝     ╚═╝╚═╝  ╚═══╝╚══════╝╚═════╝
        //
        // Loop over the properties of the record.
        _.each(_.keys(record), function (key){
  
          // Ensure that the value was not explicitly sent back as `undefined`.
          // (but if it was, log a warning.  Note that we don't strip it out like
          // we would normally, because we're careful not to munge data in this utility.)
          if(_.isUndefined(record[key])){
            console.warn('\n'+
              'Warning: A database adapter should never send back records that have `undefined`\n'+
              'on the RHS of any property (e.g. `foo: undefined`).  But after transforming\n'+
              'columnNames back to attribute names for the model `' + modelIdentity + '`, one\n'+
              'of the records sent back from this adapter has a property (`'+key+'`) with\n'+
              '`undefined` on the right-hand side.\n' +
              WARNING_SUFFIXES.HARD_TO_SEE_HOW_THIS_COULD_BE_YOUR_FAULT
            );
          }//>-
  
        });
  
  
  
        // Now, loop over each attribute in the model.
        _.each(WLModel.attributes, function (attrDef, attrName){
  
  
          //  ██████╗ ██████╗ ██╗███╗   ███╗ █████╗ ██████╗ ██╗   ██╗    ██╗  ██╗███████╗██╗   ██╗
          //  ██╔══██╗██╔══██╗██║████╗ ████║██╔══██╗██╔══██╗╚██╗ ██╔╝    ██║ ██╔╝██╔════╝╚██╗ ██╔╝
          //  ██████╔╝██████╔╝██║██╔████╔██║███████║██████╔╝ ╚████╔╝     █████╔╝ █████╗   ╚████╔╝
          //  ██╔═══╝ ██╔══██╗██║██║╚██╔╝██║██╔══██║██╔══██╗  ╚██╔╝      ██╔═██╗ ██╔══╝    ╚██╔╝
          //  ██║     ██║  ██║██║██║ ╚═╝ ██║██║  ██║██║  ██║   ██║       ██║  ██╗███████╗   ██║
          //  ╚═╝     ╚═╝  ╚═╝╚═╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝       ╚═╝  ╚═╝╚══════╝   ╚═╝
          //
          if (attrName === WLModel.primaryKey) {
  
            assert(!attrDef.allowNull, 'The primary key attribute should never be defined with `allowNull:true`.  (This should have already been caught in wl-schema during ORM initialization!  Please report this at http://sailsjs.com/bugs)');
  
            // Do quick, incomplete verification that a valid primary key value was sent back.
            var isProbablyValidPkValue = (
              record[attrName] !== '' &&
              (
                _.isString(record[attrName]) || _.isNumber(record[attrName])
              )
            );
  
            if (!isProbablyValidPkValue) {
              console.warn('\n'+
                'Warning: Records sent back from a database adapter should always have a valid property\n'+
                'that corresponds with the primary key attribute (`'+WLModel.primaryKey+'`).  But in this result set,\n'+
                'after transforming columnNames back to attribute names for model `' + modelIdentity + '`,\n'+
                'there is a record with a missing or invalid `'+WLModel.primaryKey+'`.\n'+
                'Record:\n'+
                '```\n'+
                util.inspect(record, {depth:5})+'\n'+
                '```\n'+
                WARNING_SUFFIXES.MIGHT_BE_YOUR_FAULT
              );
            }
  
          }
          //  ███████╗██╗███╗   ██╗ ██████╗ ██╗   ██╗██╗      █████╗ ██████╗
          //  ██╔════╝██║████╗  ██║██╔════╝ ██║   ██║██║     ██╔══██╗██╔══██╗
          //  ███████╗██║██╔██╗ ██║██║  ███╗██║   ██║██║     ███████║██████╔╝
          //  ╚════██║██║██║╚██╗██║██║   ██║██║   ██║██║     ██╔══██║██╔══██╗
          //  ███████║██║██║ ╚████║╚██████╔╝╚██████╔╝███████╗██║  ██║██║  ██║
          //  ╚══════╝╚═╝╚═╝  ╚═══╝ ╚═════╝  ╚═════╝ ╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝
          //
          else if (attrDef.model) {
  
            assert(!attrDef.allowNull, 'Singular ("model") association attributes should never be defined with `allowNull:true` (they always allow null, by nature!).  (This should have already been caught in wl-schema during ORM initialization!  Please report this at http://sailsjs.com/bugs)');
  
            // If record does not define a value for a singular association, that's ok.
            // It may have been deliberately excluded by the `select` or `omit` clause.
            if (_.isUndefined(record[attrName])) {
            }
            // If the value for this singular association came back as `null`, then that
            // might be ok too-- it could mean that the association is empty.
            // (Note that it might also mean that it is set, and that population was attempted,
            // but that it failed; presumably because the associated child record no longer exists)
            else if (_.isNull(record[attrName])) {
            }
            // If the value came back as something that looks vaguely like a valid primary key value,
            // then that's probably ok--  it could mean that the association was set, but not populated.
            else if ((_.isString(record[attrName]) || _.isNumber(record[attrName])) && record[attrName] !== '' && !_.isNaN(record[attrName])) {
            }
            // If the value came back as a dictionary, then that might be ok-- it could mean
            // the association was successfully populated.
            else if (_.isObject(record[attrName]) && !_.isArray(record[attrName]) && !_.isFunction(record[attrName])) {
              // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
              // FUTURE: we could check this more carefully in the future by providing more
              // information to this utility-- specifically, the `populates` key from the S2Q.
              // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            }
            // Otherwise, the value is invalid.
            else {
              console.warn('\n'+
                'An association in a result record has an unexpected data type.  Since `'+attrName+'` is\n'+
                'a singular (association), it should come back from Waterline as either:\n'+
                '• `null` (if not populated and set to null explicitly, or populated but orphaned)\n'+
                '• a dictionary (if successfully populated), or\n'+
                '• a valid primary key value for the associated model (if set + not populated)\n'+
                'But for this record, after converting column names back into attribute names, it\n'+
                'wasn\'t any of those things.\n'+
                'Record:\n'+
                '```\n'+
                util.inspect(record, {depth:5})+'\n'+
                '```\n'+
                WARNING_SUFFIXES.MIGHT_BE_YOUR_FAULT
              );
            }
  
          }
          //  ██████╗ ██╗     ██╗   ██╗██████╗  █████╗ ██╗
          //  ██╔══██╗██║     ██║   ██║██╔══██╗██╔══██╗██║
          //  ██████╔╝██║     ██║   ██║██████╔╝███████║██║
          //  ██╔═══╝ ██║     ██║   ██║██╔══██╗██╔══██║██║
          //  ██║     ███████╗╚██████╔╝██║  ██║██║  ██║███████╗
          //  ╚═╝     ╚══════╝ ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝
          //
          else if (attrDef.collection) {
            assert(!attrDef.allowNull, 'Plural ("collection") association attributes should never be defined with `allowNull:true`.  (This should have already been caught in wl-schema during ORM initialization!  Please report this at http://sailsjs.com/bugs)');
  
            // If record does not define a value for a plural association, that's ok.
            // That probably just means it was not populated.
            if (_.isUndefined(record[attrName])) {
            }
            // If the value for this singular association came back as an array, then
            // that might be ok too-- it probably means that the association was populated.
            else if (_.isArray(record[attrName])) {
              // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
              // FUTURE: we could check that it is an array of valid child records,
              // instead of just verifying that it is an array of _some kind_.
              // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            }
            // Otherwise, the value is invalid.
            else {
              console.warn('\n'+
                'An association in a result record has an unexpected data type.  Since `'+attrName+'` is\n'+
                'a plural (association), it should come back from Waterline as either:\n'+
                '• `undefined` (if not populated), or\n'+
                '• an array of child records (if populated)\n'+
                'But for this record, it wasn\'t any of those things.\n'+
                // Note that this could mean there was something else already there
                // (imagine changing your model to use a plural association instead
                // of an embedded array from a `type: 'json'` attribute)
                'Record:\n'+
                '```\n'+
                util.inspect(record, {depth:5})+'\n'+
                '```\n'+
                WARNING_SUFFIXES.MIGHT_BE_YOUR_FAULT
              );
            }
  
          }
          //  ███████╗████████╗ █████╗ ███╗   ███╗██████╗ ███████╗
          //  ██╔════╝╚══██╔══╝██╔══██╗████╗ ████║██╔══██╗██╔════╝
          //  ███████╗   ██║   ███████║██╔████╔██║██████╔╝███████╗
          //  ╚════██║   ██║   ██╔══██║██║╚██╔╝██║██╔═══╝ ╚════██║
          //  ███████║   ██║   ██║  ██║██║ ╚═╝ ██║██║     ███████║
          //  ╚══════╝   ╚═╝   ╚═╝  ╚═╝╚═╝     ╚═╝╚═╝     ╚══════╝
          //
          else if (attrDef.autoCreatedAt || attrDef.autoUpdatedAt) {
  
            assert(!attrDef.allowNull, 'Timestamp attributes should never be defined with `allowNull:true`.  (This should have already been caught in wl-schema during ORM initialization!  Please report this at http://sailsjs.com/bugs)');
  
            // If there is no value defined on the record for this attribute...
            if (_.isUndefined(record[attrName])) {
  
              // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
              // FUTURE: Log a warning (but note that, to really get this right, we'd need access to
              // a clone of the `omit` and `select` clauses from the s2q criteria, plus the `populates`
              // query key from the s2q criteria -- probably also a clone of that)
              // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  
            }
            // Otherwise, we know there's SOMETHING there at least.
            else {
  
              // Do quick, very incomplete verification that a valid timestamp was sent back.
              var isProbablyValidTimestamp = (
                record[attrName] !== '' &&
                record[attrName] !== 0 &&
                (
                  _.isString(record[attrName]) || _.isNumber(record[attrName]) || _.isDate(record[attrName])
                )
              );
  
              if (!isProbablyValidTimestamp) {
                console.warn('\n'+
                  'Warning: After transforming columnNames back to attribute names for model `' + modelIdentity + '`,\n'+
                  ' a record in the result has a value with an unexpected data type for property `'+attrName+'`.\n'+
                  'The model\'s `'+attrName+'` attribute declares itself an auto timestamp with\n'+
                  '`type: \''+attrDef.type+'\'`, but instead of a valid timestamp, the actual value\n'+
                  'in the record is:\n'+
                  '```\n'+
                  util.inspect(record[attrName],{depth:5})+'\n'+
                  '```\n'+
                  WARNING_SUFFIXES.MIGHT_BE_YOUR_FAULT
                );
              }
  
            }//</else>
  
          }
          //  ███╗   ███╗██╗███████╗ ██████╗        ██╗████████╗██╗   ██╗██████╗ ███████╗██╗
          //  ████╗ ████║██║██╔════╝██╔════╝       ██╔╝╚══██╔══╝╚██╗ ██╔╝██╔══██╗██╔════╝╚██╗
          //  ██╔████╔██║██║███████╗██║            ██║    ██║    ╚████╔╝ ██████╔╝█████╗   ██║
          //  ██║╚██╔╝██║██║╚════██║██║            ██║    ██║     ╚██╔╝  ██╔═══╝ ██╔══╝   ██║
          //  ██║ ╚═╝ ██║██║███████║╚██████╗██╗    ╚██╗   ██║      ██║   ██║     ███████╗██╔╝
          //  ╚═╝     ╚═╝╚═╝╚══════╝ ╚═════╝╚═╝     ╚═╝   ╚═╝      ╚═╝   ╚═╝     ╚══════╝╚═╝
          //
          else {
  
            // Sanity check:
            if (attrDef.type === 'json' || attrDef.type === 'ref') {
              assert(!attrDef.allowNull, '`type:\'json\'` and `type:\'ref\'` attributes should never be defined with `allowNull:true`.  (This should have already been caught in wl-schema during ORM initialization!  Please report this at http://sailsjs.com/bugs)');
            }
  
            // If there is no value defined on the record for this attribute...
            if (_.isUndefined(record[attrName])) {
  
              // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
              // FUTURE: Log a warning (but note that, to really get this right, we'd need access to
              // a clone of the `omit` and `select` clauses from the s2q criteria, plus the `populates`
              // query key from the s2q criteria -- probably also a clone of that)
              // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  
            }
            // If the value is `null`, and the attribute has `allowNull:true`, then its ok.
            else if (_.isNull(record[attrName]) && attrDef.allowNull === true) {
              // Nothing to validate here.
            }
            // Otherwise, we'll need to validate the value.
            else {
  
              // Strictly validate the value vs. the attribute's `type`, and if it is
              // obviously incorrect, then log a warning (but don't actually coerce it.)
              try {
                rttc.validateStrict(attrDef.type, record[attrName]);
              } catch (e) {
                switch (e.code) {
                  case 'E_INVALID':
  
                    if (_.isNull(record[attrName])) {
                      console.warn('\n'+
                        'Warning: After transforming columnNames back to attribute names for model `' + modelIdentity + '`,\n'+
                        ' a record in the result has a value of `null` for property `'+attrName+'`.\n'+
                        'Since the `'+attrName+'` attribute declares `type: \''+attrDef.type+'\'`,\n'+
                        'without ALSO declaring `allowNull: true`, this `null` value is unexpected.\n'+
                        '(To resolve, either change this attribute to `allowNull: true` or update\n'+
                        'existing records in the database accordingly.)\n'+
                        WARNING_SUFFIXES.MIGHT_BE_YOUR_FAULT
                      );
                    }
                    else {
                      console.warn('\n'+
                        'Warning: After transforming columnNames back to attribute names for model `' + modelIdentity + '`,\n'+
                        ' a record in the result has a value with an unexpected data type for property `'+attrName+'`.\n'+
                        'The corresponding attribute declares `type: \''+attrDef.type+'\'` but instead\n'+
                        'of that, the actual value is:\n'+
                        '```\n'+
                        util.inspect(record[attrName],{depth:5})+'\n'+
                        '```\n'+
                        WARNING_SUFFIXES.MIGHT_BE_YOUR_FAULT
                      );
                    }
                    break;
                  default: throw e;
                }
              }//>-•
  
            }
  
          }
  
  
          //>-
  
          //   ██████╗██╗  ██╗███████╗ ██████╗██╗  ██╗
          //  ██╔════╝██║  ██║██╔════╝██╔════╝██║ ██╔╝
          //  ██║     ███████║█████╗  ██║     █████╔╝
          //  ██║     ██╔══██║██╔══╝  ██║     ██╔═██╗
          //  ╚██████╗██║  ██║███████╗╚██████╗██║  ██╗
          //   ╚═════╝╚═╝  ╚═╝╚══════╝ ╚═════╝╚═╝  ╚═╝
          //
          //  ██████╗ ███████╗ ██████╗ ██╗   ██╗██╗██████╗ ███████╗██████╗ ███╗   ██╗███████╗███████╗███████╗
          //  ██╔══██╗██╔════╝██╔═══██╗██║   ██║██║██╔══██╗██╔════╝██╔══██╗████╗  ██║██╔════╝██╔════╝██╔════╝
          //  ██████╔╝█████╗  ██║   ██║██║   ██║██║██████╔╝█████╗  ██║  ██║██╔██╗ ██║█████╗  ███████╗███████╗
          //  ██╔══██╗██╔══╝  ██║▄▄ ██║██║   ██║██║██╔══██╗██╔══╝  ██║  ██║██║╚██╗██║██╔══╝  ╚════██║╚════██║
          //  ██║  ██║███████╗╚██████╔╝╚██████╔╝██║██║  ██║███████╗██████╔╝██║ ╚████║███████╗███████║███████║
          //  ╚═╝  ╚═╝╚══════╝ ╚══▀▀═╝  ╚═════╝ ╚═╝╚═╝  ╚═╝╚══════╝╚═════╝ ╚═╝  ╚═══╝╚══════╝╚══════╝╚══════╝
          //
          // If attribute is required, check that the value returned in this record
          // is neither `null` nor empty string ('') nor `undefined`.
          if (attrDef.required) {
  
            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            // FUTURE: Log a warning (but note that, to really get this right, we'd need access to
            // a clone of the `omit` and `select` clauses from the s2q criteria, plus the `populates`
            // query key from the s2q criteria -- probably also a clone of that)
            //
            // ```
            // if (_.isUndefined(record[attrName]) || _.isNull(record[attrName]) || record[attrName] === '') {
            //   // (We'd also need to make sure this wasn't deliberately exluded by custom projections
            //   //  before logging this warning.)
            //   console.warn('\n'+
            //     'Warning: After transforming columnNames back to attribute names for model `' + modelIdentity + '`,\n'+
            //     'a record in the result contains an unexpected value (`'+util.inspect(record[attrName],{depth:1})+'`)`\n'+
            //     'for its `'+attrName+'` property.  Since `'+attrName+'` is a required attribute,\n'+
            //     'it should never be returned as `null` or empty string.  This usually means there\n'+
            //     'is existing data that was persisted some time before the `'+attrName+'` attribute\n'+
            //     'was set to `required: true`.  To make this warning go away, either remove\n'+
            //     '`required: true` from this attribute, or update the existing, already-stored data\n'+
            //     'so that the `'+attrName+'` of all records is set to some value other than null or\n'+
            //     'empty string.\n'+
            //     WARNING_SUFFIXES.MIGHT_BE_YOUR_FAULT
            //   );
            // }
            // ```
            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  
          }
  
        });//</_.each>
  
      }//ﬁ    (verify records)
  
  
      //   █████╗ ████████╗████████╗ █████╗  ██████╗██╗  ██╗
      //  ██╔══██╗╚══██╔══╝╚══██╔══╝██╔══██╗██╔════╝██║  ██║
      //  ███████║   ██║      ██║   ███████║██║     ███████║
      //  ██╔══██║   ██║      ██║   ██╔══██║██║     ██╔══██║
      //  ██║  ██║   ██║      ██║   ██║  ██║╚██████╗██║  ██║
      //  ╚═╝  ╚═╝   ╚═╝      ╚═╝   ╚═╝  ╚═╝ ╚═════╝╚═╝  ╚═╝
      //
      //   ██████╗██╗   ██╗███████╗████████╗ ██████╗ ███╗   ███╗
      //  ██╔════╝██║   ██║██╔════╝╚══██╔══╝██╔═══██╗████╗ ████║
      //  ██║     ██║   ██║███████╗   ██║   ██║   ██║██╔████╔██║
      //  ██║     ██║   ██║╚════██║   ██║   ██║   ██║██║╚██╔╝██║
      //  ╚██████╗╚██████╔╝███████║   ██║   ╚██████╔╝██║ ╚═╝ ██║
      //   ╚═════╝ ╚═════╝ ╚══════╝   ╚═╝    ╚═════╝ ╚═╝     ╚═╝
      //
      //  ████████╗ ██████╗      ██╗███████╗ ██████╗ ███╗   ██╗ ██╗██╗
      //  ╚══██╔══╝██╔═══██╗     ██║██╔════╝██╔═══██╗████╗  ██║██╔╝╚██╗
      //     ██║   ██║   ██║     ██║███████╗██║   ██║██╔██╗ ██║██║  ██║
      //     ██║   ██║   ██║██   ██║╚════██║██║   ██║██║╚██╗██║██║  ██║
      //  ██╗██║   ╚██████╔╝╚█████╔╝███████║╚██████╔╝██║ ╚████║╚██╗██╔╝
      //  ╚═╝╚═╝    ╚═════╝  ╚════╝ ╚══════╝ ╚═════╝ ╚═╝  ╚═══╝ ╚═╝╚═╝
      //  ╦╔═╗  ┬─┐┌─┐┬  ┌─┐┬  ┬┌─┐┌┐┌┌┬┐
      //  ║╠╣   ├┬┘├┤ │  ├┤ └┐┌┘├─┤│││ │
      //  ╩╚    ┴└─└─┘┴─┘└─┘ └┘ ┴ ┴┘└┘ ┴ooo
      if (WLModel.customToJSON) {
        Object.defineProperty(record, 'toJSON', {
          writable: true,
          value: WLModel.customToJSON
        });
      }//>-
  
  
      //  ██████╗ ███████╗ ██████╗██████╗ ██╗   ██╗██████╗ ████████╗    ██████╗  █████╗ ████████╗ █████╗
      //  ██╔══██╗██╔════╝██╔════╝██╔══██╗╚██╗ ██╔╝██╔══██╗╚══██╔══╝    ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗
      //  ██║  ██║█████╗  ██║     ██████╔╝ ╚████╔╝ ██████╔╝   ██║       ██║  ██║███████║   ██║   ███████║
      //  ██║  ██║██╔══╝  ██║     ██╔══██╗  ╚██╔╝  ██╔═══╝    ██║       ██║  ██║██╔══██║   ██║   ██╔══██║
      //  ██████╔╝███████╗╚██████╗██║  ██║   ██║   ██║        ██║       ██████╔╝██║  ██║   ██║   ██║  ██║
      //  ╚═════╝ ╚══════╝ ╚═════╝╚═╝  ╚═╝   ╚═╝   ╚═╝        ╚═╝       ╚═════╝ ╚═╝  ╚═╝   ╚═╝   ╚═╝  ╚═╝
      //  ╦╔═╗  ┬─┐┌─┐┬  ┌─┐┬  ┬┌─┐┌┐┌┌┬┐
      //  ║╠╣   ├┬┘├┤ │  ├┤ └┐┌┘├─┤│││ │
      //  ╩╚    ┴└─└─┘┴─┘└─┘ └┘ ┴ ┴┘└┘ ┴ooo
      var willDecrypt = meta && meta.decrypt;
      if (willDecrypt) {
        _.each(WLModel.attributes, function (attrDef, attrName){
          try {
            if (attrDef.encrypt) {
  
              // Never try to decrypt `''`(empty string), `0` (zero), `false`, or `null`, since these are
              // possible base values, which might end up in the database.  (Note that if this is a required
              // attribute, we could probably be more picky-- but it seems unlikely that encrypting these base
              // values at rest will ever be a priority, since they don't contain any sensitive information.
              // Arguably, there are edge cases where knowing _whether_ a particular field is at its base value
              // could be deemed sensitive info, but building around that extreme edge case seems like a bad idea
              // that probably isn't worth the extra headache and complexity in core.)
              if (record[attrName] === '' || record[attrName] === 0 || record[attrName] === false || _.isNull(record[attrName])) {
                // Don't try to decrypt these.
              }
              else {
  
                // Decrypt using the appropriate key from the configured DEKs.
                var decryptedButStillJsonEncoded;
  
                // console.log('•••••decrypting: `'+util.inspect(record[attrName], {depth:null})+'`');
  
                // Require this down here for Node version compat.
                var EA = require('encrypted-attr');
                decryptedButStillJsonEncoded = EA([attrName], {
                  keys: WLModel.dataEncryptionKeys
                })
                .decryptAttribute(undefined, record[attrName]);
                // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                // Alternative: (hack for testing)
                // ```
                // if (!record[attrName].match(/^ENCRYPTED:/)){ throw new Error('Unexpected behavior: Can\'t decrypt something already decrypted!!!'); }
                // decryptedButStillJsonEncoded = record[attrName].replace(/^ENCRYPTED:/, '');
                // ```
                // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  
                // Finally, JSON-decode the value, to allow for differentiating between strings/numbers/booleans/null.
                try {
                  record[attrName] = JSON.parse(decryptedButStillJsonEncoded);
                } catch (err) {
                  throw flaverr({
                    message: 'After initially decrypting the raw data, Waterline attempted to JSON-parse the data '+
                    'to ensure it was accurately decoded into the correct data type (for example, `2` vs `\'2\'`).  '+
                    'But this time, JSON.parse() failed with the following error:  '+err.message
                  }, err);
                }
  
              }//ﬁ
  
            }//ﬁ
          } catch (err) {
            // console.log('•••••was attempting to decrypt this value: `'+util.inspect(record[attrName], {depth:null})+'`');
  
            // Note: Decryption might not work, because there's no way of knowing what could have gotten into
            // the database  (e.g. from other processes, apps, maybe not even Node.js, etc.)
            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            // FUTURE: Instead of failing with an error, consider logging a warning and
            // sending back the data as-is. (e.g. and attach MIGHT_BE_YOUR_FAULT suffix.)
            // But remember: this is potentially sensitive data we're talking about, so being
            // a little over-strict seems like the right idea.  Maybe the right answer is to
            // still log the warning, but instead of sending back the potentially-sensitive data,
            // log it as part of the warning and send back whatever the appropriate base value is
            // instead.
            //
            // Regardless, for now we use an actual error to be on the safe side.
            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            throw flaverr({
              message: 'Decryption failed for `'+attrName+'` (in a `'+WLModel.identity+'` record).\n'+
              'The actual value in the record that could not be decrypted is:\n'+
              '```\n'+
              util.inspect(record[attrName],{depth:5})+'\n'+
              '```\n'+
              'Error details:\n'+
              '  '+err.message
            }, _.isError(err) ? err : new Error());
          }
        });//∞
      }//ﬁ
  
  
  
    }, false, modelIdentity, orm);//</eachRecordDeep>
  
  
    //
    // Records are modified in-place above, so there is no return value.
    //
  
    // console.timeEnd('processAllRecords');
  
  };
  