diff --git a/apps/meteor/packages/autoupdate/autoupdate_client.js b/apps/meteor/packages/autoupdate/autoupdate_client.js index 36202d32bc5f..f9613935dbba 100644 --- a/apps/meteor/packages/autoupdate/autoupdate_client.js +++ b/apps/meteor/packages/autoupdate/autoupdate_client.js @@ -25,37 +25,27 @@ // The client version of the client code currently running in the // browser. -import { ClientVersions } from "./client_versions.js"; +import { ClientVersions } from './client_versions.js'; -const clientArch = Meteor.isCordova ? "web.cordova" : - Meteor.isModern ? "web.browser" : "web.browser.legacy"; +const clientArch = Meteor.isCordova ? 'web.cordova' : Meteor.isModern ? 'web.browser' : 'web.browser.legacy'; -const autoupdateVersions = - ((__meteor_runtime_config__.autoupdate || {}).versions || {})[clientArch] || { - version: "unknown", - versionRefreshable: "unknown", - versionNonRefreshable: "unknown", - assets: [], - }; +const autoupdateVersions = ((__meteor_runtime_config__.autoupdate || {}).versions || {})[clientArch] || { + version: 'unknown', + versionRefreshable: 'unknown', + versionNonRefreshable: 'unknown', + assets: [], +}; export const Autoupdate = {}; // Stores acceptable client versions. -const clientVersions = - Autoupdate._clientVersions = // Used by a self-test and hot-module-replacement - new ClientVersions(); +const clientVersions = (Autoupdate._clientVersions = // Used by a self-test and hot-module-replacement + new ClientVersions()); -Meteor.connection.registerStore( - "meteor_autoupdate_clientVersions", - clientVersions.createStore() -); +Meteor.connection.registerStore('meteor_autoupdate_clientVersions', clientVersions.createStore()); Autoupdate.newClientAvailable = function () { - return clientVersions.newClientAvailable( - clientArch, - ["versionRefreshable", "versionNonRefreshable"], - autoupdateVersions - ); + return clientVersions.newClientAvailable(clientArch, ['versionRefreshable', 'versionNonRefreshable'], autoupdateVersions); }; // Set to true if the link.onload callback ever fires for any node. @@ -71,15 +61,15 @@ const retry = new Retry({ // server fixing code will result in a restart and reconnect, but // potentially the subscription could have a transient error. minCount: 0, // don't do any immediate retries - baseTimeout: 30*1000 // start with 30s + baseTimeout: 30 * 1000, // start with 30s }); let failures = 0; Autoupdate._retrySubscription = () => { - Meteor.subscribe("meteor_autoupdate_clientVersions", { + Meteor.subscribe('meteor_autoupdate_clientVersions', { onError(error) { - Meteor._debug("autoupdate subscription failed", error); + Meteor._debug('autoupdate subscription failed', error); failures++; retry.retryLater(failures, function () { // Just retry making the subscription, don't reload the whole @@ -111,8 +101,7 @@ Autoupdate._retrySubscription = () => { return; } - if (doc.versionNonRefreshable !== - autoupdateVersions.versionNonRefreshable) { + if (doc.versionNonRefreshable !== autoupdateVersions.versionNonRefreshable) { // Non-refreshable assets have changed, so we have to reload the // whole page rather than just replacing tags. if (stop) stop(); @@ -121,7 +110,13 @@ Autoupdate._retrySubscription = () => { // is provided by the ddp package that autoupdate depends on. // Delay reload in 60 seconds - console.warn('Client version changed from', autoupdateVersions.versionNonRefreshable, 'to', doc.versionNonRefreshable, `Page will reload in ${reloadDelayInSeconds} seconds`); + console.warn( + 'Client version changed from', + autoupdateVersions.versionNonRefreshable, + 'to', + doc.versionNonRefreshable, + `Page will reload in ${reloadDelayInSeconds} seconds`, + ); setTimeout(() => { Package.reload.Reload._reload(); }, reloadDelayInSeconds * 1000); @@ -137,30 +132,27 @@ Autoupdate._retrySubscription = () => { var newCss = doc.assets || []; var oldLinks = []; - Array.prototype.forEach.call( - document.getElementsByTagName('link'), - function (link) { - if (link.className === '__meteor-css__') { - oldLinks.push(link); - } + Array.prototype.forEach.call(document.getElementsByTagName('link'), function (link) { + if (link.className === '__meteor-css__') { + oldLinks.push(link); } - ); + }); function waitUntilCssLoads(link, callback) { var called; link.onload = function () { knownToSupportCssOnLoad = true; - if (! called) { + if (!called) { called = true; callback(); } }; - if (! knownToSupportCssOnLoad) { + if (!knownToSupportCssOnLoad) { var id = Meteor.setInterval(function () { if (link.sheet) { - if (! called) { + if (!called) { called = true; callback(); } @@ -172,27 +164,26 @@ Autoupdate._retrySubscription = () => { let newLinksLeftToLoad = newCss.length; function removeOldLinks() { - if (oldLinks.length > 0 && - --newLinksLeftToLoad < 1) { - oldLinks.splice(0).forEach(link => { + if (oldLinks.length > 0 && --newLinksLeftToLoad < 1) { + oldLinks.splice(0).forEach((link) => { link.parentNode.removeChild(link); }); } } if (newCss.length > 0) { - newCss.forEach(css => { - const newLink = document.createElement("link"); - newLink.setAttribute("rel", "stylesheet"); - newLink.setAttribute("type", "text/css"); - newLink.setAttribute("class", "__meteor-css__"); - newLink.setAttribute("href", css.url); + newCss.forEach((css) => { + const newLink = document.createElement('link'); + newLink.setAttribute('rel', 'stylesheet'); + newLink.setAttribute('type', 'text/css'); + newLink.setAttribute('class', '__meteor-css__'); + newLink.setAttribute('href', css.url); waitUntilCssLoads(newLink, function () { Meteor.setTimeout(removeOldLinks, 200); }); - const head = document.getElementsByTagName("head").item(0); + const head = document.getElementsByTagName('head').item(0); head.appendChild(newLink); }); } else { @@ -200,7 +191,7 @@ Autoupdate._retrySubscription = () => { } } } - } + }, }); }; diff --git a/apps/meteor/packages/autoupdate/autoupdate_server.js b/apps/meteor/packages/autoupdate/autoupdate_server.js index a1d468490bfa..5ffbf92e82a6 100644 --- a/apps/meteor/packages/autoupdate/autoupdate_server.js +++ b/apps/meteor/packages/autoupdate/autoupdate_server.js @@ -25,18 +25,18 @@ // The ID of each document is the client architecture, and the fields of // the document are the versions described above. -import { ClientVersions } from "./client_versions.js"; -var Future = Npm.require("fibers/future"); +import { ClientVersions } from './client_versions.js'; +var Future = Npm.require('fibers/future'); -export const Autoupdate = __meteor_runtime_config__.autoupdate = { +export const Autoupdate = (__meteor_runtime_config__.autoupdate = { // Map from client architectures (web.browser, web.browser.legacy, // web.cordova) to version fields { version, versionRefreshable, // versionNonRefreshable, refreshable } that will be stored in // ClientVersions documents (whose IDs are client architectures). This // data gets serialized into the boilerplate because it's stored in // __meteor_runtime_config__.autoupdate.versions. - versions: {} -}; + versions: {}, +}); // Stores acceptable client versions. const clientVersions = new ClientVersions(); @@ -65,22 +65,18 @@ function updateVersions(shouldReloadClientProgram) { // If the AUTOUPDATE_VERSION environment variable is defined, it takes // precedence, but Autoupdate.autoupdateVersion is still supported as // a fallback. In most cases neither of these values will be defined. - AUTOUPDATE_VERSION = Autoupdate.autoupdateVersion + AUTOUPDATE_VERSION = Autoupdate.autoupdateVersion, } = process.env; // Step 2: update __meteor_runtime_config__.autoupdate.versions. const clientArchs = Object.keys(WebApp.clientPrograms); - clientArchs.forEach(arch => { + clientArchs.forEach((arch) => { Autoupdate.versions[arch] = { - version: AUTOUPDATE_VERSION || - WebApp.calculateClientHash(arch), - versionRefreshable: AUTOUPDATE_VERSION || - WebApp.calculateClientHashRefreshable(arch), - versionNonRefreshable: AUTOUPDATE_VERSION || - WebApp.calculateClientHashNonRefreshable(arch), - versionReplaceable: AUTOUPDATE_VERSION || - WebApp.calculateClientHashReplaceable(arch), - versionHmr: WebApp.clientPrograms[arch].hmrVersion + version: AUTOUPDATE_VERSION || WebApp.calculateClientHash(arch), + versionRefreshable: AUTOUPDATE_VERSION || WebApp.calculateClientHashRefreshable(arch), + versionNonRefreshable: AUTOUPDATE_VERSION || WebApp.calculateClientHashNonRefreshable(arch), + versionReplaceable: AUTOUPDATE_VERSION || WebApp.calculateClientHashReplaceable(arch), + versionHmr: WebApp.clientPrograms[arch].hmrVersion, }; }); @@ -95,7 +91,7 @@ function updateVersions(shouldReloadClientProgram) { // `WebApp.getRefreshableAssets`, which is only set after // `WebApp.generateBoilerplate` is called by `main` in webapp. WebApp.onListening(() => { - clientArchs.forEach(arch => { + clientArchs.forEach((arch) => { const payload = { ...Autoupdate.versions[arch], assets: WebApp.getRefreshableAssets(arch), @@ -107,7 +103,7 @@ function updateVersions(shouldReloadClientProgram) { } Meteor.publish( - "meteor_autoupdate_clientVersions", + 'meteor_autoupdate_clientVersions', function (appId) { // `null` happens when a client doesn't have an appId and passes // `undefined` to `Meteor.subscribe`. `undefined` is translated to @@ -116,23 +112,21 @@ Meteor.publish( // Don't notify clients using wrong appId such as mobile apps built with a // different server but pointing at the same local url - if (Autoupdate.appId && appId && Autoupdate.appId !== appId) - return []; + if (Autoupdate.appId && appId && Autoupdate.appId !== appId) return []; // Random value to delay the updates for 2-10 minutes const randomInterval = Meteor.isProduction ? (Math.floor(Math.random() * 8) + 2) * 1000 * 60 : 0; const stop = clientVersions.watch((version, isNew) => { setTimeout(() => { - (isNew ? this.added : this.changed) - .call(this, "meteor_autoupdate_clientVersions", version._id, version) + (isNew ? this.added : this.changed).call(this, 'meteor_autoupdate_clientVersions', version._id, version); }, randomInterval); }); this.onStop(() => stop()); this.ready(); }, - {is_auto: true} + { is_auto: true }, ); Meteor.startup(function () { @@ -140,12 +134,9 @@ Meteor.startup(function () { // Force any connected clients that are still looking for these older // document IDs to reload. - ["version", - "version-refreshable", - "version-cordova", - ].forEach(_id => { + ['version', 'version-refreshable', 'version-cordova'].forEach((_id) => { clientVersions.set(_id, { - version: "outdated" + version: 'outdated', }); }); }); @@ -173,10 +164,13 @@ function enqueueVersionsRefresh() { } // Listen for messages pertaining to the client-refresh topic. -import { onMessage } from "meteor/inter-process-messaging"; -onMessage("client-refresh", enqueueVersionsRefresh); +import { onMessage } from 'meteor/inter-process-messaging'; +onMessage('client-refresh', enqueueVersionsRefresh); // Another way to tell the process to refresh: send SIGHUP signal -process.on('SIGHUP', Meteor.bindEnvironment(function () { - enqueueVersionsRefresh(); -}, "handling SIGHUP signal for refresh")); +process.on( + 'SIGHUP', + Meteor.bindEnvironment(function () { + enqueueVersionsRefresh(); + }, 'handling SIGHUP signal for refresh'), +); diff --git a/apps/meteor/packages/autoupdate/client_versions.js b/apps/meteor/packages/autoupdate/client_versions.js index 1f7b6069fc2d..f0ffc0bedbeb 100644 --- a/apps/meteor/packages/autoupdate/client_versions.js +++ b/apps/meteor/packages/autoupdate/client_versions.js @@ -1,4 +1,4 @@ -import { Tracker } from "meteor/tracker"; +import { Tracker } from 'meteor/tracker'; export class ClientVersions { constructor() { @@ -12,10 +12,10 @@ export class ClientVersions { createStore() { return { update: ({ id, msg, fields }) => { - if (msg === "added" || msg === "changed") { + if (msg === 'added' || msg === 'changed') { this.set(id, fields); } - } + }, }; } @@ -39,7 +39,7 @@ export class ClientVersions { } else { version = { _id: id, - ...fields + ...fields, }; isNew = true; @@ -47,7 +47,7 @@ export class ClientVersions { } this._watchCallbacks.forEach(({ fn, filter }) => { - if (! filter || filter === version._id) { + if (!filter || filter === version._id) { fn(version, isNew); } }); @@ -59,11 +59,11 @@ export class ClientVersions { // documents. If `filter` is set, the callback is only invoked for documents // with ID `filter`. watch(fn, { skipInitial, filter } = {}) { - if (! skipInitial) { + if (!skipInitial) { const resolved = Promise.resolve(); this._versions.forEach((version) => { - if (! filter || filter === version._id) { + if (!filter || filter === version._id) { resolved.then(() => fn(version, true)); } }); @@ -78,10 +78,7 @@ export class ClientVersions { // A reactive data source for `Autoupdate.newClientAvailable`. newClientAvailable(id, fields, currentVersion) { function isNewVersion(version) { - return ( - version._id === id && - fields.some((field) => version[field] !== currentVersion[field]) - ); + return version._id === id && fields.some((field) => version[field] !== currentVersion[field]); } const dependency = new Tracker.Dependency(); @@ -96,9 +93,9 @@ export class ClientVersions { stop(); } }, - { skipInitial: true } + { skipInitial: true }, ); - return !! version && isNewVersion(version); + return !!version && isNewVersion(version); } } diff --git a/apps/meteor/packages/autoupdate/package.js b/apps/meteor/packages/autoupdate/package.js index a7ad595e0692..52941121a9b6 100644 --- a/apps/meteor/packages/autoupdate/package.js +++ b/apps/meteor/packages/autoupdate/package.js @@ -3,7 +3,7 @@ Package.describe({ version: '1.8.0', }); -Package.onUse(function(api) { +Package.onUse(function (api) { api.use(['webapp', 'check', 'inter-process-messaging'], 'server'); api.use(['tracker', 'retry'], 'client'); diff --git a/apps/meteor/packages/flow-router/client/_init.js b/apps/meteor/packages/flow-router/client/_init.js index a18fdc897bac..9bd2fe28c20e 100644 --- a/apps/meteor/packages/flow-router/client/_init.js +++ b/apps/meteor/packages/flow-router/client/_init.js @@ -5,7 +5,7 @@ FlowRouter.Route = Route; // Initialize FlowRouter Meteor.startup(function () { - if(!FlowRouter._askedToWait) { - FlowRouter.initialize(); - } + if (!FlowRouter._askedToWait) { + FlowRouter.initialize(); + } }); diff --git a/apps/meteor/packages/flow-router/client/group.js b/apps/meteor/packages/flow-router/client/group.js index b93296bc2ada..d8c9869e1378 100644 --- a/apps/meteor/packages/flow-router/client/group.js +++ b/apps/meteor/packages/flow-router/client/group.js @@ -1,57 +1,57 @@ -Group = function(router, options, parent) { - options = options || {}; - - if (options.prefix && !/^\/.*/.test(options.prefix)) { - var message = "group's prefix must start with '/'"; - throw new Error(message); - } - - this._router = router; - this.prefix = options.prefix || ''; - this.name = options.name; - this.options = options; - - this._triggersEnter = options.triggersEnter || []; - this._triggersExit = options.triggersExit || []; - this._subscriptions = options.subscriptions || Function.prototype; - - this.parent = parent; - if (this.parent) { - this.prefix = parent.prefix + this.prefix; - - this._triggersEnter = parent._triggersEnter.concat(this._triggersEnter); - this._triggersExit = this._triggersExit.concat(parent._triggersExit); - } +Group = function (router, options, parent) { + options = options || {}; + + if (options.prefix && !/^\/.*/.test(options.prefix)) { + var message = "group's prefix must start with '/'"; + throw new Error(message); + } + + this._router = router; + this.prefix = options.prefix || ''; + this.name = options.name; + this.options = options; + + this._triggersEnter = options.triggersEnter || []; + this._triggersExit = options.triggersExit || []; + this._subscriptions = options.subscriptions || Function.prototype; + + this.parent = parent; + if (this.parent) { + this.prefix = parent.prefix + this.prefix; + + this._triggersEnter = parent._triggersEnter.concat(this._triggersEnter); + this._triggersExit = this._triggersExit.concat(parent._triggersExit); + } }; -Group.prototype.route = function(pathDef, options, group) { - options = options || {}; +Group.prototype.route = function (pathDef, options, group) { + options = options || {}; - if (!/^\/.*/.test(pathDef)) { - var message = "route's path must start with '/'"; - throw new Error(message); - } + if (!/^\/.*/.test(pathDef)) { + var message = "route's path must start with '/'"; + throw new Error(message); + } - group = group || this; - pathDef = this.prefix + pathDef; + group = group || this; + pathDef = this.prefix + pathDef; - var triggersEnter = options.triggersEnter || []; - options.triggersEnter = this._triggersEnter.concat(triggersEnter); + var triggersEnter = options.triggersEnter || []; + options.triggersEnter = this._triggersEnter.concat(triggersEnter); - var triggersExit = options.triggersExit || []; - options.triggersExit = triggersExit.concat(this._triggersExit); + var triggersExit = options.triggersExit || []; + options.triggersExit = triggersExit.concat(this._triggersExit); - return this._router.route(pathDef, options, group); + return this._router.route(pathDef, options, group); }; -Group.prototype.group = function(options) { - return new Group(this._router, options, this); +Group.prototype.group = function (options) { + return new Group(this._router, options, this); }; -Group.prototype.callSubscriptions = function(current) { - if (this.parent) { - this.parent.callSubscriptions(current); - } +Group.prototype.callSubscriptions = function (current) { + if (this.parent) { + this.parent.callSubscriptions(current); + } - this._subscriptions.call(current.route, current.params, current.queryParams); + this._subscriptions.call(current.route, current.params, current.queryParams); }; diff --git a/apps/meteor/packages/flow-router/client/modules.js b/apps/meteor/packages/flow-router/client/modules.js index 7b734f449b30..c9e6e03a73f7 100644 --- a/apps/meteor/packages/flow-router/client/modules.js +++ b/apps/meteor/packages/flow-router/client/modules.js @@ -1,2 +1,2 @@ page = require('page'); -qs = require('qs'); +qs = require('qs'); diff --git a/apps/meteor/packages/flow-router/client/route.js b/apps/meteor/packages/flow-router/client/route.js index b82e9721380f..de8cfb8d3544 100644 --- a/apps/meteor/packages/flow-router/client/route.js +++ b/apps/meteor/packages/flow-router/client/route.js @@ -1,125 +1,123 @@ -Route = function(router, pathDef, options, group) { - options = options || {}; +Route = function (router, pathDef, options, group) { + options = options || {}; - this.options = options; - this.pathDef = pathDef + this.options = options; + this.pathDef = pathDef; - // Route.path is deprecated and will be removed in 3.0 - this.path = pathDef; + // Route.path is deprecated and will be removed in 3.0 + this.path = pathDef; - if (options.name) { - this.name = options.name; - } + if (options.name) { + this.name = options.name; + } - this._action = options.action || Function.prototype; - this._subscriptions = options.subscriptions || Function.prototype; - this._triggersEnter = options.triggersEnter || []; - this._triggersExit = options.triggersExit || []; - this._subsMap = {}; - this._router = router; + this._action = options.action || Function.prototype; + this._subscriptions = options.subscriptions || Function.prototype; + this._triggersEnter = options.triggersEnter || []; + this._triggersExit = options.triggersExit || []; + this._subsMap = {}; + this._router = router; - this._params = new ReactiveDict(); - this._queryParams = new ReactiveDict(); - this._routeCloseDep = new Tracker.Dependency(); + this._params = new ReactiveDict(); + this._queryParams = new ReactiveDict(); + this._routeCloseDep = new Tracker.Dependency(); - // tracks the changes in the URL - this._pathChangeDep = new Tracker.Dependency(); + // tracks the changes in the URL + this._pathChangeDep = new Tracker.Dependency(); - this.group = group; + this.group = group; }; -Route.prototype.clearSubscriptions = function() { - this._subsMap = {}; +Route.prototype.clearSubscriptions = function () { + this._subsMap = {}; }; -Route.prototype.register = function(name, sub, options) { - this._subsMap[name] = sub; +Route.prototype.register = function (name, sub, options) { + this._subsMap[name] = sub; }; - -Route.prototype.getSubscription = function(name) { - return this._subsMap[name]; +Route.prototype.getSubscription = function (name) { + return this._subsMap[name]; }; - -Route.prototype.getAllSubscriptions = function() { - return this._subsMap; +Route.prototype.getAllSubscriptions = function () { + return this._subsMap; }; -Route.prototype.callAction = function(current) { - var self = this; - self._action(current.params, current.queryParams); +Route.prototype.callAction = function (current) { + var self = this; + self._action(current.params, current.queryParams); }; -Route.prototype.callSubscriptions = function(current) { - this.clearSubscriptions(); - if (this.group) { - this.group.callSubscriptions(current); - } +Route.prototype.callSubscriptions = function (current) { + this.clearSubscriptions(); + if (this.group) { + this.group.callSubscriptions(current); + } - this._subscriptions(current.params, current.queryParams); + this._subscriptions(current.params, current.queryParams); }; -Route.prototype.getRouteName = function() { - this._routeCloseDep.depend(); - return this.name; +Route.prototype.getRouteName = function () { + this._routeCloseDep.depend(); + return this.name; }; -Route.prototype.getParam = function(key) { - this._routeCloseDep.depend(); - return this._params.get(key); +Route.prototype.getParam = function (key) { + this._routeCloseDep.depend(); + return this._params.get(key); }; -Route.prototype.getQueryParam = function(key) { - this._routeCloseDep.depend(); - return this._queryParams.get(key); +Route.prototype.getQueryParam = function (key) { + this._routeCloseDep.depend(); + return this._queryParams.get(key); }; -Route.prototype.watchPathChange = function() { - this._pathChangeDep.depend(); +Route.prototype.watchPathChange = function () { + this._pathChangeDep.depend(); }; -Route.prototype.registerRouteClose = function() { - this._params = new ReactiveDict(); - this._queryParams = new ReactiveDict(); - this._routeCloseDep.changed(); - this._pathChangeDep.changed(); +Route.prototype.registerRouteClose = function () { + this._params = new ReactiveDict(); + this._queryParams = new ReactiveDict(); + this._routeCloseDep.changed(); + this._pathChangeDep.changed(); }; -Route.prototype.registerRouteChange = function(currentContext, routeChanging) { - // register params - var params = currentContext.params; - this._updateReactiveDict(this._params, params); - - // register query params - var queryParams = currentContext.queryParams; - this._updateReactiveDict(this._queryParams, queryParams); - - // if the route is changing, we need to defer triggering path changing - // if we did this, old route's path watchers will detect this - // Real issue is, above watcher will get removed with the new route - // So, we don't need to trigger it now - // We are doing it on the route close event. So, if they exists they'll - // get notify that - if(!routeChanging) { - this._pathChangeDep.changed(); - } +Route.prototype.registerRouteChange = function (currentContext, routeChanging) { + // register params + var params = currentContext.params; + this._updateReactiveDict(this._params, params); + + // register query params + var queryParams = currentContext.queryParams; + this._updateReactiveDict(this._queryParams, queryParams); + + // if the route is changing, we need to defer triggering path changing + // if we did this, old route's path watchers will detect this + // Real issue is, above watcher will get removed with the new route + // So, we don't need to trigger it now + // We are doing it on the route close event. So, if they exists they'll + // get notify that + if (!routeChanging) { + this._pathChangeDep.changed(); + } }; -Route.prototype._updateReactiveDict = function(dict, newValues) { - var currentKeys = _.keys(newValues); - var oldKeys = _.keys(dict.keyDeps); - - // set new values - // params is an array. So, _.each(params) does not works - // to iterate params - _.each(currentKeys, function(key) { - dict.set(key, newValues[key]); - }); - - // remove keys which does not exisits here - var removedKeys = _.difference(oldKeys, currentKeys); - _.each(removedKeys, function(key) { - dict.set(key, undefined); - }); +Route.prototype._updateReactiveDict = function (dict, newValues) { + var currentKeys = _.keys(newValues); + var oldKeys = _.keys(dict.keyDeps); + + // set new values + // params is an array. So, _.each(params) does not works + // to iterate params + _.each(currentKeys, function (key) { + dict.set(key, newValues[key]); + }); + + // remove keys which does not exisits here + var removedKeys = _.difference(oldKeys, currentKeys); + _.each(removedKeys, function (key) { + dict.set(key, undefined); + }); }; diff --git a/apps/meteor/packages/flow-router/client/router.js b/apps/meteor/packages/flow-router/client/router.js index ae91751f2a72..3706b7b9fd53 100644 --- a/apps/meteor/packages/flow-router/client/router.js +++ b/apps/meteor/packages/flow-router/client/router.js @@ -1,586 +1,574 @@ Router = function () { - var self = this; - this.globals = []; - this.subscriptions = Function.prototype; - - this._tracker = this._buildTracker(); - this._current = {}; - - // tracks the current path change - this._onEveryPath = new Tracker.Dependency(); - - this._globalRoute = new Route(this); - - // holds onRoute callbacks - this._onRouteCallbacks = []; - - // if _askedToWait is true. We don't automatically start the router - // in Meteor.startup callback. (see client/_init.js) - // Instead user need to call `.initialize() - this._askedToWait = false; - this._initialized = false; - this._triggersEnter = []; - this._triggersExit = []; - this._routes = []; - this._routesMap = {}; - this._updateCallbacks(); - this.notFound = this.notfound = null; - // indicate it's okay (or not okay) to run the tracker - // when doing subscriptions - // using a number and increment it help us to support FlowRouter.go() - // and legitimate reruns inside tracker on the same event loop. - // this is a solution for #145 - this.safeToRun = 0; - - // Meteor exposes to the client the path prefix that was defined using the - // ROOT_URL environement variable on the server using the global runtime - // configuration. See #315. - this._basePath = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ''; - - // this is a chain contains a list of old routes - // most of the time, there is only one old route - // but when it's the time for a trigger redirect we've a chain - this._oldRouteChain = []; - - this.env = { - replaceState: new Meteor.EnvironmentVariable(), - reload: new Meteor.EnvironmentVariable(), - trailingSlash: new Meteor.EnvironmentVariable() - }; - - // redirect function used inside triggers - this._redirectFn = function(pathDef, fields, queryParams) { - if (/^http(s)?:\/\//.test(pathDef)) { - var message = "Redirects to URLs outside of the app are not supported in this version of Flow Router. Use 'window.location = yourUrl' instead"; - throw new Error(message); - } - self.withReplaceState(function() { - var path = FlowRouter.path(pathDef, fields, queryParams); - self._page.redirect(path); - }); - }; - this._initTriggersAPI(); + var self = this; + this.globals = []; + this.subscriptions = Function.prototype; + + this._tracker = this._buildTracker(); + this._current = {}; + + // tracks the current path change + this._onEveryPath = new Tracker.Dependency(); + + this._globalRoute = new Route(this); + + // holds onRoute callbacks + this._onRouteCallbacks = []; + + // if _askedToWait is true. We don't automatically start the router + // in Meteor.startup callback. (see client/_init.js) + // Instead user need to call `.initialize() + this._askedToWait = false; + this._initialized = false; + this._triggersEnter = []; + this._triggersExit = []; + this._routes = []; + this._routesMap = {}; + this._updateCallbacks(); + this.notFound = this.notfound = null; + // indicate it's okay (or not okay) to run the tracker + // when doing subscriptions + // using a number and increment it help us to support FlowRouter.go() + // and legitimate reruns inside tracker on the same event loop. + // this is a solution for #145 + this.safeToRun = 0; + + // Meteor exposes to the client the path prefix that was defined using the + // ROOT_URL environement variable on the server using the global runtime + // configuration. See #315. + this._basePath = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ''; + + // this is a chain contains a list of old routes + // most of the time, there is only one old route + // but when it's the time for a trigger redirect we've a chain + this._oldRouteChain = []; + + this.env = { + replaceState: new Meteor.EnvironmentVariable(), + reload: new Meteor.EnvironmentVariable(), + trailingSlash: new Meteor.EnvironmentVariable(), + }; + + // redirect function used inside triggers + this._redirectFn = function (pathDef, fields, queryParams) { + if (/^http(s)?:\/\//.test(pathDef)) { + var message = + "Redirects to URLs outside of the app are not supported in this version of Flow Router. Use 'window.location = yourUrl' instead"; + throw new Error(message); + } + self.withReplaceState(function () { + var path = FlowRouter.path(pathDef, fields, queryParams); + self._page.redirect(path); + }); + }; + this._initTriggersAPI(); }; -Router.prototype.route = function(pathDef, options, group) { - if (!/^\/.*/.test(pathDef)) { - var message = "route's path must start with '/'"; - throw new Error(message); - } - - options = options || {}; - var self = this; - var route = new Route(this, pathDef, options, group); - - // calls when the page route being activates - route._actionHandle = function (context, next) { - var oldRoute = self._current.route; - self._oldRouteChain.push(oldRoute); - - var queryParams = self._qs.parse(context.querystring); - // _qs.parse() gives us a object without prototypes, - // created with Object.create(null) - // Meteor's check doesn't play nice with it. - // So, we need to fix it by cloning it. - // see more: https://github.com/meteorhacks/flow-router/issues/164 - queryParams = JSON.parse(JSON.stringify(queryParams)); - - self._current = { - path: context.path, - context: context, - params: context.params, - queryParams: queryParams, - route: route, - oldRoute: oldRoute - }; - - // we need to invalidate if all the triggers have been completed - // if not that means, we've been redirected to another path - // then we don't need to invalidate - var afterAllTriggersRan = function() { - self._invalidateTracker(); - }; - - var triggers = self._triggersEnter.concat(route._triggersEnter); - Triggers.runTriggers( - triggers, - self._current, - self._redirectFn, - afterAllTriggersRan - ); - }; - - // calls when you exit from the page js route - route._exitHandle = function(context, next) { - var triggers = self._triggersExit.concat(route._triggersExit); - Triggers.runTriggers( - triggers, - self._current, - self._redirectFn, - next - ); - }; - - this._routes.push(route); - if (options.name) { - this._routesMap[options.name] = route; - } - - this._updateCallbacks(); - this._triggerRouteRegister(route); - - return route; +Router.prototype.route = function (pathDef, options, group) { + if (!/^\/.*/.test(pathDef)) { + var message = "route's path must start with '/'"; + throw new Error(message); + } + + options = options || {}; + var self = this; + var route = new Route(this, pathDef, options, group); + + // calls when the page route being activates + route._actionHandle = function (context, next) { + var oldRoute = self._current.route; + self._oldRouteChain.push(oldRoute); + + var queryParams = self._qs.parse(context.querystring); + // _qs.parse() gives us a object without prototypes, + // created with Object.create(null) + // Meteor's check doesn't play nice with it. + // So, we need to fix it by cloning it. + // see more: https://github.com/meteorhacks/flow-router/issues/164 + queryParams = JSON.parse(JSON.stringify(queryParams)); + + self._current = { + path: context.path, + context: context, + params: context.params, + queryParams: queryParams, + route: route, + oldRoute: oldRoute, + }; + + // we need to invalidate if all the triggers have been completed + // if not that means, we've been redirected to another path + // then we don't need to invalidate + var afterAllTriggersRan = function () { + self._invalidateTracker(); + }; + + var triggers = self._triggersEnter.concat(route._triggersEnter); + Triggers.runTriggers(triggers, self._current, self._redirectFn, afterAllTriggersRan); + }; + + // calls when you exit from the page js route + route._exitHandle = function (context, next) { + var triggers = self._triggersExit.concat(route._triggersExit); + Triggers.runTriggers(triggers, self._current, self._redirectFn, next); + }; + + this._routes.push(route); + if (options.name) { + this._routesMap[options.name] = route; + } + + this._updateCallbacks(); + this._triggerRouteRegister(route); + + return route; }; -Router.prototype.group = function(options) { - return new Group(this, options); +Router.prototype.group = function (options) { + return new Group(this, options); }; -Router.prototype.path = function(pathDef, fields, queryParams) { - if (this._routesMap[pathDef]) { - pathDef = this._routesMap[pathDef].pathDef; - } - - var path = ""; - - // Prefix the path with the router global prefix - if (this._basePath) { - path += "/" + this._basePath + "/"; - } - - fields = fields || {}; - var regExp = /(:[\w\(\)\\\+\*\.\?]+)+/g; - path += pathDef.replace(regExp, function(key) { - var firstRegexpChar = key.indexOf("("); - // get the content behind : and (\\d+/) - key = key.substring(1, (firstRegexpChar > 0)? firstRegexpChar: undefined); - // remove +?* - key = key.replace(/[\+\*\?]+/g, ""); - - // this is to allow page js to keep the custom characters as it is - // we need to encode 2 times otherwise "/" char does not work properly - // So, in that case, when I includes "/" it will think it's a part of the - // route. encoding 2times fixes it - return encodeURIComponent(encodeURIComponent(fields[key] || "")); - }); - - // Replace multiple slashes with single slash - path = path.replace(/\/\/+/g, "/"); - - // remove trailing slash - // but keep the root slash if it's the only one - path = path.match(/^\/{1}$/) ? path: path.replace(/\/$/, ""); - - // explictly asked to add a trailing slash - if(this.env.trailingSlash.get() && _.last(path) !== "/") { - path += "/"; - } - - var strQueryParams = this._qs.stringify(queryParams || {}); - if(strQueryParams) { - path += "?" + strQueryParams; - } - - return path; +Router.prototype.path = function (pathDef, fields, queryParams) { + if (this._routesMap[pathDef]) { + pathDef = this._routesMap[pathDef].pathDef; + } + + var path = ''; + + // Prefix the path with the router global prefix + if (this._basePath) { + path += '/' + this._basePath + '/'; + } + + fields = fields || {}; + var regExp = /(:[\w\(\)\\\+\*\.\?]+)+/g; + path += pathDef.replace(regExp, function (key) { + var firstRegexpChar = key.indexOf('('); + // get the content behind : and (\\d+/) + key = key.substring(1, firstRegexpChar > 0 ? firstRegexpChar : undefined); + // remove +?* + key = key.replace(/[\+\*\?]+/g, ''); + + // this is to allow page js to keep the custom characters as it is + // we need to encode 2 times otherwise "/" char does not work properly + // So, in that case, when I includes "/" it will think it's a part of the + // route. encoding 2times fixes it + return encodeURIComponent(encodeURIComponent(fields[key] || '')); + }); + + // Replace multiple slashes with single slash + path = path.replace(/\/\/+/g, '/'); + + // remove trailing slash + // but keep the root slash if it's the only one + path = path.match(/^\/{1}$/) ? path : path.replace(/\/$/, ''); + + // explictly asked to add a trailing slash + if (this.env.trailingSlash.get() && _.last(path) !== '/') { + path += '/'; + } + + var strQueryParams = this._qs.stringify(queryParams || {}); + if (strQueryParams) { + path += '?' + strQueryParams; + } + + return path; }; -Router.prototype.go = function(pathDef, fields, queryParams) { - var path = this.path(pathDef, fields, queryParams); +Router.prototype.go = function (pathDef, fields, queryParams) { + var path = this.path(pathDef, fields, queryParams); - var useReplaceState = this.env.replaceState.get(); - if(useReplaceState) { - this._page.replace(path); - } else { - this._page(path); - } + var useReplaceState = this.env.replaceState.get(); + if (useReplaceState) { + this._page.replace(path); + } else { + this._page(path); + } }; -Router.prototype.reload = function() { - var self = this; +Router.prototype.reload = function () { + var self = this; - self.env.reload.withValue(true, function() { - self._page.replace(self._current.path); - }); + self.env.reload.withValue(true, function () { + self._page.replace(self._current.path); + }); }; -Router.prototype.redirect = function(path) { - this._page.redirect(path); +Router.prototype.redirect = function (path) { + this._page.redirect(path); }; -Router.prototype.setParams = function(newParams) { - if(!this._current.route) {return false;} +Router.prototype.setParams = function (newParams) { + if (!this._current.route) { + return false; + } - var pathDef = this._current.route.pathDef; - var existingParams = this._current.params; - var params = {}; - _.each(_.keys(existingParams), function(key) { - params[key] = existingParams[key]; - }); + var pathDef = this._current.route.pathDef; + var existingParams = this._current.params; + var params = {}; + _.each(_.keys(existingParams), function (key) { + params[key] = existingParams[key]; + }); - params = _.extend(params, newParams); - var queryParams = this._current.queryParams; + params = _.extend(params, newParams); + var queryParams = this._current.queryParams; - this.go(pathDef, params, queryParams); - return true; + this.go(pathDef, params, queryParams); + return true; }; -Router.prototype.setQueryParams = function(newParams) { - if(!this._current.route) {return false;} +Router.prototype.setQueryParams = function (newParams) { + if (!this._current.route) { + return false; + } - var queryParams = _.clone(this._current.queryParams); - _.extend(queryParams, newParams); + var queryParams = _.clone(this._current.queryParams); + _.extend(queryParams, newParams); - for (var k in queryParams) { - if (queryParams[k] === null || queryParams[k] === undefined) { - delete queryParams[k]; - } - } + for (var k in queryParams) { + if (queryParams[k] === null || queryParams[k] === undefined) { + delete queryParams[k]; + } + } - var pathDef = this._current.route.pathDef; - var params = this._current.params; - this.go(pathDef, params, queryParams); - return true; + var pathDef = this._current.route.pathDef; + var params = this._current.params; + this.go(pathDef, params, queryParams); + return true; }; // .current is not reactive // This is by design. use .getParam() instead // If you really need to watch the path change, use .watchPathChange() -Router.prototype.current = function() { - // We can't trust outside, that's why we clone this - // Anyway, we can't clone the whole object since it has non-jsonable values - // That's why we clone what's really needed. - var current = _.clone(this._current); - current.queryParams = EJSON.clone(current.queryParams); - current.params = EJSON.clone(current.params); - return current; +Router.prototype.current = function () { + // We can't trust outside, that's why we clone this + // Anyway, we can't clone the whole object since it has non-jsonable values + // That's why we clone what's really needed. + var current = _.clone(this._current); + current.queryParams = EJSON.clone(current.queryParams); + current.params = EJSON.clone(current.params); + return current; }; // Implementing Reactive APIs -var reactiveApis = [ - 'getParam', 'getQueryParam', - 'getRouteName', 'watchPathChange' -]; -reactiveApis.forEach(function(api) { - Router.prototype[api] = function(arg1) { - // when this is calling, there may not be any route initiated - // so we need to handle it - var currentRoute = this._current.route; - if(!currentRoute) { - this._onEveryPath.depend(); - return; - } - - // currently, there is only one argument. If we've more let's add more args - // this is not clean code, but better in performance - return currentRoute[api].call(currentRoute, arg1); - }; +var reactiveApis = ['getParam', 'getQueryParam', 'getRouteName', 'watchPathChange']; +reactiveApis.forEach(function (api) { + Router.prototype[api] = function (arg1) { + // when this is calling, there may not be any route initiated + // so we need to handle it + var currentRoute = this._current.route; + if (!currentRoute) { + this._onEveryPath.depend(); + return; + } + + // currently, there is only one argument. If we've more let's add more args + // this is not clean code, but better in performance + return currentRoute[api].call(currentRoute, arg1); + }; }); -Router.prototype.subsReady = function() { - var callback = null; - var args = _.toArray(arguments); - - if (typeof _.last(args) === "function") { - callback = args.pop(); - } - - var currentRoute = this.current().route; - var globalRoute = this._globalRoute; - - // we need to depend for every route change and - // rerun subscriptions to check the ready state - this._onEveryPath.depend(); - - if(!currentRoute) { - return false; - } - - var subscriptions; - if(args.length === 0) { - subscriptions = _.values(globalRoute.getAllSubscriptions()); - subscriptions = subscriptions.concat(_.values(currentRoute.getAllSubscriptions())); - } else { - subscriptions = _.map(args, function(subName) { - return globalRoute.getSubscription(subName) || currentRoute.getSubscription(subName); - }); - } - - var isReady = function() { - var ready = _.every(subscriptions, function(sub) { - return sub && sub.ready(); - }); - - return ready; - }; - - if (callback) { - Tracker.autorun(function(c) { - if (isReady()) { - callback(); - c.stop(); - } - }); - } else { - return isReady(); - } +Router.prototype.subsReady = function () { + var callback = null; + var args = _.toArray(arguments); + + if (typeof _.last(args) === 'function') { + callback = args.pop(); + } + + var currentRoute = this.current().route; + var globalRoute = this._globalRoute; + + // we need to depend for every route change and + // rerun subscriptions to check the ready state + this._onEveryPath.depend(); + + if (!currentRoute) { + return false; + } + + var subscriptions; + if (args.length === 0) { + subscriptions = _.values(globalRoute.getAllSubscriptions()); + subscriptions = subscriptions.concat(_.values(currentRoute.getAllSubscriptions())); + } else { + subscriptions = _.map(args, function (subName) { + return globalRoute.getSubscription(subName) || currentRoute.getSubscription(subName); + }); + } + + var isReady = function () { + var ready = _.every(subscriptions, function (sub) { + return sub && sub.ready(); + }); + + return ready; + }; + + if (callback) { + Tracker.autorun(function (c) { + if (isReady()) { + callback(); + c.stop(); + } + }); + } else { + return isReady(); + } }; -Router.prototype.withReplaceState = function(fn) { - return this.env.replaceState.withValue(true, fn); +Router.prototype.withReplaceState = function (fn) { + return this.env.replaceState.withValue(true, fn); }; -Router.prototype.withTrailingSlash = function(fn) { - return this.env.trailingSlash.withValue(true, fn); +Router.prototype.withTrailingSlash = function (fn) { + return this.env.trailingSlash.withValue(true, fn); }; -Router.prototype._notfoundRoute = function(context) { - this._current = { - path: context.path, - context: context, - params: [], - queryParams: {}, - }; - - // XXX this.notfound kept for backwards compatibility - this.notFound = this.notFound || this.notfound; - if(!this.notFound) { - console.error("There is no route for the path:", context.path); - return; - } - - this._current.route = new Route(this, "*", this.notFound); - this._invalidateTracker(); +Router.prototype._notfoundRoute = function (context) { + this._current = { + path: context.path, + context: context, + params: [], + queryParams: {}, + }; + + // XXX this.notfound kept for backwards compatibility + this.notFound = this.notFound || this.notfound; + if (!this.notFound) { + console.error('There is no route for the path:', context.path); + return; + } + + this._current.route = new Route(this, '*', this.notFound); + this._invalidateTracker(); }; -Router.prototype.initialize = function(options) { - options = options || {}; - - if(this._initialized) { - throw new Error("FlowRouter is already initialized"); - } - - var self = this; - this._updateCallbacks(); - - // Implementing idempotent routing - // by overriding page.js`s "show" method. - // Why? - // It is impossible to bypass exit triggers, - // because they execute before the handler and - // can not know what the next path is, inside exit trigger. - // - // we need override both show, replace to make this work - // since we use redirect when we are talking about withReplaceState - _.each(['show', 'replace'], function(fnName) { - var original = self._page[fnName]; - self._page[fnName] = function(path, state, dispatch, push) { - var reload = self.env.reload.get(); - if (!reload && self._current.path === path) { - return; - } - - original.call(this, path, state, dispatch, push); - }; - }); - - // this is very ugly part of pagejs and it does decoding few times - // in unpredicatable manner. See #168 - // this is the default behaviour and we need keep it like that - // we are doing a hack. see .path() - this._page.base(this._basePath); - this._page({ - decodeURLComponents: true, - hashbang: !!options.hashbang - }); - - this._initialized = true; +Router.prototype.initialize = function (options) { + options = options || {}; + + if (this._initialized) { + throw new Error('FlowRouter is already initialized'); + } + + var self = this; + this._updateCallbacks(); + + // Implementing idempotent routing + // by overriding page.js`s "show" method. + // Why? + // It is impossible to bypass exit triggers, + // because they execute before the handler and + // can not know what the next path is, inside exit trigger. + // + // we need override both show, replace to make this work + // since we use redirect when we are talking about withReplaceState + _.each(['show', 'replace'], function (fnName) { + var original = self._page[fnName]; + self._page[fnName] = function (path, state, dispatch, push) { + var reload = self.env.reload.get(); + if (!reload && self._current.path === path) { + return; + } + + original.call(this, path, state, dispatch, push); + }; + }); + + // this is very ugly part of pagejs and it does decoding few times + // in unpredicatable manner. See #168 + // this is the default behaviour and we need keep it like that + // we are doing a hack. see .path() + this._page.base(this._basePath); + this._page({ + decodeURLComponents: true, + hashbang: !!options.hashbang, + }); + + this._initialized = true; }; -Router.prototype._buildTracker = function() { - var self = this; - - // main autorun function - var tracker = Tracker.autorun(function () { - if(!self._current || !self._current.route) { - return; - } - - // see the definition of `this._processingContexts` - var currentContext = self._current; - var route = currentContext.route; - var path = currentContext.path; - - if(self.safeToRun === 0) { - var message = - "You can't use reactive data sources like Session" + - " inside the `.subscriptions` method!"; - throw new Error(message); - } - - // We need to run subscriptions inside a Tracker - // to stop subs when switching between routes - // But we don't need to run this tracker with - // other reactive changes inside the .subscription method - // We tackle this with the `safeToRun` variable - self._globalRoute.clearSubscriptions(); - self.subscriptions.call(self._globalRoute, path); - route.callSubscriptions(currentContext); - - // otherwise, computations inside action will trigger to re-run - // this computation. which we do not need. - Tracker.nonreactive(function() { - var isRouteChange = currentContext.oldRoute !== currentContext.route; - var isFirstRoute = !currentContext.oldRoute; - // first route is not a route change - if(isFirstRoute) { - isRouteChange = false; - } - - // Clear oldRouteChain just before calling the action - // We still need to get a copy of the oldestRoute first - // It's very important to get the oldest route and registerRouteClose() it - // See: https://github.com/kadirahq/flow-router/issues/314 - var oldestRoute = self._oldRouteChain[0]; - self._oldRouteChain = []; - - currentContext.route.registerRouteChange(currentContext, isRouteChange); - route.callAction(currentContext); - - Tracker.afterFlush(function() { - self._onEveryPath.changed(); - if(isRouteChange) { - // We need to trigger that route (definition itself) has changed. - // So, we need to re-run all the register callbacks to current route - // This is pretty important, otherwise tracker - // can't identify new route's items - - // We also need to afterFlush, otherwise this will re-run - // helpers on templates which are marked for destroying - if(oldestRoute) { - oldestRoute.registerRouteClose(); - } - } - }); - }); - - self.safeToRun--; - }); - - return tracker; +Router.prototype._buildTracker = function () { + var self = this; + + // main autorun function + var tracker = Tracker.autorun(function () { + if (!self._current || !self._current.route) { + return; + } + + // see the definition of `this._processingContexts` + var currentContext = self._current; + var route = currentContext.route; + var path = currentContext.path; + + if (self.safeToRun === 0) { + var message = "You can't use reactive data sources like Session" + ' inside the `.subscriptions` method!'; + throw new Error(message); + } + + // We need to run subscriptions inside a Tracker + // to stop subs when switching between routes + // But we don't need to run this tracker with + // other reactive changes inside the .subscription method + // We tackle this with the `safeToRun` variable + self._globalRoute.clearSubscriptions(); + self.subscriptions.call(self._globalRoute, path); + route.callSubscriptions(currentContext); + + // otherwise, computations inside action will trigger to re-run + // this computation. which we do not need. + Tracker.nonreactive(function () { + var isRouteChange = currentContext.oldRoute !== currentContext.route; + var isFirstRoute = !currentContext.oldRoute; + // first route is not a route change + if (isFirstRoute) { + isRouteChange = false; + } + + // Clear oldRouteChain just before calling the action + // We still need to get a copy of the oldestRoute first + // It's very important to get the oldest route and registerRouteClose() it + // See: https://github.com/kadirahq/flow-router/issues/314 + var oldestRoute = self._oldRouteChain[0]; + self._oldRouteChain = []; + + currentContext.route.registerRouteChange(currentContext, isRouteChange); + route.callAction(currentContext); + + Tracker.afterFlush(function () { + self._onEveryPath.changed(); + if (isRouteChange) { + // We need to trigger that route (definition itself) has changed. + // So, we need to re-run all the register callbacks to current route + // This is pretty important, otherwise tracker + // can't identify new route's items + + // We also need to afterFlush, otherwise this will re-run + // helpers on templates which are marked for destroying + if (oldestRoute) { + oldestRoute.registerRouteClose(); + } + } + }); + }); + + self.safeToRun--; + }); + + return tracker; }; -Router.prototype._invalidateTracker = function() { - var self = this; - this.safeToRun++; - this._tracker.invalidate(); - // After the invalidation we need to flush to make changes imediately - // otherwise, we have face some issues context mix-maches and so on. - // But there are some cases we can't flush. So we need to ready for that. - - // we clearly know, we can't flush inside an autorun - // this may leads some issues on flow-routing - // we may need to do some warning - if(!Tracker.currentComputation) { - // Still there are some cases where we can't flush - // eg:- when there is a flush currently - // But we've no public API or hacks to get that state - // So, this is the only solution - try { - Tracker.flush(); - } catch(ex) { - // only handling "while flushing" errors - if(!/Tracker\.flush while flushing/.test(ex.message)) { - return; - } - - // XXX: fix this with a proper solution by removing subscription mgt. - // from the router. Then we don't need to run invalidate using a tracker - - // this happens when we are trying to invoke a route change - // with inside a route chnage. (eg:- Template.onCreated) - // Since we use page.js and tracker, we don't have much control - // over this process. - // only solution is to defer route execution. - - // It's possible to have more than one path want to defer - // But, we only need to pick the last one. - // self._nextPath = self._current.path; - Meteor.defer(function() { - var path = self._nextPath; - if(!path) { - return; - } - - delete self._nextPath; - self.env.reload.withValue(true, function() { - self.go(path); - }); - }); - } - } +Router.prototype._invalidateTracker = function () { + var self = this; + this.safeToRun++; + this._tracker.invalidate(); + // After the invalidation we need to flush to make changes imediately + // otherwise, we have face some issues context mix-maches and so on. + // But there are some cases we can't flush. So we need to ready for that. + + // we clearly know, we can't flush inside an autorun + // this may leads some issues on flow-routing + // we may need to do some warning + if (!Tracker.currentComputation) { + // Still there are some cases where we can't flush + // eg:- when there is a flush currently + // But we've no public API or hacks to get that state + // So, this is the only solution + try { + Tracker.flush(); + } catch (ex) { + // only handling "while flushing" errors + if (!/Tracker\.flush while flushing/.test(ex.message)) { + return; + } + + // XXX: fix this with a proper solution by removing subscription mgt. + // from the router. Then we don't need to run invalidate using a tracker + + // this happens when we are trying to invoke a route change + // with inside a route chnage. (eg:- Template.onCreated) + // Since we use page.js and tracker, we don't have much control + // over this process. + // only solution is to defer route execution. + + // It's possible to have more than one path want to defer + // But, we only need to pick the last one. + // self._nextPath = self._current.path; + Meteor.defer(function () { + var path = self._nextPath; + if (!path) { + return; + } + + delete self._nextPath; + self.env.reload.withValue(true, function () { + self.go(path); + }); + }); + } + } }; Router.prototype._updateCallbacks = function () { - var self = this; + var self = this; - self._page.callbacks = []; - self._page.exits = []; + self._page.callbacks = []; + self._page.exits = []; - _.each(self._routes, function(route) { - self._page(route.pathDef, route._actionHandle); - self._page.exit(route.pathDef, route._exitHandle); - }); + _.each(self._routes, function (route) { + self._page(route.pathDef, route._actionHandle); + self._page.exit(route.pathDef, route._exitHandle); + }); - self._page("*", function(context) { - self._notfoundRoute(context); - }); + self._page('*', function (context) { + self._notfoundRoute(context); + }); }; -Router.prototype._initTriggersAPI = function() { - var self = this; - this.triggers = { - enter: function(triggers, filter) { - triggers = Triggers.applyFilters(triggers, filter); - if(triggers.length) { - self._triggersEnter = self._triggersEnter.concat(triggers); - } - }, - - exit: function(triggers, filter) { - triggers = Triggers.applyFilters(triggers, filter); - if(triggers.length) { - self._triggersExit = self._triggersExit.concat(triggers); - } - } - }; +Router.prototype._initTriggersAPI = function () { + var self = this; + this.triggers = { + enter: function (triggers, filter) { + triggers = Triggers.applyFilters(triggers, filter); + if (triggers.length) { + self._triggersEnter = self._triggersEnter.concat(triggers); + } + }, + + exit: function (triggers, filter) { + triggers = Triggers.applyFilters(triggers, filter); + if (triggers.length) { + self._triggersExit = self._triggersExit.concat(triggers); + } + }, + }; }; -Router.prototype.wait = function() { - if(this._initialized) { - throw new Error("can't wait after FlowRouter has been initialized"); - } +Router.prototype.wait = function () { + if (this._initialized) { + throw new Error("can't wait after FlowRouter has been initialized"); + } - this._askedToWait = true; + this._askedToWait = true; }; -Router.prototype.onRouteRegister = function(cb) { - this._onRouteCallbacks.push(cb); +Router.prototype.onRouteRegister = function (cb) { + this._onRouteCallbacks.push(cb); }; -Router.prototype._triggerRouteRegister = function(currentRoute) { - // We should only need to send a safe set of fields on the route - // object. - // This is not to hide what's inside the route object, but to show - // these are the public APIs - var routePublicApi = _.pick(currentRoute, 'name', 'pathDef', 'path'); - var omittingOptionFields = [ - 'triggersEnter', 'triggersExit', 'action', 'subscriptions', 'name' - ]; - routePublicApi.options = _.omit(currentRoute.options, omittingOptionFields); - - _.each(this._onRouteCallbacks, function(cb) { - cb(routePublicApi); - }); +Router.prototype._triggerRouteRegister = function (currentRoute) { + // We should only need to send a safe set of fields on the route + // object. + // This is not to hide what's inside the route object, but to show + // these are the public APIs + var routePublicApi = _.pick(currentRoute, 'name', 'pathDef', 'path'); + var omittingOptionFields = ['triggersEnter', 'triggersExit', 'action', 'subscriptions', 'name']; + routePublicApi.options = _.omit(currentRoute.options, omittingOptionFields); + + _.each(this._onRouteCallbacks, function (cb) { + cb(routePublicApi); + }); }; Router.prototype._page = page; diff --git a/apps/meteor/packages/flow-router/client/triggers.js b/apps/meteor/packages/flow-router/client/triggers.js index 7733332ca513..3f4c04ba32f9 100644 --- a/apps/meteor/packages/flow-router/client/triggers.js +++ b/apps/meteor/packages/flow-router/client/triggers.js @@ -4,109 +4,109 @@ Triggers = {}; // Apply filters for a set of triggers // @triggers - a set of triggers -// @filter - filter with array fileds with `only` and `except` +// @filter - filter with array fileds with `only` and `except` // support only either `only` or `except`, but not both -Triggers.applyFilters = function(triggers, filter) { - if(!(triggers instanceof Array)) { - triggers = [triggers]; - } +Triggers.applyFilters = function (triggers, filter) { + if (!(triggers instanceof Array)) { + triggers = [triggers]; + } - if(!filter) { - return triggers; - } + if (!filter) { + return triggers; + } - if(filter.only && filter.except) { - throw new Error("Triggers don't support only and except filters at once"); - } + if (filter.only && filter.except) { + throw new Error("Triggers don't support only and except filters at once"); + } - if(filter.only && !(filter.only instanceof Array)) { - throw new Error("only filters needs to be an array"); - } + if (filter.only && !(filter.only instanceof Array)) { + throw new Error('only filters needs to be an array'); + } - if(filter.except && !(filter.except instanceof Array)) { - throw new Error("except filters needs to be an array"); - } + if (filter.except && !(filter.except instanceof Array)) { + throw new Error('except filters needs to be an array'); + } - if(filter.only) { - return Triggers.createRouteBoundTriggers(triggers, filter.only); - } + if (filter.only) { + return Triggers.createRouteBoundTriggers(triggers, filter.only); + } - if(filter.except) { - return Triggers.createRouteBoundTriggers(triggers, filter.except, true); - } + if (filter.except) { + return Triggers.createRouteBoundTriggers(triggers, filter.except, true); + } - throw new Error("Provided a filter but not supported"); + throw new Error('Provided a filter but not supported'); }; // create triggers by bounding them to a set of route names -// @triggers - a set of triggers +// @triggers - a set of triggers // @names - list of route names to be bound (trigger runs only for these names) // @negate - negate the result (triggers won't run for above names) -Triggers.createRouteBoundTriggers = function(triggers, names, negate) { - var namesMap = {}; - _.each(names, function(name) { - namesMap[name] = true; - }); - - var filteredTriggers = _.map(triggers, function(originalTrigger) { - var modifiedTrigger = function(context, next) { - var routeName = context.route.name; - var matched = (namesMap[routeName])? 1: -1; - matched = (negate)? matched * -1 : matched; - - if(matched === 1) { - originalTrigger(context, next); - } - }; - return modifiedTrigger; - }); - - return filteredTriggers; +Triggers.createRouteBoundTriggers = function (triggers, names, negate) { + var namesMap = {}; + _.each(names, function (name) { + namesMap[name] = true; + }); + + var filteredTriggers = _.map(triggers, function (originalTrigger) { + var modifiedTrigger = function (context, next) { + var routeName = context.route.name; + var matched = namesMap[routeName] ? 1 : -1; + matched = negate ? matched * -1 : matched; + + if (matched === 1) { + originalTrigger(context, next); + } + }; + return modifiedTrigger; + }); + + return filteredTriggers; }; // run triggers and abort if redirected or callback stopped -// @triggers - a set of triggers +// @triggers - a set of triggers // @context - context we need to pass (it must have the route) -// @redirectFn - function which used to redirect +// @redirectFn - function which used to redirect // @after - called after if only all the triggers runs -Triggers.runTriggers = function(triggers, context, redirectFn, after) { - var abort = false; - var inCurrentLoop = true; - var alreadyRedirected = false; - - for(var lc=0; lc 0)? firstRegexpChar: undefined); - // remove +?* - key = key.replace(/[\+\*\?]+/g, ""); + fields = fields || {}; + var regExp = /(:[\w\(\)\\\+\*\.\?]+)+/g; + var path = pathDef.replace(regExp, function (key) { + var firstRegexpChar = key.indexOf('('); + // get the content behind : and (\\d+/) + key = key.substring(1, firstRegexpChar > 0 ? firstRegexpChar : undefined); + // remove +?* + key = key.replace(/[\+\*\?]+/g, ''); - return fields[key] || ""; - }); + return fields[key] || ''; + }); - path = path.replace(/\/\/+/g, "/"); // Replace multiple slashes with single slash + path = path.replace(/\/\/+/g, '/'); // Replace multiple slashes with single slash - // remove trailing slash - // but keep the root slash if it's the only one - path = path.match(/^\/{1}$/) ? path: path.replace(/\/$/, ""); + // remove trailing slash + // but keep the root slash if it's the only one + path = path.match(/^\/{1}$/) ? path : path.replace(/\/$/, ''); - var strQueryParams = Qs.stringify(queryParams || {}); - if(strQueryParams) { - path += "?" + strQueryParams; - } + var strQueryParams = Qs.stringify(queryParams || {}); + if (strQueryParams) { + path += '?' + strQueryParams; + } - return path; + return path; }; -Router.prototype.onRouteRegister = function(cb) { - this._onRouteCallbacks.push(cb); +Router.prototype.onRouteRegister = function (cb) { + this._onRouteCallbacks.push(cb); }; -Router.prototype._triggerRouteRegister = function(currentRoute) { - // We should only need to send a safe set of fields on the route - // object. - // This is not to hide what's inside the route object, but to show - // these are the public APIs - var routePublicApi = _.pick(currentRoute, 'name', 'pathDef', 'path'); - var omittingOptionFields = [ - 'triggersEnter', 'triggersExit', 'action', 'subscriptions', 'name' - ]; - routePublicApi.options = _.omit(currentRoute.options, omittingOptionFields); +Router.prototype._triggerRouteRegister = function (currentRoute) { + // We should only need to send a safe set of fields on the route + // object. + // This is not to hide what's inside the route object, but to show + // these are the public APIs + var routePublicApi = _.pick(currentRoute, 'name', 'pathDef', 'path'); + var omittingOptionFields = ['triggersEnter', 'triggersExit', 'action', 'subscriptions', 'name']; + routePublicApi.options = _.omit(currentRoute.options, omittingOptionFields); - _.each(this._onRouteCallbacks, function(cb) { - cb(routePublicApi); - }); + _.each(this._onRouteCallbacks, function (cb) { + cb(routePublicApi); + }); }; - -Router.prototype.go = function() { - // client only +Router.prototype.go = function () { + // client only }; - -Router.prototype.current = function() { - // client only +Router.prototype.current = function () { + // client only }; - Router.prototype.triggers = { - enter: function() { - // client only - }, - exit: function() { - // client only - } + enter: function () { + // client only + }, + exit: function () { + // client only + }, }; -Router.prototype.middleware = function() { - // client only +Router.prototype.middleware = function () { + // client only }; - -Router.prototype.getState = function() { - // client only +Router.prototype.getState = function () { + // client only }; - -Router.prototype.getAllStates = function() { - // client only +Router.prototype.getAllStates = function () { + // client only }; - -Router.prototype.setState = function() { - // client only +Router.prototype.setState = function () { + // client only }; - -Router.prototype.removeState = function() { - // client only +Router.prototype.removeState = function () { + // client only }; - -Router.prototype.clearStates = function() { - // client only +Router.prototype.clearStates = function () { + // client only }; - -Router.prototype.ready = function() { - // client only +Router.prototype.ready = function () { + // client only }; - -Router.prototype.initialize = function() { - // client only +Router.prototype.initialize = function () { + // client only }; -Router.prototype.wait = function() { - // client only +Router.prototype.wait = function () { + // client only }; diff --git a/apps/meteor/packages/linkedin-oauth/linkedin-server.js b/apps/meteor/packages/linkedin-oauth/linkedin-server.js index 09a12a528dda..20b6ab3fae28 100644 --- a/apps/meteor/packages/linkedin-oauth/linkedin-server.js +++ b/apps/meteor/packages/linkedin-oauth/linkedin-server.js @@ -44,7 +44,9 @@ const getTokenResponse = async function (query) { const expiresIn = responseContent.expires_in; if (!accessToken) { - throw new Error(`Failed to complete OAuth handshake with Linkedin -- can't find access token in HTTP response. ${JSON.stringify(responseContent)}`); + throw new Error( + `Failed to complete OAuth handshake with Linkedin -- can't find access token in HTTP response. ${JSON.stringify(responseContent)}`, + ); } return { @@ -56,9 +58,7 @@ const getTokenResponse = async function (query) { // Request available fields from profile const getIdentity = async function (accessToken) { try { - const url = encodeURI( - `https://api.linkedin.com/v2/userinfo`, - ); + const url = encodeURI(`https://api.linkedin.com/v2/userinfo`); const request = await fetch(url, { method: 'GET', headers: { @@ -93,7 +93,7 @@ OAuth.registerService('linkedin', 2, null, async (query) => { lastName: family_name, profilePicture: picture, emailAddress: email, - email + email, }; const serviceData = { diff --git a/apps/meteor/packages/meteor-cookies/cookies.js b/apps/meteor/packages/meteor-cookies/cookies.js index f560babbfc90..8694ec04ef62 100644 --- a/apps/meteor/packages/meteor-cookies/cookies.js +++ b/apps/meteor/packages/meteor-cookies/cookies.js @@ -4,33 +4,37 @@ let fetch; let WebApp; if (Meteor.isServer) { - WebApp = require('meteor/webapp').WebApp; + WebApp = require('meteor/webapp').WebApp; } else { - fetch = require('meteor/fetch').fetch; + fetch = require('meteor/fetch').fetch; } -const NoOp = () => {}; +const NoOp = () => {}; const urlRE = /\/___cookie___\/set/; -const rootUrl = Meteor.isServer ? process.env.ROOT_URL : (window.__meteor_runtime_config__.ROOT_URL || window.__meteor_runtime_config__.meteorEnv.ROOT_URL || false); -const mobileRootUrl = Meteor.isServer ? process.env.MOBILE_ROOT_URL : (window.__meteor_runtime_config__.MOBILE_ROOT_URL || window.__meteor_runtime_config__.meteorEnv.MOBILE_ROOT_URL || false); +const rootUrl = Meteor.isServer + ? process.env.ROOT_URL + : window.__meteor_runtime_config__.ROOT_URL || window.__meteor_runtime_config__.meteorEnv.ROOT_URL || false; +const mobileRootUrl = Meteor.isServer + ? process.env.MOBILE_ROOT_URL + : window.__meteor_runtime_config__.MOBILE_ROOT_URL || window.__meteor_runtime_config__.meteorEnv.MOBILE_ROOT_URL || false; const helpers = { - isUndefined(obj) { - return obj === void 0; - }, - isArray(obj) { - return Array.isArray(obj); - }, - clone(obj) { - if (!this.isObject(obj)) return obj; - return this.isArray(obj) ? obj.slice() : Object.assign({}, obj); - } + isUndefined(obj) { + return obj === void 0; + }, + isArray(obj) { + return Array.isArray(obj); + }, + clone(obj) { + if (!this.isObject(obj)) return obj; + return this.isArray(obj) ? obj.slice() : Object.assign({}, obj); + }, }; const _helpers = ['Number', 'Object', 'Function']; for (let i = 0; i < _helpers.length; i++) { - helpers['is' + _helpers[i]] = function (obj) { - return Object.prototype.toString.call(obj) === '[object ' + _helpers[i] + ']'; - }; + helpers['is' + _helpers[i]] = function (obj) { + return Object.prototype.toString.call(obj) === '[object ' + _helpers[i] + ']'; + }; } /** @@ -84,11 +88,11 @@ const fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; * @private */ const tryDecode = (str, d) => { - try { - return d(str); - } catch (e) { - return str; - } + try { + return d(str); + } catch (e) { + return str; + } }; /** @@ -104,31 +108,31 @@ const tryDecode = (str, d) => { * @private */ const parse = (str, options) => { - if (typeof str !== 'string') { - throw new Meteor.Error(404, 'argument str must be a string'); - } - const obj = {}; - const opt = options || {}; - let val; - let key; - let eqIndx; - - str.split(pairSplitRegExp).forEach((pair) => { - eqIndx = pair.indexOf('='); - if (eqIndx < 0) { - return; - } - key = pair.substr(0, eqIndx).trim(); - key = tryDecode(unescape(key), (opt.decode || decode)); - val = pair.substr(++eqIndx, pair.length).trim(); - if (val[0] === '"') { - val = val.slice(1, -1); - } - if (void 0 === obj[key]) { - obj[key] = tryDecode(val, (opt.decode || decode)); - } - }); - return obj; + if (typeof str !== 'string') { + throw new Meteor.Error(404, 'argument str must be a string'); + } + const obj = {}; + const opt = options || {}; + let val; + let key; + let eqIndx; + + str.split(pairSplitRegExp).forEach((pair) => { + eqIndx = pair.indexOf('='); + if (eqIndx < 0) { + return; + } + key = pair.substr(0, eqIndx).trim(); + key = tryDecode(unescape(key), opt.decode || decode); + val = pair.substr(++eqIndx, pair.length).trim(); + if (val[0] === '"') { + val = val.slice(1, -1); + } + if (void 0 === obj[key]) { + obj[key] = tryDecode(val, opt.decode || decode); + } + }); + return obj; }; /** @@ -138,17 +142,17 @@ const parse = (str, options) => { * @private */ const antiCircular = (_obj) => { - const object = helpers.clone(_obj); - const cache = new Map(); - return JSON.stringify(object, (key, value) => { - if (typeof value === 'object' && value !== null) { - if (cache.get(value)) { - return void 0; - } - cache.set(value, true); - } - return value; - }); + const object = helpers.clone(_obj); + const cache = new Map(); + return JSON.stringify(object, (key, value) => { + if (typeof value === 'object' && value !== null) { + if (cache.get(value)) { + return void 0; + } + cache.set(value, true); + } + return value; + }); }; /** @@ -166,109 +170,109 @@ const antiCircular = (_obj) => { * @private */ const serialize = (key, val, opt = {}) => { - let name; - - if (!fieldContentRegExp.test(key)) { - name = escape(key); - } else { - name = key; - } - - let sanitizedValue = val; - let value = val; - if (!helpers.isUndefined(value)) { - if (helpers.isObject(value) || helpers.isArray(value)) { - const stringified = antiCircular(value); - value = encode(`JSON.parse(${stringified})`); - sanitizedValue = JSON.parse(stringified); - } else { - value = encode(value); - if (value && !fieldContentRegExp.test(value)) { - value = escape(value); - } - } - } else { - value = ''; - } - - const pairs = [`${name}=${value}`]; - - if (helpers.isNumber(opt.maxAge)) { - pairs.push(`Max-Age=${opt.maxAge}`); - } - - if (opt.domain && typeof opt.domain === 'string') { - if (!fieldContentRegExp.test(opt.domain)) { - throw new Meteor.Error(404, 'option domain is invalid'); - } - pairs.push(`Domain=${opt.domain}`); - } - - if (opt.path && typeof opt.path === 'string') { - if (!fieldContentRegExp.test(opt.path)) { - throw new Meteor.Error(404, 'option path is invalid'); - } - pairs.push(`Path=${opt.path}`); - } else { - pairs.push('Path=/'); - } - - opt.expires = opt.expires || opt.expire || false; - if (opt.expires === Infinity) { - pairs.push('Expires=Fri, 31 Dec 9999 23:59:59 GMT'); - } else if (opt.expires instanceof Date) { - pairs.push(`Expires=${opt.expires.toUTCString()}`); - } else if (opt.expires === 0) { - pairs.push('Expires=0'); - } else if (helpers.isNumber(opt.expires)) { - pairs.push(`Expires=${(new Date(opt.expires)).toUTCString()}`); - } - - if (opt.httpOnly) { - pairs.push('HttpOnly'); - } - - if (opt.secure) { - pairs.push('Secure'); - } - - if (opt.firstPartyOnly) { - pairs.push('First-Party-Only'); - } - - if (opt.sameSite) { - pairs.push(opt.sameSite === true ? 'SameSite' : `SameSite=${opt.sameSite}`); - } - - return { cookieString: pairs.join('; '), sanitizedValue }; + let name; + + if (!fieldContentRegExp.test(key)) { + name = escape(key); + } else { + name = key; + } + + let sanitizedValue = val; + let value = val; + if (!helpers.isUndefined(value)) { + if (helpers.isObject(value) || helpers.isArray(value)) { + const stringified = antiCircular(value); + value = encode(`JSON.parse(${stringified})`); + sanitizedValue = JSON.parse(stringified); + } else { + value = encode(value); + if (value && !fieldContentRegExp.test(value)) { + value = escape(value); + } + } + } else { + value = ''; + } + + const pairs = [`${name}=${value}`]; + + if (helpers.isNumber(opt.maxAge)) { + pairs.push(`Max-Age=${opt.maxAge}`); + } + + if (opt.domain && typeof opt.domain === 'string') { + if (!fieldContentRegExp.test(opt.domain)) { + throw new Meteor.Error(404, 'option domain is invalid'); + } + pairs.push(`Domain=${opt.domain}`); + } + + if (opt.path && typeof opt.path === 'string') { + if (!fieldContentRegExp.test(opt.path)) { + throw new Meteor.Error(404, 'option path is invalid'); + } + pairs.push(`Path=${opt.path}`); + } else { + pairs.push('Path=/'); + } + + opt.expires = opt.expires || opt.expire || false; + if (opt.expires === Infinity) { + pairs.push('Expires=Fri, 31 Dec 9999 23:59:59 GMT'); + } else if (opt.expires instanceof Date) { + pairs.push(`Expires=${opt.expires.toUTCString()}`); + } else if (opt.expires === 0) { + pairs.push('Expires=0'); + } else if (helpers.isNumber(opt.expires)) { + pairs.push(`Expires=${new Date(opt.expires).toUTCString()}`); + } + + if (opt.httpOnly) { + pairs.push('HttpOnly'); + } + + if (opt.secure) { + pairs.push('Secure'); + } + + if (opt.firstPartyOnly) { + pairs.push('First-Party-Only'); + } + + if (opt.sameSite) { + pairs.push(opt.sameSite === true ? 'SameSite' : `SameSite=${opt.sameSite}`); + } + + return { cookieString: pairs.join('; '), sanitizedValue }; }; const isStringifiedRegEx = /JSON\.parse\((.*)\)/; const isTypedRegEx = /false|true|null/; const deserialize = (string) => { - if (typeof string !== 'string') { - return string; - } - - if (isStringifiedRegEx.test(string)) { - let obj = string.match(isStringifiedRegEx)[1]; - if (obj) { - try { - return JSON.parse(decode(obj)); - } catch (e) { - console.error('[ostrio:cookies] [.get()] [deserialize()] Exception:', e, string, obj); - return string; - } - } - return string; - } else if (isTypedRegEx.test(string)) { - try { - return JSON.parse(string); - } catch (e) { - return string; - } - } - return string; + if (typeof string !== 'string') { + return string; + } + + if (isStringifiedRegEx.test(string)) { + let obj = string.match(isStringifiedRegEx)[1]; + if (obj) { + try { + return JSON.parse(decode(obj)); + } catch (e) { + console.error('[ostrio:cookies] [.get()] [deserialize()] Exception:', e, string, obj); + return string; + } + } + return string; + } else if (isTypedRegEx.test(string)) { + try { + return JSON.parse(string); + } catch (e) { + return string; + } + } + return string; }; /** @@ -284,197 +288,201 @@ const deserialize = (string) => { * @summary Internal Class */ class __cookies { - constructor(opts) { - this.__pendingCookies = []; - this.TTL = opts.TTL || false; - this.response = opts.response || false; - this.runOnServer = opts.runOnServer || false; - this.allowQueryStringCookies = opts.allowQueryStringCookies || false; - this.allowedCordovaOrigins = opts.allowedCordovaOrigins || false; - - if (this.allowedCordovaOrigins === true) { - this.allowedCordovaOrigins = /^http:\/\/localhost:12[0-9]{3}$/; - } - - this.originRE = new RegExp(`^https?:\/\/(${rootUrl ? rootUrl : ''}${mobileRootUrl ? ('|' + mobileRootUrl) : ''})$`); - - if (helpers.isObject(opts._cookies)) { - this.cookies = opts._cookies; - } else { - this.cookies = parse(opts._cookies); - } - } - - /** - * @locus Anywhere - * @memberOf __cookies - * @name get - * @param {String} key - The name of the cookie to read - * @param {String} _tmp - Unparsed string instead of user's cookies - * @summary Read a cookie. If the cookie doesn't exist a null value will be returned. - * @returns {String|void} - */ - get(key, _tmp) { - const cookieString = _tmp ? parse(_tmp) : this.cookies; - if (!key || !cookieString) { - return void 0; - } - - if (cookieString.hasOwnProperty(key)) { - return deserialize(cookieString[key]); - } - - return void 0; - } - - /** - * @locus Anywhere - * @memberOf __cookies - * @name set - * @param {String} key - The name of the cookie to create/overwrite - * @param {String} value - The value of the cookie - * @param {Object} opts - [Optional] Cookie options (see readme docs) - * @summary Create/overwrite a cookie. - * @returns {Boolean} - */ - set(key, value, opts = {}) { - if (key && !helpers.isUndefined(value)) { - if (helpers.isNumber(this.TTL) && opts.expires === undefined) { - opts.expires = new Date(+new Date() + this.TTL); - } - const { cookieString, sanitizedValue } = serialize(key, value, opts); - - this.cookies[key] = sanitizedValue; - if (Meteor.isClient) { - document.cookie = cookieString; - } else if (this.response) { - this.__pendingCookies.push(cookieString); - this.response.setHeader('Set-Cookie', this.__pendingCookies); - } - return true; - } - return false; - } - - /** - * @locus Anywhere - * @memberOf __cookies - * @name remove - * @param {String} key - The name of the cookie to create/overwrite - * @param {String} path - [Optional] The path from where the cookie will be - * readable. E.g., "/", "/mydir"; if not specified, defaults to the current - * path of the current document location (string or null). The path must be - * absolute (see RFC 2965). For more information on how to use relative paths - * in this argument, see: https://developer.mozilla.org/en-US/docs/Web/API/document.cookie#Using_relative_URLs_in_the_path_parameter - * @param {String} domain - [Optional] The domain from where the cookie will - * be readable. E.g., "example.com", ".example.com" (includes all subdomains) - * or "subdomain.example.com"; if not specified, defaults to the host portion - * of the current document location (string or null). - * @summary Remove a cookie(s). - * @returns {Boolean} - */ - remove(key, path = '/', domain = '') { - if (key && this.cookies.hasOwnProperty(key)) { - const { cookieString } = serialize(key, '', { - domain, - path, - expires: new Date(0) - }); - - delete this.cookies[key]; - if (Meteor.isClient) { - document.cookie = cookieString; - } else if (this.response) { - this.response.setHeader('Set-Cookie', cookieString); - } - return true; - } else if (!key && this.keys().length > 0 && this.keys()[0] !== '') { - const keys = Object.keys(this.cookies); - for (let i = 0; i < keys.length; i++) { - this.remove(keys[i]); - } - return true; - } - return false; - } - - /** - * @locus Anywhere - * @memberOf __cookies - * @name has - * @param {String} key - The name of the cookie to create/overwrite - * @param {String} _tmp - Unparsed string instead of user's cookies - * @summary Check whether a cookie exists in the current position. - * @returns {Boolean} - */ - has(key, _tmp) { - const cookieString = _tmp ? parse(_tmp) : this.cookies; - if (!key || !cookieString) { - return false; - } - - return cookieString.hasOwnProperty(key); - } - - /** - * @locus Anywhere - * @memberOf __cookies - * @name keys - * @summary Returns an array of all readable cookies from this location. - * @returns {[String]} - */ - keys() { - if (this.cookies) { - return Object.keys(this.cookies); - } - return []; - } - - /** - * @locus Client - * @memberOf __cookies - * @name send - * @param cb {Function} - Callback - * @summary Send all cookies over XHR to server. - * @returns {void} - */ - send(cb = NoOp) { - if (Meteor.isServer) { - cb(new Meteor.Error(400, 'Can\'t run `.send()` on server, it\'s Client only method!')); - } - - if (this.runOnServer) { - let path = `${window.__meteor_runtime_config__.ROOT_URL_PATH_PREFIX || window.__meteor_runtime_config__.meteorEnv.ROOT_URL_PATH_PREFIX || ''}/___cookie___/set`; - let query = ''; - - if (Meteor.isCordova && this.allowQueryStringCookies) { - const cookiesKeys = this.keys(); - const cookiesArray = []; - for (let i = 0; i < cookiesKeys.length; i++) { - const { sanitizedValue } = serialize(cookiesKeys[i], this.get(cookiesKeys[i])); - const pair = `${cookiesKeys[i]}=${sanitizedValue}`; - if (!cookiesArray.includes(pair)) { - cookiesArray.push(pair); - } - } - - if (cookiesArray.length) { - path = Meteor.absoluteUrl('___cookie___/set'); - query = `?___cookies___=${encodeURIComponent(cookiesArray.join('; '))}`; - } - } - - fetch(`${path}${query}`, { - credentials: 'include', - type: 'cors' - }).then((response) => { - cb(void 0, response); - }).catch(cb); - } else { - cb(new Meteor.Error(400, 'Can\'t send cookies on server when `runOnServer` is false.')); - } - return void 0; - } + constructor(opts) { + this.__pendingCookies = []; + this.TTL = opts.TTL || false; + this.response = opts.response || false; + this.runOnServer = opts.runOnServer || false; + this.allowQueryStringCookies = opts.allowQueryStringCookies || false; + this.allowedCordovaOrigins = opts.allowedCordovaOrigins || false; + + if (this.allowedCordovaOrigins === true) { + this.allowedCordovaOrigins = /^http:\/\/localhost:12[0-9]{3}$/; + } + + this.originRE = new RegExp(`^https?:\/\/(${rootUrl ? rootUrl : ''}${mobileRootUrl ? '|' + mobileRootUrl : ''})$`); + + if (helpers.isObject(opts._cookies)) { + this.cookies = opts._cookies; + } else { + this.cookies = parse(opts._cookies); + } + } + + /** + * @locus Anywhere + * @memberOf __cookies + * @name get + * @param {String} key - The name of the cookie to read + * @param {String} _tmp - Unparsed string instead of user's cookies + * @summary Read a cookie. If the cookie doesn't exist a null value will be returned. + * @returns {String|void} + */ + get(key, _tmp) { + const cookieString = _tmp ? parse(_tmp) : this.cookies; + if (!key || !cookieString) { + return void 0; + } + + if (cookieString.hasOwnProperty(key)) { + return deserialize(cookieString[key]); + } + + return void 0; + } + + /** + * @locus Anywhere + * @memberOf __cookies + * @name set + * @param {String} key - The name of the cookie to create/overwrite + * @param {String} value - The value of the cookie + * @param {Object} opts - [Optional] Cookie options (see readme docs) + * @summary Create/overwrite a cookie. + * @returns {Boolean} + */ + set(key, value, opts = {}) { + if (key && !helpers.isUndefined(value)) { + if (helpers.isNumber(this.TTL) && opts.expires === undefined) { + opts.expires = new Date(+new Date() + this.TTL); + } + const { cookieString, sanitizedValue } = serialize(key, value, opts); + + this.cookies[key] = sanitizedValue; + if (Meteor.isClient) { + document.cookie = cookieString; + } else if (this.response) { + this.__pendingCookies.push(cookieString); + this.response.setHeader('Set-Cookie', this.__pendingCookies); + } + return true; + } + return false; + } + + /** + * @locus Anywhere + * @memberOf __cookies + * @name remove + * @param {String} key - The name of the cookie to create/overwrite + * @param {String} path - [Optional] The path from where the cookie will be + * readable. E.g., "/", "/mydir"; if not specified, defaults to the current + * path of the current document location (string or null). The path must be + * absolute (see RFC 2965). For more information on how to use relative paths + * in this argument, see: https://developer.mozilla.org/en-US/docs/Web/API/document.cookie#Using_relative_URLs_in_the_path_parameter + * @param {String} domain - [Optional] The domain from where the cookie will + * be readable. E.g., "example.com", ".example.com" (includes all subdomains) + * or "subdomain.example.com"; if not specified, defaults to the host portion + * of the current document location (string or null). + * @summary Remove a cookie(s). + * @returns {Boolean} + */ + remove(key, path = '/', domain = '') { + if (key && this.cookies.hasOwnProperty(key)) { + const { cookieString } = serialize(key, '', { + domain, + path, + expires: new Date(0), + }); + + delete this.cookies[key]; + if (Meteor.isClient) { + document.cookie = cookieString; + } else if (this.response) { + this.response.setHeader('Set-Cookie', cookieString); + } + return true; + } else if (!key && this.keys().length > 0 && this.keys()[0] !== '') { + const keys = Object.keys(this.cookies); + for (let i = 0; i < keys.length; i++) { + this.remove(keys[i]); + } + return true; + } + return false; + } + + /** + * @locus Anywhere + * @memberOf __cookies + * @name has + * @param {String} key - The name of the cookie to create/overwrite + * @param {String} _tmp - Unparsed string instead of user's cookies + * @summary Check whether a cookie exists in the current position. + * @returns {Boolean} + */ + has(key, _tmp) { + const cookieString = _tmp ? parse(_tmp) : this.cookies; + if (!key || !cookieString) { + return false; + } + + return cookieString.hasOwnProperty(key); + } + + /** + * @locus Anywhere + * @memberOf __cookies + * @name keys + * @summary Returns an array of all readable cookies from this location. + * @returns {[String]} + */ + keys() { + if (this.cookies) { + return Object.keys(this.cookies); + } + return []; + } + + /** + * @locus Client + * @memberOf __cookies + * @name send + * @param cb {Function} - Callback + * @summary Send all cookies over XHR to server. + * @returns {void} + */ + send(cb = NoOp) { + if (Meteor.isServer) { + cb(new Meteor.Error(400, "Can't run `.send()` on server, it's Client only method!")); + } + + if (this.runOnServer) { + let path = `${ + window.__meteor_runtime_config__.ROOT_URL_PATH_PREFIX || window.__meteor_runtime_config__.meteorEnv.ROOT_URL_PATH_PREFIX || '' + }/___cookie___/set`; + let query = ''; + + if (Meteor.isCordova && this.allowQueryStringCookies) { + const cookiesKeys = this.keys(); + const cookiesArray = []; + for (let i = 0; i < cookiesKeys.length; i++) { + const { sanitizedValue } = serialize(cookiesKeys[i], this.get(cookiesKeys[i])); + const pair = `${cookiesKeys[i]}=${sanitizedValue}`; + if (!cookiesArray.includes(pair)) { + cookiesArray.push(pair); + } + } + + if (cookiesArray.length) { + path = Meteor.absoluteUrl('___cookie___/set'); + query = `?___cookies___=${encodeURIComponent(cookiesArray.join('; '))}`; + } + } + + fetch(`${path}${query}`, { + credentials: 'include', + type: 'cors', + }) + .then((response) => { + cb(void 0, response); + }) + .catch(cb); + } else { + cb(new Meteor.Error(400, "Can't send cookies on server when `runOnServer` is false.")); + } + return void 0; + } } /** @@ -484,22 +492,22 @@ class __cookies { * @private */ const __middlewareHandler = (request, response, opts) => { - let _cookies = {}; - if (opts.runOnServer) { - if (request.headers && request.headers.cookie) { - _cookies = parse(request.headers.cookie); - } - - return new __cookies({ - _cookies, - TTL: opts.TTL, - runOnServer: opts.runOnServer, - response, - allowQueryStringCookies: opts.allowQueryStringCookies - }); - } - - throw new Meteor.Error(400, 'Can\'t use middleware when `runOnServer` is false.'); + let _cookies = {}; + if (opts.runOnServer) { + if (request.headers && request.headers.cookie) { + _cookies = parse(request.headers.cookie); + } + + return new __cookies({ + _cookies, + TTL: opts.TTL, + runOnServer: opts.runOnServer, + response, + allowQueryStringCookies: opts.allowQueryStringCookies, + }); + } + + throw new Meteor.Error(400, "Can't use middleware when `runOnServer` is false."); }; /** @@ -515,96 +523,94 @@ const __middlewareHandler = (request, response, opts) => { * @summary Main Cookie class */ class Cookies extends __cookies { - constructor(opts = {}) { - opts.TTL = helpers.isNumber(opts.TTL) ? opts.TTL : false; - opts.runOnServer = (opts.runOnServer !== false) ? true : false; - opts.allowQueryStringCookies = (opts.allowQueryStringCookies !== true) ? false : true; - - if (Meteor.isClient) { - opts._cookies = document.cookie; - super(opts); - } else { - opts._cookies = {}; - super(opts); - opts.auto = (opts.auto !== false) ? true : false; - this.opts = opts; - this.handler = helpers.isFunction(opts.handler) ? opts.handler : false; - this.onCookies = helpers.isFunction(opts.onCookies) ? opts.onCookies : false; - - if (opts.runOnServer && !Cookies.isLoadedOnServer) { - Cookies.isLoadedOnServer = true; - if (opts.auto) { - WebApp.connectHandlers.use((req, res, next) => { - if (urlRE.test(req._parsedUrl.path)) { - const matchedCordovaOrigin = !!req.headers.origin - && this.allowedCordovaOrigins - && this.allowedCordovaOrigins.test(req.headers.origin); - const matchedOrigin = matchedCordovaOrigin - || (!!req.headers.origin && this.originRE.test(req.headers.origin)); - - if (matchedOrigin) { - res.setHeader('Access-Control-Allow-Credentials', 'true'); - res.setHeader('Access-Control-Allow-Origin', req.headers.origin); - } - - const cookiesArray = []; - let cookiesObject = {}; - if (matchedCordovaOrigin && opts.allowQueryStringCookies && req.query.___cookies___) { - cookiesObject = parse(decodeURIComponent(req.query.___cookies___)); - } else if (req.headers.cookie) { - cookiesObject = parse(req.headers.cookie); - } - - const cookiesKeys = Object.keys(cookiesObject); - if (cookiesKeys.length) { - for (let i = 0; i < cookiesKeys.length; i++) { - const { cookieString } = serialize(cookiesKeys[i], cookiesObject[cookiesKeys[i]]); - if (!cookiesArray.includes(cookieString)) { - cookiesArray.push(cookieString); - } - } - - if (cookiesArray.length) { - res.setHeader('Set-Cookie', cookiesArray); - } - } - - helpers.isFunction(this.onCookies) && this.onCookies(__middlewareHandler(req, res, opts)); - - res.writeHead(200); - res.end(''); - } else { - req.Cookies = __middlewareHandler(req, res, opts); - helpers.isFunction(this.handler) && this.handler(req.Cookies); - next(); - } - }); - } - } - } - } - - /** - * @locus Server - * @memberOf Cookies - * @name middleware - * @summary Get Cookies instance into callback - * @returns {void} - */ - middleware() { - if (!Meteor.isServer) { - throw new Meteor.Error(500, '[ostrio:cookies] Can\'t use `.middleware()` on Client, it\'s Server only!'); - } - - return (req, res, next) => { - helpers.isFunction(this.handler) && this.handler(__middlewareHandler(req, res, this.opts)); - next(); - }; - } + constructor(opts = {}) { + opts.TTL = helpers.isNumber(opts.TTL) ? opts.TTL : false; + opts.runOnServer = opts.runOnServer !== false ? true : false; + opts.allowQueryStringCookies = opts.allowQueryStringCookies !== true ? false : true; + + if (Meteor.isClient) { + opts._cookies = document.cookie; + super(opts); + } else { + opts._cookies = {}; + super(opts); + opts.auto = opts.auto !== false ? true : false; + this.opts = opts; + this.handler = helpers.isFunction(opts.handler) ? opts.handler : false; + this.onCookies = helpers.isFunction(opts.onCookies) ? opts.onCookies : false; + + if (opts.runOnServer && !Cookies.isLoadedOnServer) { + Cookies.isLoadedOnServer = true; + if (opts.auto) { + WebApp.connectHandlers.use((req, res, next) => { + if (urlRE.test(req._parsedUrl.path)) { + const matchedCordovaOrigin = + !!req.headers.origin && this.allowedCordovaOrigins && this.allowedCordovaOrigins.test(req.headers.origin); + const matchedOrigin = matchedCordovaOrigin || (!!req.headers.origin && this.originRE.test(req.headers.origin)); + + if (matchedOrigin) { + res.setHeader('Access-Control-Allow-Credentials', 'true'); + res.setHeader('Access-Control-Allow-Origin', req.headers.origin); + } + + const cookiesArray = []; + let cookiesObject = {}; + if (matchedCordovaOrigin && opts.allowQueryStringCookies && req.query.___cookies___) { + cookiesObject = parse(decodeURIComponent(req.query.___cookies___)); + } else if (req.headers.cookie) { + cookiesObject = parse(req.headers.cookie); + } + + const cookiesKeys = Object.keys(cookiesObject); + if (cookiesKeys.length) { + for (let i = 0; i < cookiesKeys.length; i++) { + const { cookieString } = serialize(cookiesKeys[i], cookiesObject[cookiesKeys[i]]); + if (!cookiesArray.includes(cookieString)) { + cookiesArray.push(cookieString); + } + } + + if (cookiesArray.length) { + res.setHeader('Set-Cookie', cookiesArray); + } + } + + helpers.isFunction(this.onCookies) && this.onCookies(__middlewareHandler(req, res, opts)); + + res.writeHead(200); + res.end(''); + } else { + req.Cookies = __middlewareHandler(req, res, opts); + helpers.isFunction(this.handler) && this.handler(req.Cookies); + next(); + } + }); + } + } + } + } + + /** + * @locus Server + * @memberOf Cookies + * @name middleware + * @summary Get Cookies instance into callback + * @returns {void} + */ + middleware() { + if (!Meteor.isServer) { + throw new Meteor.Error(500, "[ostrio:cookies] Can't use `.middleware()` on Client, it's Server only!"); + } + + return (req, res, next) => { + helpers.isFunction(this.handler) && this.handler(__middlewareHandler(req, res, this.opts)); + next(); + }; + } } if (Meteor.isServer) { - Cookies.isLoadedOnServer = false; + Cookies.isLoadedOnServer = false; } /* Export the Cookies class */ diff --git a/apps/meteor/packages/meteor-cookies/package.js b/apps/meteor/packages/meteor-cookies/package.js index c7f19499f838..9ffcdb0d94af 100644 --- a/apps/meteor/packages/meteor-cookies/package.js +++ b/apps/meteor/packages/meteor-cookies/package.js @@ -12,4 +12,3 @@ Package.onUse((api) => { api.use('fetch', 'client'); api.mainModule('cookies.js', ['client', 'server']); }); - diff --git a/apps/meteor/packages/meteor-run-as-user/lib/collection.overwrites.js b/apps/meteor/packages/meteor-run-as-user/lib/collection.overwrites.js index 01603673d4c7..9ea5b18a2d08 100644 --- a/apps/meteor/packages/meteor-run-as-user/lib/collection.overwrites.js +++ b/apps/meteor/packages/meteor-run-as-user/lib/collection.overwrites.js @@ -7,48 +7,40 @@ // This will allow us to run the modifiers inside of a "Meteor.runAsUser" with // security checks. _.each(['insert', 'update', 'remove'], function (method) { - - var _super = Mongo.Collection.prototype[method]; - - Mongo.Collection.prototype[method] = function ( /* arguments */ ) { - var self = this; - var args = _.toArray(arguments); - - // Check if this method is run in restricted mode and collection is - // restricted. - if (Meteor.isRestricted() && self._restricted) { - - var generatedId = null; - if (method === 'insert' && !_.has(args[0], '_id')) { - generatedId = self._makeNewID(); - } - - // short circuit if there is no way it will pass. - if (self._validators[method].allow.length === 0) { - throw new Meteor.Error( - 403, 'Access denied. No allow validators set on restricted ' + - 'collection for method \'' + method + '\'.'); - } - - var validatedMethodName = - '_validated' + method.charAt(0).toUpperCase() + method.slice(1); - args.unshift(Meteor.userId()); - - if (method === 'insert') { - args.push(generatedId); - - self[validatedMethodName].apply(self, args); - // xxx: for now we return the id since self._validatedInsert doesn't - // yet return the new id - return generatedId || args[0]._id; - - } - - return self[validatedMethodName].apply(self, args); - - } - - return _super.apply(self, args); - }; - + var _super = Mongo.Collection.prototype[method]; + + Mongo.Collection.prototype[method] = function (/* arguments */) { + var self = this; + var args = _.toArray(arguments); + + // Check if this method is run in restricted mode and collection is + // restricted. + if (Meteor.isRestricted() && self._restricted) { + var generatedId = null; + if (method === 'insert' && !_.has(args[0], '_id')) { + generatedId = self._makeNewID(); + } + + // short circuit if there is no way it will pass. + if (self._validators[method].allow.length === 0) { + throw new Meteor.Error(403, 'Access denied. No allow validators set on restricted ' + "collection for method '" + method + "'."); + } + + var validatedMethodName = '_validated' + method.charAt(0).toUpperCase() + method.slice(1); + args.unshift(Meteor.userId()); + + if (method === 'insert') { + args.push(generatedId); + + self[validatedMethodName].apply(self, args); + // xxx: for now we return the id since self._validatedInsert doesn't + // yet return the new id + return generatedId || args[0]._id; + } + + return self[validatedMethodName].apply(self, args); + } + + return _super.apply(self, args); + }; }); diff --git a/apps/meteor/packages/meteor-run-as-user/lib/common.js b/apps/meteor/packages/meteor-run-as-user/lib/common.js index e12269db2630..af64b619375f 100644 --- a/apps/meteor/packages/meteor-run-as-user/lib/common.js +++ b/apps/meteor/packages/meteor-run-as-user/lib/common.js @@ -12,7 +12,7 @@ var restrictedMode = new Meteor.EnvironmentVariable(); * @return {Boolean} True if in a runAsUser user scope */ Meteor.isRestricted = function () { - return !!restrictedMode.get(); + return !!restrictedMode.get(); }; /** @@ -20,12 +20,12 @@ Meteor.isRestricted = function () { * @param {Function} f Code to run in restricted mode * @return {Any} Result of code running */ -Meteor.runRestricted = function(f) { - if (Meteor.isRestricted()) { - return f(); - } else { - return restrictedMode.withValue(true, f); - } +Meteor.runRestricted = function (f) { + if (Meteor.isRestricted()) { + return f(); + } else { + return restrictedMode.withValue(true, f); + } }; /** @@ -33,12 +33,12 @@ Meteor.runRestricted = function(f) { * @param {Function} f Code to run in restricted mode * @return {Any} Result of code running */ -Meteor.runUnrestricted = function(f) { - if (Meteor.isRestricted()) { - return restrictedMode.withValue(false, f); - } else { - f(); - } +Meteor.runUnrestricted = function (f) { + if (Meteor.isRestricted()) { + return restrictedMode.withValue(false, f); + } else { + f(); + } }; /** @@ -48,21 +48,23 @@ Meteor.runUnrestricted = function(f) { * @return {Any} Returns function result */ Meteor.runAsUser = function (userId, f) { - var currentInvocation = DDP._CurrentInvocation.get(); + var currentInvocation = DDP._CurrentInvocation.get(); - // Create a new method invocation - var invocation = new DDPCommon.MethodInvocation( - (currentInvocation) ? currentInvocation : { - connection: null - } - ); + // Create a new method invocation + var invocation = new DDPCommon.MethodInvocation( + currentInvocation + ? currentInvocation + : { + connection: null, + }, + ); - // Now run as user on this invocation - invocation.setUserId(userId); + // Now run as user on this invocation + invocation.setUserId(userId); - return DDP._CurrentInvocation.withValue(invocation, function () { - return f.apply(invocation, [userId]); - }); + return DDP._CurrentInvocation.withValue(invocation, function () { + return f.apply(invocation, [userId]); + }); }; /** @@ -70,10 +72,10 @@ Meteor.runAsUser = function (userId, f) { * @param {Function} f Function to run unrestricted * @return {Any} Returns function result */ -Meteor.runAsRestrictedUser = function(userId, f) { - return Meteor.runRestricted(function() { - return Meteor.runAsUser(userId, f); - }); +Meteor.runAsRestrictedUser = function (userId, f) { + return Meteor.runRestricted(function () { + return Meteor.runAsUser(userId, f); + }); }; var adminMode = new Meteor.EnvironmentVariable(); @@ -81,29 +83,29 @@ var adminMode = new Meteor.EnvironmentVariable(); /** * Check if code is running isside an invocation / method */ -Meteor.isAdmin = function() { - return !!adminMode.get(); +Meteor.isAdmin = function () { + return !!adminMode.get(); }; /** * Make the function run outside invocation */ -Meteor.runAsAdmin = function(f) { - if (Meteor.isAdmin()) { - return f(); - } else { - return adminMode.withValue(false, f); - } +Meteor.runAsAdmin = function (f) { + if (Meteor.isAdmin()) { + return f(); + } else { + return adminMode.withValue(false, f); + } }; /** * Make sure code runs outside an invocation on the * server */ -Meteor.runOutsideInvocation = function(f) { - if (Meteor.isServer && DDP._CurrentInvocation.get()) { - DDP._CurrentInvocation.withValue(null, f); - } else { - f(); - } +Meteor.runOutsideInvocation = function (f) { + if (Meteor.isServer && DDP._CurrentInvocation.get()) { + DDP._CurrentInvocation.withValue(null, f); + } else { + f(); + } }; diff --git a/apps/meteor/packages/meteor-run-as-user/lib/pre.1.0.3.js b/apps/meteor/packages/meteor-run-as-user/lib/pre.1.0.3.js index f707a1128b62..2562011c00da 100644 --- a/apps/meteor/packages/meteor-run-as-user/lib/pre.1.0.3.js +++ b/apps/meteor/packages/meteor-run-as-user/lib/pre.1.0.3.js @@ -2,95 +2,93 @@ // until the next release of Meteor maybe 1.0.3? // if (typeof DDPCommon === 'undefined') { - DDPCommon = {}; + DDPCommon = {}; - DDPCommon.MethodInvocation = function (options) { - var self = this; + DDPCommon.MethodInvocation = function (options) { + var self = this; - // true if we're running not the actual method, but a stub (that is, - // if we're on a client (which may be a browser, or in the future a - // server connecting to another server) and presently running a - // simulation of a server-side method for latency compensation - // purposes). not currently true except in a client such as a browser, - // since there's usually no point in running stubs unless you have a - // zero-latency connection to the user. + // true if we're running not the actual method, but a stub (that is, + // if we're on a client (which may be a browser, or in the future a + // server connecting to another server) and presently running a + // simulation of a server-side method for latency compensation + // purposes). not currently true except in a client such as a browser, + // since there's usually no point in running stubs unless you have a + // zero-latency connection to the user. - /** - * @summary Access inside a method invocation. Boolean value, true if this invocation is a stub. - * @locus Anywhere - * @name isSimulation - * @memberOf MethodInvocation - * @instance - * @type {Boolean} - */ - this.isSimulation = options.isSimulation; + /** + * @summary Access inside a method invocation. Boolean value, true if this invocation is a stub. + * @locus Anywhere + * @name isSimulation + * @memberOf MethodInvocation + * @instance + * @type {Boolean} + */ + this.isSimulation = options.isSimulation; - // call this function to allow other method invocations (from the - // same client) to continue running without waiting for this one to - // complete. - this._unblock = options.unblock || function () {}; - this._calledUnblock = false; + // call this function to allow other method invocations (from the + // same client) to continue running without waiting for this one to + // complete. + this._unblock = options.unblock || function () {}; + this._calledUnblock = false; - // current user id + // current user id - /** - * @summary The id of the user that made this method call, or `null` if no user was logged in. - * @locus Anywhere - * @name userId - * @memberOf MethodInvocation - * @instance - */ - this.userId = options.userId; + /** + * @summary The id of the user that made this method call, or `null` if no user was logged in. + * @locus Anywhere + * @name userId + * @memberOf MethodInvocation + * @instance + */ + this.userId = options.userId; - // sets current user id in all appropriate server contexts and - // reruns subscriptions - this._setUserId = options.setUserId || function () {}; + // sets current user id in all appropriate server contexts and + // reruns subscriptions + this._setUserId = options.setUserId || function () {}; - // On the server, the connection this method call came in on. + // On the server, the connection this method call came in on. - /** - * @summary Access inside a method invocation. The [connection](#meteor_onconnection) that this method was received on. `null` if the method is not associated with a connection, eg. a server initiated method call. - * @locus Server - * @name connection - * @memberOf MethodInvocation - * @instance - */ - this.connection = options.connection; + /** + * @summary Access inside a method invocation. The [connection](#meteor_onconnection) that this method was received on. `null` if the method is not associated with a connection, eg. a server initiated method call. + * @locus Server + * @name connection + * @memberOf MethodInvocation + * @instance + */ + this.connection = options.connection; - // The seed for randomStream value generation - this.randomSeed = options.randomSeed; + // The seed for randomStream value generation + this.randomSeed = options.randomSeed; - // This is set by RandomStream.get; and holds the random stream state - this.randomStream = null; - }; + // This is set by RandomStream.get; and holds the random stream state + this.randomStream = null; + }; - _.extend(DDPCommon.MethodInvocation.prototype, { - /** - * @summary Call inside a method invocation. Allow subsequent method from this client to begin running in a new fiber. - * @locus Server - * @memberOf MethodInvocation - * @instance - */ - unblock: function () { - var self = this; - self._calledUnblock = true; - self._unblock(); - }, + _.extend(DDPCommon.MethodInvocation.prototype, { + /** + * @summary Call inside a method invocation. Allow subsequent method from this client to begin running in a new fiber. + * @locus Server + * @memberOf MethodInvocation + * @instance + */ + unblock: function () { + var self = this; + self._calledUnblock = true; + self._unblock(); + }, - /** - * @summary Set the logged in user. - * @locus Server - * @memberOf MethodInvocation - * @instance - * @param {String | null} userId The value that should be returned by `userId` on this connection. - */ - setUserId: function (userId) { - var self = this; - if (self._calledUnblock) - throw new Error("Can't call setUserId in a method after calling unblock"); - self.userId = userId; - // self._setUserId(userId); - } - - }); + /** + * @summary Set the logged in user. + * @locus Server + * @memberOf MethodInvocation + * @instance + * @param {String | null} userId The value that should be returned by `userId` on this connection. + */ + setUserId: function (userId) { + var self = this; + if (self._calledUnblock) throw new Error("Can't call setUserId in a method after calling unblock"); + self.userId = userId; + // self._setUserId(userId); + }, + }); }