Skip to content

agracio/edge-js

Repository files navigation

Edge.js: .NET and Node.js in-process

Actions Status Git Issues Closed Issues


This library is based on https://github.com/tjanczuk/edge all credit for original work goes to Tomasz Janczuk.


Overview

Edge.js allows you to run Node.js and .NET code in one process on Windows, macOS, and Linux

You can call .NET functions from Node.js and Node.js functions from .NET.
Edge.js takes care of marshaling data between CLR and V8. Edge.js also reconciles threading models of single-threaded V8 and multi-threaded CLR.
Edge.js ensures correct lifetime of objects on V8 and CLR heaps.
The CLR code can be pre-compiled or specified as C#, F#, Python (IronPython), or PowerShell source: Edge.js can compile CLR scripts at runtime.
Edge can be extended to support other CLR languages or DSLs.

Edge.js interop model

Edge.js provides an asynchronous, in-process mechanism for interoperability between Node.js and .NET. You can use this mechanism to:

  • script Node.js from a .NET application on Windows using .NET Framework
  • script C# from a Node.js application on Windows, macOS, and Linux using .NET Framework/.NET Core
  • use CLR multi-threading from Node.js for CPU intensive work more...
  • write native extensions to Node.js in C# instead of C/C++
  • integrate existing .NET components into Node.js applications
  • access MS SQL from Node.js using ADO.NET
  • script F# from Node.js
  • script Powershel from Node.js
  • script Python (IronPython) from Node.js

Read more about the background and motivations of the project here.

Updates

  • Support for new versions of Node.Js.
  • Support for .NET Core 3.1 - 9.x on Windows/Linux/macOS.
  • Fixes AccessViolationException when running Node.js code from C# PR #573.
  • Fixes StackOverflowException PR #566 that occurs when underlying C# code throws complex exception.
  • Fixes issues #469, #713
  • Other PRs: PR #725, PR #640
  • Multiple bug fixes and improvements to the original code.

NPM package edge-js

NuGet package EdgeJs


Electron

For use with Electron electron-edge-js

VS Code extensions

VS Code uses Electron shell, to write extensions for it using Edge.js use electron-edge-js

Quick start

Sample app that shows how to work with .NET Core using inline code and compiled C# libraries.
https://github.com/agracio/edge-js-quick-start

Pre-requisites

Node.js Support

edge-js support policy

  • Windows supports 4 latest LTS or candidate LTS releases (even numbered).
  • Windows supports up to 1 "Current" (odd numbered) release and drops it when superseeded by new LTS candidate.
  • macOS comes precompiled with same releases as Windows. When using Node.js version that is not pre-compiled edge-js binaries will be compiled during npm install using node-gyp.
  • Linux will always compile edge-js binaries during npm install using node-gyp.

Windows

Version x86 x64 arm64
18.x ✔️ ✔️
20.x ✔️ ✔️ ✔️
22.x ✔️ ✔️ ✔️
24.x ✔️ ✔️
25.x ✔️ ✔️

macOS binaries pre-compiled for

Version x64 arm64
18.x ✔️ ✔️
20.x ✔️ ✔️
22.x ✔️ ✔️
24.x ✔️ ✔️
25.x ✔️ ✔️

Supports

Version x64 arm64
16.x - 25.x ✔️ ✔️

Linux

Version x64 arm64
16.x - 25.x ✔️ ✔️

Other Linux architectures might work but have not been tested.

Scripting CLR from Node.js and Node.js from CRL

Script CLR from Node.js Script Node.js from CLR
.NET 4.5 Mono 6.x CoreCLR
Windows ✔️ ✔️* ✔️
Linux ✔️* ✔️
macOS ✔️ ✔️
.NET 4.5 Mono CoreCLR
Windows ✔️
Linux
macOS

Mono

Mono is no longer actively supported. Existing code will remain In Edge.Js but focus will be on .NET Core.

* Edge.js does not have a flag to force it to run in Mono environment on Windows, it would only be possible to run if you have Windows without .NET Framework present.
* On Linux there is a known bug that will throw exception when executing C# code under certain conditions. Use one of the following options.

  1. Set LD_PRELOAD env variable before running Edge.js with Mono
EXPORT LD_PRELOAD="libmono-2.0.so libmonosgen-2.0.so libstdc++.so.6"
  1. Set LD_PRELOAD in your Node.js app entry point using Javascript
Object.assign(process.env, {
    // Work around Mono problem: undefined symbol: mono_add_internal_call_with_flags
    LD_PRELOAD: 'libmono-2.0.so libmonosgen-2.0.so libstdc++.so.6',
});

Source: mono/mono#17079 (comment)

Edge.js uses this code in test setup:

edge-js/tools/test.js

Lines 19 to 24 in 8bab03a

if(process.platform === 'linux' && !process.env.EDGE_USE_CORECLR){
Object.assign(process.env, {
// Work around Mono problem: undefined symbol: mono_add_internal_call_with_flags
LD_PRELOAD: 'libmono-2.0.so libmonosgen-2.0.so libstdc++.so.6',
});
}

