author | Mike Cooper <mcooper@mozilla.com> |
Wed, 26 Jul 2017 10:13:26 -0700 | |
changeset 616080 | fcaa5a0be306ba65c7877afc482cc675dc90eb0f |
parent 616057 | add7f83758561c8d534b47af5e98379b4f2f19dd |
child 639359 | 3d73b0a0e5ced1f0747661c06d0103f43bd49c2c |
push id | 70565 |
push user | bmo:mcooper@mozilla.com |
push date | Wed, 26 Jul 2017 17:34:36 +0000 |
reviewers | rhelmer |
bugs | 1384652 |
milestone | 56.0a1 |
--- a/browser/extensions/shield-recipe-client/bootstrap.js +++ b/browser/extensions/shield-recipe-client/bootstrap.js @@ -34,17 +34,19 @@ this.shutdown = function(data, reason) { "lib/NormandyApi.jsm", "lib/NormandyDriver.jsm", "lib/PreferenceExperiments.jsm", "lib/RecipeRunner.jsm", "lib/Sampling.jsm", "lib/SandboxManager.jsm", "lib/ShieldRecipeClient.jsm", "lib/Storage.jsm", + "lib/Uptake.jsm", "lib/Utils.jsm", + "vendor/mozjexl.js", ]; for (const module of modules) { log.debug(`Unloading ${module}`); Cu.unload(`resource://shield-recipe-client/${module}`); } }; this.uninstall = function() {};
--- a/browser/extensions/shield-recipe-client/install.rdf.in +++ b/browser/extensions/shield-recipe-client/install.rdf.in @@ -3,17 +3,17 @@ #filter substitution <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"> <Description about="urn:mozilla:install-manifest"> <em:id>shield-recipe-client@mozilla.org</em:id> <em:type>2</em:type> <em:bootstrap>true</em:bootstrap> <em:unpack>false</em:unpack> - <em:version>55</em:version> + <em:version>61</em:version> <em:name>Shield Recipe Client</em:name> <em:description>Client to download and run recipes for SHIELD, Heartbeat, etc.</em:description> <em:multiprocessCompatible>true</em:multiprocessCompatible> <em:targetApplication> <Description> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
--- a/browser/extensions/shield-recipe-client/jar.mn +++ b/browser/extensions/shield-recipe-client/jar.mn @@ -1,9 +1,10 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. [features/shield-recipe-client@mozilla.org] chrome.jar: % resource shield-recipe-client %content/ content/lib/ (./lib/*) - content/node_modules/jexl/ (./node_modules/jexl/*) + content/data/ (./data/*) content/skin/ (skin/*) + content/vendor/ (./vendor/*)
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/lib/Addons.jsm @@ -0,0 +1,109 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); + +this.EXPORTED_SYMBOLS = ["Addons"]; + +/** + * SafeAddons store info about an add-on. They are single-depth + * objects to simplify cloning, and have no methods so they are safe + * to pass to sandboxes and filter expressions. + * + * @typedef {Object} SafeAddon + * @property {string} id + * Add-on id, such as "shield-recipe-client@mozilla.com" or "{4ea51ac2-adf2-4af8-a69d-17b48c558a12}" + * @property {Date} installDate + * @property {boolean} isActive + * @property {string} name + * @property {string} type + * "extension", "theme", etc. + * @property {string} version + */ + +this.Addons = { + /** + * Get information about an installed add-on by ID. + * + * @param {string} addonId + * @returns {SafeAddon?} Add-on with given ID, or null if not found. + * @throws If addonId is not specified or not a string. + */ + async get(addonId) { + const addon = await AddonManager.getAddonByID(addonId); + if (!addon) { + return null; + } + return this.serializeForSandbox(addon); + }, + + /** + * Get information about all installed add-ons. + * @async + * @returns {Array<SafeAddon>} + */ + async getAll(addonId) { + const addons = await AddonManager.getAllAddons(); + return addons.map(this.serializeForSandbox.bind(this)); + }, + + /** + * Installs an add-on + * @prop installUrl {string} Url to download the .xpi for the add-on from. + * @async + * @returns {string} Add-on ID that was installed + * @throws {string} If the add-on can not be installed. + */ + async install(installUrl) { + const installObj = await AddonManager.getInstallForURL(installUrl, null, "application/x-xpinstall"); + const result = new Promise((resolve, reject) => installObj.addListener({ + onInstallEnded(addonInstall, addon) { + resolve(addon.id); + }, + onInstallFailed(addonInstall) { + reject(`AddonInstall error code: [${addonInstall.error}]`); + }, + onDownloadFailed() { + reject(`Download failed: [${installUrl}]`); + }, + })); + installObj.install(); + return result; + }, + + /** + * Uninstalls an add-on by ID. + * @prop addonId {string} Add-on ID to uninstall. + * @async + * @throws If no add-on with `addonId` is installed. + */ + async uninstall(addonId) { + const addon = await AddonManager.getAddonByID(addonId); + if (addon === null) { + throw new Error(`No addon with ID [${addonId}] found.`); + } + addon.uninstall(); + return null; + }, + + /** + * Make a safe serialization of an add-on + * @param addon {Object} An add-on object as returned from AddonManager. + */ + serializeForSandbox(addon) { + return { + id: addon.id, + installDate: new Date(addon.installDate), + isActive: addon.isActive, + name: addon.name, + type: addon.type, + version: addon.version, + }; + }, +};
--- a/browser/extensions/shield-recipe-client/lib/ClientEnvironment.jsm +++ b/browser/extensions/shield-recipe-client/lib/ClientEnvironment.jsm @@ -11,19 +11,20 @@ Cu.import("resource://gre/modules/XPCOMU XPCOMUtils.defineLazyModuleGetter(this, "ShellService", "resource:///modules/ShellService.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "TelemetryArchive", "resource://gre/modules/TelemetryArchive.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NormandyApi", "resource://shield-recipe-client/lib/NormandyApi.jsm"); XPCOMUtils.defineLazyModuleGetter( this, "PreferenceExperiments", - "resource://shield-recipe-client/lib/PreferenceExperiments.jsm", + "resource://shield-recipe-client/lib/PreferenceExperiments.jsm" ); XPCOMUtils.defineLazyModuleGetter(this, "Utils", "resource://shield-recipe-client/lib/Utils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Addons", "resource://shield-recipe-client/lib/Addons.jsm"); const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); this.EXPORTED_SYMBOLS = ["ClientEnvironment"]; // Cached API request for client attributes that are determined by the Normandy // service. let _classifyRequest = null; @@ -31,17 +32,17 @@ let _classifyRequest = null; this.ClientEnvironment = { /** * Fetches information about the client that is calculated on the server, * like geolocation and the current time. * * The server request is made lazily and is cached for the entire browser * session. */ - getClientClassification() { + async getClientClassification() { if (!_classifyRequest) { _classifyRequest = NormandyApi.classifyClient(); } return _classifyRequest; }, clearClassifyCache() { _classifyRequest = null; @@ -192,11 +193,20 @@ this.ClientEnvironment = { } else { names.active.push(experiment.name); } } return names; }); + XPCOMUtils.defineLazyGetter(environment, "addons", async () => { + const addons = await Addons.getAll(); + return Utils.keyBy(addons, "id"); + }); + + XPCOMUtils.defineLazyGetter(environment, "isFirstRun", () => { + return Preferences.get("extensions.shield-recipe-client.first_run"); + }); + return environment; }, };
--- a/browser/extensions/shield-recipe-client/lib/FilterExpressions.jsm +++ b/browser/extensions/shield-recipe-client/lib/FilterExpressions.jsm @@ -4,40 +4,62 @@ "use strict"; const {utils: Cu} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://shield-recipe-client/lib/Sampling.jsm"); Cu.import("resource://shield-recipe-client/lib/PreferenceFilters.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "mozjexl", "resource://shield-recipe-client/vendor/mozjexl.js"); + this.EXPORTED_SYMBOLS = ["FilterExpressions"]; -XPCOMUtils.defineLazyGetter(this, "nodeRequire", () => { - const {Loader, Require} = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {}); - const loader = new Loader({ - paths: { - "": "resource://shield-recipe-client/node_modules/", - }, - }); - return new Require(loader, {}); -}); - XPCOMUtils.defineLazyGetter(this, "jexl", () => { - const {Jexl} = nodeRequire("jexl/lib/Jexl.js"); - const jexl = new Jexl(); + const jexl = new mozjexl.Jexl(); jexl.addTransforms({ date: dateString => new Date(dateString), stableSample: Sampling.stableSample, bucketSample: Sampling.bucketSample, preferenceValue: PreferenceFilters.preferenceValue, preferenceIsUserSet: PreferenceFilters.preferenceIsUserSet, preferenceExists: PreferenceFilters.preferenceExists, + keys, }); + jexl.addBinaryOp("intersect", 40, operatorIntersect); return jexl; }); this.FilterExpressions = { eval(expr, context = {}) { const onelineExpr = expr.replace(/[\t\n\r]/g, " "); return jexl.eval(onelineExpr, context); }, }; + +/** + * Return an array of the given object's own keys (specifically, its enumerable + * properties), or undefined if the argument isn't an object. + * @param {Object} obj + * @return {Array[String]|undefined} + */ +function keys(obj) { + if (typeof obj !== "object" || obj === null) { + return undefined; + } + + return Object.keys(obj); +} + +/** + * Find all the values that are present in both lists. Returns undefined if + * the arguments are not both Arrays. + * @param {Array} listA + * @param {Array} listB + * @return {Array|undefined} + */ +function operatorIntersect(listA, listB) { + if (!Array.isArray(listA) || !Array.isArray(listB)) { + return undefined; + } + + return listA.filter(item => listB.includes(item)); +}
--- a/browser/extensions/shield-recipe-client/lib/NormandyApi.jsm +++ b/browser/extensions/shield-recipe-client/lib/NormandyApi.jsm @@ -1,29 +1,34 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const {utils: Cu, classes: Cc, interfaces: Ci} = Components; Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/CanonicalJSON.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://shield-recipe-client/lib/LogManager.jsm"); -Cu.import("resource://shield-recipe-client/lib/Utils.jsm"); + +XPCOMUtils.defineLazyModuleGetter( + this, "CanonicalJSON", "resource://gre/modules/CanonicalJSON.jsm"); + Cu.importGlobalProperties(["fetch", "URL"]); /* globals fetch, URL */ this.EXPORTED_SYMBOLS = ["NormandyApi"]; const log = LogManager.getLogger("normandy-api"); const prefs = Services.prefs.getBranch("extensions.shield-recipe-client."); let indexPromise = null; this.NormandyApi = { + InvalidSignatureError: class InvalidSignatureError extends Error {}, + clearIndexCache() { indexPromise = null; }, apiCall(method, endpoint, data = {}) { const url = new URL(endpoint); method = method.toLowerCase(); @@ -58,17 +63,17 @@ this.NormandyApi = { } else if (url.startsWith("/")) { return server + url; } throw new Error("Can't use relative urls"); }, async getApiUrl(name) { if (!indexPromise) { - let apiBase = new URL(prefs.getCharPref("api_url")); + const apiBase = new URL(prefs.getCharPref("api_url")); if (!apiBase.pathname.endsWith("/")) { apiBase.pathname += "/"; } indexPromise = this.get(apiBase.toString()).then(res => res.json()); } const index = await indexPromise; if (!(name in index)) { throw new Error(`API endpoint with name "${name}" not found.`); @@ -84,35 +89,42 @@ this.NormandyApi = { const recipesWithSigs = JSON.parse(rawText); const verifiedRecipes = []; for (const {recipe, signature: {signature, x5u}} of recipesWithSigs) { const serialized = CanonicalJSON.stringify(recipe); if (!rawText.includes(serialized)) { log.debug(rawText, serialized); - throw new Error("Canonical recipe serialization does not match!"); + throw new NormandyApi.InvalidSignatureError("Canonical recipe serialization does not match!"); } - const certChainResponse = await fetch(this.absolutify(x5u)); + const certChainResponse = await this.get(this.absolutify(x5u)); const certChain = await certChainResponse.text(); const builtSignature = `p384ecdsa=${signature}`; const verifier = Cc["@mozilla.org/security/contentsignatureverifier;1"] .createInstance(Ci.nsIContentSignatureVerifier); - const valid = verifier.verifyContentSignature( - serialized, - builtSignature, - certChain, - "normandy.content-signature.mozilla.org" - ); + let valid; + try { + valid = verifier.verifyContentSignature( + serialized, + builtSignature, + certChain, + "normandy.content-signature.mozilla.org" + ); + } catch (err) { + throw new NormandyApi.InvalidSignatureError(`Recipe signature validation failed: ${err}`); + } + if (!valid) { - throw new Error("Recipe signature is not valid"); + throw new NormandyApi.InvalidSignatureError("Recipe signature is not valid"); } + verifiedRecipes.push(recipe); } log.debug( `Fetched ${verifiedRecipes.length} recipes from the server:`, verifiedRecipes.map(r => r.name).join(", ") );
--- a/browser/extensions/shield-recipe-client/lib/NormandyDriver.jsm +++ b/browser/extensions/shield-recipe-client/lib/NormandyDriver.jsm @@ -1,29 +1,34 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const {classes: Cc, interfaces: Ci, utils: Cu} = Components; +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource:///modules/ShellService.jsm"); Cu.import("resource://gre/modules/AddonManager.jsm"); Cu.import("resource://gre/modules/Timer.jsm"); +Cu.import("resource://shield-recipe-client/lib/Addons.jsm"); Cu.import("resource://shield-recipe-client/lib/LogManager.jsm"); Cu.import("resource://shield-recipe-client/lib/Storage.jsm"); Cu.import("resource://shield-recipe-client/lib/Heartbeat.jsm"); Cu.import("resource://shield-recipe-client/lib/FilterExpressions.jsm"); Cu.import("resource://shield-recipe-client/lib/ClientEnvironment.jsm"); Cu.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm"); Cu.import("resource://shield-recipe-client/lib/Sampling.jsm"); +XPCOMUtils.defineLazyModuleGetter( + this, "StudyStorage", "resource://shield-recipe-client/lib/StudyStorage.jsm"); + const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); this.EXPORTED_SYMBOLS = ["NormandyDriver"]; const log = LogManager.getLogger("normandy-driver"); const actionLog = LogManager.getLogger("normandy-driver.actions"); this.NormandyDriver = function(sandboxManager) { @@ -151,22 +156,36 @@ this.NormandyDriver = function(sandboxMa return Cu.cloneInto(token, sandbox); }, clearTimeout(token) { clearTimeout(token); sandboxManager.removeHold(`setTimeout-${token}`); }, + addons: { + get: sandboxManager.wrapAsync(Addons.get.bind(Addons), {cloneInto: true}), + install: sandboxManager.wrapAsync(Addons.install.bind(Addons)), + uninstall: sandboxManager.wrapAsync(Addons.uninstall.bind(Addons)), + }, + // Sampling ratioSample: sandboxManager.wrapAsync(Sampling.ratioSample), // Preference Experiment API preferenceExperiments: { start: sandboxManager.wrapAsync(PreferenceExperiments.start, {cloneArguments: true}), markLastSeen: sandboxManager.wrapAsync(PreferenceExperiments.markLastSeen), stop: sandboxManager.wrapAsync(PreferenceExperiments.stop), get: sandboxManager.wrapAsync(PreferenceExperiments.get, {cloneInto: true}), getAllActive: sandboxManager.wrapAsync(PreferenceExperiments.getAllActive, {cloneInto: true}), has: sandboxManager.wrapAsync(PreferenceExperiments.has), }, + + // Study storage API + studies: { + create: sandboxManager.wrapAsync(StudyStorage.create, {cloneArguments: true}), + update: sandboxManager.wrapAsync(StudyStorage.update, {cloneArguments: true}), + get: sandboxManager.wrapAsync(StudyStorage.get, {cloneInto: true}), + has: sandboxManager.wrapAsync(StudyStorage.has), + }, }; };
--- a/browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm +++ b/browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm @@ -24,37 +24,66 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, "SandboxManager", "resource://shield-recipe-client/lib/SandboxManager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ClientEnvironment", "resource://shield-recipe-client/lib/ClientEnvironment.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "CleanupManager", "resource://shield-recipe-client/lib/CleanupManager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ActionSandboxManager", "resource://shield-recipe-client/lib/ActionSandboxManager.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "StudyStorage", + "resource://shield-recipe-client/lib/StudyStorage.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Uptake", + "resource://shield-recipe-client/lib/Uptake.jsm"); Cu.importGlobalProperties(["fetch"]); this.EXPORTED_SYMBOLS = ["RecipeRunner"]; const log = LogManager.getLogger("recipe-runner"); const prefs = Services.prefs.getBranch("extensions.shield-recipe-client."); const TIMER_NAME = "recipe-client-addon-run"; const RUN_INTERVAL_PREF = "run_interval_seconds"; +const FIRST_RUN_PREF = "first_run"; +const UI_AVAILABLE_NOTIFICATION = "sessionstore-windows-restored"; +const SHIELD_INIT_NOTIFICATION = "shield-init-complete"; this.RecipeRunner = { init() { if (!this.checkPrefs()) { return; } if (prefs.getBoolPref("dev_mode")) { // Run right now in dev mode this.run(); } + if (prefs.getBoolPref(FIRST_RUN_PREF)) { + // Run once immediately after the UI is available. Do this before adding the + // timer so we can't end up racing it. + const observer = { + observe: (subject, topic, data) => { + Services.obs.removeObserver(observer, UI_AVAILABLE_NOTIFICATION); + + this.run(); + this.registerTimer(); + prefs.setBoolPref(FIRST_RUN_PREF, false); + + Services.obs.notifyObservers(null, SHIELD_INIT_NOTIFICATION); + }, + }; + Services.obs.addObserver(observer, UI_AVAILABLE_NOTIFICATION); + CleanupManager.addCleanupHandler(() => Services.obs.removeObserver(observer, UI_AVAILABLE_NOTIFICATION)); + } else { + this.registerTimer(); + } + }, + + registerTimer() { this.updateRunInterval(); CleanupManager.addCleanupHandler(() => timerManager.unregisterTimer(TIMER_NAME)); // Watch for the run interval to change, and re-register the timer with the new value prefs.addObserver(RUN_INTERVAL_PREF, this); CleanupManager.addCleanupHandler(() => prefs.removeObserver(RUN_INTERVAL_PREF, this)); }, @@ -97,106 +126,140 @@ this.RecipeRunner = { const runInterval = prefs.getIntPref(RUN_INTERVAL_PREF); timerManager.registerTimer(TIMER_NAME, () => this.run(), runInterval); }, async run() { this.clearCaches(); // Unless lazy classification is enabled, prep the classify cache. if (!Preferences.get("extensions.shield-recipe-client.experiments.lazy_classify", false)) { - await ClientEnvironment.getClientClassification(); + try { + await ClientEnvironment.getClientClassification(); + } catch (err) { + // Try to go on without this data; the filter expressions will + // gracefully fail without this info if they need it. + } + } + + // Fetch recipes before execution in case we fail and exit early. + let recipes; + try { + recipes = await NormandyApi.fetchRecipes({enabled: true}); + } catch (e) { + const apiUrl = prefs.getCharPref("api_url"); + log.error(`Could not fetch recipes from ${apiUrl}: "${e}"`); + + let status = Uptake.RUNNER_SERVER_ERROR; + if (/NetworkError/.test(e)) { + status = Uptake.RUNNER_NETWORK_ERROR; + } else if (e instanceof NormandyApi.InvalidSignatureError) { + status = Uptake.RUNNER_INVALID_SIGNATURE; + } + Uptake.reportRunner(status); + return; } const actionSandboxManagers = await this.loadActionSandboxManagers(); Object.values(actionSandboxManagers).forEach(manager => manager.addHold("recipeRunner")); // Run pre-execution hooks. If a hook fails, we don't run recipes with that // action to avoid inconsistencies. for (const [actionName, manager] of Object.entries(actionSandboxManagers)) { try { await manager.runAsyncCallback("preExecution"); manager.disabled = false; } catch (err) { log.error(`Could not run pre-execution hook for ${actionName}:`, err.message); manager.disabled = true; + Uptake.reportAction(actionName, Uptake.ACTION_PRE_EXECUTION_ERROR); } } - // Fetch recipes from the API - let recipes; - try { - recipes = await NormandyApi.fetchRecipes({enabled: true}); - } catch (e) { - const apiUrl = prefs.getCharPref("api_url"); - log.error(`Could not fetch recipes from ${apiUrl}: "${e}"`); - return; - } - // Evaluate recipe filters const recipesToRun = []; for (const recipe of recipes) { if (await this.checkFilter(recipe)) { recipesToRun.push(recipe); } } // Execute recipes, if we have any. if (recipesToRun.length === 0) { log.debug("No recipes to execute"); } else { for (const recipe of recipesToRun) { const manager = actionSandboxManagers[recipe.action]; + let status; if (!manager) { log.error( `Could not execute recipe ${recipe.name}:`, `Action ${recipe.action} is either missing or invalid.` ); + status = Uptake.RECIPE_INVALID_ACTION; } else if (manager.disabled) { log.warn( `Skipping recipe ${recipe.name} because ${recipe.action} failed during pre-execution.` ); + status = Uptake.RECIPE_ACTION_DISABLED; } else { try { log.info(`Executing recipe "${recipe.name}" (action=${recipe.action})`); await manager.runAsyncCallback("action", recipe); + status = Uptake.RECIPE_SUCCESS; } catch (e) { log.error(`Could not execute recipe ${recipe.name}:`, e); + status = Uptake.RECIPE_EXECUTION_ERROR; } } + + Uptake.reportRecipe(recipe.id, status); } } // Run post-execution hooks for (const [actionName, manager] of Object.entries(actionSandboxManagers)) { // Skip if pre-execution failed. if (manager.disabled) { log.info(`Skipping post-execution hook for ${actionName} due to earlier failure.`); continue; } try { await manager.runAsyncCallback("postExecution"); + Uptake.reportAction(actionName, Uptake.ACTION_SUCCESS); } catch (err) { log.info(`Could not run post-execution hook for ${actionName}:`, err.message); + Uptake.reportAction(actionName, Uptake.ACTION_POST_EXECUTION_ERROR); } } // Nuke sandboxes Object.values(actionSandboxManagers).forEach(manager => manager.removeHold("recipeRunner")); + + // Close storage connections + await StudyStorage.close(); + + Uptake.reportRunner(Uptake.RUNNER_SUCCESS); }, async loadActionSandboxManagers() { const actions = await NormandyApi.fetchActions(); const actionSandboxManagers = {}; for (const action of actions) { try { const implementation = await NormandyApi.fetchImplementation(action); actionSandboxManagers[action.name] = new ActionSandboxManager(implementation); } catch (err) { log.warn(`Could not fetch implementation for ${action.name}:`, err); + + let status = Uptake.ACTION_SERVER_ERROR; + if (/NetworkError/.test(err)) { + status = Uptake.ACTION_NETWORK_ERROR; + } + Uptake.reportAction(action.name, status); } } return actionSandboxManagers; }, getFilterContext(recipe) { return { normandy: Object.assign(ClientEnvironment.getEnvironment(), {
--- a/browser/extensions/shield-recipe-client/lib/ShieldRecipeClient.jsm +++ b/browser/extensions/shield-recipe-client/lib/ShieldRecipeClient.jsm @@ -34,19 +34,19 @@ const PREF_BRANCH = "extensions.shield-r const DEFAULT_PREFS = { api_url: "https://normandy.cdn.mozilla.net/api/v1", dev_mode: false, enabled: true, startup_delay_seconds: 300, "logging.level": Log.Level.Warn, user_id: "", run_interval_seconds: 86400, // 24 hours + first_run: true, }; const PREF_DEV_MODE = "extensions.shield-recipe-client.dev_mode"; -const PREF_SELF_SUPPORT_ENABLED = "browser.selfsupport.enabled"; const PREF_LOGGING_LEVEL = PREF_BRANCH + "logging.level"; let log = null; /** * Handles startup and shutdown of the entire add-on. Bootsrap.js defers to this * module for most tasks so that we can more easily test startup and shutdown * (bootstrap.js is difficult to import in tests). @@ -58,44 +58,29 @@ this.ShieldRecipeClient = { // Setup logging and listen for changes to logging prefs LogManager.configure(Services.prefs.getIntPref(PREF_LOGGING_LEVEL)); Preferences.observe(PREF_LOGGING_LEVEL, LogManager.configure); CleanupManager.addCleanupHandler( () => Preferences.ignore(PREF_LOGGING_LEVEL, LogManager.configure), ); log = LogManager.getLogger("bootstrap"); - // Disable self-support, since we replace its behavior. - // Self-support only checks its pref on start, so if we disable it, wait - // until next startup to run, unless the dev_mode preference is set. - if (Preferences.get(PREF_SELF_SUPPORT_ENABLED, true)) { - Preferences.set(PREF_SELF_SUPPORT_ENABLED, false); - if (!Preferences.get(PREF_DEV_MODE, false)) { - return; - } - } - // Initialize experiments first to avoid a race between initializing prefs // and recipes rolling back pref changes when experiments end. try { await PreferenceExperiments.init(); } catch (err) { log.error("Failed to initialize preference experiments:", err); } await RecipeRunner.init(); }, shutdown(reason) { CleanupManager.cleanup(); - - // Re-enable self-support if we're being disabled. - if (reason === REASONS.ADDON_DISABLE || reason === REASONS.ADDON_UNINSTALL) { - Services.prefs.setBoolPref(PREF_SELF_SUPPORT_ENABLED, true); - } }, setDefaultPrefs() { for (const [key, val] of Object.entries(DEFAULT_PREFS)) { const fullKey = PREF_BRANCH + key; // If someone beat us to setting a default, don't overwrite it. if (!Preferences.isSet(fullKey)) { Preferences.set(fullKey, val);
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/lib/StudyStorage.jsm @@ -0,0 +1,185 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * @typedef {Object} Study + * @property {string} name + * Unique name of the study + * @property {string} version + * Study add-on version number + * @property {string} description + * Description of the study and its intent. + * @property {string} studyStartDate + * ISO-formatted date string of when the study was started. + * @property {string} studyEndDate + * ISO-formatted date string of when the study was ended. + * @property {string} addonId + * Add-on ID for this particular study. + * @property {boolean} active + * Is the study still running? + */ + +"use strict"; + +const {utils: Cu} = Components; +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "IndexedDB", "resource://gre/modules/IndexedDB.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ajv", "resource://shield-recipe-client/vendor/ajv.js"); + +this.EXPORTED_SYMBOLS = ["StudyStorage"]; + +const DB_NAME = "shield-studies"; +const DB_OPTIONS = { + version: 1, + storage: "persistent", +}; +const DB_SCHEMA = { + type: "object", + additionalProperties: false, + properties: { + name: { + type: "string", + minLength: 1, + }, + addonId: { + type: "string", + minLength: 1, + }, + addonVersion: { + type: "string", + minLength: 1, + }, + description: { + type: "string", + minLength: 1, + }, + studyStartDate: { + type: "string", + minLength: 1, + format: "date-time", + }, + studyEndDate: { + type: ["string", "null"], + minLength: 1, + format: "date-time", + default: null, + }, + active: { + type: "boolean", + default: true, + }, + }, +}; +const CREATE_SCHEMA = Object.assign({}, DB_SCHEMA, { + required: [ + "name", + "addonId", + "addonVersion", + "description", + "studyStartDate", + ], +}); +const UPDATE_SCHEMA = DB_SCHEMA; + +const ajvInstance = new ajv({ + allErrors: true, + useDefaults: true, +}); +const validateCreate = ajvInstance.compile(CREATE_SCHEMA); +const validateUpdate = ajvInstance.compile(UPDATE_SCHEMA); + +/** + * Cache the database connection so that it is shared among multiple operations. + */ +let databasePromise; +async function getDatabase() { + if (!databasePromise) { + databasePromise = IndexedDB.open(DB_NAME, DB_OPTIONS, db => { + db.createObjectStore(DB_NAME, { + keyPath: "name", + }); + }); + } + return databasePromise; +} + +/** + * Get a transaction for interacting with the study store. + * + * NOTE: Methods on the store returned by this function MUST be called + * synchronously, otherwise the transaction with the store will expire. + * This is why the helper takes a database as an argument; if we fetched the + * database in the helper directly, the helper would be async and the + * transaction would expire before methods on the store were called. + */ +function getStore(db) { + return db.objectStore(DB_NAME, "readwrite"); +} + +this.StudyStorage = { + async clear() { + const db = await getDatabase(); + await getStore(db).clear(); + }, + + async close() { + if (databasePromise) { + const promise = databasePromise; + databasePromise = null; + const db = await promise; + await db.close(); + } + }, + + async has(studyName) { + const db = await getDatabase(); + const study = await getStore(db).get(studyName); + return !!study; + }, + + async get(studyName) { + const db = await getDatabase(); + const study = await getStore(db).get(studyName); + if (!study) { + throw new Error(`Could not find a study named ${studyName}.`); + } + + return study; + }, + + async create(study) { + if (!validateCreate(study)) { + throw new Error( + `Cannot create study: validation failed: ${ajvInstance.errorsText(validateCreate.errors)}`, + ); + } + + const db = await getDatabase(); + if (await getStore(db).get(study.name)) { + throw new Error( + `Cannot create study with name ${study.name}: a study exists with that name already.`, + ); + } + + return getStore(db).add(study); + }, + + async update(studyName, data) { + const db = await getDatabase(); + const savedStudy = await getStore(db).get(studyName); + if (!savedStudy) { + throw new Error(`Cannot update study ${studyName}: could not find study.`); + } + + if (!validateUpdate(data)) { + throw new Error(` + Cannot update study ${studyName}: validation failed: + ${ajvInstance.errorsText(validateUpdate.errors)} + `); + } + + return getStore(db).put(Object.assign({}, savedStudy, data)); + }, +};
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/lib/Uptake.jsm @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const {utils: Cu} = Components; +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter( + this, "UptakeTelemetry", "resource://services-common/uptake-telemetry.js"); + +this.EXPORTED_SYMBOLS = ["Uptake"]; + +const SOURCE_PREFIX = "shield-recipe-client"; + +this.Uptake = { + // Action uptake + ACTION_NETWORK_ERROR: UptakeTelemetry.STATUS.NETWORK_ERROR, + ACTION_PRE_EXECUTION_ERROR: UptakeTelemetry.STATUS.CUSTOM_1_ERROR, + ACTION_POST_EXECUTION_ERROR: UptakeTelemetry.STATUS.CUSTOM_2_ERROR, + ACTION_SERVER_ERROR: UptakeTelemetry.STATUS.SERVER_ERROR, + ACTION_SUCCESS: UptakeTelemetry.STATUS.SUCCESS, + + // Per-recipe uptake + RECIPE_ACTION_DISABLED: UptakeTelemetry.STATUS.CUSTOM_1_ERROR, + RECIPE_EXECUTION_ERROR: UptakeTelemetry.STATUS.APPLY_ERROR, + RECIPE_INVALID_ACTION: UptakeTelemetry.STATUS.DOWNLOAD_ERROR, + RECIPE_SUCCESS: UptakeTelemetry.STATUS.SUCCESS, + + // Uptake for the runner as a whole + RUNNER_INVALID_SIGNATURE: UptakeTelemetry.STATUS.SIGNATURE_ERROR, + RUNNER_NETWORK_ERROR: UptakeTelemetry.STATUS.NETWORK_ERROR, + RUNNER_SERVER_ERROR: UptakeTelemetry.STATUS.SERVER_ERROR, + RUNNER_SUCCESS: UptakeTelemetry.STATUS.SUCCESS, + + reportRunner(status) { + UptakeTelemetry.report(`${SOURCE_PREFIX}/runner`, status); + }, + + reportRecipe(recipeId, status) { + UptakeTelemetry.report(`${SOURCE_PREFIX}/recipe/${recipeId}`, status); + }, + + reportAction(actionName, status) { + UptakeTelemetry.report(`${SOURCE_PREFIX}/action/${actionName}`, status); + }, +};
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/node_modules/jexl/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2015 TechnologyAdvice - -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. \ No newline at end of file
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/node_modules/jexl/lib/Jexl.js +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Jexl - * Copyright (c) 2015 TechnologyAdvice - */ - -var Evaluator = require('./evaluator/Evaluator'), - Lexer = require('./Lexer'), - Parser = require('./parser/Parser'), - defaultGrammar = require('./grammar').elements; - -/** - * Jexl is the Javascript Expression Language, capable of parsing and - * evaluating basic to complex expression strings, combined with advanced - * xpath-like drilldown into native Javascript objects. - * @constructor - */ -function Jexl() { - this._customGrammar = null; - this._lexer = null; - this._transforms = {}; -} - -/** - * Adds a binary operator to Jexl at the specified precedence. The higher the - * precedence, the earlier the operator is applied in the order of operations. - * For example, * has a higher precedence than +, because multiplication comes - * before division. - * - * Please see grammar.js for a listing of all default operators and their - * precedence values in order to choose the appropriate precedence for the - * new operator. - * @param {string} operator The operator string to be added - * @param {number} precedence The operator's precedence - * @param {function} fn A function to run to calculate the result. The function - * will be called with two arguments: left and right, denoting the values - * on either side of the operator. It should return either the resulting - * value, or a Promise that resolves with the resulting value. - */ -Jexl.prototype.addBinaryOp = function(operator, precedence, fn) { - this._addGrammarElement(operator, { - type: 'binaryOp', - precedence: precedence, - eval: fn - }); -}; - -/** - * Adds a unary operator to Jexl. Unary operators are currently only supported - * on the left side of the value on which it will operate. - * @param {string} operator The operator string to be added - * @param {function} fn A function to run to calculate the result. The function - * will be called with one argument: the literal value to the right of the - * operator. It should return either the resulting value, or a Promise - * that resolves with the resulting value. - */ -Jexl.prototype.addUnaryOp = function(operator, fn) { - this._addGrammarElement(operator, { - type: 'unaryOp', - weight: Infinity, - eval: fn - }); -}; - -/** - * Adds or replaces a transform function in this Jexl instance. - * @param {string} name The name of the transform function, as it will be used - * within Jexl expressions - * @param {function} fn The function to be executed when this transform is - * invoked. It will be provided with two arguments: - * - {*} value: The value to be transformed - * - {{}} args: The arguments for this transform - * - {function} cb: A callback function to be called with an error - * if the transform fails, or a null first argument and the - * transformed value as the second argument on success. - */ -Jexl.prototype.addTransform = function(name, fn) { - this._transforms[name] = fn; -}; - -/** - * Syntactic sugar for calling {@link #addTransform} repeatedly. This function - * accepts a map of one or more transform names to their transform function. - * @param {{}} map A map of transform names to transform functions - */ -Jexl.prototype.addTransforms = function(map) { - for (var key in map) { - if (map.hasOwnProperty(key)) - this._transforms[key] = map[key]; - } -}; - -/** - * Retrieves a previously set transform function. - * @param {string} name The name of the transform function - * @returns {function} The transform function - */ -Jexl.prototype.getTransform = function(name) { - return this._transforms[name]; -}; - -/** - * Evaluates a Jexl string within an optional context. - * @param {string} expression The Jexl expression to be evaluated - * @param {Object} [context] A mapping of variables to values, which will be - * made accessible to the Jexl expression when evaluating it - * @param {function} [cb] An optional callback function to be executed when - * evaluation is complete. It will be supplied with two arguments: - * - {Error|null} err: Present if an error occurred - * - {*} result: The result of the evaluation - * @returns {Promise<*>} resolves with the result of the evaluation. Note that - * if a callback is supplied, the returned promise will already have - * a '.catch' attached to it in order to pass the error to the callback. - */ -Jexl.prototype.eval = function(expression, context, cb) { - if (typeof context === 'function') { - cb = context; - context = {}; - } - else if (!context) - context = {}; - var valPromise = this._eval(expression, context); - if (cb) { - // setTimeout is used for the callback to break out of the Promise's - // try/catch in case the callback throws. - var called = false; - return valPromise.then(function(val) { - called = true; - setTimeout(cb.bind(null, null, val), 0); - }).catch(function(err) { - if (!called) - setTimeout(cb.bind(null, err), 0); - }); - } - return valPromise; -}; - -/** - * Removes a binary or unary operator from the Jexl grammar. - * @param {string} operator The operator string to be removed - */ -Jexl.prototype.removeOp = function(operator) { - var grammar = this._getCustomGrammar(); - if (grammar[operator] && (grammar[operator].type == 'binaryOp' || - grammar[operator].type == 'unaryOp')) { - delete grammar[operator]; - this._lexer = null; - } -}; - -/** - * Adds an element to the grammar map used by this Jexl instance, cloning - * the default grammar first if necessary. - * @param {string} str The key string to be added - * @param {{type: <string>}} obj A map of configuration options for this - * grammar element - * @private - */ -Jexl.prototype._addGrammarElement = function(str, obj) { - var grammar = this._getCustomGrammar(); - grammar[str] = obj; - this._lexer = null; -}; - -/** - * Evaluates a Jexl string in the given context. - * @param {string} exp The Jexl expression to be evaluated - * @param {Object} [context] A mapping of variables to values, which will be - * made accessible to the Jexl expression when evaluating it - * @returns {Promise<*>} resolves with the result of the evaluation. - * @private - */ -Jexl.prototype._eval = function(exp, context) { - var self = this, - grammar = this._getGrammar(), - parser = new Parser(grammar), - evaluator = new Evaluator(grammar, this._transforms, context); - return Promise.resolve().then(function() { - parser.addTokens(self._getLexer().tokenize(exp)); - return evaluator.eval(parser.complete()); - }); -}; - -/** - * Gets the custom grammar object, creating it first if necessary. New custom - * grammars are created by executing a shallow clone of the default grammar - * map. The returned map is available to be changed. - * @returns {{}} a customizable grammar map. - * @private - */ -Jexl.prototype._getCustomGrammar = function() { - if (!this._customGrammar) { - this._customGrammar = {}; - for (var key in defaultGrammar) { - if (defaultGrammar.hasOwnProperty(key)) - this._customGrammar[key] = defaultGrammar[key]; - } - } - return this._customGrammar; -}; - -/** - * Gets the grammar map currently being used by Jexl; either the default map, - * or a locally customized version. The returned map should never be changed - * in any way. - * @returns {{}} the grammar map currently in use. - * @private - */ -Jexl.prototype._getGrammar = function() { - return this._customGrammar || defaultGrammar; -}; - -/** - * Gets a Lexer instance as a singleton in reference to this Jexl instance. - * @returns {Lexer} an instance of Lexer, initialized with a grammar - * appropriate to this Jexl instance. - * @private - */ -Jexl.prototype._getLexer = function() { - if (!this._lexer) - this._lexer = new Lexer(this._getGrammar()); - return this._lexer; -}; - -module.exports = new Jexl(); -module.exports.Jexl = Jexl;
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/node_modules/jexl/lib/Lexer.js +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Jexl - * Copyright (c) 2015 TechnologyAdvice - */ - -var numericRegex = /^-?(?:(?:[0-9]*\.[0-9]+)|[0-9]+)$/, - identRegex = /^[a-zA-Z_\$][a-zA-Z0-9_\$]*$/, - escEscRegex = /\\\\/, - preOpRegexElems = [ - // Strings - "'(?:(?:\\\\')?[^'])*'", - '"(?:(?:\\\\")?[^"])*"', - // Whitespace - '\\s+', - // Booleans - '\\btrue\\b', - '\\bfalse\\b' - ], - postOpRegexElems = [ - // Identifiers - '\\b[a-zA-Z_\\$][a-zA-Z0-9_\\$]*\\b', - // Numerics (without negative symbol) - '(?:(?:[0-9]*\\.[0-9]+)|[0-9]+)' - ], - minusNegatesAfter = ['binaryOp', 'unaryOp', 'openParen', 'openBracket', - 'question', 'colon']; - -/** - * Lexer is a collection of stateless, statically-accessed functions for the - * lexical parsing of a Jexl string. Its responsibility is to identify the - * "parts of speech" of a Jexl expression, and tokenize and label each, but - * to do only the most minimal syntax checking; the only errors the Lexer - * should be concerned with are if it's unable to identify the utility of - * any of its tokens. Errors stemming from these tokens not being in a - * sensible configuration should be left for the Parser to handle. - * @type {{}} - */ -function Lexer(grammar) { - this._grammar = grammar; -} - -/** - * Splits a Jexl expression string into an array of expression elements. - * @param {string} str A Jexl expression string - * @returns {Array<string>} An array of substrings defining the functional - * elements of the expression. - */ -Lexer.prototype.getElements = function(str) { - var regex = this._getSplitRegex(); - return str.split(regex).filter(function(elem) { - // Remove empty strings - return elem; - }); -}; - -/** - * Converts an array of expression elements into an array of tokens. Note that - * the resulting array may not equal the element array in length, as any - * elements that consist only of whitespace get appended to the previous - * token's "raw" property. For the structure of a token object, please see - * {@link Lexer#tokenize}. - * @param {Array<string>} elements An array of Jexl expression elements to be - * converted to tokens - * @returns {Array<{type, value, raw}>} an array of token objects. - */ -Lexer.prototype.getTokens = function(elements) { - var tokens = [], - negate = false; - for (var i = 0; i < elements.length; i++) { - if (this._isWhitespace(elements[i])) { - if (tokens.length) - tokens[tokens.length - 1].raw += elements[i]; - } - else if (elements[i] === '-' && this._isNegative(tokens)) - negate = true; - else { - if (negate) { - elements[i] = '-' + elements[i]; - negate = false; - } - tokens.push(this._createToken(elements[i])); - } - } - // Catch a - at the end of the string. Let the parser handle that issue. - if (negate) - tokens.push(this._createToken('-')); - return tokens; -}; - -/** - * Converts a Jexl string into an array of tokens. Each token is an object - * in the following format: - * - * { - * type: <string>, - * [name]: <string>, - * value: <boolean|number|string>, - * raw: <string> - * } - * - * Type is one of the following: - * - * literal, identifier, binaryOp, unaryOp - * - * OR, if the token is a control character its type is the name of the element - * defined in the Grammar. - * - * Name appears only if the token is a control string found in - * {@link grammar#elements}, and is set to the name of the element. - * - * Value is the value of the token in the correct type (boolean or numeric as - * appropriate). Raw is the string representation of this value taken directly - * from the expression string, including any trailing spaces. - * @param {string} str The Jexl string to be tokenized - * @returns {Array<{type, value, raw}>} an array of token objects. - * @throws {Error} if the provided string contains an invalid token. - */ -Lexer.prototype.tokenize = function(str) { - var elements = this.getElements(str); - return this.getTokens(elements); -}; - -/** - * Creates a new token object from an element of a Jexl string. See - * {@link Lexer#tokenize} for a description of the token object. - * @param {string} element The element from which a token should be made - * @returns {{value: number|boolean|string, [name]: string, type: string, - * raw: string}} a token object describing the provided element. - * @throws {Error} if the provided string is not a valid expression element. - * @private - */ -Lexer.prototype._createToken = function(element) { - var token = { - type: 'literal', - value: element, - raw: element - }; - if (element[0] == '"' || element[0] == "'") - token.value = this._unquote(element); - else if (element.match(numericRegex)) - token.value = parseFloat(element); - else if (element === 'true' || element === 'false') - token.value = element === 'true'; - else if (this._grammar[element]) - token.type = this._grammar[element].type; - else if (element.match(identRegex)) - token.type = 'identifier'; - else - throw new Error("Invalid expression token: " + element); - return token; -}; - -/** - * Escapes a string so that it can be treated as a string literal within a - * regular expression. - * @param {string} str The string to be escaped - * @returns {string} the RegExp-escaped string. - * @see https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions - * @private - */ -Lexer.prototype._escapeRegExp = function(str) { - str = str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - if (str.match(identRegex)) - str = '\\b' + str + '\\b'; - return str; -}; - -/** - * Gets a RegEx object appropriate for splitting a Jexl string into its core - * elements. - * @returns {RegExp} An element-splitting RegExp object - * @private - */ -Lexer.prototype._getSplitRegex = function() { - if (!this._splitRegex) { - var elemArray = Object.keys(this._grammar); - // Sort by most characters to least, then regex escape each - elemArray = elemArray.sort(function(a ,b) { - return b.length - a.length; - }).map(function(elem) { - return this._escapeRegExp(elem); - }, this); - this._splitRegex = new RegExp('(' + [ - preOpRegexElems.join('|'), - elemArray.join('|'), - postOpRegexElems.join('|') - ].join('|') + ')'); - } - return this._splitRegex; -}; - -/** - * Determines whether the addition of a '-' token should be interpreted as a - * negative symbol for an upcoming number, given an array of tokens already - * processed. - * @param {Array<Object>} tokens An array of tokens already processed - * @returns {boolean} true if adding a '-' should be considered a negative - * symbol; false otherwise - * @private - */ -Lexer.prototype._isNegative = function(tokens) { - if (!tokens.length) - return true; - return minusNegatesAfter.some(function(type) { - return type === tokens[tokens.length - 1].type; - }); -}; - -/** - * A utility function to determine if a string consists of only space - * characters. - * @param {string} str A string to be tested - * @returns {boolean} true if the string is empty or consists of only spaces; - * false otherwise. - * @private - */ -Lexer.prototype._isWhitespace = function(str) { - for (var i = 0; i < str.length; i++) { - if (str[i] != ' ') - return false; - } - return true; -}; - -/** - * Removes the beginning and trailing quotes from a string, unescapes any - * escaped quotes on its interior, and unescapes any escaped escape characters. - * Note that this function is not defensive; it assumes that the provided - * string is not empty, and that its first and last characters are actually - * quotes. - * @param {string} str A string whose first and last characters are quotes - * @returns {string} a string with the surrounding quotes stripped and escapes - * properly processed. - * @private - */ -Lexer.prototype._unquote = function(str) { - var quote = str[0], - escQuoteRegex = new RegExp('\\\\' + quote, 'g'); - return str.substr(1, str.length - 2) - .replace(escQuoteRegex, quote) - .replace(escEscRegex, '\\'); -}; - -module.exports = Lexer;
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/node_modules/jexl/lib/evaluator/Evaluator.js +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Jexl - * Copyright (c) 2015 TechnologyAdvice - */ - -var handlers = require('./handlers'); - -/** - * The Evaluator takes a Jexl expression tree as generated by the - * {@link Parser} and calculates its value within a given context. The - * collection of transforms, context, and a relative context to be used as the - * root for relative identifiers, are all specific to an Evaluator instance. - * When any of these things change, a new instance is required. However, a - * single instance can be used to simultaneously evaluate many different - * expressions, and does not have to be reinstantiated for each. - * @param {{}} grammar A grammar map against which to evaluate the expression - * tree - * @param {{}} [transforms] A map of transform names to transform functions. A - * transform function takes two arguments: - * - {*} val: A value to be transformed - * - {{}} args: A map of argument keys to their evaluated values, as - * specified in the expression string - * The transform function should return either the transformed value, or - * a Promises/A+ Promise object that resolves with the value and rejects - * or throws only when an unrecoverable error occurs. Transforms should - * generally return undefined when they don't make sense to be used on the - * given value type, rather than throw/reject. An error is only - * appropriate when the transform would normally return a value, but - * cannot due to some other failure. - * @param {{}} [context] A map of variable keys to their values. This will be - * accessed to resolve the value of each non-relative identifier. Any - * Promise values will be passed to the expression as their resolved - * value. - * @param {{}|Array<{}|Array>} [relativeContext] A map or array to be accessed - * to resolve the value of a relative identifier. - * @constructor - */ -var Evaluator = function(grammar, transforms, context, relativeContext) { - this._grammar = grammar; - this._transforms = transforms || {}; - this._context = context || {}; - this._relContext = relativeContext || this._context; -}; - -/** - * Evaluates an expression tree within the configured context. - * @param {{}} ast An expression tree object - * @returns {Promise<*>} resolves with the resulting value of the expression. - */ -Evaluator.prototype.eval = function(ast) { - var self = this; - return Promise.resolve().then(function() { - return handlers[ast.type].call(self, ast); - }); -}; - -/** - * Simultaneously evaluates each expression within an array, and delivers the - * response as an array with the resulting values at the same indexes as their - * originating expressions. - * @param {Array<string>} arr An array of expression strings to be evaluated - * @returns {Promise<Array<{}>>} resolves with the result array - */ -Evaluator.prototype.evalArray = function(arr) { - return Promise.all(arr.map(function(elem) { - return this.eval(elem); - }, this)); -}; - -/** - * Simultaneously evaluates each expression within a map, and delivers the - * response as a map with the same keys, but with the evaluated result for each - * as their value. - * @param {{}} map A map of expression names to expression trees to be - * evaluated - * @returns {Promise<{}>} resolves with the result map. - */ -Evaluator.prototype.evalMap = function(map) { - var keys = Object.keys(map), - result = {}; - var asts = keys.map(function(key) { - return this.eval(map[key]); - }, this); - return Promise.all(asts).then(function(vals) { - vals.forEach(function(val, idx) { - result[keys[idx]] = val; - }); - return result; - }); -}; - -/** - * Applies a filter expression with relative identifier elements to a subject. - * The intent is for the subject to be an array of subjects that will be - * individually used as the relative context against the provided expression - * tree. Only the elements whose expressions result in a truthy value will be - * included in the resulting array. - * - * If the subject is not an array of values, it will be converted to a single- - * element array before running the filter. - * @param {*} subject The value to be filtered; usually an array. If this value is - * not an array, it will be converted to an array with this value as the - * only element. - * @param {{}} expr The expression tree to run against each subject. If the - * tree evaluates to a truthy result, then the value will be included in - * the returned array; otherwise, it will be eliminated. - * @returns {Promise<Array>} resolves with an array of values that passed the - * expression filter. - * @private - */ -Evaluator.prototype._filterRelative = function(subject, expr) { - var promises = []; - if (!Array.isArray(subject)) - subject = [subject]; - subject.forEach(function(elem) { - var evalInst = new Evaluator(this._grammar, this._transforms, - this._context, elem); - promises.push(evalInst.eval(expr)); - }, this); - return Promise.all(promises).then(function(values) { - var results = []; - values.forEach(function(value, idx) { - if (value) - results.push(subject[idx]); - }); - return results; - }); -}; - -/** - * Applies a static filter expression to a subject value. If the filter - * expression evaluates to boolean true, the subject is returned; if false, - * undefined. - * - * For any other resulting value of the expression, this function will attempt - * to respond with the property at that name or index of the subject. - * @param {*} subject The value to be filtered. Usually an Array (for which - * the expression would generally resolve to a numeric index) or an - * Object (for which the expression would generally resolve to a string - * indicating a property name) - * @param {{}} expr The expression tree to run against the subject - * @returns {Promise<*>} resolves with the value of the drill-down. - * @private - */ -Evaluator.prototype._filterStatic = function(subject, expr) { - return this.eval(expr).then(function(res) { - if (typeof res === 'boolean') - return res ? subject : undefined; - return subject[res]; - }); -}; - -module.exports = Evaluator;
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/node_modules/jexl/lib/evaluator/handlers.js +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Jexl - * Copyright (c) 2015 TechnologyAdvice - */ - -/** - * Evaluates an ArrayLiteral by returning its value, with each element - * independently run through the evaluator. - * @param {{type: 'ObjectLiteral', value: <{}>}} ast An expression tree with an - * ObjectLiteral as the top node - * @returns {Promise.<[]>} resolves to a map contained evaluated values. - * @private - */ -exports.ArrayLiteral = function(ast) { - return this.evalArray(ast.value); -}; - -/** - * Evaluates a BinaryExpression node by running the Grammar's evaluator for - * the given operator. - * @param {{type: 'BinaryExpression', operator: <string>, left: {}, - * right: {}}} ast An expression tree with a BinaryExpression as the top - * node - * @returns {Promise<*>} resolves with the value of the BinaryExpression. - * @private - */ -exports.BinaryExpression = function(ast) { - var self = this; - return Promise.all([ - this.eval(ast.left), - this.eval(ast.right) - ]).then(function(arr) { - return self._grammar[ast.operator].eval(arr[0], arr[1]); - }); -}; - -/** - * Evaluates a ConditionalExpression node by first evaluating its test branch, - * and resolving with the consequent branch if the test is truthy, or the - * alternate branch if it is not. If there is no consequent branch, the test - * result will be used instead. - * @param {{type: 'ConditionalExpression', test: {}, consequent: {}, - * alternate: {}}} ast An expression tree with a ConditionalExpression as - * the top node - * @private - */ -exports.ConditionalExpression = function(ast) { - var self = this; - return this.eval(ast.test).then(function(res) { - if (res) { - if (ast.consequent) - return self.eval(ast.consequent); - return res; - } - return self.eval(ast.alternate); - }); -}; - -/** - * Evaluates a FilterExpression by applying it to the subject value. - * @param {{type: 'FilterExpression', relative: <boolean>, expr: {}, - * subject: {}}} ast An expression tree with a FilterExpression as the top - * node - * @returns {Promise<*>} resolves with the value of the FilterExpression. - * @private - */ -exports.FilterExpression = function(ast) { - var self = this; - return this.eval(ast.subject).then(function(subject) { - if (ast.relative) - return self._filterRelative(subject, ast.expr); - return self._filterStatic(subject, ast.expr); - }); -}; - -/** - * Evaluates an Identifier by either stemming from the evaluated 'from' - * expression tree or accessing the context provided when this Evaluator was - * constructed. - * @param {{type: 'Identifier', value: <string>, [from]: {}}} ast An expression - * tree with an Identifier as the top node - * @returns {Promise<*>|*} either the identifier's value, or a Promise that - * will resolve with the identifier's value. - * @private - */ -exports.Identifier = function(ast) { - if (ast.from) { - return this.eval(ast.from).then(function(context) { - if (context === undefined) - return undefined; - if (Array.isArray(context)) - context = context[0]; - return context[ast.value]; - }); - } - else { - return ast.relative ? this._relContext[ast.value] : - this._context[ast.value]; - } -}; - -/** - * Evaluates a Literal by returning its value property. - * @param {{type: 'Literal', value: <string|number|boolean>}} ast An expression - * tree with a Literal as its only node - * @returns {string|number|boolean} The value of the Literal node - * @private - */ -exports.Literal = function(ast) { - return ast.value; -}; - -/** - * Evaluates an ObjectLiteral by returning its value, with each key - * independently run through the evaluator. - * @param {{type: 'ObjectLiteral', value: <{}>}} ast An expression tree with an - * ObjectLiteral as the top node - * @returns {Promise<{}>} resolves to a map contained evaluated values. - * @private - */ -exports.ObjectLiteral = function(ast) { - return this.evalMap(ast.value); -}; - -/** - * Evaluates a Transform node by applying a function from the transforms map - * to the subject value. - * @param {{type: 'Transform', name: <string>, subject: {}}} ast An - * expression tree with a Transform as the top node - * @returns {Promise<*>|*} the value of the transformation, or a Promise that - * will resolve with the transformed value. - * @private - */ -exports.Transform = function(ast) { - var transform = this._transforms[ast.name]; - if (!transform) - throw new Error("Transform '" + ast.name + "' is not defined."); - return Promise.all([ - this.eval(ast.subject), - this.evalArray(ast.args || []) - ]).then(function(arr) { - return transform.apply(null, [arr[0]].concat(arr[1])); - }); -}; - -/** - * Evaluates a Unary expression by passing the right side through the - * operator's eval function. - * @param {{type: 'UnaryExpression', operator: <string>, right: {}}} ast An - * expression tree with a UnaryExpression as the top node - * @returns {Promise<*>} resolves with the value of the UnaryExpression. - * @constructor - */ -exports.UnaryExpression = function(ast) { - var self = this; - return this.eval(ast.right).then(function(right) { - return self._grammar[ast.operator].eval(right); - }); -};
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/node_modules/jexl/lib/grammar.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Jexl - * Copyright (c) 2015 TechnologyAdvice - */ - -/** - * A map of all expression elements to their properties. Note that changes - * here may require changes in the Lexer or Parser. - * @type {{}} - */ -exports.elements = { - '.': {type: 'dot'}, - '[': {type: 'openBracket'}, - ']': {type: 'closeBracket'}, - '|': {type: 'pipe'}, - '{': {type: 'openCurl'}, - '}': {type: 'closeCurl'}, - ':': {type: 'colon'}, - ',': {type: 'comma'}, - '(': {type: 'openParen'}, - ')': {type: 'closeParen'}, - '?': {type: 'question'}, - '+': {type: 'binaryOp', precedence: 30, - eval: function(left, right) { return left + right; }}, - '-': {type: 'binaryOp', precedence: 30, - eval: function(left, right) { return left - right; }}, - '*': {type: 'binaryOp', precedence: 40, - eval: function(left, right) { return left * right; }}, - '/': {type: 'binaryOp', precedence: 40, - eval: function(left, right) { return left / right; }}, - '//': {type: 'binaryOp', precedence: 40, - eval: function(left, right) { return Math.floor(left / right); }}, - '%': {type: 'binaryOp', precedence: 50, - eval: function(left, right) { return left % right; }}, - '^': {type: 'binaryOp', precedence: 50, - eval: function(left, right) { return Math.pow(left, right); }}, - '==': {type: 'binaryOp', precedence: 20, - eval: function(left, right) { return left == right; }}, - '!=': {type: 'binaryOp', precedence: 20, - eval: function(left, right) { return left != right; }}, - '>': {type: 'binaryOp', precedence: 20, - eval: function(left, right) { return left > right; }}, - '>=': {type: 'binaryOp', precedence: 20, - eval: function(left, right) { return left >= right; }}, - '<': {type: 'binaryOp', precedence: 20, - eval: function(left, right) { return left < right; }}, - '<=': {type: 'binaryOp', precedence: 20, - eval: function(left, right) { return left <= right; }}, - '&&': {type: 'binaryOp', precedence: 10, - eval: function(left, right) { return left && right; }}, - '||': {type: 'binaryOp', precedence: 10, - eval: function(left, right) { return left || right; }}, - 'in': {type: 'binaryOp', precedence: 20, - eval: function(left, right) { - if (typeof right === 'string') - return right.indexOf(left) !== -1; - if (Array.isArray(right)) { - return right.some(function(elem) { - return elem == left; - }); - } - return false; - }}, - '!': {type: 'unaryOp', precedence: Infinity, - eval: function(right) { return !right; }} -};
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/node_modules/jexl/lib/parser/Parser.js +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Jexl - * Copyright (c) 2015 TechnologyAdvice - */ - -var handlers = require('./handlers'), - states = require('./states').states; - -/** - * The Parser is a state machine that converts tokens from the {@link Lexer} - * into an Abstract Syntax Tree (AST), capable of being evaluated in any - * context by the {@link Evaluator}. The Parser expects that all tokens - * provided to it are legal and typed properly according to the grammar, but - * accepts that the tokens may still be in an invalid order or in some other - * unparsable configuration that requires it to throw an Error. - * @param {{}} grammar The grammar map to use to parse Jexl strings - * @param {string} [prefix] A string prefix to prepend to the expression string - * for error messaging purposes. This is useful for when a new Parser is - * instantiated to parse an subexpression, as the parent Parser's - * expression string thus far can be passed for a more user-friendly - * error message. - * @param {{}} [stopMap] A mapping of token types to any truthy value. When the - * token type is encountered, the parser will return the mapped value - * instead of boolean false. - * @constructor - */ -function Parser(grammar, prefix, stopMap) { - this._grammar = grammar; - this._state = 'expectOperand'; - this._tree = null; - this._exprStr = prefix || ''; - this._relative = false; - this._stopMap = stopMap || {}; -} - -/** - * Processes a new token into the AST and manages the transitions of the state - * machine. - * @param {{type: <string>}} token A token object, as provided by the - * {@link Lexer#tokenize} function. - * @throws {Error} if a token is added when the Parser has been marked as - * complete by {@link #complete}, or if an unexpected token type is added. - * @returns {boolean|*} the stopState value if this parser encountered a token - * in the stopState mapb; false if tokens can continue. - */ -Parser.prototype.addToken = function(token) { - if (this._state == 'complete') - throw new Error('Cannot add a new token to a completed Parser'); - var state = states[this._state], - startExpr = this._exprStr; - this._exprStr += token.raw; - if (state.subHandler) { - if (!this._subParser) - this._startSubExpression(startExpr); - var stopState = this._subParser.addToken(token); - if (stopState) { - this._endSubExpression(); - if (this._parentStop) - return stopState; - this._state = stopState; - } - } - else if (state.tokenTypes[token.type]) { - var typeOpts = state.tokenTypes[token.type], - handleFunc = handlers[token.type]; - if (typeOpts.handler) - handleFunc = typeOpts.handler; - if (handleFunc) - handleFunc.call(this, token); - if (typeOpts.toState) - this._state = typeOpts.toState; - } - else if (this._stopMap[token.type]) - return this._stopMap[token.type]; - else { - throw new Error('Token ' + token.raw + ' (' + token.type + - ') unexpected in expression: ' + this._exprStr); - } - return false; -}; - -/** - * Processes an array of tokens iteratively through the {@link #addToken} - * function. - * @param {Array<{type: <string>}>} tokens An array of tokens, as provided by - * the {@link Lexer#tokenize} function. - */ -Parser.prototype.addTokens = function(tokens) { - tokens.forEach(this.addToken, this); -}; - -/** - * Marks this Parser instance as completed and retrieves the full AST. - * @returns {{}|null} a full expression tree, ready for evaluation by the - * {@link Evaluator#eval} function, or null if no tokens were passed to - * the parser before complete was called - * @throws {Error} if the parser is not in a state where it's legal to end - * the expression, indicating that the expression is incomplete - */ -Parser.prototype.complete = function() { - if (this._cursor && !states[this._state].completable) - throw new Error('Unexpected end of expression: ' + this._exprStr); - if (this._subParser) - this._endSubExpression(); - this._state = 'complete'; - return this._cursor ? this._tree : null; -}; - -/** - * Indicates whether the expression tree contains a relative path identifier. - * @returns {boolean} true if a relative identifier exists; false otherwise. - */ -Parser.prototype.isRelative = function() { - return this._relative; -}; - -/** - * Ends a subexpression by completing the subParser and passing its result - * to the subHandler configured in the current state. - * @private - */ -Parser.prototype._endSubExpression = function() { - states[this._state].subHandler.call(this, this._subParser.complete()); - this._subParser = null; -}; - -/** - * Places a new tree node at the current position of the cursor (to the 'right' - * property) and then advances the cursor to the new node. This function also - * handles setting the parent of the new node. - * @param {{type: <string>}} node A node to be added to the AST - * @private - */ -Parser.prototype._placeAtCursor = function(node) { - if (!this._cursor) - this._tree = node; - else { - this._cursor.right = node; - this._setParent(node, this._cursor); - } - this._cursor = node; -}; - -/** - * Places a tree node before the current position of the cursor, replacing - * the node that the cursor currently points to. This should only be called in - * cases where the cursor is known to exist, and the provided node already - * contains a pointer to what's at the cursor currently. - * @param {{type: <string>}} node A node to be added to the AST - * @private - */ -Parser.prototype._placeBeforeCursor = function(node) { - this._cursor = this._cursor._parent; - this._placeAtCursor(node); -}; - -/** - * Sets the parent of a node by creating a non-enumerable _parent property - * that points to the supplied parent argument. - * @param {{type: <string>}} node A node of the AST on which to set a new - * parent - * @param {{type: <string>}} parent An existing node of the AST to serve as the - * parent of the new node - * @private - */ -Parser.prototype._setParent = function(node, parent) { - Object.defineProperty(node, '_parent', { - value: parent, - writable: true - }); -}; - -/** - * Prepares the Parser to accept a subexpression by (re)instantiating the - * subParser. - * @param {string} [exprStr] The expression string to prefix to the new Parser - * @private - */ -Parser.prototype._startSubExpression = function(exprStr) { - var endStates = states[this._state].endStates; - if (!endStates) { - this._parentStop = true; - endStates = this._stopMap; - } - this._subParser = new Parser(this._grammar, exprStr, endStates); -}; - -module.exports = Parser;
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/node_modules/jexl/lib/parser/handlers.js +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Jexl - * Copyright (c) 2015 TechnologyAdvice - */ - -/** - * Handles a subexpression that's used to define a transform argument's value. - * @param {{type: <string>}} ast The subexpression tree - */ -exports.argVal = function(ast) { - this._cursor.args.push(ast); -}; - -/** - * Handles new array literals by adding them as a new node in the AST, - * initialized with an empty array. - */ -exports.arrayStart = function() { - this._placeAtCursor({ - type: 'ArrayLiteral', - value: [] - }); -}; - -/** - * Handles a subexpression representing an element of an array literal. - * @param {{type: <string>}} ast The subexpression tree - */ -exports.arrayVal = function(ast) { - if (ast) - this._cursor.value.push(ast); -}; - -/** - * Handles tokens of type 'binaryOp', indicating an operation that has two - * inputs: a left side and a right side. - * @param {{type: <string>}} token A token object - */ -exports.binaryOp = function(token) { - var precedence = this._grammar[token.value].precedence || 0, - parent = this._cursor._parent; - while (parent && parent.operator && - this._grammar[parent.operator].precedence >= precedence) { - this._cursor = parent; - parent = parent._parent; - } - var node = { - type: 'BinaryExpression', - operator: token.value, - left: this._cursor - }; - this._setParent(this._cursor, node); - this._cursor = parent; - this._placeAtCursor(node); -}; - -/** - * Handles successive nodes in an identifier chain. More specifically, it - * sets values that determine how the following identifier gets placed in the - * AST. - */ -exports.dot = function() { - this._nextIdentEncapsulate = this._cursor && - (this._cursor.type != 'BinaryExpression' || - (this._cursor.type == 'BinaryExpression' && this._cursor.right)) && - this._cursor.type != 'UnaryExpression'; - this._nextIdentRelative = !this._cursor || - (this._cursor && !this._nextIdentEncapsulate); - if (this._nextIdentRelative) - this._relative = true; -}; - -/** - * Handles a subexpression used for filtering an array returned by an - * identifier chain. - * @param {{type: <string>}} ast The subexpression tree - */ -exports.filter = function(ast) { - this._placeBeforeCursor({ - type: 'FilterExpression', - expr: ast, - relative: this._subParser.isRelative(), - subject: this._cursor - }); -}; - -/** - * Handles identifier tokens by adding them as a new node in the AST. - * @param {{type: <string>}} token A token object - */ -exports.identifier = function(token) { - var node = { - type: 'Identifier', - value: token.value - }; - if (this._nextIdentEncapsulate) { - node.from = this._cursor; - this._placeBeforeCursor(node); - this._nextIdentEncapsulate = false; - } - else { - if (this._nextIdentRelative) - node.relative = true; - this._placeAtCursor(node); - } -}; - -/** - * Handles literal values, such as strings, booleans, and numerics, by adding - * them as a new node in the AST. - * @param {{type: <string>}} token A token object - */ -exports.literal = function(token) { - this._placeAtCursor({ - type: 'Literal', - value: token.value - }); -}; - -/** - * Queues a new object literal key to be written once a value is collected. - * @param {{type: <string>}} token A token object - */ -exports.objKey = function(token) { - this._curObjKey = token.value; -}; - -/** - * Handles new object literals by adding them as a new node in the AST, - * initialized with an empty object. - */ -exports.objStart = function() { - this._placeAtCursor({ - type: 'ObjectLiteral', - value: {} - }); -}; - -/** - * Handles an object value by adding its AST to the queued key on the object - * literal node currently at the cursor. - * @param {{type: <string>}} ast The subexpression tree - */ -exports.objVal = function(ast) { - this._cursor.value[this._curObjKey] = ast; -}; - -/** - * Handles traditional subexpressions, delineated with the groupStart and - * groupEnd elements. - * @param {{type: <string>}} ast The subexpression tree - */ -exports.subExpression = function(ast) { - this._placeAtCursor(ast); -}; - -/** - * Handles a completed alternate subexpression of a ternary operator. - * @param {{type: <string>}} ast The subexpression tree - */ -exports.ternaryEnd = function(ast) { - this._cursor.alternate = ast; -}; - -/** - * Handles a completed consequent subexpression of a ternary operator. - * @param {{type: <string>}} ast The subexpression tree - */ -exports.ternaryMid = function(ast) { - this._cursor.consequent = ast; -}; - -/** - * Handles the start of a new ternary expression by encapsulating the entire - * AST in a ConditionalExpression node, and using the existing tree as the - * test element. - */ -exports.ternaryStart = function() { - this._tree = { - type: 'ConditionalExpression', - test: this._tree - }; - this._cursor = this._tree; -}; - -/** - * Handles identifier tokens when used to indicate the name of a transform to - * be applied. - * @param {{type: <string>}} token A token object - */ -exports.transform = function(token) { - this._placeBeforeCursor({ - type: 'Transform', - name: token.value, - args: [], - subject: this._cursor - }); -}; - -/** - * Handles token of type 'unaryOp', indicating that the operation has only - * one input: a right side. - * @param {{type: <string>}} token A token object - */ -exports.unaryOp = function(token) { - this._placeAtCursor({ - type: 'UnaryExpression', - operator: token.value - }); -};
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/node_modules/jexl/lib/parser/states.js +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Jexl - * Copyright (c) 2015 TechnologyAdvice - */ - -var h = require('./handlers'); - -/** - * A mapping of all states in the finite state machine to a set of instructions - * for handling or transitioning into other states. Each state can be handled - * in one of two schemes: a tokenType map, or a subHandler. - * - * Standard expression elements are handled through the tokenType object. This - * is an object map of all legal token types to encounter in this state (and - * any unexpected token types will generate a thrown error) to an options - * object that defines how they're handled. The available options are: - * - * {string} toState: The name of the state to which to transition - * immediately after handling this token - * {string} handler: The handler function to call when this token type is - * encountered in this state. If omitted, the default handler - * matching the token's "type" property will be called. If the handler - * function does not exist, no call will be made and no error will be - * generated. This is useful for tokens whose sole purpose is to - * transition to other states. - * - * States that consume a subexpression should define a subHandler, the - * function to be called with an expression tree argument when the - * subexpression is complete. Completeness is determined through the - * endStates object, which maps tokens on which an expression should end to the - * state to which to transition once the subHandler function has been called. - * - * Additionally, any state in which it is legal to mark the AST as completed - * should have a 'completable' property set to boolean true. Attempting to - * call {@link Parser#complete} in any state without this property will result - * in a thrown Error. - * - * @type {{}} - */ -exports.states = { - expectOperand: { - tokenTypes: { - literal: {toState: 'expectBinOp'}, - identifier: {toState: 'identifier'}, - unaryOp: {}, - openParen: {toState: 'subExpression'}, - openCurl: {toState: 'expectObjKey', handler: h.objStart}, - dot: {toState: 'traverse'}, - openBracket: {toState: 'arrayVal', handler: h.arrayStart} - } - }, - expectBinOp: { - tokenTypes: { - binaryOp: {toState: 'expectOperand'}, - pipe: {toState: 'expectTransform'}, - dot: {toState: 'traverse'}, - question: {toState: 'ternaryMid', handler: h.ternaryStart} - }, - completable: true - }, - expectTransform: { - tokenTypes: { - identifier: {toState: 'postTransform', handler: h.transform} - } - }, - expectObjKey: { - tokenTypes: { - identifier: {toState: 'expectKeyValSep', handler: h.objKey}, - closeCurl: {toState: 'expectBinOp'} - } - }, - expectKeyValSep: { - tokenTypes: { - colon: {toState: 'objVal'} - } - }, - postTransform: { - tokenTypes: { - openParen: {toState: 'argVal'}, - binaryOp: {toState: 'expectOperand'}, - dot: {toState: 'traverse'}, - openBracket: {toState: 'filter'}, - pipe: {toState: 'expectTransform'} - }, - completable: true - }, - postTransformArgs: { - tokenTypes: { - binaryOp: {toState: 'expectOperand'}, - dot: {toState: 'traverse'}, - openBracket: {toState: 'filter'}, - pipe: {toState: 'expectTransform'} - }, - completable: true - }, - identifier: { - tokenTypes: { - binaryOp: {toState: 'expectOperand'}, - dot: {toState: 'traverse'}, - openBracket: {toState: 'filter'}, - pipe: {toState: 'expectTransform'}, - question: {toState: 'ternaryMid', handler: h.ternaryStart} - }, - completable: true - }, - traverse: { - tokenTypes: { - 'identifier': {toState: 'identifier'} - } - }, - filter: { - subHandler: h.filter, - endStates: { - closeBracket: 'identifier' - } - }, - subExpression: { - subHandler: h.subExpression, - endStates: { - closeParen: 'expectBinOp' - } - }, - argVal: { - subHandler: h.argVal, - endStates: { - comma: 'argVal', - closeParen: 'postTransformArgs' - } - }, - objVal: { - subHandler: h.objVal, - endStates: { - comma: 'expectObjKey', - closeCurl: 'expectBinOp' - } - }, - arrayVal: { - subHandler: h.arrayVal, - endStates: { - comma: 'arrayVal', - closeBracket: 'expectBinOp' - } - }, - ternaryMid: { - subHandler: h.ternaryMid, - endStates: { - colon: 'ternaryEnd' - } - }, - ternaryEnd: { - subHandler: h.ternaryEnd, - completable: true - } -};
--- a/browser/extensions/shield-recipe-client/test/browser/.eslintrc.js +++ b/browser/extensions/shield-recipe-client/test/browser/.eslintrc.js @@ -3,15 +3,9 @@ module.exports = { extends: [ "plugin:mozilla/browser-test" ], plugins: [ "mozilla" ], - - globals: { - // Bug 1366720 - SimpleTest isn't being exported correctly, so list - // it here for now. - "SimpleTest": false - } };
--- a/browser/extensions/shield-recipe-client/test/browser/browser.ini +++ b/browser/extensions/shield-recipe-client/test/browser/browser.ini @@ -1,14 +1,17 @@ [DEFAULT] +support-files = + action_server.sjs + fixtures/normandy.xpi head = head.js +[browser_ActionSandboxManager.js] [browser_NormandyDriver.js] [browser_FilterExpressions.js] [browser_EventEmitter.js] [browser_Storage.js] +[browser_StudyStorage.js] [browser_Heartbeat.js] [browser_RecipeRunner.js] -support-files = - action_server.sjs [browser_LogManager.js] [browser_ClientEnvironment.js] [browser_ShieldRecipeClient.js] [browser_PreferenceExperiments.js]
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/test/browser/browser_ActionSandboxManager.js @@ -0,0 +1,167 @@ +"use strict"; + +Cu.import("resource://shield-recipe-client/lib/ActionSandboxManager.jsm", this); +Cu.import("resource://shield-recipe-client/lib/NormandyDriver.jsm", this); + +async function withManager(script, testFunction) { + const manager = new ActionSandboxManager(script); + manager.addHold("testing"); + await testFunction(manager); + manager.removeHold("testing"); +} + +add_task(async function testMissingCallbackName() { + await withManager("1 + 1", async manager => { + is( + await manager.runAsyncCallback("missingCallback"), + undefined, + "runAsyncCallback returns undefined when given a missing callback name", + ); + }); +}); + +add_task(async function testCallback() { + const script = ` + registerAsyncCallback("testCallback", async function(normandy) { + return 5; + }); + `; + + await withManager(script, async manager => { + const result = await manager.runAsyncCallback("testCallback"); + is(result, 5, "runAsyncCallback executes the named callback inside the sandbox"); + }); +}); + +add_task(async function testArguments() { + const script = ` + registerAsyncCallback("testCallback", async function(normandy, a, b) { + return a + b; + }); + `; + + await withManager(script, async manager => { + const result = await manager.runAsyncCallback("testCallback", 4, 6); + is(result, 10, "runAsyncCallback passes arguments to the callback"); + }); +}); + +add_task(async function testCloning() { + const script = ` + registerAsyncCallback("testCallback", async function(normandy, obj) { + return {foo: "bar", baz: obj.baz}; + }); + `; + + await withManager(script, async manager => { + const result = await manager.runAsyncCallback("testCallback", {baz: "biff"}); + + Assert.deepEqual( + result, + {foo: "bar", baz: "biff"}, + ( + "runAsyncCallback clones arguments into the sandbox and return values into the " + + "context it was called from" + ), + ); + }); +}); + +add_task(async function testError() { + const script = ` + registerAsyncCallback("testCallback", async function(normandy) { + throw new Error("WHY") + }); + `; + + await withManager(script, async manager => { + try { + await manager.runAsyncCallback("testCallback"); + ok(false, "runAsnycCallbackFromScript throws errors when raised by the sandbox"); + } catch (err) { + is(err.message, "WHY", "runAsnycCallbackFromScript clones error messages"); + } + }); +}); + +add_task(async function testDriver() { + // The value returned by runAsyncCallback is cloned without the cloneFunctions + // option, so we can't inspect the driver itself since its methods will not be + // present. Instead, we inspect the properties on it available to the sandbox. + const script = ` + registerAsyncCallback("testCallback", async function(normandy) { + return Object.keys(normandy); + }); + `; + + await withManager(script, async manager => { + const sandboxDriverKeys = await manager.runAsyncCallback("testCallback"); + const referenceDriver = new NormandyDriver(manager); + for (const prop of Object.keys(referenceDriver)) { + ok(sandboxDriverKeys.includes(prop), `runAsyncCallback's driver has the "${prop}" property.`); + } + }); +}); + +add_task(async function testGlobalObject() { + // Test that window is an alias for the global object, and that it + // has some expected functions available on it. + const script = ` + window.setOnWindow = "set"; + this.setOnGlobal = "set"; + + registerAsyncCallback("testCallback", async function(normandy) { + return { + setOnWindow: setOnWindow, + setOnGlobal: window.setOnGlobal, + setTimeoutExists: setTimeout !== undefined, + clearTimeoutExists: clearTimeout !== undefined, + }; + }); + `; + + await withManager(script, async manager => { + const result = await manager.runAsyncCallback("testCallback"); + Assert.deepEqual(result, { + setOnWindow: "set", + setOnGlobal: "set", + setTimeoutExists: true, + clearTimeoutExists: true, + }, "sandbox.window is the global object and has expected functions."); + }); +}); + +add_task(async function testRegisterActionShim() { + const recipe = { + foo: "bar", + }; + const script = ` + class TestAction { + constructor(driver, recipe) { + this.driver = driver; + this.recipe = recipe; + } + + execute() { + return new Promise(resolve => { + resolve({ + foo: this.recipe.foo, + isDriver: "log" in this.driver, + }); + }); + } + } + + registerAction('test-action', TestAction); + `; + + await withManager(script, async manager => { + const result = await manager.runAsyncCallback("action", recipe); + is(result.foo, "bar", "registerAction registers an async callback for actions"); + is( + result.isDriver, + true, + "registerAction passes the driver to the action class constructor", + ); + }); +});
--- a/browser/extensions/shield-recipe-client/test/browser/browser_ClientEnvironment.js +++ b/browser/extensions/shield-recipe-client/test/browser/browser_ClientEnvironment.js @@ -1,13 +1,15 @@ "use strict"; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/TelemetryController.jsm", this); +Cu.import("resource://gre/modules/AddonManager.jsm", this); +Cu.import("resource://testing-common/AddonTestUtils.jsm", this); Cu.import("resource://shield-recipe-client/lib/ClientEnvironment.jsm", this); Cu.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm", this); add_task(async function testTelemetry() { // setup await TelemetryController.submitExternalPing("testfoo", {foo: 1}); await TelemetryController.submitExternalPing("testbar", {bar: 2}); @@ -107,8 +109,41 @@ add_task(async function testExperiments( Assert.deepEqual( experiments.expired, ["expired"], "experiments.expired returns all expired experiment names", ); getAll.restore(); }); + +add_task(withDriver(Assert, async function testAddonsInContext(driver) { + // Create before install so that the listener is added before startup completes. + const startupPromise = AddonTestUtils.promiseWebExtensionStartup("normandydriver@example.com"); + const addonId = await driver.addons.install(TEST_XPI_URL); + await startupPromise; + + const environment = ClientEnvironment.getEnvironment(); + const addons = await environment.addons; + Assert.deepEqual(addons[addonId], { + id: [addonId], + name: "normandy_fixture", + version: "1.0", + installDate: addons[addonId].installDate, + isActive: true, + type: "extension", + }, "addons should be available in context"); + + await driver.addons.uninstall(addonId); +})); + +add_task(async function isFirstRun() { + let environment = ClientEnvironment.getEnvironment(); + + // isFirstRun is set to false after the recipe client runs + ok(!environment.isFirstRun, "isFirstRun has a default value"); + + // isFirstRun is read from a preference + await SpecialPowers.pushPrefEnv({set: [["extensions.shield-recipe-client.first_run", true]]}); + environment = ClientEnvironment.getEnvironment(); + ok(environment.isFirstRun, "isFirstRun is read from preferences"); +}); +
--- a/browser/extensions/shield-recipe-client/test/browser/browser_FilterExpressions.js +++ b/browser/extensions/shield-recipe-client/test/browser/browser_FilterExpressions.js @@ -86,8 +86,103 @@ add_task(async function() { ok(val, "preferenceIsUserSet expression determines if user's preference has been set"); // Compare if the preference has _any_ value, whether it's user-set or default, val = await FilterExpressions.eval('"normandy.test.nonexistant"|preferenceExists == true'); ok(!val, "preferenceExists expression determines if preference exists at all"); val = await FilterExpressions.eval('"normandy.test.value"|preferenceExists == true'); ok(val, "preferenceExists expression fails existence check appropriately"); }); + +// keys tests +add_task(async function testKeys() { + let val; + + // Test an object defined in JEXL + val = await FilterExpressions.eval("{foo: 1, bar: 2}|keys"); + Assert.deepEqual( + new Set(val), + new Set(["foo", "bar"]), + "keys returns the keys from an object in JEXL", + ); + + // Test an object in the context + let context = {ctxObject: {baz: "string", biff: NaN}}; + val = await FilterExpressions.eval("ctxObject|keys", context); + + Assert.deepEqual( + new Set(val), + new Set(["baz", "biff"]), + "keys returns the keys from an object in the context", + ); + + // Test that values from the prototype are not included + context = {ctxObject: Object.create({fooProto: 7})}; + context.ctxObject.baz = 8; + context.ctxObject.biff = 5; + is( + await FilterExpressions.eval("ctxObject.fooProto", context), + 7, + "Prototype properties are accessible via property access", + ); + val = await FilterExpressions.eval("ctxObject|keys", context); + Assert.deepEqual( + new Set(val), + new Set(["baz", "biff"]), + "keys does not return properties from the object's prototype chain", + ); + + // Return undefined for non-objects + is( + await FilterExpressions.eval("ctxObject|keys", {ctxObject: 45}), + undefined, + "keys returns undefined for numbers", + ); + is( + await FilterExpressions.eval("ctxObject|keys", {ctxObject: null}), + undefined, + "keys returns undefined for null", + ); +}); + +// intersect tests +add_task(async function testIntersect() { + let val; + + val = await FilterExpressions.eval("[1, 2, 3] intersect [4, 2, 6, 7, 3]"); + Assert.deepEqual( + new Set(val), + new Set([2, 3]), + "intersect finds the common elements between two lists in JEXL", + ); + + const context = {left: [5, 7], right: [4, 5, 3]}; + val = await FilterExpressions.eval("left intersect right", context); + Assert.deepEqual( + new Set(val), + new Set([5]), + "intersect finds the common elements between two lists in the context", + ); + + val = await FilterExpressions.eval("['string', 2] intersect [4, 'string', 'other', 3]"); + Assert.deepEqual( + new Set(val), + new Set(["string"]), + "intersect can compare strings", + ); + + // Return undefined when intersecting things that aren't lists. + is( + await FilterExpressions.eval("5 intersect 7"), + undefined, + "intersect returns undefined for numbers", + ); + is( + await FilterExpressions.eval("val intersect other", {val: null, other: null}), + undefined, + "intersect returns undefined for null", + ); + is( + await FilterExpressions.eval("5 intersect [1, 2, 5]"), + undefined, + "intersect returns undefined if only one operand is a list", + ); +});
--- a/browser/extensions/shield-recipe-client/test/browser/browser_NormandyDriver.js +++ b/browser/extensions/shield-recipe-client/test/browser/browser_NormandyDriver.js @@ -1,22 +1,61 @@ "use strict"; +Cu.import("resource://testing-common/AddonTestUtils.jsm", this); Cu.import("resource://shield-recipe-client/lib/NormandyDriver.jsm", this); +Cu.import("resource://shield-recipe-client/lib/StudyStorage.jsm", this); add_task(withDriver(Assert, async function uuids(driver) { // Test that it is a UUID const uuid1 = driver.uuid(); ok(UUID_REGEX.test(uuid1), "valid uuid format"); // Test that UUIDs are different each time const uuid2 = driver.uuid(); isnot(uuid1, uuid2, "uuids are unique"); })); +add_task(withDriver(Assert, async function installXpi(driver) { + // Test that we can install an XPI from any URL + // Create before install so that the listener is added before startup completes. + const startupPromise = AddonTestUtils.promiseWebExtensionStartup("normandydriver@example.com"); + + var addonId = await driver.addons.install(TEST_XPI_URL); + is(addonId, "normandydriver@example.com", "Expected test addon was installed"); + isnot(addonId, null, "Addon install was successful"); + + // Wait until the add-on is fully started up to uninstall it. + await startupPromise; + + const uninstallMsg = await driver.addons.uninstall(addonId); + is(uninstallMsg, null, `Uninstall returned an unexpected message [${uninstallMsg}]`); +})); + +add_task(withDriver(Assert, async function uninstallInvalidAddonId(driver) { + const invalidAddonId = "not_a_valid_xpi_id@foo.bar"; + try { + await driver.addons.uninstall(invalidAddonId); + ok(false, `Uninstalling an invalid XPI should fail. addons.uninstall resolved successfully though.`); + } catch (e) { + ok(true, `This is the expected failure`); + } +})); + + +add_task(withDriver(Assert, async function installXpiBadURL(driver) { + const xpiUrl = "file:///tmp/invalid_xpi.xpi"; + try { + await driver.addons.install(xpiUrl); + ok(false, "Installation succeeded on an XPI that doesn't exist"); + } catch (reason) { + ok(true, `Installation was rejected: [${reason}]`); + } +})); + add_task(withDriver(Assert, async function userId(driver) { // Test that userId is a UUID ok(UUID_REGEX.test(driver.userId), "userId is a uuid"); })); add_task(withDriver(Assert, async function syncDeviceCounts(driver) { let client = await driver.client(); is(client.syncMobileDevices, 0, "syncMobileDevices defaults to zero"); @@ -75,8 +114,98 @@ add_task(withSandboxManager(Assert, asyn await store.removeItem("willremove"); is(await store.getItem("willremove"), null, "createStorage removes items"); is('prefix' in store, false, "createStorage doesn't expose non-whitelist attributes"); })(); `); })); + +add_task(withDriver(Assert, async function getAddon(driver, sandboxManager) { + const ADDON_ID = "normandydriver@example.com"; + let addon = await driver.addons.get(ADDON_ID); + Assert.equal(addon, null, "Add-on is not yet installed"); + + await driver.addons.install(TEST_XPI_URL); + addon = await driver.addons.get(ADDON_ID); + + Assert.notEqual(addon, null, "Add-on object was returned"); + ok(addon.installDate instanceof sandboxManager.sandbox.Date, "installDate should be a Date object"); + + Assert.deepEqual(addon, { + id: "normandydriver@example.com", + name: "normandy_fixture", + version: "1.0", + installDate: addon.installDate, + isActive: true, + type: "extension", + }, "Add-on is installed"); + + await driver.addons.uninstall(ADDON_ID); + addon = await driver.addons.get(ADDON_ID); + + Assert.equal(addon, null, "Add-on has been uninstalled"); +})); + +add_task(withSandboxManager(Assert, async function testAddonsGetWorksInSandbox(sandboxManager) { + const driver = new NormandyDriver(sandboxManager); + sandboxManager.cloneIntoGlobal("driver", driver, {cloneFunctions: true}); + + // Assertion helpers + sandboxManager.addGlobal("is", is); + sandboxManager.addGlobal("deepEqual", (...args) => Assert.deepEqual(...args)); + + const ADDON_ID = "normandydriver@example.com"; + + await driver.addons.install(TEST_XPI_URL); + + await sandboxManager.evalInSandbox(` + (async function sandboxTest() { + const addon = await driver.addons.get("${ADDON_ID}"); + + deepEqual(addon, { + id: "${ADDON_ID}", + name: "normandy_fixture", + version: "1.0", + installDate: addon.installDate, + isActive: true, + type: "extension", + }, "Add-on is accesible in the driver"); + })(); + `); + + await driver.addons.uninstall(ADDON_ID); +})); + +add_task(withSandboxManager(Assert, async function testStudyStorage(sandboxManager) { + const driver = new NormandyDriver(sandboxManager); + sandboxManager.cloneIntoGlobal("driver", driver, {cloneFunctions: true}); + + // Assertion helpers + sandboxManager.addGlobal("is", is); + sandboxManager.addGlobal("ok", ok); + + await sandboxManager.evalInSandbox(` + (async function sandboxTest() { + await driver.studies.create({ + name: "test-study", + addonId: "test@example.com", + addonVersion: "1.0", + description: "fake", + studyStartDate: new Date().toJSON(), + }); + + const hasStudy = await driver.studies.has("test-study"); + ok(hasStudy, "studies.create creates studies from within a sandbox."); + + let study = await driver.studies.get("test-study"); + is(study.addonVersion, "1.0", "studies.get fetches studies from within a sandbox."); + + await driver.studies.update("test-study", {addonVersion: "2.0"}); + study = await driver.studies.get("test-study"); + is(study.addonVersion, "2.0", "studies.update can update stored studies."); + })(); + `); + + await StudyStorage.clear(); + await StudyStorage.close(); +}));
--- a/browser/extensions/shield-recipe-client/test/browser/browser_PreferenceExperiments.js +++ b/browser/extensions/shield-recipe-client/test/browser/browser_PreferenceExperiments.js @@ -362,38 +362,41 @@ add_task(withMockExperiments(withMockPre stopObserver.restore(); PreferenceExperiments.stopAllObservers(); }))); // stop should also support user pref experiments add_task(withMockExperiments(withMockPreferences(async function(experiments, mockPreferences) { const stopObserver = sinon.stub(PreferenceExperiments, "stopObserver"); + const hasObserver = sinon.stub(PreferenceExperiments, "hasObserver"); + hasObserver.returns(true); + mockPreferences.set("fake.preference", "experimentvalue", "user"); experiments["test"] = experimentFactory({ name: "test", expired: false, preferenceName: "fake.preference", preferenceValue: "experimentvalue", preferenceType: "string", previousPreferenceValue: "oldvalue", preferenceBranchType: "user", }); - PreferenceExperiments.startObserver("test", "fake.preference", "experimentvalue"); await PreferenceExperiments.stop("test"); ok(stopObserver.calledWith("test"), "stop removed an observer"); is(experiments["test"].expired, true, "stop marked the experiment as expired"); is( Preferences.get("fake.preference"), "oldvalue", "stop reverted the preference to its previous value", ); stopObserver.restore(); + hasObserver.restore(); }))); // stop should not call stopObserver if there is no observer registered. add_task(withMockExperiments(withMockPreferences(async function(experiments) { const stopObserver = sinon.spy(PreferenceExperiments, "stopObserver"); experiments["test"] = experimentFactory({name: "test", expired: false}); await PreferenceExperiments.stop("test");
--- a/browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js +++ b/browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js @@ -1,16 +1,18 @@ "use strict"; Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://shield-recipe-client/lib/RecipeRunner.jsm", this); Cu.import("resource://shield-recipe-client/lib/ClientEnvironment.jsm", this); Cu.import("resource://shield-recipe-client/lib/CleanupManager.jsm", this); Cu.import("resource://shield-recipe-client/lib/NormandyApi.jsm", this); Cu.import("resource://shield-recipe-client/lib/ActionSandboxManager.jsm", this); +Cu.import("resource://shield-recipe-client/lib/StudyStorage.jsm", this); +Cu.import("resource://shield-recipe-client/lib/Uptake.jsm", this); add_task(async function getFilterContext() { const recipe = {id: 17, arguments: {foo: "bar"}, unrelated: false}; const context = RecipeRunner.getFilterContext(recipe); // Test for expected properties in the filter expression context. const expectedNormandyKeys = [ "channel", @@ -65,17 +67,17 @@ add_task(async function checkFilter() { }); add_task(withMockNormandyApi(async function testClientClassificationCache() { const getStub = sinon.stub(ClientEnvironment, "getClientClassification") .returns(Promise.resolve(false)); await SpecialPowers.pushPrefEnv({set: [ ["extensions.shield-recipe-client.api_url", - "https://example.com/selfsupport-dummy"], + "https://example.com/selfsupport-dummy"], ]}); // When the experiment pref is false, eagerly call getClientClassification. await SpecialPowers.pushPrefEnv({set: [ ["extensions.shield-recipe-client.experiments.lazy_classify", false], ]}); ok(!getStub.called, "getClientClassification hasn't been called"); await RecipeRunner.run(); @@ -94,37 +96,46 @@ add_task(withMockNormandyApi(async funct })); /** * Mocks RecipeRunner.loadActionSandboxManagers for testing run. */ async function withMockActionSandboxManagers(actions, testFunction) { const managers = {}; for (const action of actions) { - managers[action.name] = new ActionSandboxManager(""); + const manager = new ActionSandboxManager(""); + manager.addHold("testing"); + managers[action.name] = manager; sinon.stub(managers[action.name], "runAsyncCallback"); } - const loadActionSandboxManagers = sinon.stub( - RecipeRunner, - "loadActionSandboxManagers", - async () => managers, - ); + const loadActionSandboxManagers = sinon.stub(RecipeRunner, "loadActionSandboxManagers") + .resolves(managers); await testFunction(managers); loadActionSandboxManagers.restore(); + + for (const manager of Object.values(managers)) { + manager.removeHold("testing"); + await manager.isNuked(); + } } add_task(withMockNormandyApi(async function testRun(mockApi) { + const closeSpy = sinon.spy(StudyStorage, "close"); + const reportRunner = sinon.stub(Uptake, "reportRunner"); + const reportAction = sinon.stub(Uptake, "reportAction"); + const reportRecipe = sinon.stub(Uptake, "reportRecipe"); + const matchAction = {name: "matchAction"}; const noMatchAction = {name: "noMatchAction"}; mockApi.actions = [matchAction, noMatchAction]; - const matchRecipe = {action: "matchAction", filter_expression: "true"}; - const noMatchRecipe = {action: "noMatchAction", filter_expression: "false"}; - const missingRecipe = {action: "missingAction", filter_expression: "true"}; + const matchRecipe = {id: "match", action: "matchAction", filter_expression: "true"}; + const noMatchRecipe = {id: "noMatch", action: "noMatchAction", filter_expression: "false"}; + const missingRecipe = {id: "missing", action: "missingAction", filter_expression: "true"}; mockApi.recipes = [matchRecipe, noMatchRecipe, missingRecipe]; await withMockActionSandboxManagers(mockApi.actions, async managers => { const matchManager = managers["matchAction"]; const noMatchManager = managers["noMatchAction"]; await RecipeRunner.run(); @@ -135,28 +146,109 @@ add_task(withMockNormandyApi(async funct // noMatch should be called for preExecution and postExecution, and skipped // for action since the filter expression does not match. sinon.assert.calledWith(noMatchManager.runAsyncCallback, "preExecution"); sinon.assert.neverCalledWith(noMatchManager.runAsyncCallback, "action", noMatchRecipe); sinon.assert.calledWith(noMatchManager.runAsyncCallback, "postExecution"); // missing is never called at all due to no matching action/manager. - await matchManager.isNuked(); - await noMatchManager.isNuked(); + + // Test uptake reporting + sinon.assert.calledWith(reportRunner, Uptake.RUNNER_SUCCESS); + sinon.assert.calledWith(reportAction, "matchAction", Uptake.ACTION_SUCCESS); + sinon.assert.calledWith(reportAction, "noMatchAction", Uptake.ACTION_SUCCESS); + sinon.assert.calledWith(reportRecipe, "match", Uptake.RECIPE_SUCCESS); + sinon.assert.neverCalledWith(reportRecipe, "noMatch", Uptake.RECIPE_SUCCESS); + sinon.assert.calledWith(reportRecipe, "missing", Uptake.RECIPE_INVALID_ACTION); }); + + // Ensure storage is closed after the run. + sinon.assert.calledOnce(closeSpy); + + closeSpy.restore(); + reportRunner.restore(); + reportAction.restore(); + reportRecipe.restore(); +})); + +add_task(withMockNormandyApi(async function testRunRecipeError(mockApi) { + const reportRecipe = sinon.stub(Uptake, "reportRecipe"); + + const action = {name: "action"}; + mockApi.actions = [action]; + + const recipe = {id: "recipe", action: "action", filter_expression: "true"}; + mockApi.recipes = [recipe]; + + await withMockActionSandboxManagers(mockApi.actions, async managers => { + const manager = managers["action"]; + manager.runAsyncCallback.callsFake(async callbackName => { + if (callbackName === "action") { + throw new Error("Action execution failure"); + } + }); + + await RecipeRunner.run(); + + // Uptake should report that the recipe threw an exception + sinon.assert.calledWith(reportRecipe, "recipe", Uptake.RECIPE_EXECUTION_ERROR); + }); + + reportRecipe.restore(); +})); + +add_task(withMockNormandyApi(async function testRunFetchFail(mockApi) { + const closeSpy = sinon.spy(StudyStorage, "close"); + const reportRunner = sinon.stub(Uptake, "reportRunner"); + + const action = {name: "action"}; + mockApi.actions = [action]; + mockApi.fetchRecipes.rejects(new Error("Signature not valid")); + + await withMockActionSandboxManagers(mockApi.actions, async managers => { + const manager = managers["action"]; + await RecipeRunner.run(); + + // If the recipe fetch failed, do not run anything. + sinon.assert.notCalled(manager.runAsyncCallback); + sinon.assert.calledWith(reportRunner, Uptake.RUNNER_SERVER_ERROR); + + // Test that network errors report a specific uptake error + reportRunner.reset(); + mockApi.fetchRecipes.rejects(new Error("NetworkError: The system was down")); + await RecipeRunner.run(); + sinon.assert.calledWith(reportRunner, Uptake.RUNNER_NETWORK_ERROR); + + // Test that signature issues report a specific uptake error + reportRunner.reset(); + mockApi.fetchRecipes.rejects(new NormandyApi.InvalidSignatureError("Signature fail")); + await RecipeRunner.run(); + sinon.assert.calledWith(reportRunner, Uptake.RUNNER_INVALID_SIGNATURE); + }); + + // If the recipe fetch failed, we don't need to call close since nothing + // opened a connection in the first place. + sinon.assert.notCalled(closeSpy); + + closeSpy.restore(); + reportRunner.restore(); })); add_task(withMockNormandyApi(async function testRunPreExecutionFailure(mockApi) { + const closeSpy = sinon.spy(StudyStorage, "close"); + const reportAction = sinon.stub(Uptake, "reportAction"); + const reportRecipe = sinon.stub(Uptake, "reportRecipe"); + const passAction = {name: "passAction"}; const failAction = {name: "failAction"}; mockApi.actions = [passAction, failAction]; - const passRecipe = {action: "passAction", filter_expression: "true"}; - const failRecipe = {action: "failAction", filter_expression: "true"}; + const passRecipe = {id: "pass", action: "passAction", filter_expression: "true"}; + const failRecipe = {id: "fail", action: "failAction", filter_expression: "true"}; mockApi.recipes = [passRecipe, failRecipe]; await withMockActionSandboxManagers(mockApi.actions, async managers => { const passManager = managers["passAction"]; const failManager = managers["failAction"]; failManager.runAsyncCallback.returns(Promise.reject(new Error("oh no"))); await RecipeRunner.run(); @@ -166,19 +258,57 @@ add_task(withMockNormandyApi(async funct sinon.assert.calledWith(passManager.runAsyncCallback, "action", passRecipe); sinon.assert.calledWith(passManager.runAsyncCallback, "postExecution"); // fail should only be called for preExecution, since it fails during that sinon.assert.calledWith(failManager.runAsyncCallback, "preExecution"); sinon.assert.neverCalledWith(failManager.runAsyncCallback, "action", failRecipe); sinon.assert.neverCalledWith(failManager.runAsyncCallback, "postExecution"); - await passManager.isNuked(); - await failManager.isNuked(); + sinon.assert.calledWith(reportAction, "passAction", Uptake.ACTION_SUCCESS); + sinon.assert.calledWith(reportAction, "failAction", Uptake.ACTION_PRE_EXECUTION_ERROR); + sinon.assert.calledWith(reportRecipe, "fail", Uptake.RECIPE_ACTION_DISABLED); }); + + // Ensure storage is closed after the run, despite the failures. + sinon.assert.calledOnce(closeSpy); + closeSpy.restore(); + reportAction.restore(); + reportRecipe.restore(); +})); + +add_task(withMockNormandyApi(async function testRunPostExecutionFailure(mockApi) { + const reportAction = sinon.stub(Uptake, "reportAction"); + + const failAction = {name: "failAction"}; + mockApi.actions = [failAction]; + + const failRecipe = {action: "failAction", filter_expression: "true"}; + mockApi.recipes = [failRecipe]; + + await withMockActionSandboxManagers(mockApi.actions, async managers => { + const failManager = managers["failAction"]; + failManager.runAsyncCallback.callsFake(async callbackName => { + if (callbackName === "postExecution") { + throw new Error("postExecution failure"); + } + }); + + await RecipeRunner.run(); + + // fail should be called for every stage + sinon.assert.calledWith(failManager.runAsyncCallback, "preExecution"); + sinon.assert.calledWith(failManager.runAsyncCallback, "action", failRecipe); + sinon.assert.calledWith(failManager.runAsyncCallback, "postExecution"); + + // Uptake should report a post-execution error + sinon.assert.calledWith(reportAction, "failAction", Uptake.ACTION_POST_EXECUTION_ERROR); + }); + + reportAction.restore(); })); add_task(withMockNormandyApi(async function testLoadActionSandboxManagers(mockApi) { mockApi.actions = [ {name: "normalAction"}, {name: "missingImpl"}, ]; mockApi.implementations["normalAction"] = "window.scriptRan = true"; @@ -195,28 +325,40 @@ add_task(withMockNormandyApi(async funct })); add_task(async function testStartup() { const runStub = sinon.stub(RecipeRunner, "run"); const addCleanupHandlerStub = sinon.stub(CleanupManager, "addCleanupHandler"); const updateRunIntervalStub = sinon.stub(RecipeRunner, "updateRunInterval"); // in dev mode - await SpecialPowers.pushPrefEnv({set: [["extensions.shield-recipe-client.dev_mode", true]]}); + await SpecialPowers.pushPrefEnv({ + set: [ + ["extensions.shield-recipe-client.dev_mode", true], + ["extensions.shield-recipe-client.first_run", false], + ], + }); + RecipeRunner.init(); ok(runStub.called, "RecipeRunner.run is called immediately when in dev mode"); ok(addCleanupHandlerStub.called, "A cleanup function is registered when in dev mode"); ok(updateRunIntervalStub.called, "A timer is registered when in dev mode"); runStub.reset(); addCleanupHandlerStub.reset(); updateRunIntervalStub.reset(); // not in dev mode - await SpecialPowers.pushPrefEnv({set: [["extensions.shield-recipe-client.dev_mode", false]]}); + await SpecialPowers.pushPrefEnv({ + set: [ + ["extensions.shield-recipe-client.dev_mode", false], + ["extensions.shield-recipe-client.first_run", false], + ], + }); + RecipeRunner.init(); ok(!runStub.called, "RecipeRunner.run is not called immediately when not in dev mode"); ok(addCleanupHandlerStub.called, "A cleanup function is registered when not in dev mode"); ok(updateRunIntervalStub.called, "A timer is registered when not in dev mode"); runStub.restore(); addCleanupHandlerStub.restore(); updateRunIntervalStub.restore();
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/test/browser/browser_StudyStorage.js @@ -0,0 +1,192 @@ +"use strict"; + +Cu.import("resource://gre/modules/IndexedDB.jsm", this); +Cu.import("resource://shield-recipe-client/lib/StudyStorage.jsm", this); + +const REQUIRED_FIELDS = ["name", "addonId", "addonVersion", "description", "studyStartDate"]; + +function withStudyStorage(testFn) { + return async () => { + try { + await testFn(StudyStorage); + } finally { + await StudyStorage.clear(); + await StudyStorage.close(); + } + }; +} + +function studyFactory(attrs) { + return Object.assign({ + name: "Test study", + addonId: "foo@example.com", + addonVersion: "2.0.0", + description: "fake", + studyStartDate: new Date().toJSON(), + }, attrs); +} + +add_task(withStudyStorage(async function testGetMissing(storage) { + await Assert.rejects( + storage.get("does-not-exist"), + /Could not find/, + "get rejects when the requested study is not stored", + ); +})); + +add_task(withStudyStorage(async function testCreateGet(storage) { + const study = studyFactory({name: "test-study"}); + await storage.create(study); + + const storedStudy = await storage.get("test-study"); + Assert.deepEqual(study, storedStudy, "Create saved a new study to the storage."); + is(storedStudy.studyEndDate, null, "Create defaults the study end date to null."); + ok(storedStudy.active, "Create defaults the study to active."); +})); + +add_task(withStudyStorage(async function testCreateExists(storage) { + const study = studyFactory({name: "test-study"}); + await storage.create(study); + + const dupeStudy = studyFactory({name: "test-study"}); + Assert.rejects( + storage.create(dupeStudy), + /test-study/, + "create throws when a study exists with the given name already", + ); +})); + +add_task(withStudyStorage(async function testCreateSchema(storage) { + for (const requiredField of REQUIRED_FIELDS) { + let study = studyFactory({ [requiredField]: undefined }); + let msg = `create threw an error due to missing required field ${requiredField}`; + await Assert.rejects(storage.create(study), new RegExp(requiredField), msg); + + // Blank values are not allowed for required fields either + study = studyFactory({ [requiredField]: "" }); + msg = `create threw an error due to blank required field ${requiredField}`; + await Assert.rejects(storage.create(study), new RegExp(requiredField), msg); + } + + let study = studyFactory({studyStartDate: "invalid-datetime"}); + await Assert.rejects( + storage.create(study), + /studyStartDate/, + "create rejected due to an invalid start date", + ); + + study = studyFactory({studyEndDate: "invalid-datetime"}); + await Assert.rejects( + storage.create(study), + /studyEndDate/, + "create rejected due to an invalid end date", + ); + + study = studyFactory({active: "not a boolean"}); + await Assert.rejects( + storage.create(study), + /active/, + "create rejected due to an invalid active status", + ); +})); + +add_task(withStudyStorage(async function testCreateHas(storage) { + let hasStudy = await storage.has("test-study"); + ok(!hasStudy, "has returns false before the study has been created."); + + const study = studyFactory({name: "test-study"}); + await storage.create(study); + + hasStudy = await storage.has("test-study"); + ok(hasStudy, "has returns true after the study has been created"); +})); + +add_task(withStudyStorage(async function testCreateUpdate(storage) { + const study = studyFactory({name: "test-study", addonVersion: "1.0", addonId: "foo@example.com"}); + await storage.create(study); + await storage.update("test-study", {addonVersion: "2.0"}); + + const storedStudy = await storage.get("test-study"); + is(storedStudy.addonVersion, "2.0", "update modified the stored study."); + is(storedStudy.addonId, "foo@example.com", "update did not modify unspecified fields"); +})); + +add_task(withStudyStorage(async function testUpdateSchema(storage) { + const study = studyFactory({name: "test-study"}); + await storage.create(study); + + // Required fields must not be blank if they're specified + for (const requiredField of REQUIRED_FIELDS) { + const msg = `update threw an error due to blank required field ${requiredField}`; + await Assert.rejects( + storage.update("test-study", { [requiredField]: "" }), + new RegExp(requiredField), + msg, + ); + } + + await Assert.rejects( + storage.update("test-study", {studyStartDate: "invalid-datetime"}), + /studyStartDate/, + "update rejected due to an invalid start date", + ); + + await Assert.rejects( + storage.update("test-study", {studyEndDate: "invalid-datetime"}), + /studyEndDate/, + "update rejected due to an invalid end date", + ); + + await Assert.rejects( + storage.update("test-study", {active: "not a boolean"}), + /active/, + "update rejected due to an invalid active status", + ); +})); + +add_task(withStudyStorage(async function testUpdateMissing(storage) { + await Assert.rejects( + storage.update("does-not-exist", {addonVersion: "2.0"}), + /could not find/, + "update rejects when the requested study is not stored", + ); +})); + +add_task(withStudyStorage(async function testUpdateMissing(storage) { + await Assert.rejects( + storage.update("does-not-exist", {addonVersion: "2.0"}), + /could not find/, + "update rejects when the requested study is not stored", + ); +})); + +add_task(withStudyStorage(async function testCloseDatabase(storage) { + const openSpy = sinon.spy(IndexedDB, "open"); + sinon.assert.notCalled(openSpy); + + // Using storage at all should open the database, but only once. + await storage.has("foo"); + await storage.create(studyFactory({name: "test-study"})); + sinon.assert.calledOnce(openSpy); + + // close can be called multiple times + await storage.close(); + await storage.close(); + + // After being closed, new operations cause the database to be opened again + await storage.has("test-study"); + sinon.assert.calledTwice(openSpy); + + openSpy.restore(); +})); + +add_task(withStudyStorage(async function testClear(storage) { + await storage.create(studyFactory({name: "test-study1"})); + await storage.create(studyFactory({name: "test-study2"})); + const hasAll = (await storage.has("test-study1")) && (await storage.has("test-study2")); + ok(hasAll, "Before calling clear, both studies are in the storage."); + + await storage.clear(); + const hasAny = (await storage.has("test-study1")) || (await storage.has("test-study2")); + ok(!hasAny, "After calling clear, all studies are removed from storage."); +}));
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/test/browser/fixtures/addon-fixture/manifest.json @@ -0,0 +1,11 @@ +{ + "manifest_version": 2, + "name": "normandy_fixture", + "version": "1.0", + "description": "Dummy test fixture that's a webextension", + "applications": { + "gecko": { + "id": "normandydriver@example.com" + } + } +}
new file mode 100644 index 0000000000000000000000000000000000000000..71a6f8fe7cd90427ca710599e7125f986e8dd24d GIT binary patch literal 4230 zc$|$_Wl$7s*Ir-=rC})%M7qI6knWBpBwguNmXL-eq@`85q>)xax_c>!U0guAS)@x) zKKy3h@15tF=b8D=J?F=D=00bxb6@wLOGg6-2m$~A1OVgwE_K=~oAsYW0Duu00Pv@( zrKB&%rv_0LfVucNIyqVLzjC+yZsaSPw0}<|=5w4UhNvgEH_b&*6)ca!aA_2EeY3(t z*CeZ1FLJR#&lDZxbkA7P2nvKAvwjLDSs{C|8nT&iJkvDhnSIlK<)3oy`(ZcW$7VAY zcO-7C;{&aPFa39|0Gm2ub+;8fT448QfTo}yod|((Vh)ML<vI|N*9`@52e8P7C#}hL zG6~Y+iOYeJT{sm%{;!R_Lb{&?vA#Bzw<P#fgbSf1o5vj?2qS0viu|4(HJR_@uh;rU zx(Wm)A++$S<RvKWsECZ!)G1y7shCHa;wOcd3!#%Su{BJ-S54xHuA+bzdVn0}^m^`& z`&kKP|7%Va50ApcafI*n2)oLmS3ei_QBdk2a3xvtN(gB})OTDlqJ8t>@LrITn2scX zT-q=ToC{o9!<rAorJ|QM^z{W)AJ72hT?vAZ?e|7Hc7DyY9AnS71Y>o*P&{P2A~PNO zXkEn`bpHx66?g*l+8Q_vQhLPP`7F}aK^8MW)r30)av^0#A7&Lals_1PhJs{I4m>!1 zaVFfQzG*vV@)IYZ8i6z%f1uOE4c$^Nr?!Q)Mm<#tZ9LoaZ7!5}L$VI%cX%(;&?jfe zhbD=0#zvHEIN8Se_&BA4IpiC7*{Z?jL-*fL+nA~vqbp#vwF?<SwPmY>yFQN}ZmB<i zS*fXLN^iU><+m}#TrfuNLwmd%oFKOlpp=s#!a2rvJhC6j5+X2x6sw6!e&Eu;y+S$t zM9ukwD-}zcclt*mL5uWNNoH>K9=3ER=BZ*$E;Zg4TyOG<l+J&~S~FxHnZVjx@Ld;& zI4P;K$zykagg3&K{6;r<*}!kqV~PC+rFpi*pD^Pyb`C4?#k2EE&-t&mY)Q0?#T_Af z1EIs@ouO`nn_OtA+&9<0+IdecXs(_sv@a5x$OO?Bb%)97nZ_Wce<z*z&+$B+qs$aw z_9zw^eNU^`yD#jsFrxT+fZFP*&uGAURM(w*2%*xFymLf-h{3d&N__;KzJO2=N*W7k zYkeXYe~D^BQqpS2DH>3HV|s#?aViTA2+kPi@SDr*7NILps3m>?Z+MJBUb!yKO|3n3 zBrWKKQuJQI>7g{JN$E^Q8xfHy0Wa8oB|Pz^yt+fKqEn$Dl&}8Tmhd#+8c6w-C(a&Q z%{Jh%C^^Bx{kH}R2|~89T4%$f{JeAJ+6Lf}orrLWpwp6>CtM8tI}8PMU)C!{qDiss z1e9sP7pHsv%K=mS2HjLZ`A#x2hOGp27)F|?36}i|$LmHN^KiM&{UYx;a%l(;RrA3N zKc@@7gWg7JvZR@F*5zoHY3%*LxmZ}KldS~!{OeS%9xUu7xRqR)-UqT+a^RpyLyk5Z zW8bn{lq<_~%NmpHE<m_?6H<VWkrxWn&0+CjRH<&+NU{XI`4ZTG_;=@W5l3jMn|w|g ztHQ=rXRmnZkI?s=Rc>mM*CBM!bZQ)3Q7dB=Ga7W72H|gtobgBr31f1GjGcEG<QWVN zJL@+#+UFmut|y(!+D{HHcYX&-)d#aYQtAr(>Q_;|0Bd7mWitCh$!;RDzPm2_tzgx^ zHO5cCj|cSy#Pzmd9^@~hYX`{Rr`sR>MEhYZ@DqoZYoQ<_eq(iNxF<O#GyCCJsZXRO zoW}TL-o@_TMkfA35MIi+UvxD?D72Rwpp!rQ=#^f@VbVd&eEcZ!)^ga0^VV+v2O`CW z{&@R0QmHX9yQm~6_`A!^^<7ufE<E7_TmO`sI#phU3<VdK8=a^V%+XGv(AVXfe%pFI zGx{cC=!1JmxuR_@N>GI+K_36i$%{SC%wIieFB_nliOxtKZB>u_gnbfh(ub11?s+4q zV*YGTJ<`&xv;h2#)qD+{U(C0%U2sn(O(0Ew3z&mY1bycLQ9hN1u4bM3-D4vx3LWlm zuPEr2;faqRW&|d_#xo(}9UXC0x!qp?+$Ve56p^l9Uk3%T9WdaLURF~yat2?kEwwIy zqQ^;WtpizJ<>Rm8AFg2GX4SM}o0;hbruum2t;j6jjD}ol6D>@a9_+LW&H#m)e`f?M zbj2!#Eur<d`-YE4$I|`G6(n|j=5{HwQ6P=PQ0sbcmnORJlCbCdGTD|MEovK=!9GNA zR!ak+mp^$9GiIp-3AIcRxuww%Sj+;ck=nbIOS9{+kcE(V=7=R?UzOB8MGRVdgZN^g zZxgt~$2<ol;uCtS4Q5Qv%A5b48*$em!93c}14o~c+uy%`CCdFSdf7N?m|^cHoqd$m zmwk;(8w}159=Vbe;Cx3=ZW;}*MxNDLA<L!u`AMKjz}3d&v*C}@+n%9y%D>K~+bnB! zOvcZ&eVQS3$*dd<NkGN6v|qHW=u3B8@dN(BjE9m3nQxLm7>|dS3SwMcv<3{yHw!=E zSM?|?Dvmw5Lwb;@%*?CQ^Mvs}d|4=47;0bA8mzWLVwKc|uPy{XRL&(tB>kvT_9H!= zlJr;Q!lQ$D)wn-YbCO&6?qcV>9oS5i5LnQRW_agy82oe_wp-@UbLyA+IWwZzECTU+ z6d!`$;YJ3}XN*x#EqH!VQ~#7#?^v?ff5nc4uwV@N8p|-rPCt{pa8j1LNSp(;AF8y= zW3Kk#jAnm!yj-hSG1JJXqfDUG(b4EsNiZ~4fIVRN{h`E50|ArCfd%$?G)>lT6iIzG zrbh2XcG+lWsAwS4T?~YsD&x#UB40nF)3XxW$U4#EUMWs12(&BBQo-HB|Hvc>SoBR& z1tu{D2g}X@=J>4^FGJ+L`)TnGepn5e65*#=#)1XOq@-|wN7mDcPC90s4cea=yt{1V zj^-p7Kdoq}vcaS$RGBJ#X5u3v)&Z93G>!Yyqt7mrAc;6<2FYeBF(8RSv(hnQ!eN;k znPMO}0g$sZ4Kj5iTj!}o2IB7HSzcLPEsgH`ef8LiA<7uv#am4K-N1w1GwWZ;#L^^m z_b#KJ5)r_(KT3!P`cAu6BX_AE4HHteV&2R-lk7_6+b2zIyvHG9z<#uvdjPlS_K#c9 zUV^nl<Qq}h(wEG6wwA4BfdIvp*5^gj*GlV5#hzIzq0D)F8**uBIcqwWECe17hKIuA zh=t%I8H$9Zc{nnU-5t3$-?jsGnBN=gWt%lRU-}uK`j(h*m0jjxa_W6D_24A86s{VF zgUZ8vPZ88!@uJUqG_3N4$A&B=DH^nT!(S^&n!>SaAwTM}`k*+cWw2t`xkiZ+p}kai z&r?TQ>WALo{XE`QM9K?DrQcH>R=OKc!m-yf7G4)sIp0%Lz7Bx*EsZ!HuDq$6zp69L z#!tT3g?}AQd&*ieX-T-<v<();(DMuwG{y?>&(`$ZseN`3LztT>ESwwtvfiI0p74&g zHY$kv+Ss-A)W&|=G{xRY@*S5^a_i$g*roU=&-U|`&`()?SO-dDac(1+xRtt)3JG$1 zR=U(n9n$dXDn=gO!BncdJ3~Olg{^r%g+aaqtvYiNx*)6EiHYdaCSB%?5u1Q&xqA1C z=Z<k)-lAc}$;9dCj&@->wMEa)eK>XVkWuz~!6T1fNVrI<(;$MVUvJHFcPM533<WU4 zI+{5B5mV4EkG2BrX;q3>5Fq0A2}^xr(}ypt@SDv|eVC+?jop)kB4Gvc@%s+6F$|w^ z!cMO{c;9#QeG@oMai>_iNhS!nG9AI{?%+KUJ(Ecy3I1&xzT(x0L;ge~V)hy5xQIF; zXpOmJ4dioqlz_x73fYcbOtx=~gHC?t?iA=PpB4YE#<7@Q*XobCj;hHuRG*n=eC}Ql zxhn{t2B2i5$+k=dWSQRaZt_|;JAZXENK*sNy4X01!ciRrTHb_#liz3(y2@y6r?aiR zG*GM<;b@41+<5$H%aaGQ+<^<9T!tgI)_Mxc;V%;qa;M|s$21vR#wqjLj+uP#Mk8iC zXYTAkR3KjW>^z+A?%#9=S=rXB=sMryXAR(zOcmNidv233!rMaC2H@is{oGNV9PbO) zUk&k*po<qnOIK}cX?aAS^R_)2sxgH$01^A;eHZQJ1?+3dL(=}rwarCrQo0-4Y-C5z zmZ#0!plu0d^d|OYBX;oujRGsSk6{w0ZiXcRDDJX+`5Chu$&W=bciW1VACDs*hW=oN z7B^JeVPk$-tv}h|(N!yrvc85<J(|N?8Dpy~bu(ILt0O|rPq?Ld$gEZYbFzFkJ82Dh z&%_O0FuCfEJ6}TUIq$VEfIVLfVIEgWrR5y<Jd51E1EE&wHgj~Q7oPAcs&QnJK#Oq= z3U=44XwACmvbzaYgBvU+H8`v*1PgJ7owF@8KIjU!F^|SUW`f>QYX!M|?yWi-nPT~# zvfmbOLUm|1(?J0}0RP-8M-_i-cdBl;z$3(2&e~}*)7)KpA;#l5MKm?RYc6k`_7W2% z<m9ZM$*G2-{*ABme_qMKxKTafwMcyWExNu<VgM}wX6fu`XY204|H9qH*+3r`fK!*t zX5#5X2*3iKVgUgE+iDQsLeecrq;wt_o*a&A&^g%K8KnQvtt;P`(@mqi>;|IRGaSmc zxNE)G6r$g)s==r1j7Rh~i%Lb*$!J(T)DeUVuPZ7kE_!<voPS|1kJ<eWMba8DjrWP2 z_iJ`;<ACm8u=`TVb>j$#f@k4HOOsdeN$}-Ihwg^8?n<8CL3$}_H+Ivn^(rXiCof1| zuE$<f2q0%uckbx?#RtmIucn6HqEx`&ec*qVgxMWyjSOpQ@bOO&Yw&T8jf~go2~G<w ztsH5LXw~ZthO6(`Qf+R!HQ5x0g2XopH)(8BY}TluTo=u0+(3WPd#|wQn#p{5!fWKK z!7>H+?L%XupZ`*|-u(LM@$iWU6w05d-dH9ZTBBZ~7_>L-x>^>zl$1|4#ndx0vX&^T zU=HQy2mF<EeO!#oi(BM-o0R`#Uft~o$27+^c)9r0k$)k5<oO8cIi#t5yUgjIt#H5< zAYP)o%-I1_M0LdVZSEmjdbQX*`O(-gSW}49ed0&SG{9~Aa4~z8WT0}R1bukPx<7qO za!ZnrpP%CFD>tlLYFOAHoPT%L|A}BMz`ywHKNA>n{{6rGi9gMSuX%|9|G(e&cT#_s p`M;zHNd8&$zfbu4oBwx08_7Q^pre6{_vZre_R!w)GA8|l{s)EOuulL0
--- a/browser/extensions/shield-recipe-client/test/browser/head.js +++ b/browser/extensions/shield-recipe-client/test/browser/head.js @@ -21,16 +21,23 @@ sinon.assert.fail = function(message) { registerCleanupFunction(async function() { // Cleanup window or the test runner will throw an error delete window.sinon; }); this.UUID_REGEX = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/; +this.TEST_XPI_URL = (function() { + const dir = getChromeDir(getResolvedURI(gTestPath)); + dir.append("fixtures"); + dir.append("normandy.xpi"); + return Services.io.newFileURI(dir).spec; +})(); + this.withSandboxManager = function(Assert, testFunction) { return async function inner() { const sandboxManager = new SandboxManager(); sandboxManager.addHold("test running"); await testFunction(sandboxManager); sandboxManager.removeHold("test running"); @@ -38,39 +45,44 @@ this.withSandboxManager = function(Asser .then(() => Assert.ok(true, "sandbox is nuked")) .catch(e => Assert.ok(false, "sandbox is nuked", e)); }; }; this.withDriver = function(Assert, testFunction) { return withSandboxManager(Assert, async function inner(sandboxManager) { const driver = new NormandyDriver(sandboxManager); - await testFunction(driver); + await testFunction(driver, sandboxManager); }); }; this.withMockNormandyApi = function(testFunction) { return async function inner(...args) { const mockApi = {actions: [], recipes: [], implementations: {}}; - sinon.stub(NormandyApi, "fetchActions", async () => mockApi.actions); - sinon.stub(NormandyApi, "fetchRecipes", async () => mockApi.recipes); - sinon.stub(NormandyApi, "fetchImplementation", async action => { - const impl = mockApi.implementations[action.name]; - if (!impl) { - throw new Error("Missing"); + // Use callsFake instead of resolves so that the current values in mockApi are used. + mockApi.fetchActions = sinon.stub(NormandyApi, "fetchActions").callsFake(async () => mockApi.actions); + mockApi.fetchRecipes = sinon.stub(NormandyApi, "fetchRecipes").callsFake(async () => mockApi.recipes); + mockApi.fetchImplementation = sinon.stub(NormandyApi, "fetchImplementation").callsFake( + async action => { + const impl = mockApi.implementations[action.name]; + if (!impl) { + throw new Error("Missing"); + } + return impl; } - return impl; - }); + ); - await testFunction(mockApi, ...args); - - NormandyApi.fetchActions.restore(); - NormandyApi.fetchRecipes.restore(); - NormandyApi.fetchImplementation.restore(); + try { + await testFunction(mockApi, ...args); + } finally { + mockApi.fetchActions.restore(); + mockApi.fetchRecipes.restore(); + mockApi.fetchImplementation.restore(); + } }; }; const preferenceBranches = { user: Preferences, default: new Preferences({defaultBranch: true}), };
--- a/browser/extensions/shield-recipe-client/test/unit/head_xpc.js +++ b/browser/extensions/shield-recipe-client/test/unit/head_xpc.js @@ -18,24 +18,25 @@ if (!extensionDir.exists()) { extensionDir = extensionDir.parent; extensionDir.append(EXTENSION_ID + ".xpi"); } Components.manager.addBootstrappedManifestLocation(extensionDir); // ================================================ // Load mocking/stubbing library, sinon // docs: http://sinonjs.org/releases/v2.3.2/ +/* exported sinon */ Cu.import("resource://gre/modules/Timer.jsm"); const {Loader} = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {}); const loader = new Loader.Loader({ paths: { "": "resource://testing-common/", }, globals: { setTimeout, setInterval, clearTimeout, clearInterval, }, }); const require = Loader.Require(loader, {id: ""}); -const sinon = require("sinon-2.3.2"); +this.sinon = require("sinon-2.3.2"); // ================================================
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/api/v1/index.json @@ -0,0 +1,8 @@ +{ + "action-list": "/api/v1/action/", + "classify-client": "/api/v1/classify_client/", + "filters": "/api/v1/filters/", + "recipe-list": "/api/v1/recipe/", + "recipe-signed": "/api/v1/recipe/signed/", + "reciperevision-list": "/api/v1/recipe_revision/" +}
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/api/v1/recipe/signed/index.json @@ -0,0 +1,1 @@ +[{"recipe":{"action":"console-log","arguments":{"message":"this signature does not match this recipe"},"channels":[],"countries":[],"enabled":true,"extra_filter_expression":"true || true","filter_expression":"true || true","id":1,"last_updated":"2017-02-17T18:29:09.839239Z","locales":[],"name":"system-addon-test","revision_id":"b2cb8a26e132182d7d02cf50695d2c7f06cf3b954ff2ff63bca49d724ee91950"},"signature":{"public_key":"MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEVEKiCAIkwRg1VFsP8JOYdSF6a3qvgbRPoEK9eTuLbrB6QixozscKR4iWJ8ZOOX6RPCRgFdfVDoZqjFBFNJN9QtRBk0mVtHbnErx64d2vMF0oWencS1hyLW2whgOgOz7p","signature":"p4g3eurmPsJK5UcGT97BRyKstpwZ_2mNJkDGpd6QXlkXfvgwprjeyb5yeIEkKUXqc6krWid4obB_OP9-CwOi9tvKY1pV8p98CT5BhF0IVgpF3b7KBW1a0BVdg5owoG5W","timestamp":"2017-02-17T18:29:09.847614Z","x5u":"/api/v1/recipe/signed/normandy.content-signature.mozilla.org-20210705.dev.chain"}}]
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/api/v1/recipe/signed/normandy.content-signature.mozilla.org-20210705.dev.chain @@ -0,0 +1,123 @@ +-----BEGIN CERTIFICATE----- +MIIGRTCCBC2gAwIBAgIEAQAABTANBgkqhkiG9w0BAQwFADBrMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHQWxsaXpvbTEXMBUGA1UECxMOQ2xvdWQgU2VydmljZXMxMTAv +BgNVBAMTKERldnppbGxhIFNpZ25pbmcgU2VydmljZXMgSW50ZXJtZWRpYXRlIDEw +HhcNMTYwNzA2MjE1NzE1WhcNMjEwNzA1MjE1NzE1WjCBrzELMAkGA1UEBhMCVVMx +EzARBgNVBAgTCkNhbGlmb3JuaWExHDAaBgNVBAoTE01vemlsbGEgQ29ycG9yYXRp +b24xFzAVBgNVBAsTDkNsb3VkIFNlcnZpY2VzMS8wLQYDVQQDEyZub3JtYW5keS5j +b250ZW50LXNpZ25hdHVyZS5tb3ppbGxhLm9yZzEjMCEGCSqGSIb3DQEJARYUc2Vj +dXJpdHlAbW96aWxsYS5vcmcwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARUQqIIAiTB +GDVUWw/wk5h1IXpreq+BtE+gQr15O4tusHpCLGjOxwpHiJYnxk45fpE8JGAV19UO +hmqMUEU0k31C1EGTSZW0ducSvHrh3a8wXShZ6dxLWHItbbCGA6A7PumjggJYMIIC +VDAdBgNVHQ4EFgQUVfksSjlZ0i1TBiS1vcoObaMeXn0wge8GA1UdIwSB5zCB5IAU +/YboUIXAovChEpudDBuodHKbjUuhgcWkgcIwgb8xCzAJBgNVBAYTAlVTMQswCQYD +VQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEmMCQGA1UEChMdQ29udGVu +dCBTaWduYXR1cmUgRGV2IFNpZ25pbmcxJjAkBgNVBAMTHWRldi5jb250ZW50LXNp +Z25hdHVyZS5yb290LmNhMTswOQYJKoZIhvcNAQkBFixjbG91ZHNlYytkZXZyb290 +Y29udGVudHNpZ25hdHVyZUBtb3ppbGxhLmNvbYIEAQAABDAMBgNVHRMBAf8EAjAA +MA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzBEBgNVHR8E +PTA7MDmgN6A1hjNodHRwczovL2NvbnRlbnQtc2lnbmF0dXJlLmRldi5tb3phd3Mu +bmV0L2NhL2NybC5wZW0wQgYJYIZIAYb4QgEEBDUWM2h0dHBzOi8vY29udGVudC1z +aWduYXR1cmUuZGV2Lm1vemF3cy5uZXQvY2EvY3JsLnBlbTBOBggrBgEFBQcBAQRC +MEAwPgYIKwYBBQUHMAKGMmh0dHBzOi8vY29udGVudC1zaWduYXR1cmUuZGV2Lm1v +emF3cy5uZXQvY2EvY2EucGVtMDEGA1UdEQQqMCiCJm5vcm1hbmR5LmNvbnRlbnQt +c2lnbmF0dXJlLm1vemlsbGEub3JnMA0GCSqGSIb3DQEBDAUAA4ICAQCwb+8JTAB7 +ZfQmFqPUIV2cQQv696AaDPQCtA9YS4zmUfcLMvfZVAbK397zFr0RMDdLiTUQDoeq +rBEmPXhJRPiv6JAK4n7Jf6Y6XfXcNxx+q3garR09Vm/0CnEq/iV+ZAtPkoKIO9kr +Nkzecd894yQCF4hIuPQ5qtMySeqJmH3Dp13eq4T0Oew1Bu32rNHuBJh2xYBkWdun +aAw/YX0I5EqZBP/XA6gbiA160tTK+hnpnlMtw/ljkvfhHbWpICD4aSiTL8L3vABQ +j7bqjMKR5xDkuGWshZfcmonpvQhGTye/RZ1vz5IzA3VOJt1mz5bdZlitpaOm/Yv0 +x6aODz8GP/PiRWFQ5CW8Uf/7pGc5rSyvnfZV2ix8EzFlo8cUtuN1fjrPFPOFOLvG +iiB6S9nlXiKBGYIDdd8V8iC5xJpzjiAWJQigwSNzuc2K30+iPo3w0zazkwe5V8jW +gj6gItYxh5xwVQTPHD0EOd9HvV1ou42+rH5Y+ISFUm25zz02UtUHEK0BKtL0lmdt +DwVq5jcHn6bx2/iwUtlKvPXtfM/6JjTJlkLZLtS7U5/pwcS0owo9zAL0qg3bdm16 ++v/olmPqQFLUHmamJTzv3rojj5X/uVdx1HMM3wBjV9tRYoYaZw9RIInRmM8Z1pHv +JJ+CIZgCyd5vgp57BKiodRZcgHoCH+BkOQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIHijCCBXKgAwIBAgIEAQAABDANBgkqhkiG9w0BAQwFADCBvzELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSYwJAYDVQQK +Ex1Db250ZW50IFNpZ25hdHVyZSBEZXYgU2lnbmluZzEmMCQGA1UEAxMdZGV2LmNv +bnRlbnQtc2lnbmF0dXJlLnJvb3QuY2ExOzA5BgkqhkiG9w0BCQEWLGNsb3Vkc2Vj +K2RldnJvb3Rjb250ZW50c2lnbmF0dXJlQG1vemlsbGEuY29tMB4XDTE2MDcwNjIx +NDkyNloXDTIxMDcwNTIxNDkyNlowazELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0Fs +bGl6b20xFzAVBgNVBAsTDkNsb3VkIFNlcnZpY2VzMTEwLwYDVQQDEyhEZXZ6aWxs +YSBTaWduaW5nIFNlcnZpY2VzIEludGVybWVkaWF0ZSAxMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAypIfUURYgWzGw8G/1Pz9zW+Tsjirx2owThiv2gys +wJiWL/9/2gzKOrYDEqlDUudfA/BjVRtT9+NbYgnhaCkNfADOAacWS83aMhedAqhP +bVd5YhGQdpijI7f1AVTSb0ehrU2nhOZHvHX5Tk2fbRx3ryefIazNTLFGpiMBbsNv +tSI/+fjW8s0MhKNqlLnk6a9mZKo0mEy7HjGYV8nzsgI17rKLx/s2HG4TFG0+JQzc +UGlum3Tg58ritDzWdyKIkmKNZ48oLBX99Qc8j8B1UyiLv6TZmjVX0I+Ds7eSWHZk +0axLEpTyf2r7fHvN4iDNCPajw+ZpuuBfbs80Ha8b8MHvnpf9fbwiirodNQOVpY4c +t5E3Us3eYwBKdqDEbECWxCKGAS2/iVVUCNKHsg0sSxgqcwxrxyrddQRUQ0EM38DZ +F/vHt+vTdHt07kezbjJe0Kklel59uSpghA0iL4vxzbTns1fuwYOgVrNGs3acTkiB +GhFOxRXUPGtpdYmv+AaR9WlWJQY1GIEoVrinPVH7bcCwyh1CcUbHL+oAFTcmc6kZ +7azNg21tWILIRL7R0IZYQm0tF5TTwCsjVC7FuHaBtkxtVrrZqeKjvVXQ8TK5VoI0 +BUQ6BKHGeTtm+0HBpheYBDy3wkOsEGbGHLEM6cMeiz6PyCXF8wXli8Qb/TjN3LHZ +e30CAwEAAaOCAd8wggHbMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGG +MBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBT9huhQhcCi8KESm50M +G6h0cpuNSzCB7AYDVR0jBIHkMIHhgBSDx8s0qJaMyQCehKcuzgzVNRA75qGBxaSB +wjCBvzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFp +biBWaWV3MSYwJAYDVQQKEx1Db250ZW50IFNpZ25hdHVyZSBEZXYgU2lnbmluZzEm +MCQGA1UEAxMdZGV2LmNvbnRlbnQtc2lnbmF0dXJlLnJvb3QuY2ExOzA5BgkqhkiG +9w0BCQEWLGNsb3Vkc2VjK2RldnJvb3Rjb250ZW50c2lnbmF0dXJlQG1vemlsbGEu +Y29tggEBMEIGCWCGSAGG+EIBBAQ1FjNodHRwczovL2NvbnRlbnQtc2lnbmF0dXJl +LmRldi5tb3phd3MubmV0L2NhL2NybC5wZW0wTgYIKwYBBQUHAQEEQjBAMD4GCCsG +AQUFBzAChjJodHRwczovL2NvbnRlbnQtc2lnbmF0dXJlLmRldi5tb3phd3MubmV0 +L2NhL2NhLnBlbTANBgkqhkiG9w0BAQwFAAOCAgEAbum0z0ccqI1Wp49VtsGmUPHA +gjPPy2Xa5NePmqY87WrGdhjN3xbLVb3hx8T2N6pqDjMY2rEynXKEOek3oJkQ3C6J +8AFP6Y93gaAlNz6EA0J0mqdW5TMI8YEYsu2ma+dQQ8wm9f/5b+/Y8qwfhztP06W5 +H6IG04/RvgUwYMnSR4QvT309fu5UmCRUDzsO53ZmQCfmN94u3NxHc4S6n0Q6AKAM +kh5Ld9SQnlqqqDykzn7hYDi8nTLWc7IYqkGfNMilDEKbAl4CjnSfyEvpdFAJ9sPR +UL+kaWFSMvaqIPNpxS5OpoPZjmxEc9HHnCHxtfDHWdXTJILjijPrCdMaxOCHfIqV +5roOJggI4RZ0YM68IL1MfN4IEVOrHhKjDHtd1gcmy2KU4jfj9LQq9YTnyvZ2z1yS +lS310HG3or1K8Nnu5Utfe7T6ppX8bLRMkS1/w0p7DKxHaf4D/GJcCtM9lcSt9JpW +6ACKFikjWR4ZxczYKgApc0wcjd7XBuO5777xtOeyEUDHdDft3jiXA91dYM5UAzc3 +69z/3zmaELzo0gWcrjLXh7fU9AvbU4EUF6rwzxbPGF78jJcGK+oBf8uWUCkBykDt +VsAEZI1u4EDg8e/C1nFqaH9gNMArAgquYIB9rve+hdprIMnva0S147pflWopBWcb +jwzgpfquuYnnxe0CNBA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIH3DCCBcSgAwIBAgIBATANBgkqhkiG9w0BAQwFADCBvzELMAkGA1UEBhMCVVMx +CzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSYwJAYDVQQKEx1D +b250ZW50IFNpZ25hdHVyZSBEZXYgU2lnbmluZzEmMCQGA1UEAxMdZGV2LmNvbnRl +bnQtc2lnbmF0dXJlLnJvb3QuY2ExOzA5BgkqhkiG9w0BCQEWLGNsb3Vkc2VjK2Rl +dnJvb3Rjb250ZW50c2lnbmF0dXJlQG1vemlsbGEuY29tMB4XDTE2MDcwNjE4MTUy +MloXDTI2MDcwNDE4MTUyMlowgb8xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEW +MBQGA1UEBxMNTW91bnRhaW4gVmlldzEmMCQGA1UEChMdQ29udGVudCBTaWduYXR1 +cmUgRGV2IFNpZ25pbmcxJjAkBgNVBAMTHWRldi5jb250ZW50LXNpZ25hdHVyZS5y +b290LmNhMTswOQYJKoZIhvcNAQkBFixjbG91ZHNlYytkZXZyb290Y29udGVudHNp +Z25hdHVyZUBtb3ppbGxhLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAJcPcXhD8MzF6OTn5qZ0L7lX1+PEgLKhI9g1HxxDYDVup4Zm0kZhPGmFSlml +6eVO99OvvHdAlHhQGCIG7h+w1cp66mWjfpcvtQH23uRoKZfiW3jy1jUWrvdXolxR +t1taZosjzo+9OP8TvG6LpJj7AvqUiYD4wYnQJtt0jNRN4d6MUfQwiavSS5uTBuxd +ZJ4TsPvEI+Iv4A4PSobSzxkg79LTMvsGtDLQv7nN5hMs9T18EL5GnIKoqnSQCU0d +n2CN7S3QiQ+cbORWsSYqCTj1gUbFh6X3duEB/ypLcyWFbqeJmPHikjY8q8pLjZSB +IYiTJYLyvYlKdM5QleC/xuBNnMPCftrwwLHjWE4Dd7C9t7k0R5xyOetuiHLCwOcQ +tuckp7RgFKoviMNv3gdkzwVapOklcsaRkRscv6OMTKJNsdJVIDLrPF1wMABhbEQB +64BL0uL4lkFtpXXbJzQ6mgUNQveJkfUWOoB+cA/6GtI4J0aQfvQgloCYI6jxNn/e +Nvk5OV9KFOhXS2dnDft3wHU46sg5yXOuds1u6UrOoATBNFlkS95m4zIX1Svu091+ +CKTiLK85+ZiFtAlU2bPr3Bk3GhL3Z586ae6a4QUEx6SPQVXc18ezB4qxKqFc+avI +ylccYMRhVP+ruADxtUM5Vy6x3U8BwBK5RLdecRx2FEBDImY1AgMBAAGjggHfMIIB +2zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAWBgNVHSUBAf8EDDAK +BggrBgEFBQcDAzAdBgNVHQ4EFgQUg8fLNKiWjMkAnoSnLs4M1TUQO+YwgewGA1Ud +IwSB5DCB4YAUg8fLNKiWjMkAnoSnLs4M1TUQO+ahgcWkgcIwgb8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEmMCQGA1UE +ChMdQ29udGVudCBTaWduYXR1cmUgRGV2IFNpZ25pbmcxJjAkBgNVBAMTHWRldi5j +b250ZW50LXNpZ25hdHVyZS5yb290LmNhMTswOQYJKoZIhvcNAQkBFixjbG91ZHNl +YytkZXZyb290Y29udGVudHNpZ25hdHVyZUBtb3ppbGxhLmNvbYIBATBCBglghkgB +hvhCAQQENRYzaHR0cHM6Ly9jb250ZW50LXNpZ25hdHVyZS5kZXYubW96YXdzLm5l +dC9jYS9jcmwucGVtME4GCCsGAQUFBwEBBEIwQDA+BggrBgEFBQcwAoYyaHR0cHM6 +Ly9jb250ZW50LXNpZ25hdHVyZS5kZXYubW96YXdzLm5ldC9jYS9jYS5wZW0wDQYJ +KoZIhvcNAQEMBQADggIBAAAQ+fotZE79FfZ8Lz7eiTUzlwHXCdSE2XD3nMROu6n6 +uLTBPrf2C+k+U1FjKVvL5/WCUj6hIzP2X6Sb8+o0XHX9mKN0yoMORTEYJOnazYPK +KSUUFnE4vGgQkr6k/31gGRMTICdnf3VOOAlUCQ5bOmGIuWi401E3sbd85U+TJe0A +nHlU+XjtfzlqcBvQivdbA0s+GEG55uRPvn952aTBEMHfn+2JqKeLShl4AtUAfu+h +6md3Z2HrEC7B3GK8ekWPu0G/ZuWTuFvOimZ+5C8IPRJXcIR/siPQl1x6dpTCew6t +lPVcVuvg6SQwzvxetkNrGUe2Wb2s9+PK2PUvfOS8ee25SNmfG3XK9qJpqGUhzSBX +T8QQgyxd0Su5G7Wze7aaHZd/fqIm/G8YFR0HiC2xni/lnDTXFDPCe+HCnSk0bH6U +wpr6I1yK8oZ2IdnNVfuABGMmGOhvSQ8r7//ea9WKhCsGNQawpVWVioY7hpyNAJ0O +Vn4xqG5f6allz8lgpwAQ+AeEEClHca6hh6mj9KhD1Of1CC2Vx52GHNh/jMYEc3/g +zLKniencBqn3Y2XH2daITGJddcleN09+a1NaTkT3hgr7LumxM8EVssPkC+z9j4Vf +Gbste+8S5QCMhh00g5vR9QF8EaFqdxCdSxrsA4GmpCa5UQl8jtCnpp2DLKXuOh72 +-----END CERTIFICATE-----
deleted file mode 100644 --- a/browser/extensions/shield-recipe-client/test/unit/test_ActionSandboxManager.js +++ /dev/null @@ -1,169 +0,0 @@ -"use strict"; - -Cu.import("resource://shield-recipe-client/lib/ActionSandboxManager.jsm"); -Cu.import("resource://shield-recipe-client/lib/NormandyDriver.jsm"); - -async function withManager(script, testFunction) { - const manager = new ActionSandboxManager(script); - manager.addHold("testing"); - await testFunction(manager); - manager.removeHold("testing"); -} - -add_task(async function testMissingCallbackName() { - await withManager("1 + 1", async manager => { - equal( - await manager.runAsyncCallback("missingCallback"), - undefined, - "runAsyncCallback returns undefined when given a missing callback name", - ); - }); -}); - -add_task(async function testCallback() { - const script = ` - registerAsyncCallback("testCallback", async function(normandy) { - return 5; - }); - `; - - await withManager(script, async manager => { - const result = await manager.runAsyncCallback("testCallback"); - equal(result, 5, "runAsyncCallback executes the named callback inside the sandbox"); - }); -}); - -add_task(async function testArguments() { - const script = ` - registerAsyncCallback("testCallback", async function(normandy, a, b) { - return a + b; - }); - `; - - await withManager(script, async manager => { - const result = await manager.runAsyncCallback("testCallback", 4, 6); - equal(result, 10, "runAsyncCallback passes arguments to the callback"); - }); -}); - -add_task(async function testCloning() { - const script = ` - registerAsyncCallback("testCallback", async function(normandy, obj) { - return {foo: "bar", baz: obj.baz}; - }); - `; - - await withManager(script, async manager => { - const result = await manager.runAsyncCallback("testCallback", {baz: "biff"}); - - deepEqual( - result, - {foo: "bar", baz: "biff"}, - ( - "runAsyncCallback clones arguments into the sandbox and return values into the " + - "context it was called from" - ), - ); - }); -}); - -add_task(async function testError() { - const script = ` - registerAsyncCallback("testCallback", async function(normandy) { - throw new Error("WHY") - }); - `; - - await withManager(script, async manager => { - try { - await manager.runAsyncCallback("testCallback"); - ok(false, "runAsnycCallbackFromScript throws errors when raised by the sandbox"); - } catch (err) { - equal(err.message, "WHY", "runAsnycCallbackFromScript clones error messages"); - } - }); -}); - -add_task(async function testDriver() { - const script = ` - registerAsyncCallback("testCallback", async function(normandy) { - return normandy; - }); - `; - - await withManager(script, async manager => { - const sandboxDriver = await manager.runAsyncCallback("testCallback"); - const referenceDriver = new NormandyDriver(manager); - equal( - sandboxDriver.constructor.name, - "NormandyDriver", - "runAsyncCallback passes a driver as the first parameter", - ); - for (const prop in referenceDriver) { - ok(prop in sandboxDriver, "runAsyncCallback passes a driver as the first parameter"); - } - }); -}); - -add_task(async function testGlobalObject() { - // Test that window is an alias for the global object, and that it - // has some expected functions available on it. - const script = ` - window.setOnWindow = "set"; - this.setOnGlobal = "set"; - - registerAsyncCallback("testCallback", async function(normandy) { - return { - setOnWindow: setOnWindow, - setOnGlobal: window.setOnGlobal, - setTimeoutExists: setTimeout !== undefined, - clearTimeoutExists: clearTimeout !== undefined, - }; - }); - `; - - await withManager(script, async manager => { - const result = await manager.runAsyncCallback("testCallback"); - Assert.deepEqual(result, { - setOnWindow: "set", - setOnGlobal: "set", - setTimeoutExists: true, - clearTimeoutExists: true, - }, "sandbox.window is the global object and has expected functions."); - }); -}); - -add_task(async function testRegisterActionShim() { - const recipe = { - foo: "bar", - }; - const script = ` - class TestAction { - constructor(driver, recipe) { - this.driver = driver; - this.recipe = recipe; - } - - execute() { - return new Promise(resolve => { - resolve({ - foo: this.recipe.foo, - isDriver: "log" in this.driver, - }); - }); - } - } - - registerAction('test-action', TestAction); - `; - - await withManager(script, async manager => { - const result = await manager.runAsyncCallback("action", recipe); - equal(result.foo, "bar", "registerAction registers an async callback for actions"); - equal( - result.isDriver, - true, - "registerAction passes the driver to the action class constructor", - ); - }); -});
--- a/browser/extensions/shield-recipe-client/test/unit/test_NormandyApi.js +++ b/browser/extensions/shield-recipe-client/test/unit/test_NormandyApi.js @@ -1,17 +1,33 @@ +/* globals sinon */ "use strict"; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/CanonicalJSON.jsm", this); Cu.import("resource://gre/modules/osfile.jsm", this); Cu.import("resource://shield-recipe-client/lib/NormandyApi.jsm", this); load("utils.js"); /* globals withMockPreferences */ +class MockResponse { + constructor(content) { + this.content = content; + } + + async text() { + return this.content; + } + + async json() { + return JSON.loads(this.content); + } +} + function withServer(server, task) { return withMockPreferences(async function inner(preferences) { const serverUrl = `http://localhost:${server.identity.primaryPort}`; preferences.set("extensions.shield-recipe-client.api_url", `${serverUrl}/api/v1`); preferences.set( "security.content.signature.root_hash", // Hash of the key that signs the normandy dev certificates "4C:35:B1:C3:E3:12:D9:55:E7:78:ED:D0:A7:E7:8A:38:83:04:EF:01:BF:FA:03:29:B2:46:9F:3C:C5:EC:36:04" @@ -32,19 +48,19 @@ function makeScriptServer(scriptPath) { server.start(-1); return server; } function withScriptServer(scriptPath, task) { return withServer(makeScriptServer(scriptPath), task); } -function makeMockApiServer() { +function makeMockApiServer(directory) { const server = new HttpServer(); - server.registerDirectory("/", do_get_file("mock_api")); + server.registerDirectory("/", directory); server.setIndexHandler(async function(request, response) { response.processAsync(); const dir = request.getProperty("directory"); const index = dir.clone(); index.append("index.json"); if (!index.exists()) { @@ -65,17 +81,17 @@ function makeMockApiServer() { } }); server.start(-1); return server; } function withMockApiServer(task) { - return withServer(makeMockApiServer(), task); + return withServer(makeMockApiServer(do_get_file("mock_api")), task); } add_task(withMockApiServer(async function test_get(serverUrl) { // Test that NormandyApi can fetch from the test server. const response = await NormandyApi.get(`${serverUrl}/api/v1/`); const data = await response.json(); equal(data["recipe-list"], "/api/v1/recipe/", "Expected data in response"); })); @@ -120,16 +136,87 @@ add_task(withMockApiServer(async functio })); add_task(withMockApiServer(async function test_fetchRecipes() { const recipes = await NormandyApi.fetchRecipes(); equal(recipes.length, 1); equal(recipes[0].name, "system-addon-test"); })); +add_task(async function test_fetchRecipes_canonical_mismatch() { + const getApiUrl = sinon.stub(NormandyApi, "getApiUrl").resolves("http://localhost/recipes/"); + + // Recipe is non-canonical (it has whitespace, properties are out of order) + const response = new MockResponse(`[ + { + "recipe": {"b": 1, "a": 2}, + "signature": {"signature": "", "x5u": ""} + } + ]`); + const get = sinon.stub(NormandyApi, "get").resolves(response); + + try { + await NormandyApi.fetchRecipes(); + ok(false, "fetchRecipes did not throw for canonical JSON mismatch"); + } catch (err) { + ok(err instanceof NormandyApi.InvalidSignatureError, "Error is an InvalidSignatureError"); + ok(/Canonical/.test(err), "Error is due to canonical JSON mismatch"); + } + + getApiUrl.restore(); + get.restore(); +}); + +// Test validation errors due to validation throwing an exception (e.g. when +// parameters passed to validation are malformed). +add_task(async function test_fetchRecipes_validation_error() { + const getApiUrl = sinon.stub(NormandyApi, "getApiUrl").resolves("http://localhost/recipes/"); + + // Mock two URLs: recipes and the x5u + const get = sinon.stub(NormandyApi, "get").callsFake(async url => { + if (url.endsWith("recipes/")) { + return new MockResponse(CanonicalJSON.stringify([ + { + recipe: {a: 1, b: 2}, + signature: {signature: "invalidsignature", x5u: "http://localhost/x5u/"}, + }, + ])); + } else if (url.endsWith("x5u/")) { + return new MockResponse("certchain"); + } + + return null; + }); + + // Validation should fail due to a malformed x5u and signature. + try { + await NormandyApi.fetchRecipes(); + ok(false, "fetchRecipes did not throw for a validation error"); + } catch (err) { + ok(err instanceof NormandyApi.InvalidSignatureError, "Error is an InvalidSignatureError"); + ok(/signature/.test(err), "Error is due to a validation error"); + } + + getApiUrl.restore(); + get.restore(); +}); + +// Test validation errors due to validation returning false (e.g. when parameters +// passed to validation are correctly formed, but not valid for the data). +const invalidSignatureServer = makeMockApiServer(do_get_file("invalid_recipe_signature_api")); +add_task(withServer(invalidSignatureServer, async function test_fetchRecipes_invalid_signature() { + try { + await NormandyApi.fetchRecipes(); + ok(false, "fetchRecipes did not throw for an invalid signature"); + } catch (err) { + ok(err instanceof NormandyApi.InvalidSignatureError, "Error is an InvalidSignatureError"); + ok(/signature/.test(err), "Error is due to an invalid signature"); + } +})); + add_task(withMockApiServer(async function test_classifyClient() { const classification = await NormandyApi.classifyClient(); Assert.deepEqual(classification, { country: "US", request_time: new Date("2017-02-22T17:43:24.657841Z"), }); }));
--- a/browser/extensions/shield-recipe-client/test/unit/test_SandboxManager.js +++ b/browser/extensions/shield-recipe-client/test/unit/test_SandboxManager.js @@ -1,15 +1,15 @@ "use strict"; Cu.import("resource://shield-recipe-client/lib/SandboxManager.jsm"); // wrapAsync should wrap privileged Promises with Promises that are usable by // the sandbox. -add_task(async function() { +add_task(function* () { const manager = new SandboxManager(); manager.addHold("testing"); manager.cloneIntoGlobal("driver", { async privileged() { return "privileged"; }, wrapped: manager.wrapAsync(async function() { @@ -20,17 +20,17 @@ add_task(async function() { return this.aValue; }), }, {cloneFunctions: true}); // Assertion helpers manager.addGlobal("ok", ok); manager.addGlobal("equal", equal); - const sandboxResult = await new Promise(resolve => { + const sandboxResult = yield new Promise(resolve => { manager.addGlobal("resolve", result => resolve(result)); manager.evalInSandbox(` // Unwrapped privileged promises are not accessible in the sandbox try { const privilegedResult = driver.privileged().then(() => false); ok(false, "The sandbox could not use a privileged Promise"); } catch (err) { } @@ -40,31 +40,31 @@ add_task(async function() { // Resolve the Promise around the sandbox with the wrapped result to test // that the Promise in the sandbox works. wrappedResult.then(resolve); `); }); equal(sandboxResult, "wrapped", "wrapAsync methods return Promises that work in the sandbox"); - await manager.evalInSandbox(` + yield manager.evalInSandbox(` (async function sandboxTest() { equal( await driver.wrappedThis(), "aValue", "wrapAsync preserves the behavior of the this keyword", ); })(); `); manager.removeHold("testing"); }); // wrapAsync cloning options -add_task(async function() { +add_task(function* () { const manager = new SandboxManager(); manager.addHold("testing"); // clonedArgument stores the argument passed to cloned(), which we use to test // that arguments from within the sandbox are cloned outside. let clonedArgument = null; manager.cloneIntoGlobal("driver", { uncloned: manager.wrapAsync(async function() { @@ -75,17 +75,17 @@ add_task(async function() { return {value: "cloned"}; }, {cloneInto: true, cloneArguments: true}), }, {cloneFunctions: true}); // Assertion helpers manager.addGlobal("ok", ok); manager.addGlobal("deepEqual", deepEqual); - await new Promise(resolve => { + yield new Promise(resolve => { manager.addGlobal("resolve", resolve); manager.evalInSandbox(` (async function() { // The uncloned return value should be privileged and inaccesible. const uncloned = await driver.uncloned(); ok(!("value" in uncloned), "The sandbox could not use an uncloned return value"); // The cloned return value should be usable.
--- a/browser/extensions/shield-recipe-client/test/unit/xpcshell.ini +++ b/browser/extensions/shield-recipe-client/test/unit/xpcshell.ini @@ -1,11 +1,13 @@ [DEFAULT] head = head_xpc.js support-files = mock_api/** + invalid_recipe_signature_api/** query_server.sjs echo_server.sjs utils.js [test_NormandyApi.js] [test_Sampling.js] [test_SandboxManager.js] +[test_Utils.js]
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/vendor/LICENSE_THIRDPARTY @@ -0,0 +1,190 @@ +ajv@5.2.1 MIT +The MIT License (MIT) + +Copyright (c) 2015 Evgeny Poberezkin + +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. + + + +fast-deep-equal@1.0.0 MIT +MIT License + +Copyright (c) 2017 Evgeny Poberezkin + +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. + + +json-stable-stringify@1.0.1 MIT +This software is released under the MIT license: + +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. + + +co@4.6.0 MIT +(The MIT License) + +Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca> + +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. + + +mozjexl@1.1.5 MIT +Copyright for portions of mozJexl are held by TechnologyAdvice, 2015 as part of Jexl. +All other copyright for mozJexl are held by the Mozilla Foundation, 2017. + +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. + +url@0.11.0 MIT +The MIT License (MIT) + +Copyright Joyent, Inc. and other Node contributors. + +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. + + +punycode@1.4.1 MIT + +webpack@3.1.0 MIT +Copyright JS Foundation and other contributors + +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. + + +querystring-es3@0.2.1 MIT + +json-schema-traverse@0.3.1 MIT +MIT License + +Copyright (c) 2017 Evgeny Poberezkin + +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.
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/vendor/ajv.js @@ -0,0 +1,1 @@ +/* eslint-disable */this.ajv=function(e){function r(t){if(a[t])return a[t].exports;var s=a[t]={i:t,l:!1,exports:{}};return e[t].call(s.exports,s,s.exports,r),s.l=!0,s.exports}var a={};return r.m=e,r.c=a,r.d=function(e,a,t){r.o(e,a)||Object.defineProperty(e,a,{configurable:!1,enumerable:!0,get:t})},r.n=function(e){var a=e&&e.__esModule?function(){return e['default']}:function(){return e};return r.d(a,'a',a),a},r.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},r.p='',r(r.s=13)}([function(e,r,a){'use strict';function s(e,r,a){var t=a?' !== ':' === ',s=a?' || ':' && ',o=a?'!':'',i=a?'':'!';return'null'===e?r+t+'null':'array'===e?o+'Array.isArray('+r+')':'object'===e?'('+o+r+s+'typeof '+r+t+'"object"'+s+i+'Array.isArray('+r+'))':'integer'===e?'(typeof '+r+t+'"number"'+s+i+'('+r+' % 1)'+s+r+t+r+')':'typeof '+r+t+'"'+e+'"'}function o(e){for(var r={},a=0;a<e.length;a++)r[e[a]]=!0;return r}function t(e){return'number'==typeof e?'['+e+']':c.test(e)?'.'+e:'[\''+i(e)+'\']'}function i(e){return e.replace(m,'\\$&').replace(/\n/g,'\\n').replace(/\r/g,'\\r').replace(/\f/g,'\\f').replace(/\t/g,'\\t')}function l(e){return'\''+i(e)+'\''}function n(e,r){return'""'==e?r:(e+' + '+r).replace(/' \+ '/g,'')}function h(e){return e.replace(/~/g,'~0').replace(/\//g,'~1')}function d(e){return e.replace(/~1/g,'/').replace(/~0/g,'~')}e.exports={copy:function(e,r){for(var a in r=r||{},e)r[a]=e[a];return r},checkDataType:s,checkDataTypes:function(e,r){switch(e.length){case 1:return s(e[0],r,!0);default:var a='',i=o(e);for(var l in i.array&&i.object&&(a=i.null?'(':'(!'+r+' || ',a+='typeof '+r+' !== "object")',delete i.null,delete i.array,delete i.object),i.number&&delete i.integer,i)a+=(a?' && ':'')+s(l,r,!0);return a;}},coerceToTypes:function(e,r){if(Array.isArray(r)){for(var a,t=[],s=0;s<r.length;s++)a=r[s],p[a]?t[t.length]=a:'array'===e&&'array'===a&&(t[t.length]=a);if(t.length)return t}else{if(p[r])return[r];if('array'===e&&'array'===r)return['array']}},toHash:o,getProperty:t,escapeQuotes:i,equal:a(2),ucs2length:a(23),varOccurences:function(e,r){r+='[^0-9]';var a=e.match(new RegExp(r,'g'));return a?a.length:0},varReplace:function(e,r,a){return r+='([^0-9])',a=a.replace(/\$/g,'$$$$'),e.replace(new RegExp(r,'g'),a+'$1')},cleanUpCode:function(e){return e.replace(u,'').replace(f,'').replace(v,'if (!($1))')},finalCleanUpCode:function(e,r){var a=e.match(y);return a&&2==a.length&&(e=r?e.replace(P,'').replace(w,S):e.replace(g,'').replace(E,b)),a=e.match(x),a&&3===a.length?e.replace(k,''):e},schemaHasRules:function(e,r){if('boolean'==typeof e)return!e;for(var a in e)if(r[a])return!0},schemaHasRulesExcept:function(e,r,a){if('boolean'==typeof e)return!e&&'not'!=a;for(var t in e)if(t!=a&&r[t])return!0},toQuotedString:l,getPathExpr:function(e,r,a,t){var s=a?'\'/\' + '+r+(t?'':'.replace(/~/g, \'~0\').replace(/\\//g, \'~1\')'):t?'\'[\' + '+r+' + \']\'':'\'[\\\'\' + '+r+' + \'\\\']\'';return n(e,s)},getPath:function(e,r,a){var s=a?l('/'+h(r)):l(t(r));return n(e,s)},getData:function(e,r,a){var s,o,l,n;if(''===e)return'rootData';if('/'==e[0]){if(!$.test(e))throw new Error('Invalid JSON-pointer: '+e);o=e,l='rootData'}else{if(n=e.match(_),!n)throw new Error('Invalid JSON-pointer: '+e);if(s=+n[1],o=n[2],'#'==o){if(s>=r)throw new Error('Cannot access property/index '+s+' levels up, current level is '+r);return a[r-s]}if(s>r)throw new Error('Cannot access data '+s+' levels up, current level is '+r);if(l='data'+(r-s||''),!o)return l}for(var h,p=l,c=o.split('/'),m=0;m<c.length;m++)h=c[m],h&&(l+=t(d(h)),p+=' && '+l);return p},unescapeFragment:function(e){return d(decodeURIComponent(e))},unescapeJsonPointer:d,escapeFragment:function(e){return encodeURIComponent(h(e))},escapeJsonPointer:h};var p=o(['string','number','integer','boolean','null']),c=/^[a-z$_][a-z$_0-9]*$/i,m=/'|\\/g,u=/else\s*{\s*}/g,f=/if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g,v=/if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g,y=/[^v.]errors/g,g=/var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g,P=/var errors = 0;|var vErrors = null;/g,E='return errors === 0;',b='validate.errors = null; return true;',w=/if \(errors === 0\) return data;\s*else throw new ValidationError\(vErrors\);/,S='return data;',x=/[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g,k=/if \(rootData === undefined\) rootData = data;/,$=/^\/(?:[^~]|~0|~1)*$/,_=/^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/},function(e,r,a){'use strict';function t(e,r,a){var o=this._refs[a];if('string'==typeof o)if(this._refs[o])o=this._refs[o];else return t.call(this,e,r,o);if(o=o||this._schemas[a],o instanceof y)return l(o.schema,this._opts.inlineRefs)?o.schema:o.validate||this._compile(o);var i,n,h,d=s.call(this,r,a);return d&&(i=d.schema,r=d.root,h=d.baseId),i instanceof y?n=i.validate||e.call(this,i.schema,r,void 0,h):void 0!==i&&(n=l(i,this._opts.inlineRefs)?i:e.call(this,i,r,void 0,h)),n}function s(e,r){var a=u.parse(r,!1,!0),t=c(a),s=d(this._getId(e.schema));if(t!==s){var l=m(t),n=this._refs[l];if('string'==typeof n)return o.call(this,e,n,a);if(n instanceof y)n.validate||this._compile(n),e=n;else if(n=this._schemas[l],n instanceof y){if(n.validate||this._compile(n),l==m(r))return{schema:n,root:e,baseId:s};e=n}else return;if(!e.schema)return;s=d(this._getId(e.schema))}return i.call(this,a,s,e.schema,e)}function o(e,r,a){var t=s.call(this,e,r);if(t){var o=t.schema,l=t.baseId;e=t.root;var n=this._getId(o);return n&&(l=p(l,n)),i.call(this,a,l,o,e)}}function i(e,r,a,t){if(e.hash=e.hash||'','#/'==e.hash.slice(0,2)){for(var o,l=e.hash.split('/'),n=1;n<l.length;n++)if(o=l[n],o){if(o=v.unescapeFragment(o),a=a[o],void 0===a)break;var i;if(!P[o]&&(i=this._getId(a),i&&(r=p(r,i)),a.$ref)){var h=p(r,a.$ref),d=s.call(this,t,h);d&&(a=d.schema,t=d.root,r=d.baseId)}}if(void 0!==a&&a!==t.schema)return{schema:a,root:t,baseId:r}}}function l(e,r){if(!1===r)return!1;return void 0===r||!0===r?n(e):r?h(e)<=r:void 0}function n(e){var r;if(Array.isArray(e)){for(var a=0;a<e.length;a++)if(r=e[a],'object'==typeof r&&!n(r))return!1;}else for(var t in e){if('$ref'==t)return!1;if(r=e[t],'object'==typeof r&&!n(r))return!1}return!0}function h(e){var r,a=0;if(Array.isArray(e)){for(var t=0;t<e.length;t++)if(r=e[t],'object'==typeof r&&(a+=h(r)),a==Infinity)return Infinity;}else for(var s in e){if('$ref'==s)return Infinity;if(E[s])a++;else if(r=e[s],'object'==typeof r&&(a+=h(r)+1),a==Infinity)return Infinity}return a}function d(e,r){!1!==r&&(e=m(e));var a=u.parse(e,!1,!0);return c(a)}function c(e){var r=e.protocol||'//'==e.href.slice(0,2)?'//':'';return(e.protocol||'')+r+(e.host||'')+(e.path||'')+'#'}function m(e){return e?e.replace(b,''):''}function p(e,r){return r=m(r),u.resolve(e,r)}var u=a(15),f=a(2),v=a(0),y=a(4),g=a(24);e.exports=t,t.normalizeId=m,t.fullPath=d,t.url=p,t.ids=function(e){var r=m(this._getId(e)),a={"":r},t={"":d(r,!1)},s={},o=this;return g(e,{allKeys:!0},function(e,r,i,l,n,h,d){if(''!==r){var p=o._getId(e),c=a[l],y=t[l]+'/'+n;if(void 0!==d&&(y+='/'+('number'==typeof d?d:v.escapeFragment(d))),'string'==typeof p){p=c=m(c?u.resolve(c,p):p);var g=o._refs[p];if('string'==typeof g&&(g=o._refs[g]),g&&g.schema){if(!f(e,g.schema))throw new Error('id "'+p+'" resolves to more than one schema');}else if(p!=m(y))if('#'==p[0]){if(s[p]&&!f(e,s[p]))throw new Error('id "'+p+'" resolves to more than one schema');s[p]=e}else o._refs[p]=y}a[r]=c,t[r]=y}}),s},t.inlineRef=l,t.schema=s;var P=v.toHash(['properties','patternProperties','enum','dependencies','definitions']),E=v.toHash(['type','format','pattern','maxLength','minLength','maxProperties','minProperties','maxItems','minItems','maximum','minimum','uniqueItems','multipleOf','required','enum']),b=/#\/?$/},function(e){'use strict';e.exports=function e(r,t){if(r===t)return!0;var s,o=Array.isArray(r),i=Array.isArray(t);if(o&&i){if(r.length!=t.length)return!1;for(s=0;s<r.length;s++)if(!e(r[s],t[s]))return!1;return!0}if(o!=i)return!1;if(r&&t&&'object'==typeof r&&'object'==typeof t){var l=Object.keys(r);if(l.length!==Object.keys(t).length)return!1;var n=r instanceof Date,h=t instanceof Date;if(n&&h)return r.getTime()==t.getTime();if(n!=h)return!1;var d=r instanceof RegExp,p=t instanceof RegExp;if(d&&p)return r.toString()==t.toString();if(d!=p)return!1;for(s=0;s<l.length;s++)if(!Object.prototype.hasOwnProperty.call(t,l[s]))return!1;for(s=0;s<l.length;s++)if(!e(r[l[s]],t[l[s]]))return!1;return!0}return!1}},function(e,r,a){'use strict';function t(e,r,a){this.message=a||t.message(e,r),this.missingRef=o.url(e,r),this.missingSchema=o.normalizeId(o.fullPath(this.missingRef))}function s(e){return e.prototype=Object.create(Error.prototype),e.prototype.constructor=e,e}var o=a(1);e.exports={Validation:s(function(e){this.message='validation failed',this.errors=e,this.ajv=this.validation=!0}),MissingRef:s(t)},t.message=function(e,r){return'can\'t resolve reference '+r+' from id '+e}},function(e,r,a){'use strict';var t=a(0);e.exports=function(e){t.copy(e,this)}},function(e,r,a){var t='undefined'==typeof JSON?a(25):JSON;e.exports=function(e,r){r||(r={}),'function'==typeof r&&(r={cmp:r});var a=r.space||'';'number'==typeof a&&(a=Array(a+1).join(' '));var i=!('boolean'!=typeof r.cycles)&&r.cycles,l=r.replacer||function(e,r){return r},n=r.cmp&&function(e){return function(r){return function(t,s){var o={key:t,value:r[t]},i={key:s,value:r[s]};return e(o,i)}}}(r.cmp),h=[];return function e(r,d,p,c){var m=a?'\n'+Array(c+1).join(a):'',u=a?': ':':';if(p&&p.toJSON&&'function'==typeof p.toJSON&&(p=p.toJSON()),p=l.call(r,d,p),void 0!==p){if('object'!=typeof p||null===p)return t.stringify(p);if(s(p)){for(var f,v=[],y=0;y<p.length;y++)f=e(p,y,p[y],c+1)||t.stringify(null),v.push(m+a+f);return'['+v.join(',')+m+']'}if(-1!==h.indexOf(p)){if(i)return t.stringify('__cycle__');throw new TypeError('Converting circular structure to JSON')}else h.push(p);for(var g=o(p).sort(n&&n(p)),v=[],y=0;y<g.length;y++){var d=g[y],P=e(p,d,p[d],c+1);if(P){var E=t.stringify(d)+u+P;v.push(m+a+E)}}return h.splice(h.indexOf(p),1),'{'+v.join(',')+m+'}'}}({"":e},'',e,0)};var s=Array.isArray||function(e){return'[object Array]'==={}.toString.call(e)},o=Object.keys||function(e){var r=Object.prototype.hasOwnProperty||function(){return!0},a=[];for(var t in e)r.call(e,t)&&a.push(t);return a}},function(e){'use strict';e.exports=function(e,r){function a(e){for(var r=e.rules,a=0;a<r.length;a++)if(t(r[a]))return!0}function t(r){return void 0!==e.schema[r.keyword]||r.implements&&s(r)}function s(r){for(var a=r.implements,t=0;t<a.length;t++)if(void 0!==e.schema[a[t]])return!0}var o='',i=!0===e.schema.$async,l=e.util.schemaHasRulesExcept(e.schema,e.RULES.all,'$ref'),n=e.self._getId(e.schema);if(e.isTop){if(i){e.async=!0;var h='es7'==e.opts.async;e.yieldAwait=h?'await':'yield'}o+=' var validate = ',i?h?o+=' (async function ':('*'!=e.opts.async&&(o+='co.wrap'),o+='(function* '):o+=' (function ',o+=' (data, dataPath, parentData, parentDataProperty, rootData) { \'use strict\'; ',n&&(e.opts.sourceCode||e.opts.processCode)&&(o+=' /*# sourceURL='+n+' */ ')}if('boolean'==typeof e.schema||!(l||e.schema.$ref)){var d,r='false schema',p=e.level,c=e.dataLevel,m=e.schema[r],u=e.schemaPath+e.util.getProperty(r),f=e.errSchemaPath+'/'+r,v=!e.opts.allErrors,y='data'+(c||''),g='valid'+p;if(!1===e.schema){e.isTop?v=!0:o+=' var '+g+' = false; ';var P=P||[];P.push(o),o='',!1===e.createErrors?o+=' {} ':(o+=' { keyword: \''+(d||'false schema')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(f)+' , params: {} ',!1!==e.opts.messages&&(o+=' , message: \'boolean schema is false\' '),e.opts.verbose&&(o+=' , schema: false , parentSchema: validate.schema'+e.schemaPath+' , data: '+y+' '),o+=' } ');var E=o;o=P.pop(),o+=!e.compositeRule&&v?e.async?' throw new ValidationError(['+E+']); ':' validate.errors = ['+E+']; return false; ':' var err = '+E+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '}else o+=e.isTop?i?' return data; ':' validate.errors = null; return true; ':' var '+g+' = true; ';return e.isTop&&(o+=' }); return validate; '),o}if(e.isTop){var b=e.isTop,p=e.level=0,c=e.dataLevel=0,y='data';e.rootId=e.resolve.fullPath(e.self._getId(e.root.schema)),e.baseId=e.baseId||e.rootId,delete e.isTop,e.dataPathArr=[void 0],o+=' var vErrors = null; ',o+=' var errors = 0; ',o+=' if (rootData === undefined) rootData = data; '}else{var p=e.level,c=e.dataLevel,y='data'+(c||'');if(n&&(e.baseId=e.resolve.url(e.baseId,n)),i&&!e.async)throw new Error('async schema in sync schema');o+=' var errs_'+p+' = errors;'}var d,g='valid'+p,v=!e.opts.allErrors,w='',S='',x=e.schema.type,k=Array.isArray(x);if(k&&1==x.length&&(x=x[0],k=!1),e.schema.$ref&&l)if('fail'==e.opts.extendRefs)throw new Error('$ref: validation keywords used in schema at path "'+e.errSchemaPath+'" (see option extendRefs)');else!0!==e.opts.extendRefs&&(l=!1,console.warn('$ref: keywords ignored in schema at path "'+e.errSchemaPath+'"'));if(x){if(e.opts.coerceTypes)var $=e.util.coerceToTypes(e.opts.coerceTypes,x);var _=e.RULES.types[x];if($||k||!0===_||_&&!a(_)){var u=e.schemaPath+'.type',f=e.errSchemaPath+'/type',u=e.schemaPath+'.type',f=e.errSchemaPath+'/type',R=k?'checkDataTypes':'checkDataType';if(o+=' if ('+e.util[R](x,y,!0)+') { ',$){var j='dataType'+p,I='coerced'+p;o+=' var '+j+' = typeof '+y+'; ','array'==e.opts.coerceTypes&&(o+=' if ('+j+' == \'object\' && Array.isArray('+y+')) '+j+' = \'array\'; '),o+=' var '+I+' = undefined; ';var O='',A=$;if(A)for(var L,Q=-1,q=A.length-1;Q<q;)L=A[Q+=1],Q&&(o+=' if ('+I+' === undefined) { ',O+='}'),'array'==e.opts.coerceTypes&&'array'!=L&&(o+=' if ('+j+' == \'array\' && '+y+'.length == 1) { '+I+' = '+y+' = '+y+'[0]; '+j+' = typeof '+y+'; } '),'string'==L?o+=' if ('+j+' == \'number\' || '+j+' == \'boolean\') '+I+' = \'\' + '+y+'; else if ('+y+' === null) '+I+' = \'\'; ':'number'==L||'integer'==L?(o+=' if ('+j+' == \'boolean\' || '+y+' === null || ('+j+' == \'string\' && '+y+' && '+y+' == +'+y+' ','integer'==L&&(o+=' && !('+y+' % 1)'),o+=')) '+I+' = +'+y+'; '):'boolean'==L?o+=' if ('+y+' === \'false\' || '+y+' === 0 || '+y+' === null) '+I+' = false; else if ('+y+' === \'true\' || '+y+' === 1) '+I+' = true; ':'null'==L?o+=' if ('+y+' === \'\' || '+y+' === 0 || '+y+' === false) '+I+' = null; ':'array'==e.opts.coerceTypes&&'array'==L&&(o+=' if ('+j+' == \'string\' || '+j+' == \'number\' || '+j+' == \'boolean\' || '+y+' == null) '+I+' = ['+y+']; ');o+=' '+O+' if ('+I+' === undefined) { ';var P=P||[];P.push(o),o='',!1===e.createErrors?o+=' {} ':(o+=' { keyword: \''+(d||'type')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(f)+' , params: { type: \'',o+=k?''+x.join(','):''+x,o+='\' } ',!1!==e.opts.messages&&(o+=' , message: \'should be ',o+=k?''+x.join(','):''+x,o+='\' '),e.opts.verbose&&(o+=' , schema: validate.schema'+u+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+y+' '),o+=' } ');var E=o;o=P.pop(),o+=!e.compositeRule&&v?e.async?' throw new ValidationError(['+E+']); ':' validate.errors = ['+E+']; return false; ':' var err = '+E+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',o+=' } else { ';var D=c?'data'+(c-1||''):'parentData',V=c?e.dataPathArr[c]:'parentDataProperty';o+=' '+y+' = '+I+'; ',c||(o+='if ('+D+' !== undefined)'),o+=' '+D+'['+V+'] = '+I+'; } '}else{var P=P||[];P.push(o),o='',!1===e.createErrors?o+=' {} ':(o+=' { keyword: \''+(d||'type')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(f)+' , params: { type: \'',o+=k?''+x.join(','):''+x,o+='\' } ',!1!==e.opts.messages&&(o+=' , message: \'should be ',o+=k?''+x.join(','):''+x,o+='\' '),e.opts.verbose&&(o+=' , schema: validate.schema'+u+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+y+' '),o+=' } ');var E=o;o=P.pop(),o+=!e.compositeRule&&v?e.async?' throw new ValidationError(['+E+']); ':' validate.errors = ['+E+']; return false; ':' var err = '+E+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '}o+=' } '}}if(e.schema.$ref&&!l)o+=' '+e.RULES.all.$ref.code(e,'$ref')+' ',v&&(o+=' } if (errors === ',o+=b?'0':'errs_'+p,o+=') { ',S+='}');else{e.opts.v5&&e.schema.patternGroups&&console.warn('keyword "patternGroups" is deprecated and disabled. Use option patternGroups: true to enable.');var N=e.RULES;if(N)for(var _,U=-1,C=N.length-1;U<C;)if(_=N[U+=1],a(_)){if(_.type&&(o+=' if ('+e.util.checkDataType(_.type,y)+') { '),e.opts.useDefaults&&!e.compositeRule)if('object'==_.type&&e.schema.properties){var m=e.schema.properties,T=Object.keys(m),M=T;if(M)for(var K,H=-1,F=M.length-1;H<F;){K=M[H+=1];var G=m[K];if(void 0!==G.default){var J=y+e.util.getProperty(K);o+=' if ('+J+' === undefined) '+J+' = ',o+='shared'==e.opts.useDefaults?' '+e.useDefault(G.default)+' ':' '+JSON.stringify(G.default)+' ',o+='; '}}}else if('array'==_.type&&Array.isArray(e.schema.items)){var z=e.schema.items;if(z)for(var G,Q=-1,B=z.length-1;Q<B;)if(G=z[Q+=1],void 0!==G.default){var J=y+'['+Q+']';o+=' if ('+J+' === undefined) '+J+' = ',o+='shared'==e.opts.useDefaults?' '+e.useDefault(G.default)+' ':' '+JSON.stringify(G.default)+' ',o+='; '}}var Y=_.rules;if(Y)for(var X,Z=-1,W=Y.length-1;Z<W;)if(X=Y[Z+=1],t(X)){var ee=X.code(e,X.keyword,_.type);ee&&(o+=' '+ee+' ',v&&(w+='}'))}if(v&&(o+=' '+w+' ',w=''),_.type&&(o+=' } ',x&&x===_.type&&!$)){o+=' else { ';var u=e.schemaPath+'.type',f=e.errSchemaPath+'/type',P=P||[];P.push(o),o='',!1===e.createErrors?o+=' {} ':(o+=' { keyword: \''+(d||'type')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(f)+' , params: { type: \'',o+=k?''+x.join(','):''+x,o+='\' } ',!1!==e.opts.messages&&(o+=' , message: \'should be ',o+=k?''+x.join(','):''+x,o+='\' '),e.opts.verbose&&(o+=' , schema: validate.schema'+u+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+y+' '),o+=' } ');var E=o;o=P.pop(),o+=!e.compositeRule&&v?e.async?' throw new ValidationError(['+E+']); ':' validate.errors = ['+E+']; return false; ':' var err = '+E+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',o+=' } '}v&&(o+=' if (errors === ',o+=b?'0':'errs_'+p,o+=') { ',S+='}')}}return v&&(o+=' '+S+' '),b?(i?(o+=' if (errors === 0) return data; ',o+=' else throw new ValidationError(vErrors); '):(o+=' validate.errors = vErrors; ',o+=' return errors === 0; '),o+=' }); return validate;'):o+=' var '+g+' = errors === errs_'+p+';',o=e.util.cleanUpCode(o),b&&(o=e.util.finalCleanUpCode(o,i)),o}},function(e){function r(e){var r=this,t=d.call(arguments,1);return new Promise(function(s,o){function i(r){var a;try{a=e.next(r)}catch(r){return o(r)}h(a)}function n(r){var a;try{a=e.throw(r)}catch(r){return o(r)}h(a)}function h(e){if(e.done)return s(e.value);var t=a.call(r,e.value);return t&&l(t)?t.then(i,n):n(new TypeError('You may only yield a function, promise, generator, array, or object, but the following object was passed: "'+(e.value+'"')))}return'function'==typeof e&&(e=e.apply(r,t)),e&&'function'==typeof e.next?void i():s(e)})}function a(e){return e?l(e)?e:n(e)||i(e)?r.call(this,e):'function'==typeof e?t.call(this,e):Array.isArray(e)?s.call(this,e):h(e)?o.call(this,e):e:e}function t(e){var r=this;return new Promise(function(a,t){e.call(r,function(e,r){return e?t(e):void(2<arguments.length&&(r=d.call(arguments,1)),a(r))})})}function s(e){return Promise.all(e.map(a,this))}function o(e){function r(e,r){t[r]=void 0,o.push(e.then(function(e){t[r]=e}))}for(var t=new e.constructor,s=Object.keys(e),o=[],n=0;n<s.length;n++){var i=s[n],h=a.call(this,e[i]);h&&l(h)?r(h,i):t[i]=e[i]}return Promise.all(o).then(function(){return t})}function l(e){return'function'==typeof e.then}function i(e){return'function'==typeof e.next&&'function'==typeof e.throw}function n(e){var r=e.constructor;return!!r&&('GeneratorFunction'===r.name||'GeneratorFunction'===r.displayName||i(r.prototype))}function h(e){return Object==e.constructor}var d=Array.prototype.slice;e.exports=r['default']=r.co=r,r.wrap=function(e){function a(){return r.call(this,e.apply(this,arguments))}return a.__generatorFunction__=e,a}},function(e){'use strict';e.exports=function(e,r){var a,t,s=' ',o=e.level,i=e.dataLevel,l=e.schema[r],n=e.schemaPath+e.util.getProperty(r),h=e.errSchemaPath+'/'+r,d=!e.opts.allErrors,p='data'+(i||''),c=e.opts.$data&&l&&l.$data;c?(s+=' var schema'+o+' = '+e.util.getData(l.$data,i,e.dataPathArr)+'; ',t='schema'+o):t=l;var m='maximum'==r,u=m?'exclusiveMaximum':'exclusiveMinimum',f=e.schema[u],v=e.opts.$data&&f&&f.$data,y=m?'<':'>',g=m?'>':'<',a=void 0;if(v){var P=e.util.getData(f.$data,i,e.dataPathArr),E='exclusive'+o,b='exclType'+o,w='exclIsNumber'+o,S='op'+o,x='\' + '+S+' + \'';s+=' var schemaExcl'+o+' = '+P+'; ',P='schemaExcl'+o,s+=' var '+E+'; var '+b+' = typeof '+P+'; if ('+b+' != \'boolean\' && '+b+' != \'undefined\' && '+b+' != \'number\') { ';var a=u,k=k||[];k.push(s),s='',!1===e.createErrors?s+=' {} ':(s+=' { keyword: \''+(a||'_exclusiveLimit')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(h)+' , params: {} ',!1!==e.opts.messages&&(s+=' , message: \''+u+' should be boolean\' '),e.opts.verbose&&(s+=' , schema: validate.schema'+n+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+p+' '),s+=' } ');var $=s;s=k.pop(),s+=!e.compositeRule&&d?e.async?' throw new ValidationError(['+$+']); ':' validate.errors = ['+$+']; return false; ':' var err = '+$+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',s+=' } else if ( ',c&&(s+=' ('+t+' !== undefined && typeof '+t+' != \'number\') || '),s+=' '+b+' == \'number\' ? ( ('+E+' = '+t+' === undefined || '+P+' '+y+'= '+t+') ? '+p+' '+g+'= '+P+' : '+p+' '+g+' '+t+' ) : ( ('+E+' = '+P+' === true) ? '+p+' '+g+'= '+t+' : '+p+' '+g+' '+t+' ) || '+p+' !== '+p+') { var op'+o+' = '+E+' ? \''+y+'\' : \''+y+'=\';'}else{var w='number'==typeof f,x=y;if(w&&c){var S='\''+x+'\'';s+=' if ( ',c&&(s+=' ('+t+' !== undefined && typeof '+t+' != \'number\') || '),s+=' ( '+t+' === undefined || '+f+' '+y+'= '+t+' ? '+p+' '+g+'= '+f+' : '+p+' '+g+' '+t+' ) || '+p+' !== '+p+') { '}else{w&&void 0===l?(E=!0,a=u,h=e.errSchemaPath+'/'+u,t=f,g+='='):(w&&(t=Math[m?'min':'max'](f,l)),f===(!w||t)?(E=!0,a=u,h=e.errSchemaPath+'/'+u,g+='='):(E=!1,x+='='));var S='\''+x+'\'';s+=' if ( ',c&&(s+=' ('+t+' !== undefined && typeof '+t+' != \'number\') || '),s+=' '+p+' '+g+' '+t+' || '+p+' !== '+p+') { '}}a=a||r;var k=k||[];k.push(s),s='',!1===e.createErrors?s+=' {} ':(s+=' { keyword: \''+(a||'_limit')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(h)+' , params: { comparison: '+S+', limit: '+t+', exclusive: '+E+' } ',!1!==e.opts.messages&&(s+=' , message: \'should be '+x+' ',s+=c?'\' + '+t:''+t+'\''),e.opts.verbose&&(s+=' , schema: ',s+=c?'validate.schema'+n:''+l,s+=' , parentSchema: validate.schema'+e.schemaPath+' , data: '+p+' '),s+=' } ');var $=s;return s=k.pop(),s+=!e.compositeRule&&d?e.async?' throw new ValidationError(['+$+']); ':' validate.errors = ['+$+']; return false; ':' var err = '+$+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',s+=' } ',d&&(s+=' else { '),s}},function(e){'use strict';e.exports=function(e,r){var a,t,s=' ',o=e.level,i=e.dataLevel,l=e.schema[r],n=e.schemaPath+e.util.getProperty(r),h=e.errSchemaPath+'/'+r,d=!e.opts.allErrors,p='data'+(i||''),c=e.opts.$data&&l&&l.$data;c?(s+=' var schema'+o+' = '+e.util.getData(l.$data,i,e.dataPathArr)+'; ',t='schema'+o):t=l;var m='maxItems'==r?'>':'<';s+='if ( ',c&&(s+=' ('+t+' !== undefined && typeof '+t+' != \'number\') || '),s+=' '+p+'.length '+m+' '+t+') { ';var u=u||[];u.push(s),s='',!1===e.createErrors?s+=' {} ':(s+=' { keyword: \''+(r||'_limitItems')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(h)+' , params: { limit: '+t+' } ',!1!==e.opts.messages&&(s+=' , message: \'should NOT have ',s+='maxItems'==r?'more':'less',s+=' than ',s+=c?'\' + '+t+' + \'':''+l,s+=' items\' '),e.opts.verbose&&(s+=' , schema: ',s+=c?'validate.schema'+n:''+l,s+=' , parentSchema: validate.schema'+e.schemaPath+' , data: '+p+' '),s+=' } ');var f=s;return s=u.pop(),s+=!e.compositeRule&&d?e.async?' throw new ValidationError(['+f+']); ':' validate.errors = ['+f+']; return false; ':' var err = '+f+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',s+='} ',d&&(s+=' else { '),s}},function(e){'use strict';e.exports=function(e,r){var a,t,s=' ',o=e.level,i=e.dataLevel,l=e.schema[r],n=e.schemaPath+e.util.getProperty(r),h=e.errSchemaPath+'/'+r,d=!e.opts.allErrors,p='data'+(i||''),c=e.opts.$data&&l&&l.$data;c?(s+=' var schema'+o+' = '+e.util.getData(l.$data,i,e.dataPathArr)+'; ',t='schema'+o):t=l;var m='maxLength'==r?'>':'<';s+='if ( ',c&&(s+=' ('+t+' !== undefined && typeof '+t+' != \'number\') || '),s+=!1===e.opts.unicode?' '+p+'.length ':' ucs2length('+p+') ',s+=' '+m+' '+t+') { ';var u=u||[];u.push(s),s='',!1===e.createErrors?s+=' {} ':(s+=' { keyword: \''+(r||'_limitLength')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(h)+' , params: { limit: '+t+' } ',!1!==e.opts.messages&&(s+=' , message: \'should NOT be ',s+='maxLength'==r?'longer':'shorter',s+=' than ',s+=c?'\' + '+t+' + \'':''+l,s+=' characters\' '),e.opts.verbose&&(s+=' , schema: ',s+=c?'validate.schema'+n:''+l,s+=' , parentSchema: validate.schema'+e.schemaPath+' , data: '+p+' '),s+=' } ');var f=s;return s=u.pop(),s+=!e.compositeRule&&d?e.async?' throw new ValidationError(['+f+']); ':' validate.errors = ['+f+']; return false; ':' var err = '+f+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',s+='} ',d&&(s+=' else { '),s}},function(e){'use strict';e.exports=function(e,r){var a,t,s=' ',o=e.level,i=e.dataLevel,l=e.schema[r],n=e.schemaPath+e.util.getProperty(r),h=e.errSchemaPath+'/'+r,d=!e.opts.allErrors,p='data'+(i||''),c=e.opts.$data&&l&&l.$data;c?(s+=' var schema'+o+' = '+e.util.getData(l.$data,i,e.dataPathArr)+'; ',t='schema'+o):t=l;var m='maxProperties'==r?'>':'<';s+='if ( ',c&&(s+=' ('+t+' !== undefined && typeof '+t+' != \'number\') || '),s+=' Object.keys('+p+').length '+m+' '+t+') { ';var u=u||[];u.push(s),s='',!1===e.createErrors?s+=' {} ':(s+=' { keyword: \''+(r||'_limitProperties')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(h)+' , params: { limit: '+t+' } ',!1!==e.opts.messages&&(s+=' , message: \'should NOT have ',s+='maxProperties'==r?'more':'less',s+=' than ',s+=c?'\' + '+t+' + \'':''+l,s+=' properties\' '),e.opts.verbose&&(s+=' , schema: ',s+=c?'validate.schema'+n:''+l,s+=' , parentSchema: validate.schema'+e.schemaPath+' , data: '+p+' '),s+=' } ');var f=s;return s=u.pop(),s+=!e.compositeRule&&d?e.async?' throw new ValidationError(['+f+']); ':' validate.errors = ['+f+']; return false; ':' var err = '+f+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',s+='} ',d&&(s+=' else { '),s}},,function(e,r,a){'use strict';function t(e){if(!(this instanceof t))return new t(e);e=this._opts=$.copy(e)||{},this._schemas={},this._refs={},this._fragments={},this._formats=w(e.format);var r=this._schemaUriFormat=this._formats['uri-reference'];this._schemaUriFormatFunc=function(e){return r.test(e)},this._cache=e.cache||new P,this._loadingSchemas={},this._compilations=[],this.RULES=S(),this._getId=n(e),e.loopRequired=e.loopRequired||Infinity,'property'==e.errorDataPath&&(e._errorDataPathProperty=!0),e.serialize===void 0&&(e.serialize=b),this._metaOpts=v(this),e.formats&&u(this),c(this),'object'==typeof e.meta&&this.addMetaSchema(e.meta),m(this),e.patternGroups&&k(this)}function s(e){var r=e._opts.meta;return e._opts.defaultMeta='object'==typeof r?e._getId(r)||r:e.getSchema(I)?I:void 0,e._opts.defaultMeta}function o(e,r){var a=g.schema.call(e,{schema:{}},r);if(a){var t=a.schema,s=a.root,o=a.baseId,i=y.call(e,t,s,void 0,o);return e._fragments[r]=new E({ref:r,fragment:!0,schema:t,root:s,baseId:o,validate:i}),i}}function i(e,r){return r=g.normalizeId(r),e._schemas[r]||e._refs[r]||e._fragments[r]}function l(e,r,a){for(var t in r){var s=r[t];s.meta||a&&!a.test(t)||(e._cache.del(s.cacheKey),delete r[t])}}function n(e){switch(e.schemaId){case'$id':return d;case'id':return h;default:return p;}}function h(e){return e.$id&&console.warn('schema $id ignored',e.$id),e.id}function d(e){return e.id&&console.warn('schema id ignored',e.id),e.$id}function p(e){if(e.$id&&e.id&&e.$id!=e.id)throw new Error('schema $id is different from id');return e.$id||e.id}function c(e){var r;if(e._opts.$data&&(r=a(54),e.addMetaSchema(r,r.$id,!0)),!1!==e._opts.meta){var t=a(55);e._opts.$data&&(t=x(t,A)),e.addMetaSchema(t,I,!0),e._refs['http://json-schema.org/schema']=I}}function m(e){var r=e._opts.schemas;if(r)if(Array.isArray(r))e.addSchema(r);else for(var a in r)e.addSchema(r[a],a)}function u(e){for(var r in e._opts.formats){var a=e._opts.formats[r];e.addFormat(r,a)}}function f(e,r){if(e._schemas[r]||e._refs[r])throw new Error('schema with key or id "'+r+'" already exists')}function v(e){for(var r=$.copy(e._opts),a=0;a<O.length;a++)delete r[O[a]];return r}var y=a(14),g=a(1),P=a(28),E=a(4),b=a(5),w=a(29),S=a(30),x=a(49),k=a(50),$=a(0),_=a(7);e.exports=t,t.prototype.validate=function(e,r){var a;if('string'!=typeof e){var t=this._addSchema(e);a=t.validate||this._compile(t)}else if(a=this.getSchema(e),!a)throw new Error('no schema with key or ref "'+e+'"');var s=a(r);return!0===a.$async?'*'==this._opts.async?_(s):s:(this.errors=a.errors,s)},t.prototype.compile=function(e,r){var a=this._addSchema(e,void 0,r);return a.validate||this._compile(a)},t.prototype.addSchema=function(e,r,a,t){if(Array.isArray(e)){for(var s=0;s<e.length;s++)this.addSchema(e[s],void 0,a,t);return}var o=this._getId(e);if(o!==void 0&&'string'!=typeof o)throw new Error('schema id must be string');r=g.normalizeId(r||o),f(this,r),this._schemas[r]=this._addSchema(e,a,t,!0)},t.prototype.addMetaSchema=function(e,r,a){this.addSchema(e,r,a,!0)},t.prototype.validateSchema=function(e,r){var a=e.$schema;if(void 0!==a&&'string'!=typeof a)throw new Error('$schema must be a string');if(a=a||this._opts.defaultMeta||s(this),!a)return console.warn('meta-schema not available'),this.errors=null,!0;var t=this._formats.uri;this._formats.uri='function'==typeof t?this._schemaUriFormatFunc:this._schemaUriFormat;var o;try{o=this.validate(a,e)}finally{this._formats.uri=t}if(!o&&r){var i='schema is invalid: '+this.errorsText();if('log'==this._opts.validateSchema)console.error(i);else throw new Error(i)}return o},t.prototype.getSchema=function(e){var r=i(this,e);switch(typeof r){case'object':return r.validate||this._compile(r);case'string':return this.getSchema(r);case'undefined':return o(this,e);}},t.prototype.removeSchema=function(e){if(e instanceof RegExp)return l(this,this._schemas,e),void l(this,this._refs,e);switch(typeof e){case'undefined':return l(this,this._schemas),l(this,this._refs),void this._cache.clear();case'string':var r=i(this,e);return r&&this._cache.del(r.cacheKey),delete this._schemas[e],void delete this._refs[e];case'object':var a=this._opts.serialize,t=a?a(e):e;this._cache.del(t);var s=this._getId(e);s&&(s=g.normalizeId(s),delete this._schemas[s],delete this._refs[s]);}},t.prototype.addFormat=function(e,r){'string'==typeof r&&(r=new RegExp(r)),this._formats[e]=r},t.prototype.errorsText=function(r,a){if(r=r||this.errors,!r)return'No errors';a=a||{};for(var t,e=void 0===a.separator?', ':a.separator,s=void 0===a.dataVar?'data':a.dataVar,o='',l=0;l<r.length;l++)t=r[l],t&&(o+=s+t.dataPath+' '+t.message+e);return o.slice(0,-e.length)},t.prototype._addSchema=function(e,r,a,t){if('object'!=typeof e&&'boolean'!=typeof e)throw new Error('schema should be object or boolean');var s=this._opts.serialize,o=s?s(e):e,i=this._cache.get(o);if(i)return i;t=t||!1!==this._opts.addUsedSchema;var l=g.normalizeId(this._getId(e));l&&t&&f(this,l);var n,h=!1!==this._opts.validateSchema&&!r;h&&!(n=l&&l==g.normalizeId(e.$schema))&&this.validateSchema(e,!0);var d=g.ids.call(this,e),p=new E({id:l,schema:e,localRefs:d,cacheKey:o,meta:a});return'#'!=l[0]&&t&&(this._refs[l]=p),this._cache.put(o,p),h&&n&&this.validateSchema(e,!0),p},t.prototype._compile=function(e,r){function a(){var r=e.validate,t=r.apply(null,arguments);return a.errors=r.errors,t}if(e.compiling)return e.validate=a,a.schema=e.schema,a.errors=null,a.root=r?r:a,!0===e.schema.$async&&(a.$async=!0),a;e.compiling=!0;var t;e.meta&&(t=this._opts,this._opts=this._metaOpts);var s;try{s=y.call(this,e.schema,r,e.localRefs)}finally{e.compiling=!1,e.meta&&(this._opts=t)}return e.validate=s,e.refs=s.refs,e.refVal=s.refVal,e.root=s.root,s},t.prototype.compileAsync=a(51);var R=a(52);t.prototype.addKeyword=R.add,t.prototype.getKeyword=R.get,t.prototype.removeKeyword=R.remove;var j=a(3);t.ValidationError=j.Validation,t.MissingRefError=j.MissingRef,t.$dataMetaSchema=x;var I='http://json-schema.org/draft-06/schema',O=['removeAdditional','useDefaults','coerceTypes'],A=['/properties']},function(e,r,a){'use strict';function t(e,r,a,l){function b(){var e=C.validate,r=e.apply(null,arguments);return b.errors=e.errors,r}function w(e,a,s,o){var l=!a||a&&a.schema==e;if(a.schema!=r.schema)return t.call(I,e,a,s,o);var f=!0===e.$async,b=v({isTop:!0,schema:e,isRoot:l,baseId:o,root:a,schemaPath:'',errSchemaPath:'#',errorPath:'""',MissingRefError:u.MissingRef,RULES:M,validate:v,util:m,resolve:c,resolveRef:S,usePattern:_,useDefault:R,useCustomRule:j,opts:O,formats:T,self:I});b=p(A,h)+p(Q,i)+p(D,n)+p(N,d)+b,O.processCode&&(b=O.processCode(b));var w;try{var x=new Function('self','RULES','formats','root','refVal','defaults','customRules','co','equal','ucs2length','ValidationError',b);w=x(I,M,T,r,A,D,N,y,P,g,E),A[0]=w}catch(r){throw console.error('Error compiling schema, function code:',b),r}return w.schema=e,w.errors=null,w.refs=L,w.refVal=A,w.root=l?w:a,f&&(w.$async=!0),!0===O.sourceCode&&(w.source={code:b,patterns:Q,defaults:D}),w}function S(e,s,o){s=c.url(e,s);var i,l,n=L[s];if(void 0!==n)return i=A[n],l='refVal['+n+']',$(i,l);if(!o&&r.refs){var h=r.refs[s];if(void 0!==h)return i=r.refVal[h],l=x(s,i),$(i,l)}l=x(s);var d=c.call(I,w,r,s);if(d===void 0){var p=a&&a[s];p&&(d=c.inlineRef(p,O.inlineRefs)?p:t.call(I,p,r,a,e))}return void 0===d?void 0:(k(s,d),$(d,l))}function x(e,r){var a=A.length;return A[a]=r,L[e]=a,'refVal'+a}function k(e,r){var a=L[e];A[a]=r}function $(e,r){return'object'==typeof e||'boolean'==typeof e?{code:r,schema:e,inline:!0}:{code:r,$async:e&&e.$async}}function _(e){var r=q[e];return void 0===r&&(r=q[e]=Q.length,Q[r]=e),'pattern'+r}function R(e){switch(typeof e){case'boolean':case'number':return''+e;case'string':return m.toQuotedString(e);case'object':if(null===e)return'null';var r=f(e),a=V[r];return void 0===a&&(a=V[r]=D.length,D[a]=e),'default'+a;}}function j(e,r,a,t){var s=e.definition.validateSchema;if(s&&!1!==I._opts.validateSchema){var o=s(r);if(!o){var i='keyword schema is invalid: '+I.errorsText(s.errors);if('log'==I._opts.validateSchema)console.error(i);else throw new Error(i)}}var l,n=e.definition.compile,h=e.definition.inline,d=e.definition.macro;if(n)l=n.call(I,r,a,t);else if(d)l=d.call(I,r,a,t),!1!==O.validateSchema&&I.validateSchema(l,!0);else if(h)l=h.call(I,t,e.keyword,r,a);else if(l=e.definition.validate,!l)return;if(void 0===l)throw new Error('custom keyword "'+e.keyword+'"failed to compile');var p=N.length;return N[p]=l,{code:'customRule'+p,validate:l}}var I=this,O=this._opts,A=[void 0],L={},Q=[],q={},D=[],V={},N=[];r=r||{schema:e,refVal:A,refs:L};var U=s.call(this,e,r,l),C=this._compilations[U.index];if(U.compiling)return C.callValidate=b;var T=this._formats,M=this.RULES;try{var K=w(e,r,a,l);C.validate=K;var H=C.callValidate;return H&&(H.schema=K.schema,H.errors=null,H.refs=K.refs,H.refVal=K.refVal,H.root=K.root,H.$async=K.$async,O.sourceCode&&(H.source=K.source)),K}finally{o.call(this,e,r,l)}}function s(e,r,a){var t=l.call(this,e,r,a);return 0<=t?{index:t,compiling:!0}:(t=this._compilations.length,this._compilations[t]={schema:e,root:r,baseId:a},{index:t,compiling:!1})}function o(e,r,a){var t=l.call(this,e,r,a);0<=t&&this._compilations.splice(t,1)}function l(e,r,a){for(var t,s=0;s<this._compilations.length;s++)if(t=this._compilations[s],t.schema==e&&t.root==r&&t.baseId==a)return s;return-1}function i(e,r){return'var pattern'+e+' = new RegExp('+m.toQuotedString(r[e])+');'}function n(e){return'var default'+e+' = defaults['+e+'];'}function h(e,r){return r[e]===void 0?'':'var refVal'+e+' = refVal['+e+'];'}function d(e){return'var customRule'+e+' = customRules['+e+'];'}function p(e,r){if(!e.length)return'';for(var a='',t=0;t<e.length;t++)a+=r(t,e);return a}var c=a(1),m=a(0),u=a(3),f=a(5),v=a(6),y=a(7),g=m.ucs2length,P=a(2),E=u.Validation;e.exports=t},function(e,r,a){'use strict';function t(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function s(e,r,a){if(e&&n.isObject(e)&&e instanceof t)return e;var s=new t;return s.parse(e,r,a),s}var o=a(16),n=a(19);r.parse=s,r.resolve=function(e,r){return s(e,!1,!0).resolve(r)},r.resolveObject=function(e,r){return e?s(e,!1,!0).resolveObject(r):r},r.format=function(e){return n.isString(e)&&(e=s(e)),e instanceof t?e.format():t.prototype.format.call(e)},r.Url=t;var d=/^([a-z0-9.+-]+:)/i,i=/:[0-9]*$/,l=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,h=['{','}','|','\\','^','`'].concat(['<','>','"','`',' ','\r','\n','\t']),p=['\''].concat(h),c=['%','/','?',';','#'].concat(p),m=['/','?','#'],u=/^[+a-z0-9A-Z_-]{0,63}$/,f=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,v={javascript:!0,"javascript:":!0},y={javascript:!0,"javascript:":!0},g={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},P=a(20);t.prototype.parse=function(e,r,a){if(!n.isString(e))throw new TypeError('Parameter \'url\' must be a string, not '+typeof e);var t=e.indexOf('?'),E=-1!==t&&t<e.indexOf('#')?'?':'#',b=e.split(E),w=/\\/g;b[0]=b[0].replace(w,'/'),e=b.join(E);var S=e;if(S=S.trim(),!a&&1===e.split('#').length){var x=l.exec(S);if(x)return this.path=S,this.href=S,this.pathname=x[1],x[2]?(this.search=x[2],this.query=r?P.parse(this.search.substr(1)):this.search.substr(1)):r&&(this.search='',this.query={}),this}var $=d.exec(S);if($){$=$[0];var _=$.toLowerCase();this.protocol=_,S=S.substr($.length)}if(a||$||S.match(/^\/\/[^@\/]+@[^@\/]+/)){var R='//'===S.substr(0,2);R&&!($&&y[$])&&(S=S.substr(2),this.slashes=!0)}if(!y[$]&&(R||$&&!g[$])){for(var I,O=-1,A=0;A<m.length;A++)I=S.indexOf(m[A]),-1!==I&&(-1==O||I<O)&&(O=I);var i,L;L=-1===O?S.lastIndexOf('@'):S.lastIndexOf('@',O),-1!==L&&(i=S.slice(0,L),S=S.slice(L+1),this.auth=decodeURIComponent(i)),O=-1;for(var I,A=0;A<c.length;A++)I=S.indexOf(c[A]),-1!==I&&(-1===O||I<O)&&(O=I);-1===O&&(O=S.length),this.host=S.slice(0,O),S=S.slice(O),this.parseHost(),this.hostname=this.hostname||'';var Q='['===this.hostname[0]&&']'===this.hostname[this.hostname.length-1];if(!Q)for(var q,D=this.hostname.split(/\./),A=0,V=D.length;A<V;A++)if(q=D[A],q&&!q.match(u)){for(var N='',U=0,j=q.length;U<j;U++)N+=127<q.charCodeAt(U)?'x':q[U];if(!N.match(u)){var k=D.slice(0,A),C=D.slice(A+1),T=q.match(f);T&&(k.push(T[1]),C.unshift(T[2])),C.length&&(S='/'+C.join('.')+S),this.hostname=k.join('.');break}}this.hostname=this.hostname.length>255?'':this.hostname.toLowerCase(),Q||(this.hostname=o.toASCII(this.hostname));var M=this.port?':'+this.port:'',K=this.hostname||'';this.host=K+M,this.href+=this.host,Q&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),'/'!==S[0]&&(S='/'+S))}if(!v[_])for(var h,A=0,V=p.length;A<V;A++)if(h=p[A],-1!==S.indexOf(h)){var H=encodeURIComponent(h);H===h&&(H=escape(h)),S=S.split(h).join(H)}var F=S.indexOf('#');-1!==F&&(this.hash=S.substr(F),S=S.slice(0,F));var G=S.indexOf('?');if(-1===G?r&&(this.search='',this.query={}):(this.search=S.substr(G),this.query=S.substr(G+1),r&&(this.query=P.parse(this.query)),S=S.slice(0,G)),S&&(this.pathname=S),g[_]&&this.hostname&&!this.pathname&&(this.pathname='/'),this.pathname||this.search){var M=this.pathname||'',J=this.search||'';this.path=M+J}return this.href=this.format(),this},t.prototype.format=function(){var e=this.auth||'';e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,':'),e+='@');var r=this.protocol||'',a=this.pathname||'',t=this.hash||'',s=!1,o='';this.host?s=e+this.host:this.hostname&&(s=e+(-1===this.hostname.indexOf(':')?this.hostname:'['+this.hostname+']'),this.port&&(s+=':'+this.port)),this.query&&n.isObject(this.query)&&Object.keys(this.query).length&&(o=P.stringify(this.query));var i=this.search||o&&'?'+o||'';return r&&':'!==r.substr(-1)&&(r+=':'),this.slashes||(!r||g[r])&&!1!==s?(s='//'+(s||''),a&&'/'!==a.charAt(0)&&(a='/'+a)):!s&&(s=''),t&&'#'!==t.charAt(0)&&(t='#'+t),i&&'?'!==i.charAt(0)&&(i='?'+i),a=a.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),i=i.replace('#','%23'),r+s+a+i+t},t.prototype.resolve=function(e){return this.resolveObject(s(e,!1,!0)).format()},t.prototype.resolveObject=function(e){if(n.isString(e)){var r=new t;r.parse(e,!1,!0),e=r}for(var a,o=new t,l=Object.keys(this),h=0;h<l.length;h++)a=l[h],o[a]=this[a];if(o.hash=e.hash,''===e.href)return o.href=o.format(),o;if(e.slashes&&!e.protocol){for(var d,c=Object.keys(e),m=0;m<c.length;m++)d=c[m],'protocol'!==d&&(o[d]=e[d]);return g[o.protocol]&&o.hostname&&!o.pathname&&(o.path=o.pathname='/'),o.href=o.format(),o}if(e.protocol&&e.protocol!==o.protocol){if(!g[e.protocol]){for(var u,f=Object.keys(e),P=0;P<f.length;P++)u=f[P],o[u]=e[u];return o.href=o.format(),o}if(o.protocol=e.protocol,!e.host&&!y[e.protocol]){for(var v=(e.pathname||'').split('/');v.length&&!(e.host=v.shift()););e.host||(e.host=''),e.hostname||(e.hostname=''),''!==v[0]&&v.unshift(''),2>v.length&&v.unshift(''),o.pathname=v.join('/')}else o.pathname=e.pathname;if(o.search=e.search,o.query=e.query,o.host=e.host||'',o.auth=e.auth,o.hostname=e.hostname||e.host,o.port=e.port,o.pathname||o.search){var E=o.pathname||'',p=o.search||'';o.path=E+p}return o.slashes=o.slashes||e.slashes,o.href=o.format(),o}var s=o.pathname&&'/'===o.pathname.charAt(0),b=e.host||e.pathname&&'/'===e.pathname.charAt(0),w=b||s||o.host&&e.pathname,S=w,x=o.pathname&&o.pathname.split('/')||[],v=e.pathname&&e.pathname.split('/')||[],k=o.protocol&&!g[o.protocol];if(k&&(o.hostname='',o.port=null,o.host&&(''===x[0]?x[0]=o.host:x.unshift(o.host)),o.host='',e.protocol&&(e.hostname=null,e.port=null,e.host&&(''===v[0]?v[0]=e.host:v.unshift(e.host)),e.host=null),w=w&&(''===v[0]||''===x[0])),b)o.host=e.host||''===e.host?e.host:o.host,o.hostname=e.hostname||''===e.hostname?e.hostname:o.hostname,o.search=e.search,o.query=e.query,x=v;else if(v.length)x||(x=[]),x.pop(),x=x.concat(v),o.search=e.search,o.query=e.query;else if(!n.isNullOrUndefined(e.search)){if(k){o.hostname=o.host=x.shift();var $=o.host&&0<o.host.indexOf('@')&&o.host.split('@');$&&(o.auth=$.shift(),o.host=o.hostname=$.shift())}return o.search=e.search,o.query=e.query,n.isNull(o.pathname)&&n.isNull(o.search)||(o.path=(o.pathname?o.pathname:'')+(o.search?o.search:'')),o.href=o.format(),o}if(!x.length)return o.pathname=null,o.path=o.search?'/'+o.search:null,o.href=o.format(),o;for(var _=x.slice(-1)[0],R=(o.host||e.host||1<x.length)&&('.'===_||'..'===_)||''===_,j=0,I=x.length;0<=I;I--)_=x[I],'.'===_?x.splice(I,1):'..'===_?(x.splice(I,1),j++):j&&(x.splice(I,1),j--);if(!w&&!S)for(;j--;j)x.unshift('..');w&&''!==x[0]&&(!x[0]||'/'!==x[0].charAt(0))&&x.unshift(''),R&&'/'!==x.join('/').substr(-1)&&x.push('');var i=''===x[0]||x[0]&&'/'===x[0].charAt(0);if(k){o.hostname=o.host=i?'':x.length?x.shift():'';var $=o.host&&0<o.host.indexOf('@')&&o.host.split('@');$&&(o.auth=$.shift(),o.host=o.hostname=$.shift())}return w=w||o.host&&x.length,w&&!i&&x.unshift(''),x.length?o.pathname=x.join('/'):(o.pathname=null,o.path=null),n.isNull(o.pathname)&&n.isNull(o.search)||(o.path=(o.pathname?o.pathname:'')+(o.search?o.search:'')),o.auth=e.auth||o.auth,o.slashes=o.slashes||e.slashes,o.href=o.format(),o},t.prototype.parseHost=function(){var e=this.host,r=i.exec(e);r&&(r=r[0],':'!==r&&(this.port=r.substr(1)),e=e.substr(0,e.length-r.length)),e&&(this.hostname=e)}},function(e,r,a){(function(e,t){var s;(function(o){function l(e){throw new RangeError(A[e])}function i(e,r){for(var a=e.length,t=[];a--;)t[a]=r(e[a]);return t}function n(e,r){var a=e.split('@'),t='';1<a.length&&(t=a[0]+'@',e=a[1]),e=e.replace(O,'.');var s=e.split('.'),o=i(s,r).join('.');return t+o}function h(e){for(var r,a,t=[],s=0,o=e.length;s<o;)r=e.charCodeAt(s++),55296<=r&&56319>=r&&s<o?(a=e.charCodeAt(s++),56320==(64512&a)?t.push(((1023&r)<<10)+(1023&a)+65536):(t.push(r),s--)):t.push(r);return t}function d(e){return i(e,function(e){var r='';return 65535<e&&(e-=65536,r+=D(55296|1023&e>>>10),e=56320|1023&e),r+=D(e),r}).join('')}function p(e){return 10>e-48?e-22:26>e-65?e-65:26>e-97?e-97:b}function c(e,r){return e+22+75*(26>e)-((0!=r)<<5)}function m(e,r,a){var t=0;for(e=a?Q(e/$):e>>1,e+=Q(e/r);e>L*x>>1;t+=b)e=Q(e/L);return Q(t+(L+1)*e/(e+w))}function u(e){var r,a,s,o,h,c,u,f,v,t,y=[],g=e.length,P=0,i=R,n=_;for(a=e.lastIndexOf(I),0>a&&(a=0),s=0;s<a;++s)128<=e.charCodeAt(s)&&l('not-basic'),y.push(e.charCodeAt(s));for(o=0<a?a+1:0;o<g;){for(h=P,c=1,u=b;;u+=b){if(o>=g&&l('invalid-input'),f=p(e.charCodeAt(o++)),(f>=b||f>Q((E-P)/c))&&l('overflow'),P+=f*c,v=u<=n?S:u>=n+x?x:u-n,f<v)break;t=b-v,c>Q(E/t)&&l('overflow'),c*=t}r=y.length+1,n=m(P-h,r,0==h),Q(P/r)>E-i&&l('overflow'),i+=Q(P/r),P%=r,y.splice(P++,0,i)}return d(y)}function f(e){var r,a,s,o,i,n,d,p,u,f,t,v,y,g,P,w=[];for(e=h(e),v=e.length,r=R,a=0,i=_,n=0;n<v;++n)t=e[n],128>t&&w.push(D(t));for(s=o=w.length,o&&w.push(I);s<v;){for(d=E,n=0;n<v;++n)t=e[n],t>=r&&t<d&&(d=t);for(y=s+1,d-r>Q((E-a)/y)&&l('overflow'),a+=(d-r)*y,r=d,n=0;n<v;++n)if(t=e[n],t<r&&++a>E&&l('overflow'),t==r){for(p=a,u=b;;u+=b){if(f=u<=i?S:u>=i+x?x:u-i,p<f)break;P=p-f,g=b-f,w.push(D(c(f+P%g,0))),p=Q(P/g)}w.push(D(c(p,0))),i=m(a,y,s==o),a=0,++s}++a,++r}return w.join('')}var v='object'==typeof r&&r&&!r.nodeType&&r,y='object'==typeof e&&e&&!e.nodeType&&e,g='object'==typeof t&&t;(g.global===g||g.window===g||g.self===g)&&(o=g);var P,E=2147483647,b=36,S=1,x=26,w=38,$=700,_=72,R=128,I='-',k=/^xn--/,j=/[^\x20-\x7E]/,O=/[\x2E\u3002\uFF0E\uFF61]/g,A={overflow:'Overflow: input needs wider integers to process',"not-basic":'Illegal input >= 0x80 (not a basic code point)',"invalid-input":'Invalid input'},L=b-S,Q=Math.floor,D=String.fromCharCode;P={version:'1.4.1',ucs2:{decode:h,encode:d},decode:u,encode:f,toASCII:function(e){return n(e,function(e){return j.test(e)?'xn--'+f(e):e})},toUnicode:function(e){return n(e,function(e){return k.test(e)?u(e.slice(4).toLowerCase()):e})}};s=function(){return P}.call(r,a,r,e),!(void 0!==s&&(e.exports=s))})(this)}).call(r,a(17)(e),a(18))},function(e){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],!e.children&&(e.children=[]),Object.defineProperty(e,'loaded',{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,'id',{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e){var r=function(){return this}();try{r=r||Function('return this')()||(1,eval)('this')}catch(a){'object'==typeof window&&(r=window)}e.exports=r},function(e){'use strict';e.exports={isString:function(e){return'string'==typeof e},isObject:function(e){return'object'==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}}},function(e,r,a){'use strict';r.decode=r.parse=a(21),r.encode=r.stringify=a(22)},function(e){'use strict';function r(e,r){return Object.prototype.hasOwnProperty.call(e,r)}e.exports=function(e,t,s,o){t=t||'&',s=s||'=';var l={};if('string'!=typeof e||0===e.length)return l;var n=/\+/g;e=e.split(t);var h=1e3;o&&'number'==typeof o.maxKeys&&(h=o.maxKeys);var d=e.length;0<h&&d>h&&(d=h);for(var p=0;p<d;++p){var i,c,m,u,f=e[p].replace(n,'%20'),v=f.indexOf(s);0<=v?(i=f.substr(0,v),c=f.substr(v+1)):(i=f,c=''),m=decodeURIComponent(i),u=decodeURIComponent(c),r(l,m)?a(l[m])?l[m].push(u):l[m]=[l[m],u]:l[m]=u}return l};var a=Array.isArray||function(e){return'[object Array]'===Object.prototype.toString.call(e)}},function(e){'use strict';function r(e,r){if(e.map)return e.map(r);for(var a=[],t=0;t<e.length;t++)a.push(r(e[t],t));return a}var a=function(e){switch(typeof e){case'string':return e;case'boolean':return e?'true':'false';case'number':return isFinite(e)?e:'';default:return'';}};e.exports=function(e,o,i,l){return o=o||'&',i=i||'=',null===e&&(e=void 0),'object'==typeof e?r(s(e),function(s){var l=encodeURIComponent(a(s))+i;return t(e[s])?r(e[s],function(e){return l+encodeURIComponent(a(e))}).join(o):l+encodeURIComponent(a(e[s]))}).join(o):l?encodeURIComponent(a(l))+i+encodeURIComponent(a(e)):''};var t=Array.isArray||function(e){return'[object Array]'===Object.prototype.toString.call(e)},s=Object.keys||function(e){var r=[];for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&r.push(a);return r}},function(e){'use strict';e.exports=function(e){for(var r,a=0,t=e.length,s=0;s<t;)a++,r=e.charCodeAt(s++),55296<=r&&56319>=r&&s<t&&(r=e.charCodeAt(s),56320==(64512&r)&&s++);return a}},function(e){'use strict';function r(e,s,o,l,n,h,d,p,c){if(o&&'object'==typeof o&&!Array.isArray(o))for(var m in s(o,l,n,h,d,p,c),o){var u=o[m];if(Array.isArray(u)){if(m in t.arrayKeywords)for(var f=0;f<u.length;f++)r(e,s,u[f],l+'/'+m+'/'+f,n,l,m,o,f);}else if(!(m in t.propsKeywords))(m in t.keywords||e.allKeys&&!(m in t.skipKeywords))&&r(e,s,u,l+'/'+m,n,l,m,o);else if(u&&'object'==typeof u)for(var i in u)r(e,s,u[i],l+'/'+m+'/'+a(i),n,l,m,o,i)}}function a(e){return e.replace(/~/g,'~0').replace(/\//g,'~1')}var t=e.exports=function(e,a,t){'function'==typeof a&&(t=a,a={}),r(a,t,e,'',e)};t.keywords={additionalItems:!0,items:!0,contains:!0,additionalProperties:!0,propertyNames:!0,not:!0},t.arrayKeywords={items:!0,allOf:!0,anyOf:!0,oneOf:!0},t.propsKeywords={definitions:!0,properties:!0,patternProperties:!0,dependencies:!0},t.skipKeywords={enum:!0,const:!0,required:!0,maximum:!0,minimum:!0,exclusiveMaximum:!0,exclusiveMinimum:!0,multipleOf:!0,maxLength:!0,minLength:!0,pattern:!0,format:!0,maxItems:!0,minItems:!0,uniqueItems:!0,maxProperties:!0,minProperties:!0}},function(e,r,a){r.parse=a(26),r.stringify=a(27)},function(e){var r,a,t,s,o={'"':'"',"\\":'\\',"/":'/',b:'\b',f:'\f',n:'\n',r:'\r',t:'\t'},l=function(e){throw{name:'SyntaxError',message:e,at:r,text:t}},n=function(e){return e&&e!==a&&l('Expected \''+e+'\' instead of \''+a+'\''),a=t.charAt(r),r+=1,a},i=function(){var e,r='';for('-'===a&&(r='-',n('-'));'0'<=a&&'9'>=a;)r+=a,n();if('.'===a)for(r+='.';n()&&'0'<=a&&'9'>=a;)r+=a;if('e'===a||'E'===a)for(r+=a,n(),('-'===a||'+'===a)&&(r+=a,n());'0'<=a&&'9'>=a;)r+=a,n();return e=+r,isFinite(e)?e:void l('Bad number')},h=function(){var e,r,t,s='';if('"'===a)for(;n();){if('"'===a)return n(),s;if('\\'!==a)s+=a;else if(n(),'u'===a){for(t=0,r=0;4>r&&(e=parseInt(n(),16),!!isFinite(e));r+=1)t=16*t+e;s+=String.fromCharCode(t)}else if('string'==typeof o[a])s+=o[a];else break}l('Bad string')},d=function(){for(;a&&' '>=a;)n()},p=function(){switch(a){case't':return n('t'),n('r'),n('u'),n('e'),!0;case'f':return n('f'),n('a'),n('l'),n('s'),n('e'),!1;case'n':return n('n'),n('u'),n('l'),n('l'),null;}l('Unexpected \''+a+'\'')},c=function(){var e=[];if('['===a){if(n('['),d(),']'===a)return n(']'),e;for(;a;){if(e.push(s()),d(),']'===a)return n(']'),e;n(','),d()}}l('Bad array')},m=function(){var e,r={};if('{'===a){if(n('{'),d(),'}'===a)return n('}'),r;for(;a;){if(e=h(),d(),n(':'),Object.hasOwnProperty.call(r,e)&&l('Duplicate key "'+e+'"'),r[e]=s(),d(),'}'===a)return n('}'),r;n(','),d()}}l('Bad object')};s=function(){return d(),'{'===a?m():'['===a?c():'"'===a?h():'-'===a?i():'0'<=a&&'9'>=a?i():p()},e.exports=function(e,o){var i;return t=e,r=0,a=' ',i=s(),d(),a&&l('Syntax error'),'function'==typeof o?function e(r,a){var t,s,i=r[a];if(i&&'object'==typeof i)for(t in i)Object.prototype.hasOwnProperty.call(i,t)&&(s=e(i,t),void 0===s?delete i[t]:i[t]=s);return o.call(r,a,i)}({"":i},''):i}},function(e){function r(e){return l.lastIndex=0,l.test(e)?'"'+e.replace(l,function(e){var r=n[e];return'string'==typeof r?r:'\\u'+('0000'+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function a(e,l){var n,i,h,d,p,c=t,m=l[e];switch(m&&'object'==typeof m&&'function'==typeof m.toJSON&&(m=m.toJSON(e)),'function'==typeof o&&(m=o.call(l,e,m)),typeof m){case'string':return r(m);case'number':return isFinite(m)?m+'':'null';case'boolean':case'null':return m+'';case'object':if(!m)return'null';if(t+=s,p=[],'[object Array]'===Object.prototype.toString.apply(m)){for(d=m.length,n=0;n<d;n+=1)p[n]=a(n,m)||'null';return h=0===p.length?'[]':t?'[\n'+t+p.join(',\n'+t)+'\n'+c+']':'['+p.join(',')+']',t=c,h}if(o&&'object'==typeof o)for(d=o.length,n=0;n<d;n+=1)i=o[n],'string'==typeof i&&(h=a(i,m),h&&p.push(r(i)+(t?': ':':')+h));else for(i in m)Object.prototype.hasOwnProperty.call(m,i)&&(h=a(i,m),h&&p.push(r(i)+(t?': ':':')+h));return h=0===p.length?'{}':t?'{\n'+t+p.join(',\n'+t)+'\n'+c+'}':'{'+p.join(',')+'}',t=c,h;}}var t,s,o,i=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,l=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,n={"":'\\b'," ":'\\t',"\n":'\\n',"":'\\f',"\r":'\\r','"':'\\"',"\\":'\\\\'};e.exports=function(e,r,l){var n;if(t='',s='','number'==typeof l)for(n=0;n<l;n+=1)s+=' ';else'string'==typeof l&&(s=l);if(o=r,r&&'function'!=typeof r&&('object'!=typeof r||'number'!=typeof r.length))throw new Error('JSON.stringify');return a('',{"":e})}},function(e){'use strict';var r=e.exports=function(){this._cache={}};r.prototype.put=function(e,r){this._cache[e]=r},r.prototype.get=function(e){return this._cache[e]},r.prototype.del=function(e){delete this._cache[e]},r.prototype.clear=function(){this._cache={}}},function(e,r,a){'use strict';function t(e){return e='full'==e?'full':'fast',l.copy(t[e])}function s(e){var r=e.match(n);if(!r)return!1;var a=+r[1],t=+r[2];return 1<=a&&12>=a&&1<=t&&t<=h[a]}function o(e,r){var a=e.match(d);if(!a)return!1;var t=a[1],s=a[2],o=a[3],i=a[5];return 23>=t&&59>=s&&59>=o&&(!r||i)}function i(e){if(b.test(e))return!1;try{return new RegExp(e),!0}catch(r){return!1}}var l=a(0),n=/^\d\d\d\d-(\d\d)-(\d\d)$/,h=[0,31,29,31,30,31,30,31,31,30,31,30,31],d=/^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d:\d\d)?$/i,p=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*$/i,c=/^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,m=/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,u=/^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i,f=/^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i,v=/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,y=/^(?:\/(?:[^~/]|~0|~1)*)*$|^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,g=/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;e.exports=t,t.fast={date:/^\d\d\d\d-[0-1]\d-[0-3]\d$/,time:/^[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i,"date-time":/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s][0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i,uri:/^(?:[a-z][a-z0-9+-.]*)(?::|\/)\/?[^\s]*$/i,"uri-reference":/^(?:(?:[a-z][a-z0-9+-.]*:)?\/\/)?[^\s]*$/i,"uri-template":u,url:f,email:/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i,hostname:p,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:i,uuid:v,"json-pointer":y,"relative-json-pointer":g},t.full={date:s,time:o,"date-time":function(e){var r=e.split(P);return 2==r.length&&s(r[0])&&o(r[1],!0)},uri:function(e){return E.test(e)&&c.test(e)},"uri-reference":m,"uri-template":u,url:f,email:/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&''*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:function(e){return 255>=e.length&&p.test(e)},ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:i,uuid:v,"json-pointer":y,"relative-json-pointer":g};var P=/t|\s/i,E=/\/|:/,b=/[^\\]\\Z/},function(e,r,a){'use strict';var t=a(31),s=a(0).toHash;e.exports=function(){var e=[{type:'number',rules:[{maximum:['exclusiveMaximum']},{minimum:['exclusiveMinimum']},'multipleOf','format']},{type:'string',rules:['maxLength','minLength','pattern','format']},{type:'array',rules:['maxItems','minItems','uniqueItems','contains','items']},{type:'object',rules:['maxProperties','minProperties','required','dependencies','propertyNames',{properties:['additionalProperties','patternProperties']}]},{rules:['$ref','const','enum','not','anyOf','oneOf','allOf']}],r=['type'];return e.all=s(r),e.types=s(['number','integer','string','array','object','boolean','null']),e.forEach(function(a){a.rules=a.rules.map(function(a){var s;if('object'==typeof a){var o=Object.keys(a)[0];s=a[o],a=o,s.forEach(function(a){r.push(a),e.all[a]=!0})}r.push(a);var i=e.all[a]={keyword:a,code:t[a],implements:s};return i}),a.type&&(e.types[a.type]=a)}),e.keywords=s(r.concat(['additionalItems','$schema','id','title','description','default','definitions'])),e.custom={},e}},function(e,r,a){'use strict';e.exports={$ref:a(32),allOf:a(33),anyOf:a(34),const:a(35),contains:a(36),dependencies:a(37),enum:a(38),format:a(39),items:a(40),maximum:a(8),minimum:a(8),maxItems:a(9),minItems:a(9),maxLength:a(10),minLength:a(10),maxProperties:a(11),minProperties:a(11),multipleOf:a(41),not:a(42),oneOf:a(43),pattern:a(44),properties:a(45),propertyNames:a(46),required:a(47),uniqueItems:a(48),validate:a(6)}},function(e){'use strict';e.exports=function(e,r){var a,t,s=' ',o=e.level,i=e.dataLevel,l=e.schema[r],n=e.errSchemaPath+'/'+r,h=!e.opts.allErrors,d='data'+(i||''),p='valid'+o;if('#'==l||'#/'==l)e.isRoot?(a=e.async,t='validate'):(a=!0===e.root.schema.$async,t='root.refVal[0]');else{var c=e.resolveRef(e.baseId,l,e.isRoot);if(c===void 0){var m=e.MissingRefError.message(e.baseId,l);if('fail'==e.opts.missingRefs){console.error(m);var u=u||[];u.push(s),s='',!1===e.createErrors?s+=' {} ':(s+=' { keyword: \'$ref\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { ref: \''+e.util.escapeQuotes(l)+'\' } ',!1!==e.opts.messages&&(s+=' , message: \'can\\\'t resolve reference '+e.util.escapeQuotes(l)+'\' '),e.opts.verbose&&(s+=' , schema: '+e.util.toQuotedString(l)+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),s+=' } ');var f=s;s=u.pop(),s+=!e.compositeRule&&h?e.async?' throw new ValidationError(['+f+']); ':' validate.errors = ['+f+']; return false; ':' var err = '+f+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',h&&(s+=' if (false) { ')}else if('ignore'==e.opts.missingRefs)console.warn(m),h&&(s+=' if (true) { ');else throw new e.MissingRefError(e.baseId,l,m)}else if(c.inline){var v=e.util.copy(e);v.level++;var y='valid'+v.level;v.schema=c.schema,v.schemaPath='',v.errSchemaPath=l;var g=e.validate(v).replace(/validate\.schema/g,c.code);s+=' '+g+' ',h&&(s+=' if ('+y+') { ')}else a=!0===c.$async,t=c.code}if(t){var u=u||[];u.push(s),s='',s+=e.opts.passContext?' '+t+'.call(this, ':' '+t+'( ',s+=' '+d+', (dataPath || \'\')','""'!=e.errorPath&&(s+=' + '+e.errorPath);var P=i?'data'+(i-1||''):'parentData',E=i?e.dataPathArr[i]:'parentDataProperty';s+=' , '+P+' , '+E+', rootData) ';var b=s;if(s=u.pop(),a){if(!e.async)throw new Error('async schema referenced by sync schema');h&&(s+=' var '+p+'; '),s+=' try { '+e.yieldAwait+' '+b+'; ',h&&(s+=' '+p+' = true; '),s+=' } catch (e) { if (!(e instanceof ValidationError)) throw e; if (vErrors === null) vErrors = e.errors; else vErrors = vErrors.concat(e.errors); errors = vErrors.length; ',h&&(s+=' '+p+' = false; '),s+=' } ',h&&(s+=' if ('+p+') { ')}else s+=' if (!'+b+') { if (vErrors === null) vErrors = '+t+'.errors; else vErrors = vErrors.concat('+t+'.errors); errors = vErrors.length; } ',h&&(s+=' else { ')}return s}},function(e){'use strict';e.exports=function(e,r){var a=' ',t=e.schema[r],s=e.schemaPath+e.util.getProperty(r),o=e.errSchemaPath+'/'+r,i=!e.opts.allErrors,l=e.util.copy(e),n='';l.level++;var h='valid'+l.level,d=l.baseId,p=!0,c=t;if(c)for(var m,u=-1,f=c.length-1;u<f;)m=c[u+=1],e.util.schemaHasRules(m,e.RULES.all)&&(p=!1,l.schema=m,l.schemaPath=s+'['+u+']',l.errSchemaPath=o+'/'+u,a+=' '+e.validate(l)+' ',l.baseId=d,i&&(a+=' if ('+h+') { ',n+='}'));return i&&(p?a+=' if (true) { ':a+=' '+n.slice(0,-1)+' '),a=e.util.cleanUpCode(a),a}},function(e){'use strict';e.exports=function(e,r){var a=' ',t=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),l=e.errSchemaPath+'/'+r,n=!e.opts.allErrors,h='valid'+t,d='errs__'+t,p=e.util.copy(e),c='';p.level++;var m='valid'+p.level,u=o.every(function(r){return e.util.schemaHasRules(r,e.RULES.all)});if(u){var f=p.baseId;a+=' var '+d+' = errors; var '+h+' = false; ';var v=e.compositeRule;e.compositeRule=p.compositeRule=!0;var y=o;if(y)for(var g,P=-1,E=y.length-1;P<E;)g=y[P+=1],p.schema=g,p.schemaPath=i+'['+P+']',p.errSchemaPath=l+'/'+P,a+=' '+e.validate(p)+' ',p.baseId=f,a+=' '+h+' = '+h+' || '+m+'; if (!'+h+') { ',c+='}';e.compositeRule=p.compositeRule=v,a+=' '+c+' if (!'+h+') { var err = ',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'anyOf\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: {} ',!1!==e.opts.messages&&(a+=' , message: \'should match some schema in anyOf\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+('data'+(s||''))+' '),a+=' } '),a+='; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',!e.compositeRule&&n&&(e.async?a+=' throw new ValidationError(vErrors); ':a+=' validate.errors = vErrors; return false; '),a+=' } else { errors = '+d+'; if (vErrors !== null) { if ('+d+') vErrors.length = '+d+'; else vErrors = null; } ',e.opts.allErrors&&(a+=' } '),a=e.util.cleanUpCode(a)}else n&&(a+=' if (true) { ');return a}},function(e){'use strict';e.exports=function(e,r){var a,t=' ',s=e.level,o=e.dataLevel,i=e.schema[r],l=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+'/'+r,h=!e.opts.allErrors,d='data'+(o||''),p='valid'+s,c=e.opts.$data&&i&&i.$data;c?(t+=' var schema'+s+' = '+e.util.getData(i.$data,o,e.dataPathArr)+'; ',a='schema'+s):a=i,c||(t+=' var schema'+s+' = validate.schema'+l+';'),t+='var '+p+' = equal('+d+', schema'+s+'); if (!'+p+') { ';var m=m||[];m.push(t),t='',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'const\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: {} ',!1!==e.opts.messages&&(t+=' , message: \'should be equal to constant\' '),e.opts.verbose&&(t+=' , schema: validate.schema'+l+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } ');var u=t;return t=m.pop(),t+=!e.compositeRule&&h?e.async?' throw new ValidationError(['+u+']); ':' validate.errors = ['+u+']; return false; ':' var err = '+u+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',t+=' }',h&&(t+=' else { '),t}},function(e){'use strict';e.exports=function(e,r){var a=' ',t=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),l=e.errSchemaPath+'/'+r,n=!e.opts.allErrors,h='data'+(s||''),d='errs__'+t,p=e.util.copy(e);p.level++;var c='valid'+p.level,m='i'+t,u=p.dataLevel=e.dataLevel+1,f='data'+u,v=e.baseId,y=e.util.schemaHasRules(o,e.RULES.all);if(a+='var '+d+' = errors;var '+('valid'+t)+';',y){var g=e.compositeRule;e.compositeRule=p.compositeRule=!0,p.schema=o,p.schemaPath=i,p.errSchemaPath=l,a+=' var '+c+' = false; for (var '+m+' = 0; '+m+' < '+h+'.length; '+m+'++) { ',p.errorPath=e.util.getPathExpr(e.errorPath,m,e.opts.jsonPointers,!0);var P=h+'['+m+']';p.dataPathArr[u]=m;var E=e.validate(p);p.baseId=v,a+=2>e.util.varOccurences(E,f)?' '+e.util.varReplace(E,f,P)+' ':' var '+f+' = '+P+'; '+E+' ',a+=' if ('+c+') break; } ',e.compositeRule=p.compositeRule=g,a+=' '+''+' if (!'+c+') {'}else a+=' if ('+h+'.length == 0) {';var b=b||[];b.push(a),a='',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'contains\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: {} ',!1!==e.opts.messages&&(a+=' , message: \'should contain a valid item\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } ');var w=a;return a=b.pop(),a+=!e.compositeRule&&n?e.async?' throw new ValidationError(['+w+']); ':' validate.errors = ['+w+']; return false; ':' var err = '+w+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',a+=' } else { ',y&&(a+=' errors = '+d+'; if (vErrors !== null) { if ('+d+') vErrors.length = '+d+'; else vErrors = null; } '),e.opts.allErrors&&(a+=' } '),a=e.util.cleanUpCode(a),a}},function(e){'use strict';e.exports=function(e,r){var a=' ',t=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),l=e.errSchemaPath+'/'+r,n=!e.opts.allErrors,h='data'+(s||''),d='errs__'+t,p=e.util.copy(e),c='';p.level++;var m='valid'+p.level,u={},f={},v=e.opts.ownProperties;for(E in o){var y=o[E],g=Array.isArray(y)?f:u;g[E]=y}a+='var '+d+' = errors;';var P=e.errorPath;for(var E in a+='var missing'+t+';',f)if(g=f[E],g.length){if(a+=' if ( '+h+e.util.getProperty(E)+' !== undefined ',v&&(a+=' && Object.prototype.hasOwnProperty.call('+h+', \''+e.util.escapeQuotes(E)+'\') '),n){a+=' && ( ';var b=g;if(b)for(var w,S=-1,x=b.length-1;S<x;){w=b[S+=1],S&&(a+=' || ');var k=e.util.getProperty(w),$=h+k;a+=' ( ( '+$+' === undefined ',v&&(a+=' || ! Object.prototype.hasOwnProperty.call('+h+', \''+e.util.escapeQuotes(w)+'\') '),a+=') && (missing'+t+' = '+e.util.toQuotedString(e.opts.jsonPointers?w:k)+') ) '}a+=')) { ';var _='missing'+t,R='\' + '+_+' + \'';e.opts._errorDataPathProperty&&(e.errorPath=e.opts.jsonPointers?e.util.getPathExpr(P,_,!0):P+' + '+_);var j=j||[];j.push(a),a='',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'dependencies\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: { property: \''+e.util.escapeQuotes(E)+'\', missingProperty: \''+R+'\', depsCount: '+g.length+', deps: \''+e.util.escapeQuotes(1==g.length?g[0]:g.join(', '))+'\' } ',!1!==e.opts.messages&&(a+=' , message: \'should have ',a+=1==g.length?'property '+e.util.escapeQuotes(g[0]):'properties '+e.util.escapeQuotes(g.join(', ')),a+=' when property '+e.util.escapeQuotes(E)+' is present\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } ');var I=a;a=j.pop(),a+=!e.compositeRule&&n?e.async?' throw new ValidationError(['+I+']); ':' validate.errors = ['+I+']; return false; ':' var err = '+I+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '}else{a+=' ) { ';var O=g;if(O)for(var w,A=-1,L=O.length-1;A<L;){w=O[A+=1];var k=e.util.getProperty(w),R=e.util.escapeQuotes(w),$=h+k;e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPath(P,w,e.opts.jsonPointers)),a+=' if ( '+$+' === undefined ',v&&(a+=' || ! Object.prototype.hasOwnProperty.call('+h+', \''+e.util.escapeQuotes(w)+'\') '),a+=') { var err = ',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'dependencies\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: { property: \''+e.util.escapeQuotes(E)+'\', missingProperty: \''+R+'\', depsCount: '+g.length+', deps: \''+e.util.escapeQuotes(1==g.length?g[0]:g.join(', '))+'\' } ',!1!==e.opts.messages&&(a+=' , message: \'should have ',a+=1==g.length?'property '+e.util.escapeQuotes(g[0]):'properties '+e.util.escapeQuotes(g.join(', ')),a+=' when property '+e.util.escapeQuotes(E)+' is present\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } '),a+='; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } '}}a+=' } ',n&&(c+='}',a+=' else { ')}e.errorPath=P;var Q=p.baseId;for(var E in u){var y=u[E];e.util.schemaHasRules(y,e.RULES.all)&&(a+=' '+m+' = true; if ( '+h+e.util.getProperty(E)+' !== undefined ',v&&(a+=' && Object.prototype.hasOwnProperty.call('+h+', \''+e.util.escapeQuotes(E)+'\') '),a+=') { ',p.schema=y,p.schemaPath=i+e.util.getProperty(E),p.errSchemaPath=l+'/'+e.util.escapeFragment(E),a+=' '+e.validate(p)+' ',p.baseId=Q,a+=' } ',n&&(a+=' if ('+m+') { ',c+='}'))}return n&&(a+=' '+c+' if ('+d+' == errors) {'),a=e.util.cleanUpCode(a),a}},function(e){'use strict';e.exports=function(e,r){var a,t=' ',s=e.level,o=e.dataLevel,i=e.schema[r],l=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+'/'+r,h=!e.opts.allErrors,d='data'+(o||''),p='valid'+s,c=e.opts.$data&&i&&i.$data;c?(t+=' var schema'+s+' = '+e.util.getData(i.$data,o,e.dataPathArr)+'; ',a='schema'+s):a=i;var m='i'+s,u='schema'+s;c||(t+=' var '+u+' = validate.schema'+l+';'),t+='var '+p+';',c&&(t+=' if (schema'+s+' === undefined) '+p+' = true; else if (!Array.isArray(schema'+s+')) '+p+' = false; else {'),t+=''+p+' = false;for (var '+m+'=0; '+m+'<'+u+'.length; '+m+'++) if (equal('+d+', '+u+'['+m+'])) { '+p+' = true; break; }',c&&(t+=' } '),t+=' if (!'+p+') { ';var f=f||[];f.push(t),t='',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'enum\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { allowedValues: schema'+s+' } ',!1!==e.opts.messages&&(t+=' , message: \'should be equal to one of the allowed values\' '),e.opts.verbose&&(t+=' , schema: validate.schema'+l+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } ');var v=t;return t=f.pop(),t+=!e.compositeRule&&h?e.async?' throw new ValidationError(['+v+']); ':' validate.errors = ['+v+']; return false; ':' var err = '+v+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',t+=' }',h&&(t+=' else { '),t}},function(e){'use strict';e.exports=function(e,r,a){var t=' ',s=e.level,o=e.dataLevel,i=e.schema[r],l=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+'/'+r,h=!e.opts.allErrors,d='data'+(o||'');if(!1===e.opts.format)return h&&(t+=' if (true) { '),t;var p,c=e.opts.$data&&i&&i.$data;c?(t+=' var schema'+s+' = '+e.util.getData(i.$data,o,e.dataPathArr)+'; ',p='schema'+s):p=i;var m=e.opts.unknownFormats,u=Array.isArray(m);if(c){var f='format'+s,v='isObject'+s,y='formatType'+s;t+=' var '+f+' = formats['+p+']; var '+v+' = typeof '+f+' == \'object\' && !('+f+' instanceof RegExp) && '+f+'.validate; var '+y+' = '+v+' && '+f+'.type || \'string\'; if ('+v+') { ',e.async&&(t+=' var async'+s+' = '+f+'.async; '),t+=' '+f+' = '+f+'.validate; } if ( ',c&&(t+=' ('+p+' !== undefined && typeof '+p+' != \'string\') || '),t+=' (','ignore'!=m&&(t+=' ('+p+' && !'+f+' ',u&&(t+=' && self._opts.unknownFormats.indexOf('+p+') == -1 '),t+=') || '),t+=' ('+f+' && '+y+' == \''+a+'\' && !(typeof '+f+' == \'function\' ? ',t+=e.async?' (async'+s+' ? '+e.yieldAwait+' '+f+'('+d+') : '+f+'('+d+')) ':' '+f+'('+d+') ',t+=' : '+f+'.test('+d+'))))) {'}else{var f=e.formats[i];if(!f){if('ignore'==m)return console.warn('unknown format "'+i+'" ignored in schema at path "'+e.errSchemaPath+'"'),h&&(t+=' if (true) { '),t;if(u&&0<=m.indexOf(i))return h&&(t+=' if (true) { '),t;throw new Error('unknown format "'+i+'" is used in schema at path "'+e.errSchemaPath+'"')}var v='object'==typeof f&&!(f instanceof RegExp)&&f.validate,y=v&&f.type||'string';if(v){var g=!0===f.async;f=f.validate}if(y!=a)return h&&(t+=' if (true) { '),t;if(g){if(!e.async)throw new Error('async format in sync schema');var P='formats'+e.util.getProperty(i)+'.validate';t+=' if (!('+e.yieldAwait+' '+P+'('+d+'))) { '}else{t+=' if (! ';var P='formats'+e.util.getProperty(i);v&&(P+='.validate'),t+='function'==typeof f?' '+P+'('+d+') ':' '+P+'.test('+d+') ',t+=') { '}}var E=E||[];E.push(t),t='',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'format\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { format: ',t+=c?''+p:''+e.util.toQuotedString(i),t+=' } ',!1!==e.opts.messages&&(t+=' , message: \'should match format "',t+=c?'\' + '+p+' + \'':''+e.util.escapeQuotes(i),t+='"\' '),e.opts.verbose&&(t+=' , schema: ',t+=c?'validate.schema'+l:''+e.util.toQuotedString(i),t+=' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } ');var b=t;return t=E.pop(),t+=!e.compositeRule&&h?e.async?' throw new ValidationError(['+b+']); ':' validate.errors = ['+b+']; return false; ':' var err = '+b+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',t+=' } ',h&&(t+=' else { '),t}},function(e){'use strict';e.exports=function(e,r){var a=' ',t=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),l=e.errSchemaPath+'/'+r,n=!e.opts.allErrors,h='data'+(s||''),d='valid'+t,p='errs__'+t,c=e.util.copy(e),m='';c.level++;var u='valid'+c.level,f='i'+t,v=c.dataLevel=e.dataLevel+1,y='data'+v,g=e.baseId;if(a+='var '+p+' = errors;var '+d+';',Array.isArray(o)){var P=e.schema.additionalItems;if(!1===P){a+=' '+d+' = '+h+'.length <= '+o.length+'; ';var E=l;l=e.errSchemaPath+'/additionalItems',a+=' if (!'+d+') { ';var b=b||[];b.push(a),a='',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'additionalItems\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: { limit: '+o.length+' } ',!1!==e.opts.messages&&(a+=' , message: \'should NOT have more than '+o.length+' items\' '),e.opts.verbose&&(a+=' , schema: false , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } ');var w=a;a=b.pop(),a+=!e.compositeRule&&n?e.async?' throw new ValidationError(['+w+']); ':' validate.errors = ['+w+']; return false; ':' var err = '+w+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',a+=' } ',l=E,n&&(m+='}',a+=' else { ')}var S=o;if(S)for(var x,k=-1,$=S.length-1;k<$;)if(x=S[k+=1],e.util.schemaHasRules(x,e.RULES.all)){a+=' '+u+' = true; if ('+h+'.length > '+k+') { ';var _=h+'['+k+']';c.schema=x,c.schemaPath=i+'['+k+']',c.errSchemaPath=l+'/'+k,c.errorPath=e.util.getPathExpr(e.errorPath,k,e.opts.jsonPointers,!0),c.dataPathArr[v]=k;var R=e.validate(c);c.baseId=g,a+=2>e.util.varOccurences(R,y)?' '+e.util.varReplace(R,y,_)+' ':' var '+y+' = '+_+'; '+R+' ',a+=' } ',n&&(a+=' if ('+u+') { ',m+='}')}if('object'==typeof P&&e.util.schemaHasRules(P,e.RULES.all)){c.schema=P,c.schemaPath=e.schemaPath+'.additionalItems',c.errSchemaPath=e.errSchemaPath+'/additionalItems',a+=' '+u+' = true; if ('+h+'.length > '+o.length+') { for (var '+f+' = '+o.length+'; '+f+' < '+h+'.length; '+f+'++) { ',c.errorPath=e.util.getPathExpr(e.errorPath,f,e.opts.jsonPointers,!0);var _=h+'['+f+']';c.dataPathArr[v]=f;var R=e.validate(c);c.baseId=g,a+=2>e.util.varOccurences(R,y)?' '+e.util.varReplace(R,y,_)+' ':' var '+y+' = '+_+'; '+R+' ',n&&(a+=' if (!'+u+') break; '),a+=' } } ',n&&(a+=' if ('+u+') { ',m+='}')}}else if(e.util.schemaHasRules(o,e.RULES.all)){c.schema=o,c.schemaPath=i,c.errSchemaPath=l,a+=' for (var '+f+' = '+0+'; '+f+' < '+h+'.length; '+f+'++) { ',c.errorPath=e.util.getPathExpr(e.errorPath,f,e.opts.jsonPointers,!0);var _=h+'['+f+']';c.dataPathArr[v]=f;var R=e.validate(c);c.baseId=g,a+=2>e.util.varOccurences(R,y)?' '+e.util.varReplace(R,y,_)+' ':' var '+y+' = '+_+'; '+R+' ',n&&(a+=' if (!'+u+') break; '),a+=' }'}return n&&(a+=' '+m+' if ('+p+' == errors) {'),a=e.util.cleanUpCode(a),a}},function(e){'use strict';e.exports=function(e,r){var a,t=' ',s=e.level,o=e.dataLevel,i=e.schema[r],l=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+'/'+r,h=!e.opts.allErrors,d='data'+(o||''),p=e.opts.$data&&i&&i.$data;p?(t+=' var schema'+s+' = '+e.util.getData(i.$data,o,e.dataPathArr)+'; ',a='schema'+s):a=i,t+='var division'+s+';if (',p&&(t+=' '+a+' !== undefined && ( typeof '+a+' != \'number\' || '),t+=' (division'+s+' = '+d+' / '+a+', ',t+=e.opts.multipleOfPrecision?' Math.abs(Math.round(division'+s+') - division'+s+') > 1e-'+e.opts.multipleOfPrecision+' ':' division'+s+' !== parseInt(division'+s+') ',t+=' ) ',p&&(t+=' ) '),t+=' ) { ';var c=c||[];c.push(t),t='',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'multipleOf\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { multipleOf: '+a+' } ',!1!==e.opts.messages&&(t+=' , message: \'should be multiple of ',t+=p?'\' + '+a:''+a+'\''),e.opts.verbose&&(t+=' , schema: ',t+=p?'validate.schema'+l:''+i,t+=' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } ');var m=t;return t=c.pop(),t+=!e.compositeRule&&h?e.async?' throw new ValidationError(['+m+']); ':' validate.errors = ['+m+']; return false; ':' var err = '+m+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',t+='} ',h&&(t+=' else { '),t}},function(e){'use strict';e.exports=function(e,r){var a=' ',t=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),l=e.errSchemaPath+'/'+r,n=!e.opts.allErrors,h='data'+(s||''),d='errs__'+t,p=e.util.copy(e);p.level++;var c='valid'+p.level;if(e.util.schemaHasRules(o,e.RULES.all)){p.schema=o,p.schemaPath=i,p.errSchemaPath=l,a+=' var '+d+' = errors; ';var m=e.compositeRule;e.compositeRule=p.compositeRule=!0,p.createErrors=!1;var u;p.opts.allErrors&&(u=p.opts.allErrors,p.opts.allErrors=!1),a+=' '+e.validate(p)+' ',p.createErrors=!0,u&&(p.opts.allErrors=u),e.compositeRule=p.compositeRule=m,a+=' if ('+c+') { ';var f=f||[];f.push(a),a='',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'not\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: {} ',!1!==e.opts.messages&&(a+=' , message: \'should NOT be valid\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } ');var v=a;a=f.pop(),a+=!e.compositeRule&&n?e.async?' throw new ValidationError(['+v+']); ':' validate.errors = ['+v+']; return false; ':' var err = '+v+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',a+=' } else { errors = '+d+'; if (vErrors !== null) { if ('+d+') vErrors.length = '+d+'; else vErrors = null; } ',e.opts.allErrors&&(a+=' } ')}else a+=' var err = ',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'not\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: {} ',!1!==e.opts.messages&&(a+=' , message: \'should NOT be valid\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } '),a+='; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',n&&(a+=' if (false) { ');return a}},function(e){'use strict';e.exports=function(e,r){var a=' ',t=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),l=e.errSchemaPath+'/'+r,n=!e.opts.allErrors,h='valid'+t,d='errs__'+t,p=e.util.copy(e),c='';p.level++;var m='valid'+p.level;a+='var '+d+' = errors;var prevValid'+t+' = false;var '+h+' = false;';var u=p.baseId,f=e.compositeRule;e.compositeRule=p.compositeRule=!0;var v=o;if(v)for(var y,g=-1,P=v.length-1;g<P;)y=v[g+=1],e.util.schemaHasRules(y,e.RULES.all)?(p.schema=y,p.schemaPath=i+'['+g+']',p.errSchemaPath=l+'/'+g,a+=' '+e.validate(p)+' ',p.baseId=u):a+=' var '+m+' = true; ',g&&(a+=' if ('+m+' && prevValid'+t+') '+h+' = false; else { ',c+='}'),a+=' if ('+m+') '+h+' = prevValid'+t+' = true;';return e.compositeRule=p.compositeRule=f,a+=''+c+'if (!'+h+') { var err = ',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'oneOf\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: {} ',!1!==e.opts.messages&&(a+=' , message: \'should match exactly one schema in oneOf\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+('data'+(s||''))+' '),a+=' } '),a+='; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',!e.compositeRule&&n&&(e.async?a+=' throw new ValidationError(vErrors); ':a+=' validate.errors = vErrors; return false; '),a+='} else { errors = '+d+'; if (vErrors !== null) { if ('+d+') vErrors.length = '+d+'; else vErrors = null; }',e.opts.allErrors&&(a+=' } '),a}},function(e){'use strict';e.exports=function(e,r){var a,t=' ',s=e.level,o=e.dataLevel,i=e.schema[r],l=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+'/'+r,h=!e.opts.allErrors,d='data'+(o||''),p=e.opts.$data&&i&&i.$data;p?(t+=' var schema'+s+' = '+e.util.getData(i.$data,o,e.dataPathArr)+'; ',a='schema'+s):a=i;var c=p?'(new RegExp('+a+'))':e.usePattern(i);t+='if ( ',p&&(t+=' ('+a+' !== undefined && typeof '+a+' != \'string\') || '),t+=' !'+c+'.test('+d+') ) { ';var m=m||[];m.push(t),t='',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'pattern\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { pattern: ',t+=p?''+a:''+e.util.toQuotedString(i),t+=' } ',!1!==e.opts.messages&&(t+=' , message: \'should match pattern "',t+=p?'\' + '+a+' + \'':''+e.util.escapeQuotes(i),t+='"\' '),e.opts.verbose&&(t+=' , schema: ',t+=p?'validate.schema'+l:''+e.util.toQuotedString(i),t+=' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } ');var u=t;return t=m.pop(),t+=!e.compositeRule&&h?e.async?' throw new ValidationError(['+u+']); ':' validate.errors = ['+u+']; return false; ':' var err = '+u+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',t+='} ',h&&(t+=' else { '),t}},function(e){'use strict';e.exports=function(e,r){var a=' ',t=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),l=e.errSchemaPath+'/'+r,n=!e.opts.allErrors,h='data'+(s||''),d='valid'+t,p='errs__'+t,c=e.util.copy(e),m='';c.level++;var u='valid'+c.level,f='key'+t,v='idx'+t,y=c.dataLevel=e.dataLevel+1,g='data'+y,P='dataProperties'+t,E=Object.keys(o||{}),b=e.schema.patternProperties||{},w=Object.keys(b),S=e.schema.additionalProperties,x=E.length||w.length,k=!1===S,$='object'==typeof S&&Object.keys(S).length,_=e.opts.removeAdditional,R=e.opts.ownProperties,j=e.baseId,I=e.schema.required;if(I&&!(e.opts.v5&&I.$data)&&I.length<e.opts.loopRequired)var O=e.util.toHash(I);if(e.opts.patternGroups)var A=e.schema.patternGroups||{},L=Object.keys(A);if(a+='var '+p+' = errors;var '+u+' = true;',R&&(a+=' var '+P+' = undefined;'),k||$||_){if(a+=R?' '+P+' = '+P+' || Object.keys('+h+'); for (var '+v+'=0; '+v+'<'+P+'.length; '+v+'++) { var '+f+' = '+P+'['+v+']; ':' for (var '+f+' in '+h+') { ',x){if(a+=' var isAdditional'+t+' = !(false ',E.length)if(5<E.length)a+=' || validate.schema'+i+'['+f+'] ';else{var Q=E;if(Q)for(var q,D=-1,V=Q.length-1;D<V;)q=Q[D+=1],a+=' || '+f+' == '+e.util.toQuotedString(q)+' '}if(w.length){var N=w;if(N)for(var U,C=-1,T=N.length-1;C<T;)U=N[C+=1],a+=' || '+e.usePattern(U)+'.test('+f+') '}if(e.opts.patternGroups&&L.length){var M=L;if(M)for(var K,C=-1,H=M.length-1;C<H;)K=M[C+=1],a+=' || '+e.usePattern(K)+'.test('+f+') '}a+=' ); if (isAdditional'+t+') { '}if('all'==_)a+=' delete '+h+'['+f+']; ';else{var F=e.errorPath;if(e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPathExpr(e.errorPath,f,e.opts.jsonPointers)),k){if(_)a+=' delete '+h+'['+f+']; ';else{a+=' '+u+' = false; ';var G=l;l=e.errSchemaPath+'/additionalProperties';var J=J||[];J.push(a),a='',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'additionalProperties\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: { additionalProperty: \''+('\' + '+f+' + \'')+'\' } ',!1!==e.opts.messages&&(a+=' , message: \'should NOT have additional properties\' '),e.opts.verbose&&(a+=' , schema: false , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } ');var z=a;a=J.pop(),a+=!e.compositeRule&&n?e.async?' throw new ValidationError(['+z+']); ':' validate.errors = ['+z+']; return false; ':' var err = '+z+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',l=G,n&&(a+=' break; ')}}else if($)if('failing'==_){a+=' var '+p+' = errors; ';var B=e.compositeRule;e.compositeRule=c.compositeRule=!0,c.schema=S,c.schemaPath=e.schemaPath+'.additionalProperties',c.errSchemaPath=e.errSchemaPath+'/additionalProperties',c.errorPath=e.opts._errorDataPathProperty?e.errorPath:e.util.getPathExpr(e.errorPath,f,e.opts.jsonPointers);var Y=h+'['+f+']';c.dataPathArr[y]=f;var X=e.validate(c);c.baseId=j,a+=2>e.util.varOccurences(X,g)?' '+e.util.varReplace(X,g,Y)+' ':' var '+g+' = '+Y+'; '+X+' ',a+=' if (!'+u+') { errors = '+p+'; if (validate.errors !== null) { if (errors) validate.errors.length = errors; else validate.errors = null; } delete '+h+'['+f+']; } ',e.compositeRule=c.compositeRule=B}else{c.schema=S,c.schemaPath=e.schemaPath+'.additionalProperties',c.errSchemaPath=e.errSchemaPath+'/additionalProperties',c.errorPath=e.opts._errorDataPathProperty?e.errorPath:e.util.getPathExpr(e.errorPath,f,e.opts.jsonPointers);var Y=h+'['+f+']';c.dataPathArr[y]=f;var X=e.validate(c);c.baseId=j,a+=2>e.util.varOccurences(X,g)?' '+e.util.varReplace(X,g,Y)+' ':' var '+g+' = '+Y+'; '+X+' ',n&&(a+=' if (!'+u+') break; ')}e.errorPath=F}x&&(a+=' } '),a+=' } ',n&&(a+=' if ('+u+') { ',m+='}')}var Z=e.opts.useDefaults&&!e.compositeRule;if(E.length){var W=E;if(W)for(var q,ee=-1,re=W.length-1;ee<re;){q=W[ee+=1];var ae=o[q];if(e.util.schemaHasRules(ae,e.RULES.all)){var te=e.util.getProperty(q),Y=h+te,se=Z&&void 0!==ae.default;c.schema=ae,c.schemaPath=i+te,c.errSchemaPath=l+'/'+e.util.escapeFragment(q),c.errorPath=e.util.getPath(e.errorPath,q,e.opts.jsonPointers),c.dataPathArr[y]=e.util.toQuotedString(q);var X=e.validate(c);if(c.baseId=j,2>e.util.varOccurences(X,g)){X=e.util.varReplace(X,g,Y);var oe=Y}else{var oe=g;a+=' var '+g+' = '+Y+'; '}if(se)a+=' '+X+' ';else{if(O&&O[q]){a+=' if ( '+oe+' === undefined ',R&&(a+=' || ! Object.prototype.hasOwnProperty.call('+h+', \''+e.util.escapeQuotes(q)+'\') '),a+=') { '+u+' = false; ';var F=e.errorPath,G=l,ie=e.util.escapeQuotes(q);e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPath(F,q,e.opts.jsonPointers)),l=e.errSchemaPath+'/required';var J=J||[];J.push(a),a='',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'required\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: { missingProperty: \''+ie+'\' } ',!1!==e.opts.messages&&(a+=' , message: \'',a+=e.opts._errorDataPathProperty?'is a required property':'should have required property \\\''+ie+'\\\'',a+='\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } ');var z=a;a=J.pop(),a+=!e.compositeRule&&n?e.async?' throw new ValidationError(['+z+']); ':' validate.errors = ['+z+']; return false; ':' var err = '+z+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',l=G,e.errorPath=F,a+=' } else { '}else n?(a+=' if ( '+oe+' === undefined ',R&&(a+=' || ! Object.prototype.hasOwnProperty.call('+h+', \''+e.util.escapeQuotes(q)+'\') '),a+=') { '+u+' = true; } else { '):(a+=' if ('+oe+' !== undefined ',R&&(a+=' && Object.prototype.hasOwnProperty.call('+h+', \''+e.util.escapeQuotes(q)+'\') '),a+=' ) { ');a+=' '+X+' } '}}n&&(a+=' if ('+u+') { ',m+='}')}}if(w.length){var le=w;if(le)for(var U,ne=-1,he=le.length-1;ne<he;){U=le[ne+=1];var ae=b[U];if(e.util.schemaHasRules(ae,e.RULES.all)){c.schema=ae,c.schemaPath=e.schemaPath+'.patternProperties'+e.util.getProperty(U),c.errSchemaPath=e.errSchemaPath+'/patternProperties/'+e.util.escapeFragment(U),a+=R?' '+P+' = '+P+' || Object.keys('+h+'); for (var '+v+'=0; '+v+'<'+P+'.length; '+v+'++) { var '+f+' = '+P+'['+v+']; ':' for (var '+f+' in '+h+') { ',a+=' if ('+e.usePattern(U)+'.test('+f+')) { ',c.errorPath=e.util.getPathExpr(e.errorPath,f,e.opts.jsonPointers);var Y=h+'['+f+']';c.dataPathArr[y]=f;var X=e.validate(c);c.baseId=j,a+=2>e.util.varOccurences(X,g)?' '+e.util.varReplace(X,g,Y)+' ':' var '+g+' = '+Y+'; '+X+' ',n&&(a+=' if (!'+u+') break; '),a+=' } ',n&&(a+=' else '+u+' = true; '),a+=' } ',n&&(a+=' if ('+u+') { ',m+='}')}}}if(e.opts.patternGroups&&L.length){var de=L;if(de)for(var K,pe=-1,ce=de.length-1;pe<ce;){K=de[pe+=1];var me=A[K],ae=me.schema;if(e.util.schemaHasRules(ae,e.RULES.all)){c.schema=ae,c.schemaPath=e.schemaPath+'.patternGroups'+e.util.getProperty(K)+'.schema',c.errSchemaPath=e.errSchemaPath+'/patternGroups/'+e.util.escapeFragment(K)+'/schema',a+=' var pgPropCount'+t+' = 0; ',a+=R?' '+P+' = '+P+' || Object.keys('+h+'); for (var '+v+'=0; '+v+'<'+P+'.length; '+v+'++) { var '+f+' = '+P+'['+v+']; ':' for (var '+f+' in '+h+') { ',a+=' if ('+e.usePattern(K)+'.test('+f+')) { pgPropCount'+t+'++; ',c.errorPath=e.util.getPathExpr(e.errorPath,f,e.opts.jsonPointers);var Y=h+'['+f+']';c.dataPathArr[y]=f;var X=e.validate(c);c.baseId=j,a+=2>e.util.varOccurences(X,g)?' '+e.util.varReplace(X,g,Y)+' ':' var '+g+' = '+Y+'; '+X+' ',n&&(a+=' if (!'+u+') break; '),a+=' } ',n&&(a+=' else '+u+' = true; '),a+=' } ',n&&(a+=' if ('+u+') { ',m+='}');var ue=me.minimum,fe=me.maximum;if(void 0!==ue||void 0!==fe){a+=' var '+d+' = true; ';var G=l;if(void 0!==ue){var ve=ue,ye='minimum',ge='less';a+=' '+d+' = pgPropCount'+t+' >= '+ue+'; ',l=e.errSchemaPath+'/patternGroups/minimum',a+=' if (!'+d+') { ';var J=J||[];J.push(a),a='',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'patternGroups\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: { reason: \''+ye+'\', limit: '+ve+', pattern: \''+e.util.escapeQuotes(K)+'\' } ',!1!==e.opts.messages&&(a+=' , message: \'should NOT have '+ge+' than '+ve+' properties matching pattern "'+e.util.escapeQuotes(K)+'"\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } ');var z=a;a=J.pop(),a+=!e.compositeRule&&n?e.async?' throw new ValidationError(['+z+']); ':' validate.errors = ['+z+']; return false; ':' var err = '+z+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',a+=' } ',void 0!==fe&&(a+=' else ')}if(void 0!==fe){var ve=fe,ye='maximum',ge='more';a+=' '+d+' = pgPropCount'+t+' <= '+fe+'; ',l=e.errSchemaPath+'/patternGroups/maximum',a+=' if (!'+d+') { ';var J=J||[];J.push(a),a='',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'patternGroups\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: { reason: \''+ye+'\', limit: '+ve+', pattern: \''+e.util.escapeQuotes(K)+'\' } ',!1!==e.opts.messages&&(a+=' , message: \'should NOT have '+ge+' than '+ve+' properties matching pattern "'+e.util.escapeQuotes(K)+'"\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } ');var z=a;a=J.pop(),a+=!e.compositeRule&&n?e.async?' throw new ValidationError(['+z+']); ':' validate.errors = ['+z+']; return false; ':' var err = '+z+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',a+=' } '}l=G,n&&(a+=' if ('+d+') { ',m+='}')}}}}return n&&(a+=' '+m+' if ('+p+' == errors) {'),a=e.util.cleanUpCode(a),a}},function(e){'use strict';e.exports=function(e,r){var a=' ',t=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),l=e.errSchemaPath+'/'+r,n=!e.opts.allErrors,h='data'+(s||''),d='errs__'+t,p=e.util.copy(e);p.level++;var c='valid'+p.level;if(e.util.schemaHasRules(o,e.RULES.all)){p.schema=o,p.schemaPath=i,p.errSchemaPath=l;var m='key'+t,u='idx'+t,f='i'+t,v='\' + '+m+' + \'',y=p.dataLevel=e.dataLevel+1,g='data'+y,P='dataProperties'+t,E=e.opts.ownProperties,b=e.baseId;a+=' var '+d+' = errors; ',E&&(a+=' var '+P+' = undefined; '),a+=E?' '+P+' = '+P+' || Object.keys('+h+'); for (var '+u+'=0; '+u+'<'+P+'.length; '+u+'++) { var '+m+' = '+P+'['+u+']; ':' for (var '+m+' in '+h+') { ',a+=' var startErrs'+t+' = errors; ';var w=m,S=e.compositeRule;e.compositeRule=p.compositeRule=!0;var x=e.validate(p);p.baseId=b,a+=2>e.util.varOccurences(x,g)?' '+e.util.varReplace(x,g,w)+' ':' var '+g+' = '+w+'; '+x+' ',e.compositeRule=p.compositeRule=S,a+=' if (!'+c+') { for (var '+f+'=startErrs'+t+'; '+f+'<errors; '+f+'++) { vErrors['+f+'].propertyName = '+m+'; } var err = ',!1===e.createErrors?a+=' {} ':(a+=' { keyword: \'propertyNames\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(l)+' , params: { propertyName: \''+v+'\' } ',!1!==e.opts.messages&&(a+=' , message: \'property name \\\''+v+'\\\' is invalid\' '),e.opts.verbose&&(a+=' , schema: validate.schema'+i+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+h+' '),a+=' } '),a+='; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',!e.compositeRule&&n&&(e.async?a+=' throw new ValidationError(vErrors); ':a+=' validate.errors = vErrors; return false; '),n&&(a+=' break; '),a+=' } }'}return n&&(a+=' '+''+' if ('+d+' == errors) {'),a=e.util.cleanUpCode(a),a}},function(e){'use strict';e.exports=function(e,r){var a,t=' ',s=e.level,o=e.dataLevel,i=e.schema[r],l=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+'/'+r,h=!e.opts.allErrors,d='data'+(o||''),p='valid'+s,c=e.opts.$data&&i&&i.$data;c?(t+=' var schema'+s+' = '+e.util.getData(i.$data,o,e.dataPathArr)+'; ',a='schema'+s):a=i;var m='schema'+s;if(!c)if(i.length<e.opts.loopRequired&&e.schema.properties&&Object.keys(e.schema.properties).length){var u=[],f=i;if(f)for(var v,y=-1,g=f.length-1;y<g;){v=f[y+=1];var P=e.schema.properties[v];P&&e.util.schemaHasRules(P,e.RULES.all)||(u[u.length]=v)}}else var u=i;if(c||u.length){var E=e.errorPath,b=c||u.length>=e.opts.loopRequired,w=e.opts.ownProperties;if(h){if(t+=' var missing'+s+'; ',b){c||(t+=' var '+m+' = validate.schema'+l+'; ');var S='i'+s,x='schema'+s+'['+S+']',k='\' + '+x+' + \'';e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPathExpr(E,x,e.opts.jsonPointers)),t+=' var '+p+' = true; ',c&&(t+=' if (schema'+s+' === undefined) '+p+' = true; else if (!Array.isArray(schema'+s+')) '+p+' = false; else {'),t+=' for (var '+S+' = 0; '+S+' < '+m+'.length; '+S+'++) { '+p+' = '+d+'['+m+'['+S+']] !== undefined ',w&&(t+=' && Object.prototype.hasOwnProperty.call('+d+', '+m+'['+S+']) '),t+='; if (!'+p+') break; } ',c&&(t+=' } '),t+=' if (!'+p+') { ';var $=$||[];$.push(t),t='',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'required\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { missingProperty: \''+k+'\' } ',!1!==e.opts.messages&&(t+=' , message: \'',t+=e.opts._errorDataPathProperty?'is a required property':'should have required property \\\''+k+'\\\'',t+='\' '),e.opts.verbose&&(t+=' , schema: validate.schema'+l+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } ');var _=t;t=$.pop(),t+=!e.compositeRule&&h?e.async?' throw new ValidationError(['+_+']); ':' validate.errors = ['+_+']; return false; ':' var err = '+_+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',t+=' } else { '}else{t+=' if ( ';for(var R,j=u,S=-1,I=j.length-1;S<I;){R=j[S+=1],S&&(t+=' || ');var O=e.util.getProperty(R),A=d+O;t+=' ( ( '+A+' === undefined ',w&&(t+=' || ! Object.prototype.hasOwnProperty.call('+d+', \''+e.util.escapeQuotes(R)+'\') '),t+=') && (missing'+s+' = '+e.util.toQuotedString(e.opts.jsonPointers?R:O)+') ) '}t+=') { ';var x='missing'+s,k='\' + '+x+' + \'';e.opts._errorDataPathProperty&&(e.errorPath=e.opts.jsonPointers?e.util.getPathExpr(E,x,!0):E+' + '+x);var $=$||[];$.push(t),t='',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'required\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { missingProperty: \''+k+'\' } ',!1!==e.opts.messages&&(t+=' , message: \'',t+=e.opts._errorDataPathProperty?'is a required property':'should have required property \\\''+k+'\\\'',t+='\' '),e.opts.verbose&&(t+=' , schema: validate.schema'+l+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } ');var _=t;t=$.pop(),t+=!e.compositeRule&&h?e.async?' throw new ValidationError(['+_+']); ':' validate.errors = ['+_+']; return false; ':' var err = '+_+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',t+=' } else { '}}else if(b){c||(t+=' var '+m+' = validate.schema'+l+'; ');var S='i'+s,x='schema'+s+'['+S+']',k='\' + '+x+' + \'';e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPathExpr(E,x,e.opts.jsonPointers)),c&&(t+=' if ('+m+' && !Array.isArray('+m+')) { var err = ',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'required\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { missingProperty: \''+k+'\' } ',!1!==e.opts.messages&&(t+=' , message: \'',t+=e.opts._errorDataPathProperty?'is a required property':'should have required property \\\''+k+'\\\'',t+='\' '),e.opts.verbose&&(t+=' , schema: validate.schema'+l+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } '),t+='; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else if ('+m+' !== undefined) { '),t+=' for (var '+S+' = 0; '+S+' < '+m+'.length; '+S+'++) { if ('+d+'['+m+'['+S+']] === undefined ',w&&(t+=' || ! Object.prototype.hasOwnProperty.call('+d+', '+m+'['+S+']) '),t+=') { var err = ',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'required\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { missingProperty: \''+k+'\' } ',!1!==e.opts.messages&&(t+=' , message: \'',t+=e.opts._errorDataPathProperty?'is a required property':'should have required property \\\''+k+'\\\'',t+='\' '),e.opts.verbose&&(t+=' , schema: validate.schema'+l+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } '),t+='; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } } ',c&&(t+=' } ')}else for(var R,L=u,Q=-1,q=L.length-1;Q<q;){R=L[Q+=1];var O=e.util.getProperty(R),k=e.util.escapeQuotes(R),A=d+O;e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPath(E,R,e.opts.jsonPointers)),t+=' if ( '+A+' === undefined ',w&&(t+=' || ! Object.prototype.hasOwnProperty.call('+d+', \''+e.util.escapeQuotes(R)+'\') '),t+=') { var err = ',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'required\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { missingProperty: \''+k+'\' } ',!1!==e.opts.messages&&(t+=' , message: \'',t+=e.opts._errorDataPathProperty?'is a required property':'should have required property \\\''+k+'\\\'',t+='\' '),e.opts.verbose&&(t+=' , schema: validate.schema'+l+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } '),t+='; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } '}e.errorPath=E}else h&&(t+=' if (true) {');return t}},function(e){'use strict';e.exports=function(e,r){var a,t=' ',s=e.level,o=e.dataLevel,i=e.schema[r],l=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+'/'+r,h=!e.opts.allErrors,d='data'+(o||''),p='valid'+s,c=e.opts.$data&&i&&i.$data;if(c?(t+=' var schema'+s+' = '+e.util.getData(i.$data,o,e.dataPathArr)+'; ',a='schema'+s):a=i,(i||c)&&!1!==e.opts.uniqueItems){c&&(t+=' var '+p+'; if ('+a+' === false || '+a+' === undefined) '+p+' = true; else if (typeof '+a+' != \'boolean\') '+p+' = false; else { '),t+=' var '+p+' = true; if ('+d+'.length > 1) { var i = '+d+'.length, j; outer: for (;i--;) { for (j = i; j--;) { if (equal('+d+'[i], '+d+'[j])) { '+p+' = false; break outer; } } } } ',c&&(t+=' } '),t+=' if (!'+p+') { ';var m=m||[];m.push(t),t='',!1===e.createErrors?t+=' {} ':(t+=' { keyword: \'uniqueItems\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(n)+' , params: { i: i, j: j } ',!1!==e.opts.messages&&(t+=' , message: \'should NOT have duplicate items (items ## \' + j + \' and \' + i + \' are identical)\' '),e.opts.verbose&&(t+=' , schema: ',t+=c?'validate.schema'+l:''+i,t+=' , parentSchema: validate.schema'+e.schemaPath+' , data: '+d+' '),t+=' } ');var u=t;t=m.pop(),t+=!e.compositeRule&&h?e.async?' throw new ValidationError(['+u+']); ':' validate.errors = ['+u+']; return false; ':' var err = '+u+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',t+=' } ',h&&(t+=' else { ')}else h&&(t+=' if (true) { ');return t}},function(e){'use strict';var r=['multipleOf','maximum','exclusiveMaximum','minimum','exclusiveMinimum','maxLength','minLength','pattern','additionalItems','maxItems','minItems','uniqueItems','maxProperties','minProperties','required','additionalProperties','enum','format','const'];e.exports=function(e,a){for(var t=0;t<a.length;t++){e=JSON.parse(JSON.stringify(e));var s,o=a[t].split('/'),i=e;for(s=1;s<o.length;s++)i=i[o[s]];for(s=0;s<r.length;s++){var l=r[s],n=i[l];n&&(i[l]={anyOf:[n,{$ref:'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#'}]})}}return e}},function(e){'use strict';var r='http://json-schema.org/draft-06/schema';e.exports=function(e){var a=e._opts.defaultMeta,t='string'==typeof a?{$ref:a}:e.getSchema(r)?{$ref:r}:{};e.addKeyword('patternGroups',{metaSchema:{type:'object',additionalProperties:{type:'object',required:['schema'],properties:{maximum:{type:'integer',minimum:0},minimum:{type:'integer',minimum:0},schema:t},additionalProperties:!1}}}),e.RULES.all.properties.implements.push('patternGroups')}},function(e,r,a){'use strict';function t(e,r,a){function o(e){var r=e.$schema;return r&&!l.getSchema(r)?t.call(l,{$ref:r},!0):Promise.resolve()}function i(e){function a(a){function t(){delete l._loadingSchemas[n]}function s(e){return l._refs[e]||l._schemas[e]}var n=a.missingSchema;if(s(n))throw new Error('Schema '+n+' is loaded but '+a.missingRef+' cannot be resolved');var h=l._loadingSchemas[n];return h||(h=l._loadingSchemas[n]=l._opts.loadSchema(n),h.then(t,t)),h.then(function(e){if(!s(n))return o(e).then(function(){s(n)||l.addSchema(e,n,void 0,r)})}).then(function(){return i(e)})}try{return l._compile(e)}catch(r){if(r instanceof s)return a(r);throw r}}var l=this;if('function'!=typeof this._opts.loadSchema)throw new Error('options.loadSchema should be a function');'function'==typeof r&&(a=r,r=void 0);var n=o(e).then(function(){var a=l._addSchema(e,void 0,r);return a.validate||i(a)});return a&&n.then(function(e){a(null,e)},a),n}var s=a(3).MissingRef;e.exports=t},function(e,r,a){'use strict';var t=/^[a-z_$][a-z0-9_$-]*$/i,s=a(53);e.exports={add:function(e,r){function a(e,r,a){for(var t,o,n=0;n<l.length;n++)if(o=l[n],o.type==r){t=o;break}t||(t={type:r,rules:[]},l.push(t));var i={keyword:e,definition:a,custom:!0,code:s,implements:a.implements};t.rules.push(i),l.custom[e]=i}function o(e){if(!l.types[e])throw new Error('Unknown type '+e)}var l=this.RULES;if(l.keywords[e])throw new Error('Keyword '+e+' is already defined');if(!t.test(e))throw new Error('Keyword '+e+' is not a valid identifier');if(r){if(r.macro&&void 0!==r.valid)throw new Error('"valid" option cannot be used with macro keywords');var n=r.type;if(Array.isArray(n)){var h,i=n.length;for(h=0;h<i;h++)o(n[h]);for(h=0;h<i;h++)a(e,n[h],r)}else n&&o(n),a(e,n,r);var d=!0===r.$data&&this._opts.$data;if(d&&!r.validate)throw new Error('$data support: "validate" function is not defined');var p=r.metaSchema;p&&(d&&(p={anyOf:[p,{$ref:'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#'}]}),r.validateSchema=this.compile(p,!0))}l.keywords[e]=l.all[e]=!0},get:function(e){var r=this.RULES.custom[e];return r?r.definition:this.RULES.keywords[e]||!1},remove:function(e){var r=this.RULES;delete r.keywords[e],delete r.all[e],delete r.custom[e];for(var a,t=0;t<r.length;t++){a=r[t].rules;for(var s=0;s<a.length;s++)if(a[s].keyword==e){a.splice(s,1);break}}}}},function(e){'use strict';e.exports=function(e,r){var a,t,s=' ',o=e.level,i=e.dataLevel,l=e.schema[r],n=e.schemaPath+e.util.getProperty(r),h=e.errSchemaPath+'/'+r,d=!e.opts.allErrors,p='data'+(i||''),c='valid'+o,m='errs__'+o,u=e.opts.$data&&l&&l.$data;u?(s+=' var schema'+o+' = '+e.util.getData(l.$data,i,e.dataPathArr)+'; ',t='schema'+o):t=l;var f,v,y,g,P,E=this,b='definition'+o,w=E.definition,S='';if(u&&w.$data){P='keywordValidate'+o;var x=w.validateSchema;s+=' var '+b+' = RULES.custom[\''+r+'\'].definition; var '+P+' = '+b+'.validate;'}else{if(g=e.useCustomRule(E,l,e.schema,e),!g)return;t='validate.schema'+n,P=g.code,f=w.compile,v=w.inline,y=w.macro}var k=P+'.errors',$='i'+o,_='ruleErr'+o,R=w.async;if(R&&!e.async)throw new Error('async keyword in sync schema');if(v||y||(s+=''+k+' = null;'),s+='var '+m+' = errors;var '+c+';',u&&w.$data&&(S+='}',s+=' if ('+t+' === undefined) { '+c+' = true; } else { ',x&&(S+='}',s+=' '+c+' = '+b+'.validateSchema('+t+'); if ('+c+') { ')),v)s+=w.statements?' '+g.validate+' ':' '+c+' = '+g.validate+'; ';else if(y){var j=e.util.copy(e),S='';j.level++;var I='valid'+j.level;j.schema=g.validate,j.schemaPath='';var O=e.compositeRule;e.compositeRule=j.compositeRule=!0;var A=e.validate(j).replace(/validate\.schema/g,P);e.compositeRule=j.compositeRule=O,s+=' '+A}else{var L=L||[];L.push(s),s='',s+=' '+P+'.call( ',s+=e.opts.passContext?'this':'self',s+=f||!1===w.schema?' , '+p+' ':' , '+t+' , '+p+' , validate.schema'+e.schemaPath+' ',s+=' , (dataPath || \'\')','""'!=e.errorPath&&(s+=' + '+e.errorPath);var Q=i?'data'+(i-1||''):'parentData',q=i?e.dataPathArr[i]:'parentDataProperty';s+=' , '+Q+' , '+q+' , rootData ) ';var D=s;s=L.pop(),!1===w.errors?(s+=' '+c+' = ',R&&(s+=''+e.yieldAwait),s+=''+D+'; '):R?(k='customErrors'+o,s+=' var '+k+' = null; try { '+c+' = '+e.yieldAwait+D+'; } catch (e) { '+c+' = false; if (e instanceof ValidationError) '+k+' = e.errors; else throw e; } '):s+=' '+k+' = null; '+c+' = '+D+'; '}if(w.modifying&&(s+=' if ('+Q+') '+p+' = '+Q+'['+q+'];'),s+=''+S,w.valid)d&&(s+=' if (true) { ');else{s+=' if ( ',void 0===w.valid?(s+=' !',s+=y?''+I:''+c):s+=' '+!w.valid+' ',s+=') { ',a=E.keyword;var L=L||[];L.push(s),s='';var L=L||[];L.push(s),s='',!1===e.createErrors?s+=' {} ':(s+=' { keyword: \''+(a||'custom')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(h)+' , params: { keyword: \''+E.keyword+'\' } ',!1!==e.opts.messages&&(s+=' , message: \'should pass "'+E.keyword+'" keyword validation\' '),e.opts.verbose&&(s+=' , schema: validate.schema'+n+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+p+' '),s+=' } ');var V=s;s=L.pop(),s+=!e.compositeRule&&d?e.async?' throw new ValidationError(['+V+']); ':' validate.errors = ['+V+']; return false; ':' var err = '+V+'; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';var N=s;s=L.pop(),v?w.errors?'full'!=w.errors&&(s+=' for (var '+$+'='+m+'; '+$+'<errors; '+$+'++) { var '+_+' = vErrors['+$+']; if ('+_+'.dataPath === undefined) '+_+'.dataPath = (dataPath || \'\') + '+e.errorPath+'; if ('+_+'.schemaPath === undefined) { '+_+'.schemaPath = "'+h+'"; } ',e.opts.verbose&&(s+=' '+_+'.schema = '+t+'; '+_+'.data = '+p+'; '),s+=' } '):!1===w.errors?s+=' '+N+' ':(s+=' if ('+m+' == errors) { '+N+' } else { for (var '+$+'='+m+'; '+$+'<errors; '+$+'++) { var '+_+' = vErrors['+$+']; if ('+_+'.dataPath === undefined) '+_+'.dataPath = (dataPath || \'\') + '+e.errorPath+'; if ('+_+'.schemaPath === undefined) { '+_+'.schemaPath = "'+h+'"; } ',e.opts.verbose&&(s+=' '+_+'.schema = '+t+'; '+_+'.data = '+p+'; '),s+=' } } '):y?(s+=' var err = ',!1===e.createErrors?s+=' {} ':(s+=' { keyword: \''+(a||'custom')+'\' , dataPath: (dataPath || \'\') + '+e.errorPath+' , schemaPath: '+e.util.toQuotedString(h)+' , params: { keyword: \''+E.keyword+'\' } ',!1!==e.opts.messages&&(s+=' , message: \'should pass "'+E.keyword+'" keyword validation\' '),e.opts.verbose&&(s+=' , schema: validate.schema'+n+' , parentSchema: validate.schema'+e.schemaPath+' , data: '+p+' '),s+=' } '),s+='; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ',!e.compositeRule&&d&&(e.async?s+=' throw new ValidationError(vErrors); ':s+=' validate.errors = vErrors; return false; ')):!1===w.errors?s+=' '+N+' ':(s+=' if (Array.isArray('+k+')) { if (vErrors === null) vErrors = '+k+'; else vErrors = vErrors.concat('+k+'); errors = vErrors.length; for (var '+$+'='+m+'; '+$+'<errors; '+$+'++) { var '+_+' = vErrors['+$+']; if ('+_+'.dataPath === undefined) '+_+'.dataPath = (dataPath || \'\') + '+e.errorPath+'; '+_+'.schemaPath = "'+h+'"; ',e.opts.verbose&&(s+=' '+_+'.schema = '+t+'; '+_+'.data = '+p+'; '),s+=' } } else { '+N+' } '),s+=' } ',d&&(s+=' else { ')}return s}},function(e){e.exports={$schema:'http://json-schema.org/draft-06/schema#',$id:'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#',description:'Meta-schema for $data reference (JSON-schema extension proposal)',type:'object',required:['$data'],properties:{$data:{type:'string',anyOf:[{format:'relative-json-pointer'},{format:'json-pointer'}]}},additionalProperties:!1}},function(e){e.exports={$schema:'http://json-schema.org/draft-06/schema#',$id:'http://json-schema.org/draft-06/schema#',title:'Core schema meta-schema',definitions:{schemaArray:{type:'array',minItems:1,items:{$ref:'#'}},nonNegativeInteger:{type:'integer',minimum:0},nonNegativeIntegerDefault0:{allOf:[{$ref:'#/definitions/nonNegativeInteger'},{default:0}]},simpleTypes:{enum:['array','boolean','integer','null','number','object','string']},stringArray:{type:'array',items:{type:'string'},uniqueItems:!0,default:[]}},type:['object','boolean'],properties:{$id:{type:'string',format:'uri-reference'},$schema:{type:'string',format:'uri'},$ref:{type:'string',format:'uri-reference'},title:{type:'string'},description:{type:'string'},default:{},multipleOf:{type:'number',exclusiveMinimum:0},maximum:{type:'number'},exclusiveMaximum:{type:'number'},minimum:{type:'number'},exclusiveMinimum:{type:'number'},maxLength:{$ref:'#/definitions/nonNegativeInteger'},minLength:{$ref:'#/definitions/nonNegativeIntegerDefault0'},pattern:{type:'string',format:'regex'},additionalItems:{$ref:'#'},items:{anyOf:[{$ref:'#'},{$ref:'#/definitions/schemaArray'}],default:{}},maxItems:{$ref:'#/definitions/nonNegativeInteger'},minItems:{$ref:'#/definitions/nonNegativeIntegerDefault0'},uniqueItems:{type:'boolean',default:!1},contains:{$ref:'#'},maxProperties:{$ref:'#/definitions/nonNegativeInteger'},minProperties:{$ref:'#/definitions/nonNegativeIntegerDefault0'},required:{$ref:'#/definitions/stringArray'},additionalProperties:{$ref:'#'},definitions:{type:'object',additionalProperties:{$ref:'#'},default:{}},properties:{type:'object',additionalProperties:{$ref:'#'},default:{}},patternProperties:{type:'object',additionalProperties:{$ref:'#'},default:{}},dependencies:{type:'object',additionalProperties:{anyOf:[{$ref:'#'},{$ref:'#/definitions/stringArray'}]}},propertyNames:{$ref:'#'},const:{},enum:{type:'array',minItems:1,uniqueItems:!0},type:{anyOf:[{$ref:'#/definitions/simpleTypes'},{type:'array',items:{$ref:'#/definitions/simpleTypes'},minItems:1,uniqueItems:!0}]},format:{type:'string'},allOf:{$ref:'#/definitions/schemaArray'},anyOf:{$ref:'#/definitions/schemaArray'},oneOf:{$ref:'#/definitions/schemaArray'},not:{$ref:'#'}},default:{}}}]);this.EXPORTED_SYMBOLS = ["ajv"]; \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/browser/extensions/shield-recipe-client/vendor/mozjexl.js @@ -0,0 +1,1 @@ +/* eslint-disable */this.mozjexl=function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={i:d,l:!1,exports:{}};return a[d].call(e.exports,e,e.exports,b),e.l=!0,e.exports}var c={};return b.m=a,b.c=c,b.d=function(a,c,d){b.o(a,c)||Object.defineProperty(a,c,{configurable:!1,enumerable:!0,get:d})},b.n=function(a){var c=a&&a.__esModule?function(){return a['default']}:function(){return a};return b.d(c,'a',c),c},b.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)},b.p='',b(b.s=56)}({12:function(a,b){b.argVal=function(a){this._cursor.args.push(a)},b.arrayStart=function(){this._placeAtCursor({type:'ArrayLiteral',value:[]})},b.arrayVal=function(a){a&&this._cursor.value.push(a)},b.binaryOp=function(a){for(var b=this._grammar[a.value].precedence||0,c=this._cursor._parent;c&&c.operator&&this._grammar[c.operator].precedence>=b;)this._cursor=c,c=c._parent;var d={type:'BinaryExpression',operator:a.value,left:this._cursor};this._setParent(this._cursor,d),this._cursor=c,this._placeAtCursor(d)},b.dot=function(){this._nextIdentEncapsulate=this._cursor&&('BinaryExpression'!=this._cursor.type||'BinaryExpression'==this._cursor.type&&this._cursor.right)&&'UnaryExpression'!=this._cursor.type,this._nextIdentRelative=!this._cursor||this._cursor&&!this._nextIdentEncapsulate,this._nextIdentRelative&&(this._relative=!0)},b.filter=function(a){this._placeBeforeCursor({type:'FilterExpression',expr:a,relative:this._subParser.isRelative(),subject:this._cursor})},b.identifier=function(a){var b={type:'Identifier',value:a.value};this._nextIdentEncapsulate?(b.from=this._cursor,this._placeBeforeCursor(b),this._nextIdentEncapsulate=!1):(this._nextIdentRelative&&(b.relative=!0),this._placeAtCursor(b))},b.literal=function(a){this._placeAtCursor({type:'Literal',value:a.value})},b.objKey=function(a){this._curObjKey=a.value},b.objStart=function(){this._placeAtCursor({type:'ObjectLiteral',value:{}})},b.objVal=function(a){this._cursor.value[this._curObjKey]=a},b.subExpression=function(a){this._placeAtCursor(a)},b.ternaryEnd=function(a){this._cursor.alternate=a},b.ternaryMid=function(a){this._cursor.consequent=a},b.ternaryStart=function(){this._tree={type:'ConditionalExpression',test:this._tree},this._cursor=this._tree},b.transform=function(a){this._placeBeforeCursor({type:'Transform',name:a.value,args:[],subject:this._cursor})},b.unaryOp=function(a){this._placeAtCursor({type:'UnaryExpression',operator:a.value})}},56:function(a,b,c){function d(){this._customGrammar=null,this._lexer=null,this._transforms={}}var e=c(57),f=c(59),g=c(60),h=c(62).elements;d.prototype.addBinaryOp=function(a,b,c){this._addGrammarElement(a,{type:'binaryOp',precedence:b,eval:c})},d.prototype.addUnaryOp=function(a,b){this._addGrammarElement(a,{type:'unaryOp',weight:Infinity,eval:b})},d.prototype.addTransform=function(a,b){this._transforms[a]=b},d.prototype.addTransforms=function(a){for(var b in a)a.hasOwnProperty(b)&&(this._transforms[b]=a[b])},d.prototype.getTransform=function(a){return this._transforms[a]},d.prototype.eval=function(a,b,c){'function'==typeof b?(c=b,b={}):!b&&(b={});var d=this._eval(a,b);if(c){var e=!1;return d.then(function(a){e=!0,setTimeout(c.bind(null,null,a),0)}).catch(function(a){e||setTimeout(c.bind(null,a),0)})}return d},d.prototype.removeOp=function(a){var b=this._getCustomGrammar();b[a]&&('binaryOp'==b[a].type||'unaryOp'==b[a].type)&&(delete b[a],this._lexer=null)},d.prototype._addGrammarElement=function(a,b){var c=this._getCustomGrammar();c[a]=b,this._lexer=null},d.prototype._eval=function(a,b){var c=this,d=this._getGrammar(),f=new g(d),h=new e(d,this._transforms,b);return Promise.resolve().then(function(){return f.addTokens(c._getLexer().tokenize(a)),h.eval(f.complete())})},d.prototype._getCustomGrammar=function(){if(!this._customGrammar)for(var a in this._customGrammar={},h)h.hasOwnProperty(a)&&(this._customGrammar[a]=h[a]);return this._customGrammar},d.prototype._getGrammar=function(){return this._customGrammar||h},d.prototype._getLexer=function(){return this._lexer||(this._lexer=new f(this._getGrammar())),this._lexer},a.exports=new d,a.exports.Jexl=d},57:function(a,b,c){var d=c(58),e=function(a,b,c,d){this._grammar=a,this._transforms=b||{},this._context=c||{},this._relContext=d||this._context};e.prototype.eval=function(a){var b=this;return Promise.resolve().then(function(){return d[a.type].call(b,a)})},e.prototype.evalArray=function(a){return Promise.all(a.map(function(a){return this.eval(a)},this))},e.prototype.evalMap=function(a){var b=Object.keys(a),c={},d=b.map(function(b){return this.eval(a[b])},this);return Promise.all(d).then(function(a){return a.forEach(function(a,d){c[b[d]]=a}),c})},e.prototype._filterRelative=function(a,b){if(void 0!==a){var c=[];return Array.isArray(a)||(a=[a]),a.forEach(function(a){var d=new e(this._grammar,this._transforms,this._context,a);c.push(d.eval(b))},this),Promise.all(c).then(function(b){var c=[];return b.forEach(function(b,d){b&&c.push(a[d])}),c})}},e.prototype._filterStatic=function(a,b){return this.eval(b).then(function(b){return'boolean'==typeof b?b?a:void 0:void 0===a?void 0:a[b]})},a.exports=e},58:function(a,b){b.ArrayLiteral=function(a){return this.evalArray(a.value)},b.BinaryExpression=function(a){var b=this;return Promise.all([this.eval(a.left),this.eval(a.right)]).then(function(c){return b._grammar[a.operator].eval(c[0],c[1])})},b.ConditionalExpression=function(a){var b=this;return this.eval(a.test).then(function(c){return c?a.consequent?b.eval(a.consequent):c:b.eval(a.alternate)})},b.FilterExpression=function(a){var b=this;return this.eval(a.subject).then(function(c){return a.relative?b._filterRelative(c,a.expr):b._filterStatic(c,a.expr)})},b.Identifier=function(a){return a.from?this.eval(a.from).then(function(b){if(void 0!==b)return Array.isArray(b)&&(b=b[0]),b[a.value]}):a.relative?this._relContext[a.value]:this._context[a.value]},b.Literal=function(a){return a.value},b.ObjectLiteral=function(a){return this.evalMap(a.value)},b.Transform=function(a){var b=this._transforms[a.name];if(!b)throw new Error('Transform \''+a.name+'\' is not defined.');return Promise.all([this.eval(a.subject),this.evalArray(a.args||[])]).then(function(a){return b.apply(null,[a[0]].concat(a[1]))})},b.UnaryExpression=function(a){var b=this;return this.eval(a.right).then(function(c){return b._grammar[a.operator].eval(c)})}},59:function(a){function b(a){this._grammar=a}var c=/^-?(?:(?:[0-9]*\.[0-9]+)|[0-9]+)$/,d=/^[a-zA-Z_\$][a-zA-Z0-9_\$]*$/,e=/\\\\/,f=['\'(?:(?:\\\\\')?[^\'])*\'','"(?:(?:\\\\")?[^"])*"','\\s+','\\btrue\\b','\\bfalse\\b'],g=['\\b[a-zA-Z_\\$][a-zA-Z0-9_\\$]*\\b','(?:(?:[0-9]*\\.[0-9]+)|[0-9]+)'],h=['binaryOp','unaryOp','openParen','openBracket','question','colon'];b.prototype.getElements=function(a){var b=this._getSplitRegex();return a.split(b).filter(function(a){return a})},b.prototype.getTokens=function(a){for(var b=[],c=!1,d=0;d<a.length;d++)this._isWhitespace(a[d])?b.length&&(b[b.length-1].raw+=a[d]):'-'===a[d]&&this._isNegative(b)?c=!0:(c&&(a[d]='-'+a[d],c=!1),b.push(this._createToken(a[d])));return c&&b.push(this._createToken('-')),b},b.prototype.tokenize=function(a){var b=this.getElements(a);return this.getTokens(b)},b.prototype._createToken=function(a){var b={type:'literal',value:a,raw:a};if('"'==a[0]||'\''==a[0])b.value=this._unquote(a);else if(a.match(c))b.value=parseFloat(a);else if('true'===a||'false'===a)b.value='true'==a;else if(this._grammar[a])b.type=this._grammar[a].type;else if(a.match(d))b.type='identifier';else throw new Error('Invalid expression token: '+a);return b},b.prototype._escapeRegExp=function(a){return a=a.replace(/[.*+?^${}()|[\]\\]/g,'\\$&'),a.match(d)&&(a='\\b'+a+'\\b'),a},b.prototype._getSplitRegex=function(){if(!this._splitRegex){var a=Object.keys(this._grammar);a=a.sort(function(c,a){return a.length-c.length}).map(function(a){return this._escapeRegExp(a)},this),this._splitRegex=new RegExp('('+[f.join('|'),a.join('|'),g.join('|')].join('|')+')')}return this._splitRegex},b.prototype._isNegative=function(a){return!a.length||h.some(function(b){return b===a[a.length-1].type})};var i=/^\s*$/;b.prototype._isWhitespace=function(a){return i.test(a)},b.prototype._unquote=function(a){var b=a[0],c=new RegExp('\\\\'+b,'g');return a.substr(1,a.length-2).replace(c,b).replace(e,'\\')},a.exports=b},60:function(a,b,c){function d(a,b,c){this._grammar=a,this._state='expectOperand',this._tree=null,this._exprStr=b||'',this._relative=!1,this._stopMap=c||{}}var e=c(12),f=c(61).states;d.prototype.addToken=function(a){if('complete'==this._state)throw new Error('Cannot add a new token to a completed Parser');var b=f[this._state],c=this._exprStr;if(this._exprStr+=a.raw,b.subHandler){this._subParser||this._startSubExpression(c);var d=this._subParser.addToken(a);if(d){if(this._endSubExpression(),this._parentStop)return d;this._state=d}}else if(b.tokenTypes[a.type]){var g=b.tokenTypes[a.type],h=e[a.type];g.handler&&(h=g.handler),h&&h.call(this,a),g.toState&&(this._state=g.toState)}else{if(this._stopMap[a.type])return this._stopMap[a.type];throw new Error('Token '+a.raw+' ('+a.type+') unexpected in expression: '+this._exprStr)}return!1},d.prototype.addTokens=function(a){a.forEach(this.addToken,this)},d.prototype.complete=function(){if(this._cursor&&!f[this._state].completable)throw new Error('Unexpected end of expression: '+this._exprStr);return this._subParser&&this._endSubExpression(),this._state='complete',this._cursor?this._tree:null},d.prototype.isRelative=function(){return this._relative},d.prototype._endSubExpression=function(){f[this._state].subHandler.call(this,this._subParser.complete()),this._subParser=null},d.prototype._placeAtCursor=function(a){this._cursor?(this._cursor.right=a,this._setParent(a,this._cursor)):this._tree=a,this._cursor=a},d.prototype._placeBeforeCursor=function(a){this._cursor=this._cursor._parent,this._placeAtCursor(a)},d.prototype._setParent=function(a,b){Object.defineProperty(a,'_parent',{value:b,writable:!0})},d.prototype._startSubExpression=function(a){var b=f[this._state].endStates;b||(this._parentStop=!0,b=this._stopMap),this._subParser=new d(this._grammar,a,b)},a.exports=d},61:function(a,b,c){var d=c(12);b.states={expectOperand:{tokenTypes:{literal:{toState:'expectBinOp'},identifier:{toState:'identifier'},unaryOp:{},openParen:{toState:'subExpression'},openCurl:{toState:'expectObjKey',handler:d.objStart},dot:{toState:'traverse'},openBracket:{toState:'arrayVal',handler:d.arrayStart}}},expectBinOp:{tokenTypes:{binaryOp:{toState:'expectOperand'},pipe:{toState:'expectTransform'},dot:{toState:'traverse'},question:{toState:'ternaryMid',handler:d.ternaryStart}},completable:!0},expectTransform:{tokenTypes:{identifier:{toState:'postTransform',handler:d.transform}}},expectObjKey:{tokenTypes:{identifier:{toState:'expectKeyValSep',handler:d.objKey},closeCurl:{toState:'expectBinOp'}}},expectKeyValSep:{tokenTypes:{colon:{toState:'objVal'}}},postTransform:{tokenTypes:{openParen:{toState:'argVal'},binaryOp:{toState:'expectOperand'},dot:{toState:'traverse'},openBracket:{toState:'filter'},pipe:{toState:'expectTransform'}},completable:!0},postTransformArgs:{tokenTypes:{binaryOp:{toState:'expectOperand'},dot:{toState:'traverse'},openBracket:{toState:'filter'},pipe:{toState:'expectTransform'}},completable:!0},identifier:{tokenTypes:{binaryOp:{toState:'expectOperand'},dot:{toState:'traverse'},openBracket:{toState:'filter'},pipe:{toState:'expectTransform'},question:{toState:'ternaryMid',handler:d.ternaryStart}},completable:!0},traverse:{tokenTypes:{identifier:{toState:'identifier'}}},filter:{subHandler:d.filter,endStates:{closeBracket:'identifier'}},subExpression:{subHandler:d.subExpression,endStates:{closeParen:'expectBinOp'}},argVal:{subHandler:d.argVal,endStates:{comma:'argVal',closeParen:'postTransformArgs'}},objVal:{subHandler:d.objVal,endStates:{comma:'expectObjKey',closeCurl:'expectBinOp'}},arrayVal:{subHandler:d.arrayVal,endStates:{comma:'arrayVal',closeBracket:'expectBinOp'}},ternaryMid:{subHandler:d.ternaryMid,endStates:{colon:'ternaryEnd'}},ternaryEnd:{subHandler:d.ternaryEnd,completable:!0}}},62:function(a,b){b.elements={".":{type:'dot'},"[":{type:'openBracket'},"]":{type:'closeBracket'},"|":{type:'pipe'},"{":{type:'openCurl'},"}":{type:'closeCurl'},":":{type:'colon'},",":{type:'comma'},"(":{type:'openParen'},")":{type:'closeParen'},"?":{type:'question'},"+":{type:'binaryOp',precedence:30,eval:function(a,b){return a+b}},"-":{type:'binaryOp',precedence:30,eval:function(a,b){return a-b}},"*":{type:'binaryOp',precedence:40,eval:function(a,b){return a*b}},"/":{type:'binaryOp',precedence:40,eval:function(a,b){return a/b}},"//":{type:'binaryOp',precedence:40,eval:function(a,b){return Math.floor(a/b)}},"%":{type:'binaryOp',precedence:50,eval:function(a,b){return a%b}},"^":{type:'binaryOp',precedence:50,eval:function(a,b){return Math.pow(a,b)}},"==":{type:'binaryOp',precedence:20,eval:function(a,b){return a==b}},"!=":{type:'binaryOp',precedence:20,eval:function(a,b){return a!=b}},">":{type:'binaryOp',precedence:20,eval:function(a,b){return a>b}},">=":{type:'binaryOp',precedence:20,eval:function(a,b){return a>=b}},"<":{type:'binaryOp',precedence:20,eval:function(a,b){return a<b}},"<=":{type:'binaryOp',precedence:20,eval:function(a,b){return a<=b}},"&&":{type:'binaryOp',precedence:10,eval:function(a,b){return a&&b}},"||":{type:'binaryOp',precedence:10,eval:function(a,b){return a||b}},in:{type:'binaryOp',precedence:20,eval:function(a,b){return'string'==typeof b?-1!==b.indexOf(a):!!Array.isArray(b)&&b.some(function(b){return b==a})}},"!":{type:'unaryOp',precedence:Infinity,eval:function(a){return!a}}}}});this.EXPORTED_SYMBOLS = ["mozjexl"]; \ No newline at end of file