Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

architect

Package Overview
Dependencies
Maintainers
2
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

architect - npm Package Compare versions

Comparing version 0.0.3 to 0.1.1

demos/calculator/.npmignore

475

architect.js

@@ -1,330 +0,265 @@

var path = require('path');
var dirname = require('path').dirname;
var resolve = require('path').resolve;
var existsSync = require('path').existsSync;
var realpathSync = require('fs').realpathSync;
var EventEmitter = require('events').EventEmitter;
var inherits = require('util').inherits;
exports.loadConfig = loadConfig;
exports.resolveConfig = resolveConfig;
exports.createApp = createApp;
function createApp(config, options, callback) {
if (typeof callback === "undefined") {
callback = options;
options = {};
}
config = processConfig(config, options);
// console.log("compiled config:");
// console.log(JSON.stringify(config));
startContainers(config, callback);
exports.Architect = Architect;
// This is assumed to be used at startup and uses sync I/O as well as can
// throw exceptions. It loads and parses a config file.
function loadConfig(configPath) {
var config = require(configPath);
var base = dirname(configPath);
return resolveConfig(config, base);
}
// Gather and preflight the config.
exports.processConfig = processConfig;
function processConfig(configPath, options) {
options = options || {};
var config = {};
// Allow passing in either config path or config object
if (typeof configPath === "object") {
config = configPath;
configPath = "<provided config object>";
if (!config.basePath) {
var err = new Error("'basePath' required in config object");
return callback(err);
function resolveConfig(config, base) {
config.forEach(function (plugin, index) {
// Shortcut where string is used for plugin without any options.
if (typeof plugin === "string") {
plugin = config[index] = { packagePath: plugin };
}
} else {
configPath = require.resolve(configPath);
config = require(configPath);
}
// Overwrite console object from config if set in createApp options
if (typeof options.console !== "undefined") {
config.console = options.console;
} else {
config.console = console;
}
// Default basePath to the dirname of the config file
var basePath = config.basePath = config.basePath || path.dirname(configPath);
// Resolve plugin paths to the basePath and merge in plugin configs from
// package.json files.
Object.keys(config.containers).forEach(function (containerName) {
var containerConfig = config.containers[containerName];
var pluginsConfigs = containerConfig.plugins;
pluginsConfigs && pluginsConfigs.forEach(function (pluginConfig, index) {
// if plugin is a string it is interpreted as the package path
if (typeof pluginConfig === "string") {
pluginsConfigs[index] = pluginConfig = {
packagePath: pluginConfig
};
}
// packagePath is required on all plugins
if (!pluginConfig.hasOwnProperty("packagePath")) {
var err = new Error("'packagePath' required in `" +
configPath + "` at " + containerName + "[" + index + "]");
return callback(err);
}
var pluginConfigBase;
// the architect app can inject plugins into the master
if (pluginConfig.plugin) {
if (containerName !== "master")
return new Error("Plugins can only be injected into the master container");
var pluginConfigBase = pluginConfig.plugin;
}
else {
// Replace with fully resolved path
var packagePath = resolvePackage(basePath, pluginConfig.packagePath);
pluginConfig.packagePath = packagePath;
// Look up the provides and consumes in the package.json and merge.
try {
pluginConfigBase = require(packagePath).plugin;
} catch(err) {
throw new Error("Error '" + err + "' loading config from " + packagePath);
// The plugin is a package on the disk. We need to load it.
if (plugin.hasOwnProperty("packagePath")) {
plugin.packagePath = resolvePackageSync(base, plugin.packagePath);
var packageConf = require(plugin.packagePath);
var defaults = packageConf.plugin || {};
Object.keys(defaults).forEach(function (key) {
if (!plugin.hasOwnProperty(key)) {
plugin[key] = defaults[key];
}
}
if (!pluginConfigBase) {
throw new Error("Missing 'plugin' section in " + packagePath);
}
for (var key in pluginConfigBase) {
if (!pluginConfig.hasOwnProperty(key)) {
pluginConfig[key] = pluginConfigBase[key];
}
}
// provide defaults
pluginConfig.provides = pluginConfig.provides || [];
pluginConfig.consumes = pluginConfig.consumes || [];
});
});
plugin.setup = require(dirname(plugin.packagePath));
plugin.consumes = plugin.consumes || [];
plugin.provides = plugin.provides || [];
}
});
return config;
}
// Set a tmpdir for anything that might need it.
config.tmpdir = config.tmpdir || path.join(process.cwd(), ".architect");
// Check a plugin config list for bad dependencies and throw on error
function checkConfig(config) {
// Tell which containers need to listen for inbound connections. Also set
// name and tmpdir for all containers.
Object.keys(config.containers).forEach(function (containerName) {
var containerConfig = config.containers[containerName];
if (needsServe(config.containers, containerName)) {
containerConfig.needsServe = true;
// Check for the required fields in each plugin.
config.forEach(function (plugin) {
if (plugin.checked) { return; }
if (!plugin.hasOwnProperty("setup")) {
throw new Error("Plugin is missing the setup function " + JSON.stringify(plugin));
}
containerConfig.name = containerName;
containerConfig.tmpdir = config.tmpdir;
if (!plugin.hasOwnProperty("provides")) {
throw new Error("Plugin is missing the provides array " + JSON.stringify(plugin));
}
if (!plugin.hasOwnProperty("consumes")) {
throw new Error("Plugin is missing the consumes array " + JSON.stringify(plugin));
}
});
// Make sure there are no dependency cycles that would prevent the app
// from starting.
checkCycles(config);
return config;
// Stamp it approved so we don't check it again.
config.checked = true;
}
// pre flight dependency check
function checkCycles(config) {
var plugins = [];
var containers = config.containers;
Object.keys(containers).forEach(function(containerName) {
var pluginConfigs = containers[containerName].plugins || [];
pluginConfigs.forEach(function(pluginConfig) {
plugins.push({
packagePath: pluginConfig.packagePath,
provides: pluginConfig.provides.concat(),
consumes: pluginConfig.consumes.concat()
});
});
function checkCycles(config) {
var plugins = [];
config.forEach(function(pluginConfig) {
plugins.push({
packagePath: pluginConfig.packagePath,
provides: pluginConfig.provides.concat(),
consumes: pluginConfig.consumes.concat()
});
});
var resolved = {
hub: true
};
var changed = true;
var resolved = {
hub: true
};
var changed = true;
while(plugins.length && changed) {
changed = false;
while(plugins.length && changed) {
changed = false;
plugins.concat().forEach(function(plugin) {
var consumes = plugin.consumes.concat();
plugins.concat().forEach(function(plugin) {
var consumes = plugin.consumes.concat();
var resolvedAll = true;
for (var i=0; i<consumes.length; i++) {
var service = consumes[i];
if (!resolved[service]) {
resolvedAll = false;
} else {
plugin.consumes.splice(plugin.consumes.indexOf(service), 1);
}
var resolvedAll = true;
for (var i=0; i<consumes.length; i++) {
var service = consumes[i];
if (!resolved[service]) {
resolvedAll = false;
} else {
plugin.consumes.splice(plugin.consumes.indexOf(service), 1);
}
}
if (!resolvedAll)
return;
if (!resolvedAll)
return;
plugins.splice(plugins.indexOf(plugin), 1);
plugin.provides.forEach(function(service) {
resolved[service] = true;
});
changed = true;
plugins.splice(plugins.indexOf(plugin), 1);
plugin.provides.forEach(function(service) {
resolved[service] = true;
});
}
if (plugins.length) {
console.error("Could not resolve dependencies of these plugins:", plugins);
console.error("Resovled services:", resolved);
throw new Error("Could not resolve dependencies");
}
changed = true;
});
}
function calcProvides(container) {
var provides = {};
var plugins = container.plugins;
plugins && plugins.forEach(function (plugin) {
plugin.provides.forEach(function (service) {
provides[service] = true;
});
});
return provides;
if (plugins.length) {
console.error("Could not resolve dependencies of these plugins:", plugins);
console.error("Resovled services:", resolved);
throw new Error("Could not resolve dependencies");
}
}
function calcDepends(container, provides) {
if (!container.plugins) return false;
var i = container.plugins.length;
while (i--) {
var consumes = container.plugins[i].consumes;
var j = consumes.length;
while (j--) {
if (provides[consumes[j]]) return true;
function Architect(config) {
var app = this;
app.config = config;
var services = app.services = {
hub: {
on: function (name, callback) {
app.on(name, callback);
}
}
return false;
}
};
function needsServe(containers, name) {
var provides = calcProvides(containers[name]);
// First calculate what all services this container provides.
for (var key in containers) {
if (!containers.hasOwnProperty(key)) continue;
if (key === name) continue;
if (calcDepends(containers[key], provides)) return true;
// Check the config if it's not already checked.
if (!config.checked) {
try {
checkConfig(config)
} catch (err) {
app.emit("error", err);
}
return false;
}
}
exports.startContainers = startContainers;
function startContainers(config, callback) {
var hub = new EventEmitter();
var Agent = require('architect-agent').Agent;
var running;
var destructors = [];
var containers = {};
(function startPlugins() {
if (running) return;
running = true;
var pending = 0;
do {
var changed = false;
config.forEach(function (plugin) {
// Skip already-started and not-yet-ready plugins
if (plugin.started) return;
if (!plugin.consumes.every(function (name) {
return services[name];
})) { return; }
// This agent is used for the star topology of events. All child processes have access to it.
var hubAgent = new Agent({
broadcast: broadcast
});
pending++;
// Create all the containers in parallel (as dumb workers). Then once
// they are all created, tell them all to initialize in parallel. When
// they are all ready, call the callback.
var createLeft, readyLeft;
createLeft = readyLeft = Object.keys(config.containers).length;
// Create all the containers in parallel.
Object.keys(config.containers).forEach(function (name) {
var createContainer = (name === "master") ?
require('./container').createContainer :
spawnContainer;
var imports = {};
plugin.consumes.forEach(function (name) {
imports[name] = services[name];
});
plugin.started = true;
plugin.setup(plugin, imports, function (err, provided) {
if (err) { return app.emit("error", err); }
plugin.provides.forEach(function (name) {
if (!provided.hasOwnProperty(name)) {
var err = new Error("Plugin failed to provide " + name + " service. " + JSON.stringify(plugin));
return app.emit("error", err);
}
services[name] = provided[name];
app.emit("service", name, services[name]);
changed = true;
});
if (provided && provided.hasOwnProperty("onDestroy"))
destructors.push(provided.onDestroy);
createContainer(name, broadcast, function (err, container) {
if (err) throw err;
containers[name] = container;
broadcast("containerCreated", name);
if (--createLeft) return;
Object.keys(containers).forEach(function (name) {
containers[name].initialize(config.containers[name]);
pending--;
app.emit("plugin", plugin);
if (changed) { startPlugins(); }
});
});
} while (changed);
running = false;
if (!pending) {
app.emit("ready", app);
}
}());
this.destroy = function() {
destructors.forEach(function(destroy) {
destroy();
});
});
destructors = [];
};
}
inherits(Architect, EventEmitter);
hub.on('containerReady', checkReady);
// Returns an event emitter that represents the app. It can emit events.
// event: ("service" name, service) emitted when a service is ready to be consumed.
// event: ("plugin", plugin) emitted when a plugin registers.
// event: ("ready", app) emitted when all plugins are ready.
// event: ("error", err) emitted when something goes wrong.
// app.services - a hash of all the services in this app
// app.config - the plugin config that was passed in.
function createApp(config, callback) {
var app = new Architect(config);
if (callback) {
app.on("error", onError);
app.once("ready", onReady);
function checkReady() {
if (--readyLeft) return;
broadcast("containersDone", Object.keys(containers));
callback(null, containers);
}
function onError(err) {
app.removeListener("ready", done);
app.destroy();
done(err, app);
}
// A function that all containers have access to that enables broadcasting.
// The following kinds messages are broadcasts to all containers:
// - serviceReady { container, socket, name, functions }
// - servicesDone {} - all services are initialized
// - serviceDied {serviceName}
// - containerReady { container }
// - containerDied {containerName}
// - containersDone {} - all containers are initialized
function broadcast(name, message) {
if (config.console && config.console.info) {
//console.info("BROADCAST: " + name, message);
function onReady() {
done(null, app);
}
hub.emit(name, message);
process.nextTick(function () {
Object.keys(containers).forEach(function (key) {
if (typeof containers[key].onBroadcast !== "function") {
console.log("containers[%s]", key, containers[key]);
}
containers[key].onBroadcast(name, message);
});
});
}
// Create a new container in a child process
function spawnContainer(name, broadcast, callback) {
var spawn = require('child_process').spawn;
var Agent = require('architect-agent').Agent;
var socketTransport = require('architect-socket-transport');
var child = spawn(process.execPath, [require.resolve('./worker-process.js')], {
customFds: [-1, 1, 2],
stdinStream: createPipe(true),
env: { ARCHITECT_CONTAINER_NAME: name }
});
var transport = socketTransport(child.stdin);
hubAgent.attach(transport, function (container) {
callback(null, container);
});
child.stdin.resume();
// TODO: Monitor child for life
var called = false;
function done(err) {
if (called) return;
called = true;
callback(err, app);
}
}
return app;
}
// Taken from node's child process code.
var Pipe;
function createPipe(ipc) {
// Lazy load
if (!Pipe) {
Pipe = process.binding('pipe_wrap').Pipe;
}
return new Pipe(ipc);
}
// Node style package resolving so that plugins' package.json can be found relative to the config file
// It's not the full node require system algorithm, but it's the 99% case
function resolvePackage(base, packagePath) {
// This throws, make sure to wrap in try..catch
var packagePathCache = {};
function resolvePackageSync(base, packagePath) {
var originalBase = base;
if (!packagePathCache.hasOwnProperty(base)) {
packagePathCache[base] = {};
}
var cache = packagePathCache[base];
if (cache.hasOwnProperty(packagePath)) {
return cache[packagePath];
}
if (packagePath[0] === "." || packagePath[0] === "/") {
var newPath = path.resolve(base, packagePath, "package.json");
if (path.existsSync(newPath)) return newPath;
var newPath = resolve(base, packagePath, "package.json");
if (existsSync(newPath)) {
newPath = realpathSync(newPath);
cache[packagePath] = newPath;
return newPath;
}
}
else {
while (base) {
var newPath = path.resolve(base, "node_modules", packagePath, "package.json");
if (path.existsSync(newPath)) return newPath;
var newPath = resolve(base, "node_modules", packagePath, "package.json");
if (existsSync(newPath)) {
newPath = realpathSync(newPath);
cache[packagePath] = newPath;
return newPath;
}
base = base.substr(0, base.lastIndexOf("/"));
}
}
throw new Error("Can't find '" + packagePath + "' relative to '" + base + '"');
var err = new Error("Can't find '" + packagePath + "' relative to '" + originalBase + "'");
err.code = "ENOENT";
throw err;
}
{
"name": "architect",
"description": "A Simple yet powerful plugin system for large-scale node applications",
"version": "0.0.3",
"author": "ajax.org B.V. <info@ajax.org>",
"contributors": [
{ "name": "Tim Caswell", "email": "tim@c9.io>" },
{ "name": "Fabian Jakobs", "email": "fabian@c9.io" },
{ "name": "Christoph Dorn", "email": "christoph@christophdorn.com" }
],
"main": "architect.js",
"repository" : {
"type" : "git",
"url" : "http://github.com/c9/architect.git"
"name": "architect",
"description": "A Simple yet powerful plugin system for node applications",
"version": "0.1.1",
"author": "ajax.org B.V. <info@ajax.org>",
"contributors": [
{
"name": "Tim Caswell",
"email": "tim@c9.io"
},
"dependencies": {
"architect-agent": "~0.2.0",
"architect-socket-transport": "~0.2.0"
{
"name": "Fabian Jakobs",
"email": "fabian@c9.io"
},
"devDependencies": {},
"optionalDependencies": {},
"licenses" : [{
"type" : "MIT",
"url" : "http://github.com/c9/architect/raw/master/LICENSE"
}]
{
"name": "Christoph Dorn",
"email": "christoph@christophdorn.com"
}
],
"main": "architect.js",
"repository": {
"type": "git",
"url": "http://github.com/c9/architect.git"
},
"dependencies": {},
"devDependencies": {},
"optionalDependencies": {},
"licenses": [
{
"type": "MIT",
"url": "http://github.com/c9/architect/raw/master/LICENSE"
}
]
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc