{"version":3,"sources":["jsdelivr-header.js","/npm/structured-data-testing-tool@4.5.0/index.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA,ACNA,MAAM,IAAM,QAAQ,sBAAsB,QACpC,MAAQ,QAAQ,cAChB,SAAW,QAAQ,YACnB,UAAY,QAAQ,aACpB,SAAW,QAAQ,aACnB,UAAY,QAAQ,eAClB,QAAS,cAAiB,QAAQ,iBAEpC,oBAAsB,CAAC,EAAgB,KAK3C,MAAM,GAAQ,IAA4B,IAAjB,EAAQ,KAE3B,EAAoB,GAAW,EAAQ,QAAW,EAAQ,QAAU,GACpE,EAAoB,GAAW,EAAQ,QAAW,EAAQ,QAAU,GACpE,EAAe,aAAa,GAC5B,EAAU,GAChB,IAAI,EAAQ,EAAQ,OAAS,GACzB,EAAa,GACb,EAAc,GACd,EAAc,GACd,EAAe,GACf,EAAgB,GAChB,EAAe,GAGf,EAAiB,GAKnB,GAJW,IAAT,EAIe,EAAa,OAC5B,EAAiB,QAAO,IACtB,IAAM,EAAoB,GAAe,EAAgB,MAAM,KAI/D,OAHK,IACH,EAAa,IAEX,EAAa,SAAS,KAAe,EAAa,SAAS,EAI/D,KAKa,EAGnB,MAAM,EAAW,CAAC,EAClB,OAAO,KAAK,EAAe,UAAU,KAAI,IAC3B,cAAR,IACF,EAAS,GAAyC,cAAjC,EAAe,SAAS,GAAwB,EAAe,SAAS,GAAO,SAGhG,OAAO,KAAK,GAAU,OAAS,GACjC,OAAO,KAAK,GAAU,KAAI,IACxB,EAAM,KAAK,CACT,KAAM,IAAI,KACV,KAAM,UACN,MAAO,WACP,YAAa,EACb,UAAU,EACV,cAAc,GACf,IAIL,IAAI,EAAyB,GACzB,EAAsB,GAC1B,EAAe,SAAQ,IAKrB,IAAM,EAAoB,GAAe,EAAO,MAAM,KAEjD,IACH,EAAa,EACb,EAAqB,MAIvB,EAAQ,KAAK,GAEb,IAAI,EAAgB,OAAO,cAAc,eAAe,GAAe,CAAC,aAAc,GAAc,CAAC,GAErG,MAAM,EAAyB,CAAC,EAAM,EAAQ,EAAM,EAAO,EAAM,KAC3D,GACF,OAAO,KAAK,GAAO,KAAI,IAErB,MAAM,EAAY,EAAM,GAClB,EAAe,MAAM,QAAQ,GAAS,EAAK,OAAO,GAAY,CAAC,GAErE,GAA0B,iBAAhB,EACR,EAAuB,EAAM,EAAQ,EAAM,EAAW,EAAY,OAC7D,CACL,MAAM,EAAW,GAAG,KAAQ,KAAW,EAAW,KAAI,GAAa,QAAQ,KAAK,GAAa,IAAI,KAAc,KAAK,OAAa,KAAK,IAChI,EAAc,EAAW,KAAI,GAAa,QAAQ,KAAK,GAAa,IAAI,KAAc,IAAI,MAAY,KAAK,IAAI,QAAQ,MAAO,IAEpI,EAAM,KAAK,CACT,KAAM,EACN,OAAQ,EACR,KAAM,GAAsB,MAC5B,MAAO,EACP,OAAQ,EACR,cACA,UAAU,EACV,cAAc,GAElB,IAEJ,EAMF,GAAI,EAIF,GAHK,EAAoB,SAAS,IAChC,EAAoB,KAAK,GAEvB,EAAe,GAAoB,GAAa,CAClD,MAAM,EAAkB,EAAe,GAAoB,GAE5B,IAA3B,EAAgB,QAClB,mBAAmB,EAAO,EAAY,EAAc,EAAoB,GACxE,EAAuB,EAAY,EAAc,EAAoB,EAAgB,GAAI,KAAM,IAE/F,EAAgB,KAAI,CAAC,EAAgB,KACnC,mBAAmB,EAAO,EAAY,EAAa,OAAO,IAAI,KAAM,EAAoB,GACxF,EAAuB,EAAY,EAAa,OAAO,IAAI,KAAM,EAAoB,EAAgB,KAAM,EAAC,GAGlH,MACE,mBAAmB,EAAO,EAAY,EAAc,QAGjD,EAAuB,SAAS,IACnC,EAAuB,KAAK,EAChC,IAGF,EAAuB,SAAQ,IAC7B,IAAK,EAAoB,SAAS,GAAa,CAC7C,MAAM,EAAgB,OAAO,cAAc,eAAe,GAAe,CAAC,aAAc,GAAc,CAAC,GACvG,mBAAmB,EAAO,EAAY,EACxC,KAIF,MAAM,EAAuB,CAAC,EAAS,EAAgB,EAAO,EAAc,KAC1E,EAAQ,SAAQ,IACd,IAAK,EACH,MAAM,IAAI,MAAM,4BAElB,IAAK,EAAO,KACV,MAAM,IAAI,MAAM,sDAElB,MAAM,EAAU,MAAM,QAAQ,GAAe,EAAW,OAAO,EAAO,MAAQ,CAAC,EAAO,MAEtF,IAAI,GAAe,EAEnB,GAAI,EAAO,YAAa,CAGtB,MAAM,WAAE,GAAe,SAAS,EAAO,YAAa,GAC/C,IACH,GAAe,EACnB,CAGK,GAGH,mBAAmB,EAAQ,EAAgB,GAAQ,SAAQ,IACzD,IAAI,GAAW,EAIf,GAAI,EAAK,YAAa,CACpB,MAAM,WAAE,GAAe,SAAS,EAAK,YAAa,GAC7C,IACH,GAAW,EACf,CAEI,EACF,EAAa,KAAK,GAElB,EAAM,KAAK,EACb,IAKA,EAAO,SACT,EAAqB,EAAO,QAAS,EAAgB,EAAO,EAAc,EAC5E,GACD,EAKH,EAAqB,EAAkB,EAAgB,EAAO,GAE9D,EAAM,SAAQ,IACZ,EAAK,QAAS,EACT,EAAK,OAAM,EAAK,KAAO,OAExB,EAAK,QAAU,EAAW,SAAS,EAAK,QAC1C,EAAW,KAAK,EAAK,OAEvB,MAAM,WAAE,EAAU,UAAE,GAAc,SAAS,EAAM,GACjD,EAAK,OAAS,EACd,EAAK,MAAQ,EAGM,OAAf,EAAK,cAAuB,EAAK,OAGf,IAAlB,EAAK,SACP,EAAc,KAAK,IACM,IAAhB,EAAK,OACd,EAAY,KAAK,GACR,EAAK,QACd,EAAa,KAAK,GAElB,EAAY,KAAK,EACnB,IAIF,MAAM,EAAW,CACf,QACA,OAAQ,EACR,OAAQ,EACR,SAAU,EACV,SAAU,EACV,QAAS,EACT,OAAQ,EACR,QAAS,EAAa,KAAI,IACxB,MAAM,EAAc,EAAO,MAAM,KACjC,OAAQ,EAAY,GAAM,EAAY,GAAK,EAAY,EAAC,IAE1D,iBACA,WAGF,GAA2B,IAAvB,EAAY,OAEd,OAAO,QAAQ,QAAQ,GAClB,CAEL,IAAI,EAAQ,IAAI,MAAM,qBAGtB,OAFA,EAAM,KAAO,oBACb,EAAM,IAAM,EACL,QAAQ,OAAO,EACxB,GAGI,MAAQ,CAAC,EAAM,KAOnB,IAAI,GAAa,EACb,EAAY,KACZ,EAAO,KAEX,EAAK,MANsB,GAQ3B,IACE,EAAO,EAAK,KACZ,MAAM,EAAY,SAAS,OAAO,EAAM,GAExC,EAAK,MAAsB,YAAd,EAAK,KACb,GAAa,EAAU,SAA8B,IAAjB,EAAU,GAAsB,EAAU,GAb1D,GAcN,OAAd,EAAsB,EAdF,QAgBE,IAAhB,EAAK,SAA0C,IAAhB,EAAK,OAI1B,IAAf,EAAK,QAA8B,IAAf,EAAK,OAAqB,EAAK,OAA+B,IAAtB,EAAK,MAAM,OAezE,GAAa,EAXX,EAHE,EAAK,aAGK,CACV,KAAM,OACN,QAAS,uBAAuB,MAGtB,CACV,KAAM,YACN,QAAS,mBAAmB,OAMT,IAAhB,EAAK,OAEK,OAAd,GAAsC,YAAd,EAAK,MAAsB,GAAa,EAAU,SAA8B,IAAjB,EAAU,GACpG,GAAa,EAEb,EAAY,CACV,KAAM,4BACN,QAAS,aAAa,4BAGjB,EAAK,OAAS,EAAK,kBAAkB,QAE1C,MAAM,QAAQ,EAAK,OAErB,EAAK,MAAM,KAAI,IACT,EAAM,MAAM,EAAK,UACnB,GAAa,MAIjB,EAAa,EAAK,MAAM,MAAM,EAAK,QAGhC,IACH,EAAY,CACV,KAAM,gBACN,QAAS,2BAA2B,KACpC,SAAU,EAAK,OACf,MAAO,EAAK,SAGN,EAAK,OAA+B,IAAtB,EAAK,MAAM,OAM1B,EAAK,OAAS,EAAK,QAAW,MAAM,QAAQ,EAAK,QAAU,EAAK,MAAM,SAAS,EAAK,QAI3F,GAAa,EAGf,EAAY,CACV,KAAM,kBACN,QAAS,8BAA8B,KACvC,SAAU,EAAK,OACf,MAAO,EAAK,OAfd,EAAY,CACV,KAAM,cACN,QAAS,SAAS,YAgBxB,CAAE,MAAO,GACP,EAAY,CACV,KAAM,aACN,QAAS,0BAA0B,wBACnC,IAEJ,CAEA,MAAO,CACL,aACA,YACF,EAGI,SAAW,CAAC,EAAM,KACtB,GAAiB,WAAb,EAAK,KAEP,OAAO,MAAM,EAAM,EAAe,UAC7B,GAAiB,UAAb,EAAK,KAEd,OAAO,MAAM,EAAM,EAAe,QAC7B,GAAiB,aAAb,EAAK,KAEd,OAAO,MAAM,EAAM,EAAe,WAC7B,GAAiB,QAAb,EAAK,KAEd,OAAO,MAAM,EAAM,EAAe,MAC7B,GAAiB,OAAb,EAAK,MAAkB,OAAO,GAAM,eAAe,QAyC5D,MAAO,CACL,YAAY,EACZ,UAAW,CACT,KAAM,eACN,QAAS,aAAa,EAAK,qBA7CsC,CACrE,IAAI,EAAS,CAAC,EAKd,OADA,EAAS,MAAM,EAAM,EAAe,QAChC,EAAO,YACT,EAAK,KAAO,SACL,IAIT,EAAS,MAAM,EAAM,EAAe,WAChC,EAAO,YACT,EAAK,KAAO,YACL,IAIT,EAAS,MAAM,EAAM,EAAe,MAChC,EAAO,YACT,EAAK,KAAO,OACL,IAIT,EAAS,MAAM,EAAM,EAAe,UAChC,EAAO,YACT,EAAK,KAAO,UACL,GAIF,CACL,YAAY,EACZ,UAAW,CACT,KAAM,YACN,QAAS,iBAAiB,EAAK,0BAGrC,CASA,EAGI,aAAgB,IACpB,MAAM,EAAU,GAEH,OAAO,KAAK,EAAe,MACnC,KAAI,IACQ,cAAX,GAA2B,EAAQ,SAAS,IAAS,EAAQ,KAAK,QAAQ,IAAQ,IAGtE,OAAO,KAAK,EAAe,WACnC,KAAI,IACG,cAAX,GAA2B,EAAQ,SAAS,IAAS,EAAQ,KAAK,aAAa,IAAQ,IAQ7F,OALe,OAAO,KAAK,EAAe,QACnC,KAAI,IACM,cAAX,GAA2B,EAAQ,SAAS,IAAS,EAAQ,KAAK,UAAU,IAAQ,IAGnF,GAGH,sBAAwB,MAAO,EAAK,KACxC,MAAM,QAAY,MAAM,GAClB,QAAa,EAAI,OACvB,OAAO,yBAAyB,EAAM,CAAE,MAAK,SAAQ,GAAS,EAG1D,yBAA2B,MAAO,EAAO,KAQ7C,IAAI,EAAO,EACX,IAAM,EAAO,KAAK,MAAM,GAAS,sCAAsC,cAAmB,CAAM,CAAE,MAAO,GAAM,CAC/G,OAAO,uBAAuB,EAAM,EAAO,EAGvC,uBAAyB,MAAO,EAAM,KAC1C,IAAI,EAAiB,MAAM,MAAM,GAEjC,OADA,EAAiB,0BAA0B,GACpC,oBAAoB,EAAgB,CAAE,UAAS,GAAS,EAG3D,mBAAqB,MAAO,EAAO,KACvC,GAAsB,iBAAZ,EAAsB,CAG9B,GAAI,UAAU,MAAM,EAAO,CAAE,aAAa,IAAU,CAElD,MAAM,EAAM,EACN,QAAY,MAAM,GAExB,UADmB,EAAI,OACZ,CACT,IAAI,EAAQ,IAAI,MAAM,oBAEtB,MADA,EAAM,KAAO,aACP,CACR,CACA,OAAO,sBAAsB,EAAK,EACpC,CAEE,OAAO,yBADM,EACyB,EAE1C,CAAO,GAAI,OAAO,SAAS,GAAQ,CAGjC,MAAM,EAAO,EAAM,SAAS,QAC5B,OAAO,yBAAyB,EAAM,EACxC,CAAO,GAAI,SAAS,SAAS,GAAQ,CAGnC,MAAM,QAAa,UAAU,GAC7B,OAAO,yBAAyB,EAAM,EACxC,CAGE,OAAO,oBADgB,EACoB,EAC7C,EAQI,0BAA6B,IACjC,IAAI,EAAS,EAab,OAZA,OAAO,KAAK,EAAO,WAAW,SAAQ,IACpC,EAAO,UAAU,GAAQ,SAAQ,IAC/B,OAAO,KAAK,GAAQ,SAAQ,IACtB,EAAI,SAAS,OACf,EAAI,MAAM,KAAK,SAAQ,IACrB,EAAO,GAAU,EAAO,EAAG,WAEtB,EAAO,GAChB,GACD,GACF,IAEI,GAGH,mBAAqB,CAAC,EAAQ,EAAgB,KAClD,MAAM,EAAQ,GAEd,IAAK,EAAO,MACV,OAAO,EAmDT,OAlCI,EAAO,OAET,OAAO,KAAK,GAAgB,KAAI,IACb,aAAb,GAEJ,OAAO,KAAK,EAAe,IAAW,KAAK,IACrC,IAAe,EAAO,QACxB,EAAe,GAAU,GAAY,SAAQ,CAAC,EAAU,KACtD,MAAM,EAAS,EAAU,OAAO,IAAI,MAAM,MAE1C,EAAO,MAAM,SAAQ,IAEnB,MAAM,EAAO,OAAO,OAAO,CAAC,EAAG,GAC/B,EAAK,OAAS,EAAO,OACrB,EAAK,KAAO,EAAK,KAAK,QAAQ,cAAe,GAAG,EAAO,UAAU,MACjE,EAAK,KAAO,EACZ,EAAK,OAAS,EACT,EAAK,cACR,EAAK,YAAc,EAAK,KAAK,QAAQ,gBAAiB,IAAI,QAAQ,KAAM,KAE1E,EAAM,KAAK,EAAI,GAChB,GAEL,GACD,IAIH,EAAO,MAAM,SAAQ,IA3CD,EAAC,EAAM,KAItB,EAAK,QACJ,EAAO,MACT,EAAK,MAAQ,EAAO,MACX,EAAO,KAChB,EAAK,MAAQ,EAAO,KAEpB,EAAK,MAAQ,UAEjB,EAgCE,CAAc,EAAM,GACpB,EAAM,KAAK,EAAI,IAIZ,GAIH,mBAAqB,CAAC,EAAO,EAAM,EAAQ,EAAM,KACrD,EAAM,KAAK,CACT,KAAM,GAAG,UAA2B,IAAZ,EAA2B,IAAK,KACxD,OAAQ,EACR,KAAM,GAAQ,MACd,MAAO,EACP,OAAQ,EACR,YAAa,EAAS,aAAa,IAAS,gBAC7C,EAGH,OAAO,QAAU,CACf,wCACA,sCACA,4CACA,kDACA","file":"/npm/structured-data-testing-tool@4.5.0/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/structured-data-testing-tool@4.5.0/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","const WAE = require('web-auto-extractor').default\nconst fetch = require('node-fetch')\nconst jmespath = require('jmespath')\nconst validator = require('validator')\nconst isStream = require('is-stream')\nconst getStream = require('get-stream')\nconst { schemas: validSchemas } = require('./lib/schemas')\n\nconst _structuredDataTest = (structuredData, options) => {\n  // By default, will auto-detect data structures and test them for validity\n  // If you want to avoid this (e.g. and only perform explicit tests you pass)\n  // you can set this to false when using the API to disable this feature.\n  // This may mean you miss some errors but may make it easier to debug issues.\n  const auto = (options && options.auto === false) ? false : true\n\n  const presetsSpecified = (options && options.presets) ? options.presets : []\n  const schemasSpecified = (options && options.schemas) ? options.schemas : []\n  const schemasFound = _findSchemas(structuredData) // Find schemas in structuredData\n  const schemas = [] // All schemas found *or* explicitly specified\n  let tests = options.tests || [] // Contains all tests to be run\n  let testGroups = []\n  let testsPassed = [] // Only tests that passed\n  let testsFailed = [] // Only tests that failed\n  let testsWarning = [] // Only tests that generated warnings (technically non critical failures)\n  let testsOptional = [] // Optional tests (regardless if passed or failed; they do not count towards either)\n  let testsSkipped = [] // Only that were skipped\n\n  // Combine schemas found with any schemas specified.\n  let arrayOfSchemas = []\n  if (auto === true) {\n    // When auto is true, automatically combine schemas found with schemas explicitly specified.\n    // Schemas found automatically actually take precedent, as we know the type for them already\n    // (jsonld, microdata, rdfa, etc) so can easily add the specific tests for them.\n    arrayOfSchemas = schemasFound.concat(\n      schemasSpecified.filter(schemaSpecified => {\n        let [ structuredDataType, schemaName ] = schemaSpecified.split(':')\n        if (!schemaName)\n          schemaName = structuredDataType\n\n        if (schemasFound.includes(schemaName) || schemasFound.includes(schemaSpecified)) {\n          return false\n        } else { \n          return true\n        }\n      })\n    )\n  } else {\n    // When auto is false, only test schemas explicitly specified.\n    arrayOfSchemas = schemasSpecified\n  }\n\n  const metatags = {}\n  Object.keys(structuredData.metatags).map(tag => {\n    if (tag !== 'undefined')\n      metatags[tag] = (structuredData.metatags[tag] !== 'undefined') ? structuredData.metatags[tag] : null\n  })\n\n  if (Object.keys(metatags).length > 0) {\n    Object.keys(metatags).map(tag => {\n      tests.push({\n        test: `\"${tag}\"`,\n        type: 'metatag',\n        group: 'Metatags',\n        description: tag,\n        optional: true,\n        autoDetected: true\n      })\n    })\n  }\n\n  let schemaTestsWithoutType = []\n  let schemaTestsWithType = []\n  arrayOfSchemas.forEach(schema => {\n    // Schema names can optionally have a suffix to indicate how it should be declared.\n    // A schema can just be the schema name like 'Article', but it can also be \n    // 'jsonld:Article', 'microdata:Article' or 'rdfa:Article' if you wanted to check\n    // for a schema specified on a page in a particular way.\n    let [ structuredDataType, schemaName ] = schema.split(':')\n\n    if (!schemaName) {\n      schemaName = structuredDataType\n      structuredDataType = null\n    }\n    \n    // Add schema name to list of schemas\n    schemas.push(schemaName)\n\n    let schemaGroups = (Object(validSchemas).hasOwnProperty(schemaName)) ? ['Schema.org', schemaName] : [schemaName]\n    \n    const _addTestsForProperties = (name, groups, type, props, path, index) => {\n      if (props) {\n        Object.keys(props).map(propName => {\n          // @TODO Add test to check if prop contents is valid\n          const propValue = props[propName]\n          const pathToProp =  (Array.isArray(path)) ? path.concat(propName) : [propName]\n\n          if (typeof(propValue) === 'object') {\n            _addTestsForProperties(name, groups, type, propValue, pathToProp, index)\n          } else {\n            const testPath = `${name}[${index}]` + pathToProp.map(pathItem => (/^\\d+$/.test(pathItem)) ? `[${pathItem}]` : `.\"${pathItem}\"`).join('')\n            const description = pathToProp.map(pathItem => (/^\\d+$/.test(pathItem)) ? `[${pathItem}]` : `.${pathItem}`).join('').replace(/^\\./, '')\n            // As we do not have schema property validation in place, treat all autotoamted tests for properties as optional\n            tests.push({\n              test: testPath,\n              schema: name,\n              type: structuredDataType || 'any',\n              group: name,\n              groups: groups,\n              description,\n              optional: true,\n              autoDetected: true\n            })\n          }\n        })\n      }\n    }\n\n    // If there is more than one schema of the same type on a page, then group\n    // them by putting them in subgroups named #0, #1, #2… etc. so that the results\n    // for each instances of a schema are easy to iterate over.\n    if (structuredDataType) {\n      if (!schemaTestsWithType.includes(schemaName))\n        schemaTestsWithType.push(schemaName)\n\n      if (structuredData[structuredDataType][schemaName]) {\n        const schemaInstances = structuredData[structuredDataType][schemaName]\n\n        if (schemaInstances.length === 1) {\n          _addTestsForSchema(tests, schemaName, schemaGroups, structuredDataType, 0)\n          _addTestsForProperties(schemaName, schemaGroups, structuredDataType, schemaInstances[0], null, 0)\n        } else {\n          schemaInstances.map((schemaInstance, i) => {\n            _addTestsForSchema(tests, schemaName, schemaGroups.concat(`#${i}`), structuredDataType, i)\n            _addTestsForProperties(schemaName, schemaGroups.concat(`#${i}`), structuredDataType, schemaInstance, null, i)\n          })\n        }\n      } else {\n        _addTestsForSchema(tests, schemaName, schemaGroups, structuredDataType)\n      }\n    } else {\n      if (!schemaTestsWithoutType.includes(schemaName))\n        schemaTestsWithoutType.push(schemaName)\n    }\n  })\n\n  schemaTestsWithoutType.forEach(schemaName => {\n    if (!schemaTestsWithType.includes(schemaName)) {\n      const schemaGroups = (Object(validSchemas).hasOwnProperty(schemaName)) ? ['Schema.org', schemaName] : [schemaName]\n      _addTestsForSchema(tests, schemaName, schemaGroups)\n    }\n  })\n\n  // This is a recursive function scoped to this function\n  const _addTestsFromPresets = (presets, structuredData, tests, testsSkipped, testGroups) => {\n    presets.forEach(preset => {\n      if (!preset)\n        throw new Error(`Invalid preset specified`)\n\n      if (!preset.name)\n        throw new Error(`Preset specified does not have a 'name' (required)`)\n\n      const groups = (Array.isArray(testGroups)) ? testGroups.concat(preset.name) : [preset.name]\n\n      let ignorePreset = false\n\n      if (preset.conditional) {\n        // Evaluate `preset.conditional` test against structuredData to see if\n        // preset tests should be added (don't run if does not pass)\n        const { testPassed } = _runTest(preset.conditional, structuredData)\n        if (!testPassed)\n          ignorePreset = true\n      }\n\n      // Ignore all tests in preset if the preset conditional does not pass\n      if (!ignorePreset) {\n        // Loop through all tests in preset, but only add the ones that either don't have\n        // a conditional tests or where the conditional test passes.\n        getTestsFromPreset(preset, structuredData, groups).forEach(test => {\n          let skipTest = false\n\n          // Evaluate `test.conditional` test against structuredData to see if\n          // test should be added (add test to skipped tests if not)\n          if (test.conditional) {\n            const { testPassed } = _runTest(test.conditional, structuredData)\n            if (!testPassed)\n              skipTest = true\n          }\n\n          if (skipTest) {\n            testsSkipped.push(test)\n          } else {\n            tests.push(test)\n          }\n        })\n      }\n\n      // If the preset has other presets associated with it, apply them too.\n      if (preset.presets) {\n        _addTestsFromPresets(preset.presets, structuredData, tests, testsSkipped, groups)\n      }\n    })\n  }\n  \n  // Add tests for each preset specified to the tests to be performed.\n  // This function is recursive to handle presets with other presets (to any depth level)\n  _addTestsFromPresets(presetsSpecified, structuredData, tests, testsSkipped)\n\n  tests.forEach(test => {\n    test.passed = false\n    if (!test.type) test.type = 'any'\n\n    if (test.group && !testGroups.includes(test.group))\n      testGroups.push(test.group)\n\n    const { testPassed, testError } = _runTest(test, structuredData)\n    test.passed = testPassed\n    test.error = testError\n\n    // Delete test.error property if it is null\n    if (test.error === null) delete test.error\n\n    // Put test into appropriate array for response object\n    if (test.optional === true) {\n      testsOptional.push(test)\n    } else if (test.passed === true) {\n      testsPassed.push(test)\n    } else if (test.warning) {\n      testsWarning.push(test)\n    } else {\n      testsFailed.push(test)\n    }\n  })\n\n  // Create response object\n  const response = {\n    tests,\n    passed: testsPassed,\n    failed: testsFailed,\n    warnings: testsWarning,\n    optional: testsOptional,\n    skipped: testsSkipped,\n    groups: testGroups,\n    schemas: schemasFound.map(schema => { \n      const splitResult = schema.split(':')\n      return (splitResult[1]) ? splitResult[1] : splitResult[0]\n    }),\n    structuredData,\n    options,\n  }\n\n  if (testsFailed.length === 0) {\n    // If all tests passed, resolve promise\n    return Promise.resolve(response)\n  } else {\n    // If any tests did not pass, reject promise\n    let error = new Error('Validation failed')\n    error.type = 'VALIDATION_FAILED'\n    error.res = response\n    return Promise.reject(error) \n  }\n}\n\nconst _test = (test, json) => {\n  // TEST_DEFAULT_VALUE is useful to be able to set to normalize null/undefined/empty values\n  // This is particularly useful when testing metadata properties which would otherwise have\n  // different default values depending on the reason for a test failing, depending on if the\n  // property is missing, the value is missing or if the value is empty.\n  const TEST_DEFAULT_VALUE = ''\n\n  let testPassed = false\n  let testError = null\n  let path = null\n\n  test.value = TEST_DEFAULT_VALUE\n\n  try {\n    path = test.test\n    const pathValue = jmespath.search(json, path)\n\n    test.value = test.type === 'metatag'\n      ? (pathValue && pathValue[0] && typeof pathValue[0] !== 'undefined') ? pathValue[0] : TEST_DEFAULT_VALUE\n      : (pathValue !== null) ? pathValue : TEST_DEFAULT_VALUE\n\n    if (typeof test.expect === 'undefined' || test.expect === true) {\n      // If 'expect' is 'true' then a pathValue should exist.\n      // If no value for expect then assume is a simple check to see it exists.\n      // Note: It's okay if the value is zero, or false but it should not be empty!\n      if (test.value !== 0 && test.value !== false && (!test.value || test.value.length === 0)) {\n        if (test.autoDetected) {\n          // Auto Detected properties by definition must exist, so if\n          // the test fail it can only be because they have no value.\n          testError = {\n            type: 'NULL',\n            message: `No value found for \"${path}\"`,\n          }\n        } else {\n          testError = {\n            type: 'NOT_FOUND',\n            message: `Could not find \"${path}\"`,\n          }\n        }\n      } else {\n        testPassed = true\n      }\n    } else if (test.expect === false) {\n      // If 'expect' is 'false' then a pathValue SHOULD NOT exist\n      if ((pathValue === null) || (test.type === 'metatag' && pathValue && pathValue[0] && typeof pathValue[0] === 'undefined')) {\n        testPassed = true\n      } else {\n        testError = {\n          type: 'PROPERTY_SHOULD_NOT_EXIST',\n          message: `Property \"${path}\" should not be defined`,\n        }\n      }\n    } else if (test.value && test.expect instanceof RegExp) {\n      // If test is a Regular Expression…\n      if (Array.isArray(test.value)) {\n        // If value is array and any key matches then test passes\n        test.value.map(value => {\n          if (value.match(test.expect))\n            testPassed = true\n        })\n      } else {\n        // If value is not an array, treat as a string\n        testPassed = test.value.match(test.expect)\n      }\n\n      if (!testPassed) {\n        testError = {\n          type: 'REGEXP_FAILED',\n          message: `Failed RegExp test for \"${path}\"`,\n          expected: test.expect,\n          found: test.value\n        }\n      }\n    } else if (!test.value || test.value.length === 0) {\n      // If item not found (or has no value) then error\n      testError = {\n        type: 'TEST_FAILED',\n        message: `Test \"${path}\" failed`,\n      }\n    } else if (test.value == test.expect || (Array.isArray(test.value) && test.value.includes(test.expect))) {\n      // If value is found and matches what we expect…\n      // …or in the case of metadata, if the returned value is an array \n      // and one of the items matches what we expect then the test passes\n        testPassed = true\n    } else {\n      // If a value is found, but is does not match what we expect then fail\n      testError = {\n        type: 'INCORRECT_VALUE',\n        message: `Incorrect value found for \"${path}\"`,\n        expected: test.expect,\n        found: test.value,\n      }\n    }\n  } catch (e) {\n    testError = {\n      type: 'TEST_ERROR',\n      message: `Error evaluating test \"${path}\", check test syntax`,\n      e\n    }\n  }\n\n  return {\n    testPassed,\n    testError\n  }\n}\n\nconst _runTest = (test, structuredData) => {\n  if (test.type == 'metatag') {\n    // Look for meta tags`\n    return _test(test, structuredData.metatags)\n  } else if (test.type == 'jsonld') {\n    // Look for data in jsonld\n    return _test(test, structuredData.jsonld)\n  } else if (test.type == 'microdata') {\n    // Look for data in microdata\n    return _test(test, structuredData.microdata)\n  } else if (test.type == 'rdfa') {\n    // Look for data in rdfa\n    return _test(test, structuredData.rdfa)\n  } else if (test.type == 'any' || !Object(test).hasOwnProperty('type')) {\n    let result = {}\n\n    // Look for data in jsonld, then microdata then rdfa then metatags until found\n    // If a test passes, set the test type to reflect where it was found\n    result = _test(test, structuredData.jsonld)\n    if (result.testPassed) {\n      test.type = 'jsonld'\n      return result\n    }\n\n    // If was not found in jsonld, look for data in microdata\n    result = _test(test, structuredData.microdata)\n    if (result.testPassed) {\n      test.type = 'microdata'\n      return result\n    }\n\n    // If was not found in jsonld or microdata, look for data in rdfa\n    result = _test(test, structuredData.rdfa)\n    if (result.testPassed) {\n      test.type = 'rdfa'\n      return result\n    }\n\n    // If was not found in jsonld or microdata or rdfa, look for data in metatags\n    result = _test(test, structuredData.metatags)\n    if (result.testPassed) {\n      test.type = 'metatag'\n      return result\n    }\n\n    // If property not found anywhere\n    return {\n      testPassed: false,\n      testError: {\n        type: 'NOT_FOUND',\n        message: `The property \"${test.test}\" was not found`,\n      }\n    }\n  } else {\n    return {\n      testPassed: false,\n      testError: {\n        type: 'INVALID_TYPE',\n        message: `The type \"${test.type}\" is invalid`,\n      }\n    }\n\n  }\n}\n\nconst _findSchemas = (structuredData) => {\n  const schemas = []\n\n  const rdfa = Object.keys(structuredData.rdfa)\n  rdfa.map(schema => {\n    if (schema !== 'undefined' && !schemas.includes(schema)) schemas.push(`rdfa:${schema}`)\n  })\n\n  const microdata = Object.keys(structuredData.microdata)\n  microdata.map(schema => {\n    if (schema !== 'undefined' && !schemas.includes(schema)) schemas.push(`microdata:${schema}`)\n  })\n\n  const jsonld = Object.keys(structuredData.jsonld)\n  jsonld.map(schema => {\n    if (schema !== 'undefined' && !schemas.includes(schema)) schemas.push(`jsonld:${schema}`)\n  })\n\n  return schemas\n}\n\nconst structuredDataTestUrl = async (url, options) => {\n  const res = await fetch(url)\n  const html = await res.text()\n  return structuredDataTestString(html, { url, res, ...options })\n}\n\nconst structuredDataTestString = async (input, options) => {\n  // Try to parse the string input as a JSON object.\n  //\n  // If it is a JSON object, then wrap it in <script> tags and then parse it as HTML.\n  // If it is not, then assume it is HTML and try to parse it as it is.\n  //\n  // This is potentially computationally expensive, but making the call that \n  // performance concerns are secondary to utility in the case of this tool.\n  let html = input\n  try { html = JSON.parse(input) ? `<script type=\"application/ld+json\">${input}</script>` : input } catch (e) {  }\n  return structuredDataTestHtml(html, options)\n}\n\nconst structuredDataTestHtml = async (html, options) => {\n  let structuredData = WAE().parse(html)\n  structuredData = __transformStructuredData(structuredData)\n  return _structuredDataTest(structuredData, { html, ...options })\n}\n\nconst structuredDataTest = async (input, options) => {\n  if (typeof(input) === 'string') {\n    // If is a string…\n    // Assumed to be URL or HTML\n    if (validator.isURL(input, { require_tld: false })) {\n      // @TODO Improve URL error handling\n      const url = input\n      const res = await fetch(url)\n      const html = await res.text()\n      if (!html) {\n        let error = new Error('No HTML returned')\n        error.type = 'HTML_EMPTY'\n        throw error\n      }\n      return structuredDataTestUrl(url, options)\n    } else {\n      const html = input\n      return structuredDataTestString(html, options)\n    }\n  } else if (Buffer.isBuffer(input)) {\n    // If is a buffer…\n    // Convert buffer to string\n    const html = input.toString('utf8')\n    return structuredDataTestString(html, options)\n  } else if (isStream.readable(input)) {\n    // If is a readable stream…\n    // Convert readable stream to string\n    const html = await getStream(input)\n    return structuredDataTestString(html, options)\n  } else {\n    // Else ??\n    const structuredData = input\n    return _structuredDataTest(structuredData, options)\n  }\n}\n\n// Fixes a big when finding itemprops in microdata, where the itemprop\n// contained more than more property (separated by a space).\n// Have not seen this behaviour in examples on Schema.org but is supported\n// by the Google Structured Data Testing Tool. For more info see:\n// https://github.com/glitchdigital/structured-data-testing-tool/issues/4\nconst __transformStructuredData = (structuredData) => {\n  let result = structuredData\n  Object.keys(result.microdata).forEach(schema => {\n    result.microdata[schema].forEach(object => {\n      Object.keys(object).forEach(key => {\n        if (key.includes(' ')) {\n          key.split(' ').forEach(newKey => {\n            object[newKey] = object[key]\n          })\n          delete object[key]\n        }\n      })\n    })\n  })\n  return result\n}\n\nconst getTestsFromPreset = (preset, structuredData, testGroup) => {\n  const tests = []\n\n  if (!preset.tests)\n    return tests\n\n  const _setTestGroup = (test, preset) => {\n    // If test does not explicitly have a group defined, use the the default one preset,\n    // if defined. If no default group is is defined for the preset, fallback to using\n    // the preset name to group the results (every preset SHOULD have a name!)\n    if (!test.group) {\n      if (preset.group) {\n        test.group = preset.group\n      } else if (preset.name) {\n        test.group = preset.name\n      } else {\n        test.group = 'DEFAULT'\n      }\n    }\n  }\n\n  if (preset.schema) {\n    // If preset has a schema, return tests that check every instance of that schema\n    Object.keys(structuredData).map(dataType => {\n      if (dataType === 'metatags')\n        return\n      Object.keys(structuredData[dataType]).map((schemaName) => {\n        if (schemaName === preset.schema) {\n          structuredData[dataType][schemaName].forEach((instance, i) => {\n            const groups = testGroup.concat(`#${i} (${dataType})`)\n\n            preset.tests.forEach(t => {\n              // Note: Must copy object so we can re-use tests objects from a preset in different tests\n              const test = Object.assign({}, t)\n              test.schema = preset.schema\n              test.test = test.test.replace(/(.*)?\\[\\*\\]/, `${preset.schema}[${i}]`)\n              test.type = dataType\n              test.groups = groups\n              if (!test.description)\n                test.description = test.test.replace(/(.*)?\\[\\d\\]\\./, '').replace(/\"/g, '')\n              \n              tests.push(test)\n            })\n          })\n        }\n      })\n    })\n  } else {\n    // If preset does not have a schema, then return only the tests in the preset\n    preset.tests.forEach(test => {\n      _setTestGroup(test, preset)\n      tests.push(test)\n    })\n  }\n\n  return tests\n}\n\n// Add a test for any schema explicitly specified (or that was detected)\nconst _addTestsForSchema = (tests, name, groups, type, index) => {\n  tests.push({\n    test: `${name}[${(typeof(index) === 'undefined') ? '*': index}]`,\n    schema: name,\n    type: type || 'any',\n    group: name,\n    groups: groups,\n    description: (type) ? `schema in ${type}` : `schema found`\n  })\n}\n\nmodule.exports = {\n  _structuredDataTest,\n  structuredDataTest,\n  structuredDataTestUrl,\n  structuredDataTestString,\n  structuredDataTestHtml\n}"]}