From 49425cab39905f508b163d84fbab1d3877e32dbb Mon Sep 17 00:00:00 2001 From: K35 Date: Wed, 5 Jan 2022 21:16:17 +0000 Subject: [PATCH] Adding C# Support. Badly tested currently, but kindof working --- .gitignore | 7 +- .vscode/launch.json | 19 + examples/CSharp/Example/CSharp_Example.csproj | 14 + examples/CSharp/Example/Program.cs | 94 + examples/{ => Typescript}/test.ts | 4 + examples/example.jrpc | 4 + lib/jrpc.js | 2870 ++++++++++++++++- package.json | 9 +- src/compile.ts | 17 +- src/ir.ts | 13 +- src/parser.ts | 28 +- src/process.ts | 74 +- src/targets/csharp.ts | 328 ++ src/tokenizer.ts | 2 +- templates/CSharp/CSharp.csproj | 9 + templates/CSharp/JRpcClient.cs | 130 + templates/CSharp/JRpcServer.cs | 196 ++ templates/CSharp/JRpcTransport.cs | 14 + templates/ts_service_base.ts | 8 +- yarn.lock | 56 + 20 files changed, 3825 insertions(+), 71 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 examples/CSharp/Example/CSharp_Example.csproj create mode 100644 examples/CSharp/Example/Program.cs rename examples/{ => Typescript}/test.ts (96%) create mode 100644 src/targets/csharp.ts create mode 100644 templates/CSharp/CSharp.csproj create mode 100644 templates/CSharp/JRpcClient.cs create mode 100644 templates/CSharp/JRpcServer.cs create mode 100644 templates/CSharp/JRpcTransport.cs diff --git a/.gitignore b/.gitignore index a430c18..5844ea4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ node_modules/ .yarn/cache .yarn/install-state.gz -examples/out +examples/Typescript/out +examples/CSharp/Generated +examples/CSharp/Example/bin +examples/CSharp/Example/obj examples/definition.json +templates/CSharp/bin +templates/CSharp/obj diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8ee6fc3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + // "preLaunchTask": "build", + "program": "${workspaceFolder}/examples/CSharp_Example/bin/Debug/net6.0/CSharp_Example.dll", + "args": [], + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "stopAtEntry": false + } + ] +} diff --git a/examples/CSharp/Example/CSharp_Example.csproj b/examples/CSharp/Example/CSharp_Example.csproj new file mode 100644 index 0000000..bab5dbb --- /dev/null +++ b/examples/CSharp/Example/CSharp_Example.csproj @@ -0,0 +1,14 @@ + + + + + + + + Exe + net6.0 + enable + enable + + + diff --git a/examples/CSharp/Example/Program.cs b/examples/CSharp/Example/Program.cs new file mode 100644 index 0000000..96940a6 --- /dev/null +++ b/examples/CSharp/Example/Program.cs @@ -0,0 +1,94 @@ +// See https://aka.ms/new-console-template for more information +using Example; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; + +class TestSrvimpl : Example.TestServiceServer { + public TestSrvimpl(): base() {} + public override async Task AddValuesSingleParam(AddValueRequest request, int ctx) { + var res = new Example.AddValueResponse(); + res.value = 1; + return res; + } + + public override async Task AddValuesMultipleParams(double value1,double value2,int ctx) { + return value1 + value2; + } + + public override async Task ReturningVoid(double param1,int ctx) { + + } + + public override void OnEvent(string param1,int ctx) { + Console.WriteLine($"OnEvent {param1}"); + } + + public override async Task> FunctionWithArrayAsParamAndReturn(List values1,List values2, int ctx) { + var l = new List(); + l.Append(1); + return l; + } + + public override async Task ThrowingError(int ctx) { + throw new Exception("This is a remote error :)"); + } + +} + +class CopyTransportS2 : Example.JRpcTransport { + CopyTransportS1 tr1; + public Queue backlog = new Queue(); + + public CopyTransportS2(CopyTransportS1 tr1) { + this.tr1 = tr1; + } + + public override Task Write(string data) { + Console.WriteLine("--> " + data); + this.tr1.DevSendPacket(data); + + return Task.CompletedTask; + } +} + +class CopyTransportS1 : Example.JRpcTransport { + public Queue backlog = new Queue(); + + public CopyTransportS2 tr2; + + public CopyTransportS1() { + this.tr2 = new CopyTransportS2(this); + } + + public override Task Write(string data) { + Console.WriteLine("<-- " + data); + this.tr2.DevSendPacket(data); + return Task.CompletedTask; + } +} + +class Program { + public static void Main() { + Program.Start().Wait(); + } + + public static async Task Start() { + var server = new Example.JRpcServer(); + server.AddService("TestService", new TestSrvimpl()); + var transport = new CopyTransportS1(); + + var sess = server.GetSession(transport, 0); + var client = new Example.JRpcClient(transport.tr2); + var testService = new Example.TestServiceClient(client); + + var result = await testService.AddValuesMultipleParams(1,2); + + Console.WriteLine($"Add 1 + 2 = {result}"); + + await testService.ThrowingError(); + } +} + + + diff --git a/examples/test.ts b/examples/Typescript/test.ts similarity index 96% rename from examples/test.ts rename to examples/Typescript/test.ts index e74bf58..5529293 100644 --- a/examples/test.ts +++ b/examples/Typescript/test.ts @@ -44,6 +44,10 @@ class TestService extends Server.TestService { async FunctionWithArrayAsParamAndReturn(vals1, vals2) { return [...vals1, ...vals2]; } + + async ThrowingError(ctx: undefined): Promise { + throw new Error("Remote error!"); + } } server.addService(new TestService()); diff --git a/examples/example.jrpc b/examples/example.jrpc index 312a20f..723243c 100644 --- a/examples/example.jrpc +++ b/examples/example.jrpc @@ -1,5 +1,7 @@ import "./import"; +define CSharp_Namespace Example; + enum TestEnum { VAL1, VAL2, @@ -44,6 +46,8 @@ service TestService { @Param("param1", "Parameter with some string for event") notification OnEvent(param1: string); + ThrowingError(): void; + FunctionWithArrayAsParamAndReturn(values1: number[], values2: number[]): number[]; } diff --git a/lib/jrpc.js b/lib/jrpc.js index c621cf6..a2cfbc3 100755 --- a/lib/jrpc.js +++ b/lib/jrpc.js @@ -1656,7 +1656,7 @@ var require_route = __commonJS({ } module2.exports = function(fromModel) { const graph = deriveBFS(fromModel); - const conversion2 = {}; + const conversion3 = {}; const models = Object.keys(graph); for (let len = models.length, i = 0; i < len; i++) { const toModel = models[i]; @@ -1664,9 +1664,9 @@ var require_route = __commonJS({ if (node.parent === null) { continue; } - conversion2[toModel] = wrapConversion(toModel, graph); + conversion3[toModel] = wrapConversion(toModel, graph); } - return conversion2; + return conversion3; }; } }); @@ -2198,6 +2198,2577 @@ var require_source = __commonJS({ } }); +// node_modules/universalify/index.js +var require_universalify = __commonJS({ + "node_modules/universalify/index.js"(exports) { + "use strict"; + exports.fromCallback = function(fn) { + return Object.defineProperty(function(...args) { + if (typeof args[args.length - 1] === "function") + fn.apply(this, args); + else { + return new Promise((resolve6, reject) => { + fn.call(this, ...args, (err, res) => err != null ? reject(err) : resolve6(res)); + }); + } + }, "name", { value: fn.name }); + }; + exports.fromPromise = function(fn) { + return Object.defineProperty(function(...args) { + const cb = args[args.length - 1]; + if (typeof cb !== "function") + return fn.apply(this, args); + else + fn.apply(this, args.slice(0, -1)).then((r) => cb(null, r), cb); + }, "name", { value: fn.name }); + }; + } +}); + +// node_modules/graceful-fs/polyfills.js +var require_polyfills = __commonJS({ + "node_modules/graceful-fs/polyfills.js"(exports, module2) { + var constants = require("constants"); + var origCwd = process.cwd; + var cwd = null; + var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform; + process.cwd = function() { + if (!cwd) + cwd = origCwd.call(process); + return cwd; + }; + try { + process.cwd(); + } catch (er) { + } + if (typeof process.chdir === "function") { + chdir = process.chdir; + process.chdir = function(d) { + cwd = null; + chdir.call(process, d); + }; + if (Object.setPrototypeOf) + Object.setPrototypeOf(process.chdir, chdir); + } + var chdir; + module2.exports = patch; + function patch(fs) { + if (constants.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) { + patchLchmod(fs); + } + if (!fs.lutimes) { + patchLutimes(fs); + } + fs.chown = chownFix(fs.chown); + fs.fchown = chownFix(fs.fchown); + fs.lchown = chownFix(fs.lchown); + fs.chmod = chmodFix(fs.chmod); + fs.fchmod = chmodFix(fs.fchmod); + fs.lchmod = chmodFix(fs.lchmod); + fs.chownSync = chownFixSync(fs.chownSync); + fs.fchownSync = chownFixSync(fs.fchownSync); + fs.lchownSync = chownFixSync(fs.lchownSync); + fs.chmodSync = chmodFixSync(fs.chmodSync); + fs.fchmodSync = chmodFixSync(fs.fchmodSync); + fs.lchmodSync = chmodFixSync(fs.lchmodSync); + fs.stat = statFix(fs.stat); + fs.fstat = statFix(fs.fstat); + fs.lstat = statFix(fs.lstat); + fs.statSync = statFixSync(fs.statSync); + fs.fstatSync = statFixSync(fs.fstatSync); + fs.lstatSync = statFixSync(fs.lstatSync); + if (!fs.lchmod) { + fs.lchmod = function(path, mode, cb) { + if (cb) + process.nextTick(cb); + }; + fs.lchmodSync = function() { + }; + } + if (!fs.lchown) { + fs.lchown = function(path, uid, gid, cb) { + if (cb) + process.nextTick(cb); + }; + fs.lchownSync = function() { + }; + } + if (platform === "win32") { + fs.rename = function(fs$rename) { + return function(from, to, cb) { + var start = Date.now(); + var backoff = 0; + fs$rename(from, to, function CB(er) { + if (er && (er.code === "EACCES" || er.code === "EPERM") && Date.now() - start < 6e4) { + setTimeout(function() { + fs.stat(to, function(stater, st) { + if (stater && stater.code === "ENOENT") + fs$rename(from, to, CB); + else + cb(er); + }); + }, backoff); + if (backoff < 100) + backoff += 10; + return; + } + if (cb) + cb(er); + }); + }; + }(fs.rename); + } + fs.read = function(fs$read) { + function read(fd, buffer, offset, length, position, callback_) { + var callback; + if (callback_ && typeof callback_ === "function") { + var eagCounter = 0; + callback = function(er, _, __) { + if (er && er.code === "EAGAIN" && eagCounter < 10) { + eagCounter++; + return fs$read.call(fs, fd, buffer, offset, length, position, callback); + } + callback_.apply(this, arguments); + }; + } + return fs$read.call(fs, fd, buffer, offset, length, position, callback); + } + if (Object.setPrototypeOf) + Object.setPrototypeOf(read, fs$read); + return read; + }(fs.read); + fs.readSync = function(fs$readSync) { + return function(fd, buffer, offset, length, position) { + var eagCounter = 0; + while (true) { + try { + return fs$readSync.call(fs, fd, buffer, offset, length, position); + } catch (er) { + if (er.code === "EAGAIN" && eagCounter < 10) { + eagCounter++; + continue; + } + throw er; + } + } + }; + }(fs.readSync); + function patchLchmod(fs2) { + fs2.lchmod = function(path, mode, callback) { + fs2.open(path, constants.O_WRONLY | constants.O_SYMLINK, mode, function(err, fd) { + if (err) { + if (callback) + callback(err); + return; + } + fs2.fchmod(fd, mode, function(err2) { + fs2.close(fd, function(err22) { + if (callback) + callback(err2 || err22); + }); + }); + }); + }; + fs2.lchmodSync = function(path, mode) { + var fd = fs2.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode); + var threw = true; + var ret; + try { + ret = fs2.fchmodSync(fd, mode); + threw = false; + } finally { + if (threw) { + try { + fs2.closeSync(fd); + } catch (er) { + } + } else { + fs2.closeSync(fd); + } + } + return ret; + }; + } + function patchLutimes(fs2) { + if (constants.hasOwnProperty("O_SYMLINK")) { + fs2.lutimes = function(path, at, mt, cb) { + fs2.open(path, constants.O_SYMLINK, function(er, fd) { + if (er) { + if (cb) + cb(er); + return; + } + fs2.futimes(fd, at, mt, function(er2) { + fs2.close(fd, function(er22) { + if (cb) + cb(er2 || er22); + }); + }); + }); + }; + fs2.lutimesSync = function(path, at, mt) { + var fd = fs2.openSync(path, constants.O_SYMLINK); + var ret; + var threw = true; + try { + ret = fs2.futimesSync(fd, at, mt); + threw = false; + } finally { + if (threw) { + try { + fs2.closeSync(fd); + } catch (er) { + } + } else { + fs2.closeSync(fd); + } + } + return ret; + }; + } else { + fs2.lutimes = function(_a, _b, _c, cb) { + if (cb) + process.nextTick(cb); + }; + fs2.lutimesSync = function() { + }; + } + } + function chmodFix(orig) { + if (!orig) + return orig; + return function(target, mode, cb) { + return orig.call(fs, target, mode, function(er) { + if (chownErOk(er)) + er = null; + if (cb) + cb.apply(this, arguments); + }); + }; + } + function chmodFixSync(orig) { + if (!orig) + return orig; + return function(target, mode) { + try { + return orig.call(fs, target, mode); + } catch (er) { + if (!chownErOk(er)) + throw er; + } + }; + } + function chownFix(orig) { + if (!orig) + return orig; + return function(target, uid, gid, cb) { + return orig.call(fs, target, uid, gid, function(er) { + if (chownErOk(er)) + er = null; + if (cb) + cb.apply(this, arguments); + }); + }; + } + function chownFixSync(orig) { + if (!orig) + return orig; + return function(target, uid, gid) { + try { + return orig.call(fs, target, uid, gid); + } catch (er) { + if (!chownErOk(er)) + throw er; + } + }; + } + function statFix(orig) { + if (!orig) + return orig; + return function(target, options, cb) { + if (typeof options === "function") { + cb = options; + options = null; + } + function callback(er, stats) { + if (stats) { + if (stats.uid < 0) + stats.uid += 4294967296; + if (stats.gid < 0) + stats.gid += 4294967296; + } + if (cb) + cb.apply(this, arguments); + } + return options ? orig.call(fs, target, options, callback) : orig.call(fs, target, callback); + }; + } + function statFixSync(orig) { + if (!orig) + return orig; + return function(target, options) { + var stats = options ? orig.call(fs, target, options) : orig.call(fs, target); + if (stats) { + if (stats.uid < 0) + stats.uid += 4294967296; + if (stats.gid < 0) + stats.gid += 4294967296; + } + return stats; + }; + } + function chownErOk(er) { + if (!er) + return true; + if (er.code === "ENOSYS") + return true; + var nonroot = !process.getuid || process.getuid() !== 0; + if (nonroot) { + if (er.code === "EINVAL" || er.code === "EPERM") + return true; + } + return false; + } + } + } +}); + +// node_modules/graceful-fs/legacy-streams.js +var require_legacy_streams = __commonJS({ + "node_modules/graceful-fs/legacy-streams.js"(exports, module2) { + var Stream = require("stream").Stream; + module2.exports = legacy; + function legacy(fs) { + return { + ReadStream, + WriteStream + }; + function ReadStream(path, options) { + if (!(this instanceof ReadStream)) + return new ReadStream(path, options); + Stream.call(this); + var self = this; + this.path = path; + this.fd = null; + this.readable = true; + this.paused = false; + this.flags = "r"; + this.mode = 438; + this.bufferSize = 64 * 1024; + options = options || {}; + var keys = Object.keys(options); + for (var index = 0, length = keys.length; index < length; index++) { + var key = keys[index]; + this[key] = options[key]; + } + if (this.encoding) + this.setEncoding(this.encoding); + if (this.start !== void 0) { + if (typeof this.start !== "number") { + throw TypeError("start must be a Number"); + } + if (this.end === void 0) { + this.end = Infinity; + } else if (typeof this.end !== "number") { + throw TypeError("end must be a Number"); + } + if (this.start > this.end) { + throw new Error("start must be <= end"); + } + this.pos = this.start; + } + if (this.fd !== null) { + process.nextTick(function() { + self._read(); + }); + return; + } + fs.open(this.path, this.flags, this.mode, function(err, fd) { + if (err) { + self.emit("error", err); + self.readable = false; + return; + } + self.fd = fd; + self.emit("open", fd); + self._read(); + }); + } + function WriteStream(path, options) { + if (!(this instanceof WriteStream)) + return new WriteStream(path, options); + Stream.call(this); + this.path = path; + this.fd = null; + this.writable = true; + this.flags = "w"; + this.encoding = "binary"; + this.mode = 438; + this.bytesWritten = 0; + options = options || {}; + var keys = Object.keys(options); + for (var index = 0, length = keys.length; index < length; index++) { + var key = keys[index]; + this[key] = options[key]; + } + if (this.start !== void 0) { + if (typeof this.start !== "number") { + throw TypeError("start must be a Number"); + } + if (this.start < 0) { + throw new Error("start must be >= zero"); + } + this.pos = this.start; + } + this.busy = false; + this._queue = []; + if (this.fd === null) { + this._open = fs.open; + this._queue.push([this._open, this.path, this.flags, this.mode, void 0]); + this.flush(); + } + } + } + } +}); + +// node_modules/graceful-fs/clone.js +var require_clone = __commonJS({ + "node_modules/graceful-fs/clone.js"(exports, module2) { + "use strict"; + module2.exports = clone; + var getPrototypeOf = Object.getPrototypeOf || function(obj) { + return obj.__proto__; + }; + function clone(obj) { + if (obj === null || typeof obj !== "object") + return obj; + if (obj instanceof Object) + var copy = { __proto__: getPrototypeOf(obj) }; + else + var copy = /* @__PURE__ */ Object.create(null); + Object.getOwnPropertyNames(obj).forEach(function(key) { + Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key)); + }); + return copy; + } + } +}); + +// node_modules/graceful-fs/graceful-fs.js +var require_graceful_fs = __commonJS({ + "node_modules/graceful-fs/graceful-fs.js"(exports, module2) { + var fs = require("fs"); + var polyfills = require_polyfills(); + var legacy = require_legacy_streams(); + var clone = require_clone(); + var util = require("util"); + var gracefulQueue; + var previousSymbol; + if (typeof Symbol === "function" && typeof Symbol.for === "function") { + gracefulQueue = Symbol.for("graceful-fs.queue"); + previousSymbol = Symbol.for("graceful-fs.previous"); + } else { + gracefulQueue = "___graceful-fs.queue"; + previousSymbol = "___graceful-fs.previous"; + } + function noop() { + } + function publishQueue(context, queue2) { + Object.defineProperty(context, gracefulQueue, { + get: function() { + return queue2; + } + }); + } + var debug = noop; + if (util.debuglog) + debug = util.debuglog("gfs4"); + else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) + debug = function() { + var m = util.format.apply(util, arguments); + m = "GFS4: " + m.split(/\n/).join("\nGFS4: "); + console.error(m); + }; + if (!fs[gracefulQueue]) { + queue = global[gracefulQueue] || []; + publishQueue(fs, queue); + fs.close = function(fs$close) { + function close(fd, cb) { + return fs$close.call(fs, fd, function(err) { + if (!err) { + resetQueue(); + } + if (typeof cb === "function") + cb.apply(this, arguments); + }); + } + Object.defineProperty(close, previousSymbol, { + value: fs$close + }); + return close; + }(fs.close); + fs.closeSync = function(fs$closeSync) { + function closeSync(fd) { + fs$closeSync.apply(fs, arguments); + resetQueue(); + } + Object.defineProperty(closeSync, previousSymbol, { + value: fs$closeSync + }); + return closeSync; + }(fs.closeSync); + if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) { + process.on("exit", function() { + debug(fs[gracefulQueue]); + require("assert").equal(fs[gracefulQueue].length, 0); + }); + } + } + var queue; + if (!global[gracefulQueue]) { + publishQueue(global, fs[gracefulQueue]); + } + module2.exports = patch(clone(fs)); + if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) { + module2.exports = patch(fs); + fs.__patched = true; + } + function patch(fs2) { + polyfills(fs2); + fs2.gracefulify = patch; + fs2.createReadStream = createReadStream; + fs2.createWriteStream = createWriteStream; + var fs$readFile = fs2.readFile; + fs2.readFile = readFile; + function readFile(path, options, cb) { + if (typeof options === "function") + cb = options, options = null; + return go$readFile(path, options, cb); + function go$readFile(path2, options2, cb2, startTime) { + return fs$readFile(path2, options2, function(err) { + if (err && (err.code === "EMFILE" || err.code === "ENFILE")) + enqueue([go$readFile, [path2, options2, cb2], err, startTime || Date.now(), Date.now()]); + else { + if (typeof cb2 === "function") + cb2.apply(this, arguments); + } + }); + } + } + var fs$writeFile = fs2.writeFile; + fs2.writeFile = writeFile2; + function writeFile2(path, data, options, cb) { + if (typeof options === "function") + cb = options, options = null; + return go$writeFile(path, data, options, cb); + function go$writeFile(path2, data2, options2, cb2, startTime) { + return fs$writeFile(path2, data2, options2, function(err) { + if (err && (err.code === "EMFILE" || err.code === "ENFILE")) + enqueue([go$writeFile, [path2, data2, options2, cb2], err, startTime || Date.now(), Date.now()]); + else { + if (typeof cb2 === "function") + cb2.apply(this, arguments); + } + }); + } + } + var fs$appendFile = fs2.appendFile; + if (fs$appendFile) + fs2.appendFile = appendFile; + function appendFile(path, data, options, cb) { + if (typeof options === "function") + cb = options, options = null; + return go$appendFile(path, data, options, cb); + function go$appendFile(path2, data2, options2, cb2, startTime) { + return fs$appendFile(path2, data2, options2, function(err) { + if (err && (err.code === "EMFILE" || err.code === "ENFILE")) + enqueue([go$appendFile, [path2, data2, options2, cb2], err, startTime || Date.now(), Date.now()]); + else { + if (typeof cb2 === "function") + cb2.apply(this, arguments); + } + }); + } + } + var fs$copyFile = fs2.copyFile; + if (fs$copyFile) + fs2.copyFile = copyFile; + function copyFile(src, dest, flags, cb) { + if (typeof flags === "function") { + cb = flags; + flags = 0; + } + return go$copyFile(src, dest, flags, cb); + function go$copyFile(src2, dest2, flags2, cb2, startTime) { + return fs$copyFile(src2, dest2, flags2, function(err) { + if (err && (err.code === "EMFILE" || err.code === "ENFILE")) + enqueue([go$copyFile, [src2, dest2, flags2, cb2], err, startTime || Date.now(), Date.now()]); + else { + if (typeof cb2 === "function") + cb2.apply(this, arguments); + } + }); + } + } + var fs$readdir = fs2.readdir; + fs2.readdir = readdir; + function readdir(path, options, cb) { + if (typeof options === "function") + cb = options, options = null; + return go$readdir(path, options, cb); + function go$readdir(path2, options2, cb2, startTime) { + return fs$readdir(path2, options2, function(err, files) { + if (err && (err.code === "EMFILE" || err.code === "ENFILE")) + enqueue([go$readdir, [path2, options2, cb2], err, startTime || Date.now(), Date.now()]); + else { + if (files && files.sort) + files.sort(); + if (typeof cb2 === "function") + cb2.call(this, err, files); + } + }); + } + } + if (process.version.substr(0, 4) === "v0.8") { + var legStreams = legacy(fs2); + ReadStream = legStreams.ReadStream; + WriteStream = legStreams.WriteStream; + } + var fs$ReadStream = fs2.ReadStream; + if (fs$ReadStream) { + ReadStream.prototype = Object.create(fs$ReadStream.prototype); + ReadStream.prototype.open = ReadStream$open; + } + var fs$WriteStream = fs2.WriteStream; + if (fs$WriteStream) { + WriteStream.prototype = Object.create(fs$WriteStream.prototype); + WriteStream.prototype.open = WriteStream$open; + } + Object.defineProperty(fs2, "ReadStream", { + get: function() { + return ReadStream; + }, + set: function(val) { + ReadStream = val; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(fs2, "WriteStream", { + get: function() { + return WriteStream; + }, + set: function(val) { + WriteStream = val; + }, + enumerable: true, + configurable: true + }); + var FileReadStream = ReadStream; + Object.defineProperty(fs2, "FileReadStream", { + get: function() { + return FileReadStream; + }, + set: function(val) { + FileReadStream = val; + }, + enumerable: true, + configurable: true + }); + var FileWriteStream = WriteStream; + Object.defineProperty(fs2, "FileWriteStream", { + get: function() { + return FileWriteStream; + }, + set: function(val) { + FileWriteStream = val; + }, + enumerable: true, + configurable: true + }); + function ReadStream(path, options) { + if (this instanceof ReadStream) + return fs$ReadStream.apply(this, arguments), this; + else + return ReadStream.apply(Object.create(ReadStream.prototype), arguments); + } + function ReadStream$open() { + var that = this; + open(that.path, that.flags, that.mode, function(err, fd) { + if (err) { + if (that.autoClose) + that.destroy(); + that.emit("error", err); + } else { + that.fd = fd; + that.emit("open", fd); + that.read(); + } + }); + } + function WriteStream(path, options) { + if (this instanceof WriteStream) + return fs$WriteStream.apply(this, arguments), this; + else + return WriteStream.apply(Object.create(WriteStream.prototype), arguments); + } + function WriteStream$open() { + var that = this; + open(that.path, that.flags, that.mode, function(err, fd) { + if (err) { + that.destroy(); + that.emit("error", err); + } else { + that.fd = fd; + that.emit("open", fd); + } + }); + } + function createReadStream(path, options) { + return new fs2.ReadStream(path, options); + } + function createWriteStream(path, options) { + return new fs2.WriteStream(path, options); + } + var fs$open = fs2.open; + fs2.open = open; + function open(path, flags, mode, cb) { + if (typeof mode === "function") + cb = mode, mode = null; + return go$open(path, flags, mode, cb); + function go$open(path2, flags2, mode2, cb2, startTime) { + return fs$open(path2, flags2, mode2, function(err, fd) { + if (err && (err.code === "EMFILE" || err.code === "ENFILE")) + enqueue([go$open, [path2, flags2, mode2, cb2], err, startTime || Date.now(), Date.now()]); + else { + if (typeof cb2 === "function") + cb2.apply(this, arguments); + } + }); + } + } + return fs2; + } + function enqueue(elem) { + debug("ENQUEUE", elem[0].name, elem[1]); + fs[gracefulQueue].push(elem); + retry(); + } + var retryTimer; + function resetQueue() { + var now = Date.now(); + for (var i = 0; i < fs[gracefulQueue].length; ++i) { + if (fs[gracefulQueue][i].length > 2) { + fs[gracefulQueue][i][3] = now; + fs[gracefulQueue][i][4] = now; + } + } + retry(); + } + function retry() { + clearTimeout(retryTimer); + retryTimer = void 0; + if (fs[gracefulQueue].length === 0) + return; + var elem = fs[gracefulQueue].shift(); + var fn = elem[0]; + var args = elem[1]; + var err = elem[2]; + var startTime = elem[3]; + var lastTime = elem[4]; + if (startTime === void 0) { + debug("RETRY", fn.name, args); + fn.apply(null, args); + } else if (Date.now() - startTime >= 6e4) { + debug("TIMEOUT", fn.name, args); + var cb = args.pop(); + if (typeof cb === "function") + cb.call(null, err); + } else { + var sinceAttempt = Date.now() - lastTime; + var sinceStart = Math.max(lastTime - startTime, 1); + var desiredDelay = Math.min(sinceStart * 1.2, 100); + if (sinceAttempt >= desiredDelay) { + debug("RETRY", fn.name, args); + fn.apply(null, args.concat([startTime])); + } else { + fs[gracefulQueue].push(elem); + } + } + if (retryTimer === void 0) { + retryTimer = setTimeout(retry, 0); + } + } + } +}); + +// node_modules/fs-extra/lib/fs/index.js +var require_fs = __commonJS({ + "node_modules/fs-extra/lib/fs/index.js"(exports) { + "use strict"; + var u = require_universalify().fromCallback; + var fs = require_graceful_fs(); + var api = [ + "access", + "appendFile", + "chmod", + "chown", + "close", + "copyFile", + "fchmod", + "fchown", + "fdatasync", + "fstat", + "fsync", + "ftruncate", + "futimes", + "lchmod", + "lchown", + "link", + "lstat", + "mkdir", + "mkdtemp", + "open", + "opendir", + "readdir", + "readFile", + "readlink", + "realpath", + "rename", + "rm", + "rmdir", + "stat", + "symlink", + "truncate", + "unlink", + "utimes", + "writeFile" + ].filter((key) => { + return typeof fs[key] === "function"; + }); + Object.assign(exports, fs); + api.forEach((method) => { + exports[method] = u(fs[method]); + }); + exports.realpath.native = u(fs.realpath.native); + exports.exists = function(filename, callback) { + if (typeof callback === "function") { + return fs.exists(filename, callback); + } + return new Promise((resolve6) => { + return fs.exists(filename, resolve6); + }); + }; + exports.read = function(fd, buffer, offset, length, position, callback) { + if (typeof callback === "function") { + return fs.read(fd, buffer, offset, length, position, callback); + } + return new Promise((resolve6, reject) => { + fs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer2) => { + if (err) + return reject(err); + resolve6({ bytesRead, buffer: buffer2 }); + }); + }); + }; + exports.write = function(fd, buffer, ...args) { + if (typeof args[args.length - 1] === "function") { + return fs.write(fd, buffer, ...args); + } + return new Promise((resolve6, reject) => { + fs.write(fd, buffer, ...args, (err, bytesWritten, buffer2) => { + if (err) + return reject(err); + resolve6({ bytesWritten, buffer: buffer2 }); + }); + }); + }; + if (typeof fs.writev === "function") { + exports.writev = function(fd, buffers, ...args) { + if (typeof args[args.length - 1] === "function") { + return fs.writev(fd, buffers, ...args); + } + return new Promise((resolve6, reject) => { + fs.writev(fd, buffers, ...args, (err, bytesWritten, buffers2) => { + if (err) + return reject(err); + resolve6({ bytesWritten, buffers: buffers2 }); + }); + }); + }; + } + } +}); + +// node_modules/fs-extra/lib/mkdirs/utils.js +var require_utils = __commonJS({ + "node_modules/fs-extra/lib/mkdirs/utils.js"(exports, module2) { + "use strict"; + var path = require("path"); + module2.exports.checkPath = function checkPath(pth) { + if (process.platform === "win32") { + const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, "")); + if (pathHasInvalidWinCharacters) { + const error = new Error(`Path contains invalid characters: ${pth}`); + error.code = "EINVAL"; + throw error; + } + } + }; + } +}); + +// node_modules/fs-extra/lib/mkdirs/make-dir.js +var require_make_dir = __commonJS({ + "node_modules/fs-extra/lib/mkdirs/make-dir.js"(exports, module2) { + "use strict"; + var fs = require_fs(); + var { checkPath } = require_utils(); + var getMode = (options) => { + const defaults = { mode: 511 }; + if (typeof options === "number") + return options; + return __spreadValues(__spreadValues({}, defaults), options).mode; + }; + module2.exports.makeDir = async (dir, options) => { + checkPath(dir); + return fs.mkdir(dir, { + mode: getMode(options), + recursive: true + }); + }; + module2.exports.makeDirSync = (dir, options) => { + checkPath(dir); + return fs.mkdirSync(dir, { + mode: getMode(options), + recursive: true + }); + }; + } +}); + +// node_modules/fs-extra/lib/mkdirs/index.js +var require_mkdirs = __commonJS({ + "node_modules/fs-extra/lib/mkdirs/index.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromPromise; + var { makeDir: _makeDir, makeDirSync } = require_make_dir(); + var makeDir = u(_makeDir); + module2.exports = { + mkdirs: makeDir, + mkdirsSync: makeDirSync, + mkdirp: makeDir, + mkdirpSync: makeDirSync, + ensureDir: makeDir, + ensureDirSync: makeDirSync + }; + } +}); + +// node_modules/fs-extra/lib/util/utimes.js +var require_utimes = __commonJS({ + "node_modules/fs-extra/lib/util/utimes.js"(exports, module2) { + "use strict"; + var fs = require_graceful_fs(); + function utimesMillis(path, atime, mtime, callback) { + fs.open(path, "r+", (err, fd) => { + if (err) + return callback(err); + fs.futimes(fd, atime, mtime, (futimesErr) => { + fs.close(fd, (closeErr) => { + if (callback) + callback(futimesErr || closeErr); + }); + }); + }); + } + function utimesMillisSync(path, atime, mtime) { + const fd = fs.openSync(path, "r+"); + fs.futimesSync(fd, atime, mtime); + return fs.closeSync(fd); + } + module2.exports = { + utimesMillis, + utimesMillisSync + }; + } +}); + +// node_modules/fs-extra/lib/util/stat.js +var require_stat = __commonJS({ + "node_modules/fs-extra/lib/util/stat.js"(exports, module2) { + "use strict"; + var fs = require_fs(); + var path = require("path"); + var util = require("util"); + function getStats(src, dest, opts) { + const statFunc = opts.dereference ? (file) => fs.stat(file, { bigint: true }) : (file) => fs.lstat(file, { bigint: true }); + return Promise.all([ + statFunc(src), + statFunc(dest).catch((err) => { + if (err.code === "ENOENT") + return null; + throw err; + }) + ]).then(([srcStat, destStat]) => ({ srcStat, destStat })); + } + function getStatsSync(src, dest, opts) { + let destStat; + const statFunc = opts.dereference ? (file) => fs.statSync(file, { bigint: true }) : (file) => fs.lstatSync(file, { bigint: true }); + const srcStat = statFunc(src); + try { + destStat = statFunc(dest); + } catch (err) { + if (err.code === "ENOENT") + return { srcStat, destStat: null }; + throw err; + } + return { srcStat, destStat }; + } + function checkPaths(src, dest, funcName, opts, cb) { + util.callbackify(getStats)(src, dest, opts, (err, stats) => { + if (err) + return cb(err); + const { srcStat, destStat } = stats; + if (destStat) { + if (areIdentical(srcStat, destStat)) { + const srcBaseName = path.basename(src); + const destBaseName = path.basename(dest); + if (funcName === "move" && srcBaseName !== destBaseName && srcBaseName.toLowerCase() === destBaseName.toLowerCase()) { + return cb(null, { srcStat, destStat, isChangingCase: true }); + } + return cb(new Error("Source and destination must not be the same.")); + } + if (srcStat.isDirectory() && !destStat.isDirectory()) { + return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)); + } + if (!srcStat.isDirectory() && destStat.isDirectory()) { + return cb(new Error(`Cannot overwrite directory '${dest}' with non-directory '${src}'.`)); + } + } + if (srcStat.isDirectory() && isSrcSubdir(src, dest)) { + return cb(new Error(errMsg(src, dest, funcName))); + } + return cb(null, { srcStat, destStat }); + }); + } + function checkPathsSync(src, dest, funcName, opts) { + const { srcStat, destStat } = getStatsSync(src, dest, opts); + if (destStat) { + if (areIdentical(srcStat, destStat)) { + const srcBaseName = path.basename(src); + const destBaseName = path.basename(dest); + if (funcName === "move" && srcBaseName !== destBaseName && srcBaseName.toLowerCase() === destBaseName.toLowerCase()) { + return { srcStat, destStat, isChangingCase: true }; + } + throw new Error("Source and destination must not be the same."); + } + if (srcStat.isDirectory() && !destStat.isDirectory()) { + throw new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`); + } + if (!srcStat.isDirectory() && destStat.isDirectory()) { + throw new Error(`Cannot overwrite directory '${dest}' with non-directory '${src}'.`); + } + } + if (srcStat.isDirectory() && isSrcSubdir(src, dest)) { + throw new Error(errMsg(src, dest, funcName)); + } + return { srcStat, destStat }; + } + function checkParentPaths(src, srcStat, dest, funcName, cb) { + const srcParent = path.resolve(path.dirname(src)); + const destParent = path.resolve(path.dirname(dest)); + if (destParent === srcParent || destParent === path.parse(destParent).root) + return cb(); + fs.stat(destParent, { bigint: true }, (err, destStat) => { + if (err) { + if (err.code === "ENOENT") + return cb(); + return cb(err); + } + if (areIdentical(srcStat, destStat)) { + return cb(new Error(errMsg(src, dest, funcName))); + } + return checkParentPaths(src, srcStat, destParent, funcName, cb); + }); + } + function checkParentPathsSync(src, srcStat, dest, funcName) { + const srcParent = path.resolve(path.dirname(src)); + const destParent = path.resolve(path.dirname(dest)); + if (destParent === srcParent || destParent === path.parse(destParent).root) + return; + let destStat; + try { + destStat = fs.statSync(destParent, { bigint: true }); + } catch (err) { + if (err.code === "ENOENT") + return; + throw err; + } + if (areIdentical(srcStat, destStat)) { + throw new Error(errMsg(src, dest, funcName)); + } + return checkParentPathsSync(src, srcStat, destParent, funcName); + } + function areIdentical(srcStat, destStat) { + return destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev; + } + function isSrcSubdir(src, dest) { + const srcArr = path.resolve(src).split(path.sep).filter((i) => i); + const destArr = path.resolve(dest).split(path.sep).filter((i) => i); + return srcArr.reduce((acc, cur, i) => acc && destArr[i] === cur, true); + } + function errMsg(src, dest, funcName) { + return `Cannot ${funcName} '${src}' to a subdirectory of itself, '${dest}'.`; + } + module2.exports = { + checkPaths, + checkPathsSync, + checkParentPaths, + checkParentPathsSync, + isSrcSubdir, + areIdentical + }; + } +}); + +// node_modules/fs-extra/lib/copy-sync/copy-sync.js +var require_copy_sync = __commonJS({ + "node_modules/fs-extra/lib/copy-sync/copy-sync.js"(exports, module2) { + "use strict"; + var fs = require_graceful_fs(); + var path = require("path"); + var mkdirsSync = require_mkdirs().mkdirsSync; + var utimesMillisSync = require_utimes().utimesMillisSync; + var stat = require_stat(); + function copySync2(src, dest, opts) { + if (typeof opts === "function") { + opts = { filter: opts }; + } + opts = opts || {}; + opts.clobber = "clobber" in opts ? !!opts.clobber : true; + opts.overwrite = "overwrite" in opts ? !!opts.overwrite : opts.clobber; + if (opts.preserveTimestamps && process.arch === "ia32") { + console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended; + + see https://github.com/jprichardson/node-fs-extra/issues/269`); + } + const { srcStat, destStat } = stat.checkPathsSync(src, dest, "copy", opts); + stat.checkParentPathsSync(src, srcStat, dest, "copy"); + return handleFilterAndCopy(destStat, src, dest, opts); + } + function handleFilterAndCopy(destStat, src, dest, opts) { + if (opts.filter && !opts.filter(src, dest)) + return; + const destParent = path.dirname(dest); + if (!fs.existsSync(destParent)) + mkdirsSync(destParent); + return getStats(destStat, src, dest, opts); + } + function startCopy(destStat, src, dest, opts) { + if (opts.filter && !opts.filter(src, dest)) + return; + return getStats(destStat, src, dest, opts); + } + function getStats(destStat, src, dest, opts) { + const statSync3 = opts.dereference ? fs.statSync : fs.lstatSync; + const srcStat = statSync3(src); + if (srcStat.isDirectory()) + return onDir(srcStat, destStat, src, dest, opts); + else if (srcStat.isFile() || srcStat.isCharacterDevice() || srcStat.isBlockDevice()) + return onFile(srcStat, destStat, src, dest, opts); + else if (srcStat.isSymbolicLink()) + return onLink(destStat, src, dest, opts); + else if (srcStat.isSocket()) + throw new Error(`Cannot copy a socket file: ${src}`); + else if (srcStat.isFIFO()) + throw new Error(`Cannot copy a FIFO pipe: ${src}`); + throw new Error(`Unknown file: ${src}`); + } + function onFile(srcStat, destStat, src, dest, opts) { + if (!destStat) + return copyFile(srcStat, src, dest, opts); + return mayCopyFile(srcStat, src, dest, opts); + } + function mayCopyFile(srcStat, src, dest, opts) { + if (opts.overwrite) { + fs.unlinkSync(dest); + return copyFile(srcStat, src, dest, opts); + } else if (opts.errorOnExist) { + throw new Error(`'${dest}' already exists`); + } + } + function copyFile(srcStat, src, dest, opts) { + fs.copyFileSync(src, dest); + if (opts.preserveTimestamps) + handleTimestamps(srcStat.mode, src, dest); + return setDestMode(dest, srcStat.mode); + } + function handleTimestamps(srcMode, src, dest) { + if (fileIsNotWritable(srcMode)) + makeFileWritable(dest, srcMode); + return setDestTimestamps(src, dest); + } + function fileIsNotWritable(srcMode) { + return (srcMode & 128) === 0; + } + function makeFileWritable(dest, srcMode) { + return setDestMode(dest, srcMode | 128); + } + function setDestMode(dest, srcMode) { + return fs.chmodSync(dest, srcMode); + } + function setDestTimestamps(src, dest) { + const updatedSrcStat = fs.statSync(src); + return utimesMillisSync(dest, updatedSrcStat.atime, updatedSrcStat.mtime); + } + function onDir(srcStat, destStat, src, dest, opts) { + if (!destStat) + return mkDirAndCopy(srcStat.mode, src, dest, opts); + return copyDir(src, dest, opts); + } + function mkDirAndCopy(srcMode, src, dest, opts) { + fs.mkdirSync(dest); + copyDir(src, dest, opts); + return setDestMode(dest, srcMode); + } + function copyDir(src, dest, opts) { + fs.readdirSync(src).forEach((item) => copyDirItem(item, src, dest, opts)); + } + function copyDirItem(item, src, dest, opts) { + const srcItem = path.join(src, item); + const destItem = path.join(dest, item); + const { destStat } = stat.checkPathsSync(srcItem, destItem, "copy", opts); + return startCopy(destStat, srcItem, destItem, opts); + } + function onLink(destStat, src, dest, opts) { + let resolvedSrc = fs.readlinkSync(src); + if (opts.dereference) { + resolvedSrc = path.resolve(process.cwd(), resolvedSrc); + } + if (!destStat) { + return fs.symlinkSync(resolvedSrc, dest); + } else { + let resolvedDest; + try { + resolvedDest = fs.readlinkSync(dest); + } catch (err) { + if (err.code === "EINVAL" || err.code === "UNKNOWN") + return fs.symlinkSync(resolvedSrc, dest); + throw err; + } + if (opts.dereference) { + resolvedDest = path.resolve(process.cwd(), resolvedDest); + } + if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) { + throw new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`); + } + if (fs.statSync(dest).isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) { + throw new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`); + } + return copyLink(resolvedSrc, dest); + } + } + function copyLink(resolvedSrc, dest) { + fs.unlinkSync(dest); + return fs.symlinkSync(resolvedSrc, dest); + } + module2.exports = copySync2; + } +}); + +// node_modules/fs-extra/lib/copy-sync/index.js +var require_copy_sync2 = __commonJS({ + "node_modules/fs-extra/lib/copy-sync/index.js"(exports, module2) { + "use strict"; + module2.exports = { + copySync: require_copy_sync() + }; + } +}); + +// node_modules/fs-extra/lib/path-exists/index.js +var require_path_exists = __commonJS({ + "node_modules/fs-extra/lib/path-exists/index.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromPromise; + var fs = require_fs(); + function pathExists(path) { + return fs.access(path).then(() => true).catch(() => false); + } + module2.exports = { + pathExists: u(pathExists), + pathExistsSync: fs.existsSync + }; + } +}); + +// node_modules/fs-extra/lib/copy/copy.js +var require_copy = __commonJS({ + "node_modules/fs-extra/lib/copy/copy.js"(exports, module2) { + "use strict"; + var fs = require_graceful_fs(); + var path = require("path"); + var mkdirs = require_mkdirs().mkdirs; + var pathExists = require_path_exists().pathExists; + var utimesMillis = require_utimes().utimesMillis; + var stat = require_stat(); + function copy(src, dest, opts, cb) { + if (typeof opts === "function" && !cb) { + cb = opts; + opts = {}; + } else if (typeof opts === "function") { + opts = { filter: opts }; + } + cb = cb || function() { + }; + opts = opts || {}; + opts.clobber = "clobber" in opts ? !!opts.clobber : true; + opts.overwrite = "overwrite" in opts ? !!opts.overwrite : opts.clobber; + if (opts.preserveTimestamps && process.arch === "ia32") { + console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended; + + see https://github.com/jprichardson/node-fs-extra/issues/269`); + } + stat.checkPaths(src, dest, "copy", opts, (err, stats) => { + if (err) + return cb(err); + const { srcStat, destStat } = stats; + stat.checkParentPaths(src, srcStat, dest, "copy", (err2) => { + if (err2) + return cb(err2); + if (opts.filter) + return handleFilter(checkParentDir, destStat, src, dest, opts, cb); + return checkParentDir(destStat, src, dest, opts, cb); + }); + }); + } + function checkParentDir(destStat, src, dest, opts, cb) { + const destParent = path.dirname(dest); + pathExists(destParent, (err, dirExists) => { + if (err) + return cb(err); + if (dirExists) + return getStats(destStat, src, dest, opts, cb); + mkdirs(destParent, (err2) => { + if (err2) + return cb(err2); + return getStats(destStat, src, dest, opts, cb); + }); + }); + } + function handleFilter(onInclude, destStat, src, dest, opts, cb) { + Promise.resolve(opts.filter(src, dest)).then((include) => { + if (include) + return onInclude(destStat, src, dest, opts, cb); + return cb(); + }, (error) => cb(error)); + } + function startCopy(destStat, src, dest, opts, cb) { + if (opts.filter) + return handleFilter(getStats, destStat, src, dest, opts, cb); + return getStats(destStat, src, dest, opts, cb); + } + function getStats(destStat, src, dest, opts, cb) { + const stat2 = opts.dereference ? fs.stat : fs.lstat; + stat2(src, (err, srcStat) => { + if (err) + return cb(err); + if (srcStat.isDirectory()) + return onDir(srcStat, destStat, src, dest, opts, cb); + else if (srcStat.isFile() || srcStat.isCharacterDevice() || srcStat.isBlockDevice()) + return onFile(srcStat, destStat, src, dest, opts, cb); + else if (srcStat.isSymbolicLink()) + return onLink(destStat, src, dest, opts, cb); + else if (srcStat.isSocket()) + return cb(new Error(`Cannot copy a socket file: ${src}`)); + else if (srcStat.isFIFO()) + return cb(new Error(`Cannot copy a FIFO pipe: ${src}`)); + return cb(new Error(`Unknown file: ${src}`)); + }); + } + function onFile(srcStat, destStat, src, dest, opts, cb) { + if (!destStat) + return copyFile(srcStat, src, dest, opts, cb); + return mayCopyFile(srcStat, src, dest, opts, cb); + } + function mayCopyFile(srcStat, src, dest, opts, cb) { + if (opts.overwrite) { + fs.unlink(dest, (err) => { + if (err) + return cb(err); + return copyFile(srcStat, src, dest, opts, cb); + }); + } else if (opts.errorOnExist) { + return cb(new Error(`'${dest}' already exists`)); + } else + return cb(); + } + function copyFile(srcStat, src, dest, opts, cb) { + fs.copyFile(src, dest, (err) => { + if (err) + return cb(err); + if (opts.preserveTimestamps) + return handleTimestampsAndMode(srcStat.mode, src, dest, cb); + return setDestMode(dest, srcStat.mode, cb); + }); + } + function handleTimestampsAndMode(srcMode, src, dest, cb) { + if (fileIsNotWritable(srcMode)) { + return makeFileWritable(dest, srcMode, (err) => { + if (err) + return cb(err); + return setDestTimestampsAndMode(srcMode, src, dest, cb); + }); + } + return setDestTimestampsAndMode(srcMode, src, dest, cb); + } + function fileIsNotWritable(srcMode) { + return (srcMode & 128) === 0; + } + function makeFileWritable(dest, srcMode, cb) { + return setDestMode(dest, srcMode | 128, cb); + } + function setDestTimestampsAndMode(srcMode, src, dest, cb) { + setDestTimestamps(src, dest, (err) => { + if (err) + return cb(err); + return setDestMode(dest, srcMode, cb); + }); + } + function setDestMode(dest, srcMode, cb) { + return fs.chmod(dest, srcMode, cb); + } + function setDestTimestamps(src, dest, cb) { + fs.stat(src, (err, updatedSrcStat) => { + if (err) + return cb(err); + return utimesMillis(dest, updatedSrcStat.atime, updatedSrcStat.mtime, cb); + }); + } + function onDir(srcStat, destStat, src, dest, opts, cb) { + if (!destStat) + return mkDirAndCopy(srcStat.mode, src, dest, opts, cb); + return copyDir(src, dest, opts, cb); + } + function mkDirAndCopy(srcMode, src, dest, opts, cb) { + fs.mkdir(dest, (err) => { + if (err) + return cb(err); + copyDir(src, dest, opts, (err2) => { + if (err2) + return cb(err2); + return setDestMode(dest, srcMode, cb); + }); + }); + } + function copyDir(src, dest, opts, cb) { + fs.readdir(src, (err, items) => { + if (err) + return cb(err); + return copyDirItems(items, src, dest, opts, cb); + }); + } + function copyDirItems(items, src, dest, opts, cb) { + const item = items.pop(); + if (!item) + return cb(); + return copyDirItem(items, item, src, dest, opts, cb); + } + function copyDirItem(items, item, src, dest, opts, cb) { + const srcItem = path.join(src, item); + const destItem = path.join(dest, item); + stat.checkPaths(srcItem, destItem, "copy", opts, (err, stats) => { + if (err) + return cb(err); + const { destStat } = stats; + startCopy(destStat, srcItem, destItem, opts, (err2) => { + if (err2) + return cb(err2); + return copyDirItems(items, src, dest, opts, cb); + }); + }); + } + function onLink(destStat, src, dest, opts, cb) { + fs.readlink(src, (err, resolvedSrc) => { + if (err) + return cb(err); + if (opts.dereference) { + resolvedSrc = path.resolve(process.cwd(), resolvedSrc); + } + if (!destStat) { + return fs.symlink(resolvedSrc, dest, cb); + } else { + fs.readlink(dest, (err2, resolvedDest) => { + if (err2) { + if (err2.code === "EINVAL" || err2.code === "UNKNOWN") + return fs.symlink(resolvedSrc, dest, cb); + return cb(err2); + } + if (opts.dereference) { + resolvedDest = path.resolve(process.cwd(), resolvedDest); + } + if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) { + return cb(new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`)); + } + if (destStat.isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) { + return cb(new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`)); + } + return copyLink(resolvedSrc, dest, cb); + }); + } + }); + } + function copyLink(resolvedSrc, dest, cb) { + fs.unlink(dest, (err) => { + if (err) + return cb(err); + return fs.symlink(resolvedSrc, dest, cb); + }); + } + module2.exports = copy; + } +}); + +// node_modules/fs-extra/lib/copy/index.js +var require_copy2 = __commonJS({ + "node_modules/fs-extra/lib/copy/index.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromCallback; + module2.exports = { + copy: u(require_copy()) + }; + } +}); + +// node_modules/fs-extra/lib/remove/rimraf.js +var require_rimraf = __commonJS({ + "node_modules/fs-extra/lib/remove/rimraf.js"(exports, module2) { + "use strict"; + var fs = require_graceful_fs(); + var path = require("path"); + var assert = require("assert"); + var isWindows = process.platform === "win32"; + function defaults(options) { + const methods = [ + "unlink", + "chmod", + "stat", + "lstat", + "rmdir", + "readdir" + ]; + methods.forEach((m) => { + options[m] = options[m] || fs[m]; + m = m + "Sync"; + options[m] = options[m] || fs[m]; + }); + options.maxBusyTries = options.maxBusyTries || 3; + } + function rimraf(p, options, cb) { + let busyTries = 0; + if (typeof options === "function") { + cb = options; + options = {}; + } + assert(p, "rimraf: missing path"); + assert.strictEqual(typeof p, "string", "rimraf: path should be a string"); + assert.strictEqual(typeof cb, "function", "rimraf: callback function required"); + assert(options, "rimraf: invalid options argument provided"); + assert.strictEqual(typeof options, "object", "rimraf: options should be object"); + defaults(options); + rimraf_(p, options, function CB(er) { + if (er) { + if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && busyTries < options.maxBusyTries) { + busyTries++; + const time = busyTries * 100; + return setTimeout(() => rimraf_(p, options, CB), time); + } + if (er.code === "ENOENT") + er = null; + } + cb(er); + }); + } + function rimraf_(p, options, cb) { + assert(p); + assert(options); + assert(typeof cb === "function"); + options.lstat(p, (er, st) => { + if (er && er.code === "ENOENT") { + return cb(null); + } + if (er && er.code === "EPERM" && isWindows) { + return fixWinEPERM(p, options, er, cb); + } + if (st && st.isDirectory()) { + return rmdir(p, options, er, cb); + } + options.unlink(p, (er2) => { + if (er2) { + if (er2.code === "ENOENT") { + return cb(null); + } + if (er2.code === "EPERM") { + return isWindows ? fixWinEPERM(p, options, er2, cb) : rmdir(p, options, er2, cb); + } + if (er2.code === "EISDIR") { + return rmdir(p, options, er2, cb); + } + } + return cb(er2); + }); + }); + } + function fixWinEPERM(p, options, er, cb) { + assert(p); + assert(options); + assert(typeof cb === "function"); + options.chmod(p, 438, (er2) => { + if (er2) { + cb(er2.code === "ENOENT" ? null : er); + } else { + options.stat(p, (er3, stats) => { + if (er3) { + cb(er3.code === "ENOENT" ? null : er); + } else if (stats.isDirectory()) { + rmdir(p, options, er, cb); + } else { + options.unlink(p, cb); + } + }); + } + }); + } + function fixWinEPERMSync(p, options, er) { + let stats; + assert(p); + assert(options); + try { + options.chmodSync(p, 438); + } catch (er2) { + if (er2.code === "ENOENT") { + return; + } else { + throw er; + } + } + try { + stats = options.statSync(p); + } catch (er3) { + if (er3.code === "ENOENT") { + return; + } else { + throw er; + } + } + if (stats.isDirectory()) { + rmdirSync(p, options, er); + } else { + options.unlinkSync(p); + } + } + function rmdir(p, options, originalEr, cb) { + assert(p); + assert(options); + assert(typeof cb === "function"); + options.rmdir(p, (er) => { + if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) { + rmkids(p, options, cb); + } else if (er && er.code === "ENOTDIR") { + cb(originalEr); + } else { + cb(er); + } + }); + } + function rmkids(p, options, cb) { + assert(p); + assert(options); + assert(typeof cb === "function"); + options.readdir(p, (er, files) => { + if (er) + return cb(er); + let n = files.length; + let errState; + if (n === 0) + return options.rmdir(p, cb); + files.forEach((f) => { + rimraf(path.join(p, f), options, (er2) => { + if (errState) { + return; + } + if (er2) + return cb(errState = er2); + if (--n === 0) { + options.rmdir(p, cb); + } + }); + }); + }); + } + function rimrafSync(p, options) { + let st; + options = options || {}; + defaults(options); + assert(p, "rimraf: missing path"); + assert.strictEqual(typeof p, "string", "rimraf: path should be a string"); + assert(options, "rimraf: missing options"); + assert.strictEqual(typeof options, "object", "rimraf: options should be object"); + try { + st = options.lstatSync(p); + } catch (er) { + if (er.code === "ENOENT") { + return; + } + if (er.code === "EPERM" && isWindows) { + fixWinEPERMSync(p, options, er); + } + } + try { + if (st && st.isDirectory()) { + rmdirSync(p, options, null); + } else { + options.unlinkSync(p); + } + } catch (er) { + if (er.code === "ENOENT") { + return; + } else if (er.code === "EPERM") { + return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er); + } else if (er.code !== "EISDIR") { + throw er; + } + rmdirSync(p, options, er); + } + } + function rmdirSync(p, options, originalEr) { + assert(p); + assert(options); + try { + options.rmdirSync(p); + } catch (er) { + if (er.code === "ENOTDIR") { + throw originalEr; + } else if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") { + rmkidsSync(p, options); + } else if (er.code !== "ENOENT") { + throw er; + } + } + } + function rmkidsSync(p, options) { + assert(p); + assert(options); + options.readdirSync(p).forEach((f) => rimrafSync(path.join(p, f), options)); + if (isWindows) { + const startTime = Date.now(); + do { + try { + const ret = options.rmdirSync(p, options); + return ret; + } catch { + } + } while (Date.now() - startTime < 500); + } else { + const ret = options.rmdirSync(p, options); + return ret; + } + } + module2.exports = rimraf; + rimraf.sync = rimrafSync; + } +}); + +// node_modules/fs-extra/lib/remove/index.js +var require_remove = __commonJS({ + "node_modules/fs-extra/lib/remove/index.js"(exports, module2) { + "use strict"; + var fs = require_graceful_fs(); + var u = require_universalify().fromCallback; + var rimraf = require_rimraf(); + function remove(path, callback) { + if (fs.rm) + return fs.rm(path, { recursive: true, force: true }, callback); + rimraf(path, callback); + } + function removeSync(path) { + if (fs.rmSync) + return fs.rmSync(path, { recursive: true, force: true }); + rimraf.sync(path); + } + module2.exports = { + remove: u(remove), + removeSync + }; + } +}); + +// node_modules/fs-extra/lib/empty/index.js +var require_empty = __commonJS({ + "node_modules/fs-extra/lib/empty/index.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromPromise; + var fs = require_fs(); + var path = require("path"); + var mkdir = require_mkdirs(); + var remove = require_remove(); + var emptyDir = u(async function emptyDir2(dir) { + let items; + try { + items = await fs.readdir(dir); + } catch { + return mkdir.mkdirs(dir); + } + return Promise.all(items.map((item) => remove.remove(path.join(dir, item)))); + }); + function emptyDirSync(dir) { + let items; + try { + items = fs.readdirSync(dir); + } catch { + return mkdir.mkdirsSync(dir); + } + items.forEach((item) => { + item = path.join(dir, item); + remove.removeSync(item); + }); + } + module2.exports = { + emptyDirSync, + emptydirSync: emptyDirSync, + emptyDir, + emptydir: emptyDir + }; + } +}); + +// node_modules/fs-extra/lib/ensure/file.js +var require_file = __commonJS({ + "node_modules/fs-extra/lib/ensure/file.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromCallback; + var path = require("path"); + var fs = require_graceful_fs(); + var mkdir = require_mkdirs(); + function createFile(file, callback) { + function makeFile() { + fs.writeFile(file, "", (err) => { + if (err) + return callback(err); + callback(); + }); + } + fs.stat(file, (err, stats) => { + if (!err && stats.isFile()) + return callback(); + const dir = path.dirname(file); + fs.stat(dir, (err2, stats2) => { + if (err2) { + if (err2.code === "ENOENT") { + return mkdir.mkdirs(dir, (err3) => { + if (err3) + return callback(err3); + makeFile(); + }); + } + return callback(err2); + } + if (stats2.isDirectory()) + makeFile(); + else { + fs.readdir(dir, (err3) => { + if (err3) + return callback(err3); + }); + } + }); + }); + } + function createFileSync(file) { + let stats; + try { + stats = fs.statSync(file); + } catch { + } + if (stats && stats.isFile()) + return; + const dir = path.dirname(file); + try { + if (!fs.statSync(dir).isDirectory()) { + fs.readdirSync(dir); + } + } catch (err) { + if (err && err.code === "ENOENT") + mkdir.mkdirsSync(dir); + else + throw err; + } + fs.writeFileSync(file, ""); + } + module2.exports = { + createFile: u(createFile), + createFileSync + }; + } +}); + +// node_modules/fs-extra/lib/ensure/link.js +var require_link = __commonJS({ + "node_modules/fs-extra/lib/ensure/link.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromCallback; + var path = require("path"); + var fs = require_graceful_fs(); + var mkdir = require_mkdirs(); + var pathExists = require_path_exists().pathExists; + var { areIdentical } = require_stat(); + function createLink(srcpath, dstpath, callback) { + function makeLink(srcpath2, dstpath2) { + fs.link(srcpath2, dstpath2, (err) => { + if (err) + return callback(err); + callback(null); + }); + } + fs.lstat(dstpath, (_, dstStat) => { + fs.lstat(srcpath, (err, srcStat) => { + if (err) { + err.message = err.message.replace("lstat", "ensureLink"); + return callback(err); + } + if (dstStat && areIdentical(srcStat, dstStat)) + return callback(null); + const dir = path.dirname(dstpath); + pathExists(dir, (err2, dirExists) => { + if (err2) + return callback(err2); + if (dirExists) + return makeLink(srcpath, dstpath); + mkdir.mkdirs(dir, (err3) => { + if (err3) + return callback(err3); + makeLink(srcpath, dstpath); + }); + }); + }); + }); + } + function createLinkSync(srcpath, dstpath) { + let dstStat; + try { + dstStat = fs.lstatSync(dstpath); + } catch { + } + try { + const srcStat = fs.lstatSync(srcpath); + if (dstStat && areIdentical(srcStat, dstStat)) + return; + } catch (err) { + err.message = err.message.replace("lstat", "ensureLink"); + throw err; + } + const dir = path.dirname(dstpath); + const dirExists = fs.existsSync(dir); + if (dirExists) + return fs.linkSync(srcpath, dstpath); + mkdir.mkdirsSync(dir); + return fs.linkSync(srcpath, dstpath); + } + module2.exports = { + createLink: u(createLink), + createLinkSync + }; + } +}); + +// node_modules/fs-extra/lib/ensure/symlink-paths.js +var require_symlink_paths = __commonJS({ + "node_modules/fs-extra/lib/ensure/symlink-paths.js"(exports, module2) { + "use strict"; + var path = require("path"); + var fs = require_graceful_fs(); + var pathExists = require_path_exists().pathExists; + function symlinkPaths(srcpath, dstpath, callback) { + if (path.isAbsolute(srcpath)) { + return fs.lstat(srcpath, (err) => { + if (err) { + err.message = err.message.replace("lstat", "ensureSymlink"); + return callback(err); + } + return callback(null, { + toCwd: srcpath, + toDst: srcpath + }); + }); + } else { + const dstdir = path.dirname(dstpath); + const relativeToDst = path.join(dstdir, srcpath); + return pathExists(relativeToDst, (err, exists) => { + if (err) + return callback(err); + if (exists) { + return callback(null, { + toCwd: relativeToDst, + toDst: srcpath + }); + } else { + return fs.lstat(srcpath, (err2) => { + if (err2) { + err2.message = err2.message.replace("lstat", "ensureSymlink"); + return callback(err2); + } + return callback(null, { + toCwd: srcpath, + toDst: path.relative(dstdir, srcpath) + }); + }); + } + }); + } + } + function symlinkPathsSync(srcpath, dstpath) { + let exists; + if (path.isAbsolute(srcpath)) { + exists = fs.existsSync(srcpath); + if (!exists) + throw new Error("absolute srcpath does not exist"); + return { + toCwd: srcpath, + toDst: srcpath + }; + } else { + const dstdir = path.dirname(dstpath); + const relativeToDst = path.join(dstdir, srcpath); + exists = fs.existsSync(relativeToDst); + if (exists) { + return { + toCwd: relativeToDst, + toDst: srcpath + }; + } else { + exists = fs.existsSync(srcpath); + if (!exists) + throw new Error("relative srcpath does not exist"); + return { + toCwd: srcpath, + toDst: path.relative(dstdir, srcpath) + }; + } + } + } + module2.exports = { + symlinkPaths, + symlinkPathsSync + }; + } +}); + +// node_modules/fs-extra/lib/ensure/symlink-type.js +var require_symlink_type = __commonJS({ + "node_modules/fs-extra/lib/ensure/symlink-type.js"(exports, module2) { + "use strict"; + var fs = require_graceful_fs(); + function symlinkType(srcpath, type, callback) { + callback = typeof type === "function" ? type : callback; + type = typeof type === "function" ? false : type; + if (type) + return callback(null, type); + fs.lstat(srcpath, (err, stats) => { + if (err) + return callback(null, "file"); + type = stats && stats.isDirectory() ? "dir" : "file"; + callback(null, type); + }); + } + function symlinkTypeSync(srcpath, type) { + let stats; + if (type) + return type; + try { + stats = fs.lstatSync(srcpath); + } catch { + return "file"; + } + return stats && stats.isDirectory() ? "dir" : "file"; + } + module2.exports = { + symlinkType, + symlinkTypeSync + }; + } +}); + +// node_modules/fs-extra/lib/ensure/symlink.js +var require_symlink = __commonJS({ + "node_modules/fs-extra/lib/ensure/symlink.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromCallback; + var path = require("path"); + var fs = require_fs(); + var _mkdirs = require_mkdirs(); + var mkdirs = _mkdirs.mkdirs; + var mkdirsSync = _mkdirs.mkdirsSync; + var _symlinkPaths = require_symlink_paths(); + var symlinkPaths = _symlinkPaths.symlinkPaths; + var symlinkPathsSync = _symlinkPaths.symlinkPathsSync; + var _symlinkType = require_symlink_type(); + var symlinkType = _symlinkType.symlinkType; + var symlinkTypeSync = _symlinkType.symlinkTypeSync; + var pathExists = require_path_exists().pathExists; + var { areIdentical } = require_stat(); + function createSymlink(srcpath, dstpath, type, callback) { + callback = typeof type === "function" ? type : callback; + type = typeof type === "function" ? false : type; + fs.lstat(dstpath, (err, stats) => { + if (!err && stats.isSymbolicLink()) { + Promise.all([ + fs.stat(srcpath), + fs.stat(dstpath) + ]).then(([srcStat, dstStat]) => { + if (areIdentical(srcStat, dstStat)) + return callback(null); + _createSymlink(srcpath, dstpath, type, callback); + }); + } else + _createSymlink(srcpath, dstpath, type, callback); + }); + } + function _createSymlink(srcpath, dstpath, type, callback) { + symlinkPaths(srcpath, dstpath, (err, relative2) => { + if (err) + return callback(err); + srcpath = relative2.toDst; + symlinkType(relative2.toCwd, type, (err2, type2) => { + if (err2) + return callback(err2); + const dir = path.dirname(dstpath); + pathExists(dir, (err3, dirExists) => { + if (err3) + return callback(err3); + if (dirExists) + return fs.symlink(srcpath, dstpath, type2, callback); + mkdirs(dir, (err4) => { + if (err4) + return callback(err4); + fs.symlink(srcpath, dstpath, type2, callback); + }); + }); + }); + }); + } + function createSymlinkSync(srcpath, dstpath, type) { + let stats; + try { + stats = fs.lstatSync(dstpath); + } catch { + } + if (stats && stats.isSymbolicLink()) { + const srcStat = fs.statSync(srcpath); + const dstStat = fs.statSync(dstpath); + if (areIdentical(srcStat, dstStat)) + return; + } + const relative2 = symlinkPathsSync(srcpath, dstpath); + srcpath = relative2.toDst; + type = symlinkTypeSync(relative2.toCwd, type); + const dir = path.dirname(dstpath); + const exists = fs.existsSync(dir); + if (exists) + return fs.symlinkSync(srcpath, dstpath, type); + mkdirsSync(dir); + return fs.symlinkSync(srcpath, dstpath, type); + } + module2.exports = { + createSymlink: u(createSymlink), + createSymlinkSync + }; + } +}); + +// node_modules/fs-extra/lib/ensure/index.js +var require_ensure = __commonJS({ + "node_modules/fs-extra/lib/ensure/index.js"(exports, module2) { + "use strict"; + var file = require_file(); + var link = require_link(); + var symlink = require_symlink(); + module2.exports = { + createFile: file.createFile, + createFileSync: file.createFileSync, + ensureFile: file.createFile, + ensureFileSync: file.createFileSync, + createLink: link.createLink, + createLinkSync: link.createLinkSync, + ensureLink: link.createLink, + ensureLinkSync: link.createLinkSync, + createSymlink: symlink.createSymlink, + createSymlinkSync: symlink.createSymlinkSync, + ensureSymlink: symlink.createSymlink, + ensureSymlinkSync: symlink.createSymlinkSync + }; + } +}); + +// node_modules/jsonfile/utils.js +var require_utils2 = __commonJS({ + "node_modules/jsonfile/utils.js"(exports, module2) { + function stringify(obj, { EOL = "\n", finalEOL = true, replacer = null, spaces } = {}) { + const EOF = finalEOL ? EOL : ""; + const str = JSON.stringify(obj, replacer, spaces); + return str.replace(/\n/g, EOL) + EOF; + } + function stripBom(content) { + if (Buffer.isBuffer(content)) + content = content.toString("utf8"); + return content.replace(/^\uFEFF/, ""); + } + module2.exports = { stringify, stripBom }; + } +}); + +// node_modules/jsonfile/index.js +var require_jsonfile = __commonJS({ + "node_modules/jsonfile/index.js"(exports, module2) { + var _fs; + try { + _fs = require_graceful_fs(); + } catch (_) { + _fs = require("fs"); + } + var universalify = require_universalify(); + var { stringify, stripBom } = require_utils2(); + async function _readFile(file, options = {}) { + if (typeof options === "string") { + options = { encoding: options }; + } + const fs = options.fs || _fs; + const shouldThrow = "throws" in options ? options.throws : true; + let data = await universalify.fromCallback(fs.readFile)(file, options); + data = stripBom(data); + let obj; + try { + obj = JSON.parse(data, options ? options.reviver : null); + } catch (err) { + if (shouldThrow) { + err.message = `${file}: ${err.message}`; + throw err; + } else { + return null; + } + } + return obj; + } + var readFile = universalify.fromPromise(_readFile); + function readFileSync6(file, options = {}) { + if (typeof options === "string") { + options = { encoding: options }; + } + const fs = options.fs || _fs; + const shouldThrow = "throws" in options ? options.throws : true; + try { + let content = fs.readFileSync(file, options); + content = stripBom(content); + return JSON.parse(content, options.reviver); + } catch (err) { + if (shouldThrow) { + err.message = `${file}: ${err.message}`; + throw err; + } else { + return null; + } + } + } + async function _writeFile(file, obj, options = {}) { + const fs = options.fs || _fs; + const str = stringify(obj, options); + await universalify.fromCallback(fs.writeFile)(file, str, options); + } + var writeFile2 = universalify.fromPromise(_writeFile); + function writeFileSync3(file, obj, options = {}) { + const fs = options.fs || _fs; + const str = stringify(obj, options); + return fs.writeFileSync(file, str, options); + } + var jsonfile = { + readFile, + readFileSync: readFileSync6, + writeFile: writeFile2, + writeFileSync: writeFileSync3 + }; + module2.exports = jsonfile; + } +}); + +// node_modules/fs-extra/lib/json/jsonfile.js +var require_jsonfile2 = __commonJS({ + "node_modules/fs-extra/lib/json/jsonfile.js"(exports, module2) { + "use strict"; + var jsonFile = require_jsonfile(); + module2.exports = { + readJson: jsonFile.readFile, + readJsonSync: jsonFile.readFileSync, + writeJson: jsonFile.writeFile, + writeJsonSync: jsonFile.writeFileSync + }; + } +}); + +// node_modules/fs-extra/lib/output/index.js +var require_output = __commonJS({ + "node_modules/fs-extra/lib/output/index.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromCallback; + var fs = require_graceful_fs(); + var path = require("path"); + var mkdir = require_mkdirs(); + var pathExists = require_path_exists().pathExists; + function outputFile(file, data, encoding, callback) { + if (typeof encoding === "function") { + callback = encoding; + encoding = "utf8"; + } + const dir = path.dirname(file); + pathExists(dir, (err, itDoes) => { + if (err) + return callback(err); + if (itDoes) + return fs.writeFile(file, data, encoding, callback); + mkdir.mkdirs(dir, (err2) => { + if (err2) + return callback(err2); + fs.writeFile(file, data, encoding, callback); + }); + }); + } + function outputFileSync(file, ...args) { + const dir = path.dirname(file); + if (fs.existsSync(dir)) { + return fs.writeFileSync(file, ...args); + } + mkdir.mkdirsSync(dir); + fs.writeFileSync(file, ...args); + } + module2.exports = { + outputFile: u(outputFile), + outputFileSync + }; + } +}); + +// node_modules/fs-extra/lib/json/output-json.js +var require_output_json = __commonJS({ + "node_modules/fs-extra/lib/json/output-json.js"(exports, module2) { + "use strict"; + var { stringify } = require_utils2(); + var { outputFile } = require_output(); + async function outputJson(file, data, options = {}) { + const str = stringify(data, options); + await outputFile(file, str, options); + } + module2.exports = outputJson; + } +}); + +// node_modules/fs-extra/lib/json/output-json-sync.js +var require_output_json_sync = __commonJS({ + "node_modules/fs-extra/lib/json/output-json-sync.js"(exports, module2) { + "use strict"; + var { stringify } = require_utils2(); + var { outputFileSync } = require_output(); + function outputJsonSync(file, data, options) { + const str = stringify(data, options); + outputFileSync(file, str, options); + } + module2.exports = outputJsonSync; + } +}); + +// node_modules/fs-extra/lib/json/index.js +var require_json = __commonJS({ + "node_modules/fs-extra/lib/json/index.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromPromise; + var jsonFile = require_jsonfile2(); + jsonFile.outputJson = u(require_output_json()); + jsonFile.outputJsonSync = require_output_json_sync(); + jsonFile.outputJSON = jsonFile.outputJson; + jsonFile.outputJSONSync = jsonFile.outputJsonSync; + jsonFile.writeJSON = jsonFile.writeJson; + jsonFile.writeJSONSync = jsonFile.writeJsonSync; + jsonFile.readJSON = jsonFile.readJson; + jsonFile.readJSONSync = jsonFile.readJsonSync; + module2.exports = jsonFile; + } +}); + +// node_modules/fs-extra/lib/move-sync/move-sync.js +var require_move_sync = __commonJS({ + "node_modules/fs-extra/lib/move-sync/move-sync.js"(exports, module2) { + "use strict"; + var fs = require_graceful_fs(); + var path = require("path"); + var copySync2 = require_copy_sync2().copySync; + var removeSync = require_remove().removeSync; + var mkdirpSync = require_mkdirs().mkdirpSync; + var stat = require_stat(); + function moveSync(src, dest, opts) { + opts = opts || {}; + const overwrite = opts.overwrite || opts.clobber || false; + const { srcStat, isChangingCase = false } = stat.checkPathsSync(src, dest, "move", opts); + stat.checkParentPathsSync(src, srcStat, dest, "move"); + if (!isParentRoot(dest)) + mkdirpSync(path.dirname(dest)); + return doRename(src, dest, overwrite, isChangingCase); + } + function isParentRoot(dest) { + const parent = path.dirname(dest); + const parsedPath = path.parse(parent); + return parsedPath.root === parent; + } + function doRename(src, dest, overwrite, isChangingCase) { + if (isChangingCase) + return rename(src, dest, overwrite); + if (overwrite) { + removeSync(dest); + return rename(src, dest, overwrite); + } + if (fs.existsSync(dest)) + throw new Error("dest already exists."); + return rename(src, dest, overwrite); + } + function rename(src, dest, overwrite) { + try { + fs.renameSync(src, dest); + } catch (err) { + if (err.code !== "EXDEV") + throw err; + return moveAcrossDevice(src, dest, overwrite); + } + } + function moveAcrossDevice(src, dest, overwrite) { + const opts = { + overwrite, + errorOnExist: true + }; + copySync2(src, dest, opts); + return removeSync(src); + } + module2.exports = moveSync; + } +}); + +// node_modules/fs-extra/lib/move-sync/index.js +var require_move_sync2 = __commonJS({ + "node_modules/fs-extra/lib/move-sync/index.js"(exports, module2) { + "use strict"; + module2.exports = { + moveSync: require_move_sync() + }; + } +}); + +// node_modules/fs-extra/lib/move/move.js +var require_move = __commonJS({ + "node_modules/fs-extra/lib/move/move.js"(exports, module2) { + "use strict"; + var fs = require_graceful_fs(); + var path = require("path"); + var copy = require_copy2().copy; + var remove = require_remove().remove; + var mkdirp = require_mkdirs().mkdirp; + var pathExists = require_path_exists().pathExists; + var stat = require_stat(); + function move(src, dest, opts, cb) { + if (typeof opts === "function") { + cb = opts; + opts = {}; + } + const overwrite = opts.overwrite || opts.clobber || false; + stat.checkPaths(src, dest, "move", opts, (err, stats) => { + if (err) + return cb(err); + const { srcStat, isChangingCase = false } = stats; + stat.checkParentPaths(src, srcStat, dest, "move", (err2) => { + if (err2) + return cb(err2); + if (isParentRoot(dest)) + return doRename(src, dest, overwrite, isChangingCase, cb); + mkdirp(path.dirname(dest), (err3) => { + if (err3) + return cb(err3); + return doRename(src, dest, overwrite, isChangingCase, cb); + }); + }); + }); + } + function isParentRoot(dest) { + const parent = path.dirname(dest); + const parsedPath = path.parse(parent); + return parsedPath.root === parent; + } + function doRename(src, dest, overwrite, isChangingCase, cb) { + if (isChangingCase) + return rename(src, dest, overwrite, cb); + if (overwrite) { + return remove(dest, (err) => { + if (err) + return cb(err); + return rename(src, dest, overwrite, cb); + }); + } + pathExists(dest, (err, destExists) => { + if (err) + return cb(err); + if (destExists) + return cb(new Error("dest already exists.")); + return rename(src, dest, overwrite, cb); + }); + } + function rename(src, dest, overwrite, cb) { + fs.rename(src, dest, (err) => { + if (!err) + return cb(); + if (err.code !== "EXDEV") + return cb(err); + return moveAcrossDevice(src, dest, overwrite, cb); + }); + } + function moveAcrossDevice(src, dest, overwrite, cb) { + const opts = { + overwrite, + errorOnExist: true + }; + copy(src, dest, opts, (err) => { + if (err) + return cb(err); + return remove(src, cb); + }); + } + module2.exports = move; + } +}); + +// node_modules/fs-extra/lib/move/index.js +var require_move2 = __commonJS({ + "node_modules/fs-extra/lib/move/index.js"(exports, module2) { + "use strict"; + var u = require_universalify().fromCallback; + module2.exports = { + move: u(require_move()) + }; + } +}); + +// node_modules/fs-extra/lib/index.js +var require_lib = __commonJS({ + "node_modules/fs-extra/lib/index.js"(exports, module2) { + "use strict"; + module2.exports = __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, require_fs()), require_copy_sync2()), require_copy2()), require_empty()), require_ensure()), require_json()), require_mkdirs()), require_move_sync2()), require_move2()), require_output()), require_path_exists()), require_remove()); + } +}); + // node_modules/yargs/lib/platform-shims/esm.mjs var import_assert = require("assert"); @@ -3882,8 +6453,8 @@ var GlobalMiddleware = class { this.frozens = []; this.yargs = yargs; } - addMiddleware(callback, applyBeforeValidation, global = true, mutates = false) { - argsert(" [boolean] [boolean] [boolean]", [callback, applyBeforeValidation, global], arguments.length); + addMiddleware(callback, applyBeforeValidation, global2 = true, mutates = false) { + argsert(" [boolean] [boolean] [boolean]", [callback, applyBeforeValidation, global2], arguments.length); if (Array.isArray(callback)) { for (let i = 0; i < callback.length; i++) { if (typeof callback[i] !== "function") { @@ -3891,13 +6462,13 @@ var GlobalMiddleware = class { } const m = callback[i]; m.applyBeforeValidation = applyBeforeValidation; - m.global = global; + m.global = global2; } Array.prototype.push.apply(this.globalMiddleware, callback); } else if (typeof callback === "function") { const m = callback; m.applyBeforeValidation = applyBeforeValidation; - m.global = global; + m.global = global2; m.mutates = mutates; this.globalMiddleware.push(callback); } @@ -5673,8 +8244,8 @@ var YargsInstance = class { this[kTrackManuallySetKeys](keys); return this; } - check(f, global) { - argsert(" [boolean]", [f, global], arguments.length); + check(f, global2) { + argsert(" [boolean]", [f, global2], arguments.length); this.middleware((argv, _yargs) => { return maybeAsyncResult(() => { return f(argv, _yargs.getOptions()); @@ -5689,7 +8260,7 @@ var YargsInstance = class { __classPrivateFieldGet(this, _YargsInstance_usage, "f").fail(err.message ? err.message : err.toString(), err); return argv; }); - }, false, global); + }, false, global2); return this; } choices(key, value) { @@ -5990,10 +8561,10 @@ var YargsInstance = class { getStrictOptions() { return __classPrivateFieldGet(this, _YargsInstance_strictOptions, "f"); } - global(globals, global) { - argsert(" [boolean]", [globals, global], arguments.length); + global(globals, global2) { + argsert(" [boolean]", [globals, global2], arguments.length); globals = [].concat(globals); - if (global !== false) { + if (global2 !== false) { __classPrivateFieldGet(this, _YargsInstance_options, "f").local = __classPrivateFieldGet(this, _YargsInstance_options, "f").local.filter((l) => globals.indexOf(l) === -1); } else { globals.forEach((g) => { @@ -6037,8 +8608,8 @@ var YargsInstance = class { __classPrivateFieldGet(this, _YargsInstance_shim, "f").y18n.setLocale(locale); return this; } - middleware(callback, applyBeforeValidation, global) { - return __classPrivateFieldGet(this, _YargsInstance_globalMiddleware, "f").addMiddleware(callback, !!applyBeforeValidation, global); + middleware(callback, applyBeforeValidation, global2) { + return __classPrivateFieldGet(this, _YargsInstance_globalMiddleware, "f").addMiddleware(callback, !!applyBeforeValidation, global2); } nargs(key, value) { argsert(" [number]", [key, value], arguments.length); @@ -6987,7 +9558,7 @@ var matcher = [ regexMatcher(/^\/\/.+/, "comment"), regexMatcher(/^#.+/, "comment"), regexMatcher(/^".*?"/, "string"), - regexMatcher(/^(type|enum|import|service)\b/, "keyword"), + regexMatcher(/^(type|enum|import|service|define)\b/, "keyword"), regexMatcher(/^\@/, "at"), regexMatcher(/^\:/, "colon"), regexMatcher(/^\;/, "semicolon"), @@ -7257,6 +9828,18 @@ function parse(tokens, file) { location: { file, idx } }; }; + const parseDefine = () => { + const idx = eatToken("define"); + let [key] = eatText(); + let [value] = eatText(); + eatToken(";"); + return { + type: "define", + location: { file, idx }, + key, + value + }; + }; const parseStatement = () => { if (currentToken.type === "keyword") { switch (currentToken.value) { @@ -7268,6 +9851,8 @@ function parse(tokens, file) { return parseEnumStatement(); case "service": return parseServiceStatement(); + case "define": + return parseDefine(); default: throw new ParserError(`Unknown keyword ${currentToken.value}`, currentToken); } @@ -7298,6 +9883,7 @@ function get_ir(parsed) { let types = []; let enums = []; let steps = []; + let options = {}; parsed.forEach((statement) => { log("Working on statement of type %s", statement.type); if (statement.type == "import") @@ -7451,19 +10037,26 @@ function get_ir(parsed) { functions } ]); + } else if (statement.type == "define") { + options[statement.key] = statement.value; } else { throw new IRError(statement, "Invalid statement!"); } }); - return steps; + return { + options, + steps + }; } // src/compile.ts var FS = __toESM(require("fs")); +var FSE = __toESM(require_lib()); var Path = __toESM(require("path")); var CompileTarget = class { - constructor(outputFolder) { + constructor(outputFolder, options) { this.outputFolder = outputFolder; + this.options = options; if (!FS.existsSync(outputFolder)) { FS.mkdirSync(outputFolder, { recursive: true @@ -7494,10 +10087,14 @@ var CompileTarget = class { } return res.join("\n"); } + loadTemplateFolder(name) { + let root = Path.join(__dirname, "../templates/", name); + FSE.copySync(root, this.outputFolder, {}); + } }; function compile(ir, target) { target.start(); - ir.forEach((step) => { + ir.steps.forEach((step) => { const [type, def] = step; if (type == "type") target.generateType(def); @@ -7507,7 +10104,7 @@ function compile(ir, target) { target.generateService(def); }); if (target.finalize) - target.finalize(ir); + target.finalize(ir.steps); } // src/targets/typescript.ts @@ -7580,7 +10177,7 @@ var TypescriptTarget = class extends CompileTarget { a(0, `export function apply_${def.name}(data: ${def.name}): ${def.name} {`); { a(1, `if(typeof data !== "object") throw new VerificationError("${def.name}", undefined, data);`); - a(1, `let res = {} as any;`); + a(1, `let res = new ${def.name}() as any;`); def.fields.forEach((field) => { a(1, `if(data["${field.name}"] !== null && data["${field.name}"] !== undefined) {`); if (field.array) { @@ -7652,6 +10249,7 @@ var TypescriptTarget = class extends CompileTarget { const params = fnc.inputs.map((e) => `${e.name}: ${toJSType(e.type) + (e.array ? "[]" : "")}`).join(", "); if (!fnc.return) { a(1, `${fnc.name}(${params}): void {`); + 1; a(2, `this._provider.sendMessage({`); a(3, `jsonrpc: "2.0",`); a(3, `method: "${def.name}.${fnc.name}",`); @@ -7672,11 +10270,11 @@ var TypescriptTarget = class extends CompileTarget { a(3, `});`); a(2, `}).then(result => {`); if (fnc.return.array) { - a(2, `for(const elm of result) {`); - a(3, `apply_${fnc.return.type}(elm);`); - a(2, `}`); + a(3, `for(let i = 0; i < result.length; i++) {`); + a(4, `result[i] = apply_${fnc.return.type}(result[i]);`); + a(3, `}`); } else { - a(3, `apply_${fnc.return.type}(result);`); + a(3, `result = apply_${fnc.return.type}(result);`); } a(3, `return result;`); a(2, `});`); @@ -7826,6 +10424,225 @@ var NodeJSTypescriptTarget = class extends TypescriptTarget { flavour = "node"; }; +// src/targets/csharp.ts +var conversion2 = { + boolean: "bool", + number: "double", + string: "string", + void: "void" +}; +function toCSharpType(type) { + return conversion2[type] || type; +} +var CSharpTarget = class extends CompileTarget { + name = "c#"; + get namespace() { + return this.options.CSharp_Namespace || "JRPC"; + } + start() { + this.writeFile(this.namespace + ".csproj", this.getTemplate("CSharp/CSharp.csproj")); + const fixNS = (input) => input.replace("__NAMESPACE__", this.namespace); + const copyClass = (name) => this.writeFile(name + ".cs", fixNS(this.getTemplate(`CSharp/${name}.cs`))); + copyClass("JRpcClient"); + copyClass("JRpcServer"); + copyClass("JRpcTransport"); + } + generateType(definition) { + let lines = []; + const a = (i, t) => { + if (!Array.isArray(t)) { + t = [t]; + } + t.forEach((l) => lines.push(" ".repeat(i) + l.trim())); + }; + a(0, `using System.Text.Json;`); + a(0, `using System.Text.Json.Serialization;`); + a(0, `using System.Collections.Generic;`); + a(0, ``); + a(0, `namespace ${this.namespace};`); + a(0, ``); + a(0, `public class ${definition.name} {`); + for (const field of definition.fields) { + if (field.array) { + a(1, `public IList<${toCSharpType(field.type)}>? ${field.name} { get; set; }`); + } else if (field.map) { + a(1, `public Dictionary<${toCSharpType(field.map)}, ${toCSharpType(field.type)}>? ${field.name} { get; set; }`); + } else { + a(1, `public ${toCSharpType(field.type)}? ${field.name} { get; set; }`); + } + } + a(0, `}`); + this.writeFile(`${definition.name}.cs`, lines.join("\n")); + } + generateEnum(definition) { + let lines = []; + const a = (i, t) => { + if (!Array.isArray(t)) { + t = [t]; + } + t.forEach((l) => lines.push(" ".repeat(i) + l.trim())); + }; + a(0, `using System.Text.Json;`); + a(0, `using System.Text.Json.Serialization;`); + a(0, ``); + a(0, `namespace ${this.namespace};`); + a(0, ``); + a(0, `public enum ${definition.name} {`); + for (const field of definition.values) { + a(1, `${field.name} = ${field.value},`); + } + a(0, `}`); + this.writeFile(`${definition.name}.cs`, lines.join("\n")); + } + generateServiceClient(definition) { + let lines = []; + const a = (i, t) => { + if (!Array.isArray(t)) { + t = [t]; + } + t.forEach((l) => lines.push(" ".repeat(i) + l.trim())); + }; + a(0, `using System.Text.Json;`); + a(0, `using System.Text.Json.Serialization;`); + a(0, `using System.Text.Json.Nodes;`); + a(0, `using System.Threading.Tasks;`); + a(0, ``); + a(0, `namespace ${this.namespace};`); + a(0, ``); + a(0, `public class ${definition.name}Client {`); + a(0, ``); + a(1, `private JRpcClient Client;`); + a(0, ``); + a(1, `public ${definition.name}Client(JRpcClient client) {`); + a(2, `this.Client = client;`); + a(1, `}`); + a(0, ``); + for (const fnc of definition.functions) { + let params = fnc.inputs.map((inp) => { + if (inp.array) { + return `List<${toCSharpType(inp.type)}> ${inp.name}`; + } else { + return `${toCSharpType(inp.type)} ${inp.name}`; + } + }).join(","); + const genParam = () => a(2, `var param = new JsonArray(${fnc.inputs.map((e) => `JsonSerializer.SerializeToNode(${e.name})`).join(",")});`); + if (fnc.return) { + if (fnc.return.type == "void") { + a(1, `public async Task ${fnc.name}(${params}) {`); + genParam(); + a(2, `await this.Client.SendRequestRaw("${definition.name}.${fnc.name}", param);`); + a(1, `}`); + } else { + let ret = fnc.return ? fnc.return.array ? `IList<${toCSharpType(fnc.return.type)}>` : toCSharpType(fnc.return.type) : void 0; + a(1, `public async Task<${ret}> ${fnc.name}(${params}) {`); + genParam(); + a(2, `return await this.Client.SendRequest<${ret}>("${definition.name}.${fnc.name}", param);`); + a(1, `}`); + } + } else { + a(1, `public void ${fnc.name}(${params}) {`); + genParam(); + a(2, `this.Client.SendNotification("${definition.name}.${fnc.name}", param);`); + a(1, `}`); + } + a(1, ``); + } + a(0, `}`); + this.writeFile(`${definition.name}Client.cs`, lines.join("\n")); + } + generateServiceServer(definition) { + let lines = []; + const a = (i, t) => { + if (!Array.isArray(t)) { + t = [t]; + } + t.forEach((l) => lines.push(" ".repeat(i) + l.trim())); + }; + a(0, `using System.Text.Json;`); + a(0, `using System.Text.Json.Serialization;`); + a(0, `using System.Text.Json.Nodes;`); + a(0, `using System.Threading.Tasks;`); + a(0, ``); + a(0, `namespace ${this.namespace};`); + a(0, ``); + a(0, `public abstract class ${definition.name}Server : JRpcService {`); + a(0, ``); + a(1, `public ${definition.name}Server(JRpcClient client) {`); + for (const fnc of definition.functions) { + a(2, `this.RegisterFunction("${fnc.name}");`); + } + a(1, `}`); + a(0, ``); + for (const fnc of definition.functions) { + let params = [ + fnc.inputs.map((inp) => { + if (inp.array) { + return `List<${toCSharpType(inp.type)}> ${inp.name}`; + } else { + return `${toCSharpType(inp.type)} ${inp.name}`; + } + }), + "TContext ctx" + ].join(","); + if (fnc.return) { + if (fnc.return.type == "void") { + a(1, `public abstract Task ${fnc.name}(${params});`); + } else { + let ret = fnc.return ? fnc.return.array ? `IList<${toCSharpType(fnc.return.type)}>` : toCSharpType(fnc.return.type) : void 0; + a(1, `public abstract Task<${ret}> ${fnc.name}(${params});`); + } + } else { + a(1, `public abstract void ${fnc.name}(${params});`); + } + } + a(0, ``); + a(1, `public async override Task HandleRequest(string func, JsonNode param, TContext context) {`); + a(2, `switch(func) {`); + for (const fnc of definition.functions) { + a(3, `case "${fnc.name}": {`); + a(4, `if(param is JsonObject) {`); + a(5, `var ja = new JsonArray(${fnc.inputs.map((inp) => { + return `param["${inp.name}"]`; + }).join(", ")});`); + a(5, `param = ja;`); + a(4, `}`); + let pref = ""; + if (fnc.return) { + if (fnc.return.type != "void") + pref = "var result = await "; + else + pref = "await "; + } + a(4, pref + `this.${fnc.name}(${[ + ...fnc.inputs.map((inp, idx) => { + let type = inp.array ? `List<${toCSharpType(inp.type)}>` : `${toCSharpType(inp.type)}`; + return `param[${idx}]!.Deserialize<${type}>()`; + }), + "context" + ].join(", ")});`); + if (fnc.return && fnc.return.type != "void") { + a(4, `return JsonSerializer.SerializeToNode(result);`); + } else { + a(4, `return null;`); + } + a(3, `}`); + a(0, ``); + } + a(3, `default:`); + a(4, `throw new Exception("Invalid Method!");`); + a(2, `}`); + a(1, `}`); + a(0, `}`); + this.writeFile(`${definition.name}Server.cs`, lines.join("\n")); + } + generateService(definition) { + this.generateServiceClient(definition); + this.generateServiceServer(definition); + } + finalize(steps) { + } +}; + // src/process.ts var CatchedError = class extends Error { }; @@ -7833,6 +10650,7 @@ var log2 = (0, import_debug2.default)("app"); var Targets = /* @__PURE__ */ new Map(); Targets.set("ts-esm", ESMTypescriptTarget); Targets.set("ts-node", NodeJSTypescriptTarget); +Targets.set("c#", CSharpTarget); function indexToLineAndCol(src, index) { let line = 1; let col = 1; @@ -7952,7 +10770,7 @@ function startCompile(options) { console.log(import_chalk.default.red("ERROR:"), "Target not supported!"); return; } - compile(ir, new tg(target.output)); + compile(ir, new tg(target.output, ir.options)); }); } diff --git a/package.json b/package.json index b77968e..c613765 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,10 @@ "packageManager": "yarn@3.1.1", "scripts": { "start": "ts-node src/index.ts", - "test": "npm run start -- compile examples/example.jrpc --definition=examples/definition.json -o=ts-node:examples/out && ts-node examples/test.ts", + "test-start": "npm run start -- compile examples/example.jrpc --definition=examples/definition.json -o=ts-node:examples/Typescript/out -o=c#:examples/CSharp/Generated", + "test-csharp": "cd examples/CSharp/Example/ && dotnet run", + "test-typescript": "ts-node examples/test.ts", + "test": "npm run test-start && npm run test-csharp && npm run test-typescript", "build": "esbuild src/index.ts --bundle --platform=node --target=node14 --outfile=lib/jrpc.js", "prepublishOnly": "npm run build" }, @@ -22,6 +25,7 @@ ], "devDependencies": { "@types/debug": "^4.1.7", + "@types/fs-extra": "^9.0.13", "@types/node": "^17.0.5", "@types/prettier": "^2.4.2", "@types/yargs": "^17.0.8", @@ -32,5 +36,8 @@ "ts-node": "^10.4.0", "typescript": "^4.5.4", "yargs": "^17.3.1" + }, + "dependencies": { + "fs-extra": "^10.0.0" } } diff --git a/src/compile.ts b/src/compile.ts index 51ff9a0..2014e53 100644 --- a/src/compile.ts +++ b/src/compile.ts @@ -1,4 +1,5 @@ import * as FS from "fs"; +import * as FSE from "fs-extra" import * as Path from "path"; import { EnumDefinition, @@ -8,9 +9,9 @@ import { TypeDefinition, } from "./ir"; -export abstract class CompileTarget { +export abstract class CompileTarget { abstract name: string; - constructor(private outputFolder: string) { + constructor(private outputFolder: string, protected options: T) { if (!FS.existsSync(outputFolder)) { FS.mkdirSync(outputFolder, { recursive: true, @@ -57,6 +58,14 @@ export abstract class CompileTarget { return res.join("\n"); } + + protected loadTemplateFolder(name:string) { + let root = Path.join(__dirname, "../templates/", name); + + FSE.copySync(root, this.outputFolder, { + + }); + } } export default function compile(ir: IR, target: CompileTarget) { @@ -64,12 +73,12 @@ export default function compile(ir: IR, target: CompileTarget) { // setState("Building for target: " + target.name); target.start(); - ir.forEach((step) => { + ir.steps.forEach((step) => { const [type, def] = step; if (type == "type") target.generateType(def as TypeDefinition); else if (type == "enum") target.generateEnum(def as EnumDefinition); else if (type == "service") target.generateService(def as ServiceDefinition); }); - if (target.finalize) target.finalize(ir); + if (target.finalize) target.finalize(ir.steps); } diff --git a/src/ir.ts b/src/ir.ts index 6a635b4..dc2d810 100644 --- a/src/ir.ts +++ b/src/ir.ts @@ -61,7 +61,10 @@ export type Step = [ TypeDefinition | EnumDefinition | ServiceDefinition ]; -export type IR = Step[]; +export type IR = { + options: { [key:string]: string}, + steps: Step[] +}; export default function get_ir(parsed: Parsed): IR { log("Generatie IR from parse output"); @@ -72,6 +75,7 @@ export default function get_ir(parsed: Parsed): IR { // Verifiy and generating steps let steps: Step[] = []; + let options = {} as any; parsed.forEach((statement) => { log("Working on statement of type %s", statement.type); @@ -276,10 +280,15 @@ export default function get_ir(parsed: Parsed): IR { functions, } as ServiceDefinition, ]); + } else if(statement.type == "define") { + options[statement.key] = statement.value; } else { throw new IRError(statement, "Invalid statement!"); } }); - return steps; + return { + options, + steps + }; } diff --git a/src/parser.ts b/src/parser.ts index aed7546..618de89 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -67,11 +67,19 @@ export interface ServiceStatement extends DefinitionNode { functions: ServiceFunctionStatement[]; } +export interface DefineStatement extends DefinitionNode { + type: "define"; + key: string; + value: string; +} + export type RootStatementNode = | ImportStatement | TypeStatement | ServiceStatement - | EnumStatement; + | EnumStatement + | DefineStatement; + export type StatementNode = | RootStatementNode | TypeFieldStatement @@ -414,6 +422,22 @@ export default function parse(tokens: Token[], file: string): Parsed { }; }; + const parseDefine = (): DefineStatement => { + const idx = eatToken("define"); + + let [key] = eatText() + let [value] = eatText() + + eatToken(";"); + + return { + type :"define", + location: {file, idx}, + key, + value + } + } + const parseStatement = () => { if (currentToken.type === "keyword") { switch (currentToken.value) { @@ -427,6 +451,8 @@ export default function parse(tokens: Token[], file: string): Parsed { return parseEnumStatement(); case "service": return parseServiceStatement(); + case "define": + return parseDefine(); default: throw new ParserError( `Unknown keyword ${currentToken.value}`, diff --git a/src/process.ts b/src/process.ts index ba2f25d..cfa9661 100644 --- a/src/process.ts +++ b/src/process.ts @@ -6,18 +6,21 @@ import tokenize, { TokenizerError } from "./tokenizer"; import parse, { Parsed, ParserError } from "./parser"; import get_ir, { IR, IRError } from "./ir"; import compile, { CompileTarget } from "./compile"; -import { ESMTypescriptTarget, NodeJSTypescriptTarget } from "./targets/typescript"; +import { + ESMTypescriptTarget, + NodeJSTypescriptTarget, +} from "./targets/typescript"; +import { CSharpTarget } from "./targets/csharp"; class CatchedError extends Error {} const log = dbg("app"); +const Targets = new Map(); -const Targets = new Map(); - -Targets.set("ts-esm", ESMTypescriptTarget) -Targets.set("ts-node", NodeJSTypescriptTarget) - +Targets.set("ts-esm", ESMTypescriptTarget); +Targets.set("ts-node", NodeJSTypescriptTarget); +Targets.set("c#", CSharpTarget as typeof CompileTarget); function indexToLineAndCol(src: string, index: number) { let line = 1; @@ -77,7 +80,11 @@ type ProcessContext = { processedFiles: Set; }; -function processFile(ctx: ProcessContext, file: string, root = false): Parsed | null { +function processFile( + ctx: ProcessContext, + file: string, + root = false +): Parsed | null { file = Path.resolve(file); if (ctx.processedFiles.has(file)) { log("Skipping file %s since it has already be processed", file); @@ -99,7 +106,9 @@ function processFile(ctx: ProcessContext, file: string, root = false): Parsed | .map((statement) => { if (statement.type == "import") { const base = Path.dirname(file); - const resolved = Path.resolve(Path.join(base, statement.path + ".jrpc")); + const resolved = Path.resolve( + Path.join(base, statement.path + ".jrpc") + ); return processFile(ctx, resolved); } else { return statement; @@ -111,16 +120,14 @@ function processFile(ctx: ProcessContext, file: string, root = false): Parsed | } catch (err) { if (err instanceof TokenizerError) { printError(err, file, err.index); - if(!root) - throw new CatchedError(); + if (!root) throw new CatchedError(); } else if (err instanceof ParserError) { printError(err, file, err.token.startIdx); - if(!root) - throw new CatchedError(); - } else if(root && err instanceof CatchedError) { + if (!root) throw new CatchedError(); + } else if (root && err instanceof CatchedError) { return null; } else { - throw err; + throw err; } } } @@ -132,18 +139,21 @@ export default function startCompile(options: CompileOptions) { } as ProcessContext; let ir: IR | undefined = undefined; - if(options.input.endsWith(".json")) { + if (options.input.endsWith(".json")) { ir = JSON.parse(FS.readFileSync(options.input, "utf-8")); } else { const parsed = processFile(ctx, options.input, true); - if(!parsed) - process.exit(1); // Errors should have already been emitted + if (!parsed) process.exit(1); // Errors should have already been emitted try { - ir = get_ir(parsed); - } catch(err) { - if(err instanceof IRError) { - printError(err, err.statement.location.file, err.statement.location.idx); + ir = get_ir(parsed); + } catch (err) { + if (err instanceof IRError) { + printError( + err, + err.statement.location.file, + err.statement.location.idx + ); process.exit(1); } else { throw err; @@ -151,23 +161,25 @@ export default function startCompile(options: CompileOptions) { } } - if(!ir) - throw new Error("Error compiling: Cannot get IR"); + if (!ir) throw new Error("Error compiling: Cannot get IR"); - if(options.emitDefinitions) { - FS.writeFileSync(options.emitDefinitions, JSON.stringify(ir, undefined, 3)); + if (options.emitDefinitions) { + FS.writeFileSync( + options.emitDefinitions, + JSON.stringify(ir, undefined, 3) + ); } - if(options.targets.length <= 0) { + if (options.targets.length <= 0) { console.log(Color.yellow("WARNING:"), "No targets selected!"); } - options.targets.forEach(target => { - const tg = Targets.get(target.type) as any - if(!tg) { + options.targets.forEach((target) => { + const tg = Targets.get(target.type) as any; + if (!tg) { console.log(Color.red("ERROR:"), "Target not supported!"); return; } - compile(ir, new tg(target.output)); - }) + compile(ir, new tg(target.output, ir.options)); + }); } diff --git a/src/targets/csharp.ts b/src/targets/csharp.ts new file mode 100644 index 0000000..a46ff0a --- /dev/null +++ b/src/targets/csharp.ts @@ -0,0 +1,328 @@ +import { + TypeDefinition, + ServiceDefinition, + EnumDefinition, + TypeFieldDefinition, + Step, +} from "../ir"; + +import { CompileTarget } from "../compile"; + +type lineAppender = (ind: number, line: string | string[]) => void; + +const conversion = { + boolean: "bool", + number: "double", + string: "string", + void: "void", +}; + +function toCSharpType(type: string): string { + return (conversion as any)[type] || type; +} + +export class CSharpTarget extends CompileTarget<{ CSharp_Namespace: string }> { + name: string = "c#"; + + get namespace() { + return this.options.CSharp_Namespace || "JRPC"; + } + + start(): void { + this.writeFile( + this.namespace + ".csproj", + this.getTemplate("CSharp/CSharp.csproj") + ); + + const fixNS = (input: string) => + input.replace("__NAMESPACE__", this.namespace); + const copyClass = (name: string) => + this.writeFile( + name + ".cs", + fixNS(this.getTemplate(`CSharp/${name}.cs`)) + ); + copyClass("JRpcClient"); + copyClass("JRpcServer"); + copyClass("JRpcTransport"); + } + + generateType(definition: TypeDefinition): void { + let lines: string[] = []; + const a: lineAppender = (i, t) => { + if (!Array.isArray(t)) { + t = [t]; + } + t.forEach((l) => lines.push(" ".repeat(i) + l.trim())); + }; + + a(0, `using System.Text.Json;`); + a(0, `using System.Text.Json.Serialization;`); + a(0, `using System.Collections.Generic;`); + a(0, ``); + a(0, `namespace ${this.namespace};`); + a(0, ``); + a(0, `public class ${definition.name} {`); + for (const field of definition.fields) { + if (field.array) { + a( + 1, + `public IList<${toCSharpType(field.type)}>? ${ + field.name + } { get; set; }` + ); + } else if (field.map) { + a( + 1, + `public Dictionary<${toCSharpType(field.map)}, ${toCSharpType( + field.type + )}>? ${field.name} { get; set; }` + ); + } else { + a( + 1, + `public ${toCSharpType(field.type)}? ${field.name} { get; set; }` + ); + } + } + a(0, `}`); + + this.writeFile(`${definition.name}.cs`, lines.join("\n")); + } + + generateEnum(definition: EnumDefinition): void { + let lines: string[] = []; + const a: lineAppender = (i, t) => { + if (!Array.isArray(t)) { + t = [t]; + } + t.forEach((l) => lines.push(" ".repeat(i) + l.trim())); + }; + + a(0, `using System.Text.Json;`); + a(0, `using System.Text.Json.Serialization;`); + a(0, ``); + a(0, `namespace ${this.namespace};`); + a(0, ``); + a(0, `public enum ${definition.name} {`); + for (const field of definition.values) { + a(1, `${field.name} = ${field.value},`); + } + a(0, `}`); + + this.writeFile(`${definition.name}.cs`, lines.join("\n")); + } + + generateServiceClient(definition: ServiceDefinition) { + let lines: string[] = []; + const a: lineAppender = (i, t) => { + if (!Array.isArray(t)) { + t = [t]; + } + t.forEach((l) => lines.push(" ".repeat(i) + l.trim())); + }; + + a(0, `using System.Text.Json;`); + a(0, `using System.Text.Json.Serialization;`); + a(0, `using System.Text.Json.Nodes;`); + a(0, `using System.Threading.Tasks;`); + a(0, ``); + a(0, `namespace ${this.namespace};`); + a(0, ``); + a(0, `public class ${definition.name}Client {`); + a(0, ``); + a(1, `private JRpcClient Client;`); + a(0, ``); + a(1, `public ${definition.name}Client(JRpcClient client) {`); + a(2, `this.Client = client;`); + a(1, `}`); + a(0, ``); + for (const fnc of definition.functions) { + let params = fnc.inputs + .map((inp) => { + if (inp.array) { + return `List<${toCSharpType(inp.type)}> ${inp.name}`; + } else { + return `${toCSharpType(inp.type)} ${inp.name}`; + } + }) + .join(", "); + + const genParam = () => + a( + 2, + `var param = new JsonArray(${fnc.inputs + .map((e) => `JsonSerializer.SerializeToNode(${e.name})`) + .join(", ")});` + ); + + if (fnc.return) { + if (fnc.return.type == "void") { + a(1, `public async Task ${fnc.name}(${params}) {`); + genParam(); + a( + 2, + `await this.Client.SendRequestRaw("${definition.name}.${fnc.name}", param);` + ); + a(1, `}`); + } else { + let ret = fnc.return + ? fnc.return.array + ? `IList<${toCSharpType(fnc.return.type)}>` + : toCSharpType(fnc.return.type) + : undefined; + a(1, `public async Task<${ret}> ${fnc.name}(${params}) {`); + genParam(); + a( + 2, + `return await this.Client.SendRequest<${ret}>("${definition.name}.${fnc.name}", param);` + ); + a(1, `}`); + } + } else { + //Notification + a(1, `public void ${fnc.name}(${params}) {`); + genParam(); + a( + 2, + `this.Client.SendNotification("${definition.name}.${fnc.name}", param);` + ); + a(1, `}`); + } + a(1, ``); + } + // a(0, ``); + // a(0, ``); + // a(0, ``); + a(0, `}`); + + this.writeFile(`${definition.name}Client.cs`, lines.join("\n")); + } + + generateServiceServer(definition: ServiceDefinition) { + let lines: string[] = []; + const a: lineAppender = (i, t) => { + if (!Array.isArray(t)) { + t = [t]; + } + t.forEach((l) => lines.push(" ".repeat(i) + l.trim())); + }; + + a(0, `using System.Text.Json;`); + a(0, `using System.Text.Json.Serialization;`); + a(0, `using System.Text.Json.Nodes;`); + a(0, `using System.Threading.Tasks;`); + a(0, ``); + a(0, `namespace ${this.namespace};`); + a(0, ``); + a( + 0, + `public abstract class ${definition.name}Server : JRpcService {` + ); + + a(0, ``); + a(1, `public ${definition.name}Server() {`); + for (const fnc of definition.functions) { + a(2, `this.RegisterFunction("${fnc.name}");`); + } + a(1, `}`); + a(0, ``); + for (const fnc of definition.functions) { + let params = [ + ...fnc.inputs.map((inp) => { + if (inp.array) { + return `List<${toCSharpType(inp.type)}> ${inp.name}`; + } else { + return `${toCSharpType(inp.type)} ${inp.name}`; + } + }), + "TContext ctx", + ].join(", "); + + if (fnc.return) { + if (fnc.return.type == "void") { + a(1, `public abstract Task ${fnc.name}(${params});`); + } else { + let ret = fnc.return + ? fnc.return.array + ? `IList<${toCSharpType(fnc.return.type)}>` + : toCSharpType(fnc.return.type) + : undefined; + a(1, `public abstract Task<${ret}> ${fnc.name}(${params});`); + } + } else { + a(1, `public abstract void ${fnc.name}(${params});`); + } + } + a(0, ``); + a( + 1, + `public async override Task HandleRequest(string func, JsonNode param, TContext context) {` + ); + a(2, `switch(func) {`); + for (const fnc of definition.functions) { + a(3, `case "${fnc.name}": {`); + a(4, `if(param is JsonObject) {`); + a( + 5, + `var ja = new JsonArray(${fnc.inputs + .map((inp) => { + return `param["${inp.name}"]`; + }) + .join(", ")});` + ); + a(5, `param = ja;`); + a(4, `}`); + + let pref = ""; + if (fnc.return) { + if (fnc.return.type != "void") pref = "var result = await "; + else pref = "await "; + } + + a( + 4, + pref + + `this.${fnc.name}(${[ + ...fnc.inputs.map((inp, idx) => { + let type = inp.array + ? `List<${toCSharpType(inp.type)}>` + : `${toCSharpType(inp.type)}`; + return `param[${idx}]!.Deserialize<${type}>()`; + }), + "context", + ].join(", ")});` + ); + + if (fnc.return && fnc.return.type != "void") { + // if(fnc.return.type == "void") { + // a(3, `return null;`); + // } else { + // a(3, ``); + // } + a(4, `return JsonSerializer.SerializeToNode(result);`); + // a(3, ``); + } else { + a(4, `return null;`); + } + a(3, `}`); + a(0, ``); + } + a(3, `default:`); + a(4, `throw new Exception("Invalid Method!");`); + // a(0, ``); + // a(0, ``); + // a(0, ``); + a(2, `}`); + a(1, `}`); + a(0, `}`); + + this.writeFile(`${definition.name}Server.cs`, lines.join("\n")); + } + + generateService(definition: ServiceDefinition): void { + this.generateServiceClient(definition); + this.generateServiceServer(definition); + } + + finalize(steps: Step[]): void {} +} diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 952afd2..9750138 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -57,7 +57,7 @@ const matcher = [ regexMatcher(/^#.+/, "comment"), regexMatcher(/^".*?"/, "string"), // regexMatcher(/(?<=^")(.*?)(?=")/, "string"), - regexMatcher(/^(type|enum|import|service)\b/, "keyword"), + regexMatcher(/^(type|enum|import|service|define)\b/, "keyword"), regexMatcher(/^\@/, "at"), regexMatcher(/^\:/, "colon"), regexMatcher(/^\;/, "semicolon"), diff --git a/templates/CSharp/CSharp.csproj b/templates/CSharp/CSharp.csproj new file mode 100644 index 0000000..bafd05b --- /dev/null +++ b/templates/CSharp/CSharp.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/templates/CSharp/JRpcClient.cs b/templates/CSharp/JRpcClient.cs new file mode 100644 index 0000000..502be4b --- /dev/null +++ b/templates/CSharp/JRpcClient.cs @@ -0,0 +1,130 @@ +using System; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace __NAMESPACE__; + +public class JRpcClient +{ + private JRpcTransport Transport; + private IDictionary> Requests; + + public JRpcClient(JRpcTransport transport) + { + this.Transport = transport; + this.Requests = new Dictionary>(); + + this.Transport.OnPacket += this.HandlePacket; + } + + private void HandlePacket(string packet) + { + try + { + var parsed = JsonNode.Parse(packet); + if (parsed == null || (string?)parsed["jsonrpc"] != "2.0") + return; + + if (parsed["method"] != null) + { // Request or Notification + if (parsed["id"] != null) // Requests are not supported on the Client + return; + //TODO: implement Notifications + } + else if (parsed["id"] != null && parsed["method"] == null) + { + // Response + //TODO: Somehow remove this warning, since it has no meaning in this context. + // ID has to be something, that was checked before + var id = (string)parsed["id"]!; + + var task = this.Requests[id]; + if (task == null) + return; //This Request was not from here + + if (parsed["error"] != null) + { + var err = parsed["error"].Deserialize(); + if (err == null) + { + task.SetException(new JRpcException("Internal Server Error")); + } + else + { + task.SetException(err.ToException()); + } + } + else + { + task.SetResult(parsed["result"]); + } + } + else + { + // Ignoring invalid packet! + return; + } + } + catch (Exception) + { + //TODO: Maybe log exception, but don't break! + } + } + + public async Task SendRequestRaw(string method, JsonArray param) + { + var id = Guid.NewGuid().ToString(); + var request = new JsonObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["method"] = method, + ["params"] = param + }; + + var task = new TaskCompletionSource(); + this.Requests.Add(id, task); + await this.Transport.Write(request.ToJsonString()); + + return await task.Task; + } + + public async Task SendRequest(string method, JsonArray param) + { + var result = await this.SendRequestRaw(method, param); + return result.Deserialize(); + } + + public async void SendNotification(string method, JsonArray param) + { + var not = new JsonObject + { + ["jsonrpc"] = "2.0", + ["method"] = method, + ["params"] = param, + }; + + await this.Transport.Write(not.ToJsonString()); + } +} + + +class JRpcError +{ + public int code { get; set; } + public string? message { get; set; } + public JsonNode? data { get; set; } + + public JRpcException ToException() + { + return new JRpcException(this.message!); + } +} + +public class JRpcException : Exception +{ + public JRpcException(string message) : base(message) { } +} diff --git a/templates/CSharp/JRpcServer.cs b/templates/CSharp/JRpcServer.cs new file mode 100644 index 0000000..329ac21 --- /dev/null +++ b/templates/CSharp/JRpcServer.cs @@ -0,0 +1,196 @@ +using System; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace __NAMESPACE__; + +public class JRpcServer +{ + public IDictionary> Services; + + public JRpcServer() + { + this.Services = new Dictionary>(); + } + + public void AddService(string name, JRpcService service) + { + this.Services.Add(name, service); + } + + public JRpcServerSession GetSession(JRpcTransport transport, TContext context) + { + return new JRpcServerSession(this, transport, context); + } +} + +public class JRpcServerSession +{ + JRpcServer Server; + JRpcTransport Transport; + TContext Context; + + public JRpcServerSession(JRpcServer server, JRpcTransport transport, TContext context) + { + this.Server = server; + this.Transport = transport; + this.Context = context; + + this.Transport.OnPacket += this.HandlePacket; + } + + private void HandlePacket(string packet) + { + try + { + var parsed = JsonNode.Parse(packet); + if (parsed == null || (string?)parsed["jsonrpc"] != "2.0") + return; + + if (parsed["method"] != null) // Request or Notification + { + var id = (string?)parsed["id"]; + var splitted = ((string)parsed["method"]!).Split(".", 2); + var serviceName = splitted[0]; + var functionName = splitted[1]; + + if (serviceName == null || functionName == null) + { + if (id != null) + { + var response = new JsonObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = new JsonObject + { + ["code"] = -32700, + ["message"] = "Method not found!", + } + }; + _ = this.Transport.Write(response.ToJsonString()!); + } + return; + } + + var service = this.Server.Services[serviceName]; + if (service == null) + { + if (id != null) + { + var response = new JsonObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = new JsonObject + { + ["code"] = -32700, + ["message"] = "Method not found!", + } + }; + _ = this.Transport.Write(response.ToJsonString()!); + } + return; + } + + if (!service.Functions.Contains(functionName)) + { + if (id != null) + { + var response = new JsonObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = new JsonObject + { + ["code"] = -32700, + ["message"] = "Method not found!", + } + }; + _ = this.Transport.Write(response.ToJsonString()!); + } + return; + } + + if (!(parsed["params"] is JsonArray || parsed["params"] is JsonObject)) + { + if (id != null) + { + var response = new JsonObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = new JsonObject + { + ["code"] = -32602, + ["message"] = "Invalid Parameters!", + } + }; + _ = this.Transport.Write(response.ToJsonString()!); + } + return; + } + + _ = service.HandleRequest(functionName, parsed["params"]!, this.Context).ContinueWith(result => + { + if (id != null) + { + var response = new JsonObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = result.IsFaulted ? new JsonObject + { + ["code"] = -32603, + ["message"] = result.Exception!.InnerException!.Message + } : null, + ["result"] = !result.IsFaulted ? result.Result : null, + }; + _ = this.Transport.Write(response.ToJsonString()!); + } + }); + + + //TODO: implement Notifications + } + else + { + // Ignoring everyting else. Don't care! + return; + } + } + catch (Exception) + { + //TODO: Maybe log exception, but don't break! + } + } + + + public async void SendNotification(string method, JsonArray param) + { + var not = new JsonObject + { + ["jsonrpc"] = "2.0", + ["method"] = method, + ["params"] = param, + }; + + await this.Transport.Write(not.ToJsonString()); + } +} + +public abstract class JRpcService +{ + public HashSet Functions = new HashSet(); + + protected void RegisterFunction(string name) + { + this.Functions.Add(name); + } + + public abstract Task HandleRequest(string function, JsonNode param, TContext context); + // public abstract Task HandleNotification(string notification, JsonNode param); +} diff --git a/templates/CSharp/JRpcTransport.cs b/templates/CSharp/JRpcTransport.cs new file mode 100644 index 0000000..ee9709a --- /dev/null +++ b/templates/CSharp/JRpcTransport.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; + +namespace __NAMESPACE__; + +public delegate void NotifyPacket(string data); + +public abstract class JRpcTransport { + public event NotifyPacket OnPacket; + public abstract Task Write(string data); + + public void DevSendPacket(string data) { + this.OnPacket.Invoke(data); + } +} diff --git a/templates/ts_service_base.ts b/templates/ts_service_base.ts index 86e44e4..f67c7fe 100644 --- a/templates/ts_service_base.ts +++ b/templates/ts_service_base.ts @@ -9,10 +9,10 @@ export const Logging = { export enum ErrorCodes { ParseError = -32700, - InvalidRequest = -32700, - MethodNotFound = -32700, - InvalidParams = -32700, - InternalError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, } export interface RequestObject { diff --git a/yarn.lock b/yarn.lock index 5a4d18c..ac8e022 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26,12 +26,14 @@ __metadata: resolution: "@hibas123/jrpcgen@workspace:." dependencies: "@types/debug": ^4.1.7 + "@types/fs-extra": ^9.0.13 "@types/node": ^17.0.5 "@types/prettier": ^2.4.2 "@types/yargs": ^17.0.8 chalk: 4 debug: ^4.3.3 esbuild: ^0.14.10 + fs-extra: ^10.0.0 prettier: ^2.5.1 ts-node: ^10.4.0 typescript: ^4.5.4 @@ -78,6 +80,15 @@ __metadata: languageName: node linkType: hard +"@types/fs-extra@npm:^9.0.13": + version: 9.0.13 + resolution: "@types/fs-extra@npm:9.0.13" + dependencies: + "@types/node": "*" + checksum: add79e212acd5ac76b97b9045834e03a7996aef60a814185e0459088fd290519a3c1620865d588fa36c4498bf614210d2a703af5cf80aa1dbc125db78f6edac3 + languageName: node + linkType: hard + "@types/ms@npm:*": version: 0.7.31 resolution: "@types/ms@npm:0.7.31" @@ -85,6 +96,13 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:*": + version: 17.0.8 + resolution: "@types/node@npm:17.0.8" + checksum: f4cadeb9e602027520abc88c77142697e33cf6ac98bb02f8b595a398603cbd33df1f94d01c055c9f13cde0c8eaafc5e396ca72645458d42b4318b845bc7f1d0f + languageName: node + linkType: hard + "@types/node@npm:^17.0.5": version: 17.0.5 resolution: "@types/node@npm:17.0.5" @@ -422,6 +440,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:^10.0.0": + version: 10.0.0 + resolution: "fs-extra@npm:10.0.0" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^6.0.1 + universalify: ^2.0.0 + checksum: 5285a3d8f34b917cf2b66af8c231a40c1623626e9d701a20051d3337be16c6d7cac94441c8b3732d47a92a2a027886ca93c69b6a4ae6aee3c89650d2a8880c0a + languageName: node + linkType: hard + "get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" @@ -429,6 +458,13 @@ __metadata: languageName: node linkType: hard +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0": + version: 4.2.9 + resolution: "graceful-fs@npm:4.2.9" + checksum: 68ea4e07ff2c041ada184f9278b830375f8e0b75154e3f080af6b70f66172fabb4108d19b3863a96b53fc068a310b9b6493d86d1291acc5f3861eb4b79d26ad6 + languageName: node + linkType: hard + "has-flag@npm:^4.0.0": version: 4.0.0 resolution: "has-flag@npm:4.0.0" @@ -443,6 +479,19 @@ __metadata: languageName: node linkType: hard +"jsonfile@npm:^6.0.1": + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" + dependencies: + graceful-fs: ^4.1.6 + universalify: ^2.0.0 + dependenciesMeta: + graceful-fs: + optional: true + checksum: 7af3b8e1ac8fe7f1eccc6263c6ca14e1966fcbc74b618d3c78a0a2075579487547b94f72b7a1114e844a1e15bb00d440e5d1720bfc4612d790a6f285d5ea8354 + languageName: node + linkType: hard + "make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" @@ -558,6 +607,13 @@ __metadata: languageName: node linkType: hard +"universalify@npm:^2.0.0": + version: 2.0.0 + resolution: "universalify@npm:2.0.0" + checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44 + languageName: node + linkType: hard + "wrap-ansi@npm:^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0"