baristalabs / baristacore Goto Github PK
View Code? Open in Web Editor NEWBaristaCore is a framework for providing a serverless platform using ChakraCore and .Net Core
License: MIT License
BaristaCore is a framework for providing a serverless platform using ChakraCore and .Net Core
License: MIT License
Runtime/Context need not inherit DynamicObject, only JsValues.
Allows multiple module loaders to be used. Scripts specify a particular module loader with a prefix, e.g. github!baristalabs/foo/bar.js. nuget!my.package.name/module
Future Module imports in dependent modules use the loader without needing a prefix
Thinking this would consist of the following:
Things like the built-in script-based modules would return the corresponding .d.ts from their original repos or @types.
https://github.com/jkoritzinsky/SharpGenTools
Could reduce the interop layer generation steps and provide a cleaner interop layer to boot. If anything, we should examine the output to refine what interop types are exposed.
There are some additional auto-conversions to consider:
The VSTS/MSBuild teams have stated that code coverage is only supported in Visual Studio enterprise despite being a fundamental development necessity.
Use Coverlet(https://github.com/tonerdo/coverlet) in build scripts to generate and submit code coverage to coveralls.
Thanks to chakra-core/ChakraCore#4707, it's now possible to programmatically get at namespace objects.
This allows the functionality in
https://github.com/BaristaLabs/BaristaCore/blob/master/src/BaristaLabs.BaristaCore.Common/BaristaContext.cs#L456
and
https://github.com/BaristaLabs/BaristaCore/blob/master/src/BaristaLabs.BaristaCore.Common/BaristaModuleRecord.cs#L260
to no longer rely on a 'shim' script in order to get at the variables exported by modules. This should improve execution speed, remove pollution of the global namespace, reduce the possibility of down-stream side effects, and allow the export of non-default values as well.
There's some confusion between JsObject and things like JsString, which are objects, when supplied to certain Object.xxx methods cause an exception.
A workaround is to manually test that the jsObj.Value == JavaScriptValueType.Object
Use HttpClient instead.
It appears that the ChakraCore engine doesn't provide an intrinsic Blob object.
Implement this as a module, perhaps packaged up in a set of common base objects module if we find other common objects that don't exist. (FileAPI Module?)
For ref: https://developer.mozilla.org/en-US/docs/Web/API/Blob
The built-in HttpClient in .Net core does not perform any caching.
Create a wrapper around the HttpClient that does, and use it in the fetch implementation.
Requires a dotnet core http/2 client ;-/
I initially had a hard time getting started b/c if an unhandled exception BaristaLabs.BaristaCore.JsScriptException: fetch import module failed
. Here's the references used in a fresh console app:
<PackageReference Include="BaristaCore" Version="1.0.2-beta03" />
<PackageReference Include="BaristaLabs.BaristaCore.ChakraCore" Version="1.10.2" />
After pinning to 1.8.1, everything worked just fine. I then built from source and everything worked with the 1.10.2 version, so my best guess is BaristaCore/Extensions may need a newer publish to match ChakraCore, but I didn't look any further.
Besides this initial minor bump, I'm having a lot of fun experimenting with it this weekend; I'm really glad I stumbled across it. Thanks for sharing!
Currently there's logic to catch JavaScriptExecptions and throw corresponding BaristaExceptions all over the place -- consolidate this logic.
Would look at, say, a barista_modules folder and using unity or ninject — or possibly Microsoft.Extensions.DI — finds all implementations and makes them available to scripts
Implement an IConversionStrategy of which concrete implementations can shape how conversion happens
[x] Add the interface
[x] Utilize the interface in dynamic object overloads
[x] Allow for DI at the context level
[x] Perform primitive value conversion
[x] Perform various direct object conversions (Undefined, Task, etc)
[ ] Perform delegate to JsFunction conversion
[ ] Code Coverage
[ ] Implement non value complex object conversion using reflection.
Implement another strategy that allows for AttributeBasedConversion - uses something similar to jurassic's JsPropertyAttribute - to specify specific object members that will be part of the JsObject. This still requires the full gamut of complex object conversion already available, however.
As a good test of bundling and dynamic module discovery, implement a 'Fetch' bundle.
Similar to the AggregateModuleLoader except that
e.g.
loader0 - webresource moduleloader
loader1 - github moduleloader
loader2 - assembly moduleloader
import foo from 'super-man'
The current construction patterns have a lot to be desired -- RT classes should probably use factories that handle the pooling of resources rather than having that all gathered up in the rt/ctx/etc classes themselves
Goals here are to improve the ability to add additional dependencies via constructor easily.
The converter would
Things to think about:
Generics?
imports the specified dependency by name, version into a variable defined by the script.
The intrinsic ChakraCore data types appear to support very large arrays > 2gb traditional .net limit, however, we've been limited to using byte[Int32.MaxValue] when marshaling them between ArrayBuffers, Strings and so forth.
We can probably leverage the new Memory object in C# 7.2 to get really big data sets in/out.
Per the standard available here: https://developer.mozilla.org/en-US/docs/Web/API/URL
Wrapper around System.Uri
I'm trying to get started with BaristaCore and it is a bumpy ride due to the lack of documentation. I could file a few issues, but I'm starting with this.
I created a new .NET Core 2.0 command line project, added the reference to the BaristaCore
package (version 1.0.2-beta03) and used the following:
using System;
using BaristaLabs.BaristaCore;
using BaristaLabs.BaristaCore.Extensions;
using BaristaLabs.BaristaCore.ModuleLoaders;
using Microsoft.Extensions.DependencyInjection;
namespace TestBaristaRepro
{
class Program
{
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddBaristaCore();
var moduleLoader = new InMemoryModuleLoader();
moduleLoader.RegisterModule(new Test1Module());
moduleLoader.RegisterModule(new Test2Module());
serviceCollection.AddSingleton<IBaristaModuleLoader>(moduleLoader);
var provider = serviceCollection.BuildServiceProvider();
var baristaRuntimeFactory = provider.GetRequiredService<IBaristaRuntimeFactory>();
using (var rt = baristaRuntimeFactory.CreateRuntime())
using (var ctx = rt.CreateContext()) {
var script = @"var objs = [{ name: 'm1', obj: m1 }, { name: 'm2', obj: m2 }];
var summary = """";
summary += ""result of manually running m1.test(1, 2): "" + m1.test(1, 2) + ""\n"";
//summary += ""result of manually running m2.test(1, 2): "" + m2.test(1, 2) + ""\n"";
for (let obj of objs) {
var x = obj.obj;
var name = obj.name;
summary += ""#"" + name + ""#\n"" + ""= (toString: "" + (x === null ? ""NULL"" : (x === undefined ? ""UNDEFINED"" : ""'"" + x.toString() + ""'"")) + "" (JSON: "" + JSON.stringify(x) + "")\n"";
for (var key in x) {
summary += "" "" + key + "": "" + global[key] + ""\n"";
}
}
return summary;";
using (ctx.Scope()) {
var moduleResult = ctx.EvaluateModule("import m1 from 'first-module'; import m2 from 'second-module'; export default function() {" + script + "}");
if (moduleResult is JsFunction fun) {
var resultOfCalling = fun.Call();
if (resultOfCalling is JsString str) {
var res = str.ToString();
Console.WriteLine("result is: ");
Console.WriteLine(res);
}
}
}
}
}
[BaristaModule("first-module", "Module one")]
public class Test1Module : IBaristaModule
{
public JsValue ExportDefault(BaristaContext context, BaristaModuleRecord referencingModule) {
var obj = context.CreateObject();
var fnAdd = context.CreateFunction(new Func<JsObject, int, int, int>((thisObj, a, b) =>
{
return a + b;
}));
obj.SetProperty("test", fnAdd);
return obj;
}
}
[BaristaModule("second-module", "Module two")]
public class Test2Module : IBaristaModule
{
public JsValue ExportDefault(BaristaContext context, BaristaModuleRecord referencingModule) {
return context.CreateExternalObject(new Test2());
}
}
[BaristaObject("test2")]
public class Test2 {
[BaristaProperty("test", Configurable = false, Enumerable = true)]
public int Test(int a, int b) {
return a + b;
}
}
}
}
This uses two different modules to make two different functions available. The code outputs the following:
result is:
result of manually running m1.test(1, 2): 3
#m1#
= (toString: '[object Object]' (JSON: {})
test: undefined
#m2#
= (toString: '[object Object]' (JSON: {})
In other words, on the first module, test
is callable, and it is visible as a property (so enumerable), but its value is undefined
instead of a function. On the second module, test
is neither callable nor visible.
So the question being: is there a way for the real value to be visible in case of the first module, and what do I need to do to make the second module work?
(Additionally, outside the scope of this issue, some background: I am trying to use BaristaCore to provide Javascript execution to make a small piece of business logic an interchangeable script. It would have a number of either modules or objects available that would return domain-specific information or allow it to perform the purpose of the script.
I try to follow along as well as I can in how to put this together, but the documentation is slim and differs from the code (in the documentation, the IBaristaModule
interface is asynchronous and in the code it's not, and I don't even know which behavior is the outdated one and will be going away). I have been working with Jurassic previously and am weighing BaristaCore and using the C# wrappers for ChakraCore directly.
I don't quite understand how you're meant to just evaluate code without wrapping it inside a module which exports a default function that you then call. That you had to use Scope()
took a while to figure out as well.)
Minimize git pull time.
Better usability when getting/setting/deleting object properties.
An article located here describes how Cloudflare workers use an “isolate” pattern rather than containers to run user-defined code in order to allow a greater request rate and associated performance.
Once the threading has been fixed, think about refactoring the http pipeline to use something similar.
Add JS spec getter/setters and so forth.
More testing around type conversion.
Reflect over properties/methods and expose to runtime.
When using Promise.Race
, it immediately resolves the promise. I imagine this is to resolve the race's own return promise. I'm trying to race a promise created from user code with one created from my code, set to reject after a time interval, in order to provide a timeout for the user code running in the promise, or finish promises that were created but never neither resolved nor rejected, akin to this article.
VSTS now has Linux and macOS build agents in preview. Consider switching over to VSTS. And advantage will be that the Windows test harness can start auto-reporting code coverage again as the enterprise bits required to do so are on VSTS
Will reduce unit test execution time and failures due to intermittent network issues
https://github.com/WireMock-Net/WireMock.Net/wiki/Using-WireMock-in-UnitTests
This is a bit theoretical, but with Blazor, it should be possible to have BaristaCore compile C# code to WASM and then execute the result using the ChakraCore runtime. This would go towards one of the BaristaCore goals of having multiple languages (not just JavaScript) participate in the serverless execution environment that it provides.
Doing so would be an interesting foray to having a completely sandboxed C# runtime/serverless execution environment with BaristaCore (Remember VS for Applications?...)
Given the current goals of Blazor, and it's seeming desire to be a WASM web framework rather than a generic C# WASM generator, it may require stripping out the pertinent bits.
Add the following built-in types to make it easier when creating/providing these types to/from .net
Objects
Functions:
Note these are built-in methods/types intrinsic to the JavaScript engine, not new objects that need to be created, such as Blob.
Implement an IPromiseHandler that ensures execution of promises when using EvaluateModule.
Within the BaristaConversionStrategy, allow classes to specify an explicit conversion To/From JsValue objects by using the IConvertible interface.
Also let custom Converter<TFrom, TTo> classes be specified.
Module loader that loads all modules in a specified type.
Unit tests of the extensions project are a bit flaky as they depend on uptime of httpbin, which is good, but sometimes can throttle or have invalid responses.
Steps:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.