Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
mendel-core
Advanced tools
Mendel Core is the brain behind mendel-middleware
. It is used to resolve hundreds or even thousands of potential permutations of "application trees". It uses deterministic hashing to provide a two step resolving:
Mendel Core works by loading Mendel Manifests on a per-process basis, and resolves "application trees" on a per request (or per user session) basis.
+-----------------------------------------------+
| |
| Mendel Core long running instance |
| |
| (contains all variations of |
| all pre-compiled files) |
| |
+----+--------------+------------+----------+---+
| | | |
v v v |
|
+-----------+ +-----------+ +-----------+ v
| Tree for | | Tree for | | Tree for |
| variations| | variations| | variations| etc
| A | | B, C | | A, C |
+-----------+ +-----------+ +-----------+
Each tree representation can be used to server-side render, to hash generation or to resolve a bundle for a particular set of variations.
When the user first visits the application, Mendel Core use a variation array to resolve the application code:
+--------------------+
| |
| Mendel Manifests |
| |
+---------+----------+
|
v
+---------------------------+
| |
| Mendel Core per process |
| instance |
| |
+------------------+--------+
^ |
NodeJS Process | |
+------------------------------------------------------------------+
HTTP Request | |
| |
+------------+ | | +--------------+
| | | | | |
| variations +-----------+ +------------> | dependencies |
| | | | |
+------------+ | +--------------+
|
| +--------------+
| | |
+------------> | hash |
| |
+--------------+
Dependencies are a list of filenames and their content. Dependencies can than be used to server-side render and the hash can be used to generate bundle URLs that are safe for CDN caching.
When a request comes in with a hash, Mendel Core is able to safely recover all the dependencies from the manifest:
+--------------------+
| |
| Mendel Manifests |
| |
+---------+----------+
|
v
+---------------------------+
| |
| MendelCore per process |
| instance |
| |
+------------------+--------+
^ |
NodeJS Process | |
+------------------------------------------------------------------+
HTTP Request | |
| |
+------------+ | | +--------------+
| | | | | |
| hash +-----------+ +------------> | dependencies |
| | | |
+------------+ +--------------+
This request will usually be used to serve a bundle. The request can come from a user or from a CDN or any other proxy/caching layers. It does not need cookies and won't need a "Vary" header to prevent it to be cached incorrectly by proxies. The hash is sufficient to consistently resolve the dependencies.
The Mendel hash is a binary format encoded in URLSafeBase64 (RFC4648 section 5). The binary has the format below, where each number in parenthesis is the number of bytes used for a particular information:
1 2 3 4 5 6
+------------+---------+--------------+---------+----------+---------+
| ascii(6*8) | uint(8) | loop uint(8) | uint(8) | uint(16) | bin(20) |
| === mendel | | !== 255 | == 255 | | |
+------------+---------+--------------+---------+----------+---------+
The 6 pieces stand for:
Mendel starts with ID and VERSION and will walk the manifest, starting in the entry points of the package. Each "dep" is a browserify-like payload and has a sha1 of the source code, and also the dependencies of this file. Mendel will collect sha1 of all files and count how many files were walked.
Every time Mendel finds a file with variations, it uses a one of two desired method for choosing a variation (discussed below), and it will then push the index of the chosen variation to the variation index array. The numbers are also appended to VARIATIONS_ARRAY of the binary.
Once walking is done, Mendel adds VARIATIONS_LIMITER, to the binary, adds FILE_COUNT with the number of files walked, and computes CONTENT_HASH, the sha1 of all walked sha1 of the bundle. The final binary is than encoded with URLSafeBase64.
The variations can be resolved in two ways: By an array of desired variation names. This is useful for generating the hash for the first time. The second way, is when we decode a hash binary, and walk the manifest to collect and bundle source code, using the variation index array.
Because we need to make sure the contents are the same requested by user, the hash is calculated both when generating it for the first time (when generating HTML request) and when collecting the source code payload (when dynamically serving the bundle). If it is a match, the source code can be concatenated using browser_pack
.
Usually, you can use the mendel-middleware
instead of using mendel-core
directly. We also provide a reference implementation for the middleware use. In case you need advanced use of Mendel, the minimal server bellow should be enough for you to start your custom implementation.
// we call it MendelTrees because it is like a seed that will grow many
// different trees depending on variations and bundles. It is almost a
// tree factory, if you must :wink:
const MendelTrees = require('mendel-core');
const trees = MendelTrees(); // if no options passed, will read .mendelrc
function requestHandler(request, response) {
// bundle should match .mendelrc bundle and can be multiple bundles
const bundle = 'main';
if (/index.html/.test(request.url)) {
// variations can come from request cookie
const variations = ['base'];
// if you have multiple bundles you need to find one tree per bundle
const tree = trees.findTreeForVariations(bundle, variations);
// bundle url can be wrapped in a cookie-less CDN url
const src = '/' + bundle + '.' + tree.hash + '.js';
const script = '<script src="'+ src + '"></script>';
response.end('<html><head>'+ script + '</head></html>');
} else {
// if you have multiple bundles you will need routes based on bundle
const jsPath = new RegExp('/' + bundle + '\.(.*)\.js');
if (jsPath.test(request.url)) {
// your CDN can safely cache this url without cookies
// and without "Vary" header
const hash = request.url.match(jsPath)[1];
const tree = trees.findTreeForHash(bundle, hash);
response.end(JSON.stringify(tree)); // use your bundler
} else {
response.end('Not found');
}
}
}
const http = require('http');
const server = http.createServer(requestHandler);
server.listen(3000);
Usually, you won't be serving a JSON representation for your bundle, you will instead add error control, conflict detection and etc and eventually pack your bundle using UMD, browser-pack, rollup or even just concatenating all sources on global scope. Here is a minimal example of using browser pack:
// on the example above, add this line at the top of the file
const bpack = require('browser-pack');
// and replace the line `response.end(JSON.stringify(tree));` by:
if (!tree || tree.error || tree.conflicts) {
return response.end('Error ' + tree && tree.error || tree.conflictList);
}
const pack = bpack({raw: true, hasExports: true});
pack.pipe(response);
const modules = tree.deps.filter(Boolean);
for (var i = 0; i < modules.length; i++) {
pack.write(modules[i]);
}
pack.end();
Please, see mendel-middlware
for a production ready implementation of mendel-core with an express compatible API.
Please, see mendel-development-middleware
for a viable implementation of development bundles without using mendel-core
and with cached re-bundling based on source file changes.
FAQs
Mendel shared dependencies production use
The npm package mendel-core receives a total of 48 weekly downloads. As such, mendel-core popularity was classified as not popular.
We found that mendel-core demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.