mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-05-16 17:21:24 +00:00
Combine js and ts backends (#307)
This commit is contained in:
parent
f27ae5eda9
commit
4e63da83f5
5
.github/workflows/test_branch.yml
vendored
5
.github/workflows/test_branch.yml
vendored
@ -51,13 +51,8 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
git clone https://github.com/fluencelabs/aqua-playground.git
|
git clone https://github.com/fluencelabs/aqua-playground.git
|
||||||
cd aqua-playground
|
cd aqua-playground
|
||||||
rm -rf src/compiled/examples/*
|
|
||||||
npm i
|
npm i
|
||||||
cd ..
|
cd ..
|
||||||
sbt "cli/run -i aqua-playground/aqua/examples -o aqua-playground/src/compiled/examples -m aqua-playground/node_modules -c \"UNIQUE_CONST = 1\" -c \"ANOTHER_CONST = \\\"ab\\\"\""
|
|
||||||
cd aqua-playground
|
|
||||||
npm run examples
|
|
||||||
cd ..
|
|
||||||
sbt "cliJS/fastOptJS"
|
sbt "cliJS/fastOptJS"
|
||||||
rm -rf aqua-playground/src/compiled/examples/*
|
rm -rf aqua-playground/src/compiled/examples/*
|
||||||
node cli/.js/target/scala-3.0.2/cli-fastopt.js -i aqua-playground/aqua/examples -o aqua-playground/src/compiled/examples -m aqua-playground/node_modules -c "UNIQUE_CONST = 1" -c "ANOTHER_CONST = \"ab\""
|
node cli/.js/target/scala-3.0.2/cli-fastopt.js -i aqua-playground/aqua/examples -o aqua-playground/src/compiled/examples -m aqua-playground/node_modules -c "UNIQUE_CONST = 1" -c "ANOTHER_CONST = \"ab\""
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
package aqua.backend.js
|
|
||||||
|
|
||||||
import aqua.backend.{Backend, Generated}
|
|
||||||
import aqua.model.transform.res.AquaRes
|
|
||||||
import cats.data.NonEmptyChain
|
|
||||||
|
|
||||||
object JavaScriptBackend extends Backend {
|
|
||||||
|
|
||||||
val ext = ".js"
|
|
||||||
|
|
||||||
override def generate(res: AquaRes): Seq[Generated] =
|
|
||||||
if (res.isEmpty) Nil else Generated(ext, JavaScriptFile(res).generate) :: Nil
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package aqua.backend.js
|
|
||||||
|
|
||||||
import aqua.backend.air.FuncAirGen
|
|
||||||
import aqua.model.transform.res.FuncRes
|
|
||||||
import aqua.types.*
|
|
||||||
import cats.syntax.show.*
|
|
||||||
|
|
||||||
object JavaScriptCommon {
|
|
||||||
|
|
||||||
// TODO: handle cases if there is already peer_ or config_ variable defined
|
|
||||||
def fixupArgName(arg: String): String =
|
|
||||||
if (arg == "peer" || arg == "config") {
|
|
||||||
arg + "_"
|
|
||||||
} else {
|
|
||||||
arg
|
|
||||||
}
|
|
||||||
|
|
||||||
def callBackExprBody(at: ArrowType, callbackName: String): String = {
|
|
||||||
val arrowArgumentsToCallbackArgumentsList =
|
|
||||||
at.domain.toList.zipWithIndex
|
|
||||||
.map((`type`, idx) => {
|
|
||||||
val valueFromArg = s"req.args[$idx]"
|
|
||||||
`type` match {
|
|
||||||
case OptionType(t) =>
|
|
||||||
s"${valueFromArg}.length === 0 ? null : ${valueFromArg}[0]"
|
|
||||||
case _ => valueFromArg
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.concat(List("callParams"))
|
|
||||||
.mkString(", ")
|
|
||||||
|
|
||||||
val callCallbackStatement = s"$callbackName($arrowArgumentsToCallbackArgumentsList)"
|
|
||||||
|
|
||||||
val callCallbackStatementAndReturn =
|
|
||||||
at.res.fold(s"${callCallbackStatement}; resp.result = {}")(`type` =>
|
|
||||||
`type` match {
|
|
||||||
case OptionType(t) => s"""
|
|
||||||
| var respResult = ${callCallbackStatement};
|
|
||||||
| resp.result = respResult === null ? [] : [respResult]
|
|
||||||
|""".stripMargin
|
|
||||||
case _ => s"resp.result = ${callCallbackStatement}"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
val tetraplets = FuncRes
|
|
||||||
.arrowArgs(at)
|
|
||||||
.zipWithIndex
|
|
||||||
.map((x, idx) => {
|
|
||||||
s"${x.name}: req.tetraplets[${idx}]"
|
|
||||||
})
|
|
||||||
.mkString(",")
|
|
||||||
|
|
||||||
s"""
|
|
||||||
| const callParams = {
|
|
||||||
| ...req.particleContext,
|
|
||||||
| tetraplets: {
|
|
||||||
| ${tetraplets}
|
|
||||||
| },
|
|
||||||
| };
|
|
||||||
| resp.retCode = ResultCodes.success;
|
|
||||||
| ${callCallbackStatementAndReturn}
|
|
||||||
|""".stripMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package aqua.backend.js
|
|
||||||
|
|
||||||
import aqua.backend.Version
|
|
||||||
import aqua.model.transform.res.AquaRes
|
|
||||||
|
|
||||||
case class JavaScriptFile(res: AquaRes) {
|
|
||||||
|
|
||||||
import JavaScriptFile.Header
|
|
||||||
|
|
||||||
def generate: String =
|
|
||||||
s"""${Header}
|
|
||||||
|
|
|
||||||
|function missingFields(obj, fields) {
|
|
||||||
| return fields.filter(f => !(f in obj))
|
|
||||||
|}
|
|
||||||
|
|
|
||||||
|// Services
|
|
||||||
|${res.services.map(JavaScriptService(_)).map(_.generate).toList.mkString("\n\n")}
|
|
||||||
|
|
|
||||||
|// Functions
|
|
||||||
|${res.funcs.map(JavaScriptFunc(_)).map(_.generate).toList.mkString("\n\n")}
|
|
||||||
|""".stripMargin
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
object JavaScriptFile {
|
|
||||||
|
|
||||||
val Header: String =
|
|
||||||
s"""/**
|
|
||||||
| *
|
|
||||||
| * This file is auto-generated. Do not edit manually: changes may be erased.
|
|
||||||
| * Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
|
|
||||||
| * If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
|
||||||
| * Aqua version: ${Version.version}
|
|
||||||
| *
|
|
||||||
| */
|
|
||||||
|import { Fluence, FluencePeer } from '@fluencelabs/fluence';
|
|
||||||
|import {
|
|
||||||
| ResultCodes,
|
|
||||||
| RequestFlow,
|
|
||||||
| RequestFlowBuilder,
|
|
||||||
| CallParams,
|
|
||||||
|} from '@fluencelabs/fluence/dist/internal/compilerSupport/v1.js';
|
|
||||||
|""".stripMargin
|
|
||||||
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
package aqua.backend.js
|
|
||||||
|
|
||||||
import aqua.backend.air.FuncAirGen
|
|
||||||
import aqua.model.transform.res.FuncRes
|
|
||||||
import aqua.types.*
|
|
||||||
import cats.syntax.show.*
|
|
||||||
|
|
||||||
case class JavaScriptFunc(func: FuncRes) {
|
|
||||||
|
|
||||||
import JavaScriptCommon._
|
|
||||||
import FuncRes._
|
|
||||||
import func._
|
|
||||||
|
|
||||||
private def returnCallback: String =
|
|
||||||
val respBody = func.returnType match {
|
|
||||||
case Some(x) => x match {
|
|
||||||
case OptionType(_) =>
|
|
||||||
""" let [opt] = args;
|
|
||||||
| if (Array.isArray(opt)) {
|
|
||||||
| if (opt.length === 0) { resolve(null); }
|
|
||||||
| opt = opt[0];
|
|
||||||
| }
|
|
||||||
| return resolve(opt);""".stripMargin
|
|
||||||
case pt: ProductType =>
|
|
||||||
val unwrapOpts = pt.toList.zipWithIndex.collect { case (OptionType(_), i) =>
|
|
||||||
s"""
|
|
||||||
| if(Array.isArray(opt[$i])) {
|
|
||||||
| if (opt[$i].length === 0) { opt[$i] = null; }
|
|
||||||
| else {opt[$i] = opt[$i][0]; }
|
|
||||||
| }""".stripMargin
|
|
||||||
}.mkString
|
|
||||||
|
|
||||||
s""" let opt = args;
|
|
||||||
|$unwrapOpts
|
|
||||||
| return resolve(opt);""".stripMargin
|
|
||||||
case _ =>
|
|
||||||
""" const [res] = args;
|
|
||||||
| resolve(res);""".stripMargin
|
|
||||||
}
|
|
||||||
case None => ""
|
|
||||||
}
|
|
||||||
s"""h.onEvent('$callbackServiceId', '$respFuncName', (args) => {
|
|
||||||
| $respBody
|
|
||||||
|});
|
|
||||||
|""".stripMargin
|
|
||||||
|
|
||||||
def generate: String = {
|
|
||||||
|
|
||||||
val jsAir = FuncAirGen(func).generate
|
|
||||||
|
|
||||||
val setCallbacks = func.args.collect { // Product types are not handled
|
|
||||||
case Arg(argName, OptionType(_)) =>
|
|
||||||
s"""h.on('$dataServiceId', '$argName', () => {return ${fixupArgName(argName)} === null ? [] : [${fixupArgName(argName)}];});"""
|
|
||||||
case Arg(argName, _: DataType) =>
|
|
||||||
s"""h.on('$dataServiceId', '$argName', () => {return ${fixupArgName(argName)};});"""
|
|
||||||
case Arg(argName, at: ArrowType) =>
|
|
||||||
s"""
|
|
||||||
| h.use((req, resp, next) => {
|
|
||||||
| if(req.serviceId === '${conf.callbackService}' && req.fnName === '$argName') {
|
|
||||||
| ${callBackExprBody(at, argName)}
|
|
||||||
| }
|
|
||||||
| next();
|
|
||||||
| });
|
|
||||||
""".stripMargin
|
|
||||||
}
|
|
||||||
.mkString("\n")
|
|
||||||
|
|
||||||
val returnVal =
|
|
||||||
func.returnType.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")
|
|
||||||
|
|
||||||
val clientArgName = genArgName("client")
|
|
||||||
val configArgName = genArgName("config")
|
|
||||||
|
|
||||||
val funcName = s"${func.funcName}"
|
|
||||||
|
|
||||||
val argsLets = args.map(arg => s"let ${fixupArgName(arg.name)};").mkString("\n")
|
|
||||||
|
|
||||||
val argsFormAssingment = args
|
|
||||||
.map(arg => fixupArgName(arg.name))
|
|
||||||
.concat(List("config"))
|
|
||||||
.zipWithIndex
|
|
||||||
|
|
||||||
// Argument unpacking has two forms:
|
|
||||||
// One starting from the first (by index) argument,
|
|
||||||
// One starting from zero
|
|
||||||
val argsAssignmentStartingFrom1 = argsFormAssingment.map((name, ix) => s"${name} = args[${ix + 1}];").mkString("\n")
|
|
||||||
val argsAssignmentStartingFrom0 = argsFormAssingment.map((name, ix) => s"${name} = args[${ix}];").mkString("\n")
|
|
||||||
|
|
||||||
s"""
|
|
||||||
| export function ${func.funcName}(...args) {
|
|
||||||
| let peer;
|
|
||||||
| ${argsLets}
|
|
||||||
| let config;
|
|
||||||
| if (FluencePeer.isInstance(args[0])) {
|
|
||||||
| peer = args[0];
|
|
||||||
| ${argsAssignmentStartingFrom1}
|
|
||||||
| } else {
|
|
||||||
| peer = Fluence.getPeer();
|
|
||||||
| ${argsAssignmentStartingFrom0}
|
|
||||||
| }
|
|
||||||
|
|
|
||||||
| let request;
|
|
||||||
| const promise = new Promise((resolve, reject) => {
|
|
||||||
| const r = new RequestFlowBuilder()
|
|
||||||
| .disableInjections()
|
|
||||||
| .withRawScript(
|
|
||||||
| `
|
|
||||||
| ${jsAir.show}
|
|
||||||
| `,
|
|
||||||
| )
|
|
||||||
| .configHandler((h) => {
|
|
||||||
| ${conf.relayVarName.fold("") { r =>
|
|
||||||
s"""h.on('${conf.getDataService}', '$r', () => {
|
|
||||||
| return peer.getStatus().relayPeerId;
|
|
||||||
| });""".stripMargin }}
|
|
||||||
| $setCallbacks
|
|
||||||
| $returnCallback
|
|
||||||
| h.onEvent('${conf.errorHandlingService}', '${conf.errorFuncName}', (args) => {
|
|
||||||
| const [err] = args;
|
|
||||||
| reject(err);
|
|
||||||
| });
|
|
||||||
| })
|
|
||||||
| .handleScriptError(reject)
|
|
||||||
| .handleTimeout(() => {
|
|
||||||
| reject('Request timed out for ${func.funcName}');
|
|
||||||
| })
|
|
||||||
| if(${configArgName} && ${configArgName}.ttl) {
|
|
||||||
| r.withTTL(${configArgName}.ttl)
|
|
||||||
| }
|
|
||||||
| request = r.build();
|
|
||||||
| });
|
|
||||||
| peer.internals.initiateFlow(request);
|
|
||||||
| return ${returnVal};
|
|
||||||
|}
|
|
||||||
""".stripMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
22
backend/ts/src/main/scala/aqua/backend/Header.scala
Normal file
22
backend/ts/src/main/scala/aqua/backend/Header.scala
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package aqua.backend
|
||||||
|
|
||||||
|
object Header {
|
||||||
|
|
||||||
|
def header(isJs: Boolean): String =
|
||||||
|
s"""/**
|
||||||
|
| *
|
||||||
|
| * This file is auto-generated. Do not edit manually: changes may be erased.
|
||||||
|
| * Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
|
||||||
|
| * If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
||||||
|
| * Aqua version: ${Version.version}
|
||||||
|
| *
|
||||||
|
| */
|
||||||
|
|import { Fluence, FluencePeer } from '@fluencelabs/fluence';
|
||||||
|
|import {
|
||||||
|
| ResultCodes,
|
||||||
|
| RequestFlow,
|
||||||
|
| RequestFlowBuilder,
|
||||||
|
| CallParams,
|
||||||
|
|} from '@fluencelabs/fluence/dist/internal/compilerSupport/v1${if (isJs) ".js" else ""}';
|
||||||
|
|""".stripMargin
|
||||||
|
}
|
33
backend/ts/src/main/scala/aqua/backend/OutputFile.scala
Normal file
33
backend/ts/src/main/scala/aqua/backend/OutputFile.scala
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package aqua.backend
|
||||||
|
|
||||||
|
import aqua.backend.ts.{TSFuncTypes, TSServiceTypes}
|
||||||
|
import aqua.backend.{Header, OutputService}
|
||||||
|
import aqua.model.transform.res.AquaRes
|
||||||
|
|
||||||
|
case class OutputFile(res: AquaRes) {
|
||||||
|
|
||||||
|
def generate(types: Types): String = {
|
||||||
|
import types.*
|
||||||
|
val services = res.services
|
||||||
|
.map(s => OutputService(s, types))
|
||||||
|
.map(_.generate)
|
||||||
|
.toList
|
||||||
|
.mkString("\n\n")
|
||||||
|
val functions =
|
||||||
|
res.funcs.map(f => OutputFunc(f, types)).map(_.generate).toList.mkString("\n\n")
|
||||||
|
s"""${Header.header(false)}
|
||||||
|
|
|
||||||
|
|function ${typed(
|
||||||
|
s"""missingFields(${typed("obj", "any")}, ${typed("fields", "string[]")})""",
|
||||||
|
"string[]")} {
|
||||||
|
| return fields.filter(f => !(f in obj))
|
||||||
|
|}
|
||||||
|
|
|
||||||
|
|// Services
|
||||||
|
|$services
|
||||||
|
|// Functions
|
||||||
|
|$functions
|
||||||
|
|""".stripMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,15 +1,26 @@
|
|||||||
package aqua.backend.ts
|
package aqua.backend
|
||||||
|
|
||||||
import aqua.backend.air.FuncAirGen
|
import aqua.backend.air.FuncAirGen
|
||||||
|
import aqua.backend.ts.TypeScriptCommon.{callBackExprBody, fixupArgName}
|
||||||
|
import aqua.backend.ts.{TSFuncTypes, TypeScriptCommon}
|
||||||
import aqua.model.transform.res.FuncRes
|
import aqua.model.transform.res.FuncRes
|
||||||
import aqua.types.*
|
import aqua.model.transform.res.FuncRes.Arg
|
||||||
|
import aqua.types.{ArrowType, DataType, OptionType, ProductType}
|
||||||
import cats.syntax.show.*
|
import cats.syntax.show.*
|
||||||
|
|
||||||
case class TypeScriptFunc(func: FuncRes) {
|
case class OutputFunc(func: FuncRes, types: Types) {
|
||||||
|
|
||||||
import TypeScriptCommon._
|
import FuncRes.*
|
||||||
import FuncRes._
|
import TypeScriptCommon.*
|
||||||
import func._
|
import types.*
|
||||||
|
import func.*
|
||||||
|
val funcTypes = types.funcType(func)
|
||||||
|
import funcTypes.*
|
||||||
|
|
||||||
|
val argsFormAssingment = args
|
||||||
|
.map(arg => fixupArgName(arg.name))
|
||||||
|
.appended("config")
|
||||||
|
.zipWithIndex
|
||||||
|
|
||||||
private def returnCallback: String =
|
private def returnCallback: String =
|
||||||
val respBody = func.returnType match {
|
val respBody = func.returnType match {
|
||||||
@ -29,7 +40,7 @@ case class TypeScriptFunc(func: FuncRes) {
|
|||||||
| }""".stripMargin
|
| }""".stripMargin
|
||||||
}.mkString
|
}.mkString
|
||||||
|
|
||||||
s""" let opt: any = args;
|
s""" let ${typed("opt", "any")} = args;
|
||||||
|$unwrapOpts
|
|$unwrapOpts
|
||||||
| return resolve(opt);""".stripMargin
|
| return resolve(opt);""".stripMargin
|
||||||
case _ =>
|
case _ =>
|
||||||
@ -46,9 +57,6 @@ case class TypeScriptFunc(func: FuncRes) {
|
|||||||
|
|
||||||
val tsAir = FuncAirGen(func).generate
|
val tsAir = FuncAirGen(func).generate
|
||||||
|
|
||||||
val retTypeTs = func.returnType
|
|
||||||
.fold("void")(typeToTs)
|
|
||||||
|
|
||||||
val setCallbacks = func.args.collect { // Product types are not handled
|
val setCallbacks = func.args.collect { // Product types are not handled
|
||||||
case Arg(argName, OptionType(_)) =>
|
case Arg(argName, OptionType(_)) =>
|
||||||
s""" h.on('$dataServiceId', '$argName', () => {return ${fixupArgName(argName)} === null ? [] : [${fixupArgName(argName)}];});"""
|
s""" h.on('$dataServiceId', '$argName', () => {return ${fixupArgName(argName)} === null ? [] : [${fixupArgName(argName)}];});"""
|
||||||
@ -68,27 +76,11 @@ case class TypeScriptFunc(func: FuncRes) {
|
|||||||
val returnVal =
|
val returnVal =
|
||||||
func.returnType.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")
|
func.returnType.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")
|
||||||
|
|
||||||
val clientArgName = genArgName("client")
|
|
||||||
val configArgName = genArgName("config")
|
val configArgName = genArgName("config")
|
||||||
|
|
||||||
val configType = "{ttl?: number}"
|
val codeLeftSpace = " " * 20
|
||||||
|
|
||||||
val funcName = s"${func.funcName}"
|
val argsLets = args.map(arg => s" let ${typed(fixupArgName(arg.name), "any")};").mkString("\n")
|
||||||
|
|
||||||
val argsTypescript = args
|
|
||||||
.map(arg => s"${fixupArgName(arg.name)}: " + typeToTs(arg.`type`))
|
|
||||||
.concat(List(s"config?: $configType"))
|
|
||||||
|
|
||||||
// defines different types for overloaded service registration function.
|
|
||||||
var funcTypeOverload1 = argsTypescript.mkString(", ")
|
|
||||||
var funcTypeOverload2 = ("peer: FluencePeer" :: argsTypescript).mkString(", ")
|
|
||||||
|
|
||||||
val argsLets = args.map(arg => s" let ${fixupArgName(arg.name)}: any;").mkString("\n")
|
|
||||||
|
|
||||||
val argsFormAssingment = args
|
|
||||||
.map(arg => fixupArgName(arg.name))
|
|
||||||
.concat(List("config"))
|
|
||||||
.zipWithIndex
|
|
||||||
|
|
||||||
// argument upnacking has two forms.
|
// argument upnacking has two forms.
|
||||||
// One starting from the first (by index) argument,
|
// One starting from the first (by index) argument,
|
||||||
@ -96,17 +88,11 @@ case class TypeScriptFunc(func: FuncRes) {
|
|||||||
val argsAssignmentStartingFrom1 = argsFormAssingment.map((name, ix) => s" ${name} = args[${ix + 1}];").mkString("\n")
|
val argsAssignmentStartingFrom1 = argsFormAssingment.map((name, ix) => s" ${name} = args[${ix + 1}];").mkString("\n")
|
||||||
val argsAssignmentStartingFrom0 = argsFormAssingment.map((name, ix) => s" ${name} = args[${ix}];").mkString("\n")
|
val argsAssignmentStartingFrom0 = argsFormAssingment.map((name, ix) => s" ${name} = args[${ix}];").mkString("\n")
|
||||||
|
|
||||||
val funcTypeRes = s"Promise<$retTypeTs>"
|
s"""${funcTypes.generate}
|
||||||
|
|export function ${func.funcName}(${typed("...args", "any")}) {
|
||||||
val codeLeftSpace = " " * 20
|
| let ${typed("peer", "FluencePeer")};
|
||||||
|
|
||||||
s"""
|
|
||||||
|export function ${func.funcName}(${funcTypeOverload1}) : ${funcTypeRes};
|
|
||||||
|export function ${func.funcName}(${funcTypeOverload2}) : ${funcTypeRes};
|
|
||||||
|export function ${func.funcName}(...args: any) {
|
|
||||||
| let peer: FluencePeer;
|
|
||||||
|${argsLets}
|
|${argsLets}
|
||||||
| let config: any;
|
| let ${typed("config", "any")};
|
||||||
| if (FluencePeer.isInstance(args[0])) {
|
| if (FluencePeer.isInstance(args[0])) {
|
||||||
| peer = args[0];
|
| peer = args[0];
|
||||||
|${argsAssignmentStartingFrom1}
|
|${argsAssignmentStartingFrom1}
|
||||||
@ -115,8 +101,8 @@ case class TypeScriptFunc(func: FuncRes) {
|
|||||||
|${argsAssignmentStartingFrom0}
|
|${argsAssignmentStartingFrom0}
|
||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| let request: RequestFlow;
|
| let ${typed("request", "RequestFlow")};
|
||||||
| const promise = new Promise<$retTypeTs>((resolve, reject) => {
|
| const promise = new ${generic("Promise", retTypeTs._2)}((resolve, reject) => {
|
||||||
| const r = new RequestFlowBuilder()
|
| const r = new RequestFlowBuilder()
|
||||||
| .disableInjections()
|
| .disableInjections()
|
||||||
| .withRawScript(`
|
| .withRawScript(`
|
||||||
@ -146,7 +132,7 @@ case class TypeScriptFunc(func: FuncRes) {
|
|||||||
|
|
|
|
||||||
| request = r.build();
|
| request = r.build();
|
||||||
| });
|
| });
|
||||||
| peer.internals.initiateFlow(request!);
|
| peer.internals.initiateFlow(${bang("request")});
|
||||||
| return ${returnVal};
|
| return ${returnVal};
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
}
|
}
|
@ -1,21 +1,21 @@
|
|||||||
package aqua.backend.js
|
package aqua.backend
|
||||||
|
|
||||||
import aqua.backend.air.FuncAirGen
|
import aqua.backend.ts.TypeScriptCommon.callBackExprBody
|
||||||
import aqua.model.transform.res.FuncRes
|
import aqua.backend.ts.TypeScriptCommon
|
||||||
import aqua.types.*
|
|
||||||
import cats.syntax.show.*
|
|
||||||
import aqua.model.transform.res.ServiceRes
|
import aqua.model.transform.res.ServiceRes
|
||||||
|
import aqua.types.ArrowType
|
||||||
|
|
||||||
case class JavaScriptService(srv: ServiceRes) {
|
case class OutputService(srv: ServiceRes, types: Types) {
|
||||||
|
|
||||||
import JavaScriptCommon._
|
import TypeScriptCommon.*
|
||||||
|
import types.*
|
||||||
|
val serviceTypes = types.serviceType(srv)
|
||||||
|
import serviceTypes.*
|
||||||
|
|
||||||
def fnHandler(arrow: ArrowType, memberName: String) = {
|
def fnHandler(arrow: ArrowType, memberName: String) = {
|
||||||
s"""
|
s"""if (req.fnName === '${memberName}') {
|
||||||
| if (req.fnName === '${memberName}') {
|
|${callBackExprBody(arrow, "service." + memberName, 12)}
|
||||||
| ${callBackExprBody(arrow, "service." + memberName)}
|
}""".stripMargin
|
||||||
| }
|
|
||||||
""".stripMargin
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate: String =
|
def generate: String =
|
||||||
@ -25,22 +25,21 @@ case class JavaScriptService(srv: ServiceRes) {
|
|||||||
}
|
}
|
||||||
.mkString("\n\n")
|
.mkString("\n\n")
|
||||||
|
|
||||||
val registerName = s"register${srv.name}"
|
|
||||||
|
|
||||||
val defaultServiceIdBranch = srv.defaultId.fold("")(x =>
|
val defaultServiceIdBranch = srv.defaultId.fold("")(x =>
|
||||||
s"""
|
s"""else {
|
||||||
| else {
|
|
||||||
| serviceId = ${x}
|
| serviceId = ${x}
|
||||||
|}""".stripMargin
|
| }""".stripMargin
|
||||||
)
|
)
|
||||||
|
|
||||||
val membersNames = srv.members.map(_._1)
|
val membersNames = srv.members.map(_._1)
|
||||||
|
|
||||||
s"""
|
s"""
|
||||||
|export function ${registerName}(...args) {
|
|${serviceTypes.generate}
|
||||||
| let peer;
|
|
|
||||||
| let serviceId;
|
|export function register${srv.name}(${typed("...args", "any")}) {
|
||||||
| let service;
|
| let ${typed("peer", "FluencePeer")};
|
||||||
|
| let ${typed("serviceId", "any")};
|
||||||
|
| let ${typed("service", "any")};
|
||||||
| if (FluencePeer.isInstance(args[0])) {
|
| if (FluencePeer.isInstance(args[0])) {
|
||||||
| peer = args[0];
|
| peer = args[0];
|
||||||
| } else {
|
| } else {
|
||||||
@ -66,8 +65,8 @@ case class JavaScriptService(srv: ServiceRes) {
|
|||||||
| service = args[2];
|
| service = args[2];
|
||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| const incorrectServiceDefinitions = missingFields(service, [${membersNames.map { n => s"'$n'"}.mkString(", ")}]);
|
| const incorrectServiceDefinitions = missingFields(service, [${membersNames.map { n => s"'$n'" }.mkString(", ")}]);
|
||||||
| if (!incorrectServiceDefinitions.length) {
|
| if (!!incorrectServiceDefinitions.length) {
|
||||||
| throw new Error("Error registering service ${srv.name}: missing functions: " + incorrectServiceDefinitions.map((d) => "'" + d + "'").join(", "))
|
| throw new Error("Error registering service ${srv.name}: missing functions: " + incorrectServiceDefinitions.map((d) => "'" + d + "'").join(", "))
|
||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
@ -77,10 +76,10 @@ case class JavaScriptService(srv: ServiceRes) {
|
|||||||
| return;
|
| return;
|
||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
|${fnHandlers}
|
| ${fnHandlers}
|
||||||
|
|
|
|
||||||
| next();
|
| next();
|
||||||
| });
|
| });
|
||||||
| }
|
|}
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
}
|
}
|
36
backend/ts/src/main/scala/aqua/backend/Types.scala
Normal file
36
backend/ts/src/main/scala/aqua/backend/Types.scala
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package aqua.backend
|
||||||
|
|
||||||
|
import aqua.backend.ts.{TSFuncTypes, TSServiceTypes}
|
||||||
|
import aqua.model.transform.res.{FuncRes, ServiceRes}
|
||||||
|
|
||||||
|
trait Types {
|
||||||
|
def typed(field: String, `type`: String): String
|
||||||
|
def generic(field: String, `type`: String): String
|
||||||
|
def bang(field: String): String
|
||||||
|
def funcType(f: FuncRes): FuncTypes
|
||||||
|
def serviceType(s: ServiceRes): ServiceTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
trait FuncTypes {
|
||||||
|
def retTypeTs: (Option[String], String)
|
||||||
|
def generate: String
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ServiceTypes {
|
||||||
|
def generate: String
|
||||||
|
}
|
||||||
|
|
||||||
|
object EmptyTypes extends Types {
|
||||||
|
override def typed(field: String, `type`: String): String = field
|
||||||
|
override def generic(field: String, `type`: String): String = field
|
||||||
|
override def bang(field: String): String = field
|
||||||
|
override def funcType(f: FuncRes): FuncTypes = new FuncTypes {
|
||||||
|
override def retTypeTs: (Option[String], String) = (None, "")
|
||||||
|
override def generate: String = ""
|
||||||
|
}
|
||||||
|
override def serviceType(s: ServiceRes): ServiceTypes = new ServiceTypes {
|
||||||
|
override def generate: String = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
|||||||
|
package aqua.backend.js
|
||||||
|
|
||||||
|
import aqua.backend.ts.TypeScriptTypes
|
||||||
|
import aqua.backend.{Backend, EmptyTypes, Generated, Header, OutputFile, OutputFunc, OutputService}
|
||||||
|
import aqua.model.transform.res.AquaRes
|
||||||
|
|
||||||
|
object JavaScriptBackend extends Backend {
|
||||||
|
|
||||||
|
val ext = ".js"
|
||||||
|
val tsExt = ".d.ts"
|
||||||
|
|
||||||
|
def typesFile(res: AquaRes): Generated = {
|
||||||
|
val services = res.services
|
||||||
|
.map(s => TypeScriptTypes.serviceType(s))
|
||||||
|
.map(_.generate)
|
||||||
|
.toList
|
||||||
|
.mkString("\n\n")
|
||||||
|
val functions =
|
||||||
|
res.funcs.map(f => TypeScriptTypes.funcType(f)).map(_.generate).toList.mkString("\n\n")
|
||||||
|
|
||||||
|
val body = s"""${Header.header(false)}
|
||||||
|
|
|
||||||
|
|// Services
|
||||||
|
|$services
|
||||||
|
|
|
||||||
|
|// Functions
|
||||||
|
|$functions
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
Generated(tsExt, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def generate(res: AquaRes): Seq[Generated] =
|
||||||
|
if (res.isEmpty) Nil
|
||||||
|
else {
|
||||||
|
Generated(ext, OutputFile(res).generate(EmptyTypes)):: typesFile(res) :: Nil
|
||||||
|
}
|
||||||
|
}
|
37
backend/ts/src/main/scala/aqua/backend/ts/TSFuncTypes.scala
Normal file
37
backend/ts/src/main/scala/aqua/backend/ts/TSFuncTypes.scala
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package aqua.backend.ts
|
||||||
|
|
||||||
|
import aqua.backend.FuncTypes
|
||||||
|
import aqua.backend.ts.TypeScriptCommon.{fixupArgName, typeToTs, genTypeName}
|
||||||
|
import aqua.model.transform.res.FuncRes
|
||||||
|
import aqua.types.*
|
||||||
|
|
||||||
|
case class TSFuncTypes(func: FuncRes) extends FuncTypes {
|
||||||
|
import TypeScriptTypes._
|
||||||
|
|
||||||
|
override val retTypeTs = func.returnType
|
||||||
|
.fold((None, "void")) { t => genTypeName(t, func.funcName.capitalize + "Result") }
|
||||||
|
|
||||||
|
override def generate = {
|
||||||
|
val configType = "?: {ttl?: number}"
|
||||||
|
|
||||||
|
val argsTypescript = func.args
|
||||||
|
.map { arg =>
|
||||||
|
val (typeDesc, t) = genTypeName(arg.`type`, func.funcName.capitalize + "Arg" + arg.name.capitalize)
|
||||||
|
(typeDesc, s"${typed(fixupArgName(arg.name), t)}")
|
||||||
|
} :+ (None, s"config$configType")
|
||||||
|
|
||||||
|
val args = argsTypescript.map(_._2)
|
||||||
|
val argsDesc = argsTypescript.map(_._1).flatten
|
||||||
|
|
||||||
|
// defines different types for overloaded service registration function.
|
||||||
|
var funcTypeOverload1 = args.mkString(", ")
|
||||||
|
var funcTypeOverload2 = (typed("peer", "FluencePeer") :: args).mkString(", ")
|
||||||
|
|
||||||
|
val (resTypeDesc, resType) = retTypeTs
|
||||||
|
|
||||||
|
s"""${argsDesc.mkString("\n")}
|
||||||
|
|${resTypeDesc.getOrElse("")}
|
||||||
|
|export function ${func.funcName}(${funcTypeOverload1}): ${generic("Promise", resType)};
|
||||||
|
|export function ${func.funcName}(${funcTypeOverload2}): ${generic("Promise", resType)};""".stripMargin
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package aqua.backend.ts
|
||||||
|
|
||||||
|
import aqua.backend.ServiceTypes
|
||||||
|
import aqua.backend.ts.TypeScriptCommon.fnDef
|
||||||
|
import aqua.model.transform.res.ServiceRes
|
||||||
|
|
||||||
|
case class TSServiceTypes(srv: ServiceRes) extends ServiceTypes {
|
||||||
|
import TypeScriptTypes._
|
||||||
|
|
||||||
|
private val serviceTypeName = s"${srv.name}Def";
|
||||||
|
|
||||||
|
def registerServiceArgs = {
|
||||||
|
|
||||||
|
// defined arguments used in overloads below
|
||||||
|
val peerDecl = s"${typed("peer", "FluencePeer")}";
|
||||||
|
val serviceIdDecl = s"${typed("serviceId", "string")}";
|
||||||
|
val serviceDecl = s"${typed("service", serviceTypeName)}"
|
||||||
|
|
||||||
|
// Service registration functions has several overloads.
|
||||||
|
// Depending on whether the the service has the default id or not
|
||||||
|
// there would be different number of overloads
|
||||||
|
// This variable contain defines the list of lists where
|
||||||
|
// the outmost list describes the list of overloads
|
||||||
|
// and the innermost one defines the list of arguments in the overload
|
||||||
|
val registerServiceArgsSource = srv.defaultId.fold(
|
||||||
|
List(
|
||||||
|
List(serviceIdDecl, serviceDecl),
|
||||||
|
List(peerDecl, serviceIdDecl, serviceDecl)
|
||||||
|
)
|
||||||
|
)(_ =>
|
||||||
|
List(
|
||||||
|
List(serviceDecl),
|
||||||
|
List(serviceIdDecl, serviceDecl),
|
||||||
|
List(peerDecl, serviceDecl),
|
||||||
|
List(peerDecl, serviceIdDecl, serviceDecl)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Service registration functions has several overloads.
|
||||||
|
// Depending on whether the the service has the default id or not
|
||||||
|
// there would be different number of overloads
|
||||||
|
// This variable contain defines the list of lists where
|
||||||
|
// the outmost list describes the list of overloads
|
||||||
|
// and the innermost one defines the list of arguments in the overload
|
||||||
|
registerServiceArgsSource.map { x =>
|
||||||
|
val args = x.mkString(", ")
|
||||||
|
s"export function register${srv.name}(${args}): void;"
|
||||||
|
}
|
||||||
|
.mkString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
def exportInterface = {
|
||||||
|
val fnDefs = srv.members.map { case (name, arrow) =>
|
||||||
|
s"${typed(name, fnDef(arrow))};"
|
||||||
|
}
|
||||||
|
.mkString("\n")
|
||||||
|
|
||||||
|
s"""export interface ${serviceTypeName} {
|
||||||
|
| ${fnDefs}
|
||||||
|
|}""".stripMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate = {
|
||||||
|
s"""$exportInterface
|
||||||
|
|$registerServiceArgs
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.backend.ts
|
package aqua.backend.ts
|
||||||
|
|
||||||
import aqua.backend.{Backend, Generated}
|
import aqua.backend.{Backend, Generated, OutputFile}
|
||||||
import aqua.model.transform.res.AquaRes
|
import aqua.model.transform.res.AquaRes
|
||||||
import cats.data.NonEmptyChain
|
import cats.data.NonEmptyChain
|
||||||
|
|
||||||
@ -9,5 +9,5 @@ object TypeScriptBackend extends Backend {
|
|||||||
val ext = ".ts"
|
val ext = ".ts"
|
||||||
|
|
||||||
override def generate(res: AquaRes): Seq[Generated] =
|
override def generate(res: AquaRes): Seq[Generated] =
|
||||||
if (res.isEmpty) Nil else Generated(ext, TypeScriptFile(res).generate) :: Nil
|
if (res.isEmpty) Nil else Generated(ext, OutputFile(res).generate(TypeScriptTypes)) :: Nil
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,20 @@ import cats.syntax.show.*
|
|||||||
|
|
||||||
object TypeScriptCommon {
|
object TypeScriptCommon {
|
||||||
|
|
||||||
|
def genTypeName(t: Type, name: String): (Option[String], String) = {
|
||||||
|
val genType = typeToTs(t)
|
||||||
|
t match {
|
||||||
|
case tt: ProductType =>
|
||||||
|
val gen = s"export type $name = $genType"
|
||||||
|
(Some(gen), name)
|
||||||
|
case tt: StructType =>
|
||||||
|
val gen = s"export type $name = $genType"
|
||||||
|
(Some(gen), name)
|
||||||
|
case _ => (None, genType)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def typeToTs(t: Type): String = t match {
|
def typeToTs(t: Type): String = t match {
|
||||||
case OptionType(t) => typeToTs(t) + " | null"
|
case OptionType(t) => typeToTs(t) + " | null"
|
||||||
case ArrayType(t) => typeToTs(t) + "[]"
|
case ArrayType(t) => typeToTs(t) + "[]"
|
||||||
@ -14,7 +28,7 @@ object TypeScriptCommon {
|
|||||||
case pt: ProductType =>
|
case pt: ProductType =>
|
||||||
"[" + pt.toList.map(typeToTs).mkString(", ") + "]"
|
"[" + pt.toList.map(typeToTs).mkString(", ") + "]"
|
||||||
case st: StructType =>
|
case st: StructType =>
|
||||||
s"{ ${st.fields.map(typeToTs).toNel.map(kv => kv._1 + ": " + kv._2).toList.mkString("; ")} }"
|
s"{ ${st.fields.map(typeToTs).toNel.map(kv => kv._1 + ": " + kv._2 + ";").toList.mkString(" ")} }"
|
||||||
case st: ScalarType if ScalarType.number(st) => "number"
|
case st: ScalarType if ScalarType.number(st) => "number"
|
||||||
case ScalarType.bool => "boolean"
|
case ScalarType.bool => "boolean"
|
||||||
case ScalarType.string => "string"
|
case ScalarType.string => "string"
|
||||||
@ -37,8 +51,7 @@ object TypeScriptCommon {
|
|||||||
at.res.fold("void")(typeToTs)
|
at.res.fold("void")(typeToTs)
|
||||||
|
|
||||||
def fnDef(at: ArrowType): String =
|
def fnDef(at: ArrowType): String =
|
||||||
val args = argsToTs(at)
|
val args = (argsToTs(at) :+ callParamsArg(at))
|
||||||
.concat(List(callParamsArg(at)))
|
|
||||||
.mkString(", ")
|
.mkString(", ")
|
||||||
|
|
||||||
val retType = returnType(at)
|
val retType = returnType(at)
|
||||||
@ -63,9 +76,6 @@ object TypeScriptCommon {
|
|||||||
}
|
}
|
||||||
s"callParams: CallParams<${generic}>"
|
s"callParams: CallParams<${generic}>"
|
||||||
|
|
||||||
def argsCallToTs(at: ArrowType): List[String] =
|
|
||||||
FuncRes.arrowArgIndices(at).map(idx => s"args[$idx]")
|
|
||||||
|
|
||||||
def callBackExprBody(at: ArrowType, callbackName: String, leftSpace: Int): String = {
|
def callBackExprBody(at: ArrowType, callbackName: String, leftSpace: Int): String = {
|
||||||
val arrowArgumentsToCallbackArgumentsList =
|
val arrowArgumentsToCallbackArgumentsList =
|
||||||
at.domain.toList
|
at.domain.toList
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
package aqua.backend.ts
|
|
||||||
|
|
||||||
import aqua.backend.Version
|
|
||||||
import aqua.model.transform.res.AquaRes
|
|
||||||
|
|
||||||
case class TypeScriptFile(res: AquaRes) {
|
|
||||||
|
|
||||||
import TypeScriptFile.Header
|
|
||||||
|
|
||||||
def generate: String =
|
|
||||||
s"""${Header}
|
|
||||||
|
|
|
||||||
|function missingFields(obj: any, fields: string[]): string[] {
|
|
||||||
| return fields.filter(f => !(f in obj))
|
|
||||||
|}
|
|
||||||
|
|
|
||||||
|// Services
|
|
||||||
|${res.services.map(TypeScriptService(_)).map(_.generate).toList.mkString("\n\n")}
|
|
||||||
|// Functions
|
|
||||||
|${res.funcs.map(TypeScriptFunc(_)).map(_.generate).toList.mkString("\n\n")}
|
|
||||||
|""".stripMargin
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
object TypeScriptFile {
|
|
||||||
|
|
||||||
val Header: String =
|
|
||||||
s"""/**
|
|
||||||
| *
|
|
||||||
| * This file is auto-generated. Do not edit manually: changes may be erased.
|
|
||||||
| * Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
|
|
||||||
| * If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
|
||||||
| * Aqua version: ${Version.version}
|
|
||||||
| *
|
|
||||||
| */
|
|
||||||
|import { Fluence, FluencePeer } from '@fluencelabs/fluence';
|
|
||||||
|import {
|
|
||||||
| ResultCodes,
|
|
||||||
| RequestFlow,
|
|
||||||
| RequestFlowBuilder,
|
|
||||||
| CallParams,
|
|
||||||
|} from '@fluencelabs/fluence/dist/internal/compilerSupport/v1';
|
|
||||||
|""".stripMargin
|
|
||||||
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
package aqua.backend.ts
|
|
||||||
|
|
||||||
import aqua.backend.air.FuncAirGen
|
|
||||||
import aqua.model.transform.res.FuncRes
|
|
||||||
import aqua.types.*
|
|
||||||
import cats.syntax.show.*
|
|
||||||
import aqua.model.transform.res.ServiceRes
|
|
||||||
|
|
||||||
case class TypeScriptService(srv: ServiceRes) {
|
|
||||||
|
|
||||||
import TypeScriptCommon._
|
|
||||||
|
|
||||||
def fnHandler(arrow: ArrowType, memberName: String) = {
|
|
||||||
s"""if (req.fnName === '${memberName}') {
|
|
||||||
|${callBackExprBody(arrow, "service." + memberName, 12)}
|
|
||||||
}""".stripMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
def generate: String =
|
|
||||||
val fnHandlers = srv.members
|
|
||||||
.map{ case (name, arrow) =>
|
|
||||||
fnHandler(arrow, name)
|
|
||||||
}
|
|
||||||
.mkString("\n\n")
|
|
||||||
|
|
||||||
val fnDefs = srv.members
|
|
||||||
.map{ case (name, arrow) =>
|
|
||||||
s"${name}: ${fnDef(arrow)};"
|
|
||||||
}
|
|
||||||
.mkString("\n")
|
|
||||||
|
|
||||||
val serviceTypeName = s"${srv.name}Def";
|
|
||||||
|
|
||||||
val registerName = s"register${srv.name}"
|
|
||||||
|
|
||||||
// defined arguments used in overloads below
|
|
||||||
val peerDecl = "peer: FluencePeer";
|
|
||||||
val serviceIdDecl = "serviceId: string";
|
|
||||||
val serviceDecl = s"service: ${serviceTypeName}"
|
|
||||||
|
|
||||||
// Service registration functions has several overloads.
|
|
||||||
// Depending on whether the the service has the default id or not
|
|
||||||
// there would be different number of overloads
|
|
||||||
// This variable contain defines the list of lists where
|
|
||||||
// the outmost list describes the list of overloads
|
|
||||||
// and the innermost one defines the list of arguments in the overload
|
|
||||||
val registerServiceArgsSource = srv.defaultId.fold(
|
|
||||||
List(
|
|
||||||
List(serviceIdDecl, serviceDecl),
|
|
||||||
List(peerDecl, serviceIdDecl, serviceDecl)
|
|
||||||
)
|
|
||||||
)(_ =>
|
|
||||||
List(
|
|
||||||
List(serviceDecl),
|
|
||||||
List(serviceIdDecl, serviceDecl),
|
|
||||||
List(peerDecl, serviceDecl),
|
|
||||||
List(peerDecl, serviceIdDecl, serviceDecl),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Service registration functions has several overloads.
|
|
||||||
// Depending on whether the the service has the default id or not
|
|
||||||
// there would be different number of overloads
|
|
||||||
// This variable contain defines the list of lists where
|
|
||||||
// the outmost list describes the list of overloads
|
|
||||||
// and the innermost one defines the list of arguments in the overload
|
|
||||||
val registerServiceArgs = registerServiceArgsSource.map{ x =>
|
|
||||||
val args = x.mkString(", ")
|
|
||||||
s"export function ${registerName}(${args}): void;"
|
|
||||||
}
|
|
||||||
.mkString("\n");
|
|
||||||
|
|
||||||
val defaultServiceIdBranch = srv.defaultId.fold("")(x =>
|
|
||||||
s"""else {
|
|
||||||
| serviceId = ${x}
|
|
||||||
| }""".stripMargin
|
|
||||||
)
|
|
||||||
|
|
||||||
val membersNames = srv.members.map(_._1)
|
|
||||||
|
|
||||||
s"""
|
|
||||||
|export interface ${serviceTypeName} {
|
|
||||||
| ${fnDefs}
|
|
||||||
|}
|
|
||||||
|
|
|
||||||
|$registerServiceArgs
|
|
||||||
|export function ${registerName}(...args: any) {
|
|
||||||
| let peer: FluencePeer;
|
|
||||||
| let serviceId: any;
|
|
||||||
| let service: any;
|
|
||||||
| if (FluencePeer.isInstance(args[0])) {
|
|
||||||
| peer = args[0];
|
|
||||||
| } else {
|
|
||||||
| peer = Fluence.getPeer();
|
|
||||||
| }
|
|
||||||
|
|
|
||||||
| if (typeof args[0] === 'string') {
|
|
||||||
| serviceId = args[0];
|
|
||||||
| } else if (typeof args[1] === 'string') {
|
|
||||||
| serviceId = args[1];
|
|
||||||
| } ${defaultServiceIdBranch}
|
|
||||||
|
|
|
||||||
| // Figuring out which overload is the service.
|
|
||||||
| // If the first argument is not Fluence Peer and it is an object, then it can only be the service def
|
|
||||||
| // If the first argument is peer, we are checking further. The second argument might either be
|
|
||||||
| // an object, that it must be the service object
|
|
||||||
| // or a string, which is the service id. In that case the service is the third argument
|
|
||||||
| if (!(FluencePeer.isInstance(args[0])) && typeof args[0] === 'object') {
|
|
||||||
| service = args[0];
|
|
||||||
| } else if (typeof args[1] === 'object') {
|
|
||||||
| service = args[1];
|
|
||||||
| } else {
|
|
||||||
| service = args[2];
|
|
||||||
| }
|
|
||||||
|
|
|
||||||
| const incorrectServiceDefinitions = missingFields(service, [${membersNames.map { n => s"'$n'" }.mkString(", ")}]);
|
|
||||||
| if (!!incorrectServiceDefinitions.length) {
|
|
||||||
| throw new Error("Error registering service ${srv.name}: missing functions: " + incorrectServiceDefinitions.map((d) => "'" + d + "'").join(", "))
|
|
||||||
| }
|
|
||||||
|
|
|
||||||
| peer.internals.callServiceHandler.use((req, resp, next) => {
|
|
||||||
| if (req.serviceId !== serviceId) {
|
|
||||||
| next();
|
|
||||||
| return;
|
|
||||||
| }
|
|
||||||
|
|
|
||||||
| ${fnHandlers}
|
|
||||||
|
|
|
||||||
| next();
|
|
||||||
| });
|
|
||||||
|}
|
|
||||||
""".stripMargin
|
|
||||||
}
|
|
@ -0,0 +1,12 @@
|
|||||||
|
package aqua.backend.ts
|
||||||
|
|
||||||
|
import aqua.backend.{FuncTypes, ServiceTypes, Types}
|
||||||
|
import aqua.model.transform.res.{FuncRes, ServiceRes}
|
||||||
|
|
||||||
|
object TypeScriptTypes extends Types {
|
||||||
|
override def typed(field: String, t: String): String = s"$field: $t"
|
||||||
|
override def generic(field: String, t: String): String = s"$field<$t>"
|
||||||
|
override def bang(field: String): String = s"$field!"
|
||||||
|
def funcType(f: FuncRes): FuncTypes = TSFuncTypes(f)
|
||||||
|
def serviceType(s: ServiceRes): ServiceTypes = TSServiceTypes(s)
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package aqua.backend.ts
|
||||||
|
|
||||||
|
import aqua.backend.Header
|
||||||
|
import aqua.model.transform.res.AquaRes
|
||||||
|
|
||||||
|
case class TypeScriptTypesFile(res: AquaRes) {
|
||||||
|
def generate: String =
|
||||||
|
s"""${Header.header(false)}
|
||||||
|
|
|
||||||
|
|// Services
|
||||||
|
|${res.services.map(TSServiceTypes(_)).map(_.generate).toList.mkString("\n\n")}
|
||||||
|
|
|
||||||
|
|// Functions
|
||||||
|
|${res.funcs.map(TSFuncTypes(_)).map(_.generate).toList.mkString("\n\n")}
|
||||||
|
|""".stripMargin
|
||||||
|
}
|
@ -52,7 +52,7 @@ lazy val cli = crossProject(JSPlatform, JVMPlatform)
|
|||||||
"co.fs2" %%% "fs2-io" % fs2V
|
"co.fs2" %%% "fs2-io" % fs2V
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.dependsOn(compiler, `backend-air`, `backend-ts`, `backend-js`)
|
.dependsOn(compiler, `backend-air`, `backend-ts`)
|
||||||
|
|
||||||
lazy val cliJS = cli.js
|
lazy val cliJS = cli.js
|
||||||
.settings(
|
.settings(
|
||||||
@ -166,10 +166,3 @@ lazy val `backend-ts` = crossProject(JVMPlatform, JSPlatform)
|
|||||||
.in(file("backend/ts"))
|
.in(file("backend/ts"))
|
||||||
.settings(commons: _*)
|
.settings(commons: _*)
|
||||||
.dependsOn(`backend-air`)
|
.dependsOn(`backend-air`)
|
||||||
|
|
||||||
lazy val `backend-js` = crossProject(JVMPlatform, JSPlatform)
|
|
||||||
.withoutSuffixFor(JVMPlatform)
|
|
||||||
.crossType(CrossType.Pure)
|
|
||||||
.in(file("backend/js"))
|
|
||||||
.settings(commons: _*)
|
|
||||||
.dependsOn(`backend-air`)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user