mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'devel' into release-1.4.3.x
This commit is contained in:
@@ -209,6 +209,10 @@ We have found that writing software to meet both these standards at the
|
||||
same time is hard but
|
||||
incredibly rewarding. We hope you come to feel the same way.
|
||||
|
||||
### Understanding the core
|
||||
|
||||
For more information about how to work with Meteor core, take a look at the [Development](Development.md) document which explains many important details, including how to [run from a checkout](Development.md#running-from-a-git-checkout), [run tests](Development.md#tests), and more.
|
||||
|
||||
### Proposing your change
|
||||
|
||||
You'll have the best chance of getting a change into core if you can build consensus in the community for it. Start by creating a well specified feature request as a Github issue.
|
||||
@@ -237,9 +241,10 @@ these guidelines:
|
||||
|
||||
* Include tests that prove your code works.
|
||||
|
||||
* Follow the
|
||||
[MDG style guide](https://guide.meteor.com/code-style.html#javascript)
|
||||
for code and commit messages.
|
||||
* Follow appropriate style for
|
||||
[code contributions](Development.md#code-style)
|
||||
and
|
||||
[commit messages](Development.md#commit-messages)
|
||||
|
||||
* Be sure your author field in git is properly filled out with your full name
|
||||
and email address so we can credit you.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## v.NEXT
|
||||
|
||||
## v1.4.3.2, TBD
|
||||
## v1.4.3.2, 2017-03-14
|
||||
|
||||
* Node has been upgraded to version 4.8.0.
|
||||
|
||||
|
||||
@@ -707,8 +707,8 @@
|
||||
"from": "source-map@>=0.5.0 <0.6.0"
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.4.12",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.12.tgz",
|
||||
"version": "0.4.13",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.13.tgz",
|
||||
"from": "source-map-support@>=0.4.2 <0.5.0"
|
||||
},
|
||||
"strip-ansi": {
|
||||
|
||||
@@ -29,5 +29,11 @@ Babel = {
|
||||
|
||||
setCacheDir: function (cacheDir) {
|
||||
Npm.require('meteor-babel').setCacheDir(cacheDir);
|
||||
},
|
||||
|
||||
minify: function(source, options) {
|
||||
var meteorBabel = Npm.require('meteor-babel');
|
||||
var options = options || meteorBabel.getMinifierOptions();
|
||||
return meteorBabel.minify(source, options);
|
||||
}
|
||||
};
|
||||
|
||||
254
packages/force-ssl-common/.npm/package/npm-shrinkwrap.json
generated
Normal file
254
packages/force-ssl-common/.npm/package/npm-shrinkwrap.json
generated
Normal file
@@ -0,0 +1,254 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"arr-diff": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
|
||||
"from": "arr-diff@>=2.0.0 <3.0.0"
|
||||
},
|
||||
"arr-flatten": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.1.tgz",
|
||||
"from": "arr-flatten@>=1.0.1 <2.0.0"
|
||||
},
|
||||
"arr-map": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz",
|
||||
"from": "arr-map@>=2.0.0 <3.0.0"
|
||||
},
|
||||
"array-unique": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
|
||||
"from": "array-unique@>=0.2.1 <0.3.0"
|
||||
},
|
||||
"braces": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
|
||||
"from": "braces@>=1.8.2 <2.0.0"
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
|
||||
"from": "commander@>=2.8.1 <3.0.0"
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.1.tgz",
|
||||
"from": "debug@>=2.2.0 <3.0.0"
|
||||
},
|
||||
"deep-equal": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
|
||||
"from": "deep-equal@>=1.0.1 <2.0.0"
|
||||
},
|
||||
"expand-brackets": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
|
||||
"from": "expand-brackets@>=0.1.4 <0.2.0"
|
||||
},
|
||||
"expand-range": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
|
||||
"from": "expand-range@>=1.8.1 <2.0.0"
|
||||
},
|
||||
"extglob": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
|
||||
"from": "extglob@>=0.3.1 <0.4.0"
|
||||
},
|
||||
"filename-regex": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.0.tgz",
|
||||
"from": "filename-regex@>=2.0.0 <3.0.0"
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz",
|
||||
"from": "fill-range@>=2.1.0 <3.0.0"
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
||||
"from": "for-in@>=1.0.1 <2.0.0"
|
||||
},
|
||||
"for-own": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
|
||||
"from": "for-own@>=0.1.4 <0.2.0"
|
||||
},
|
||||
"forwarded-http": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded-http/-/forwarded-http-0.3.0.tgz",
|
||||
"from": "forwarded-http@0.3.0"
|
||||
},
|
||||
"glob-base": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
|
||||
"from": "glob-base@>=0.3.0 <0.4.0"
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
|
||||
"from": "glob-parent@>=2.0.0 <3.0.0"
|
||||
},
|
||||
"graceful-readlink": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
|
||||
"from": "graceful-readlink@>=1.0.0"
|
||||
},
|
||||
"ip": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-0.3.3.tgz",
|
||||
"from": "ip@>=0.3.2 <0.4.0"
|
||||
},
|
||||
"ip-filter": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ip-filter/-/ip-filter-1.0.2.tgz",
|
||||
"from": "ip-filter@>=1.0.0 <2.0.0"
|
||||
},
|
||||
"ip-port-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ip-port-regex/-/ip-port-regex-1.0.0.tgz",
|
||||
"from": "ip-port-regex@>=1.0.0 <2.0.0"
|
||||
},
|
||||
"ip-regex": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz",
|
||||
"from": "ip-regex@>=1.0.3 <2.0.0"
|
||||
},
|
||||
"is-arguments": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.2.tgz",
|
||||
"from": "is-arguments@>=1.0.2 <2.0.0"
|
||||
},
|
||||
"is-buffer": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz",
|
||||
"from": "is-buffer@>=1.0.2 <2.0.0"
|
||||
},
|
||||
"is-dotfile": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.2.tgz",
|
||||
"from": "is-dotfile@>=1.0.0 <2.0.0"
|
||||
},
|
||||
"is-equal-shallow": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
|
||||
"from": "is-equal-shallow@>=0.1.3 <0.2.0"
|
||||
},
|
||||
"is-extendable": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
||||
"from": "is-extendable@>=0.1.1 <0.2.0"
|
||||
},
|
||||
"is-extglob": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
|
||||
"from": "is-extglob@>=1.0.0 <2.0.0"
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
|
||||
"from": "is-glob@>=2.0.1 <3.0.0"
|
||||
},
|
||||
"is-match": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/is-match/-/is-match-0.4.1.tgz",
|
||||
"from": "is-match@>=0.4.0 <0.5.0"
|
||||
},
|
||||
"is-number": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
|
||||
"from": "is-number@>=2.1.0 <3.0.0"
|
||||
},
|
||||
"is-posix-bracket": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
|
||||
"from": "is-posix-bracket@>=0.1.0 <0.2.0"
|
||||
},
|
||||
"is-primitive": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
|
||||
"from": "is-primitive@>=2.0.0 <3.0.0"
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"from": "isarray@1.0.0"
|
||||
},
|
||||
"isobject": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
|
||||
"from": "isobject@>=2.0.0 <3.0.0"
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.1.0.tgz",
|
||||
"from": "kind-of@>=3.0.2 <4.0.0"
|
||||
},
|
||||
"lazy-cache": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
|
||||
"from": "lazy-cache@>=1.0.3 <2.0.0"
|
||||
},
|
||||
"make-iterator": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.0.tgz",
|
||||
"from": "make-iterator@>=1.0.0 <2.0.0"
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "2.3.11",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
|
||||
"from": "micromatch@>=2.3.7 <3.0.0"
|
||||
},
|
||||
"ms": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
|
||||
"from": "ms@0.7.2"
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.0.1.tgz",
|
||||
"from": "normalize-path@>=2.0.1 <3.0.0"
|
||||
},
|
||||
"object.omit": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
|
||||
"from": "object.omit@>=2.0.0 <3.0.0"
|
||||
},
|
||||
"parse-glob": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
|
||||
"from": "parse-glob@>=3.0.4 <4.0.0"
|
||||
},
|
||||
"preserve": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
|
||||
"from": "preserve@>=0.2.0 <0.3.0"
|
||||
},
|
||||
"randomatic": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz",
|
||||
"from": "randomatic@>=1.1.3 <2.0.0"
|
||||
},
|
||||
"regex-cache": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz",
|
||||
"from": "regex-cache@>=0.4.2 <0.5.0"
|
||||
},
|
||||
"repeat-element": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
|
||||
"from": "repeat-element@>=1.1.2 <2.0.0"
|
||||
},
|
||||
"repeat-string": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
||||
"from": "repeat-string@>=1.5.2 <2.0.0"
|
||||
},
|
||||
"to-file-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-file-path/-/to-file-path-1.0.0.tgz",
|
||||
"from": "to-file-path@>=1.0.0 <2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
5
packages/force-ssl-common/README.md
Normal file
5
packages/force-ssl-common/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# force-ssl-common
|
||||
[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/force-ssl-common) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/force-ssl-common)
|
||||
***
|
||||
|
||||
This is an internal Meteor package.
|
||||
32
packages/force-ssl-common/force_ssl_common.js
Normal file
32
packages/force-ssl-common/force_ssl_common.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import forwarded from 'forwarded-http';
|
||||
|
||||
// Determine if the connection is only over localhost. Both we
|
||||
// received it on localhost, and all proxies involved received on
|
||||
// localhost (supports "forwarded" and "x-forwarded-for").
|
||||
const isLocalConnection = (req) => {
|
||||
const localhostRegexp = /^\s*(127\.0\.0\.1|\[?::1\]?)\s*$/;
|
||||
const request = Object.create(req);
|
||||
request.connection = Object.assign(
|
||||
{},
|
||||
req.connection,
|
||||
{ remoteAddress: req.connection.remoteAddress || req.socket.remoteAddress }
|
||||
);
|
||||
const forwardedParams = forwarded(request);
|
||||
let isLocal = true;
|
||||
Object.keys(forwardedParams.for).forEach((forKey) => {
|
||||
if (!localhostRegexp.test(forKey)) {
|
||||
isLocal = false;
|
||||
}
|
||||
});
|
||||
return isLocal;
|
||||
};
|
||||
|
||||
// Determine if the connection was over SSL at any point. Either we
|
||||
// received it as SSL, or a proxy did and translated it for us.
|
||||
const isSslConnection = (req) => {
|
||||
const forwardedParams = forwarded(req);
|
||||
return req.connection.pair
|
||||
|| forwardedParams.proto && forwardedParams.proto.indexOf('https') !== -1;
|
||||
};
|
||||
|
||||
export { isLocalConnection, isSslConnection };
|
||||
112
packages/force-ssl-common/force_ssl_tests.js
Normal file
112
packages/force-ssl-common/force_ssl_tests.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { isLocalConnection, isSslConnection } from './force_ssl_common';
|
||||
import http from 'http';
|
||||
|
||||
Tinytest.add('force-ssl - check for a local connection', function (test) {
|
||||
const req = new http.IncomingMessage();
|
||||
req.connection = { remoteAddress: null };
|
||||
req.socket = { remoteAddress: null };
|
||||
|
||||
// Remote address check (connection)
|
||||
|
||||
['127.0.0.1', '::1'].forEach((ip) => {
|
||||
req.connection.remoteAddress = ip;
|
||||
test.isTrue(isLocalConnection(req), 'Is a local connection');
|
||||
});
|
||||
|
||||
['1.2.3.4', '2001:0db8:0000:0042:0000:8a2e:0370:7334'].forEach((ip) => {
|
||||
req.connection.remoteAddress = ip;
|
||||
test.isFalse(isLocalConnection(req), 'Not a local connection');
|
||||
});
|
||||
|
||||
// Remote address check (socket)
|
||||
|
||||
['127.0.0.1', '::1'].forEach((ip) => {
|
||||
req.connection = {};
|
||||
req.socket.remoteAddress = ip;
|
||||
test.isTrue(isLocalConnection(req), 'Is a local connection');
|
||||
});
|
||||
|
||||
['1.2.3.4', '2001:0db8:0000:0042:0000:8a2e:0370:7334'].forEach((ip) => {
|
||||
req.connection = {};
|
||||
req.socket.remoteAddress = ip;
|
||||
test.isFalse(isLocalConnection(req), 'Not a local connection');
|
||||
});
|
||||
|
||||
// Header check
|
||||
|
||||
const localHeaders = [
|
||||
{
|
||||
name: 'forwarded',
|
||||
value: 'for=127.0.0.1; proto=http',
|
||||
ip: '127.0.0.1',
|
||||
},
|
||||
{
|
||||
name: 'forwarded',
|
||||
value: 'for="[::1]"; proto=http',
|
||||
ip: '::1',
|
||||
},
|
||||
{
|
||||
name: 'x-forwarded-for',
|
||||
value: '127.0.0.1',
|
||||
ip: '127.0.0.1',
|
||||
},
|
||||
];
|
||||
localHeaders.forEach((header) => {
|
||||
req.connection.remoteAddress = header.ip;
|
||||
req.headers[header.name] = header.value;
|
||||
test.isTrue(isLocalConnection(req), 'Is a local connection');
|
||||
});
|
||||
|
||||
const remoteHeaders = [
|
||||
{
|
||||
name: 'forwarded',
|
||||
value: 'for=1.2.3.4; proto=http',
|
||||
ip: '1.2.3.4',
|
||||
},
|
||||
{
|
||||
name: 'forwarded',
|
||||
value: 'for=1.2.3.4; proto=http',
|
||||
ip: '127.0.0.1',
|
||||
},
|
||||
{
|
||||
name: 'forwarded',
|
||||
value: 'for="[2001:0db8:0000:0042:0000:8a2e:0370:7334]"; proto=http',
|
||||
ip: '2001:0db8:0000:0042:0000:8a2e:0370:7334',
|
||||
},
|
||||
{
|
||||
name: 'x-forwarded-for',
|
||||
value: '1.2.3.4',
|
||||
ip: '1.2.3.4',
|
||||
},
|
||||
{
|
||||
name: 'x-forwarded-for',
|
||||
value: '2001:0db8:0000:0042:0000:8a2e:0370:7334',
|
||||
ip: '2001:0db8:0000:0042:0000:8a2e:0370:7334',
|
||||
},
|
||||
];
|
||||
remoteHeaders.forEach((header) => {
|
||||
req.connection.remoteAddress = header.ip;
|
||||
req.headers[header.name] = header.value;
|
||||
test.isFalse(isLocalConnection(req), 'Not a local connection');
|
||||
});
|
||||
});
|
||||
|
||||
Tinytest.add('force-ssl - check for an SSL based connection', function (test) {
|
||||
const req = new http.IncomingMessage();
|
||||
|
||||
req.connection = { pair: {} };
|
||||
test.isTrue(isSslConnection(req), 'Is an SSL based connection');
|
||||
|
||||
req.connection = {};
|
||||
req.headers = { forwarded: 'for=127.0.0.1; proto=https' };
|
||||
test.isTrue(isSslConnection(req), 'Is an SSL based connection');
|
||||
|
||||
req.headers = { 'x-forwarded-proto': 'https' };
|
||||
test.isTrue(isSslConnection(req), 'Is an SSL based connection');
|
||||
|
||||
req.headers = { forwarded: 'for=127.0.0.1; proto=http' };
|
||||
test.isFalse(isSslConnection(req), 'Is not an SSL based connection');
|
||||
|
||||
req.headers = { 'x-forwarded-proto': 'http' };
|
||||
test.isFalse(isSslConnection(req), 'Is not an SSL based connection');
|
||||
});
|
||||
19
packages/force-ssl-common/package.js
Normal file
19
packages/force-ssl-common/package.js
Normal file
@@ -0,0 +1,19 @@
|
||||
Package.describe({
|
||||
summary: 'Internal force-ssl common code.',
|
||||
version: '1.0.14'
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
'forwarded-http': '0.3.0'
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use('ecmascript');
|
||||
api.mainModule('force_ssl_common.js', 'server');
|
||||
});
|
||||
|
||||
Package.onTest(function (api) {
|
||||
api.use('ecmascript');
|
||||
api.use('tinytest');
|
||||
api.mainModule('force_ssl_tests.js', 'server');
|
||||
});
|
||||
@@ -9,12 +9,14 @@ is always encrypted to protect users from active spoofing attacks.
|
||||
|
||||
Meteor bundles (i.e. `meteor build`) do not include an HTTPS server or
|
||||
certificate. A proxy server that terminates SSL in front of a Meteor
|
||||
bundle must set the standard `x-forwarded-proto` header for this package to work.
|
||||
bundle must set the `x-forwarded-proto` or `forwarded`
|
||||
([RFC 7239](https://tools.ietf.org/html/rfc7239)) header for this package to
|
||||
work.
|
||||
|
||||
The `x-forwarded-proto` header is used to determine if the connection arrived
|
||||
over HTTPS and a heuristic is used to guess if it's running in development. To
|
||||
simplify development, unencrypted connections from `localhost` are always
|
||||
accepted over HTTP.
|
||||
The `x-forwarded-proto` or `forwarded` header is used to determine if the
|
||||
connection arrived over HTTPS and a heuristic is used to guess if it's running
|
||||
in development. To simplify development, unencrypted connections from
|
||||
`localhost` are always accepted over HTTP.
|
||||
|
||||
We recommend this package only for deployment platforms that do not have their
|
||||
own ability to force SSL. If you're deploying with
|
||||
|
||||
1
packages/force-ssl/force_ssl_both.js
Normal file
1
packages/force-ssl/force_ssl_both.js
Normal file
@@ -0,0 +1 @@
|
||||
Object.assign(Meteor.absoluteUrl.defaultOptions, { secure: true });
|
||||
@@ -1 +0,0 @@
|
||||
_.extend(Meteor.absoluteUrl.defaultOptions, {secure: true});
|
||||
@@ -1,4 +1,5 @@
|
||||
var url = Npm.require("url");
|
||||
import { isLocalConnection, isSslConnection } from 'meteor/force-ssl-common';
|
||||
|
||||
// Unfortunately we can't use a connect middleware here since
|
||||
// sockjs installs itself prior to all existing listeners
|
||||
@@ -16,29 +17,10 @@ httpServer.addListener('request', function (req, res) {
|
||||
// localhost (development mode).
|
||||
//
|
||||
// Note: someone could trick us into serving over non-ssl by setting
|
||||
// x-forwarded-for or x-forwarded-proto. Not much we can do there if
|
||||
// we still want to operate behind proxies.
|
||||
// x-forwarded-for, x-forwarded-proto, forwarded, etc. Not much we can do
|
||||
// there if we still want to operate behind proxies.
|
||||
|
||||
var remoteAddress =
|
||||
req.connection.remoteAddress || req.socket.remoteAddress;
|
||||
// Determine if the connection is only over localhost. Both we
|
||||
// received it on localhost, and all proxies involved received on
|
||||
// localhost.
|
||||
var localhostRegexp = /^\s*(127\.0\.0\.1|::1)\s*$/;
|
||||
var isLocal = (
|
||||
localhostRegexp.test(remoteAddress) &&
|
||||
(!req.headers['x-forwarded-for'] ||
|
||||
_.all(req.headers['x-forwarded-for'].split(','), function (x) {
|
||||
return localhostRegexp.test(x);
|
||||
})));
|
||||
|
||||
// Determine if the connection was over SSL at any point. Either we
|
||||
// received it as SSL, or a proxy did and translated it for us.
|
||||
var isSsl = req.connection.pair ||
|
||||
(req.headers['x-forwarded-proto'] &&
|
||||
req.headers['x-forwarded-proto'].indexOf('https') !== -1);
|
||||
|
||||
if (!isLocal && !isSsl) {
|
||||
if (!isLocalConnection(req) && !isSslConnection(req)) {
|
||||
// connection is not cool. send a 302 redirect!
|
||||
|
||||
var host = url.parse(Meteor.absoluteUrl()).hostname;
|
||||
@@ -57,7 +39,7 @@ httpServer.addListener('request', function (req, res) {
|
||||
|
||||
// connection is OK. Proceed normally.
|
||||
var args = arguments;
|
||||
_.each(oldHttpServerListeners, function(oldListener) {
|
||||
oldHttpServerListeners.forEach((oldListener) => {
|
||||
oldListener.apply(httpServer, args);
|
||||
});
|
||||
});
|
||||
@@ -67,8 +49,8 @@ httpServer.addListener('request', function (req, res) {
|
||||
//
|
||||
// Websockets come in via the 'upgrade' request. We can override this,
|
||||
// however the problem is we're not sure if the websocket is actually
|
||||
// encrypted. We don't get x-forwarded-for or x-forwarded-proto on
|
||||
// websockets. It's possible the 'sec-websocket-origin' header does
|
||||
// encrypted. We don't get x-forwarded-for, x-forwarded-proto, forwarded, etc.
|
||||
// on websockets. It's possible the 'sec-websocket-origin' header does
|
||||
// what we want, but that's not clear.
|
||||
//
|
||||
// For now, this package allows raw unencrypted DDP connections over
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
Package.describe({
|
||||
summary: "Require this application to use HTTPS",
|
||||
version: "1.0.13",
|
||||
version: "1.0.14",
|
||||
prodOnly: true
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use('ecmascript');
|
||||
api.use('webapp', 'server');
|
||||
api.use('underscore');
|
||||
// make sure we come after livedata, so we load after the sockjs
|
||||
// server has been instantiated.
|
||||
api.use('ddp', 'server');
|
||||
api.use('force-ssl-common', 'server');
|
||||
|
||||
api.addFiles('force_ssl_common.js', ['client', 'server']);
|
||||
api.addFiles('force_ssl_server.js', 'server');
|
||||
api.mainModule('force_ssl_both.js', ['client', 'server']);
|
||||
api.mainModule('force_ssl_server.js', 'server');
|
||||
|
||||
// Another thing we could do is add a force_ssl_client.js file that
|
||||
// makes sure document.location.protocol is 'https'. If it detected
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"align-text": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
|
||||
"from": "align-text@>=0.1.3 <0.2.0"
|
||||
},
|
||||
"async": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
|
||||
"from": "async@>=0.2.6 <0.3.0"
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
|
||||
"from": "camelcase@>=1.0.2 <2.0.0"
|
||||
},
|
||||
"center-align": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
|
||||
"from": "center-align@>=0.1.1 <0.2.0"
|
||||
},
|
||||
"cliui": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
|
||||
"from": "cliui@>=2.1.0 <3.0.0"
|
||||
},
|
||||
"decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"from": "decamelize@>=1.0.0 <2.0.0"
|
||||
},
|
||||
"is-buffer": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz",
|
||||
"from": "is-buffer@>=1.0.2 <2.0.0"
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.0.4.tgz",
|
||||
"from": "kind-of@>=3.0.2 <4.0.0"
|
||||
},
|
||||
"lazy-cache": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
|
||||
"from": "lazy-cache@>=1.0.3 <2.0.0"
|
||||
},
|
||||
"longest": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
|
||||
"from": "longest@>=1.0.1 <2.0.0"
|
||||
},
|
||||
"repeat-string": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
||||
"from": "repeat-string@>=1.5.2 <2.0.0"
|
||||
},
|
||||
"right-align": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
|
||||
"from": "right-align@>=0.1.1 <0.2.0"
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
|
||||
"from": "source-map@>=0.5.1 <0.6.0"
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "2.7.5",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz",
|
||||
"from": "uglify-js@2.7.5"
|
||||
},
|
||||
"uglify-to-browserify": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
|
||||
"from": "uglify-to-browserify@>=1.0.0 <1.1.0"
|
||||
},
|
||||
"window-size": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
|
||||
"from": "window-size@0.1.0"
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
|
||||
"from": "wordwrap@0.0.2"
|
||||
},
|
||||
"yargs": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
|
||||
"from": "yargs@>=3.10.0 <3.11.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
// The UglifyJSMinify API can also be used for beautification. Test that it
|
||||
// behaves as expected.
|
||||
|
||||
Tinytest.add('minifier-js - uglify beautify', function (test) {
|
||||
// See <https://github.com/mishoo/UglifyJS2#the-simple-way> and
|
||||
// <http://lisperator.net/uglifyjs/codegen> for the API we're calling.
|
||||
test.equal(UglifyJSMinify('one = function () { return 1; };',
|
||||
{ fromString: true,
|
||||
output: { beautify: true,
|
||||
indent_level: 2,
|
||||
width: 80 } }).code,
|
||||
'one = function() {\n' +
|
||||
' return 1;\n' +
|
||||
'};');
|
||||
});
|
||||
@@ -1,2 +1 @@
|
||||
UglifyJS = Npm.require('uglify-js');
|
||||
UglifyJSMinify = UglifyJS.minify;
|
||||
meteorBabelMinify = Babel.minify;
|
||||
@@ -1,26 +1,10 @@
|
||||
Package.describe({
|
||||
summary: "JavaScript minifier",
|
||||
version: "1.2.18"
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
"uglify-js": "2.7.5"
|
||||
});
|
||||
|
||||
Npm.strip({
|
||||
"uglify-js": ["test/"]
|
||||
version: "2.0.0"
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.export(['UglifyJSMinify', 'UglifyJS']);
|
||||
api.use('babel-compiler');
|
||||
api.export(['meteorBabelMinify']);
|
||||
api.addFiles(['minifier.js'], 'server');
|
||||
});
|
||||
|
||||
Package.onTest(function (api) {
|
||||
api.use('minifier-js', 'server');
|
||||
api.use('tinytest');
|
||||
|
||||
api.addFiles([
|
||||
'beautify-tests.js',
|
||||
], 'server');
|
||||
});
|
||||
|
||||
Submodule packages/non-core/blaze updated: 637dac5914...0ded311a13
@@ -1,23 +1,20 @@
|
||||
Package.describe({
|
||||
name: 'standard-minifier-js',
|
||||
version: '1.2.3',
|
||||
version: '2.0.0',
|
||||
summary: 'Standard javascript minifiers used with Meteor apps by default.',
|
||||
documentation: 'README.md'
|
||||
documentation: 'README.md',
|
||||
});
|
||||
|
||||
Package.registerBuildPlugin({
|
||||
name: "minifyStdJS",
|
||||
use: [
|
||||
'minifier-js'
|
||||
'minifier-js@2.0.0',
|
||||
],
|
||||
sources: [
|
||||
'plugin/minify-js.js'
|
||||
]
|
||||
'plugin/minify-js.js',
|
||||
],
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
api.use('isobuild:minifier-plugin@1.0.0');
|
||||
});
|
||||
|
||||
Package.onTest(function(api) {
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
Plugin.registerMinifier({
|
||||
extensions: ["js"],
|
||||
archMatching: "web"
|
||||
extensions: ['js'],
|
||||
archMatching: 'web'
|
||||
}, function () {
|
||||
var minifier = new UglifyJSMinifier();
|
||||
var minifier = new MeteorBabelMinifier();
|
||||
return minifier;
|
||||
});
|
||||
|
||||
function UglifyJSMinifier () {};
|
||||
function MeteorBabelMinifier () {};
|
||||
|
||||
UglifyJSMinifier.prototype.processFilesForBundle = function (files, options) {
|
||||
MeteorBabelMinifier.prototype.processFilesForBundle = function(files, options) {
|
||||
var mode = options.minifyMode;
|
||||
|
||||
// don't minify anything for development
|
||||
@@ -17,96 +17,92 @@ UglifyJSMinifier.prototype.processFilesForBundle = function (files, options) {
|
||||
file.addJavaScript({
|
||||
data: file.getContentsAsBuffer(),
|
||||
sourceMap: file.getSourceMap(),
|
||||
path: file.getPathInBundle()
|
||||
path: file.getPathInBundle(),
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var minifyOptions = {
|
||||
fromString: true,
|
||||
compress: {
|
||||
drop_debugger: false,
|
||||
unused: false,
|
||||
dead_code: false
|
||||
}
|
||||
};
|
||||
|
||||
function maybeThrowMinifyErrorBySourceFile(error, file) {
|
||||
var minifierErrorRegex = /\(line: (\d+), col: (\d+), pos: (\d+)\)/;
|
||||
var parseError = minifierErrorRegex.exec(error.toString());
|
||||
var minifierErrorRegex = /^(.*?)\s?\((\d+):(\d+)\)$/;
|
||||
var parseError = minifierErrorRegex.exec(error.message);
|
||||
|
||||
if (parseError) {
|
||||
var lineErrorMessage = parseError[0];
|
||||
var lineErrorLineNumber = parseError[1];
|
||||
if (!parseError) {
|
||||
// If we were unable to parse it, just let the usual error handling work.
|
||||
return;
|
||||
}
|
||||
|
||||
var parseErrorContentIndex = lineErrorLineNumber - 1;
|
||||
var lineErrorMessage = parseError[1];
|
||||
var lineErrorLineNumber = parseError[2];
|
||||
|
||||
// Unlikely, since we have a multi-line fixed header in this file.
|
||||
if (parseErrorContentIndex < 0) {
|
||||
return;
|
||||
}
|
||||
var parseErrorContentIndex = lineErrorLineNumber - 1;
|
||||
|
||||
/*
|
||||
// Unlikely, since we have a multi-line fixed header in this file.
|
||||
if (parseErrorContentIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
What we're parsing looks like this:
|
||||
/*
|
||||
|
||||
/////////////////////////////////////////
|
||||
// //
|
||||
// path/to/file.js //
|
||||
// //
|
||||
/////////////////////////////////////////
|
||||
// 1
|
||||
var illegalECMAScript = true; // 2
|
||||
// 3
|
||||
/////////////////////////////////////////
|
||||
What we're parsing looks like this:
|
||||
|
||||
Btw, the above code is intentionally not newer ECMAScript so
|
||||
we don't break ourselves.
|
||||
/////////////////////////////////////////
|
||||
// //
|
||||
// path/to/file.js //
|
||||
// //
|
||||
/////////////////////////////////////////
|
||||
// 1
|
||||
var illegalECMAScript = true; // 2
|
||||
// 3
|
||||
/////////////////////////////////////////
|
||||
|
||||
*/
|
||||
Btw, the above code is intentionally not newer ECMAScript so
|
||||
we don't break ourselves.
|
||||
|
||||
var contents = file.getContentsAsString().split(/\n/);
|
||||
var lineContent = contents[parseErrorContentIndex];
|
||||
*/
|
||||
|
||||
// Try to grab the line number, which sometimes doesn't exist on
|
||||
// line, abnormally-long lines in a larger block.
|
||||
var lineSrcLineParts = /^(.*?)(?:\s*\/\/ (\d+))?$/.exec(lineContent);
|
||||
var contents = file.getContentsAsString().split(/\n/);
|
||||
var lineContent = contents[parseErrorContentIndex];
|
||||
|
||||
// The line didn't match at all? Let's just not try.
|
||||
if (!lineSrcLineParts) {
|
||||
return;
|
||||
}
|
||||
// Try to grab the line number, which sometimes doesn't exist on
|
||||
// line, abnormally-long lines in a larger block.
|
||||
var lineSrcLineParts = /^(.*?)(?:\s*\/\/ (\d+))?$/.exec(lineContent);
|
||||
|
||||
var lineSrcLineContent = lineSrcLineParts[1];
|
||||
var lineSrcLineNumber = lineSrcLineParts[2];
|
||||
// The line didn't match at all? Let's just not try.
|
||||
if (!lineSrcLineParts) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Count backward from the failed line to find the filename.
|
||||
for (var c = parseErrorContentIndex - 1; c >= 0; c--) {
|
||||
var sourceLine = contents[c];
|
||||
var lineSrcLineContent = lineSrcLineParts[1];
|
||||
var lineSrcLineNumber = lineSrcLineParts[2];
|
||||
|
||||
// If the line is a boatload of slashes, we're in the right place.
|
||||
if (/^\/\/\/{6,}$/.test(sourceLine)) {
|
||||
// Count backward from the failed line to find the filename.
|
||||
for (var c = parseErrorContentIndex - 1; c >= 0; c--) {
|
||||
var sourceLine = contents[c];
|
||||
|
||||
// If 4 lines back is the same exact line, we've found the framing.
|
||||
if (contents[c - 4] === sourceLine) {
|
||||
// If the line is a boatload of slashes, we're in the right place.
|
||||
if (/^\/\/\/{6,}$/.test(sourceLine)) {
|
||||
|
||||
// So in that case, 2 lines back is the file path.
|
||||
var parseErrorPath = contents[c - 2]
|
||||
.substring(3)
|
||||
.replace(/\s+\/\//, "");
|
||||
// If 4 lines back is the same exact line, we've found the framing.
|
||||
if (contents[c - 4] === sourceLine) {
|
||||
|
||||
var minError = new Error(
|
||||
"UglifyJS minification error: \n\n" +
|
||||
error.message + " at " + parseErrorPath +
|
||||
(lineSrcLineNumber ? " line " + lineSrcLineNumber + "\n\n" : "") +
|
||||
" within " + file.getPathInBundle() + " " +
|
||||
lineErrorMessage + ":\n\n" +
|
||||
lineSrcLineContent + "\n"
|
||||
);
|
||||
// So in that case, 2 lines back is the file path.
|
||||
var parseErrorPath = contents[c - 2]
|
||||
.substring(3)
|
||||
.replace(/\s+\/\//, "");
|
||||
|
||||
throw minError;
|
||||
}
|
||||
var minError = new Error(
|
||||
"Babili minification error " +
|
||||
"within " + file.getPathInBundle() + ":\n" +
|
||||
parseErrorPath +
|
||||
(lineSrcLineNumber ? ", line " + lineSrcLineNumber : "") + "\n" +
|
||||
"\n" +
|
||||
lineErrorMessage + ":\n" +
|
||||
"\n" +
|
||||
lineSrcLineContent + "\n"
|
||||
);
|
||||
|
||||
throw minError;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,32 +110,33 @@ UglifyJSMinifier.prototype.processFilesForBundle = function (files, options) {
|
||||
|
||||
var allJs = '';
|
||||
files.forEach(function (file) {
|
||||
// Don't reminify *.min.js.
|
||||
if (/\.min\.js$/.test(file.getPathInBundle())) {
|
||||
allJs += file.getContentsAsString();
|
||||
} else {
|
||||
var minified;
|
||||
try {
|
||||
minified = UglifyJSMinify(file.getContentsAsString(), minifyOptions);
|
||||
if (!(minified && typeof minified.code === "string")) {
|
||||
throw new Error();
|
||||
// Don't reminify *.min.js.
|
||||
if (/\.min\.js$/.test(file.getPathInBundle())) {
|
||||
allJs += file.getContentsAsString();
|
||||
} else {
|
||||
var minified;
|
||||
|
||||
try {
|
||||
minified = meteorBabelMinify(file.getContentsAsString());
|
||||
|
||||
if (!(minified && typeof minified.code === "string")) {
|
||||
throw new Error();
|
||||
}
|
||||
} catch (err) {
|
||||
var filePath = file.getPathInBundle();
|
||||
|
||||
maybeThrowMinifyErrorBySourceFile(err, file);
|
||||
|
||||
err.message += " while minifying " + filePath;
|
||||
throw err;
|
||||
}
|
||||
} catch (err) {
|
||||
var filePath = file.getPathInBundle();
|
||||
|
||||
// Try to catch the ugly Uglify error.
|
||||
maybeThrowMinifyErrorBySourceFile(err, file);
|
||||
|
||||
err.message += " while minifying " + filePath;
|
||||
throw err;
|
||||
allJs += minified.code;
|
||||
}
|
||||
allJs += '\n\n';
|
||||
|
||||
allJs += minified.code;
|
||||
}
|
||||
allJs += '\n\n';
|
||||
|
||||
Plugin.nudge();
|
||||
});
|
||||
Plugin.nudge();
|
||||
});
|
||||
|
||||
if (files.length) {
|
||||
files[0].addJavaScript({ data: allJs });
|
||||
|
||||
@@ -20,7 +20,7 @@ modules-test-package
|
||||
dispatch:mocha-phantomjs
|
||||
dispatch:mocha-browser
|
||||
standard-minifier-css@1.3.3
|
||||
standard-minifier-js@1.2.2
|
||||
standard-minifier-js@2.0.0
|
||||
client-only-ecmascript
|
||||
modules-test-plugin
|
||||
shell-server@0.2.2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
allow-deny@1.0.5
|
||||
autoupdate@1.3.12
|
||||
babel-compiler@6.14.1
|
||||
babel-compiler@6.15.0
|
||||
babel-runtime@1.0.1
|
||||
base64@1.0.10
|
||||
binary-heap@1.0.10
|
||||
@@ -42,7 +42,7 @@ logging@1.1.17
|
||||
meteor@1.6.1
|
||||
meteor-base@1.0.4
|
||||
minifier-css@1.2.16
|
||||
minifier-js@1.2.17
|
||||
minifier-js@2.0.0
|
||||
minimongo@1.0.20
|
||||
mobile-experience@1.0.4
|
||||
mobile-status-bar@1.0.14
|
||||
@@ -68,7 +68,7 @@ shell-server@0.2.2
|
||||
spacebars@1.0.13
|
||||
spacebars-compiler@1.1.0
|
||||
standard-minifier-css@1.3.3
|
||||
standard-minifier-js@1.2.2
|
||||
standard-minifier-js@2.0.0
|
||||
templating@1.3.0
|
||||
templating-compiler@1.3.0
|
||||
templating-runtime@1.3.0
|
||||
|
||||
Reference in New Issue
Block a user