feat(core): implement sharedStorage for js runtime

... and also URL, URLSearchParams
This commit is contained in:
genteure 2023-01-14 22:35:05 +08:00
parent 6514bcf9c1
commit 702e33c0cb
5 changed files with 222 additions and 0 deletions

View File

@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="Fluid.Core" Version="2.2.15" />
<PackageReference Include="Flurl" Version="3.0.7" />
<PackageReference Include="Jint" Version="3.0.0-preview-348" />
<PackageReference Include="JsonSubTypes" Version="1.9.0" />
<PackageReference Include="HierarchicalPropertyDefault" Version="0.1.4-beta-g75fdf624b1" />

View File

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Linq;
namespace BililiveRecorder.Core.Scripting.Runtime
{
internal class JintStorage
{
private readonly Dictionary<string, string> storage = new();
public string? GetItem(string key) => this.storage.TryGetValue(key, out var value) ? value : null;
public void SetItem(string key, string value) => this.storage[key] = value;
public void RemoveItem(string key) => this.storage.Remove(key);
public void Clear() => this.storage.Clear();
public string? Key(int index) => this.storage.Count < index ? this.storage.Keys.ElementAt(index) : null;
public int Length => this.storage.Count;
}
}

View File

@ -0,0 +1,98 @@
using Flurl;
namespace BililiveRecorder.Core.Scripting.Runtime
{
internal class JintURL
{
private Url url;
public JintURL(string url) : this(url, null) { }
public JintURL(string url, string? @base)
{
this.url = @base is not null ? new Url(Url.Combine(@base, url)) : new Url(url);
}
public string Hash
{
get => '#' + this.url.Fragment;
set => this.url.Fragment = value.TrimStart('#');
}
public string Host
{
get => this.url.Authority;
set
{
if (value.Contains(":"))
{
var parts = value.Split(':');
this.url.Host = parts[0];
this.url.Port = int.Parse(parts[1]);
}
else
{
this.url.Host = value;
this.url.Port = null;
}
}
}
public string Hostname
{
get => this.url.Host;
set => this.url.Host = value;
}
public string Href
{
get => this.url.ToString();
set => this.url = new Url(value);
}
public string Origin => this.url.Scheme + "://" + this.url.Authority;
public string Password
{
get
{
var parts = this.url.UserInfo.Split(':');
return parts.Length == 1 ? "" : parts[1];
}
set
{
var result = string.IsNullOrEmpty(this.url.UserInfo) ? ":" + value : this.url.UserInfo.Split(':')[0] + ":" + value;
this.url.UserInfo = result == ":" ? "" : result;
}
}
public string Pathname
{
get => this.url.Path;
set => this.url.Path = value;
}
public string Port
{
get => this.url.Port?.ToString() ?? string.Empty;
set => this.url.Port = int.TryParse(value, out var port) ? port : null;
}
public string Protocol
{
get => this.url.Scheme + ':';
set => this.url.Scheme = value.TrimEnd(':');
}
public string Search
{
get => '?' + this.url.Query;
set => this.url.Query = value.TrimStart('?');
}
public JintURLSearchParams SearchParams => new JintURLSearchParams(this.url.QueryParams);
public string ToJSON() => this.url.ToString();
public override string ToString() => this.url.ToString();
}
}

View File

@ -0,0 +1,101 @@
using System.Linq;
using Flurl;
using Jint;
using Jint.Native;
using Jint.Native.Function;
using Jint.Runtime;
namespace BililiveRecorder.Core.Scripting.Runtime
{
internal class JintURLSearchParams
{
private readonly QueryParamCollection query;
public JintURLSearchParams(QueryParamCollection query)
{
this.query = query;
}
public JintURLSearchParams(JsValue jsValue)
{
if (jsValue.IsObject())
{
this.query = new QueryParamCollection();
var obj = jsValue.AsObject();
foreach (var p in obj.GetOwnProperties())
{
this.query.Add(p.Key.ToString(), p.Value.Value.ToString());
}
}
else
{
this.query = new QueryParamCollection(TypeConverter.ToString(jsValue));
}
}
public void Append(string name, string value)
{
this.query.Add(name, value, nullValueHandling: NullValueHandling.NameOnly);
}
public void Delete(string name)
{
this.query.Remove(name);
}
public string[][] Entries()
{
return this.query.Select(x => new string[] { x.Name, x.Value.ToString() }).ToArray();
}
public void ForEach(FunctionInstance callback, JsValue thisArg)
{
var entries = this.Entries();
for (var i = 0; i < entries.Length; i++)
{
var entry = entries[i];
callback.Engine.Invoke(callback, thisArg, entry[1], entry[0], this);
}
}
public string? Get(string name)
{
return this.query.TryGetFirst(name, out var value) ? value.ToString() : null;
}
public string[] GetAll(string name)
{
return this.query.GetAll(name).Select(x => x.ToString()).ToArray();
}
public bool Has(string name)
{
return this.query.Contains(name);
}
public string[] Keys()
{
return this.query.Select(x => x.Name).ToArray();
}
public void Set(string name, string value)
{
this.query.AddOrReplace(name, value, nullValueHandling: NullValueHandling.NameOnly);
}
public void Sort()
{
// do nothing
}
public override string ToString()
{
return this.query.ToString();
}
public string[] Values()
{
return this.query.Select(x => x.Value.ToString()).ToArray();
}
}
}

View File

@ -22,6 +22,7 @@ namespace BililiveRecorder.Core.Scripting
private readonly Options jintOptions;
private static readonly Script setupScript;
private static readonly JintStorage sharedStorage = new();
private string? cachedScriptSource;
private Script? cachedScript;
@ -41,11 +42,15 @@ globalThis.recorderEvents = {};
.CatchClrExceptions()
.LimitRecursion(100)
.RegexTimeoutInterval(TimeSpan.FromSeconds(2))
.AllowClr()
.Configure(engine =>
{
engine.Realm.GlobalObject.FastAddProperty("dns", new JintDns(engine), writable: false, enumerable: false, configurable: false);
engine.Realm.GlobalObject.FastAddProperty("dotnet", new JintDotnet(engine), writable: false, enumerable: false, configurable: false);
engine.Realm.GlobalObject.FastAddProperty("fetchSync", new JintFetchSync(engine), writable: false, enumerable: false, configurable: false);
engine.Realm.GlobalObject.FastAddProperty("URL", TypeReference.CreateTypeReference<JintURL>(engine), writable: false, enumerable: false, configurable: false);
engine.Realm.GlobalObject.FastAddProperty("URLSearchParams", TypeReference.CreateTypeReference<JintURLSearchParams>(engine), writable: false, enumerable: false, configurable: false);
engine.Realm.GlobalObject.FastAddProperty("sharedStorage", new ObjectWrapper(engine, sharedStorage), writable: false, enumerable: false, configurable: false);
});
}