| | | 1 | | using System.Text; |
| | | 2 | | using System.Text.Encodings.Web; |
| | | 3 | | using System.Text.Json; |
| | | 4 | | using System.Text.Json.Serialization; |
| | | 5 | | using System.Text.RegularExpressions; |
| | | 6 | | using System.Text.Unicode; |
| | | 7 | | using Elsa.Extensions; |
| | | 8 | | using Elsa.Expressions.JavaScript.Helpers; |
| | | 9 | | using Elsa.Expressions.JavaScript.Notifications; |
| | | 10 | | using Elsa.Expressions.JavaScript.Options; |
| | | 11 | | using Elsa.Mediator.Contracts; |
| | | 12 | | using JetBrains.Annotations; |
| | | 13 | | using Jint; |
| | | 14 | | using Jint.Native; |
| | | 15 | | using Jint.Runtime.Descriptors; |
| | | 16 | | using Microsoft.Extensions.Options; |
| | | 17 | | |
| | | 18 | | namespace Elsa.Expressions.JavaScript.Handlers; |
| | | 19 | | |
| | | 20 | | /// <summary> |
| | | 21 | | /// A handler that configures the Jint engine with common functions. |
| | | 22 | | /// </summary> |
| | | 23 | | [UsedImplicitly] |
| | 909 | 24 | | public class ConfigureEngineWithCommonFunctions(IOptions<JintOptions> options) : INotificationHandler<EvaluatingJavaScri |
| | | 25 | | { |
| | 909 | 26 | | private readonly JsonSerializerOptions _jsonSerializerOptions = CreateJsonSerializerOptions(); |
| | | 27 | | |
| | | 28 | | /// <inheritdoc /> |
| | | 29 | | public Task HandleAsync(EvaluatingJavaScript notification, CancellationToken cancellationToken) |
| | | 30 | | { |
| | 285 | 31 | | var engine = notification.Engine; |
| | 285 | 32 | | var context = notification.Context; |
| | | 33 | | |
| | | 34 | | // Add common functions. Each one is installed as a global whose value is produced the first time a script |
| | | 35 | | // reads the name, so a fresh engine no longer builds twenty-seven interop function wrappers for an |
| | | 36 | | // expression that calls none of them. The CLR delegate is created inside the factory for the same reason. |
| | 288 | 37 | | AddFunction("getWorkflowDefinitionId", e => JsValue.FromObject(e, (Func<string>)(() => context.GetWorkflowExecut |
| | 288 | 38 | | AddFunction("getWorkflowDefinitionVersionId", e => JsValue.FromObject(e, (Func<string>)(() => context.GetWorkflo |
| | 288 | 39 | | AddFunction("getWorkflowDefinitionVersion", e => JsValue.FromObject(e, (Func<int>)(() => context.GetWorkflowExec |
| | 288 | 40 | | AddFunction("getWorkflowInstanceId", e => JsValue.FromObject(e, (Func<string>)(() => context.GetActivityExecutio |
| | 288 | 41 | | AddFunction("setCorrelationId", e => JsValue.FromObject(e, (Action<string?>)(value => context.GetActivityExecuti |
| | 290 | 42 | | AddFunction("getCorrelationId", e => JsValue.FromObject(e, (Func<string?>)(() => context.GetActivityExecutionCon |
| | 288 | 43 | | AddFunction("setWorkflowInstanceName", e => JsValue.FromObject(e, (Action<string?>)(value => context.GetWorkflow |
| | 290 | 44 | | AddFunction("getWorkflowInstanceName", e => JsValue.FromObject(e, (Func<string?>)(() => context.GetWorkflowExecu |
| | 289 | 45 | | AddFunction("setVariable", e => JsValue.FromObject(e, (Action<string, object>)((name, value) => |
| | 289 | 46 | | { |
| | 2 | 47 | | engine.SyncVariablesContainer(options, name, value); |
| | 2 | 48 | | context.SetVariableInScope(name, value); |
| | 291 | 49 | | }))); |
| | 431 | 50 | | AddFunction("getVariable", e => JsValue.FromObject(e, (Func<string, object?>)(name => context.GetVariableInScope |
| | 287 | 51 | | AddFunction("getInput", e => JsValue.FromObject(e, (Func<string, object?>)(name => context.GetInput(name)))); |
| | 287 | 52 | | AddFunction("getOutputFrom", e => JsValue.FromObject(e, (Func<string, string?, object?>)((activityIdName, output |
| | 287 | 53 | | AddFunction("getLastResult", e => JsValue.FromObject(e, (Func<object?>)(() => context.GetLastResult()))); |
| | 293 | 54 | | AddFunction("isNullOrWhiteSpace", e => JsValue.FromObject(e, (Func<string, bool>)(value => string.IsNullOrWhiteS |
| | 294 | 55 | | AddFunction("isNullOrEmpty", e => JsValue.FromObject(e, (Func<string, bool>)(value => string.IsNullOrEmpty(value |
| | 296 | 56 | | AddFunction("toJson", e => JsValue.FromObject(e, (Func<object, string>)(value => Serialize(value)))); |
| | 289 | 57 | | AddFunction("parseGuid", e => JsValue.FromObject(e, (Func<string, Guid>)(value => Guid.Parse(value)))); |
| | 293 | 58 | | AddFunction("newGuid", e => JsValue.FromObject(e, (Func<Guid>)(() => Guid.NewGuid()))); |
| | 290 | 59 | | AddFunction("newGuidString", e => JsValue.FromObject(e, (Func<string>)(() => Guid.NewGuid().ToString()))); |
| | 290 | 60 | | AddFunction("newShortGuid", e => JsValue.FromObject(e, (Func<string>)(() => Regex.Replace(Convert.ToBase64String |
| | 292 | 61 | | AddFunction("bytesToString", e => JsValue.FromObject(e, (Func<byte[], string>)(value => Encoding.UTF8.GetString( |
| | 290 | 62 | | AddFunction("bytesFromString", e => JsValue.FromObject(e, (Func<string, byte[]>)(value => Encoding.UTF8.GetBytes |
| | 292 | 63 | | AddFunction("bytesToBase64", e => JsValue.FromObject(e, (Func<byte[], string>)(value => Convert.ToBase64String(v |
| | 290 | 64 | | AddFunction("bytesFromBase64", e => JsValue.FromObject(e, (Func<string, byte[]>)(value => Convert.FromBase64Stri |
| | 292 | 65 | | AddFunction("stringToBase64", e => JsValue.FromObject(e, (Func<string, string>)(value => Convert.ToBase64String( |
| | 290 | 66 | | AddFunction("stringFromBase64", e => JsValue.FromObject(e, (Func<string, string>)(value => Encoding.UTF8.GetStri |
| | 287 | 67 | | AddFunction("streamToBytes", e => JsValue.FromObject(e, (Func<Stream, byte[]>)(value => StreamToBytes(value)))); |
| | 286 | 68 | | AddFunction("streamToBase64", e => JsValue.FromObject(e, (Func<Stream, string>)(value => Convert.ToBase64String( |
| | | 69 | | |
| | | 70 | | // Deprecated, use newGuidString instead. |
| | 288 | 71 | | AddFunction("getGuidString", e => JsValue.FromObject(e, (Func<string>)(() => Guid.NewGuid().ToString()))); |
| | | 72 | | |
| | | 73 | | // Deprecated, use newShortGuid instead. |
| | 289 | 74 | | AddFunction("getShortGuid", e => JsValue.FromObject(e, (Func<string>)(() => Regex.Replace(Convert.ToBase64String |
| | 285 | 75 | | return Task.CompletedTask; |
| | | 76 | | |
| | | 77 | | // AddLazyGlobal installs the property itself eagerly and defers only its value, so `in`, `hasOwnProperty` |
| | | 78 | | // and Object.getOwnPropertyNames see every function immediately and the factory runs at most once, on the |
| | | 79 | | // first read. NonEnumerable is the flag engine.SetValue(string, Delegate) applies, so the attributes — and |
| | | 80 | | // therefore what Object.keys(globalThis) reports — are exactly what they were. |
| | | 81 | | void AddFunction(string name, Func<Engine, JsValue> valueFactory) => engine.Advanced.AddLazyGlobal(name, valueFa |
| | | 82 | | } |
| | | 83 | | |
| | | 84 | | private string Serialize(object value) |
| | | 85 | | { |
| | 5 | 86 | | return JsonSerializer.Serialize(value, _jsonSerializerOptions); |
| | | 87 | | } |
| | | 88 | | |
| | | 89 | | private static JsonSerializerOptions CreateJsonSerializerOptions() |
| | | 90 | | { |
| | 909 | 91 | | var options = new JsonSerializerOptions |
| | 909 | 92 | | { |
| | 909 | 93 | | Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) |
| | 909 | 94 | | }; |
| | 909 | 95 | | options.Converters.Add(new JsonStringEnumConverter()); |
| | 909 | 96 | | return options; |
| | | 97 | | } |
| | | 98 | | |
| | | 99 | | private byte[] StreamToBytes(Stream stream) |
| | | 100 | | { |
| | 0 | 101 | | using var memoryStream = new MemoryStream(); |
| | 0 | 102 | | stream.CopyTo(memoryStream); |
| | 0 | 103 | | return memoryStream.ToArray(); |
| | 0 | 104 | | } |
| | | 105 | | } |