/** * Copyright 2023 Fluence Labs Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import type { FnConfig, FunctionCallDef, ServiceDef, PassedArgs, ServiceImpl, } from "@fluencelabs/interfaces"; import { getArgumentTypes } from "@fluencelabs/interfaces"; import { FluencePeer } from "./jsPeer/FluencePeer.js"; import { callAquaFunction, Fluence, registerService } from "./index.js"; export const isFluencePeer = ( fluencePeerCandidate: unknown, ): fluencePeerCandidate is FluencePeer => { return fluencePeerCandidate instanceof FluencePeer; }; /** * Convenience function to support Aqua `func` generation backend * The compiler only need to generate a call the function and provide the corresponding definitions and the air script * * @param rawFnArgs - raw arguments passed by user to the generated function * @param def - function definition generated by the Aqua compiler * @param script - air script with function execution logic generated by the Aqua compiler */ export const v5_callFunction = async ( rawFnArgs: unknown[], def: FunctionCallDef, script: string, ): Promise => { const { args, client: peer, config } = extractFunctionArgs(rawFnArgs, def); return callAquaFunction({ args, def, script, config, peer, }); }; /** * Convenience function to support Aqua `service` generation backend * The compiler only need to generate a call the function and provide the corresponding definitions and the air script * @param args - raw arguments passed by user to the generated function * TODO: dont forget to add jsdoc for new arg * @param def - service definition generated by the Aqua compiler */ export const v5_registerService = (args: unknown[], def: ServiceDef): void => { // TODO: Support this in aqua-to-js package // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const service: ServiceImpl = args.pop() as ServiceImpl; const { peer, serviceId } = extractServiceArgs(args, def.defaultServiceId); registerService({ def, service, serviceId, peer, }); }; function isConfig(arg: unknown): arg is FnConfig { return typeof arg === "object" && arg !== null; } /** * Arguments could be passed in one these configurations: * [...actualArgs] * [peer, ...actualArgs] * [...actualArgs, config] * [peer, ...actualArgs, config] * * This function select the appropriate configuration and returns * arguments in a structured way of: { peer, config, args } */ function extractFunctionArgs( args: unknown[], def: FunctionCallDef, ): { client: FluencePeer; config: FnConfig; args: PassedArgs; } { const argumentTypes = getArgumentTypes(def); const argumentNames = Object.keys(argumentTypes); const numberOfExpectedArgs = argumentNames.length; let peer: FluencePeer; let config: FnConfig; if (isFluencePeer(args[0])) { peer = args[0]; args = args.slice(1); } else { if (Fluence.defaultClient == null) { throw new Error( "Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?", ); } peer = Fluence.defaultClient; } const maybeConfig = args[numberOfExpectedArgs]; if (isConfig(maybeConfig)) { config = maybeConfig; } else { config = {}; } const structuredArgs = args.slice(0, numberOfExpectedArgs); if (structuredArgs.length !== numberOfExpectedArgs) { throw new Error( `Incorrect number of arguments. Expecting ${numberOfExpectedArgs}`, ); } const argsRes = argumentNames.reduce((acc, name, index) => { return { ...acc, [name]: structuredArgs[index] }; }, {}); return { client: peer, args: argsRes, config: config, }; } /** * Arguments could be passed in one these configurations: * [serviceObject] * [peer, serviceObject] * [defaultId, serviceObject] * [peer, defaultId, serviceObject] * * Where serviceObject is the raw object with function definitions passed by user * * This function select the appropriate configuration and returns * arguments in a structured way of: { peer, serviceId, service } */ const extractServiceArgs = ( args: unknown[], defaultServiceId?: string, ): { peer: FluencePeer; serviceId: string | undefined; } => { let peer: FluencePeer; let serviceId: string | undefined; if (isFluencePeer(args[0])) { peer = args[0]; args = args.slice(1); } else { if (Fluence.defaultClient == null) { throw new Error( "Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?", ); } peer = Fluence.defaultClient; } if (typeof args[0] === "string") { serviceId = args[0]; } else { serviceId = defaultServiceId; } return { peer, serviceId, }; };