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);
+ },
+ });
}