Node.js application packaging

When packaging your application using Webpack make sure that edge-js is specified as external module.

Webpack

module.exports = {
  target: 'node',
  externals: {
    'edge-js': 'commonjs2 edge-js',
    'edge-cs': 'commonjs2 edge-cs',
  },
  node: {
    __dirname: true,
    __filename: true,
  },
}

Next.js

next.config.js

const nextConfig = {
  serverExternalPackages: ['edge-js'],
}
 
module.exports = nextConfig

next.config.ts

const nextConfig: NextConfig = {
  serverExternalPackages: ['edge-js'],
};

export default nextConfig;

Node.js single executable application packaging

edge-js-pkg

Additional languages support

SQL scripting

Provides simple access to SQL without the need to write separate C# code.

Framework Platform NPM Package Language code Documentation
.NET 4.5 Windows edge-sql sql Script SQL in Node.js 🔗
CoreCLR Any edge-sql sql Script SQL in Node.js 🔗

Python (IronPython) scripting

NOTE This functionality requires IronPython 3.4

Framework Platform NPM Package Language code Documentation
.NET 4.5 Windows edge-py py Script Python in Node.js 🔗
CoreCLR Any edge-py py Script Python in Node.js 🔗

PowerShell scripting

NOTE CoreCLR requires dotnet 8

Framework Platform NPM Package Language code Documentation
.NET 4.5 Windows edge-ps ps Script PowerShell in Node.js 🔗
CoreCLR Windows edge-ps ps Script PowerShell in Node.js 🔗

F# scripting

Framework Platform NPM Package Language code Documentation
.NET 4.5 Windows edge-fs fs Script F# in Node.js 🔗
CoreCLR Windows edge-fs fs Script F# in Node.js 🔗

How to use

Scripting CLR from Node.js - full documentation

Scripting Node.js from CLR - full documentation

Scripting CLR from Node.js sample app https://github.com/agracio/edge-js-quick-start


Short guide

Inline C# code

ES5

var edge = require('edge-js');

var helloWorld = edge.func(function () {/*
    async (input) => { 
        return ".NET Welcomes " + input.ToString(); 
    }
*/});

helloWorld('JavaScript', function (error, result) {
    if (error) throw error;
    console.log(result);
});

ES6 with templated strings

var edge = require('edge-js');

var helloWorld = edge.func(`
    async (input) => { 
        return ".NET Welcomes " + input.ToString(); 
    }
`);

helloWorld('JavaScript', function (error, result) {
    if (error) throw error;
    console.log(result);
});

Passing parameters

var edge = require('edge-js');

var helloWorld = edge.func(function () {/*
    async (dynamic input) => { 
        return "Welcome " + input.name + " " + input.surname; 
    }
*/});

helloWorld({name: 'John', surname: 'Smith'}, function (error, result) {
    if (error) throw error;
    console.log(result);
});

Using C# class

var getPerson = edge.func({
    source: function () {/* 
        using System.Threading.Tasks;
        using System;

        public class Person
        {
            public Person(string name, string email, int age)
            {
                Id =  Guid.NewGuid();
                Name = name;
                Email = email;
                Age = age;
            }
            public Guid Id {get;set;}
            public string Name {get;set;}
            public string Email {get;set;}
            public int Age {get;set;}
        }

        public class Startup
        {
            public async Task<object> Invoke(dynamic input)
            {
                return new Person(input.name, input.email, input.age);
            }
        }
    */}
});

getPerson({name: 'John Smith', email: 'john.smith@myemailprovider', age: 35}, function(error, result) {
    if (error) throw error;
    console.log(result);
});

When using inline C# class code must include

public class Startup
{
    public async Task<object> Invoke(object|dynamic input)
    {
        // code
        // return results
    }
}

Using compiled assembly

// People.cs

using System;

namespace People
{
    public class Person
    {
        public Person(string name, string email, int age)
        {
            Id =  Guid.NewGuid();
            Name = name;
            Email = email;
            Age = age;
        }
        public Guid Id {get;}
        public string Name {get;}
        public string Email {get;}
        public int Age {get;}
    }
}

// EdgeJsMethods.cs

using System.Threading.Tasks;
using People;

namespace EdgeJsMethods
{
    class Methods
    {
        public async Task<object> GetPerson(dynamic input)
        {
            return await Task.Run(() => new Person(input.name, input.email, input.age));
        }
    }
}
var edge = require('edge-js');

var getPerson = edge.func({
    assemblyFile: myDll, // path to compiled .dll
    typeName: 'EdgeJsMethods.Methods',
    methodName: 'GetPerson'
});

getPerson({name: 'John Smith', email: 'john.smith@myemailprovider', age: 35}, function(error, result) {
    if (error) throw error;
    console.log(result);
});

Edge.js C# method must have the following signature

public async Task<object> MyMethod(object|dynamic input)
{
    //return results sync/async;
}

CoreCLR