feat: Simplify JS Client public API (#257)

This commit is contained in:
Pavel
2023-02-16 14:38:48 +03:00
committed by GitHub
parent 35dfb1bfbc
commit 9daaf41096
24 changed files with 774 additions and 476 deletions

View File

@ -19,7 +19,7 @@ import type { IFluenceClient } from '@fluencelabs/interfaces';
import { getArgumentTypes } from '@fluencelabs/interfaces';
import { isFluencePeer } from '@fluencelabs/interfaces';
import { getDefaultPeer } from '../util.js';
import { getFluenceInterface } from '../util.js';
/**
* Convenience function to support Aqua `func` generation backend
@ -30,12 +30,20 @@ import { getDefaultPeer } from '../util.js';
* @param script - air script with function execution logic generated by the Aqua compiler
*/
export const callFunction = async (rawFnArgs: Array<any>, def: FunctionCallDef, script: string): Promise<unknown> => {
const { args, peer, config } = await extractFunctionArgs(rawFnArgs, def);
return peer.compilerSupport.callFunction({
const { args, client: peer, config } = await extractFunctionArgs(rawFnArgs, def);
if (peer.internals.getConnectionState() !== 'connected') {
throw new Error(
'Could not call the Aqua function because client is disconnected. Did you forget to call Fluence.connect()?',
);
}
const fluence = await getFluenceInterface();
return fluence.callAquaFunction({
args,
def,
script,
config: config || {},
peer: peer,
});
};
@ -47,10 +55,21 @@ export const callFunction = async (rawFnArgs: Array<any>, def: FunctionCallDef,
*/
export const registerService = async (args: any[], def: ServiceDef): Promise<unknown> => {
const { peer, service, serviceId } = await extractServiceArgs(args, def.defaultServiceId);
return peer.compilerSupport.registerService({
// TODO: TBH service registration is just putting some stuff into a hashmap
// there should not be such a check at all
if (peer.internals.getConnectionState() !== 'connected') {
throw new Error(
'Could not register Aqua service because the client is disconnected. Did you forget to call Fluence.connect()?',
);
}
const fluence = await getFluenceInterface();
return fluence.registerService({
def,
service,
serviceId,
peer,
});
};
@ -68,7 +87,7 @@ const extractFunctionArgs = async (
args: any[],
def: FunctionCallDef,
): Promise<{
peer: IFluenceClient;
client: IFluenceClient;
config?: FnConfig;
args: { [key: string]: any };
}> => {
@ -84,7 +103,8 @@ const extractFunctionArgs = async (
structuredArgs = args.slice(1, numberOfExpectedArgs + 1);
config = args[numberOfExpectedArgs + 1];
} else {
peer = await getDefaultPeer();
const fluence = await getFluenceInterface();
peer = fluence.defaultClient;
structuredArgs = args.slice(0, numberOfExpectedArgs);
config = args[numberOfExpectedArgs];
}
@ -96,7 +116,7 @@ const extractFunctionArgs = async (
const argsRes = argumentNames.reduce((acc, name, index) => ({ ...acc, [name]: structuredArgs[index] }), {});
return {
peer: peer,
client: peer,
config: config,
args: argsRes,
};
@ -124,7 +144,8 @@ const extractServiceArgs = async (
if (isFluencePeer(args[0])) {
peer = args[0];
} else {
peer = await getDefaultPeer();
const fluence = await getFluenceInterface();
peer = fluence.defaultClient;
}
if (typeof args[0] === 'string') {

View File

@ -1,5 +1,11 @@
import { getDefaultPeer } from './util.js';
import type { IFluenceClient, ClientOptions } from '@fluencelabs/interfaces';
import { getFluenceInterface, getFluenceInterfaceFromGlobalThis } from './util.js';
import {
IFluenceClient,
ClientOptions,
RelayOptions,
ConnectionState,
ConnectionStates,
} from '@fluencelabs/interfaces';
export type { IFluenceClient, ClientOptions, CallParams } from '@fluencelabs/interfaces';
export {
@ -30,33 +36,56 @@ export {
} from './compilerSupport/implementation.js';
/**
* Public interface to Fluence JS
* Public interface to Fluence Network
*/
export const Fluence = {
/**
* Initializes the default peer: starts the Aqua VM, initializes the default call service handlers
* and (optionally) connect to the Fluence network
* @param options - object specifying peer configuration
* Connect to the Fluence network
* @param relay - relay node to connect to
* @param options - client options
*/
start: async (options?: ClientOptions): Promise<void> => {
const peer = await getDefaultPeer();
return peer.start(options);
connect: async (relay: RelayOptions, options?: ClientOptions): Promise<void> => {
const fluence = await getFluenceInterface();
return fluence.defaultClient.connect(relay, options);
},
/**
* Un-initializes the default peer: stops all the underlying workflows, stops the Aqua VM
* and disconnects from the Fluence network
* Disconnect from the Fluence network
*/
stop: async (): Promise<void> => {
const peer = await getDefaultPeer();
return peer.stop();
disconnect: async (): Promise<void> => {
const fluence = await getFluenceInterface();
return fluence.defaultClient.disconnect();
},
/**
* Get the default peer instance
* @returns the default peer instance
* Handle connection state changes. Immediately returns the current connection state
*/
getPeer: async (): Promise<IFluenceClient> => {
return getDefaultPeer();
onConnectionStateChange(handler: (state: ConnectionState) => void): ConnectionState {
const optimisticResult = getFluenceInterfaceFromGlobalThis();
if (optimisticResult) {
return optimisticResult.defaultClient.onConnectionStateChange(handler);
}
getFluenceInterface().then((fluence) => fluence.defaultClient.onConnectionStateChange(handler));
return 'disconnected';
},
/**
* Low level API. Get the underlying client instance which holds the connection to the network
* @returns IFluenceClient instance
*/
getClient: async (): Promise<IFluenceClient> => {
const fluence = await getFluenceInterface();
return fluence.defaultClient;
},
};
/**
* Low level API. Generally you need Fluence.connect() instead.
* @returns IFluenceClient instance
*/
export const createClient = async (): Promise<IFluenceClient> => {
const fluence = await getFluenceInterface();
return fluence.clientFactory();
};

View File

@ -1,12 +1,23 @@
import type { IFluenceClient } from '@fluencelabs/interfaces';
import type { CallAquaFunction, IFluenceClient, RegisterService } from '@fluencelabs/interfaces';
const getPeerFromGlobalThis = (): IFluenceClient | undefined => {
// @ts-ignore
return globalThis.defaultPeer;
type PublicFluenceInterface = {
clientFactory: () => IFluenceClient;
defaultClient: IFluenceClient;
callAquaFunction: CallAquaFunction;
registerService: RegisterService;
};
// TODO: DXJ-271
const REJECT_MESSAGE = 'You probably forgot to add script tag. Read about it here: ';
export const getFluenceInterfaceFromGlobalThis = (): PublicFluenceInterface | undefined => {
// @ts-ignore
return globalThis.fluence;
};
// TODO: fix link DXJ-271
const REJECT_MESSAGE = `Could not load Fluence JS Client library.
If you are using Node.js that probably means that you forgot in install or import the @fluencelabs/js-client.node package.
If you are using a browser, then you probably forgot to add the <script> tag to your HTML.
Please refer to the documentation page for more details: https://fluence.dev/
`;
// Let's assume that if the library has not been loaded in 5 seconds, then the user has forgotten to add the script tag
const POLL_PEER_TIMEOUT = 5000;
@ -17,7 +28,13 @@ const POLL_PEER_INTERVAL = 100;
/**
* Wait until the js client script it loaded and return the default peer from globalThis
*/
export const getDefaultPeer = (): Promise<IFluenceClient> => {
export const getFluenceInterface = (): Promise<PublicFluenceInterface> => {
// If the script is already loaded, then return the value immediately
const optimisticResult = getFluenceInterfaceFromGlobalThis();
if (optimisticResult) {
return Promise.resolve(optimisticResult);
}
return new Promise((resolve, reject) => {
// This function is internal
// Make it sure that would be zero way for unnecessary types
@ -30,7 +47,7 @@ export const getDefaultPeer = (): Promise<IFluenceClient> => {
reject(REJECT_MESSAGE);
}
let res = getPeerFromGlobalThis();
let res = getFluenceInterfaceFromGlobalThis();
if (res) {
clearInterval(interval);
resolve(res);