1
0
mirror of https://github.com/fluencelabs/gitbook-docs synced 2025-06-22 19:21:43 +00:00

GitBook: [2.0.0] 20 pages and 8 assets modified

This commit is contained in:
Pavel Murygin
2021-09-27 19:18:18 +00:00
committed by gitbook-bot
parent bbe456ed96
commit 59ea3e0e75
18 changed files with 75 additions and 70 deletions

44
fluence-js/1_concepts.md Normal file

@ -0,0 +1,44 @@
# Concepts
## Creating applications with Aqua language
The official way to write applications for Fluence is using Aqua programming language. Aqua compiler emits TypeScript or JavaScript which in turn can be called from a js-based environment. The compiler outputs code for the following entities:
1. Exported `func` declarations are turned into callable async functions
2. Exported `service` declarations are turned into functions which register callback handler in a typed manner
To learn more about Aqua see [aqua book](https://doc.fluence.dev/aqua-book/)
The building block of the application are:
* Aqua code for peer-to-peer communication
* Compiler cli package for aqua to \(java\)typescript compilation
* Initialization of the `FluencePeer`
* Application specific code \(java\)typescript in the framework of your choice
In the next section we see it in action
## Facade API
The main entry point `@fluencelabs/fluence` is `Fluence` facade. It provides easy way to start and stop the Fluence Peer. The facade API is enough for the most of the uses cases.
## Fluence peer in JS
`@fluencelabs/fluence` package also exports the `FluencePeer` class. This class implements the Fluence protocol for javascript-based environments. It provides all the necessary features to communicate with Fluence network namely:
1. Connectivity with one or many Fluence Node which allows sending particles to and receiving from other Peers
2. The Peer Id identifying the node in the network
3. Aqua VM which allows the execution of air scripts inside particles
4. A set of builtin functions required by Fluence protocol
5. Support for the typescript code which is generated by Aqua compiler
Even though the js-based implementation closely resembles [node](https://github.com/fluencelabs/gitbook-docs/js-sdk/node.md) there are some considerable differences to the latter.
`FluencePeer` does not host services composed of wasm modules. Instead it allows to register service call handlers directly in javascript. The Aqua language compiler creates a typed helpers for that task.
Due to the limitations of browser-based environment `FluencePeer` cannot be discovered by it's Peer Id on it's own. To overcome this `FluencePeer` must use an existing node which will act as a `relay`. When a peer is connected through a relay it is considered to be `client`. The `FluencePeer` routes all it's particle through it's relay thus taking advantage of the peer discovery implemented on the node. A particle sent to the connected client must be routed through it's relay.
The js-based peer does not implement the full set of builtin functions due the limitations described previously. E.g there is no built-ins implementation for _kad_ or _srv_ services. However _op_ service is fully implemented. For the full descriptions of implemented built-ins refer to [Api reference](https://github.com/fluencelabs/gitbook-docs/js-sdk/js-sdk/6_reference/modules.md)
In contrast with the node implementation `FluencePeer` can initiate new particles execution. Aqua compiler generates executable functions from `func` definitions in aqua code.

173
fluence-js/2_basics.md Normal file

@ -0,0 +1,173 @@
# Basics
## Intro
In this section we will show you how Fluence JS can be used to create a hello world application with Fluence stack.
## Aqua code
Let's start with the aqua code first:
```text
service HelloWorld("hello-world"):
hello(str: string)
func sayHello():
HelloWorld.hello("Hello, world!")
func getRelayTime() -> u64:
on HOST_PEER:
ts <- Peer.timestamp_ms()
<- ts
```
This file has three definitions.
The first one is a service named `HelloWorld`. A Service interfaces functions executable on a peer. We will register a handler for this interface in our typescript application.
The second definition is the function `sayHello`. The only thing the function is doing is calling the `hello` method of `HelloWorld` service located on the current peer. We will show you how to call this function from the typescript application.
Finally we have a functions which demonstrate how to work with the network. It asks the current time from the relay peer and return back the our peer.
## Installing dependencies
Initialize an empty npm package:
```bash
npm init
```
We will need these two packages for the application runtime
```bash
npm install @fluencelabs/fluence @fluencelabs/fluence-network-environment
```
The first one is the SDK itself and the second is a maintained list of Fluence networks and nodes to connect to.
Aqua compiler cli has to be installed, but is not needed at runtime.
```bash
npm install --save-dev @fluencelabs/aqua
```
Aqua comes with the standard library which can accessed from "@fluencelabs/aqua-lib" package. All the aqua packages are only needed at compiler time, so we install it as a development dependency
```bash
npm install --save-dev @fluencelabs/aqua-lib
```
Also we might want to have aqua source files automatically recompiled on every save. We will take advantage of chokidar for that:
```bash
npm install --save-dev @fluencelabs/chokidar-cli
```
And last, but no least we will need TypeScript
```text
npm install --save-dev typescript
npx tsc --init
```
## Setting up aqua compiler
Let's put aqua described earlier into `aqua/hello-world.aqua` file. You probably want to keep the generated TypeScript in the same directory with other typescript files, usually `src`. Let's create the `src/_aqua` directory for that.
The overall project structure looks like this:
```text
┣ aqua
┃ ┗ hello-world.aqua
┣ src
┃ ┣ _aqua
┃ ┃ ┗ hello-world.ts
┃ ┗ index.ts
┣ package-lock.json
┣ package.json
┗ tsconfig.json
```
The Aqua compiler can be run with `npm`:
```bash
npx aqua -i ./aqua/ -o ./src/_aqua
```
We recommend to store this logic inside a script in `packages.json` file:
```javascript
{
...
"scripts": {
...
"compile-aqua": "aqua -i ./aqua/ -o ./src/_aqua", // (1)
"watch-aqua": "chokidar \"**/*.aqua\" -c \"npm run compile-aqua\"" // (2)
},
...
}
```
`compile-aqua` \(1\) runs the compilation once, producing `src/_aqua/hello-world.ts` in our case `watch-aqua` \(2\) starts watching for any changes in .aqua files recompiling them on the fly
## Using the compiled code in typescript application
Using the code generated by the compiler is as easy as calling a function. The compiler generates all the boilerplate needed to send a particle into the network and wraps it into a single call. It also generate a function for service callback registration. Note that all the type information and therefore type checking and code completion facilities are there!
Let's see how use generated code in our application. `index.ts`:
```typescript
import { Fluence } from "@fluencelabs/fluence";
import { krasnodar } from "@fluencelabs/fluence-network-environment"; // (1)
import {
registerHelloWorld,
sayHello,
getRelayTime,
} from "./_aqua/hello-world"; // (2)
async function main() {
await Fluence.start({ connectTo: krasnodar[0] }); // (3)
// (3)
registerHelloWorld({
hello: async (str) => {
console.log(str);
},
});
await sayHello(); // (4)
const relayTime = await getRelayTime(); // (5)
console.log("The relay time is: ", new Date(relayTime).toLocaleString());
await Fluence.stop(); // (6)
}
main();
```
\(1\) Import list of possible relay nodes \(network environment\)
\(2\) Aqua compiler provides functions which can be directly imported like any normal typescript function.
\(3\) A Fluence peer has to be started before running any application in Fluence Network. For the vast majority of use cases you should use `Fluence` facade to start and stop the peer. The `start` method accepts a parameters object which. The most common parameter is the address of the relay node the peer should connect to. In this example we are using the first node of the `krasnodar` network. If you do not specify the `connectTo` options will only be able to execute air on the local machine only. Please keep in mind that the init function is asynchronous
For every exported `service XXX` definition in aqua code, the compiler provides a `registerXXX` counterpart. These functions provide a type-safe way of registering callback handlers for the services. The callbacks are executed when the appropriate service is called in aqua on the current peer. The handlers take form of the object where keys are the name of functions and the values are async functions used as the corresponding callbacks. For example in \(3\) we are registering handler for `HelloWorld` service which outputs it's parameter to the console
For every exported `func XXX` definition in aqua code, the compiler provides an async function which can be directly called from typescript. In \(4, 5\) we are calling exported aqua function with no arguments. Note that every function is asynchronous.
\(6\) You should call `stop` when the peer is no longer needed. As a rule of thumb all the peers should be uninitialized before destroying the application.
Let's try running the example:
```bash
node -r ts-node/register src/index.ts
```
If everything has been done correctly yuo should see `Hello, world!` in the console.
The next section will cover in-depth and advanced usage of Fluence JS
The code from this section is available in on [github](https://github.com/fluencelabs/examples/tree/main/js-sdk-examples/hello-world)

315
fluence-js/3_in_depth.md Normal file

@ -0,0 +1,315 @@
# In-depth
## Intro
In this section we will cover the Fluence JS in-depth.
## Fluence
`@fluencelabs/fluence` exports a facade `Fluence` which provides all the needed functionality for the most uses cases. It defined 4 functions:
* `start`: Start the default peer.
* `stop`: Stops the default peer
* `getStatus`: Gets the status of the default peer. This includes connection
* `getPeer`: Gets the default Fluence Peer instance \(see below\)
Under the hood `Fluence` facade calls the corresponding method on the default instance of FluencePeer. This instance is passed to the Aqua-compiler generated functions by default.
## FluencePeer class
The second export `@fluencelabs/fluence` package is `FluencePeer` class. It is useful in scenarios when the application need to run several different peer at once. The overall workflow with the `FluencePeer` is the following:
1. Create an instance of the peer
2. Starting the peer
3. Using the peer in the application
4. Stopping the peer
To create a new peer simple instantiate the `FluencePeer` class:
```typescript
const peer = new FluencePeer();
```
The constructor simply creates a new object and does not initialize any workflow. The `start` function starts the Aqua VM, initializes the default call service handlers and \(optionally\) connect to the Fluence network. The function takes an optional object specifying additional peer configuration. On option you will be using a lot is `connectTo`. It tells the peer to connect to a relay. For example:
```typescript
await peer.star({
connectTo: krasnodar[0],
});
```
connects the first node of the Krasnodar network. You can find the officially maintained list networks in the `@fluencelabs/fluence-network-environment` package. The full list of supported options is described in the [API reference](https://github.com/fluencelabs/gitbook-docs/tree/77344eb147c2ce17fe1c0f37013082fc85c1ffa3/js-sdk/js-sdk/6_reference/modules.md)
```typescript
await peer.stop();
```
## Using multiple peers in one application
The peer by itself does not do any useful work. You should take advantage of functions generated by the Aqua compiler.
If your application needs several peers, you should create a separate `FluencePeer` instance for each of them. The generated functions accept the peer as the first argument. For example:
```typescript
import { FluencePeer } from "@fluencelabs/fluence";
import {
registerSomeService,
someCallableFunction,
} from "./_aqua/someFunction";
async function main() {
const peer1 = new FluencePeer();
const peer2 = new FluencePeer();
// Don't forget to initialize peers
await peer1.start({
connectTo: relay,
});
await peer2.start({
connectTo: relay,
});
// ... more application logic
// Pass the peer as the first argument
// ||
// \/
registerSomeService(peer1, {
handler: async (str) => {
console.log("Called service on peer 1: " str);
},
});
// Pass the peer as the first argument
// ||
// \/
registerSomeService(peer2, {
handler: async (str) => {
console.log("Called service on peer 2: " str);
},
});
// Pass the peer as the first argument
// ||
// \/
await someCallableFunction(peer1, arg1, arg2, arg3);
await peer1.stop();
await peer2.stop();
}
// ... more application logic
```
It is possible to combine usage of the default peer with another one. Pay close attention to which peer you are calling the functions against.
```typescript
// Registering handler for the default peerS
registerSomeService({
handler: async (str) => {
console.log("Called against the default peer: " str);
},
});
// Pay close attention to this
// ||
// \/
registerSomeService(someOtherPeer, {
handler: async (str) => {
console.log("Called against the peer named someOtherPeer: " str);
},
});
```
## Understanding the Aqua compiler output
Aqua compiler emits TypeScript or JavaScript which in turn can be called from a js-based environment. The compiler outputs code for the following entities:
1. Exported `func` declarations are turned into callable async functions
2. Exported `service` declarations are turned into functions which register callback handler in a typed manner
3. For every exported `service` the compiler generated it's interface under the name `{serviceName}Def`
### Function definitions
For every exported function definition in aqua the compiler generated two overloads. One accepting the `FluencePeer` instance as the first argument, and one without it. Otherwise arguments are the same and correspond to the arguments of aqua functions. The last argument is always an optional config object with the following properties:
* `ttl`: Optional parameter which specify TTL \(time to live\) of particle with execution logic for the function
The return type is always a promise of the aqua function return type. If the function does not return anything, the return type will be `Promise<void>`.
Consider the following example:
```text
func myFunc(arg0: string, arg1: string):
-- implementation
```
The compiler will generate the following overloads:
```typescript
export async function myFunc(
arg0: string,
arg1: string,
config?: { ttl?: number }
): Promise<void>;
export async function callMeBack(
peer: FluencePeer,
arg0: string,
arg1: string,
config?: { ttl?: number }
): Promise<void>;
```
### Service definitions
```text
service ServiceName:
-- service interface
```
For every exported `service` declaration the compiler will generate two entities: service interface under the name `{serviceName}Def` and a function named `register{serviceName}` with several overloads. First let's describe the most complete one using the following example:
```typescript
export interface ServiceNameDef {
//... service function definitions
}
export function registerServiceName(
peer: FluencePeer,
serviceId: string,
service: ServiceNameDef
): void;
```
* `peer` - the Fluence Peer instance where the handler should be registered. The peer can be omitted. In that case the default Fluence Peer will be used instead
* `serviceId` - the name of the service id. If the service was defined with the default service id in aqua code, this argument can be omitted.
* `service` - the handler for the service.
Depending on whether or not the services was defined with the default id the number of overloads will be different. In the case it **is defined**, there would be four overloads:
```typescript
// (1)
export function registerServiceName(
//
service: ServiceNameDef
): void;
// (2)
export function registerServiceName(
serviceId: string,
service: ServiceNameDef
): void;
// (3)
export function registerServiceName(
peer: FluencePeer,
service: ServiceNameDef
): void;
// (4)
export function registerServiceName(
peer: FluencePeer,
serviceId: string,
service: ServiceNameDef
): void;
```
1. Uses default Fluence Peer and the default id taken from aqua definition
2. Uses default Fluence Peer and specifies the service id explicitly
3. The default id is taken from aqua definition. The peer is specified explicitly
4. Specifying both peer and the service id.
If the default id **is not defined** in aqua code the overloads will exclude ones without service id:
```typescript
// (1)
export function registerServiceName(
serviceId: string,
service: ServiceNameDef
): void;
// (2)
export function registerServiceName(
peer: FluencePeer,
serviceId: string,
service: ServiceNameDef
): void;
```
1. Uses default Fluence Peer and specifies the service id explicitly
2. Specifying both peer and the service id.
### Service interface
The service interface type follows closely the definition in aqua code. It has the form of the object which keys correspond to the names of service members and the values are functions of the type translated from aqua definition \(see Type convertion\). For example, for the following aqua definition:
```text
service Calc("calc"):
add(n: f32)
subtract(n: f32)
multiply(n: f32)
divide(n: f32)
reset()
getResult() -> f32
```
The typescript interface will be:
```typescript
export interface CalcDef {
add: (n: number, callParams: CallParams<"n">) => void;
subtract: (n: number, callParams: CallParams<"n">) => void;
multiply: (n: number, callParams: CallParams<"n">) => void;
divide: (n: number, callParams: CallParams<"n">) => void;
reset: (callParams: CallParams<null>) => void;
getResult: (callParams: CallParams<null>) => number;
}
```
`CallParams` will be described later in the section
### Type conversion
Basic types conversion is pretty much straightforward:
* `string` is converted to `string` in typescript
* `bool` is converted to `boolean` in typescript
* All number types \(`u8`, `u16`, `u32`, `u64`, `s8`, `s16`, `s32`, `s64`, `f32`, `f64`\) are converted to `number` in typescript
Arrow types translate to functions in typescript which have their arguments translated to typescript types. In addition to arguments defined in aqua, typescript counterparts have an additional argument for call params. For the majority of use cases this parameter is not needed and can be omitted.
The type conversion works the same way for `service` and `func` definitions. For example a `func` with a callback might look like this:
```text
func callMeBack(callback: string, i32 -> ()):
callback("hello, world", 42)
```
The type for `callback` argument will be:
```typescript
callback: (arg0: string, arg1: number, callParams: CallParams<'arg0' | 'arg1'>) => void,
```
For the service definitions arguments are named \(see calc example above\)
### Call params and tetraplets
Each service call is accompanied by additional information specific to Fluence Protocol. Including `initPeerId` - the peer which initiated the particle execution, particle signature and most importantly security tetraplets. All this data is contained inside the last `callParams` argument in every generated function definition. These data is passed to the handler on each function call can be used in the application.
Tetraplets have the form of:
```typescript
{
argName0: SecurityTetraplet[],
argName1: SecurityTetraplet[],
// ...
}
```
To learn more about tetraplets and application security see [Security](https://github.com/fluencelabs/gitbook-docs/tree/77344eb147c2ce17fe1c0f37013082fc85c1ffa3/js-sdk/knowledge_security.md)
To see full specification of `CallParms` type see [Api reference](https://github.com/fluencelabs/gitbook-docs/tree/77344eb147c2ce17fe1c0f37013082fc85c1ffa3/js-sdk/js-sdk/6_reference/modules.md)

@ -0,0 +1,8 @@
# Running app in browser
You can use the Fluence JS with any framework \(or even without it\). The "fluence" part of the application is a collection of pure typescript\javascript functions which can be called withing any framework of your choosing.
See the browser-example which demonstrate integrating Fluence with React: [github](https://github.com/fluencelabs/examples/tree/main/js-sdk-examples/browser-example)
Also take a look at FluentPad. It is an example application written in React: [https://github.com/fluencelabs/fluent-pad](https://github.com/fluencelabs/fluent-pad)

123
fluence-js/5_run_in_node.md Normal file

@ -0,0 +1,123 @@
# Running app in nodejs
## Intro
It is easy to use Fluence JS in NodeJS applications. You can take full advantage of the javascript ecosystem and at the save time expose service to the Fluence Network. That makes is an excellent choice for quick prototyping of applications for Fluence Stack.
## Calc app example
Lets implement a very simple app which simulates a desk calculator. The calculator has internal memory and implements the following set of operations:
* Add a number
* Subtract a number
* Multiply by a number
* Divide by a number
* Get the current memory state
* Reset the memory state to 0.0
First, let's write the service definition in aqua:
```text
-- service definition
service Calc("calc"):
add(n: f32)
subtract(n: f32)
multiply(n: f32)
divide(n: f32)
reset()
getResult() -> f32
```
Now write the implementation for this service in typescript:
```typescript
import { Fluence } from "@fluencelabs/fluence";
import { krasnodar } from "@fluencelabs/fluence-network-environment";
import { registerCalc, CalcDef } from "./_aqua/calc";
class Calc implements CalcDef {
private _state: number = 0;
add(n: number) {
this._state += n;
}
subtract(n: number) {
this._state -= n;
}
multiply(n: number) {
this._state *= n;
}
divide(n: number) {
this._state /= n;
}
reset() {
this._state = 0;
}
getResult() {
return this._state;
}
}
const keypress = async () => {
process.stdin.setRawMode(true);
return new Promise<void>((resolve) =>
process.stdin.once("data", () => {
process.stdin.setRawMode(false);
resolve();
})
);
};
async function main() {
await Fluence.start({
connectTo: krasnodar[0],
});
registerCalc(new Calc());
console.log("application started");
console.log("peer id is: ", Fluence.getStatus().peerId);
console.log("relay is: ", Fluence.getStatus().relayPeerId);
console.log("press any key to continue");
await keypress();
await Fluence.stop();
}
main();
```
As you can see all the service logic has been implemented in typescript. You have full power of npm at your disposal.
Now try running the application:
```bash
> node -r ts-node/register src/index.ts
application started
peer id is: 12D3KooWLBkw4Tz8bRoSriy5WEpHyWfU11jEK3b5yCa7FBRDRWH3
relay is: 12D3KooWSD5PToNiLQwKDXsu8JSysCwUt8BVUJEqCHcDe7P5h45e
press any key to continue
```
And the service can be called from aqua. For example:
```bash
const peer ?= "12D3KooWLBkw4Tz8bRoSriy5WEpHyWfU11jEK3b5yCa7FBRDRWH3"
const relay ?= "12D3KooWSD5PToNiLQwKDXsu8JSysCwUt8BVUJEqCHcDe7P5h45e"
func demoCalculation() -> f32:
on peer via relay
Calc.add(10)
Calc.multiply(5)
Calc.subtract(8)
Calc.divide(6)
res <- Calc.getResult()
<- res
```

@ -0,0 +1,4 @@
# API reference
API reference is available at [https://fluence.network/fluence-js/](https://fluence.network/fluence-js/)

14
fluence-js/README.md Normal file

@ -0,0 +1,14 @@
# Fluence JS
Fluence JS is an implementation of the Fluence protocol for JavaScript-based environments. It can connect browsers, Node.js applications, and so on to the Fluence p2p network.
Similar to the [Rust Fluence Peer implementation](https://github.com/fluencelabs/fluence) it includes:
* Peer-to-peer communication layer \(via [js-libp2p](https://github.com/libp2p/js-libp2p)\)
* [Aqua VM](https://github.com/fluencelabs/aquavm)
* Builtin services
Fluence JS can call services and functions on the Fluence network, and expose new APIs to the p2p network directly from TypeScript and JavaScript. [Aqua language](https://github.com/fluencelabs/aqua) uses Fluence JS as a compilation target, and they are designed to [work in tandem](https://doc.fluence.dev/docs/js-sdk/3_in_depth#understanding-the-aqua-compiler-output).
Fluence JS can be used with any framework of your choice \(or even without frameworks\).

34
fluence-js/changelog.md Normal file

@ -0,0 +1,34 @@
# Changelog
Fluence JS versioning scheme is the following: `0.BREAKING.ENHANCING`
* `0` shows that Fluence JS does not meet its vision yet, so API can change quickly
* `BREAKING` part is incremented for each breaking API change
* `ENHANCING` part is incremented for every fix and update which is compatible on API level
## [0.13.0](https://github.com/fluencelabs/fluence-js/releases/tag/v0.13.0) September 24, 2021
* `PeerStatus` is now exported from index file \([\#80](https://github.com/fluencelabs/fluence-js/pull/80)\)
* `KeyPair`: method `fromBytes` is replaced with fromEd25519SK \([\#81](https://github.com/fluencelabs/fluence-js/pull/81)\)
## [0.12.1](https://github.com/fluencelabs/fluence-js/releases/tag/v0.12.1) September 14, 2021
* `KeyPair`: add fromBytes, toEd25519PrivateKey \([\#78](https://github.com/fluencelabs/fluence-js/pull/78)\)
## [0.12.0](https://github.com/fluencelabs/fluence-js/releases/tag/v0.13.0) September 10, 2021
* The API to work with the default Fluence Peer has been put under the facade `Fluence`. Method `init` was renamed to `start` and `uninit` renamed to `stop`. `connectionStatus` migrated to `getStatus`.
To migrate from 0.11.0 to 0.12.0
1. `import { Fluence } from "@fluencelabs/fluence"`; instead of `FluencePeer`
2. replace `Fluence.default` with just `Fluence`
3. replace `init` with `start` and `uninit` with `stop`
4. replace `connectionInfo()` with `getStatus()`
\([\#72](https://github.com/fluencelabs/fluence-js/pull/72)\)
## [0.11.0](https://github.com/fluencelabs/fluence-js/releases/tag/v0.11.0) September 08, 2021
* Update JS SDK api to the new version \([\#61](https://github.com/fluencelabs/fluence-js/pull/61)\)