Skip to content
This repository was archived by the owner on Jul 10, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/@tests/aqua/_aqua/smoke_test.aqua
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import "@fluencelabs/registry/resources-api.aqua"

func smokeTest(label: string) -> ?string, *string:
service HelloWorld("hello-world"):
hello(str: string) -> string

func smokeTest(label: string) -> ?string, *string, string:
res, errors <- createResource(label)
<- res, errors
hello <- HelloWorld.hello("Fluence user")
<- res, errors, hello
4 changes: 2 additions & 2 deletions packages/@tests/aqua/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"author": "Fluence Labs",
"license": "Apache-2.0",
"dependencies": {
"@fluencelabs/js-client.api": "workspace:*",
"@fluencelabs/fluence-network-environment": "1.0.13",
"@fluencelabs/js-client.api": "workspace:^",
"@fluencelabs/fluence-network-environment": "1.0.14",
"base64-js": "1.5.1"
},
"devDependencies": {
Expand Down
292 changes: 170 additions & 122 deletions packages/@tests/aqua/src/_aqua/smoke_test.ts

Large diffs are not rendered by default.

83 changes: 49 additions & 34 deletions packages/@tests/aqua/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,65 @@
import { fromByteArray } from 'base64-js';
import { Fluence } from '@fluencelabs/js-client.api';
import { krasnodar } from '@fluencelabs/fluence-network-environment';
import { smokeTest } from './_aqua/smoke_test.js';
import { kras, randomKras } from '@fluencelabs/fluence-network-environment';
import { registerHelloWorld, smokeTest } from './_aqua/smoke_test.js';

// const relay = {
// multiaddr: '/ip4/127.0.0.1/tcp/4310/ws/p2p/12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',
// peerId: '12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',
// };

const relay = krasnodar[4];
const relay = randomKras();

const rndSk = () => {
// if (getRandomValues) {
// return getRandomValues(new Uint8Array(32));
// }
// @ts-ignore
// return globalThis.crypto.webcrypto.getRandomValues(new Uint8Array(32));
function generateRandomUint8Array() {
const uint8Array = new Uint8Array(32);
for (let i = 0; i < uint8Array.length; i++) {
uint8Array[i] = Math.floor(Math.random() * 256);
}
return uint8Array;
}

const optsWithRandomKeyPair = () => {
return {
keyPair: {
type: 'Ed25519',
source: generateRandomUint8Array(),
},
} as const;
};

export const main = async () => {
console.log('starting fluence...');
await Fluence.start({
relay: relay,
// keyPair: {
// type: 'Ed25519',
// source: rndSk(),
// },
});

console.log('started fluence');
const p = await Fluence.getPeer();

console.log('my peer id: ', p.getStatus().peerId);
console.log('my sk id: ', fromByteArray(p.getSk()));

console.log('running some aqua...');
const [res, errors] = await smokeTest('my_resource');
if (res === null) {
console.log('aqua failed, errors', errors);
} else {
console.log('aqua finished, result', res);
}
try {
Fluence.onConnectionStateChange((state) => console.info('connection state changed: ', state));

console.log('connecting to Fluence Network...');
await Fluence.connect(relay, optsWithRandomKeyPair());

console.log('connected');

console.log('stopping fluence...');
await Fluence.stop();
console.log('stopped fluence...');
await registerHelloWorld({
hello(str) {
return 'Hello, ' + str + '!';
},
});

const client = await Fluence.getClient();

console.log('my peer id: ', client.getPeerId());
console.log('my sk id: ', fromByteArray(client.getPeerSecretKey()));

console.log('running some aqua...');
const [res, errors, hello] = await smokeTest('my_resource');
console.log(hello);
if (res === null) {
console.log('aqua failed, errors', errors);
} else {
console.log('aqua finished, result', res);
}
} finally {
console.log('disconnecting from Fluence Network...');
await Fluence.disconnect();
console.log('disconnected');
}
};

export const runMain = () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/@tests/frameworks/cra-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@fluencelabs/js-client.api": "workspace:*",
"@test/aqua_for_test": "workspace:*",
"@fluencelabs/js-client.api": "workspace:^",
"@test/aqua_for_test": "workspace:^",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "13.4.0",
"@testing-library/user-event": "13.5.0",
Expand Down
6 changes: 3 additions & 3 deletions packages/@tests/smoke_node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
"author": "Fluence Labs",
"license": "Apache-2.0",
"dependencies": {
"@fluencelabs/js-client.api": "workspace:*",
"@fluencelabs/js-client.node": "workspace:*",
"@test/aqua_for_test": "workspace:*"
"@fluencelabs/js-client.api": "workspace:^",
"@fluencelabs/js-client.node": "workspace:^",
"@test/aqua_for_test": "workspace:^"
},
"devDependencies": {}
}
2 changes: 1 addition & 1 deletion packages/@tests/smoke_web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"license": "Apache-2.0",
"dependencies": {},
"devDependencies": {
"@fluencelabs/js-client.web.standalone": "workspace:*",
"@fluencelabs/js-client.web.standalone": "workspace:^",
"http-server": "14.1.1"
}
}
37 changes: 29 additions & 8 deletions packages/client/api/src/compilerSupport/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
});
};

Expand All @@ -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,
});
};

Expand All @@ -68,7 +87,7 @@ const extractFunctionArgs = async (
args: any[],
def: FunctionCallDef,
): Promise<{
peer: IFluenceClient;
client: IFluenceClient;
config?: FnConfig;
args: { [key: string]: any };
}> => {
Expand All @@ -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];
}
Expand All @@ -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,
};
Expand Down Expand Up @@ -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') {
Expand Down
65 changes: 47 additions & 18 deletions packages/client/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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();
};
31 changes: 24 additions & 7 deletions packages/client/api/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import type { IFluenceClient } from '@fluencelabs/interfaces';
import type { CallAquaFunction, IFluenceClient, RegisterService } from '@fluencelabs/interfaces';

const getPeerFromGlobalThis = (): IFluenceClient | undefined => {
type PublicFluenceInterface = {
clientFactory: () => IFluenceClient;
defaultClient: IFluenceClient;
callAquaFunction: CallAquaFunction;
registerService: RegisterService;
};

export const getFluenceInterfaceFromGlobalThis = (): PublicFluenceInterface | undefined => {
// @ts-ignore
return globalThis.defaultPeer;
return globalThis.fluence;
};

// TODO: DXJ-271
const REJECT_MESSAGE = 'You probably forgot to add script tag. Read about it here: ';
// 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;
Expand All @@ -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
Expand All @@ -30,7 +47,7 @@ export const getDefaultPeer = (): Promise<IFluenceClient> => {
reject(REJECT_MESSAGE);
}

let res = getPeerFromGlobalThis();
let res = getFluenceInterfaceFromGlobalThis();
if (res) {
clearInterval(interval);
resolve(res);
Expand Down
Loading