Split out 'socket' hosting model into a separate optional NuGet package, since most developers won't need it

This commit is contained in:
SteveSandersonMS
2016-11-30 11:59:56 +00:00
parent ebf5a18344
commit 832da2a451
24 changed files with 244 additions and 135 deletions

View File

@@ -2,6 +2,7 @@ versionSuffix=$1
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
projects=( projects=(
./src/Microsoft.AspNetCore.NodeServices ./src/Microsoft.AspNetCore.NodeServices
./src/Microsoft.AspNetCore.NodeServices.Sockets
./src/Microsoft.AspNetCore.SpaServices ./src/Microsoft.AspNetCore.SpaServices
./src/Microsoft.AspNetCore.AngularServices ./src/Microsoft.AspNetCore.AngularServices
./src/Microsoft.AspNetCore.ReactServices ./src/Microsoft.AspNetCore.ReactServices

View File

@@ -3,6 +3,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.NodeServices; using Microsoft.AspNetCore.NodeServices;
using Microsoft.AspNetCore.NodeServices.Sockets;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApplication namespace ConsoleApplication
@@ -16,6 +17,10 @@ namespace ConsoleApplication
// Set up the DI system // Set up the DI system
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddNodeServices(options => { services.AddNodeServices(options => {
// To compare with Socket hosting, uncomment the following line
// Since .NET Core 1.1, the HTTP hosting model has become basically as fast as the Socket hosting model
//options.UseSocketHosting();
options.ProjectPath = Directory.GetCurrentDirectory(); options.ProjectPath = Directory.GetCurrentDirectory();
options.WatchFileExtensions = new string[] {}; // Don't watch anything options.WatchFileExtensions = new string[] {}; // Don't watch anything
}); });

View File

@@ -9,6 +9,7 @@
"type": "platform" "type": "platform"
}, },
"Microsoft.AspNetCore.NodeServices": "1.1.0-*", "Microsoft.AspNetCore.NodeServices": "1.1.0-*",
"Microsoft.AspNetCore.NodeServices.Sockets": "1.1.0-*",
"Microsoft.Extensions.DependencyInjection": "1.1.0" "Microsoft.Extensions.DependencyInjection": "1.1.0"
}, },
"frameworks": { "frameworks": {

View File

@@ -0,0 +1,3 @@
/bin/
/node_modules/
yarn.lock

View File

@@ -44,11 +44,81 @@
/* 0 */ /* 0 */
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(7); module.exports = __webpack_require__(1);
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive,
// but simplifies things for the consumer of this module.
__webpack_require__(2);
var net = __webpack_require__(3);
var path = __webpack_require__(4);
var readline = __webpack_require__(5);
var ArgsUtil_1 = __webpack_require__(6);
var ExitWhenParentExits_1 = __webpack_require__(7);
var virtualConnectionServer = __webpack_require__(8);
// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct
// reference to Node's runtime 'require' function.
var dynamicRequire = eval('require');
// Signal to the .NET side when we're ready to accept invocations
var server = net.createServer().on('listening', function () {
console.log('[Microsoft.AspNetCore.NodeServices:Listening]');
});
// Each virtual connection represents a separate invocation
virtualConnectionServer.createInterface(server).on('connection', function (connection) {
readline.createInterface(connection, null).on('line', function (line) {
try {
// Get a reference to the function to invoke
var invocation = JSON.parse(line);
var invokedModule = dynamicRequire(path.resolve(process.cwd(), invocation.moduleName));
var invokedFunction = invocation.exportedFunctionName ? invokedModule[invocation.exportedFunctionName] : invokedModule;
// Prepare a callback for accepting non-streamed JSON responses
var hasInvokedCallback_1 = false;
var invocationCallback = function (errorValue, successValue) {
if (hasInvokedCallback_1) {
throw new Error('Cannot supply more than one result. The callback has already been invoked,'
+ ' or the result stream has already been accessed');
}
hasInvokedCallback_1 = true;
connection.end(JSON.stringify({
result: successValue,
errorMessage: errorValue && (errorValue.message || errorValue),
errorDetails: errorValue && (errorValue.stack || null)
}));
};
// Also support streamed binary responses
Object.defineProperty(invocationCallback, 'stream', {
enumerable: true,
get: function () {
hasInvokedCallback_1 = true;
return connection;
}
});
// Actually invoke it, passing through any supplied args
invokedFunction.apply(null, [invocationCallback].concat(invocation.args));
}
catch (ex) {
connection.end(JSON.stringify({
errorMessage: ex.message,
errorDetails: ex.stack
}));
}
});
});
// Begin listening now. The underlying transport varies according to the runtime platform.
// On Windows it's Named Pipes; on Linux/OSX it's Domain Sockets.
var useWindowsNamedPipes = /^win/.test(process.platform);
var parsedArgs = ArgsUtil_1.parseArgs(process.argv);
var listenAddress = (useWindowsNamedPipes ? '\\\\.\\pipe\\' : '/tmp/') + parsedArgs.listenAddress;
server.listen(listenAddress);
ExitWhenParentExits_1.exitWhenParentExits(parseInt(parsedArgs.parentPid));
/***/ }, /***/ },
/* 1 */,
/* 2 */ /* 2 */
/***/ function(module, exports) { /***/ function(module, exports) {
@@ -90,7 +160,12 @@
/***/ }, /***/ },
/* 3 */, /* 3 */
/***/ function(module, exports) {
module.exports = require("net");
/***/ },
/* 4 */ /* 4 */
/***/ function(module, exports) { /***/ function(module, exports) {
@@ -98,6 +173,12 @@
/***/ }, /***/ },
/* 5 */ /* 5 */
/***/ function(module, exports) {
module.exports = require("readline");
/***/ },
/* 6 */
/***/ function(module, exports) { /***/ function(module, exports) {
"use strict"; "use strict";
@@ -123,7 +204,7 @@
/***/ }, /***/ },
/* 6 */ /* 7 */
/***/ function(module, exports) { /***/ function(module, exports) {
/* /*
@@ -189,96 +270,13 @@
} }
/***/ },
/* 7 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive,
// but simplifies things for the consumer of this module.
__webpack_require__(2);
var net = __webpack_require__(8);
var path = __webpack_require__(4);
var readline = __webpack_require__(9);
var ArgsUtil_1 = __webpack_require__(5);
var ExitWhenParentExits_1 = __webpack_require__(6);
var virtualConnectionServer = __webpack_require__(10);
// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct
// reference to Node's runtime 'require' function.
var dynamicRequire = eval('require');
// Signal to the .NET side when we're ready to accept invocations
var server = net.createServer().on('listening', function () {
console.log('[Microsoft.AspNetCore.NodeServices:Listening]');
});
// Each virtual connection represents a separate invocation
virtualConnectionServer.createInterface(server).on('connection', function (connection) {
readline.createInterface(connection, null).on('line', function (line) {
try {
// Get a reference to the function to invoke
var invocation = JSON.parse(line);
var invokedModule = dynamicRequire(path.resolve(process.cwd(), invocation.moduleName));
var invokedFunction = invocation.exportedFunctionName ? invokedModule[invocation.exportedFunctionName] : invokedModule;
// Prepare a callback for accepting non-streamed JSON responses
var hasInvokedCallback_1 = false;
var invocationCallback = function (errorValue, successValue) {
if (hasInvokedCallback_1) {
throw new Error('Cannot supply more than one result. The callback has already been invoked,'
+ ' or the result stream has already been accessed');
}
hasInvokedCallback_1 = true;
connection.end(JSON.stringify({
result: successValue,
errorMessage: errorValue && (errorValue.message || errorValue),
errorDetails: errorValue && (errorValue.stack || null)
}));
};
// Also support streamed binary responses
Object.defineProperty(invocationCallback, 'stream', {
enumerable: true,
get: function () {
hasInvokedCallback_1 = true;
return connection;
}
});
// Actually invoke it, passing through any supplied args
invokedFunction.apply(null, [invocationCallback].concat(invocation.args));
}
catch (ex) {
connection.end(JSON.stringify({
errorMessage: ex.message,
errorDetails: ex.stack
}));
}
});
});
// Begin listening now. The underlying transport varies according to the runtime platform.
// On Windows it's Named Pipes; on Linux/OSX it's Domain Sockets.
var useWindowsNamedPipes = /^win/.test(process.platform);
var parsedArgs = ArgsUtil_1.parseArgs(process.argv);
var listenAddress = (useWindowsNamedPipes ? '\\\\.\\pipe\\' : '/tmp/') + parsedArgs.listenAddress;
server.listen(listenAddress);
ExitWhenParentExits_1.exitWhenParentExits(parseInt(parsedArgs.parentPid));
/***/ }, /***/ },
/* 8 */ /* 8 */
/***/ function(module, exports) {
module.exports = require("net");
/***/ },
/* 9 */
/***/ function(module, exports) {
module.exports = require("readline");
/***/ },
/* 10 */
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
"use strict"; "use strict";
var events_1 = __webpack_require__(11); var events_1 = __webpack_require__(9);
var VirtualConnection_1 = __webpack_require__(12); var VirtualConnection_1 = __webpack_require__(10);
// Keep this in sync with the equivalent constant in the .NET code. Both sides split up their transmissions into frames with this max length, // Keep this in sync with the equivalent constant in the .NET code. Both sides split up their transmissions into frames with this max length,
// and both will reject longer frames. // and both will reject longer frames.
var MaxFrameBodyLength = 16 * 1024; var MaxFrameBodyLength = 16 * 1024;
@@ -460,13 +458,13 @@
/***/ }, /***/ },
/* 11 */ /* 9 */
/***/ function(module, exports) { /***/ function(module, exports) {
module.exports = require("events"); module.exports = require("events");
/***/ }, /***/ },
/* 12 */ /* 10 */
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
"use strict"; "use strict";
@@ -475,17 +473,18 @@
function __() { this.constructor = d; } function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}; };
var stream_1 = __webpack_require__(13); var stream_1 = __webpack_require__(11);
/** /**
* Represents a virtual connection. Multiple virtual connections may be multiplexed over a single physical socket connection. * Represents a virtual connection. Multiple virtual connections may be multiplexed over a single physical socket connection.
*/ */
var VirtualConnection = (function (_super) { var VirtualConnection = (function (_super) {
__extends(VirtualConnection, _super); __extends(VirtualConnection, _super);
function VirtualConnection(_beginWriteCallback) { function VirtualConnection(_beginWriteCallback) {
_super.call(this); var _this = _super.call(this) || this;
this._beginWriteCallback = _beginWriteCallback; _this._beginWriteCallback = _beginWriteCallback;
this._flowing = false; _this._flowing = false;
this._receivedDataQueue = []; _this._receivedDataQueue = [];
return _this;
} }
VirtualConnection.prototype._read = function () { VirtualConnection.prototype._read = function () {
this._flowing = true; this._flowing = true;
@@ -516,7 +515,7 @@
/***/ }, /***/ },
/* 13 */ /* 11 */
/***/ function(module, exports) { /***/ function(module, exports) {
module.exports = require("stream"); module.exports = require("stream");

View File

@@ -2,7 +2,7 @@ using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.AspNetCore.NodeServices.HostingModels.PhysicalConnections namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
{ {
internal class NamedPipeConnection : StreamConnection internal class NamedPipeConnection : StreamConnection
{ {

View File

@@ -2,7 +2,7 @@ using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.AspNetCore.NodeServices.HostingModels.PhysicalConnections namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
{ {
internal abstract class StreamConnection : IDisposable internal abstract class StreamConnection : IDisposable
{ {

View File

@@ -2,7 +2,7 @@ using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.AspNetCore.NodeServices.HostingModels.PhysicalConnections namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
{ {
internal class UnixDomainSocketConnection : StreamConnection internal class UnixDomainSocketConnection : StreamConnection
{ {

View File

@@ -3,7 +3,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
namespace Microsoft.AspNetCore.NodeServices.HostingModels.PhysicalConnections namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
{ {
// From System.IO.Pipes/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs (an internal class in System.IO.Pipes) // From System.IO.Pipes/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs (an internal class in System.IO.Pipes)
internal sealed class UnixDomainSocketEndPoint : EndPoint internal sealed class UnixDomainSocketEndPoint : EndPoint

View File

@@ -1,16 +1,15 @@
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.NodeServices.HostingModels.PhysicalConnections; using Microsoft.AspNetCore.NodeServices.HostingModels;
using Microsoft.AspNetCore.NodeServices.HostingModels.VirtualConnections; using Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections;
using Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
namespace Microsoft.AspNetCore.NodeServices.HostingModels namespace Microsoft.AspNetCore.NodeServices.Sockets
{ {
/// <summary> /// <summary>
/// A specialisation of the OutOfProcessNodeInstance base class that uses a lightweight binary streaming protocol /// A specialisation of the OutOfProcessNodeInstance base class that uses a lightweight binary streaming protocol
@@ -77,7 +76,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
// wait for the same connection task. There's no reason why the first caller should have the // wait for the same connection task. There's no reason why the first caller should have the
// special ability to cancel the connection process in a way that would affect subsequent // special ability to cancel the connection process in a way that would affect subsequent
// callers. So, each caller just independently stops awaiting connection if that call is cancelled. // callers. So, each caller just independently stops awaiting connection if that call is cancelled.
await EnsureVirtualConnectionClientCreated().OrThrowOnCancellation(cancellationToken); await ThrowOnCancellation(EnsureVirtualConnectionClientCreated(), cancellationToken);
} }
// For each invocation, we open a new virtual connection. This gives an API equivalent to opening a new // For each invocation, we open a new virtual connection. This gives an API equivalent to opening a new
@@ -213,6 +212,17 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
return $"--listenAddress {listenAddress}"; return $"--listenAddress {listenAddress}";
} }
private static Task ThrowOnCancellation(Task task, CancellationToken cancellationToken)
{
return task.IsCompleted
? task // If the task is already completed, no need to wrap it in a further layer of task
: task.ContinueWith(
_ => {}, // If the task completes, allow execution to continue
cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
#pragma warning disable 649 // These properties are populated via JSON deserialization #pragma warning disable 649 // These properties are populated via JSON deserialization
private class RpcJsonResponse<TResult> private class RpcJsonResponse<TResult>
{ {

View File

@@ -0,0 +1,21 @@
using System;
namespace Microsoft.AspNetCore.NodeServices.Sockets
{
/// <summary>
/// Extension methods that help with populating a <see cref="NodeServicesOptions"/> object.
/// </summary>
public static class NodeServicesOptionsExtensions
{
/// <summary>
/// Configures the <see cref="INodeServices"/> service so that it will use out-of-process
/// Node.js instances and perform RPC calls over binary sockets (on Windows, this is
/// implemented as named pipes; on other platforms it uses domain sockets).
/// </summary>
public static void UseSocketHosting(this NodeServicesOptions options)
{
var pipeName = "pni-" + Guid.NewGuid().ToString("D"); // Arbitrary non-clashing string
options.NodeInstanceFactory = () => new SocketNodeInstance(options, pipeName);
}
}
}

View File

@@ -1,12 +1,12 @@
// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive, // Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive,
// but simplifies things for the consumer of this module. // but simplifies things for the consumer of this module.
import './Util/OverrideStdOutputs'; import '../../Microsoft.AspNetCore.NodeServices/TypeScript/Util/OverrideStdOutputs';
import * as net from 'net'; import * as net from 'net';
import * as path from 'path'; import * as path from 'path';
import * as readline from 'readline'; import * as readline from 'readline';
import { Duplex } from 'stream'; import { Duplex } from 'stream';
import { parseArgs } from './Util/ArgsUtil'; import { parseArgs } from '../../Microsoft.AspNetCore.NodeServices/TypeScript/Util/ArgsUtil';
import { exitWhenParentExits } from './Util/ExitWhenParentExits'; import { exitWhenParentExits } from '../../Microsoft.AspNetCore.NodeServices/TypeScript/Util/ExitWhenParentExits';
import * as virtualConnectionServer from './VirtualConnections/VirtualConnectionServer'; import * as virtualConnectionServer from './VirtualConnections/VirtualConnectionServer';
// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct // Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct

View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es3",
"module": "commonjs",
"moduleResolution": "node",
"types": ["node"]
},
"exclude": [
"node_modules"
]
}

View File

@@ -4,7 +4,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow; using System.Threading.Tasks.Dataflow;
namespace Microsoft.AspNetCore.NodeServices.HostingModels.VirtualConnections namespace Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections
{ {
/// <summary> /// <summary>
/// A virtual read/write connection, typically to a remote process. Multiple virtual connections can be /// A virtual read/write connection, typically to a remote process. Multiple virtual connections can be

View File

@@ -5,7 +5,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.AspNetCore.NodeServices.HostingModels.VirtualConnections namespace Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections
{ {
/// <summary> /// <summary>
/// A callback that will be invoked if the <see cref="VirtualConnectionClient"/> encounters a read error. /// A callback that will be invoked if the <see cref="VirtualConnectionClient"/> encounters a read error.

View File

@@ -0,0 +1,18 @@
{
"name": "nodeservices.sockets",
"version": "1.0.0",
"description": "This is not really an NPM package and will not be published. This file exists only to reference compilation tools.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "./node_modules/.bin/webpack"
},
"author": "Microsoft",
"license": "Apache-2.0",
"devDependencies": {
"@types/node": "^6.0.42",
"ts-loader": "^0.8.2",
"typescript": "^2.0.0",
"webpack": "^1.13.1"
}
}

View File

@@ -0,0 +1,39 @@
{
"description": "Socket-based RPC for Microsoft.AspNetCore.NodeServices",
"version": "1.1.0-*",
"packOptions": {
"repository": {
"type": "git",
"url": "git://github.com/aspnet/javascriptservices"
},
"tags": [
"aspnetcore",
"aspnetcoremvc",
"nodeservices"
]
},
"buildOptions": {
"warningsAsErrors": true,
"keyFile": "../../tools/Key.snk",
"embed": [
"Content/**/*"
],
"xmlDoc": true
},
"dependencies": {
"Microsoft.AspNetCore.NodeServices": "1.1.0-*"
},
"frameworks": {
"net451": {
"dependencies": {
"Microsoft.Tpl.Dataflow": "4.5.24"
}
},
"netstandard1.6": {
"dependencies": {
"System.IO.Pipes": "4.3.0",
"System.Threading.Tasks.Dataflow": "4.7.0"
}
}
}
}

View File

@@ -0,0 +1,20 @@
module.exports = {
target: 'node',
externals: ['fs', 'net', 'events', 'readline', 'stream'],
resolve: {
extensions: [ '.ts' ]
},
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' },
]
},
entry: {
'entrypoint-socket': ['./TypeScript/SocketNodeInstanceEntryPoint'],
},
output: {
libraryTarget: 'commonjs',
path: './Content/Node',
filename: '[name].js'
}
};

View File

@@ -1,2 +1,3 @@
/bin/ /bin/
/node_modules/ /node_modules/
yarn.lock

View File

@@ -1,5 +1,3 @@
using System;
namespace Microsoft.AspNetCore.NodeServices.HostingModels namespace Microsoft.AspNetCore.NodeServices.HostingModels
{ {
/// <summary> /// <summary>
@@ -15,16 +13,5 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
{ {
options.NodeInstanceFactory = () => new HttpNodeInstance(options); options.NodeInstanceFactory = () => new HttpNodeInstance(options);
} }
/// <summary>
/// Configures the <see cref="INodeServices"/> service so that it will use out-of-process
/// Node.js instances and perform RPC calls over binary sockets (on Windows, this is
/// implemented as named pipes; on other platforms it uses domain sockets).
/// </summary>
public static void UseSocketHosting(this NodeServicesOptions options)
{
var pipeName = "pni-" + Guid.NewGuid().ToString("D"); // Arbitrary non-clashing string
options.NodeInstanceFactory = () => new SocketNodeInstance(options, pipeName);
}
} }
} }

View File

@@ -27,17 +27,11 @@
"NETStandard.Library": "1.6.1-*" "NETStandard.Library": "1.6.1-*"
}, },
"frameworks": { "frameworks": {
"net451": { "net451": {},
"dependencies": {
"Microsoft.Tpl.Dataflow": "4.5.24"
}
},
"netstandard1.6": { "netstandard1.6": {
"dependencies": { "dependencies": {
"System.Diagnostics.Process": "4.3.0", "System.Diagnostics.Process": "4.3.0",
"System.IO.FileSystem.Watcher": "4.3.0", "System.IO.FileSystem.Watcher": "4.3.0"
"System.IO.Pipes": "4.3.0",
"System.Threading.Tasks.Dataflow": "4.7.0"
} }
} }
}, },

View File

@@ -10,8 +10,7 @@ module.exports = {
] ]
}, },
entry: { entry: {
'entrypoint-http': ['./TypeScript/HttpNodeInstanceEntryPoint'], 'entrypoint-http': ['./TypeScript/HttpNodeInstanceEntryPoint']
'entrypoint-socket': ['./TypeScript/SocketNodeInstanceEntryPoint'],
}, },
output: { output: {
libraryTarget: 'commonjs', libraryTarget: 'commonjs',