mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2024-12-04 09:50:17 +00:00
feat(js-client)!: Adding strictes eslint and ts config to all packages [fixes DXJ-464] (#355)
* introduce eslint * Fix all eslint errors * Eslint fix and some touches * Fix tests * Fix misc errors * change semver * change semver #2 * Fix path * Fix path #2 * freeze lock file in CI * fix package install * Fix formatting of surrounding files * Add empty prettier config * Fix formatting * Fix build errors * Remove unused deps * remove changelog from formatting * deps cleanup * make resource importers async * Refactor * Fix error message * remove comment * more refactoring * Update packages/core/js-client/src/compilerSupport/registerService.ts Co-authored-by: shamsartem <shamsartem@gmail.com> * refactoring * refactoring fix * optimize import * Update packages/@tests/smoke/node/src/index.ts Co-authored-by: shamsartem <shamsartem@gmail.com> * Revert package * Fix pnpm lock * Lint-fix * Fix CI * Update tests * Fix build * Fix import * Use forked threads dep * Use fixed version * Update threads * Fix lint * Fix test * Fix test * Add polyfill for assert * Add subpath import * Fix tests * Fix deps --------- Co-authored-by: shamsartem <shamsartem@gmail.com>
This commit is contained in:
parent
b46933252a
commit
919c7d6ea1
@ -1,12 +0,0 @@
|
|||||||
# EditorConfig is awesome: https://EditorConfig.org
|
|
||||||
|
|
||||||
# top-most EditorConfig file
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
insert_final_newline = false
|
|
133
.eslintrc.json
Normal file
133
.eslintrc.json
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2022,
|
||||||
|
"project": ["./tsconfig.eslint.json"],
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/strict-type-checked",
|
||||||
|
"plugin:import/recommended",
|
||||||
|
"plugin:import/typescript",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint",
|
||||||
|
"import",
|
||||||
|
"license-header",
|
||||||
|
"unused-imports"
|
||||||
|
],
|
||||||
|
"ignorePatterns": ["**/node_modules/**/*", "**/dist/**/*"],
|
||||||
|
"rules": {
|
||||||
|
"eqeqeq": [
|
||||||
|
"error",
|
||||||
|
"always",
|
||||||
|
{
|
||||||
|
"null": "ignore"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-console": ["error"],
|
||||||
|
"arrow-body-style": ["error", "always"],
|
||||||
|
"no-empty": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allowEmptyCatch": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"curly": ["error", "all"],
|
||||||
|
"no-unused-expressions": ["error"],
|
||||||
|
"dot-notation": ["off"],
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
"padding-line-between-statements": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "multiline-expression",
|
||||||
|
"next": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "*",
|
||||||
|
"next": "multiline-expression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "multiline-block-like",
|
||||||
|
"next": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "*",
|
||||||
|
"next": "multiline-block-like"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "multiline-const",
|
||||||
|
"next": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "*",
|
||||||
|
"next": "multiline-const"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "multiline-let",
|
||||||
|
"next": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "*",
|
||||||
|
"next": "multiline-let"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "any",
|
||||||
|
"prev": "case",
|
||||||
|
"next": "case"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"import/extensions": ["error", "ignorePackages"],
|
||||||
|
"import/no-unresolved": "off",
|
||||||
|
"import/no-cycle": ["error"],
|
||||||
|
"import/order": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"newlines-between": "always",
|
||||||
|
"alphabetize": {
|
||||||
|
"order": "asc",
|
||||||
|
"caseInsensitive": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"node/no-unsupported-features/es-syntax": "off",
|
||||||
|
"node/no-unpublished-import": "off",
|
||||||
|
"node/no-missing-import": "off",
|
||||||
|
"@typescript-eslint/explicit-member-accessibility": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"accessibility": "no-public"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/strict-boolean-expressions": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allowString": false,
|
||||||
|
"allowNumber": false,
|
||||||
|
"allowNullableObject": false,
|
||||||
|
"allowNullableBoolean": false,
|
||||||
|
"allowNullableString": false,
|
||||||
|
"allowNullableNumber": false,
|
||||||
|
"allowAny": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/consistent-type-assertions": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"assertionStyle": "never"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
"license-header/header": ["error", "./resources/license-header.js"]
|
||||||
|
}
|
||||||
|
}
|
9
.github/workflows/tests.yml
vendored
9
.github/workflows/tests.yml
vendored
@ -88,6 +88,11 @@ jobs:
|
|||||||
registry-url: "https://npm.fluence.dev"
|
registry-url: "https://npm.fluence.dev"
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- run: pnpm -r i
|
||||||
|
- run: pnpm -r build
|
||||||
|
- run: pnpm lint-check
|
||||||
|
|
||||||
|
|
||||||
- name: Override dependencies
|
- name: Override dependencies
|
||||||
uses: fluencelabs/github-actions/pnpm-set-dependency@main
|
uses: fluencelabs/github-actions/pnpm-set-dependency@main
|
||||||
with:
|
with:
|
||||||
@ -97,10 +102,8 @@ jobs:
|
|||||||
"@fluencelabs/marine-js": "${{ inputs.marine-js-version }}"
|
"@fluencelabs/marine-js": "${{ inputs.marine-js-version }}"
|
||||||
}
|
}
|
||||||
|
|
||||||
- run: pnpm -r --no-frozen-lockfile i
|
- run: pnpm -r i
|
||||||
- run: pnpm -r build
|
|
||||||
- run: pnpm -r test
|
- run: pnpm -r test
|
||||||
|
|
||||||
- name: Dump rust-peer logs
|
- name: Dump rust-peer logs
|
||||||
if: always()
|
if: always()
|
||||||
uses: jwalton/gh-docker-logs@v2
|
uses: jwalton/gh-docker-logs@v2
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
*.log
|
*.log
|
||||||
.idea
|
.idea
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
# Dependency directories
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
|
@ -1 +1,10 @@
|
|||||||
.github
|
.github
|
||||||
|
.eslintcache
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
**/node_modules
|
||||||
|
**/dist
|
||||||
|
**/build
|
||||||
|
**/public
|
||||||
|
|
||||||
|
**/CHANGELOG.md
|
1
.prettierrc
Normal file
1
.prettierrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
@ -1,8 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
semi: true,
|
|
||||||
trailingComma: 'all',
|
|
||||||
singleQuote: true,
|
|
||||||
printWidth: 120,
|
|
||||||
tabWidth: 4,
|
|
||||||
useTabs: false,
|
|
||||||
};
|
|
@ -10,4 +10,4 @@ Things you need to know:
|
|||||||
|
|
||||||
### Contributor License Agreement
|
### Contributor License Agreement
|
||||||
|
|
||||||
When you contribute, you have to be aware that your contribution is covered by **[Apache License 2.0](./LICENSE)**, but might relicensed under few other software licenses mentioned in the **Contributor License Agreement**. In particular, you need to agree to the Contributor License Agreement. If you agree to its content, you simply have to click on the link posted by the CLA assistant as a comment to the pull request. Click it to check the CLA, then accept it on the following screen if you agree to it. The CLA assistant will save this decision for upcoming contributions and will notify you if there is any change to the CLA in the meantime.
|
When you contribute, you have to be aware that your contribution is covered by **[Apache License 2.0](./LICENSE)**, but might relicensed under few other software licenses mentioned in the **Contributor License Agreement**. In particular, you need to agree to the Contributor License Agreement. If you agree to its content, you simply have to click on the link posted by the CLA assistant as a comment to the pull request. Click it to check the CLA, then accept it on the following screen if you agree to it. The CLA assistant will save this decision for upcoming contributions and will notify you if there is any change to the CLA in the meantime.
|
||||||
|
154
README.md
154
README.md
@ -13,34 +13,34 @@ Adding the Fluence JS client for your web application is very easy.
|
|||||||
|
|
||||||
1. Add a script tag with the JS Client bundle to your `index.html`. The easiest way to do this is using a CDN (like [JSDELIVR](https://www.jsdelivr.com/) or [UNPKG](https://unpkg.com/)). The script is large, thus we highly recommend to use the `async` attribute.
|
1. Add a script tag with the JS Client bundle to your `index.html`. The easiest way to do this is using a CDN (like [JSDELIVR](https://www.jsdelivr.com/) or [UNPKG](https://unpkg.com/)). The script is large, thus we highly recommend to use the `async` attribute.
|
||||||
|
|
||||||
Here is an example using the JSDELIVR CDN:
|
Here is an example using the JSDELIVR CDN:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<head>
|
<head>
|
||||||
<title>Cool App</title>
|
<title>Cool App</title>
|
||||||
<script
|
<script
|
||||||
src="https://cdn.jsdelivr.net/npm/@fluencelabs/js-client.web.standalone@0.13.3/dist/js-client.min.js"
|
src="https://cdn.jsdelivr.net/npm/@fluencelabs/js-client.web.standalone@0.13.3/dist/js-client.min.js"
|
||||||
async
|
async
|
||||||
></script>
|
></script>
|
||||||
</head>
|
</head>
|
||||||
```
|
```
|
||||||
|
|
||||||
If you cannot or don't want to use a CDN, feel free to get the script directly from the [npm package](https://www.npmjs.com/package/@fluencelabs/js-client.web.standalone) and host it yourself. You can find the script in the `/dist` directory of the package. (Note: this option means that developers understand what they are doing and know how to serve this file from their own web server.)
|
If you cannot or don't want to use a CDN, feel free to get the script directly from the [npm package](https://www.npmjs.com/package/@fluencelabs/js-client.web.standalone) and host it yourself. You can find the script in the `/dist` directory of the package. (Note: this option means that developers understand what they are doing and know how to serve this file from their own web server.)
|
||||||
|
|
||||||
2. Install the following packages:
|
2. Install the following packages:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm i @fluencelabs/js-client.api @fluencelabs/fluence-network-environment
|
npm i @fluencelabs/js-client.api @fluencelabs/fluence-network-environment
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Add the following lines at the beginning of your code:
|
3. Add the following lines at the beginning of your code:
|
||||||
|
|
||||||
```
|
```
|
||||||
import { Fluence } from "@fluencelabs/js-client.api";
|
import { Fluence } from "@fluencelabs/js-client.api";
|
||||||
import { randomKras } from '@fluencelabs/fluence-network-environment';
|
import { randomKras } from '@fluencelabs/fluence-network-environment';
|
||||||
|
|
||||||
Fluence.connect(randomKras());
|
Fluence.connect(randomKras());
|
||||||
```
|
```
|
||||||
|
|
||||||
### Node.js Apps
|
### Node.js Apps
|
||||||
|
|
||||||
@ -48,37 +48,37 @@ Adding the Fluence JS client for your web application is very easy.
|
|||||||
|
|
||||||
The Fluence JS Client only supports the ESM format. This implies that a few preliminary steps are required if your project is not already using ESM:
|
The Fluence JS Client only supports the ESM format. This implies that a few preliminary steps are required if your project is not already using ESM:
|
||||||
|
|
||||||
- Add `"type": "module"` to your package.json.
|
- Add `"type": "module"` to your package.json.
|
||||||
- Replace `"main": "index.js"` with `"exports": "./index.js"` in your package.json.
|
- Replace `"main": "index.js"` with `"exports": "./index.js"` in your package.json.
|
||||||
- Remove `'use strict';` from all JavaScript files.
|
- Remove `'use strict';` from all JavaScript files.
|
||||||
- Replace all `require()`/`module.export` with `import`/`export`.
|
- Replace all `require()`/`module.export` with `import`/`export`.
|
||||||
- Use only full relative file paths for imports: `import x from '.';` → `import x from './index.js';`.
|
- Use only full relative file paths for imports: `import x from '.';` → `import x from './index.js';`.
|
||||||
|
|
||||||
If you are using TypeScript:
|
If you are using TypeScript:
|
||||||
|
|
||||||
- Make sure you are using TypeScript 4.7 or later.
|
- Make sure you are using TypeScript 4.7 or later.
|
||||||
- Add [`"module": "ESNext", "target": "ESNext", "moduleResolution": "nodenext"`](https://www.typescriptlang.org/tsconfig#module) to your tsconfig.json.
|
- Add [`"module": "ESNext", "target": "ESNext", "moduleResolution": "nodenext"`](https://www.typescriptlang.org/tsconfig#module) to your tsconfig.json.
|
||||||
- Use only full relative file paths for imports: `import x from '.';` → `import x from './index.js';`.
|
- Use only full relative file paths for imports: `import x from '.';` → `import x from './index.js';`.
|
||||||
- Remove `namespace` usage and use `export` instead.
|
- Remove `namespace` usage and use `export` instead.
|
||||||
- You must use a `.js` extension in relative imports even though you're importing `.ts` files.
|
- You must use a `.js` extension in relative imports even though you're importing `.ts` files.
|
||||||
|
|
||||||
**Installation:**
|
**Installation:**
|
||||||
|
|
||||||
1. Install the following packages:
|
1. Install the following packages:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm i @fluencelabs/js-client.api"@fluencelabs/js-client.node @fluencelabs/fluence-network-environment
|
npm i @fluencelabs/js-client.api"@fluencelabs/js-client.node @fluencelabs/fluence-network-environment
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Add the following lines at the beginning of your code:
|
2. Add the following lines at the beginning of your code:
|
||||||
|
|
||||||
```
|
```
|
||||||
import '@fluencelabs/js-client.node';
|
import '@fluencelabs/js-client.node';
|
||||||
import { Fluence } from "@fluencelabs/js-client.api";
|
import { Fluence } from "@fluencelabs/js-client.api";
|
||||||
import { randomKras } from '@fluencelabs/fluence-network-environment';
|
import { randomKras } from '@fluencelabs/fluence-network-environment';
|
||||||
|
|
||||||
Fluence.connect(randomKras());
|
Fluence.connect(randomKras());
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage in an Application
|
## Usage in an Application
|
||||||
|
|
||||||
@ -86,9 +86,9 @@ Once you've added the client, you can compile [Aqua](https://github.com/fluencel
|
|||||||
|
|
||||||
1. Install the package:
|
1. Install the package:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm i -D @fluencelabs/cli
|
npm i -D @fluencelabs/cli
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Add a directory in your project for Aqua code, e.g., `_aqua`.
|
2. Add a directory in your project for Aqua code, e.g., `_aqua`.
|
||||||
|
|
||||||
@ -98,46 +98,46 @@ Once you've added the client, you can compile [Aqua](https://github.com/fluencel
|
|||||||
|
|
||||||
5. To compile Aqua code once, run `npx fluence aqua -i ./_aqua -o ./src/_aqua/`. To watch the changes and to recompile on the fly, add the `-w` flag: `npx fluence aqua -w -i ./_aqua -o ./src/_aqua/`.
|
5. To compile Aqua code once, run `npx fluence aqua -i ./_aqua -o ./src/_aqua/`. To watch the changes and to recompile on the fly, add the `-w` flag: `npx fluence aqua -w -i ./_aqua -o ./src/_aqua/`.
|
||||||
|
|
||||||
**Hint**: it might be a good idea to add these scripts to your `package.json` file.
|
**Hint**: it might be a good idea to add these scripts to your `package.json` file.
|
||||||
For example, you project structure could look like this:
|
For example, you project structure could look like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
┣ _aqua
|
┣ _aqua
|
||||||
┃ ┗ demo.aqua
|
┃ ┗ demo.aqua
|
||||||
┣ src
|
┣ src
|
||||||
┃ ┣ _aqua
|
┃ ┣ _aqua
|
||||||
┃ ┃ ┗ demo.ts
|
┃ ┃ ┗ demo.ts
|
||||||
┃ ┗ index.ts
|
┃ ┗ index.ts
|
||||||
┣ package-lock.json
|
┣ package-lock.json
|
||||||
┣ package.json
|
┣ package.json
|
||||||
┗ tsconfig.json
|
┗ tsconfig.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, your `package.json` file should include the following lines:
|
Then, your `package.json` file should include the following lines:
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
"scripts": {
|
"scripts": {
|
||||||
...
|
...
|
||||||
"aqua:compile": "fluence aqua -i ./aqua/ -o ./src/_aqua",
|
"aqua:compile": "fluence aqua -i ./aqua/ -o ./src/_aqua",
|
||||||
"aqua:watch": "fluence aqua -w -i ./aqua/ -o ./src/_aqua"
|
"aqua:watch": "fluence aqua -w -i ./aqua/ -o ./src/_aqua"
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Now you can import and call Aqua code from your application like
|
6. Now you can import and call Aqua code from your application like
|
||||||
this:
|
this:
|
||||||
|
|
||||||
```
|
```
|
||||||
import { getRelayTime } from "./_aqua/demo";
|
import { getRelayTime } from "./_aqua/demo";
|
||||||
|
|
||||||
async function buttonClick() {
|
async function buttonClick() {
|
||||||
const time = await getRelayTime();
|
const time = await getRelayTime();
|
||||||
alert("relay time: " + time);
|
alert("relay time: " + time);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Debug
|
## Debug
|
||||||
|
|
||||||
@ -165,10 +165,10 @@ Star (`*`) character can be used as a wildcard to enable logs for multiple compo
|
|||||||
|
|
||||||
### Index of components:
|
### Index of components:
|
||||||
|
|
||||||
- `particle`: everything related to particle processing queue
|
- `particle`: everything related to particle processing queue
|
||||||
- `aqua`: infrastructure of aqua compiler support
|
- `aqua`: infrastructure of aqua compiler support
|
||||||
- `connection`: connection layer
|
- `connection`: connection layer
|
||||||
- `marine`: Marine JS logs
|
- `marine`: Marine JS logs
|
||||||
|
|
||||||
### Enabling logs in Node.js
|
### Enabling logs in Node.js
|
||||||
|
|
||||||
|
217
ci.cjs
217
ci.cjs
@ -4,164 +4,163 @@ const fs = require("fs").promises;
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
function printUsage() {
|
function printUsage() {
|
||||||
console.log(
|
console.log(
|
||||||
`Usage: "ci check-consistency" or "ci bump-version %postfix%" or "ci get-version"`
|
`Usage: "ci check-consistency" or "ci bump-version %postfix%" or "ci get-version"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let postfix;
|
let postfix;
|
||||||
const mode = process.argv[2];
|
const mode = process.argv[2];
|
||||||
|
|
||||||
function validateArgs() {
|
function validateArgs() {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "get-version":
|
case "get-version":
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case "bump-version":
|
case "bump-version":
|
||||||
postfix = process.argv[3];
|
postfix = process.argv[3];
|
||||||
if (!postfix) {
|
if (!postfix) {
|
||||||
printUsage();
|
printUsage();
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case "":
|
case "":
|
||||||
case undefined:
|
case undefined:
|
||||||
case "check-consistency":
|
case "check-consistency":
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const PATH_TO_PACKAGES = "./packages/";
|
const PATH_TO_PACKAGES = "./packages/";
|
||||||
|
|
||||||
async function getPackageJsonsRecursive(currentPath) {
|
async function getPackageJsonsRecursive(currentPath) {
|
||||||
return (
|
return (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
(await fs.readdir(currentPath, { withFileTypes: true }))
|
(await fs.readdir(currentPath, { withFileTypes: true }))
|
||||||
.filter(
|
.filter(
|
||||||
(file) =>
|
(file) =>
|
||||||
file.name !== "node_modules" && file.name !== "@tests" &&
|
file.name !== "node_modules" &&
|
||||||
(file.isDirectory() || file.name === "package.json")
|
file.name !== "@tests" &&
|
||||||
)
|
(file.isDirectory() || file.name === "package.json"),
|
||||||
.map((file) =>
|
|
||||||
file.isDirectory()
|
|
||||||
? getPackageJsonsRecursive(
|
|
||||||
path.join(currentPath, file.name)
|
|
||||||
)
|
|
||||||
: Promise.resolve([
|
|
||||||
path.join(process.cwd(), currentPath, file.name),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
).flat();
|
.map((file) =>
|
||||||
|
file.isDirectory()
|
||||||
|
? getPackageJsonsRecursive(path.join(currentPath, file.name))
|
||||||
|
: Promise.resolve([
|
||||||
|
path.join(process.cwd(), currentPath, file.name),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
).flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getVersion(file) {
|
async function getVersion(file) {
|
||||||
const content = await fs.readFile(file);
|
const content = await fs.readFile(file);
|
||||||
const json = JSON.parse(content);
|
const json = JSON.parse(content);
|
||||||
return [json.name, json.version];
|
return [json.name, json.version];
|
||||||
}
|
}
|
||||||
|
|
||||||
function processDep(obj, name, fn) {
|
function processDep(obj, name, fn) {
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!obj[name]) {
|
if (!obj[name]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn(obj, obj[name]);
|
fn(obj, obj[name]);
|
||||||
}
|
}
|
||||||
async function getVersionsMap(allPackageJsons) {
|
async function getVersionsMap(allPackageJsons) {
|
||||||
return new Map(await Promise.all(allPackageJsons.map(getVersion)));
|
return new Map(await Promise.all(allPackageJsons.map(getVersion)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersionForPackageOrThrow(versionsMap, packageName) {
|
function getVersionForPackageOrThrow(versionsMap, packageName) {
|
||||||
const version = versionsMap.get(packageName);
|
const version = versionsMap.get(packageName);
|
||||||
if (!version) {
|
if (!version) {
|
||||||
console.log("Failed to get version for package: ", packageName);
|
console.log("Failed to get version for package: ", packageName);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkConsistency(file, versionsMap) {
|
async function checkConsistency(file, versionsMap) {
|
||||||
console.log("Checking: ", file);
|
console.log("Checking: ", file);
|
||||||
const content = await fs.readFile(file);
|
const content = await fs.readFile(file);
|
||||||
const json = JSON.parse(content);
|
const json = JSON.parse(content);
|
||||||
|
|
||||||
for (const [name, versionInDep] of versionsMap) {
|
for (const [name, versionInDep] of versionsMap) {
|
||||||
const check = (x, version) => {
|
const check = (x, version) => {
|
||||||
if (version.includes("*")) {
|
if (version.includes("*")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (versionInDep !== version) {
|
if (versionInDep !== version) {
|
||||||
console.log(
|
console.log(
|
||||||
`Error, versions don't match: ${name}:${version} !== ${versionInDep}`,
|
`Error, versions don't match: ${name}:${version} !== ${versionInDep}`,
|
||||||
file
|
file,
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
processDep(json.dependencies, name, check);
|
processDep(json.dependencies, name, check);
|
||||||
processDep(json.devDependencies, name, check);
|
processDep(json.devDependencies, name, check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bumpVersions(file, versionsMap) {
|
async function bumpVersions(file, versionsMap) {
|
||||||
console.log("Updating: ", file);
|
console.log("Updating: ", file);
|
||||||
const content = await fs.readFile(file);
|
const content = await fs.readFile(file);
|
||||||
const json = JSON.parse(content);
|
const json = JSON.parse(content);
|
||||||
|
|
||||||
// bump dependencies
|
// bump dependencies
|
||||||
for (const [name, version] of versionsMap) {
|
for (const [name, version] of versionsMap) {
|
||||||
const update = (x) => (x[name] = `${version}-${postfix}`);
|
const update = (x) => (x[name] = `${version}-${postfix}`);
|
||||||
processDep(json.dependencies, name, update);
|
processDep(json.dependencies, name, update);
|
||||||
processDep(json.devDependencies, name, update);
|
processDep(json.devDependencies, name, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
// also bump version in package itself
|
// also bump version in package itself
|
||||||
const version = getVersionForPackageOrThrow(versionsMap, json.name);
|
const version = getVersionForPackageOrThrow(versionsMap, json.name);
|
||||||
json.version = `${version}-${postfix}`;
|
json.version = `${version}-${postfix}`;
|
||||||
|
|
||||||
const newContent = JSON.stringify(json, undefined, 4) + "\n";
|
const newContent = JSON.stringify(json, undefined, 4) + "\n";
|
||||||
await fs.writeFile(file, newContent);
|
await fs.writeFile(file, newContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processPackageJsons(allPackageJsons, versionsMap, fn) {
|
async function processPackageJsons(allPackageJsons, versionsMap, fn) {
|
||||||
await Promise.all(allPackageJsons.map((x) => fn(x, versionsMap)));
|
await Promise.all(allPackageJsons.map((x) => fn(x, versionsMap)));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
if (!validateArgs()) {
|
if (!validateArgs()) {
|
||||||
printUsage();
|
printUsage();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const packageJsons = await getPackageJsonsRecursive(PATH_TO_PACKAGES);
|
const packageJsons = await getPackageJsonsRecursive(PATH_TO_PACKAGES);
|
||||||
const versionsMap = await getVersionsMap(packageJsons);
|
const versionsMap = await getVersionsMap(packageJsons);
|
||||||
|
|
||||||
if (mode === "get-version") {
|
if (mode === "get-version") {
|
||||||
const fjs = versionsMap.get("@fluencelabs/fluence");
|
const fjs = versionsMap.get("@fluencelabs/fluence");
|
||||||
console.log(fjs);
|
console.log(fjs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// always check consistency
|
// always check consistency
|
||||||
console.log("Checking versions consistency...");
|
console.log("Checking versions consistency...");
|
||||||
await processPackageJsons(packageJsons, versionsMap, checkConsistency);
|
await processPackageJsons(packageJsons, versionsMap, checkConsistency);
|
||||||
console.log("Versions are consistent");
|
console.log("Versions are consistent");
|
||||||
|
|
||||||
if (mode === "bump-version") {
|
if (mode === "bump-version") {
|
||||||
console.log("Adding postfix: ", postfix);
|
console.log("Adding postfix: ", postfix);
|
||||||
await processPackageJsons(packageJsons, versionsMap, bumpVersions);
|
await processPackageJsons(packageJsons, versionsMap, bumpVersions);
|
||||||
console.log("Done");
|
console.log("Done");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
run();
|
||||||
|
26
package.json
26
package.json
@ -8,15 +8,29 @@
|
|||||||
"node": ">=10",
|
"node": ">=10",
|
||||||
"pnpm": ">=3"
|
"pnpm": ">=3"
|
||||||
},
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint-check": "pnpm run prettier --check && pnpm run eslint",
|
||||||
|
"lint-fix": "pnpm run prettier --write && pnpm run eslint --fix",
|
||||||
|
"prettier": "prettier .",
|
||||||
|
"eslint": "eslint --cache \"**/src/**/*.{js,ts}\""
|
||||||
|
},
|
||||||
"author": "Fluence Labs",
|
"author": "Fluence Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"http-server": "14.1.1",
|
"@total-typescript/ts-reset": "0.5.1",
|
||||||
"puppeteer": "19.7.2",
|
"@tsconfig/strictest": "2.0.2",
|
||||||
"@types/node": "18.13.0",
|
"@types/node": "18.13.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "6.7.3",
|
||||||
|
"@typescript-eslint/parser": "6.7.3",
|
||||||
|
"eslint": "8.50.0",
|
||||||
|
"eslint-config-prettier": "9.0.0",
|
||||||
|
"eslint-plugin-import": "2.28.1",
|
||||||
|
"eslint-plugin-license-header": "0.6.0",
|
||||||
|
"eslint-plugin-unused-imports": "3.0.0",
|
||||||
|
"http-server": "14.1.1",
|
||||||
|
"prettier": "3.0.3",
|
||||||
|
"puppeteer": "19.7.2",
|
||||||
"ts-node": "10.9.1",
|
"ts-node": "10.9.1",
|
||||||
"typescript": "4.7",
|
"typescript": "5.1.6"
|
||||||
"@fluencelabs/aqua-lib": "0.6.0",
|
|
||||||
"@fluencelabs/aqua": "0.9.1-374"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
packages/@tests/.eslintrc.json
Normal file
6
packages/@tests/.eslintrc.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ignorePatterns": ["**/*.css"],
|
||||||
|
"rules": {
|
||||||
|
"no-console": "off"
|
||||||
|
}
|
||||||
|
}
|
@ -53,3 +53,6 @@ func marineTest(wasm64: string) -> f64:
|
|||||||
<- res
|
<- res
|
||||||
|
|
||||||
|
|
||||||
|
func callHappy(a: string, b: f64, c: f64, d: string -> f64) -> f64:
|
||||||
|
res <- d("abc")
|
||||||
|
<- res
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
{
|
{
|
||||||
"name": "@test/aqua_for_test",
|
"name": "@test/aqua_for_test",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Shared aqua code for tests",
|
"description": "Shared aqua code for tests",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"typings": "./dist/index.d.ts",
|
"typings": "./dist/index.d.ts",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10",
|
"node": ">=10",
|
||||||
"pnpm": ">=3"
|
"pnpm": ">=3"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"compile-aqua": "fluence aqua -i ./_aqua -o ./src/_aqua"
|
"compile-aqua": "fluence aqua -i ./_aqua -o ./src/_aqua"
|
||||||
},
|
},
|
||||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||||
"author": "Fluence Labs",
|
"author": "Fluence Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluencelabs/js-client": "workspace:^",
|
"base64-js": "1.5.1"
|
||||||
"base64-js": "1.5.1"
|
},
|
||||||
},
|
"devDependencies": {
|
||||||
"devDependencies": {
|
"@fluencelabs/aqua-lib": "0.6.0",
|
||||||
"@fluencelabs/cli": "0.7.2",
|
"@fluencelabs/cli": "0.7.2",
|
||||||
"@fluencelabs/registry": "0.8.2",
|
"@fluencelabs/js-client": "workspace:^",
|
||||||
"@fluencelabs/aqua-lib": "0.6.0",
|
"@fluencelabs/registry": "0.8.2",
|
||||||
"@fluencelabs/trust-graph": "3.1.2"
|
"@fluencelabs/trust-graph": "3.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,14 @@
|
|||||||
* Aqua version: 0.12.0
|
* Aqua version: 0.12.0
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import type { IFluenceClient as IFluenceClient$$, CallParams as CallParams$$ } from '@fluencelabs/js-client';
|
import type {
|
||||||
|
IFluenceClient as IFluenceClient$$,
|
||||||
|
CallParams as CallParams$$,
|
||||||
|
} from "@fluencelabs/js-client";
|
||||||
import {
|
import {
|
||||||
v5_callFunction as callFunction$$,
|
v5_callFunction as callFunction$$,
|
||||||
v5_registerService as registerService$$,
|
v5_registerService as registerService$$,
|
||||||
} from '@fluencelabs/js-client';
|
} from "@fluencelabs/js-client";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
|
|
||||||
@ -30,49 +31,42 @@ export const test_script = `
|
|||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 0])
|
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 0])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
`
|
`;
|
||||||
|
|
||||||
|
export function test(config?: { ttl?: number }): Promise<void>;
|
||||||
|
|
||||||
export function test(
|
export function test(
|
||||||
config?: {ttl?: number}
|
peer: IFluenceClient$$,
|
||||||
): Promise<void>;
|
config?: { ttl?: number },
|
||||||
|
|
||||||
export function test(
|
|
||||||
peer: IFluenceClient$$,
|
|
||||||
config?: {ttl?: number}
|
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
|
||||||
export function test(...args: any) {
|
export function test(...args: any) {
|
||||||
|
return callFunction$$(
|
||||||
|
args,
|
||||||
return callFunction$$(
|
{
|
||||||
args,
|
functionName: "test",
|
||||||
{
|
arrow: {
|
||||||
"functionName" : "test",
|
tag: "arrow",
|
||||||
"arrow" : {
|
domain: {
|
||||||
"tag" : "arrow",
|
tag: "labeledProduct",
|
||||||
"domain" : {
|
fields: {},
|
||||||
"tag" : "labeledProduct",
|
|
||||||
"fields" : {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"codomain" : {
|
codomain: {
|
||||||
"tag" : "nil"
|
tag: "nil",
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
names: {
|
||||||
|
relay: "-relay-",
|
||||||
|
getDataSrv: "getDataSrv",
|
||||||
|
callbackSrv: "callbackSrv",
|
||||||
|
responseSrv: "callbackSrv",
|
||||||
|
responseFnName: "response",
|
||||||
|
errorHandlingSrv: "errorHandlingSrv",
|
||||||
|
errorFnName: "error",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"names" : {
|
test_script,
|
||||||
"relay" : "-relay-",
|
);
|
||||||
"getDataSrv" : "getDataSrv",
|
|
||||||
"callbackSrv" : "callbackSrv",
|
|
||||||
"responseSrv" : "callbackSrv",
|
|
||||||
"responseFnName" : "response",
|
|
||||||
"errorHandlingSrv" : "errorHandlingSrv",
|
|
||||||
"errorFnName" : "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
test_script
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,84 +1,117 @@
|
|||||||
import { fromByteArray } from 'base64-js';
|
/**
|
||||||
import { Fluence } from '@fluencelabs/js-client';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import type { ClientConfig } from '@fluencelabs/js-client';
|
*
|
||||||
import { registerHelloWorld, helloTest, marineTest, resourceTest } from './_aqua/smoke_test.js';
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
import { test as particleTest } from './_aqua/finalize_particle.js';
|
* you may not use this file except in compliance with the License.
|
||||||
import { wasm } from './wasmb64.js';
|
* 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 { Fluence } from "@fluencelabs/js-client";
|
||||||
|
import type { ClientConfig } from "@fluencelabs/js-client";
|
||||||
|
import { fromByteArray } from "base64-js";
|
||||||
|
|
||||||
|
import { test as particleTest } from "./_aqua/finalize_particle.js";
|
||||||
|
import {
|
||||||
|
registerHelloWorld,
|
||||||
|
helloTest,
|
||||||
|
marineTest,
|
||||||
|
} from "./_aqua/smoke_test.js";
|
||||||
|
import { wasm } from "./wasmb64.js";
|
||||||
|
|
||||||
const relay = {
|
const relay = {
|
||||||
multiaddr: '/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
multiaddr:
|
||||||
peerId: '12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
|
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
};
|
};
|
||||||
|
|
||||||
function generateRandomUint8Array() {
|
function generateRandomUint8Array() {
|
||||||
const uint8Array = new Uint8Array(32);
|
const uint8Array = new Uint8Array(32);
|
||||||
for (let i = 0; i < uint8Array.length; i++) {
|
|
||||||
uint8Array[i] = Math.floor(Math.random() * 256);
|
for (let i = 0; i < uint8Array.length; i++) {
|
||||||
}
|
uint8Array[i] = Math.floor(Math.random() * 256);
|
||||||
return uint8Array;
|
}
|
||||||
|
|
||||||
|
return uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
const optsWithRandomKeyPair = (): ClientConfig => {
|
const optsWithRandomKeyPair = (): ClientConfig => {
|
||||||
return {
|
return {
|
||||||
keyPair: {
|
keyPair: {
|
||||||
type: 'Ed25519',
|
type: "Ed25519",
|
||||||
source: generateRandomUint8Array(),
|
source: generateRandomUint8Array(),
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TestResult = { type: 'success'; data: string } | { type: 'failure'; error: string };
|
export type TestResult =
|
||||||
|
| { type: "success"; data: string }
|
||||||
|
| { type: "failure"; error: string };
|
||||||
|
|
||||||
export const runTest = async (): Promise<TestResult> => {
|
export const runTest = async (): Promise<TestResult> => {
|
||||||
try {
|
try {
|
||||||
Fluence.onConnectionStateChange((state) => console.info('connection state changed: ', state));
|
Fluence.onConnectionStateChange((state) => {
|
||||||
|
console.info("connection state changed: ", state);
|
||||||
|
});
|
||||||
|
|
||||||
console.log('connecting to Fluence Network...');
|
console.log("connecting to Fluence Network...");
|
||||||
console.log('multiaddr: ', relay.multiaddr);
|
console.log("multiaddr: ", relay.multiaddr);
|
||||||
await Fluence.connect(relay, optsWithRandomKeyPair());
|
await Fluence.connect(relay, optsWithRandomKeyPair());
|
||||||
|
|
||||||
console.log('connected');
|
console.log("connected");
|
||||||
|
|
||||||
const relayPeerId = (await Fluence.getClient()).getRelayPeerId();
|
const relayPeerId = Fluence.getClient().getRelayPeerId();
|
||||||
console.log('relay:', relayPeerId);
|
console.log("relay:", relayPeerId);
|
||||||
|
|
||||||
await registerHelloWorld({
|
registerHelloWorld({
|
||||||
hello(str) {
|
hello(str) {
|
||||||
return 'Hello, ' + str + '!';
|
return "Hello, " + str + "!";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const client = await Fluence.getClient();
|
const client = Fluence.getClient();
|
||||||
|
|
||||||
console.log('my peer id: ', client.getPeerId());
|
console.log("my peer id: ", client.getPeerId());
|
||||||
console.log('my sk id: ', fromByteArray(client.getPeerSecretKey()));
|
console.log("my sk id: ", fromByteArray(client.getPeerSecretKey()));
|
||||||
|
|
||||||
console.log('running hello test...');
|
console.log("running hello test...");
|
||||||
const hello = await helloTest();
|
const hello = await helloTest();
|
||||||
console.log('hello test finished, result: ', hello);
|
console.log("hello test finished, result: ", hello);
|
||||||
|
|
||||||
console.log('running marine test...');
|
console.log("running marine test...");
|
||||||
const marine = await marineTest(wasm);
|
const marine = await marineTest(wasm);
|
||||||
|
|
||||||
console.log('running particle test...');
|
console.log("running particle test...");
|
||||||
await particleTest();
|
await particleTest();
|
||||||
|
|
||||||
console.log('marine test finished, result: ', marine);
|
|
||||||
|
|
||||||
const returnVal = {
|
console.log("marine test finished, result: ", marine);
|
||||||
hello,
|
|
||||||
marine,
|
const returnVal = {
|
||||||
};
|
hello,
|
||||||
return { type: 'success', data: JSON.stringify(returnVal) };
|
marine,
|
||||||
} finally {
|
};
|
||||||
console.log('disconnecting from Fluence Network...');
|
|
||||||
await Fluence.disconnect();
|
return { type: "success", data: JSON.stringify(returnVal) };
|
||||||
console.log('disconnected');
|
} finally {
|
||||||
}
|
console.log("disconnecting from Fluence Network...");
|
||||||
|
await Fluence.disconnect();
|
||||||
|
console.log("disconnected");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const runMain = () => {
|
export const runMain = () => {
|
||||||
runTest()
|
runTest()
|
||||||
.then(() => console.log('done!'))
|
.then(() => {
|
||||||
.catch((err) => console.error('error: ', err));
|
console.log("done!");
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("error: ", err);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../../tsconfig.json",
|
"extends": "../../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"module": "NodeNext"
|
"module": "NodeNext"
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"]
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "@test/smoke",
|
"name": "@test/smoke",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Smoke test",
|
"description": "Smoke test",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"typings": "./dist/index.d.ts",
|
"typings": "./dist/index.d.ts",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10",
|
"node": ">=10",
|
||||||
"pnpm": ">=3"
|
"pnpm": ">=3"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"test": "node --loader ts-node/esm ./src/index.ts"
|
"test": "node --loader ts-node/esm ./src/index.ts"
|
||||||
},
|
},
|
||||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||||
"author": "Fluence Labs",
|
"author": "Fluence Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluencelabs/js-client": "workspace:*",
|
"@fluencelabs/js-client": "workspace:*",
|
||||||
"@test/aqua_for_test": "workspace:*"
|
"@test/aqua_for_test": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,21 @@
|
|||||||
import '@fluencelabs/js-client';
|
/**
|
||||||
import { runTest } from '@test/aqua_for_test';
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
runTest().then(() => console.log('Smoke tests succeed!'));
|
import "@fluencelabs/js-client";
|
||||||
|
import { runTest } from "@test/aqua_for_test";
|
||||||
|
|
||||||
|
await runTest();
|
||||||
|
console.log("Smoke tests succeed!");
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../../../tsconfig.json",
|
"extends": "../../../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,50 @@
|
|||||||
{
|
{
|
||||||
"name": "cra-ts",
|
"name": "cra-ts",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@test/aqua_for_test": "workspace:^",
|
"@test/aqua_for_test": "workspace:*",
|
||||||
"@testing-library/jest-dom": "5.16.5",
|
"@testing-library/jest-dom": "5.16.5",
|
||||||
"@testing-library/react": "13.4.0",
|
"@testing-library/react": "13.4.0",
|
||||||
"@testing-library/user-event": "13.5.0",
|
"@testing-library/user-event": "13.5.0",
|
||||||
"@types/jest": "27.5.2",
|
"@types/jest": "27.5.2",
|
||||||
"@types/node": "16.18.12",
|
"@types/node": "16.18.12",
|
||||||
"@types/react": "18.0.27",
|
"@types/react": "18.0.27",
|
||||||
"@types/react-dom": "18.0.10",
|
"@types/react-dom": "18.0.10",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"typescript": "4.9.5",
|
"web-vitals": "2.1.4"
|
||||||
"web-vitals": "2.1.4"
|
},
|
||||||
},
|
"devDependencies": {
|
||||||
"devDependencies": {
|
"@test/test-utils": "workspace:*",
|
||||||
"@test/test-utils": "workspace:^",
|
"puppeteer": "19.7.2"
|
||||||
"puppeteer": "19.7.2"
|
},
|
||||||
},
|
"scripts": {
|
||||||
"scripts": {
|
"test": "node --loader ts-node/esm ./test/index.ts",
|
||||||
"test": "node --loader ts-node/esm ./test/index.ts",
|
"simulate-cdn": "http-server -p 8766 ../../../client/js-client.web.standalone/dist",
|
||||||
"simulate-cdn": "http-server -p 8766 ../../../client/js-client.web.standalone/dist",
|
"start": "react-scripts start",
|
||||||
"start": "react-scripts start",
|
"build": "react-scripts build",
|
||||||
"build": "react-scripts build",
|
"_test": "react-scripts test",
|
||||||
"_test": "react-scripts test",
|
"eject": "react-scripts eject"
|
||||||
"eject": "react-scripts eject"
|
},
|
||||||
},
|
"eslintConfig": {
|
||||||
"eslintConfig": {
|
"extends": [
|
||||||
"extends": [
|
"react-app",
|
||||||
"react-app",
|
"react-app/jest"
|
||||||
"react-app/jest"
|
]
|
||||||
]
|
},
|
||||||
},
|
"browserslist": {
|
||||||
"browserslist": {
|
"production": [
|
||||||
"production": [
|
">0.2%",
|
||||||
">0.2%",
|
"not dead",
|
||||||
"not dead",
|
"not op_mini all"
|
||||||
"not op_mini all"
|
],
|
||||||
],
|
"development": [
|
||||||
"development": [
|
"last 1 chrome version",
|
||||||
"last 1 chrome version",
|
"last 1 firefox version",
|
||||||
"last 1 firefox version",
|
"last 1 safari version"
|
||||||
"last 1 safari version"
|
]
|
||||||
]
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,49 @@
|
|||||||
import { runTest, TestResult } from '@test/aqua_for_test';
|
import { runTest, TestResult } from "@test/aqua_for_test";
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import logo from './logo.svg';
|
import logo from "./logo.svg";
|
||||||
import './App.css';
|
import "./App.css";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [result, setResult] = React.useState<TestResult | null>(null);
|
const [result, setResult] = React.useState<TestResult | null>(null);
|
||||||
|
|
||||||
const onButtonClick = () => {
|
const onButtonClick = () => {
|
||||||
runTest()
|
runTest()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setResult(res);
|
setResult(res);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setResult({ type: 'failure', error: err.toString() });
|
setResult({ type: "failure", error: err.toString() });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<header className="App-header">
|
<header className="App-header">
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
<img src={logo} className="App-logo" alt="logo" />
|
||||||
<p>
|
<p>
|
||||||
Edit <code>src/App.tsx</code> and save to reload.
|
Edit <code>src/App.tsx</code> and save to reload.
|
||||||
</p>
|
</p>
|
||||||
<button id="btn" onClick={onButtonClick}>
|
<button id="btn" onClick={onButtonClick}>
|
||||||
Click to run test
|
Click to run test
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{result && result.type === 'success' && <div id="res">{result.data}</div>}
|
{result && result.type === "success" && (
|
||||||
{result && result.type === 'failure' && <div id="error">{result.error}</div>}
|
<div id="res">{result.data}</div>
|
||||||
<a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer">
|
)}
|
||||||
Learn React
|
{result && result.type === "failure" && (
|
||||||
</a>
|
<div id="error">{result.error}</div>
|
||||||
</header>
|
)}
|
||||||
</div>
|
<a
|
||||||
);
|
className="App-link"
|
||||||
|
href="https://reactjs.org"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Learn React
|
||||||
|
</a>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||||
sans-serif;
|
sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||||
monospace;
|
monospace;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from "react-dom/client";
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from "./reportWebVitals";
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
const root = ReactDOM.createRoot(
|
||||||
|
document.getElementById("root") as HTMLElement,
|
||||||
|
);
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ReportHandler } from 'web-vitals';
|
import { ReportHandler } from "web-vitals";
|
||||||
|
|
||||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
getCLS(onPerfEntry);
|
getCLS(onPerfEntry);
|
||||||
getFID(onPerfEntry);
|
getFID(onPerfEntry);
|
||||||
getFCP(onPerfEntry);
|
getFCP(onPerfEntry);
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
// allows you to do things like:
|
// allows you to do things like:
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
import '@testing-library/jest-dom';
|
import "@testing-library/jest-dom";
|
||||||
|
@ -1,49 +1,53 @@
|
|||||||
import puppeteer from 'puppeteer';
|
import puppeteer from "puppeteer";
|
||||||
import { dirname, join } from 'path';
|
import { dirname, join } from "path";
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
import { CDN_PUBLIC_PATH, startContentServer, stopServer } from '@test/test-utils';
|
import {
|
||||||
import { access, symlink } from 'fs/promises';
|
CDN_PUBLIC_PATH,
|
||||||
|
startContentServer,
|
||||||
|
stopServer,
|
||||||
|
} from "@test/test-utils";
|
||||||
|
import { access, symlink } from "fs/promises";
|
||||||
|
|
||||||
const port = 3001;
|
const port = 3001;
|
||||||
const uri = `http://localhost:${port}/`;
|
const uri = `http://localhost:${port}/`;
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const publicPath = join(__dirname, '../build/');
|
const publicPath = join(__dirname, "../build/");
|
||||||
|
|
||||||
const test = async () => {
|
const test = async () => {
|
||||||
const localServer = await startContentServer(port, publicPath);
|
const localServer = await startContentServer(port, publicPath);
|
||||||
try {
|
try {
|
||||||
await access(join(publicPath, 'source'))
|
await access(join(publicPath, "source"));
|
||||||
} catch {
|
} catch {
|
||||||
await symlink(CDN_PUBLIC_PATH, join(publicPath, 'source'));
|
await symlink(CDN_PUBLIC_PATH, join(publicPath, "source"));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('starting puppeteer...');
|
console.log("starting puppeteer...");
|
||||||
const browser = await puppeteer.launch();
|
const browser = await puppeteer.launch();
|
||||||
const page = (await browser.pages())[0];
|
const page = (await browser.pages())[0];
|
||||||
|
|
||||||
// uncomment to debug what's happening inside the browser
|
// uncomment to debug what's happening inside the browser
|
||||||
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
|
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
|
||||||
|
|
||||||
console.log('going to the page in browser...');
|
console.log("going to the page in browser...");
|
||||||
await page.goto(uri);
|
await page.goto(uri);
|
||||||
|
|
||||||
console.log('clicking button...');
|
console.log("clicking button...");
|
||||||
await page.click('#btn');
|
await page.click("#btn");
|
||||||
|
|
||||||
console.log('waiting for result to appear...');
|
console.log("waiting for result to appear...");
|
||||||
const elem = await page.waitForSelector('#res');
|
const elem = await page.waitForSelector("#res");
|
||||||
|
|
||||||
console.log('getting the content of result div...');
|
console.log("getting the content of result div...");
|
||||||
const content = await elem?.evaluate((x) => x.textContent);
|
const content = await elem?.evaluate((x) => x.textContent);
|
||||||
console.log('raw result: ', content);
|
console.log("raw result: ", content);
|
||||||
|
|
||||||
await browser.close();
|
await browser.close();
|
||||||
await stopServer(localServer);
|
await stopServer(localServer);
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
throw new Error('smoke test failed!');
|
throw new Error("smoke test failed!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test().then(() => console.log('smoke tests succeed!'));
|
test().then(() => console.log("smoke tests succeed!"));
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": ["src", "test"]
|
"include": ["src", "test"]
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "@tests/smoke_web",
|
"name": "@tests/smoke_web",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Smoke test web",
|
"description": "Smoke test web",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"typings": "./dist/index.d.ts",
|
"typings": "./dist/index.d.ts",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10",
|
"node": ">=10",
|
||||||
"pnpm": ">=3"
|
"pnpm": ">=3"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"simulate-cdn": "http-server -p 8765 ../../../client/js-client.web.standalone/dist",
|
"simulate-cdn": "http-server -p 8765 ../../../client/js-client.web.standalone/dist",
|
||||||
"test": "node --loader ts-node/esm ./src/index.ts",
|
"test": "node --loader ts-node/esm ./src/index.ts",
|
||||||
"serve": "http-server public"
|
"serve": "http-server public"
|
||||||
},
|
},
|
||||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||||
"author": "Fluence Labs",
|
"author": "Fluence Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluencelabs/js-client": "workspace:^",
|
"@fluencelabs/js-client": "workspace:*",
|
||||||
"@test/test-utils": "workspace:../../test-utils"
|
"@test/test-utils": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"puppeteer": "19.7.2"
|
"puppeteer": "19.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
const fluence = globalThis.fluence;
|
const fluence = globalThis.fluence;
|
||||||
|
|
||||||
const relay = {
|
const relay = {
|
||||||
multiaddr: '/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
multiaddr:
|
||||||
peerId: '12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
|
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRelayTime = () => {
|
const getRelayTime = () => {
|
||||||
const script = `
|
const script = `
|
||||||
(xor
|
(xor
|
||||||
(seq
|
(seq
|
||||||
(seq
|
(seq
|
||||||
@ -36,75 +37,75 @@ const getRelayTime = () => {
|
|||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const def = {
|
const def = {
|
||||||
functionName: 'getRelayTime',
|
functionName: "getRelayTime",
|
||||||
arrow: {
|
arrow: {
|
||||||
tag: 'arrow',
|
tag: "arrow",
|
||||||
domain: {
|
domain: {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
relayPeerId: {
|
relayPeerId: {
|
||||||
tag: 'scalar',
|
tag: "scalar",
|
||||||
name: 'string',
|
name: "string",
|
||||||
},
|
},
|
||||||
},
|
|
||||||
},
|
|
||||||
codomain: {
|
|
||||||
tag: 'unlabeledProduct',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
tag: 'scalar',
|
|
||||||
name: 'u64',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
names: {
|
},
|
||||||
relay: '-relay-',
|
codomain: {
|
||||||
getDataSrv: 'getDataSrv',
|
tag: "unlabeledProduct",
|
||||||
callbackSrv: 'callbackSrv',
|
items: [
|
||||||
responseSrv: 'callbackSrv',
|
{
|
||||||
responseFnName: 'response',
|
tag: "scalar",
|
||||||
errorHandlingSrv: 'errorHandlingSrv',
|
name: "u64",
|
||||||
errorFnName: 'error',
|
},
|
||||||
},
|
],
|
||||||
};
|
},
|
||||||
|
},
|
||||||
|
names: {
|
||||||
|
relay: "-relay-",
|
||||||
|
getDataSrv: "getDataSrv",
|
||||||
|
callbackSrv: "callbackSrv",
|
||||||
|
responseSrv: "callbackSrv",
|
||||||
|
responseFnName: "response",
|
||||||
|
errorHandlingSrv: "errorHandlingSrv",
|
||||||
|
errorFnName: "error",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const config = {};
|
const config = {};
|
||||||
|
|
||||||
const args = { relayPeerId: relay.peerId };
|
const args = { relayPeerId: relay.peerId };
|
||||||
return fluence.callAquaFunction({
|
return fluence.callAquaFunction({
|
||||||
args,
|
args,
|
||||||
def,
|
def,
|
||||||
script,
|
script,
|
||||||
config,
|
config,
|
||||||
peer: fluence.defaultClient,
|
peer: fluence.defaultClient,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
console.log('starting fluence...');
|
console.log("starting fluence...");
|
||||||
fluence.defaultClient = await fluence.clientFactory(relay);
|
fluence.defaultClient = await fluence.clientFactory(relay, {});
|
||||||
console.log('started fluence');
|
console.log("started fluence");
|
||||||
|
|
||||||
console.log('getting relay time...');
|
console.log("getting relay time...");
|
||||||
const relayTime = await getRelayTime();
|
const relayTime = await getRelayTime();
|
||||||
console.log('got relay time, ', relayTime);
|
console.log("got relay time, ", relayTime);
|
||||||
|
|
||||||
console.log('stopping fluence...');
|
console.log("stopping fluence...");
|
||||||
await fluence.defaultClient.stop();
|
await fluence.defaultClient.stop();
|
||||||
console.log('stopped fluence...');
|
console.log("stopped fluence...");
|
||||||
|
|
||||||
return relayTime;
|
return relayTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
const btn = document.getElementById('btn');
|
const btn = document.getElementById("btn");
|
||||||
|
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener("click", () => {
|
||||||
main().then((res) => {
|
main().then((res) => {
|
||||||
const inner = document.createElement('div');
|
const inner = document.createElement("div");
|
||||||
inner.id = 'res';
|
inner.id = "res";
|
||||||
inner.innerText = res;
|
inner.innerText = res;
|
||||||
document.getElementById('res-placeholder').appendChild(inner);
|
document.getElementById("res-placeholder").appendChild(inner);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,49 +1,76 @@
|
|||||||
import puppeteer from 'puppeteer';
|
/**
|
||||||
import { dirname, join } from 'path';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import { fileURLToPath } from 'url';
|
*
|
||||||
|
* 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 { CDN_PUBLIC_PATH, startCdn, startContentServer, stopServer } from '@test/test-utils';
|
import { symlink, access } from "fs/promises";
|
||||||
import { symlink, access } from 'fs/promises';
|
import { dirname, join } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CDN_PUBLIC_PATH,
|
||||||
|
startContentServer,
|
||||||
|
stopServer,
|
||||||
|
} from "@test/test-utils";
|
||||||
|
import puppeteer from "puppeteer";
|
||||||
|
|
||||||
const port = 3000;
|
const port = 3000;
|
||||||
const uri = `http://localhost:${port}/`;
|
const uri = `http://localhost:${port}/`;
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const publicPath = join(__dirname, '../public/');
|
const publicPath = join(__dirname, "../public/");
|
||||||
|
|
||||||
const test = async () => {
|
const test = async () => {
|
||||||
const localServer = await startContentServer(port, publicPath);
|
const localServer = await startContentServer(port, publicPath);
|
||||||
try {
|
|
||||||
await access(join(publicPath, 'source'))
|
|
||||||
} catch {
|
|
||||||
await symlink(CDN_PUBLIC_PATH, join(publicPath, 'source'));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('starting puppeteer...');
|
try {
|
||||||
const browser = await puppeteer.launch();
|
await access(join(publicPath, "source"));
|
||||||
const page = (await browser.pages())[0];
|
} catch {
|
||||||
|
await symlink(CDN_PUBLIC_PATH, join(publicPath, "source"));
|
||||||
|
}
|
||||||
|
|
||||||
// uncomment to debug what's happening inside the browser
|
console.log("starting puppeteer...");
|
||||||
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
|
const browser = await puppeteer.launch();
|
||||||
|
const page = (await browser.pages())[0];
|
||||||
|
|
||||||
console.log('going to the page in browser...');
|
// uncomment to debug what's happening inside the browser
|
||||||
await page.goto(uri);
|
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
|
||||||
|
|
||||||
console.log('clicking button...');
|
console.log("going to the page in browser...");
|
||||||
await page.click('#btn');
|
await page.goto(uri);
|
||||||
|
|
||||||
console.log('waiting for result to appear...');
|
console.log("clicking button...");
|
||||||
const elem = await page.waitForSelector('#res');
|
await page.click("#btn");
|
||||||
|
|
||||||
console.log('getting the content of result div...');
|
console.log("waiting for result to appear...");
|
||||||
const content = await elem?.evaluate((x) => x.textContent);
|
const elem = await page.waitForSelector("#res");
|
||||||
console.log('raw result: ', content);
|
|
||||||
|
|
||||||
await browser.close();
|
console.log("getting the content of result div...");
|
||||||
await stopServer(localServer);
|
|
||||||
|
|
||||||
if (!content) {
|
const content = await elem?.evaluate((x) => {
|
||||||
throw new Error('smoke test failed!');
|
return x.textContent;
|
||||||
}
|
});
|
||||||
|
|
||||||
|
console.log("raw result: ", content);
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
await stopServer(localServer);
|
||||||
|
|
||||||
|
if (content == null) {
|
||||||
|
throw new Error("smoke test failed!");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test().then(() => console.log('smoke tests succeed!'));
|
void test().then(() => {
|
||||||
|
console.log("smoke tests succeed!");
|
||||||
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../../../tsconfig.json",
|
"extends": "../../../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist", "public"]
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "@test/test-utils",
|
"name": "@test/test-utils",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Test utils",
|
"description": "Test utils",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"typings": "./dist/index.d.ts",
|
"typings": "./dist/index.d.ts",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10",
|
"node": ">=10",
|
||||||
"pnpm": ">=3"
|
"pnpm": ">=3"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc"
|
"build": "tsc"
|
||||||
},
|
},
|
||||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||||
"author": "Fluence Labs",
|
"author": "Fluence Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"serve-handler": "6.1.5"
|
"serve-handler": "6.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/serve-handler": "6.1.1"
|
"@types/serve-handler": "6.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,82 @@
|
|||||||
import handler from 'serve-handler';
|
/**
|
||||||
import { createServer } from 'http';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import type { Server } from 'http';
|
*
|
||||||
|
* 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 { dirname, join } from 'path';
|
import { createServer } from "http";
|
||||||
import { fileURLToPath } from 'url';
|
import type { Server } from "http";
|
||||||
|
import { dirname, join } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
import handler from "serve-handler";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export const CDN_PUBLIC_PATH = join(__dirname, '../../../core/js-client/dist/browser');
|
export const CDN_PUBLIC_PATH = join(
|
||||||
|
__dirname,
|
||||||
|
"../../../core/js-client/dist/browser",
|
||||||
|
);
|
||||||
|
|
||||||
export const startCdn = (port: number) => startContentServer(port, CDN_PUBLIC_PATH);
|
export const startCdn = (port: number) => {
|
||||||
|
return startContentServer(port, CDN_PUBLIC_PATH);
|
||||||
|
};
|
||||||
|
|
||||||
export const startContentServer = (port: number, publicDir: string): Promise<Server> => {
|
export const startContentServer = (
|
||||||
const server = createServer((request, response) => {
|
port: number,
|
||||||
return handler(request, response, {
|
publicDir: string,
|
||||||
public: publicDir,
|
): Promise<Server> => {
|
||||||
rewrites: [{
|
const server = createServer((request, response) => {
|
||||||
source: '/js-client.min.js',
|
void handler(request, response, {
|
||||||
destination: '/source/index.umd.cjs'
|
public: publicDir,
|
||||||
}],
|
rewrites: [
|
||||||
headers: [{
|
{
|
||||||
source: '**/*',
|
source: "/js-client.min.js",
|
||||||
headers: [
|
destination: "/source/index.umd.cjs",
|
||||||
{ key: 'Cross-Origin-Opener-Policy', value: 'same-origin' },
|
},
|
||||||
{ key: 'Cross-Origin-Embedder-Policy', value: 'require-corp' }
|
],
|
||||||
]
|
headers: [
|
||||||
}]
|
{
|
||||||
});
|
source: "**/*",
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
key: "Cross-Origin-Opener-Policy",
|
||||||
|
value: "same-origin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Cross-Origin-Embedder-Policy",
|
||||||
|
value: "require-corp",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return new Promise<Server>((resolve) => {
|
return new Promise<Server>((resolve) => {
|
||||||
const result = server.listen(port, () => {
|
const result = server.listen(port, () => {
|
||||||
console.log(`server started on port ${port}`);
|
console.log(`server started on port ${port}`);
|
||||||
console.log(`public dir ${publicDir}`);
|
console.log(`public dir ${publicDir}`);
|
||||||
resolve(result);
|
resolve(result);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const stopServer = (app: Server): Promise<void> => {
|
export const stopServer = (app: Server): Promise<void> => {
|
||||||
return new Promise<void>((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
app.close(() => {
|
app.close(() => {
|
||||||
console.log('server stopped');
|
console.log("server stopped");
|
||||||
resolve();
|
resolve();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../../tsconfig.json",
|
"extends": "../../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"]
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
3
packages/core/aqua-to-js/.eslintrc.json
Normal file
3
packages/core/aqua-to-js/.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignorePatterns": ["src/**/__snapshots__/**/*"]
|
||||||
|
}
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/js-client bumped to 0.1.7
|
- @fluencelabs/js-client bumped to 0.1.7
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
## 0.0.1 (2023-09-22)
|
## 0.0.1 (2023-09-22)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **aqua-compiler:** JS-client aqua wrapper [fixes DXJ-461] ([#347](https://github.com/fluencelabs/js-client/issues/347)) ([7fff3b1](https://github.com/fluencelabs/js-client/commit/7fff3b1c0374eef76ab4e665b13cf97b5c50ff70))
|
- **aqua-compiler:** JS-client aqua wrapper [fixes DXJ-461] ([#347](https://github.com/fluencelabs/js-client/issues/347)) ([7fff3b1](https://github.com/fluencelabs/js-client/commit/7fff3b1c0374eef76ab4e665b13cf97b5c50ff70))
|
||||||
|
@ -1,31 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "@fluencelabs/aqua-to-js",
|
"name": "@fluencelabs/aqua-to-js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.4",
|
"version": "0.0.4",
|
||||||
"description": "Tool for generating aqua wrapper",
|
"description": "Tool for generating aqua wrapper",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"build": "tsc"
|
"build": "tsc"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Fluence Labs",
|
"author": "Fluence Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ts-pattern": "5.0.5"
|
"ts-pattern": "5.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fluencelabs/aqua-api": "0.12.0",
|
"@fluencelabs/aqua-api": "0.12.0",
|
||||||
"@fluencelabs/aqua-lib": "0.7.3",
|
"@fluencelabs/aqua-lib": "0.7.3",
|
||||||
"@fluencelabs/interfaces": "workspace:*",
|
"@fluencelabs/interfaces": "workspace:*",
|
||||||
"@fluencelabs/js-client": "workspace:*",
|
"@fluencelabs/js-client": "workspace:*",
|
||||||
"@fluencelabs/registry": "0.8.7",
|
"@fluencelabs/registry": "0.8.7",
|
||||||
"@fluencelabs/spell": "0.5.20",
|
"@fluencelabs/spell": "0.5.20",
|
||||||
"@fluencelabs/trust-graph": "0.4.7",
|
"@fluencelabs/trust-graph": "0.4.7",
|
||||||
"typescript": "5.1.6",
|
"vitest": "0.34.6"
|
||||||
"vitest": "0.29.7"
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,85 +14,136 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ArrowType, ArrowWithoutCallbacks, NonArrowType, ProductType } from '@fluencelabs/interfaces';
|
import { ArrowWithoutCallbacks, NonArrowType } from "@fluencelabs/interfaces";
|
||||||
import { match, P } from 'ts-pattern';
|
import { match, P } from "ts-pattern";
|
||||||
import { getFuncArgs } from './utils.js';
|
|
||||||
|
|
||||||
export function genTypeName(t: NonArrowType | ProductType<NonArrowType> | ArrowWithoutCallbacks, name: string): readonly [string | undefined, string] {
|
import { getFuncArgs } from "./utils.js";
|
||||||
const genType = typeToTs(t);
|
|
||||||
return match(t)
|
|
||||||
.with(
|
|
||||||
{ tag: 'nil' },
|
|
||||||
() => [undefined, 'void'] as const
|
|
||||||
).with(
|
|
||||||
{ tag: 'struct' },
|
|
||||||
() => [`export type ${name} = ${genType}`, name] as const
|
|
||||||
).with(
|
|
||||||
{ tag: P.union('labeledProduct', 'unlabeledProduct') },
|
|
||||||
(item) => {
|
|
||||||
const args = item.tag === 'labeledProduct'
|
|
||||||
? Object.values(item.fields)
|
|
||||||
: item.items;
|
|
||||||
|
|
||||||
if (args.length === 1) {
|
|
||||||
return genTypeName(args[0], name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [`export type ${name} = ${genType}`, name] as const;
|
export function genTypeName(
|
||||||
},
|
t: NonArrowType | ArrowWithoutCallbacks,
|
||||||
).otherwise(() => [undefined, genType] as const);
|
name: string,
|
||||||
|
): readonly [string | undefined, string] {
|
||||||
|
const genType = typeToTs(t);
|
||||||
|
return match(t)
|
||||||
|
.with({ tag: "nil" }, () => {
|
||||||
|
return [undefined, "void"] as const;
|
||||||
|
})
|
||||||
|
.with({ tag: "struct" }, () => {
|
||||||
|
return [`export type ${name} = ${genType}`, name] as const;
|
||||||
|
})
|
||||||
|
.with({ tag: P.union("labeledProduct", "unlabeledProduct") }, (item) => {
|
||||||
|
const args =
|
||||||
|
item.tag === "labeledProduct" ? Object.values(item.fields) : item.items;
|
||||||
|
|
||||||
|
if (args.length === 1) {
|
||||||
|
return genTypeName(args[0], name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [`export type ${name} = ${genType}`, name] as const;
|
||||||
|
})
|
||||||
|
.otherwise(() => {
|
||||||
|
return [undefined, genType] as const;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks | ProductType<NonArrowType>): string {
|
export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks): string {
|
||||||
return match(t)
|
return match(t)
|
||||||
|
.with({ tag: "nil" }, () => {
|
||||||
|
return "null";
|
||||||
|
})
|
||||||
|
.with({ tag: "option" }, ({ type }) => {
|
||||||
|
return typeToTs(type) + " | null";
|
||||||
|
})
|
||||||
|
.with({ tag: "scalar" }, ({ name }) => {
|
||||||
|
return match(name)
|
||||||
.with(
|
.with(
|
||||||
{ tag: 'nil' },
|
P.union(
|
||||||
() => 'null'
|
"u8",
|
||||||
).with(
|
"u16",
|
||||||
{ tag: 'option' },
|
"u32",
|
||||||
({ type }) => typeToTs(type) + ' | null'
|
"u64",
|
||||||
).with(
|
"i8",
|
||||||
{ tag: 'scalar' },
|
"i16",
|
||||||
({ name }) => match(name)
|
"i32",
|
||||||
.with(P.union('u8', 'u16', 'u32', 'u64', 'i8', 'i16', 'i32', 'i64', 'f32', 'f64'), () => 'number')
|
"i64",
|
||||||
.with('bool', () => 'boolean')
|
"f32",
|
||||||
.with('string', () => 'string')
|
"f64",
|
||||||
.with(P._, () => 'any').exhaustive()
|
),
|
||||||
).with(
|
() => {
|
||||||
{ tag: 'array' },
|
return "number";
|
||||||
({ type }) => typeToTs(type) + '[]'
|
},
|
||||||
).with(
|
)
|
||||||
{ tag: 'struct' },
|
.with("bool", () => {
|
||||||
({ fields }) => `{ ${Object.entries(fields).map(([field, type]) => `${field}: ${typeToTs(type)};`).join(' ')} }`
|
return "boolean";
|
||||||
).with(
|
})
|
||||||
{ tag: 'labeledProduct' },
|
.with("string", () => {
|
||||||
({ fields }) => `{ ${Object.entries(fields).map(([field, type]) => `${field}: ${typeToTs(type)};`).join(' ')} }`
|
return "string";
|
||||||
).with(
|
})
|
||||||
{ tag: 'unlabeledProduct' },
|
.with(P._, () => {
|
||||||
({ items }) => `[${items.map(item => typeToTs(item)).join(', ')}]`
|
return "any";
|
||||||
).with(
|
})
|
||||||
{ tag: 'arrow' },
|
.exhaustive();
|
||||||
({ tag, domain, codomain }) => {
|
})
|
||||||
const retType = codomain.tag === 'nil'
|
.with({ tag: "array" }, ({ type }) => {
|
||||||
? 'void'
|
return typeToTs(type) + "[]";
|
||||||
: codomain.items.length === 1
|
})
|
||||||
? typeToTs(codomain.items[0])
|
.with({ tag: "struct" }, ({ fields }) => {
|
||||||
: typeToTs(codomain);
|
return `{ ${Object.entries(fields)
|
||||||
|
.map(([field, type]) => {
|
||||||
const args = getFuncArgs(domain).map(([name, type]) => ([name, typeToTs(type)]));
|
return `${field}: ${typeToTs(type)};`;
|
||||||
|
})
|
||||||
|
.join(" ")} }`;
|
||||||
|
})
|
||||||
|
.with({ tag: "labeledProduct" }, ({ fields }) => {
|
||||||
|
return `{ ${Object.entries(fields)
|
||||||
|
.map(([field, type]) => {
|
||||||
|
return `${field}: ${typeToTs(type)};`;
|
||||||
|
})
|
||||||
|
.join(" ")} }`;
|
||||||
|
})
|
||||||
|
.with({ tag: "unlabeledProduct" }, ({ items }) => {
|
||||||
|
return `[${items
|
||||||
|
.map((item) => {
|
||||||
|
return typeToTs(item);
|
||||||
|
})
|
||||||
|
.join(", ")}]`;
|
||||||
|
})
|
||||||
|
.with({ tag: "arrow" }, ({ domain, codomain }) => {
|
||||||
|
const retType =
|
||||||
|
codomain.tag === "nil"
|
||||||
|
? "void"
|
||||||
|
: codomain.items.length === 1
|
||||||
|
? typeToTs(codomain.items[0])
|
||||||
|
: typeToTs(codomain);
|
||||||
|
|
||||||
const generic = args.length === 0 ? 'null' : args.map(([name]) => `'${name}'`).join(' | ');
|
const args = getFuncArgs(domain).map(([name, type]) => {
|
||||||
args.push(['callParams', `CallParams$$<${generic}>`]);
|
return [name, typeToTs(type)];
|
||||||
|
});
|
||||||
|
|
||||||
const funcArgs = args.map(([name, type]) => `${name}: ${type}`).join(', ');
|
const generic =
|
||||||
|
args.length === 0
|
||||||
|
? "null"
|
||||||
|
: args
|
||||||
|
.map(([name]) => {
|
||||||
|
return `'${name}'`;
|
||||||
|
})
|
||||||
|
.join(" | ");
|
||||||
|
|
||||||
return `(${funcArgs}) => ${retType} | Promise<${retType}>`;
|
args.push(["callParams", `CallParams$$<${generic}>`]);
|
||||||
}
|
|
||||||
).with(
|
const funcArgs = args
|
||||||
{ tag: 'topType' },
|
.map(([name, type]) => {
|
||||||
() => 'unknown'
|
return `${name}: ${type}`;
|
||||||
).with(
|
})
|
||||||
{ tag: 'bottomType' },
|
.join(", ");
|
||||||
() => 'never'
|
|
||||||
).exhaustive();
|
return `(${funcArgs}) => ${retType} | Promise<${retType}>`;
|
||||||
}
|
})
|
||||||
|
.with({ tag: "topType" }, () => {
|
||||||
|
return "unknown";
|
||||||
|
})
|
||||||
|
.with({ tag: "bottomType" }, () => {
|
||||||
|
return "never";
|
||||||
|
})
|
||||||
|
.exhaustive();
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,4 +14,4 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const CLIENT = 'IFluenceClient$$';
|
export const CLIENT = "IFluenceClient$$";
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 {
|
|
||||||
ArrayType,
|
|
||||||
BottomType, LabeledProductType,
|
|
||||||
NilType,
|
|
||||||
NonArrowType,
|
|
||||||
OptionType,
|
|
||||||
ScalarType,
|
|
||||||
StructType,
|
|
||||||
TopType, UnlabeledProductType
|
|
||||||
} from '@fluencelabs/interfaces';
|
|
||||||
|
|
||||||
// Type definitions for inferring ts types from air json definition
|
|
||||||
// In the future we may remove string type declaration and move to type inference.
|
|
||||||
|
|
||||||
type GetTsTypeFromScalar<T extends ScalarType> = T['name'] extends 'u8' | 'u16' | 'u32' | 'u64' | 'i8' | 'i16' | 'i32' | 'i64' | 'f32' | 'f64'
|
|
||||||
? number
|
|
||||||
: T['name'] extends 'bool'
|
|
||||||
? boolean
|
|
||||||
: T['name'] extends 'string'
|
|
||||||
? string
|
|
||||||
: never;
|
|
||||||
|
|
||||||
type MapTuple<T> = { [K in keyof T]: T[K] extends NonArrowType ? GetTsType<T[K]> : never }
|
|
||||||
|
|
||||||
type GetTsType<T extends NonArrowType> = T extends NilType
|
|
||||||
? null
|
|
||||||
: T extends ArrayType
|
|
||||||
? GetTsType<T['type']>[]
|
|
||||||
: T extends StructType
|
|
||||||
? { [K in keyof T]: GetTsType<T> }
|
|
||||||
: T extends OptionType
|
|
||||||
? GetTsType<T['type']> | null
|
|
||||||
: T extends ScalarType
|
|
||||||
? GetTsTypeFromScalar<T>
|
|
||||||
: T extends TopType
|
|
||||||
? unknown
|
|
||||||
: T extends BottomType
|
|
||||||
? never
|
|
||||||
: T extends Exclude<UnlabeledProductType<infer H>, NilType>
|
|
||||||
? MapTuple<H>
|
|
||||||
: T extends Exclude<LabeledProductType<infer H>, NilType>
|
|
||||||
? H extends NonArrowType
|
|
||||||
? { [K in keyof T['fields']]: GetTsType<H> }
|
|
||||||
: never
|
|
||||||
: never;
|
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,36 +14,40 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, expect, it } from 'vitest';
|
import url from "url";
|
||||||
import { generateTypes, generateSources } from '../index.js';
|
|
||||||
import { compileFromPath } from '@fluencelabs/aqua-api';
|
|
||||||
import url from 'url';
|
|
||||||
import { getPackageJsonContent, PackageJson } from '../../utils.js';
|
|
||||||
|
|
||||||
describe('Aqua to js/ts compiler', () => {
|
import { compileFromPath } from "@fluencelabs/aqua-api";
|
||||||
it('compiles smoke tests successfully', async () => {
|
import { describe, expect, it } from "vitest";
|
||||||
const res = await compileFromPath({
|
|
||||||
filePath: url.fileURLToPath(new URL('./sources/smoke_test.aqua', import.meta.url)),
|
|
||||||
imports: ['./node_modules'],
|
|
||||||
targetType: 'air'
|
|
||||||
});
|
|
||||||
|
|
||||||
const pkg: PackageJson = {
|
|
||||||
...(await getPackageJsonContent()),
|
|
||||||
version: '0.0.0',
|
|
||||||
devDependencies: {
|
|
||||||
'@fluencelabs/aqua-api': '0.0.0'
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const jsResult = await generateSources(res, 'js', pkg);
|
|
||||||
const jsTypes = await generateTypes(res, pkg);
|
|
||||||
|
|
||||||
expect(jsResult).toMatchSnapshot();
|
|
||||||
expect(jsTypes).toMatchSnapshot();
|
|
||||||
|
|
||||||
const tsResult = await generateSources(res, 'ts', pkg);
|
import { getPackageJsonContent, PackageJson } from "../../utils.js";
|
||||||
|
import { generateTypes, generateSources } from "../index.js";
|
||||||
|
|
||||||
expect(tsResult).toMatchSnapshot();
|
describe("Aqua to js/ts compiler", () => {
|
||||||
|
it("compiles smoke tests successfully", async () => {
|
||||||
|
const res = await compileFromPath({
|
||||||
|
filePath: url.fileURLToPath(
|
||||||
|
new URL("./sources/smoke_test.aqua", import.meta.url),
|
||||||
|
),
|
||||||
|
imports: ["./node_modules"],
|
||||||
|
targetType: "air",
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
const pkg: PackageJson = {
|
||||||
|
...(await getPackageJsonContent()),
|
||||||
|
version: "0.0.0",
|
||||||
|
devDependencies: {
|
||||||
|
"@fluencelabs/aqua-api": "0.0.0",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const jsResult = generateSources(res, "js", pkg);
|
||||||
|
const jsTypes = generateTypes(res, pkg);
|
||||||
|
|
||||||
|
expect(jsResult).toMatchSnapshot();
|
||||||
|
expect(jsTypes).toMatchSnapshot();
|
||||||
|
|
||||||
|
const tsResult = generateSources(res, "ts", pkg);
|
||||||
|
|
||||||
|
expect(tsResult).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,24 +14,38 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { recursiveRenameLaquaProps } from '../utils.js';
|
import { recursiveRenameLaquaProps } from "../utils.js";
|
||||||
import { AquaFunction, TypeGenerator } from './interfaces.js';
|
|
||||||
|
|
||||||
export function generateFunctions(typeGenerator: TypeGenerator, functions: Record<string, AquaFunction>) {
|
import { AquaFunction, TypeGenerator } from "./interfaces.js";
|
||||||
return Object.values(functions).map(func => generateFunction(typeGenerator, func)).join('\n\n');
|
|
||||||
|
export function generateFunctions(
|
||||||
|
typeGenerator: TypeGenerator,
|
||||||
|
functions: Record<string, AquaFunction>,
|
||||||
|
) {
|
||||||
|
return Object.values(functions)
|
||||||
|
.map((func) => {
|
||||||
|
return generateFunction(typeGenerator, func);
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeepToType<T> = { [K in keyof T]: DeepToType<T[K]> };
|
||||||
|
|
||||||
function generateFunction(typeGenerator: TypeGenerator, func: AquaFunction) {
|
function generateFunction(typeGenerator: TypeGenerator, func: AquaFunction) {
|
||||||
const scriptConstName = func.funcDef.functionName + '_script';
|
const funcDef: DeepToType<typeof func.funcDef> = func.funcDef;
|
||||||
return `export const ${scriptConstName} = \`
|
const scriptConstName = func.funcDef.functionName + "_script";
|
||||||
|
return `export const ${scriptConstName} = \`
|
||||||
${func.script}\`;
|
${func.script}\`;
|
||||||
|
|
||||||
${typeGenerator.funcType(func)}
|
${typeGenerator.funcType(func)}
|
||||||
export function ${func.funcDef.functionName}(${typeGenerator.type('...args', 'any[]')}) {
|
export function ${func.funcDef.functionName}(${typeGenerator.type(
|
||||||
|
"...args",
|
||||||
|
"any[]",
|
||||||
|
)}) {
|
||||||
return callFunction$$(
|
return callFunction$$(
|
||||||
args,
|
args,
|
||||||
${JSON.stringify(recursiveRenameLaquaProps(func.funcDef), null, 4)},
|
${JSON.stringify(recursiveRenameLaquaProps(funcDef), null, 4)},
|
||||||
${scriptConstName}
|
${scriptConstName}
|
||||||
);
|
);
|
||||||
}`
|
}`;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,25 +14,33 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { OutputType } from './interfaces.js';
|
import { PackageJson } from "../utils.js";
|
||||||
import { PackageJson } from '../utils.js';
|
|
||||||
|
|
||||||
export default function generateHeader({ version, devDependencies }: PackageJson, outputType: OutputType) {
|
import { OutputType } from "./interfaces.js";
|
||||||
return `/* eslint-disable */
|
|
||||||
|
export default function generateHeader(
|
||||||
|
{ version, devDependencies }: PackageJson,
|
||||||
|
outputType: OutputType,
|
||||||
|
) {
|
||||||
|
return `/* eslint-disable */
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* This file is generated using:
|
* This file is generated using:
|
||||||
* @fluencelabs/aqua-api version: ${devDependencies['@fluencelabs/aqua-api']}
|
* @fluencelabs/aqua-api version: ${devDependencies["@fluencelabs/aqua-api"]}
|
||||||
* @fluencelabs/aqua-to-js version: ${version}
|
* @fluencelabs/aqua-to-js version: ${version}
|
||||||
* If you find any bugs in generated AIR, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
* If you find any bugs in generated AIR, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
||||||
* If you find any bugs in generated JS/TS, please write an issue on GitHub: https://github.com/fluencelabs/js-client/issues
|
* If you find any bugs in generated JS/TS, please write an issue on GitHub: https://github.com/fluencelabs/js-client/issues
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
${outputType === 'ts' ? 'import type { IFluenceClient as IFluenceClient$$, CallParams as CallParams$$ } from \'@fluencelabs/js-client\';' : ''}
|
${
|
||||||
|
outputType === "ts"
|
||||||
|
? "import type { IFluenceClient as IFluenceClient$$, CallParams as CallParams$$ } from '@fluencelabs/js-client';"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
|
||||||
import {
|
import {
|
||||||
v5_callFunction as callFunction$$,
|
v5_callFunction as callFunction$$,
|
||||||
v5_registerService as registerService$$,
|
v5_registerService as registerService$$,
|
||||||
} from '@fluencelabs/js-client';`;
|
} from '@fluencelabs/js-client';`;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,46 +14,80 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CompilationResult, JSTypeGenerator, OutputType, TSTypeGenerator, TypeGenerator } from './interfaces.js';
|
import { PackageJson } from "../utils.js";
|
||||||
import { PackageJson } from '../utils.js';
|
|
||||||
import { generateServices } from './service.js';
|
|
||||||
import { generateFunctions } from './function.js';
|
|
||||||
import header from './header.js';
|
|
||||||
|
|
||||||
const typeGenerators: Record<OutputType, TypeGenerator> = {
|
import { generateFunctions } from "./function.js";
|
||||||
'js': new JSTypeGenerator(),
|
import header from "./header.js";
|
||||||
'ts': new TSTypeGenerator()
|
import {
|
||||||
|
CompilationResult,
|
||||||
|
JSTypeGenerator,
|
||||||
|
OutputType,
|
||||||
|
TSTypeGenerator,
|
||||||
|
TypeGenerator,
|
||||||
|
} from "./interfaces.js";
|
||||||
|
import { generateServices } from "./service.js";
|
||||||
|
|
||||||
|
const typeGenerators: Record<OutputType, TypeGenerator> = {
|
||||||
|
js: new JSTypeGenerator(),
|
||||||
|
ts: new TSTypeGenerator(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function generateSources({ services, functions }: CompilationResult, outputType: OutputType, packageJson: PackageJson) {
|
export function generateSources(
|
||||||
const typeGenerator = typeGenerators[outputType];
|
{ services, functions }: CompilationResult,
|
||||||
return `${header(packageJson, outputType)}
|
outputType: OutputType,
|
||||||
|
packageJson: PackageJson,
|
||||||
|
) {
|
||||||
|
const typeGenerator = typeGenerators[outputType];
|
||||||
|
return `${header(packageJson, outputType)}
|
||||||
|
|
||||||
${Object.entries(services).length > 0 ? `// Services
|
${
|
||||||
|
Object.entries(services).length > 0
|
||||||
|
? `// Services
|
||||||
${generateServices(typeGenerator, services)}
|
${generateServices(typeGenerator, services)}
|
||||||
` : ''}
|
`
|
||||||
${Object.entries(functions).length > 0 ? `// Functions
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
Object.entries(functions).length > 0
|
||||||
|
? `// Functions
|
||||||
${generateFunctions(typeGenerator, functions)}
|
${generateFunctions(typeGenerator, functions)}
|
||||||
`: ''}`
|
`
|
||||||
|
: ""
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateTypes({ services, functions }: CompilationResult, packageJson: PackageJson) {
|
export function generateTypes(
|
||||||
const typeGenerator = typeGenerators['ts'];
|
{ services, functions }: CompilationResult,
|
||||||
|
packageJson: PackageJson,
|
||||||
const generatedServices = Object.entries(services)
|
) {
|
||||||
.map(([srvName, srvDef]) => typeGenerator.serviceType(srvName, srvDef))
|
const typeGenerator = typeGenerators["ts"];
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
const generatedFunctions = Object.entries(functions)
|
|
||||||
.map(([funcName, funcDef]) => typeGenerator.funcType(funcDef))
|
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
return `${header(packageJson, 'ts')}
|
|
||||||
|
|
||||||
${Object.entries(services).length > 0 ? `// Services
|
const generatedServices = Object.entries(services)
|
||||||
|
.map(([srvName, srvDef]) => {
|
||||||
|
return typeGenerator.serviceType(srvName, srvDef);
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const generatedFunctions = Object.entries(functions)
|
||||||
|
.map(([, funcDef]) => {
|
||||||
|
return typeGenerator.funcType(funcDef);
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
return `${header(packageJson, "ts")}
|
||||||
|
|
||||||
|
${
|
||||||
|
Object.entries(services).length > 0
|
||||||
|
? `// Services
|
||||||
${generatedServices}
|
${generatedServices}
|
||||||
` : ''}
|
`
|
||||||
${Object.entries(functions).length > 0 ? `// Functions
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
Object.entries(functions).length > 0
|
||||||
|
? `// Functions
|
||||||
${generatedFunctions}
|
${generatedFunctions}
|
||||||
`: ''}`
|
`
|
||||||
}
|
: ""
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,123 +14,160 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CLIENT } from '../constants.js';
|
import { FunctionCallDef, ServiceDef } from "@fluencelabs/interfaces";
|
||||||
import { FunctionCallDef, ServiceDef } from '@fluencelabs/interfaces';
|
|
||||||
import { genTypeName, typeToTs } from '../common.js';
|
import { genTypeName, typeToTs } from "../common.js";
|
||||||
import { capitalize, getFuncArgs } from '../utils.js';
|
import { CLIENT } from "../constants.js";
|
||||||
|
import { capitalize, getFuncArgs } from "../utils.js";
|
||||||
|
|
||||||
export interface TypeGenerator {
|
export interface TypeGenerator {
|
||||||
type(field: string, type: string): string;
|
type(field: string, type: string): string;
|
||||||
generic(field: string, type: string): string;
|
generic(field: string, type: string): string;
|
||||||
bang(field: string): string;
|
bang(field: string): string;
|
||||||
funcType(funcDef: AquaFunction): string;
|
funcType(funcDef: AquaFunction): string;
|
||||||
serviceType(srvName: string, srvDef: ServiceDef): string;
|
serviceType(srvName: string, srvDef: ServiceDef): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TSTypeGenerator implements TypeGenerator {
|
export class TSTypeGenerator implements TypeGenerator {
|
||||||
bang(field: string): string {
|
bang(field: string): string {
|
||||||
return `${field}!`;
|
return `${field}!`;
|
||||||
}
|
}
|
||||||
|
|
||||||
generic(field: string, type: string): string {
|
generic(field: string, type: string): string {
|
||||||
return `${field}<${type}>`;
|
return `${field}<${type}>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
type(field: string, type: string): string {
|
type(field: string, type: string): string {
|
||||||
return `${field}: ${type}`;
|
return `${field}: ${type}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
funcType({ funcDef }: AquaFunction): string {
|
funcType({ funcDef }: AquaFunction): string {
|
||||||
const args = getFuncArgs(funcDef.arrow.domain).map(([name, type]) => {
|
const args = getFuncArgs(funcDef.arrow.domain).map(([name, type]) => {
|
||||||
const [typeDesc, t] = genTypeName(type, capitalize(funcDef.functionName) + 'Arg' + capitalize(name));
|
const [typeDesc, t] = genTypeName(
|
||||||
return [typeDesc, `${name}: ${t}`] as const;
|
type,
|
||||||
});
|
capitalize(funcDef.functionName) + "Arg" + capitalize(name),
|
||||||
args.push([undefined, `config?: {ttl?: number}`]);
|
);
|
||||||
|
|
||||||
const argsDefs = args.map(([, def]) => " " + def);
|
return [typeDesc, `${name}: ${t}`] as const;
|
||||||
const argsDesc = args.filter(([desc]) => desc !== undefined).map(([desc]) => desc);
|
});
|
||||||
|
|
||||||
const functionOverloads = [
|
args.push([undefined, `config?: {ttl?: number}`]);
|
||||||
argsDefs.join(',\n'),
|
|
||||||
[` peer: ${CLIENT}`, ...argsDefs].join(',\n')
|
|
||||||
];
|
|
||||||
|
|
||||||
const [resTypeDesc, resType] = genTypeName(funcDef.arrow.codomain, capitalize(funcDef.functionName) + "Result");
|
|
||||||
|
|
||||||
return [
|
const argsDefs = args.map(([, def]) => {
|
||||||
argsDesc.join('\n'),
|
return " " + def;
|
||||||
resTypeDesc || "",
|
});
|
||||||
functionOverloads.flatMap(fo => [
|
|
||||||
`export function ${funcDef.functionName}(`,
|
|
||||||
fo,
|
|
||||||
`): Promise<${resType}>;`,
|
|
||||||
''
|
|
||||||
]).join('\n')
|
|
||||||
].filter(s => s !== '').join('\n\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceType(srvName: string, srvDef: ServiceDef): string {
|
const argsDesc = args
|
||||||
const members = srvDef.functions.tag === 'nil' ? [] : Object.entries(srvDef.functions.fields);
|
.filter(([desc]) => {
|
||||||
|
return desc !== undefined;
|
||||||
|
})
|
||||||
|
.map(([desc]) => {
|
||||||
|
return desc;
|
||||||
|
});
|
||||||
|
|
||||||
const interfaceDefs = members
|
const functionOverloads = [
|
||||||
.map(([name, arrow]) => {
|
argsDefs.join(",\n"),
|
||||||
return ` ${name}: ${typeToTs(arrow)};`;
|
[` peer: ${CLIENT}`, ...argsDefs].join(",\n"),
|
||||||
})
|
];
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
const interfaces = [`export interface ${srvName}Def {`, interfaceDefs, '}'].join('\n');
|
const [resTypeDesc, resType] = genTypeName(
|
||||||
|
funcDef.arrow.codomain,
|
||||||
const peerDecl = `peer: ${CLIENT}`;
|
capitalize(funcDef.functionName) + "Result",
|
||||||
const serviceDecl = `service: ${srvName}Def`;
|
);
|
||||||
const serviceIdDecl = `serviceId: string`;
|
|
||||||
const registerServiceArgs = [
|
|
||||||
[serviceDecl],
|
|
||||||
[serviceIdDecl, serviceDecl],
|
|
||||||
[peerDecl, serviceDecl],
|
|
||||||
[peerDecl, serviceIdDecl, serviceDecl]
|
|
||||||
];
|
|
||||||
|
|
||||||
return [interfaces, ...registerServiceArgs.map(registerServiceArg => {
|
return [
|
||||||
const args = registerServiceArg.join(', ');
|
argsDesc.join("\n"),
|
||||||
return `export function register${srvName}(${args}): void;`
|
resTypeDesc ?? "",
|
||||||
})].join('\n');
|
functionOverloads
|
||||||
}
|
.flatMap((fo) => {
|
||||||
|
return [
|
||||||
|
`export function ${funcDef.functionName}(`,
|
||||||
|
fo,
|
||||||
|
`): Promise<${resType}>;`,
|
||||||
|
"",
|
||||||
|
];
|
||||||
|
})
|
||||||
|
.join("\n"),
|
||||||
|
]
|
||||||
|
.filter((s) => {
|
||||||
|
return s !== "";
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceType(srvName: string, srvDef: ServiceDef): string {
|
||||||
|
const members =
|
||||||
|
srvDef.functions.tag === "nil"
|
||||||
|
? []
|
||||||
|
: Object.entries(srvDef.functions.fields);
|
||||||
|
|
||||||
|
const interfaceDefs = members
|
||||||
|
.map(([name, arrow]) => {
|
||||||
|
return ` ${name}: ${typeToTs(arrow)};`;
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const interfaces = [
|
||||||
|
`export interface ${srvName}Def {`,
|
||||||
|
interfaceDefs,
|
||||||
|
"}",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const peerDecl = `peer: ${CLIENT}`;
|
||||||
|
const serviceDecl = `service: ${srvName}Def`;
|
||||||
|
const serviceIdDecl = `serviceId: string`;
|
||||||
|
|
||||||
|
const registerServiceArgs = [
|
||||||
|
[serviceDecl],
|
||||||
|
[serviceIdDecl, serviceDecl],
|
||||||
|
[peerDecl, serviceDecl],
|
||||||
|
[peerDecl, serviceIdDecl, serviceDecl],
|
||||||
|
];
|
||||||
|
|
||||||
|
return [
|
||||||
|
interfaces,
|
||||||
|
...registerServiceArgs.map((registerServiceArg) => {
|
||||||
|
const args = registerServiceArg.join(", ");
|
||||||
|
return `export function register${srvName}(${args}): void;`;
|
||||||
|
}),
|
||||||
|
].join("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class JSTypeGenerator implements TypeGenerator {
|
export class JSTypeGenerator implements TypeGenerator {
|
||||||
bang(field: string): string {
|
bang(field: string): string {
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
generic(field: string, type: string): string {
|
generic(field: string): string {
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
type(field: string, type: string): string {
|
type(field: string): string {
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
funcType(): string {
|
funcType(): string {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceType(): string {
|
serviceType(): string {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AquaFunction {
|
export interface AquaFunction {
|
||||||
funcDef: FunctionCallDef;
|
funcDef: FunctionCallDef;
|
||||||
script: string;
|
script: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompilationResult {
|
export interface CompilationResult {
|
||||||
services: Record<string, ServiceDef>;
|
services: Record<string, ServiceDef>;
|
||||||
functions: Record<string, AquaFunction>;
|
functions: Record<string, AquaFunction>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EntityGenerator {
|
export interface EntityGenerator {
|
||||||
generate(compilationResult: CompilationResult): string;
|
generate(compilationResult: CompilationResult): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OutputType = 'js' | 'ts';
|
export type OutputType = "js" | "ts";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,45 +14,74 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ServiceDef } from '@fluencelabs/interfaces';
|
import { ServiceDef } from "@fluencelabs/interfaces";
|
||||||
import { recursiveRenameLaquaProps } from '../utils.js';
|
|
||||||
import { TypeGenerator } from './interfaces.js';
|
import { recursiveRenameLaquaProps } from "../utils.js";
|
||||||
|
|
||||||
|
import { TypeGenerator } from "./interfaces.js";
|
||||||
|
|
||||||
interface DefaultServiceId {
|
interface DefaultServiceId {
|
||||||
s_Some__f_value?: string
|
s_Some__f_value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateServices(typeGenerator: TypeGenerator, services: Record<string, ServiceDef>) {
|
export function generateServices(
|
||||||
const generated = Object.entries(services).map(([srvName, srvDef]) => generateService(typeGenerator, srvName, srvDef)).join('\n\n');
|
typeGenerator: TypeGenerator,
|
||||||
|
services: Record<string, ServiceDef>,
|
||||||
|
) {
|
||||||
|
const generated = Object.entries(services)
|
||||||
|
.map(([srvName, srvDef]) => {
|
||||||
|
return generateService(typeGenerator, srvName, srvDef);
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
|
|
||||||
return generated + '\n';
|
return generated + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateService(typeGenerator: TypeGenerator, srvName: string, srvDef: ServiceDef) {
|
function generateService(
|
||||||
return [
|
typeGenerator: TypeGenerator,
|
||||||
typeGenerator.serviceType(srvName, srvDef),
|
srvName: string,
|
||||||
generateRegisterServiceOverload(typeGenerator, srvName, srvDef)
|
srvDef: ServiceDef,
|
||||||
].join('\n');
|
) {
|
||||||
|
return [
|
||||||
|
typeGenerator.serviceType(srvName, srvDef),
|
||||||
|
generateRegisterServiceOverload(typeGenerator, srvName, srvDef),
|
||||||
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateRegisterServiceOverload(typeGenerator: TypeGenerator, srvName: string, srvDef: ServiceDef) {
|
function generateRegisterServiceOverload(
|
||||||
return [
|
typeGenerator: TypeGenerator,
|
||||||
`export function register${srvName}(${typeGenerator.type('...args', 'any[]')}) {`,
|
srvName: string,
|
||||||
' registerService$$(',
|
srvDef: ServiceDef,
|
||||||
' args,',
|
) {
|
||||||
` ${serviceToJson(srvDef)}`,
|
return [
|
||||||
' );',
|
`export function register${srvName}(${typeGenerator.type(
|
||||||
'}'
|
"...args",
|
||||||
].join('\n');
|
"any[]",
|
||||||
|
)}) {`,
|
||||||
|
" registerService$$(",
|
||||||
|
" args,",
|
||||||
|
` ${serviceToJson(srvDef)}`,
|
||||||
|
" );",
|
||||||
|
"}",
|
||||||
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function serviceToJson(service: ServiceDef): string {
|
function serviceToJson(service: ServiceDef): string {
|
||||||
return JSON.stringify({
|
return JSON.stringify(
|
||||||
...(
|
{
|
||||||
(service.defaultServiceId as DefaultServiceId)?.s_Some__f_value
|
// This assertion is required because aqua-api gives bad types
|
||||||
? { defaultServiceId: (service.defaultServiceId as DefaultServiceId).s_Some__f_value }
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
: {}
|
...((service.defaultServiceId as DefaultServiceId).s_Some__f_value != null
|
||||||
),
|
? {
|
||||||
functions: recursiveRenameLaquaProps(service.functions)
|
defaultServiceId:
|
||||||
}, null, 4);
|
// This assertion is required because aqua-api gives bad types
|
||||||
}
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
(service.defaultServiceId as DefaultServiceId).s_Some__f_value,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
functions: recursiveRenameLaquaProps(service.functions),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,34 +14,47 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { generateSources, generateTypes } from "./generate/index.js";
|
||||||
generateSources,
|
import { CompilationResult, OutputType } from "./generate/interfaces.js";
|
||||||
generateTypes,
|
import { getPackageJsonContent } from "./utils.js";
|
||||||
} from './generate/index.js';
|
|
||||||
import { CompilationResult, OutputType } from './generate/interfaces.js';
|
|
||||||
import { getPackageJsonContent } from './utils.js';
|
|
||||||
|
|
||||||
interface JsOutput {
|
interface JsOutput {
|
||||||
sources: string;
|
sources: string;
|
||||||
types: string;
|
types: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TsOutput {
|
interface TsOutput {
|
||||||
sources: string;
|
sources: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type LanguageOutput = {
|
type LanguageOutput = {
|
||||||
"js": JsOutput,
|
js: JsOutput;
|
||||||
"ts": TsOutput
|
ts: TsOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function aquaToJs<T extends OutputType>(res: CompilationResult, outputType: T): Promise<LanguageOutput[T]> {
|
type NothingToGenerate = null;
|
||||||
const packageJson = await getPackageJsonContent();
|
|
||||||
|
export default async function aquaToJs<T extends OutputType>(
|
||||||
return outputType === 'js' ? {
|
res: CompilationResult,
|
||||||
sources: await generateSources(res, 'js', packageJson),
|
outputType: T,
|
||||||
types: await generateTypes(res, packageJson)
|
): Promise<LanguageOutput[T] | NothingToGenerate> {
|
||||||
} : {
|
if (
|
||||||
sources: await generateSources(res, 'ts', packageJson),
|
Object.keys(res.services).length === 0 &&
|
||||||
} as LanguageOutput[T];
|
Object.keys(res.functions).length === 0
|
||||||
};
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageJson = await getPackageJsonContent();
|
||||||
|
|
||||||
|
return outputType === "js"
|
||||||
|
? {
|
||||||
|
sources: generateSources(res, "js", packageJson),
|
||||||
|
types: generateTypes(res, packageJson),
|
||||||
|
}
|
||||||
|
: // TODO: probably there is a way to remove this type assert
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
({
|
||||||
|
sources: generateSources(res, "ts", packageJson),
|
||||||
|
} as LanguageOutput[T]);
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,57 +14,94 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ArrowWithoutCallbacks, NonArrowType, ProductType } from '@fluencelabs/interfaces';
|
import assert from "assert";
|
||||||
import { readFile } from 'fs/promises';
|
import { readFile } from "fs/promises";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
|
|
||||||
|
import {
|
||||||
|
ArrowType,
|
||||||
|
ArrowWithoutCallbacks,
|
||||||
|
JSONValue,
|
||||||
|
LabeledProductType,
|
||||||
|
NilType,
|
||||||
|
SimpleTypes,
|
||||||
|
UnlabeledProductType,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
export interface PackageJson {
|
export interface PackageJson {
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
['@fluencelabs/aqua-api']: string
|
["@fluencelabs/aqua-api"]: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPackageJsonContent(): Promise<PackageJson> {
|
export async function getPackageJsonContent(): Promise<PackageJson> {
|
||||||
const content = await readFile(new URL(path.join('..', 'package.json'), import.meta.url), 'utf-8');
|
const content = await readFile(
|
||||||
return JSON.parse(content);
|
new URL(path.join("..", "package.json"), import.meta.url),
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Add validation here
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
return JSON.parse(content) as PackageJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFuncArgs(domain: ProductType<NonArrowType | ArrowWithoutCallbacks>): [string, NonArrowType | ArrowWithoutCallbacks][] {
|
export function getFuncArgs(
|
||||||
if (domain.tag === 'labeledProduct') {
|
domain:
|
||||||
return Object.entries(domain.fields).map(([label, type]) => [label, type]);
|
| LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||||
} else if (domain.tag === 'unlabeledProduct') {
|
| UnlabeledProductType
|
||||||
return domain.items.map((type, index) => ['arg' + index, type]);
|
| NilType,
|
||||||
} else {
|
): [string, SimpleTypes | ArrowWithoutCallbacks][] {
|
||||||
return [];
|
if (domain.tag === "labeledProduct") {
|
||||||
}
|
return Object.entries(domain.fields).map(([label, type]) => {
|
||||||
|
return [label, type];
|
||||||
|
});
|
||||||
|
} else if (domain.tag === "unlabeledProduct") {
|
||||||
|
return domain.items.map((type, index) => {
|
||||||
|
return ["arg" + index, type];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function recursiveRenameLaquaProps(obj: unknown): unknown {
|
export function recursiveRenameLaquaProps(obj: JSONValue): unknown {
|
||||||
if (typeof obj !== 'object' || obj === null) return obj;
|
if (typeof obj !== "object" || obj === null) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(obj)) {
|
if (Array.isArray(obj)) {
|
||||||
return obj.map(item => recursiveRenameLaquaProps(item));
|
return obj.map((item) => {
|
||||||
|
return recursiveRenameLaquaProps(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.getOwnPropertyNames(obj).reduce((acc, prop) => {
|
||||||
|
let accessProp = prop;
|
||||||
|
|
||||||
|
if (prop.includes("Laqua_js")) {
|
||||||
|
// Last part of the property separated by "_" is a correct name
|
||||||
|
const refinedProperty = prop.split("_").pop();
|
||||||
|
|
||||||
|
if (refinedProperty == null) {
|
||||||
|
throw new Error(`Bad property name: ${prop}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refinedProperty in obj) {
|
||||||
|
accessProp = refinedProperty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.getOwnPropertyNames(obj).reduce((acc, prop) => {
|
assert(accessProp in obj);
|
||||||
let accessProp = prop;
|
|
||||||
if (prop.includes('Laqua_js')) {
|
|
||||||
// Last part of the property separated by "_" is a correct name
|
|
||||||
const refinedProperty = prop.split('_').pop()!;
|
|
||||||
if (refinedProperty in obj) {
|
|
||||||
accessProp = refinedProperty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
[accessProp]: recursiveRenameLaquaProps(obj[accessProp as keyof typeof obj])
|
[accessProp]: recursiveRenameLaquaProps(obj[accessProp]),
|
||||||
};
|
};
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function capitalize(str: string) {
|
export function capitalize(str: string) {
|
||||||
return str.slice(0, 1).toUpperCase() + str.slice(1);
|
return str.slice(0, 1).toUpperCase() + str.slice(1);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../../tsconfig.json",
|
"extends": "../../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist", "src/**/__test__"],
|
"exclude": ["node_modules", "dist", "src/**/__test__"]
|
||||||
}
|
}
|
||||||
|
@ -2,85 +2,74 @@
|
|||||||
|
|
||||||
## [0.8.2](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.1...interfaces-v0.8.2) (2023-08-24)
|
## [0.8.2](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.1...interfaces-v0.8.2) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* use marine-js 0.7.2 ([#321](https://github.com/fluencelabs/js-client/issues/321)) ([c99a509](https://github.com/fluencelabs/js-client/commit/c99a509c8743471856b0beb25696ffe7357d5399))
|
- use marine-js 0.7.2 ([#321](https://github.com/fluencelabs/js-client/issues/321)) ([c99a509](https://github.com/fluencelabs/js-client/commit/c99a509c8743471856b0beb25696ffe7357d5399))
|
||||||
|
|
||||||
## [0.8.1](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.0...interfaces-v0.8.1) (2023-08-08)
|
## [0.8.1](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.0...interfaces-v0.8.1) (2023-08-08)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.43.1 ([#322](https://github.com/fluencelabs/js-client/issues/322)) ([c1d1fa6](https://github.com/fluencelabs/js-client/commit/c1d1fa6659b6dc2c6707786748b3410fab7f1bcd))
|
- **deps:** update dependency @fluencelabs/avm to v0.43.1 ([#322](https://github.com/fluencelabs/js-client/issues/322)) ([c1d1fa6](https://github.com/fluencelabs/js-client/commit/c1d1fa6659b6dc2c6707786748b3410fab7f1bcd))
|
||||||
|
|
||||||
## [0.8.0](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.6...interfaces-v0.8.0) (2023-06-29)
|
## [0.8.0](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.6...interfaces-v0.8.0) (2023-06-29)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315)
|
- **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315) ([8bae6e2](https://github.com/fluencelabs/js-client/commit/8bae6e24e62153b567f320ccecc7bce76bc826d1))
|
- **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315) ([8bae6e2](https://github.com/fluencelabs/js-client/commit/8bae6e24e62153b567f320ccecc7bce76bc826d1))
|
||||||
|
|
||||||
## [0.7.6](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.5...interfaces-v0.7.6) (2023-06-20)
|
## [0.7.6](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.5...interfaces-v0.7.6) (2023-06-20)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* support signatures [fixes DXJ-389] ([#310](https://github.com/fluencelabs/js-client/issues/310)) ([a60dfe0](https://github.com/fluencelabs/js-client/commit/a60dfe0d680b4d9ac5092dec64e2ebf478bf80eb))
|
- support signatures [fixes DXJ-389] ([#310](https://github.com/fluencelabs/js-client/issues/310)) ([a60dfe0](https://github.com/fluencelabs/js-client/commit/a60dfe0d680b4d9ac5092dec64e2ebf478bf80eb))
|
||||||
|
|
||||||
## [0.7.5](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.4...interfaces-v0.7.5) (2023-04-04)
|
## [0.7.5](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.4...interfaces-v0.7.5) (2023-04-04)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Cleaning up technical debts ([#295](https://github.com/fluencelabs/js-client/issues/295)) ([0b2f12d](https://github.com/fluencelabs/js-client/commit/0b2f12d8ac223db341d6c30ff403166b3eae2e56))
|
- Cleaning up technical debts ([#295](https://github.com/fluencelabs/js-client/issues/295)) ([0b2f12d](https://github.com/fluencelabs/js-client/commit/0b2f12d8ac223db341d6c30ff403166b3eae2e56))
|
||||||
|
|
||||||
## [0.7.4](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.3...interfaces-v0.7.4) (2023-03-31)
|
## [0.7.4](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.3...interfaces-v0.7.4) (2023-03-31)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **logs:** Use `debug.js` library for logging [DXJ-327] ([#285](https://github.com/fluencelabs/js-client/issues/285)) ([e95c34a](https://github.com/fluencelabs/js-client/commit/e95c34a79220bd8ecdcee806802ac3d69a2af0cb))
|
- **logs:** Use `debug.js` library for logging [DXJ-327] ([#285](https://github.com/fluencelabs/js-client/issues/285)) ([e95c34a](https://github.com/fluencelabs/js-client/commit/e95c34a79220bd8ecdcee806802ac3d69a2af0cb))
|
||||||
|
|
||||||
## [0.7.3](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.2...interfaces-v0.7.3) (2023-02-16)
|
## [0.7.3](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.2...interfaces-v0.7.3) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Trigger release to publish packages that were built ([#262](https://github.com/fluencelabs/js-client/issues/262)) ([47abf38](https://github.com/fluencelabs/js-client/commit/47abf3882956ffbdc52df372db26ba6252e8306b))
|
- Trigger release to publish packages that were built ([#262](https://github.com/fluencelabs/js-client/issues/262)) ([47abf38](https://github.com/fluencelabs/js-client/commit/47abf3882956ffbdc52df372db26ba6252e8306b))
|
||||||
|
|
||||||
## [0.7.2](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.1...interfaces-v0.7.2) (2023-02-16)
|
## [0.7.2](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.1...interfaces-v0.7.2) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add `getRelayPeerId` method for `IFluenceClient` ([#260](https://github.com/fluencelabs/js-client/issues/260)) ([a10278a](https://github.com/fluencelabs/js-client/commit/a10278afaa782a307feb10c4eac060094c101230))
|
- Add `getRelayPeerId` method for `IFluenceClient` ([#260](https://github.com/fluencelabs/js-client/issues/260)) ([a10278a](https://github.com/fluencelabs/js-client/commit/a10278afaa782a307feb10c4eac060094c101230))
|
||||||
|
|
||||||
## [0.7.1](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.0...interfaces-v0.7.1) (2023-02-16)
|
## [0.7.1](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.0...interfaces-v0.7.1) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Simplify JS Client public API ([#257](https://github.com/fluencelabs/js-client/issues/257)) ([9daaf41](https://github.com/fluencelabs/js-client/commit/9daaf410964d43228192c829c7ff785db6e88081))
|
- Simplify JS Client public API ([#257](https://github.com/fluencelabs/js-client/issues/257)) ([9daaf41](https://github.com/fluencelabs/js-client/commit/9daaf410964d43228192c829c7ff785db6e88081))
|
||||||
|
|
||||||
## [0.7.0](https://github.com/fluencelabs/fluence-js/compare/interfaces-v0.6.0...interfaces-v0.7.0) (2023-02-15)
|
## [0.7.0](https://github.com/fluencelabs/fluence-js/compare/interfaces-v0.6.0...interfaces-v0.7.0) (2023-02-15)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246))
|
- Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246))
|
||||||
* Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243))
|
- Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243))
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246)) ([d4bb8fb](https://github.com/fluencelabs/fluence-js/commit/d4bb8fb42964b3ba25154232980b9ae82c21e627))
|
- Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246)) ([d4bb8fb](https://github.com/fluencelabs/fluence-js/commit/d4bb8fb42964b3ba25154232980b9ae82c21e627))
|
||||||
* Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243)) ([9667c4f](https://github.com/fluencelabs/fluence-js/commit/9667c4fec6868f984bba13249f3c47d293396406))
|
- Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243)) ([9667c4f](https://github.com/fluencelabs/fluence-js/commit/9667c4fec6868f984bba13249f3c47d293396406))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* NodeJS package building ([#248](https://github.com/fluencelabs/fluence-js/issues/248)) ([0d05e51](https://github.com/fluencelabs/fluence-js/commit/0d05e517d89529af513fcb96cfa6c722ccc357a7))
|
- NodeJS package building ([#248](https://github.com/fluencelabs/fluence-js/issues/248)) ([0d05e51](https://github.com/fluencelabs/fluence-js/commit/0d05e517d89529af513fcb96cfa6c722ccc357a7))
|
||||||
|
@ -1,56 +1,56 @@
|
|||||||
{
|
{
|
||||||
"name": "@fluencelabs/interfaces",
|
"name": "@fluencelabs/interfaces",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.8.2",
|
"version": "0.8.2",
|
||||||
"description": "Interfaces",
|
"description": "Interfaces",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"typings": "./dist/index.d.ts",
|
"typings": "./dist/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"import": "./dist/index.js",
|
"import": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts"
|
"types": "./dist/index.d.ts"
|
||||||
},
|
|
||||||
"./fluenceClient": {
|
|
||||||
"import": "./dist/fluenceClient.js",
|
|
||||||
"types": "./dist/fluenceClient.d.ts"
|
|
||||||
},
|
|
||||||
"./compilerSupport": {
|
|
||||||
"import": "./dist/compilerSupport.js",
|
|
||||||
"types": "./dist/compilerSupport.d.ts"
|
|
||||||
},
|
|
||||||
"./dist/fluenceClient": {
|
|
||||||
"import": "./dist/fluenceClient.js",
|
|
||||||
"types": "./dist/fluenceClient.d.ts"
|
|
||||||
},
|
|
||||||
"./dist/compilerSupport": {
|
|
||||||
"import": "./dist/compilerSupport.js",
|
|
||||||
"types": "./dist/compilerSupport.d.ts"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
"./fluenceClient": {
|
||||||
"*": {
|
"import": "./dist/fluenceClient.js",
|
||||||
"fluenceClient.d.ts": [
|
"types": "./dist/fluenceClient.d.ts"
|
||||||
"./dist/fluenceClient.d.ts"
|
|
||||||
],
|
|
||||||
"compilerSupport.d.ts": [
|
|
||||||
"./dist/compilerSupport.d.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"./compilerSupport": {
|
||||||
"node": ">=10",
|
"import": "./dist/compilerSupport.js",
|
||||||
"pnpm": ">=3"
|
"types": "./dist/compilerSupport.d.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"./dist/fluenceClient": {
|
||||||
"build": "tsc"
|
"import": "./dist/fluenceClient.js",
|
||||||
|
"types": "./dist/fluenceClient.d.ts"
|
||||||
},
|
},
|
||||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
"./dist/compilerSupport": {
|
||||||
"author": "Fluence Labs",
|
"import": "./dist/compilerSupport.js",
|
||||||
"license": "Apache-2.0",
|
"types": "./dist/compilerSupport.d.ts"
|
||||||
"dependencies": {},
|
|
||||||
"devDependencies": {
|
|
||||||
"@multiformats/multiaddr": "11.3.0",
|
|
||||||
"@fluencelabs/avm": "0.48.0",
|
|
||||||
"@fluencelabs/marine-js": "0.7.2"
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"typesVersions": {
|
||||||
|
"*": {
|
||||||
|
"fluenceClient.d.ts": [
|
||||||
|
"./dist/fluenceClient.d.ts"
|
||||||
|
],
|
||||||
|
"compilerSupport.d.ts": [
|
||||||
|
"./dist/compilerSupport.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10",
|
||||||
|
"pnpm": ">=3"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc"
|
||||||
|
},
|
||||||
|
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||||
|
"author": "Fluence Labs",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@multiformats/multiaddr": "11.3.0",
|
||||||
|
"@fluencelabs/avm": "0.48.0",
|
||||||
|
"hotscript": "1.0.13"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,7 +13,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { SecurityTetraplet } from '@fluencelabs/avm';
|
|
||||||
|
import type { SecurityTetraplet } from "@fluencelabs/avm";
|
||||||
|
|
||||||
|
import { InterfaceToType, MaybePromise } from "./utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peer ID's id as a base58 string (multihash/CIDv0).
|
* Peer ID's id as a base58 string (multihash/CIDv0).
|
||||||
@ -24,42 +27,61 @@ export type PeerIdB58 = string;
|
|||||||
* Node of the Fluence network specified as a pair of node's multiaddr and it's peer id
|
* Node of the Fluence network specified as a pair of node's multiaddr and it's peer id
|
||||||
*/
|
*/
|
||||||
export type Node = {
|
export type Node = {
|
||||||
peerId: PeerIdB58;
|
peerId: PeerIdB58;
|
||||||
multiaddr: string;
|
multiaddr: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional information about a service call
|
* Additional information about a service call
|
||||||
* @typeparam ArgName
|
* @typeparam ArgName
|
||||||
*/
|
*/
|
||||||
export interface CallParams<ArgName extends string | null> {
|
export type CallParams<ArgName extends string | null> = {
|
||||||
/**
|
/**
|
||||||
* The identifier of particle which triggered the call
|
* The identifier of particle which triggered the call
|
||||||
*/
|
*/
|
||||||
particleId: string;
|
particleId: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The peer id which created the particle
|
* The peer id which created the particle
|
||||||
*/
|
*/
|
||||||
initPeerId: PeerIdB58;
|
initPeerId: PeerIdB58;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Particle's timestamp when it was created
|
* Particle's timestamp when it was created
|
||||||
*/
|
*/
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time to live in milliseconds. The time after the particle should be expired
|
* Time to live in milliseconds. The time after the particle should be expired
|
||||||
*/
|
*/
|
||||||
ttl: number;
|
ttl: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Particle's signature
|
* Particle's signature
|
||||||
*/
|
*/
|
||||||
signature?: string;
|
signature?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security tetraplets
|
* Security tetraplets
|
||||||
*/
|
*/
|
||||||
tetraplets: ArgName extends string ? Record<ArgName, SecurityTetraplet[]> : Record<string, never>;
|
tetraplets: ArgName extends string
|
||||||
}
|
? Record<ArgName, InterfaceToType<SecurityTetraplet>[]>
|
||||||
|
: Record<string, never>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServiceImpl = Record<
|
||||||
|
string,
|
||||||
|
(
|
||||||
|
...args: [...JSONArray, CallParams<string>]
|
||||||
|
) => MaybePromise<JSONValue | undefined>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type JSONValue =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| { [x: string]: JSONValue }
|
||||||
|
| Array<JSONValue>;
|
||||||
|
export type JSONArray = Array<JSONValue>;
|
||||||
|
export type JSONObject = { [x: string]: JSONValue };
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,257 +13,279 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
type SimpleTypes = ScalarType | OptionType | ArrayType | StructType | TopType | BottomType | NilType;
|
|
||||||
|
|
||||||
export type NonArrowType = SimpleTypes | ProductType<SimpleTypes>;
|
export type SimpleTypes =
|
||||||
|
| ScalarType
|
||||||
|
| OptionType
|
||||||
|
| ArrayType
|
||||||
|
| StructType
|
||||||
|
| TopType
|
||||||
|
| BottomType
|
||||||
|
| NilType;
|
||||||
|
|
||||||
|
export type NonArrowType = SimpleTypes | ProductType;
|
||||||
|
|
||||||
export type TopType = {
|
export type TopType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'topType';
|
tag: "topType";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BottomType = {
|
export type BottomType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'bottomType';
|
tag: "bottomType";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OptionType = {
|
export type OptionType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'option';
|
tag: "option";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Underlying type of the option
|
* Underlying type of the option
|
||||||
*/
|
*/
|
||||||
type: NonArrowType;
|
type: SimpleTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NilType = {
|
export type NilType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'nil';
|
tag: "nil";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ArrayType = {
|
export type ArrayType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'array';
|
tag: "array";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of array elements
|
* Type of array elements
|
||||||
*/
|
*/
|
||||||
type: NonArrowType;
|
type: SimpleTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All possible scalar type names
|
* All possible scalar type names
|
||||||
*/
|
*/
|
||||||
export type ScalarNames =
|
export type ScalarNames =
|
||||||
| 'u8'
|
| "u8"
|
||||||
| 'u16'
|
| "u16"
|
||||||
| 'u32'
|
| "u32"
|
||||||
| 'u64'
|
| "u64"
|
||||||
| 'i8'
|
| "i8"
|
||||||
| 'i16'
|
| "i16"
|
||||||
| 'i32'
|
| "i32"
|
||||||
| 'i64'
|
| "i64"
|
||||||
| 'f32'
|
| "f32"
|
||||||
| 'f64'
|
| "f64"
|
||||||
| 'bool'
|
| "bool"
|
||||||
| 'string';
|
| "string";
|
||||||
|
|
||||||
export type ScalarType = {
|
export type ScalarType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'scalar';
|
tag: "scalar";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the scalar type
|
* Name of the scalar type
|
||||||
*/
|
*/
|
||||||
name: ScalarNames;
|
name: ScalarNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StructType = {
|
export type StructType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'struct';
|
tag: "struct";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Struct name
|
* Struct name
|
||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Struct fields
|
* Struct fields
|
||||||
*/
|
*/
|
||||||
fields: { [key: string]: NonArrowType };
|
fields: { [key: string]: SimpleTypes };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LabeledProductType<T> = {
|
export type LabeledProductType<
|
||||||
/**
|
T extends
|
||||||
* Type descriptor. Used for pattern-matching
|
| SimpleTypes
|
||||||
*/
|
| ArrowType<LabeledProductType<SimpleTypes> | UnlabeledProductType> =
|
||||||
tag: 'labeledProduct';
|
| SimpleTypes
|
||||||
|
| ArrowType<LabeledProductType<SimpleTypes> | UnlabeledProductType>,
|
||||||
|
K extends { [key: string]: T } = { [key: string]: T },
|
||||||
|
> = {
|
||||||
|
/**
|
||||||
|
* Type descriptor. Used for pattern-matching
|
||||||
|
*/
|
||||||
|
tag: "labeledProduct";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Labelled product fields
|
* Labelled product fields
|
||||||
*/
|
*/
|
||||||
fields: { [key: string]: T };
|
fields: K;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UnlabeledProductType<T> = {
|
export type UnlabeledProductType<T extends Array<SimpleTypes> = SimpleTypes[]> =
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'unlabeledProduct';
|
tag: "unlabeledProduct";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items in unlabelled product
|
* Items in unlabelled product
|
||||||
*/
|
*/
|
||||||
items: Array<T>;
|
items: T;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProductType<T> = UnlabeledProductType<T> | LabeledProductType<T> | NilType;
|
export type ProductType = UnlabeledProductType | LabeledProductType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ArrowType is a profunctor pointing its domain to codomain.
|
* ArrowType is a profunctor pointing its domain to codomain.
|
||||||
* Profunctor means variance: Arrow is contravariant on domain, and variant on codomain.
|
* Profunctor means variance: Arrow is contravariant on domain, and variant on codomain.
|
||||||
*/
|
*/
|
||||||
export type ArrowType<T> = {
|
export type ArrowType<T extends LabeledProductType | UnlabeledProductType> = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'arrow';
|
tag: "arrow";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where this Arrow is defined
|
* Where this Arrow is defined
|
||||||
*/
|
*/
|
||||||
domain: ProductType<T>;
|
domain: T | NilType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where this Arrow points to
|
* Where this Arrow points to
|
||||||
*/
|
*/
|
||||||
codomain: UnlabeledProductType<NonArrowType> | NilType;
|
codomain: UnlabeledProductType | NilType;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrow which domain contains only non-arrow types
|
* Arrow which domain contains only non-arrow types
|
||||||
*/
|
*/
|
||||||
export type ArrowWithoutCallbacks = ArrowType<NonArrowType>;
|
export type ArrowWithoutCallbacks = ArrowType<
|
||||||
|
UnlabeledProductType | LabeledProductType<SimpleTypes>
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrow which domain does can contain both non-arrow types and arrows (which themselves cannot contain arrows)
|
* Arrow which domain does can contain both non-arrow types and arrows (which themselves cannot contain arrows)
|
||||||
*/
|
*/
|
||||||
export type ArrowWithCallbacks = ArrowType<NonArrowType | ArrowWithoutCallbacks>;
|
export type ArrowWithCallbacks = ArrowType<LabeledProductType>;
|
||||||
|
|
||||||
export interface FunctionCallConstants {
|
export interface FunctionCallConstants {
|
||||||
/**
|
/**
|
||||||
* The name of the relay variable
|
* The name of the relay variable
|
||||||
*/
|
*/
|
||||||
relay: string;
|
relay: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the serviceId used load variables at the beginning of the script
|
* The name of the serviceId used load variables at the beginning of the script
|
||||||
*/
|
*/
|
||||||
getDataSrv: string;
|
getDataSrv: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of serviceId is used to execute callbacks for the current particle
|
* The name of serviceId is used to execute callbacks for the current particle
|
||||||
*/
|
*/
|
||||||
callbackSrv: string;
|
callbackSrv: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the serviceId which is called to propagate return value to the generated function caller
|
* The name of the serviceId which is called to propagate return value to the generated function caller
|
||||||
*/
|
*/
|
||||||
responseSrv: string;
|
responseSrv: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the functionName which is called to propagate return value to the generated function caller
|
* The name of the functionName which is called to propagate return value to the generated function caller
|
||||||
*/
|
*/
|
||||||
responseFnName: string;
|
responseFnName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the serviceId which is called to report errors to the generated function caller
|
* The name of the serviceId which is called to report errors to the generated function caller
|
||||||
*/
|
*/
|
||||||
errorHandlingSrv: string;
|
errorHandlingSrv: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the functionName which is called to report errors to the generated function caller
|
* The name of the functionName which is called to report errors to the generated function caller
|
||||||
*/
|
*/
|
||||||
errorFnName: string;
|
errorFnName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of function (`func` instruction) generated by the Aqua compiler
|
* Definition of function (`func` instruction) generated by the Aqua compiler
|
||||||
*/
|
*/
|
||||||
export interface FunctionCallDef {
|
export interface FunctionCallDef {
|
||||||
/**
|
/**
|
||||||
* The name of the function in Aqua language
|
* The name of the function in Aqua language
|
||||||
*/
|
*/
|
||||||
functionName: string;
|
functionName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Underlying arrow which represents function in aqua
|
* Underlying arrow which represents function in aqua
|
||||||
*/
|
*/
|
||||||
arrow: ArrowWithCallbacks;
|
arrow: ArrowType<
|
||||||
|
LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Names of the different entities used in generated air script
|
* Names of the different entities used in generated air script
|
||||||
*/
|
*/
|
||||||
names: FunctionCallConstants;
|
names: FunctionCallConstants;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of service registration function (`service` instruction) generated by the Aqua compiler
|
* Definition of service registration function (`service` instruction) generated by the Aqua compiler
|
||||||
*/
|
*/
|
||||||
export interface ServiceDef {
|
export interface ServiceDef {
|
||||||
/**
|
/**
|
||||||
* Default service id. If the service has no default id the value should be undefined
|
* Default service id. If the service has no default id the value should be undefined
|
||||||
*/
|
*/
|
||||||
defaultServiceId?: string;
|
defaultServiceId?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of functions which the service consists of
|
* List of functions which the service consists of
|
||||||
*/
|
*/
|
||||||
functions: LabeledProductType<ArrowWithoutCallbacks> | NilType;
|
functions:
|
||||||
|
| LabeledProductType<ArrowType<LabeledProductType<SimpleTypes>>>
|
||||||
|
| NilType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options to configure Aqua function execution
|
* Options to configure Aqua function execution
|
||||||
*/
|
*/
|
||||||
export interface FnConfig {
|
export interface FnConfig {
|
||||||
/**
|
/**
|
||||||
* Sets the TTL (time to live) for particle responsible for the function execution
|
* Sets the TTL (time to live) for particle responsible for the function execution
|
||||||
* If the option is not set the default TTL from FluencePeer config is used
|
* If the option is not set the default TTL from FluencePeer config is used
|
||||||
*/
|
*/
|
||||||
ttl?: number;
|
ttl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getArgumentTypes = (
|
export const getArgumentTypes = (
|
||||||
def: FunctionCallDef,
|
def: FunctionCallDef,
|
||||||
): {
|
): {
|
||||||
[key: string]: NonArrowType | ArrowWithoutCallbacks;
|
[key: string]: NonArrowType | ArrowWithoutCallbacks;
|
||||||
} => {
|
} => {
|
||||||
if (def.arrow.domain.tag !== 'labeledProduct') {
|
if (def.arrow.domain.tag !== "labeledProduct") {
|
||||||
throw new Error('Should be impossible');
|
throw new Error("Should be impossible");
|
||||||
}
|
}
|
||||||
|
|
||||||
return def.arrow.domain.fields;
|
return def.arrow.domain.fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isReturnTypeVoid = (def: FunctionCallDef): boolean => {
|
export const isReturnTypeVoid = (def: FunctionCallDef): boolean => {
|
||||||
if (def.arrow.codomain.tag === 'nil') {
|
if (def.arrow.codomain.tag === "nil") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return def.arrow.codomain.items.length == 0;
|
return def.arrow.codomain.items.length === 0;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,72 +13,88 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { IFluenceInternalApi } from '../fluenceClient.js';
|
|
||||||
import { FnConfig, FunctionCallDef, ServiceDef } from './aquaTypeDefinitions.js';
|
import { JSONValue } from "../commonTypes.js";
|
||||||
|
import { IFluenceInternalApi } from "../fluenceClient.js";
|
||||||
|
|
||||||
|
import {
|
||||||
|
FnConfig,
|
||||||
|
FunctionCallDef,
|
||||||
|
ServiceDef,
|
||||||
|
} from "./aquaTypeDefinitions.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for callback passed as aqua function argument
|
||||||
|
*/
|
||||||
|
export type ArgCallbackFunction = (
|
||||||
|
...args: JSONValue[]
|
||||||
|
) => JSONValue | Promise<JSONValue>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments passed to Aqua function
|
* Arguments passed to Aqua function
|
||||||
*/
|
*/
|
||||||
export type PassedArgs = { [key: string]: any };
|
export type PassedArgs = { [key: string]: JSONValue | ArgCallbackFunction };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments for callAquaFunction function
|
* Arguments for callAquaFunction function
|
||||||
*/
|
*/
|
||||||
export interface CallAquaFunctionArgs {
|
export interface CallAquaFunctionArgs {
|
||||||
/**
|
/**
|
||||||
* Peer to call the function on
|
* Peer to call the function on
|
||||||
*/
|
*/
|
||||||
peer: IFluenceInternalApi;
|
peer: IFluenceInternalApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function definition
|
* Function definition
|
||||||
*/
|
*/
|
||||||
def: FunctionCallDef;
|
def: FunctionCallDef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Air script used by the aqua function
|
* Air script used by the aqua function
|
||||||
*/
|
*/
|
||||||
script: string;
|
script: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function configuration
|
* Function configuration
|
||||||
*/
|
*/
|
||||||
config: FnConfig;
|
config: FnConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments to pass to the function
|
* Arguments to pass to the function
|
||||||
*/
|
*/
|
||||||
args: PassedArgs;
|
args: PassedArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call a function from Aqua script
|
* Call a function from Aqua script
|
||||||
*/
|
*/
|
||||||
export type CallAquaFunctionType = (args: CallAquaFunctionArgs) => Promise<unknown>;
|
export type CallAquaFunctionType = (
|
||||||
|
args: CallAquaFunctionArgs,
|
||||||
|
) => Promise<unknown>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments for registerService function
|
* Arguments for registerService function
|
||||||
*/
|
*/
|
||||||
export interface RegisterServiceArgs {
|
export interface RegisterServiceArgs {
|
||||||
/**
|
/**
|
||||||
* Peer to register the service on
|
* Peer to register the service on
|
||||||
*/
|
*/
|
||||||
peer: IFluenceInternalApi;
|
peer: IFluenceInternalApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service definition
|
* Service definition
|
||||||
*/
|
*/
|
||||||
def: ServiceDef;
|
def: ServiceDef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service id
|
* Service id
|
||||||
*/
|
*/
|
||||||
serviceId: string | undefined;
|
serviceId: string | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service implementation
|
* Service implementation
|
||||||
*/
|
*/
|
||||||
service: any;
|
service: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,7 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { Node } from './commonTypes.js';
|
|
||||||
|
import type { Node } from "./commonTypes.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A node in Fluence network a client can connect to.
|
* A node in Fluence network a client can connect to.
|
||||||
@ -26,78 +27,83 @@ export type RelayOptions = string | Node;
|
|||||||
/**
|
/**
|
||||||
* Fluence Peer's key pair types
|
* Fluence Peer's key pair types
|
||||||
*/
|
*/
|
||||||
export type KeyTypes = 'RSA' | 'Ed25519' | 'secp256k1';
|
export type KeyTypes = "RSA" | "Ed25519" | "secp256k1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options to specify key pair used in Fluence Peer
|
* Options to specify key pair used in Fluence Peer
|
||||||
*/
|
*/
|
||||||
export type KeyPairOptions = {
|
export type KeyPairOptions = {
|
||||||
type: 'Ed25519';
|
type: "Ed25519";
|
||||||
source: 'random' | Uint8Array;
|
source: "random" | Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration used when initiating Fluence Client
|
* Configuration used when initiating Fluence Client
|
||||||
*/
|
*/
|
||||||
export interface ClientConfig {
|
export interface ClientConfig {
|
||||||
|
/**
|
||||||
|
* Specify the KeyPair to be used to identify the Fluence Peer.
|
||||||
|
* Will be generated randomly if not specified
|
||||||
|
*/
|
||||||
|
keyPair?: KeyPairOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options to configure the connection to the Fluence network
|
||||||
|
*/
|
||||||
|
connectionOptions?: {
|
||||||
/**
|
/**
|
||||||
* Specify the KeyPair to be used to identify the Fluence Peer.
|
* When the peer established the connection to the network it sends a ping-like message to check if it works correctly.
|
||||||
* Will be generated randomly if not specified
|
* The options allows to specify the timeout for that message in milliseconds.
|
||||||
|
* If not specified the default timeout will be used
|
||||||
*/
|
*/
|
||||||
keyPair?: KeyPairOptions;
|
skipCheckConnection?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options to configure the connection to the Fluence network
|
* The dialing timeout in milliseconds
|
||||||
*/
|
*/
|
||||||
connectionOptions?: {
|
dialTimeoutMs?: number;
|
||||||
/**
|
|
||||||
* When the peer established the connection to the network it sends a ping-like message to check if it works correctly.
|
|
||||||
* The options allows to specify the timeout for that message in milliseconds.
|
|
||||||
* If not specified the default timeout will be used
|
|
||||||
*/
|
|
||||||
skipCheckConnection?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The dialing timeout in milliseconds
|
|
||||||
*/
|
|
||||||
dialTimeoutMs?: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum number of inbound streams for the libp2p node.
|
|
||||||
* Default: 1024
|
|
||||||
*/
|
|
||||||
maxInboundStreams?: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum number of outbound streams for the libp2p node.
|
|
||||||
* Default: 1024
|
|
||||||
*/
|
|
||||||
maxOutboundStreams?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the default TTL for all particles originating from the peer with no TTL specified.
|
* The maximum number of inbound streams for the libp2p node.
|
||||||
* If the originating particle's TTL is defined then that value will be used
|
* Default: 1024
|
||||||
* If the option is not set default TTL will be 7000
|
|
||||||
*/
|
*/
|
||||||
defaultTtlMs?: number;
|
maxInboundStreams?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables\disabled various debugging features
|
* The maximum number of outbound streams for the libp2p node.
|
||||||
|
* Default: 1024
|
||||||
*/
|
*/
|
||||||
debug?: {
|
maxOutboundStreams?: number;
|
||||||
/**
|
};
|
||||||
* If set to true, newly initiated particle ids will be printed to console.
|
|
||||||
* Useful to see what particle id is responsible for aqua function
|
/**
|
||||||
*/
|
* Sets the default TTL for all particles originating from the peer with no TTL specified.
|
||||||
printParticleId?: boolean;
|
* If the originating particle's TTL is defined then that value will be used
|
||||||
};
|
* If the option is not set default TTL will be 7000
|
||||||
|
*/
|
||||||
|
defaultTtlMs?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables\disabled various debugging features
|
||||||
|
*/
|
||||||
|
debug?: {
|
||||||
|
/**
|
||||||
|
* If set to true, newly initiated particle ids will be printed to console.
|
||||||
|
* Useful to see what particle id is responsible for aqua function
|
||||||
|
*/
|
||||||
|
printParticleId?: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fluence JS Client connection states as string literals
|
* Fluence JS Client connection states as string literals
|
||||||
*/
|
*/
|
||||||
export const ConnectionStates = ['disconnected', 'connecting', 'connected', 'disconnecting'] as const;
|
export const ConnectionStates = [
|
||||||
|
"disconnected",
|
||||||
|
"connecting",
|
||||||
|
"connected",
|
||||||
|
"disconnecting",
|
||||||
|
] as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fluence JS Client connection states
|
* Fluence JS Client connection states
|
||||||
@ -105,47 +111,45 @@ export const ConnectionStates = ['disconnected', 'connecting', 'connected', 'dis
|
|||||||
export type ConnectionState = (typeof ConnectionStates)[number];
|
export type ConnectionState = (typeof ConnectionStates)[number];
|
||||||
|
|
||||||
export interface IFluenceInternalApi {
|
export interface IFluenceInternalApi {
|
||||||
/**
|
/**
|
||||||
* Internal API
|
* Internal API
|
||||||
*/
|
*/
|
||||||
internals: any;
|
internals: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public API of Fluence JS Client
|
* Public API of Fluence JS Client
|
||||||
*/
|
*/
|
||||||
export interface IFluenceClient extends IFluenceInternalApi {
|
export interface IFluenceClient extends IFluenceInternalApi {
|
||||||
/**
|
/**
|
||||||
* Connect to the Fluence network
|
* Connect to the Fluence network
|
||||||
*/
|
*/
|
||||||
connect: () => Promise<void>;
|
connect: () => Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnect from the Fluence network
|
* Disconnect from the Fluence network
|
||||||
*/
|
*/
|
||||||
disconnect(): Promise<void>;
|
disconnect(): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle connection state changes. Immediately returns current connection state
|
* Handle connection state changes. Immediately returns current connection state
|
||||||
*/
|
*/
|
||||||
onConnectionStateChange(handler: (state: ConnectionState) => void): ConnectionState;
|
onConnectionStateChange(
|
||||||
|
handler: (state: ConnectionState) => void,
|
||||||
|
): ConnectionState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return peer's secret key as byte array.
|
* Return peer's secret key as byte array.
|
||||||
*/
|
*/
|
||||||
getPeerSecretKey(): Uint8Array;
|
getPeerSecretKey(): Uint8Array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return peer's public key as a base58 string (multihash/CIDv0).
|
* Return peer's public key as a base58 string (multihash/CIDv0).
|
||||||
*/
|
*/
|
||||||
getPeerId(): string;
|
getPeerId(): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return relay's public key as a base58 string (multihash/CIDv0).
|
* Return relay's public key as a base58 string (multihash/CIDv0).
|
||||||
*/
|
*/
|
||||||
getRelayPeerId(): string;
|
getRelayPeerId(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
90
packages/core/interfaces/src/future.ts
Normal file
90
packages/core/interfaces/src/future.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
ArrayType,
|
||||||
|
ArrowType,
|
||||||
|
LabeledProductType,
|
||||||
|
NilType,
|
||||||
|
OptionType,
|
||||||
|
ScalarType,
|
||||||
|
SimpleTypes,
|
||||||
|
StructType,
|
||||||
|
TopType,
|
||||||
|
UnlabeledProductType,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
import { Call, Pipe, Objects, Tuples, Unions, Fn } from "hotscript";
|
||||||
|
|
||||||
|
// Type definitions for inferring ts types from air json definition
|
||||||
|
// In the future we may remove string type declaration and move to type inference.
|
||||||
|
|
||||||
|
type GetTsTypeFromScalar<T extends ScalarType> = [T["name"]] extends [
|
||||||
|
"u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64",
|
||||||
|
]
|
||||||
|
? number
|
||||||
|
: [T["name"]] extends ["bool"]
|
||||||
|
? boolean
|
||||||
|
: [T["name"]] extends ["string"]
|
||||||
|
? string
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type MapTuple<T> = {
|
||||||
|
[K in keyof T]: [T[K]] extends [SimpleTypes] ? GetSimpleType<T[K]> : never;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UnpackIfSingle<T> = [T] extends [[infer R]] ? R : T;
|
||||||
|
|
||||||
|
type GetSimpleType<T> = [T] extends [NilType]
|
||||||
|
? null
|
||||||
|
: [T] extends [ArrayType]
|
||||||
|
? GetSimpleType<T["type"]>[]
|
||||||
|
: [T] extends [StructType]
|
||||||
|
? { [K in keyof T["fields"]]: GetSimpleType<T["fields"][K]> }
|
||||||
|
: [T] extends [OptionType]
|
||||||
|
? GetSimpleType<T["type"]> | null
|
||||||
|
: [T] extends [ScalarType]
|
||||||
|
? GetTsTypeFromScalar<T>
|
||||||
|
: [T] extends [TopType]
|
||||||
|
? unknown
|
||||||
|
: never;
|
||||||
|
|
||||||
|
interface Access<T> extends Fn {
|
||||||
|
return: __GetTsType<Call<Objects.Get<this["arg0"]>, T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type __GetTsType<T> = [T] extends [SimpleTypes]
|
||||||
|
? GetSimpleType<T>
|
||||||
|
: [T] extends [UnlabeledProductType]
|
||||||
|
? MapTuple<T["items"]>
|
||||||
|
: [T] extends [LabeledProductType]
|
||||||
|
? { [K in keyof T["fields"]]: __GetTsType<T["fields"][K]> }
|
||||||
|
: [T] extends [ArrowType<infer H>]
|
||||||
|
? (
|
||||||
|
...t: [H] extends [UnlabeledProductType<infer K>]
|
||||||
|
? MapTuple<K>
|
||||||
|
: [H] extends [LabeledProductType<infer _V, infer K>]
|
||||||
|
? Pipe<K, [Objects.Keys, Unions.ToTuple, Tuples.Map<Access<K>>]>
|
||||||
|
: []
|
||||||
|
) => [T["codomain"]] extends [UnlabeledProductType]
|
||||||
|
? UnpackIfSingle<MapTuple<T["codomain"]["items"]>>
|
||||||
|
: undefined
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type DeepMutable<T> = {
|
||||||
|
-readonly [K in keyof T]: DeepMutable<T[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetTsType<T> = __GetTsType<DeepMutable<T>>;
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,7 +13,9 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
export * from './compilerSupport/aquaTypeDefinitions.js';
|
|
||||||
export * from './compilerSupport/compilerSupportInterface.js';
|
export * from "./compilerSupport/aquaTypeDefinitions.js";
|
||||||
export * from './commonTypes.js';
|
export * from "./compilerSupport/compilerSupportInterface.js";
|
||||||
export * from './fluenceClient.js';
|
export * from "./commonTypes.js";
|
||||||
|
export * from "./fluenceClient.js";
|
||||||
|
export * from "./future.js";
|
||||||
|
21
packages/core/interfaces/src/utils.ts
Normal file
21
packages/core/interfaces/src/utils.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type InterfaceToType<T extends object> = {
|
||||||
|
[K in keyof T]: T[K];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MaybePromise<T> = T | Promise<T>;
|
@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../../tsconfig.json",
|
"extends": "../../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist"
|
"outDir": "./dist",
|
||||||
},
|
"rootDir": "src"
|
||||||
"include": ["src/**/*"],
|
},
|
||||||
"exclude": ["node_modules", "dist"]
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
@ -25,271 +25,234 @@
|
|||||||
|
|
||||||
## [0.1.7](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.6...js-client-v0.1.7) (2023-09-22)
|
## [0.1.7](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.6...js-client-v0.1.7) (2023-09-22)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.48.0 ([#350](https://github.com/fluencelabs/js-client/issues/350)) ([945908a](https://github.com/fluencelabs/js-client/commit/945908a992976f2ad953bcaa3918741f890ffeeb))
|
- **deps:** update dependency @fluencelabs/avm to v0.48.0 ([#350](https://github.com/fluencelabs/js-client/issues/350)) ([945908a](https://github.com/fluencelabs/js-client/commit/945908a992976f2ad953bcaa3918741f890ffeeb))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/marine-worker bumped to 0.3.3
|
- @fluencelabs/marine-worker bumped to 0.3.3
|
||||||
|
|
||||||
## [0.1.6](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.5...js-client-v0.1.6) (2023-09-15)
|
## [0.1.6](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.5...js-client-v0.1.6) (2023-09-15)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.47.0 ([#341](https://github.com/fluencelabs/js-client/issues/341)) ([f186f20](https://github.com/fluencelabs/js-client/commit/f186f209366c29f12e6677e03564ee2fa14b51ae))
|
- **deps:** update dependency @fluencelabs/avm to v0.47.0 ([#341](https://github.com/fluencelabs/js-client/issues/341)) ([f186f20](https://github.com/fluencelabs/js-client/commit/f186f209366c29f12e6677e03564ee2fa14b51ae))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/marine-worker bumped to 0.3.2
|
- @fluencelabs/marine-worker bumped to 0.3.2
|
||||||
|
|
||||||
## [0.1.5](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.4...js-client-v0.1.5) (2023-09-14)
|
## [0.1.5](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.4...js-client-v0.1.5) (2023-09-14)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **libp2p:** Add fluence protocol to local peer store protocols [fixes DXJ-471] ([#343](https://github.com/fluencelabs/js-client/issues/343)) ([88fcf02](https://github.com/fluencelabs/js-client/commit/88fcf02d5fd3d28db619427c31b38154646f7ad2))
|
- **libp2p:** Add fluence protocol to local peer store protocols [fixes DXJ-471] ([#343](https://github.com/fluencelabs/js-client/issues/343)) ([88fcf02](https://github.com/fluencelabs/js-client/commit/88fcf02d5fd3d28db619427c31b38154646f7ad2))
|
||||||
|
|
||||||
## [0.1.4](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.3...js-client-v0.1.4) (2023-09-14)
|
## [0.1.4](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.3...js-client-v0.1.4) (2023-09-14)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Fire and forget [fixes DXJ-446] ([#336](https://github.com/fluencelabs/js-client/issues/336)) ([e0a970d](https://github.com/fluencelabs/js-client/commit/e0a970d86a13f1617778a461c1c4d558d7dbafcb))
|
- Fire and forget [fixes DXJ-446] ([#336](https://github.com/fluencelabs/js-client/issues/336)) ([e0a970d](https://github.com/fluencelabs/js-client/commit/e0a970d86a13f1617778a461c1c4d558d7dbafcb))
|
||||||
|
|
||||||
## [0.1.3](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.2...js-client-v0.1.3) (2023-09-07)
|
## [0.1.3](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.2...js-client-v0.1.3) (2023-09-07)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.46.0 ([#338](https://github.com/fluencelabs/js-client/issues/338)) ([8e6918c](https://github.com/fluencelabs/js-client/commit/8e6918c4da5bc4cdfe1c840312f477d782d9ca20))
|
- **deps:** update dependency @fluencelabs/avm to v0.46.0 ([#338](https://github.com/fluencelabs/js-client/issues/338)) ([8e6918c](https://github.com/fluencelabs/js-client/commit/8e6918c4da5bc4cdfe1c840312f477d782d9ca20))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/marine-worker bumped to 0.3.1
|
- @fluencelabs/marine-worker bumped to 0.3.1
|
||||||
|
|
||||||
## [0.1.2](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.1...js-client-v0.1.2) (2023-09-05)
|
## [0.1.2](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.1...js-client-v0.1.2) (2023-09-05)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* remove obsolete packages [fixes DXJ-462] ([#337](https://github.com/fluencelabs/js-client/issues/337)) ([e7e6176](https://github.com/fluencelabs/js-client/commit/e7e617661f39e1df36a703d5dad93ba52a338919))
|
- remove obsolete packages [fixes DXJ-462] ([#337](https://github.com/fluencelabs/js-client/issues/337)) ([e7e6176](https://github.com/fluencelabs/js-client/commit/e7e617661f39e1df36a703d5dad93ba52a338919))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **logger:** Change formatter that collides with new libp2p version [fixes DXJ-459] ([#334](https://github.com/fluencelabs/js-client/issues/334)) ([18a972b](https://github.com/fluencelabs/js-client/commit/18a972b573559d0717ec93a95b8c63dd1cbcd93b))
|
- **logger:** Change formatter that collides with new libp2p version [fixes DXJ-459] ([#334](https://github.com/fluencelabs/js-client/issues/334)) ([18a972b](https://github.com/fluencelabs/js-client/commit/18a972b573559d0717ec93a95b8c63dd1cbcd93b))
|
||||||
|
|
||||||
## [0.1.1](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.0...js-client-v0.1.1) (2023-08-25)
|
## [0.1.1](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.0...js-client-v0.1.1) (2023-08-25)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Use info log level instead trace [Fixes DXJ-457] ([#328](https://github.com/fluencelabs/js-client/issues/328)) ([477c6f0](https://github.com/fluencelabs/js-client/commit/477c6f0c151ef6759aaa2802c5e9907065d58e17))
|
- Use info log level instead trace [Fixes DXJ-457] ([#328](https://github.com/fluencelabs/js-client/issues/328)) ([477c6f0](https://github.com/fluencelabs/js-client/commit/477c6f0c151ef6759aaa2802c5e9907065d58e17))
|
||||||
|
|
||||||
## [0.1.0](https://github.com/fluencelabs/js-client/compare/js-client-v0.0.10...js-client-v0.1.0) (2023-08-24)
|
## [0.1.0](https://github.com/fluencelabs/js-client/compare/js-client-v0.0.10...js-client-v0.1.0) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* Unify all packages ([#327](https://github.com/fluencelabs/js-client/issues/327))
|
- Unify all packages ([#327](https://github.com/fluencelabs/js-client/issues/327))
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Unify all packages ([#327](https://github.com/fluencelabs/js-client/issues/327)) ([97c2491](https://github.com/fluencelabs/js-client/commit/97c24918d84b34e7ac58337838dc8343cbd44b19))
|
- Unify all packages ([#327](https://github.com/fluencelabs/js-client/issues/327)) ([97c2491](https://github.com/fluencelabs/js-client/commit/97c24918d84b34e7ac58337838dc8343cbd44b19))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.8.1 to 0.8.2
|
- @fluencelabs/interfaces bumped from 0.8.1 to 0.8.2
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/marine-worker bumped to 0.3.0
|
- @fluencelabs/marine-worker bumped to 0.3.0
|
||||||
|
|
||||||
## [0.9.1](https://github.com/fluencelabs/js-client/compare/js-peer-v0.9.0...js-peer-v0.9.1) (2023-08-08)
|
## [0.9.1](https://github.com/fluencelabs/js-client/compare/js-peer-v0.9.0...js-peer-v0.9.1) (2023-08-08)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.43.1 ([#322](https://github.com/fluencelabs/js-client/issues/322)) ([c1d1fa6](https://github.com/fluencelabs/js-client/commit/c1d1fa6659b6dc2c6707786748b3410fab7f1bcd))
|
- **deps:** update dependency @fluencelabs/avm to v0.43.1 ([#322](https://github.com/fluencelabs/js-client/issues/322)) ([c1d1fa6](https://github.com/fluencelabs/js-client/commit/c1d1fa6659b6dc2c6707786748b3410fab7f1bcd))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.8.0 to 0.8.1
|
- @fluencelabs/interfaces bumped from 0.8.0 to 0.8.1
|
||||||
|
|
||||||
## [0.9.0](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.10...js-peer-v0.9.0) (2023-06-29)
|
## [0.9.0](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.10...js-peer-v0.9.0) (2023-06-29)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315)
|
- **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315) ([8bae6e2](https://github.com/fluencelabs/js-client/commit/8bae6e24e62153b567f320ccecc7bce76bc826d1))
|
- **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315) ([8bae6e2](https://github.com/fluencelabs/js-client/commit/8bae6e24e62153b567f320ccecc7bce76bc826d1))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.6 to 0.8.0
|
- @fluencelabs/interfaces bumped from 0.7.6 to 0.8.0
|
||||||
|
|
||||||
## [0.8.10](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.9...js-peer-v0.8.10) (2023-06-20)
|
## [0.8.10](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.9...js-peer-v0.8.10) (2023-06-20)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* support signatures [fixes DXJ-389] ([#310](https://github.com/fluencelabs/js-client/issues/310)) ([a60dfe0](https://github.com/fluencelabs/js-client/commit/a60dfe0d680b4d9ac5092dec64e2ebf478bf80eb))
|
- support signatures [fixes DXJ-389] ([#310](https://github.com/fluencelabs/js-client/issues/310)) ([a60dfe0](https://github.com/fluencelabs/js-client/commit/a60dfe0d680b4d9ac5092dec64e2ebf478bf80eb))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.5 to 0.7.6
|
- @fluencelabs/interfaces bumped from 0.7.5 to 0.7.6
|
||||||
|
|
||||||
## [0.8.9](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.8...js-peer-v0.8.9) (2023-06-14)
|
## [0.8.9](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.8...js-peer-v0.8.9) (2023-06-14)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add tracing service [fixes DXJ-388] ([#307](https://github.com/fluencelabs/js-client/issues/307)) ([771086f](https://github.com/fluencelabs/js-client/commit/771086fddf52b7a5a1280894c7238e409cdf6a64))
|
- Add tracing service [fixes DXJ-388] ([#307](https://github.com/fluencelabs/js-client/issues/307)) ([771086f](https://github.com/fluencelabs/js-client/commit/771086fddf52b7a5a1280894c7238e409cdf6a64))
|
||||||
* improve ttl error message ([#300](https://github.com/fluencelabs/js-client/issues/300)) ([9821183](https://github.com/fluencelabs/js-client/commit/9821183d53870240cb5700be67cb8d57533b954b))
|
- improve ttl error message ([#300](https://github.com/fluencelabs/js-client/issues/300)) ([9821183](https://github.com/fluencelabs/js-client/commit/9821183d53870240cb5700be67cb8d57533b954b))
|
||||||
|
|
||||||
## [0.8.8](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.7...js-peer-v0.8.8) (2023-05-30)
|
## [0.8.8](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.7...js-peer-v0.8.8) (2023-05-30)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add run-console ([#305](https://github.com/fluencelabs/js-client/issues/305)) ([cf1f029](https://github.com/fluencelabs/js-client/commit/cf1f02963c1d7e1a17866f5798901a0f61b8bc31))
|
- add run-console ([#305](https://github.com/fluencelabs/js-client/issues/305)) ([cf1f029](https://github.com/fluencelabs/js-client/commit/cf1f02963c1d7e1a17866f5798901a0f61b8bc31))
|
||||||
|
|
||||||
## [0.8.7](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.6...js-peer-v0.8.7) (2023-04-04)
|
## [0.8.7](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.6...js-peer-v0.8.7) (2023-04-04)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Cleaning up technical debts ([#295](https://github.com/fluencelabs/js-client/issues/295)) ([0b2f12d](https://github.com/fluencelabs/js-client/commit/0b2f12d8ac223db341d6c30ff403166b3eae2e56))
|
- Cleaning up technical debts ([#295](https://github.com/fluencelabs/js-client/issues/295)) ([0b2f12d](https://github.com/fluencelabs/js-client/commit/0b2f12d8ac223db341d6c30ff403166b3eae2e56))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.4 to 0.7.5
|
- @fluencelabs/interfaces bumped from 0.7.4 to 0.7.5
|
||||||
|
|
||||||
## [0.8.6](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.5...js-peer-v0.8.6) (2023-03-31)
|
## [0.8.6](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.5...js-peer-v0.8.6) (2023-03-31)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **logs:** Use `debug.js` library for logging [DXJ-327] ([#285](https://github.com/fluencelabs/js-client/issues/285)) ([e95c34a](https://github.com/fluencelabs/js-client/commit/e95c34a79220bd8ecdcee806802ac3d69a2af0cb))
|
- **logs:** Use `debug.js` library for logging [DXJ-327] ([#285](https://github.com/fluencelabs/js-client/issues/285)) ([e95c34a](https://github.com/fluencelabs/js-client/commit/e95c34a79220bd8ecdcee806802ac3d69a2af0cb))
|
||||||
* **test:** Automate smoke tests for JS Client [DXJ-293] ([#282](https://github.com/fluencelabs/js-client/issues/282)) ([10d7eae](https://github.com/fluencelabs/js-client/commit/10d7eaed809dde721b582d4b3228a48bbec50884))
|
- **test:** Automate smoke tests for JS Client [DXJ-293] ([#282](https://github.com/fluencelabs/js-client/issues/282)) ([10d7eae](https://github.com/fluencelabs/js-client/commit/10d7eaed809dde721b582d4b3228a48bbec50884))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **test:** All tests are working with vitest [DXJ-306] ([#291](https://github.com/fluencelabs/js-client/issues/291)) ([58ad3ca](https://github.com/fluencelabs/js-client/commit/58ad3ca6f666e8580997bb47609947645903436d))
|
- **test:** All tests are working with vitest [DXJ-306] ([#291](https://github.com/fluencelabs/js-client/issues/291)) ([58ad3ca](https://github.com/fluencelabs/js-client/commit/58ad3ca6f666e8580997bb47609947645903436d))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.3 to 0.7.4
|
- @fluencelabs/interfaces bumped from 0.7.3 to 0.7.4
|
||||||
|
|
||||||
## [0.8.5](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.4...js-peer-v0.8.5) (2023-03-03)
|
## [0.8.5](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.4...js-peer-v0.8.5) (2023-03-03)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Increase number of inbound and outbound streams to 1024 ([#280](https://github.com/fluencelabs/js-client/issues/280)) ([1ccc483](https://github.com/fluencelabs/js-client/commit/1ccc4835328426b546f31e1646d3a49ed042fdf9))
|
- Increase number of inbound and outbound streams to 1024 ([#280](https://github.com/fluencelabs/js-client/issues/280)) ([1ccc483](https://github.com/fluencelabs/js-client/commit/1ccc4835328426b546f31e1646d3a49ed042fdf9))
|
||||||
|
|
||||||
## [0.8.4](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.3...js-peer-v0.8.4) (2023-02-22)
|
## [0.8.4](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.3...js-peer-v0.8.4) (2023-02-22)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* `nodenext` moduleResolution for js peer ([#271](https://github.com/fluencelabs/js-client/issues/271)) ([78d98f1](https://github.com/fluencelabs/js-client/commit/78d98f15c12431dee9fdd7b9869d57760503f8c7))
|
- `nodenext` moduleResolution for js peer ([#271](https://github.com/fluencelabs/js-client/issues/271)) ([78d98f1](https://github.com/fluencelabs/js-client/commit/78d98f15c12431dee9fdd7b9869d57760503f8c7))
|
||||||
|
|
||||||
## [0.8.3](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.2...js-peer-v0.8.3) (2023-02-16)
|
## [0.8.3](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.2...js-peer-v0.8.3) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Trigger release to publish packages that were built ([#262](https://github.com/fluencelabs/js-client/issues/262)) ([47abf38](https://github.com/fluencelabs/js-client/commit/47abf3882956ffbdc52df372db26ba6252e8306b))
|
- Trigger release to publish packages that were built ([#262](https://github.com/fluencelabs/js-client/issues/262)) ([47abf38](https://github.com/fluencelabs/js-client/commit/47abf3882956ffbdc52df372db26ba6252e8306b))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.2 to 0.7.3
|
- @fluencelabs/interfaces bumped from 0.7.2 to 0.7.3
|
||||||
|
|
||||||
## [0.8.2](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.1...js-peer-v0.8.2) (2023-02-16)
|
## [0.8.2](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.1...js-peer-v0.8.2) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add `getRelayPeerId` method for `IFluenceClient` ([#260](https://github.com/fluencelabs/js-client/issues/260)) ([a10278a](https://github.com/fluencelabs/js-client/commit/a10278afaa782a307feb10c4eac060094c101230))
|
- Add `getRelayPeerId` method for `IFluenceClient` ([#260](https://github.com/fluencelabs/js-client/issues/260)) ([a10278a](https://github.com/fluencelabs/js-client/commit/a10278afaa782a307feb10c4eac060094c101230))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.1 to 0.7.2
|
- @fluencelabs/interfaces bumped from 0.7.1 to 0.7.2
|
||||||
|
|
||||||
## [0.8.1](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.0...js-peer-v0.8.1) (2023-02-16)
|
## [0.8.1](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.0...js-peer-v0.8.1) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Simplify JS Client public API ([#257](https://github.com/fluencelabs/js-client/issues/257)) ([9daaf41](https://github.com/fluencelabs/js-client/commit/9daaf410964d43228192c829c7ff785db6e88081))
|
- Simplify JS Client public API ([#257](https://github.com/fluencelabs/js-client/issues/257)) ([9daaf41](https://github.com/fluencelabs/js-client/commit/9daaf410964d43228192c829c7ff785db6e88081))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.0 to 0.7.1
|
- @fluencelabs/interfaces bumped from 0.7.0 to 0.7.1
|
||||||
|
|
||||||
## [0.8.0](https://github.com/fluencelabs/fluence-js/compare/js-peer-v0.7.0...js-peer-v0.8.0) (2023-02-15)
|
## [0.8.0](https://github.com/fluencelabs/fluence-js/compare/js-peer-v0.7.0...js-peer-v0.8.0) (2023-02-15)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246))
|
- Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246))
|
||||||
* Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243))
|
- Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243))
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246)) ([d4bb8fb](https://github.com/fluencelabs/fluence-js/commit/d4bb8fb42964b3ba25154232980b9ae82c21e627))
|
- Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246)) ([d4bb8fb](https://github.com/fluencelabs/fluence-js/commit/d4bb8fb42964b3ba25154232980b9ae82c21e627))
|
||||||
* Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243)) ([9667c4f](https://github.com/fluencelabs/fluence-js/commit/9667c4fec6868f984bba13249f3c47d293396406))
|
- Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243)) ([9667c4f](https://github.com/fluencelabs/fluence-js/commit/9667c4fec6868f984bba13249f3c47d293396406))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* NodeJS package building ([#248](https://github.com/fluencelabs/fluence-js/issues/248)) ([0d05e51](https://github.com/fluencelabs/fluence-js/commit/0d05e517d89529af513fcb96cfa6c722ccc357a7))
|
- NodeJS package building ([#248](https://github.com/fluencelabs/fluence-js/issues/248)) ([0d05e51](https://github.com/fluencelabs/fluence-js/commit/0d05e517d89529af513fcb96cfa6c722ccc357a7))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.6.0 to 0.7.0
|
- @fluencelabs/interfaces bumped from 0.6.0 to 0.7.0
|
||||||
|
@ -1,68 +1,77 @@
|
|||||||
{
|
{
|
||||||
"name": "@fluencelabs/js-client",
|
"name": "@fluencelabs/js-client",
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"description": "Client for interacting with Fluence network",
|
"description": "Client for interacting with Fluence network",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10",
|
"node": ">=10",
|
||||||
"pnpm": ">=8"
|
"pnpm": ">=8"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"unpkg": "./dist/browser/index.umd.js",
|
"unpkg": "./dist/browser/index.umd.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"exports": {
|
"node": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"default": "./dist/browser/index.js"
|
||||||
"node": "./dist/index.js",
|
},
|
||||||
"default": "./dist/browser/index.js"
|
"imports": {
|
||||||
},
|
"#fetcher": {
|
||||||
"type": "module",
|
"node": "./dist/fetchers/node.js",
|
||||||
"scripts": {
|
"default": "./dist/fetchers/browser.js"
|
||||||
"build": "tsc && vite build",
|
|
||||||
"test": "vitest --threads false run"
|
|
||||||
},
|
|
||||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
|
||||||
"author": "Fluence Labs",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@chainsafe/libp2p-noise": "13.0.0",
|
|
||||||
"@chainsafe/libp2p-yamux": "5.0.0",
|
|
||||||
"@fluencelabs/interfaces": "workspace:*",
|
|
||||||
"@fluencelabs/marine-worker": "0.3.3",
|
|
||||||
"@libp2p/crypto": "2.0.3",
|
|
||||||
"@libp2p/interface": "0.1.2",
|
|
||||||
"@libp2p/peer-id": "3.0.2",
|
|
||||||
"@libp2p/peer-id-factory": "3.0.3",
|
|
||||||
"@libp2p/websockets": "7.0.4",
|
|
||||||
"@multiformats/multiaddr": "11.3.0",
|
|
||||||
"async": "3.2.4",
|
|
||||||
"bs58": "5.0.0",
|
|
||||||
"buffer": "6.0.3",
|
|
||||||
"debug": "4.3.4",
|
|
||||||
"it-length-prefixed": "8.0.4",
|
|
||||||
"it-map": "2.0.0",
|
|
||||||
"it-pipe": "2.0.5",
|
|
||||||
"js-base64": "3.7.5",
|
|
||||||
"libp2p": "0.46.6",
|
|
||||||
"multiformats": "11.0.1",
|
|
||||||
"rxjs": "7.5.5",
|
|
||||||
"threads": "1.7.0",
|
|
||||||
"ts-pattern": "3.3.3",
|
|
||||||
"uint8arrays": "4.0.3",
|
|
||||||
"uuid": "8.3.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@fluencelabs/aqua-api": "0.9.3",
|
|
||||||
"@fluencelabs/avm": "0.48.0",
|
|
||||||
"@fluencelabs/marine-js": "0.7.2",
|
|
||||||
"@rollup/plugin-inject": "5.0.3",
|
|
||||||
"@types/bs58": "4.0.1",
|
|
||||||
"@types/debug": "4.1.7",
|
|
||||||
"@types/node": "20.7.0",
|
|
||||||
"@types/uuid": "8.3.2",
|
|
||||||
"vite": "4.0.4",
|
|
||||||
"vite-tsconfig-paths": "4.0.3",
|
|
||||||
"vitest": "0.29.7"
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"test": "vitest --threads false run"
|
||||||
|
},
|
||||||
|
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||||
|
"author": "Fluence Labs",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@chainsafe/libp2p-noise": "13.0.0",
|
||||||
|
"@chainsafe/libp2p-yamux": "5.0.0",
|
||||||
|
"@fluencelabs/avm": "0.48.0",
|
||||||
|
"@fluencelabs/interfaces": "workspace:*",
|
||||||
|
"@fluencelabs/marine-worker": "0.3.3",
|
||||||
|
"@libp2p/crypto": "2.0.3",
|
||||||
|
"@libp2p/interface": "0.1.2",
|
||||||
|
"@libp2p/peer-id": "3.0.2",
|
||||||
|
"@libp2p/peer-id-factory": "3.0.3",
|
||||||
|
"@libp2p/websockets": "7.0.4",
|
||||||
|
"@multiformats/multiaddr": "11.3.0",
|
||||||
|
"assert": "2.1.0",
|
||||||
|
"async": "3.2.4",
|
||||||
|
"bs58": "5.0.0",
|
||||||
|
"buffer": "6.0.3",
|
||||||
|
"debug": "4.3.4",
|
||||||
|
"it-length-prefixed": "8.0.4",
|
||||||
|
"it-map": "2.0.0",
|
||||||
|
"it-pipe": "2.0.5",
|
||||||
|
"js-base64": "3.7.5",
|
||||||
|
"libp2p": "0.46.6",
|
||||||
|
"multiformats": "11.0.1",
|
||||||
|
"rxjs": "7.5.5",
|
||||||
|
"threads": "fluencelabs/threads.js#b00a5342380b0278d3ae56dcfb170effb3cad7cd",
|
||||||
|
"ts-pattern": "3.3.3",
|
||||||
|
"uint8arrays": "4.0.3",
|
||||||
|
"uuid": "8.3.2",
|
||||||
|
"zod": "3.22.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@fluencelabs/aqua-api": "0.9.3",
|
||||||
|
"@fluencelabs/marine-js": "0.7.2",
|
||||||
|
"@rollup/plugin-inject": "5.0.3",
|
||||||
|
"@types/bs58": "4.0.1",
|
||||||
|
"@types/debug": "4.1.7",
|
||||||
|
"@types/node": "20.7.0",
|
||||||
|
"@types/uuid": "8.3.2",
|
||||||
|
"hotscript": "1.0.13",
|
||||||
|
"vite": "4.4.11",
|
||||||
|
"vite-tsconfig-paths": "4.0.3",
|
||||||
|
"vitest": "0.34.6"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,14 +14,23 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { FnConfig, FunctionCallDef, ServiceDef } from '@fluencelabs/interfaces';
|
import type {
|
||||||
import type { IFluenceClient } from '@fluencelabs/interfaces';
|
FnConfig,
|
||||||
import { getArgumentTypes } from '@fluencelabs/interfaces';
|
FunctionCallDef,
|
||||||
import { callAquaFunction, Fluence, registerService } from './index.js';
|
ServiceDef,
|
||||||
import { FluencePeer } from './jsPeer/FluencePeer.js';
|
PassedArgs,
|
||||||
|
ServiceImpl,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
import { getArgumentTypes } from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
export const isFluencePeer = (fluencePeerCandidate: unknown): fluencePeerCandidate is IFluenceClient => {
|
import { FluencePeer } from "./jsPeer/FluencePeer.js";
|
||||||
return fluencePeerCandidate instanceof FluencePeer;
|
|
||||||
|
import { callAquaFunction, Fluence, registerService } from "./index.js";
|
||||||
|
|
||||||
|
export const isFluencePeer = (
|
||||||
|
fluencePeerCandidate: unknown,
|
||||||
|
): fluencePeerCandidate is FluencePeer => {
|
||||||
|
return fluencePeerCandidate instanceof FluencePeer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,38 +42,47 @@ export const isFluencePeer = (fluencePeerCandidate: unknown): fluencePeerCandida
|
|||||||
* @param script - air script with function execution logic generated by the Aqua compiler
|
* @param script - air script with function execution logic generated by the Aqua compiler
|
||||||
*/
|
*/
|
||||||
export const v5_callFunction = async (
|
export const v5_callFunction = async (
|
||||||
rawFnArgs: Array<any>,
|
rawFnArgs: unknown[],
|
||||||
def: FunctionCallDef,
|
def: FunctionCallDef,
|
||||||
script: string,
|
script: string,
|
||||||
): Promise<unknown> => {
|
): Promise<unknown> => {
|
||||||
const { args, client: peer, config } = await extractFunctionArgs(rawFnArgs, def);
|
const { args, client: peer, config } = extractFunctionArgs(rawFnArgs, def);
|
||||||
|
|
||||||
return callAquaFunction({
|
return callAquaFunction({
|
||||||
args,
|
args,
|
||||||
def,
|
def,
|
||||||
script,
|
script,
|
||||||
config: config || {},
|
config,
|
||||||
peer: peer,
|
peer,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function to support Aqua `service` generation backend
|
* 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
|
* 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
|
* @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
|
* @param def - service definition generated by the Aqua compiler
|
||||||
*/
|
*/
|
||||||
export const v5_registerService = async (args: any[], def: ServiceDef): Promise<unknown> => {
|
export const v5_registerService = (args: unknown[], def: ServiceDef): void => {
|
||||||
const { peer, service, serviceId } = await extractServiceArgs(args, def.defaultServiceId);
|
// TODO: Support this in aqua-to-js package
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
return registerService({
|
const service: ServiceImpl = args.pop() as ServiceImpl;
|
||||||
def,
|
|
||||||
service,
|
const { peer, serviceId } = extractServiceArgs(args, def.defaultServiceId);
|
||||||
serviceId,
|
|
||||||
peer,
|
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:
|
* Arguments could be passed in one these configurations:
|
||||||
* [...actualArgs]
|
* [...actualArgs]
|
||||||
@ -75,48 +93,60 @@ export const v5_registerService = async (args: any[], def: ServiceDef): Promise<
|
|||||||
* This function select the appropriate configuration and returns
|
* This function select the appropriate configuration and returns
|
||||||
* arguments in a structured way of: { peer, config, args }
|
* arguments in a structured way of: { peer, config, args }
|
||||||
*/
|
*/
|
||||||
const extractFunctionArgs = async (
|
function extractFunctionArgs(
|
||||||
args: any[],
|
args: unknown[],
|
||||||
def: FunctionCallDef,
|
def: FunctionCallDef,
|
||||||
): Promise<{
|
): {
|
||||||
client: IFluenceClient;
|
client: FluencePeer;
|
||||||
config?: FnConfig;
|
config: FnConfig;
|
||||||
args: { [key: string]: any };
|
args: PassedArgs;
|
||||||
}> => {
|
} {
|
||||||
const argumentTypes = getArgumentTypes(def);
|
const argumentTypes = getArgumentTypes(def);
|
||||||
const argumentNames = Object.keys(argumentTypes);
|
const argumentNames = Object.keys(argumentTypes);
|
||||||
const numberOfExpectedArgs = argumentNames.length;
|
const numberOfExpectedArgs = argumentNames.length;
|
||||||
|
|
||||||
let peer: IFluenceClient;
|
let peer: FluencePeer;
|
||||||
let structuredArgs: any[];
|
let config: FnConfig;
|
||||||
let config: FnConfig;
|
|
||||||
if (isFluencePeer(args[0])) {
|
if (isFluencePeer(args[0])) {
|
||||||
peer = args[0];
|
peer = args[0];
|
||||||
structuredArgs = args.slice(1, numberOfExpectedArgs + 1);
|
args = args.slice(1);
|
||||||
config = args[numberOfExpectedArgs + 1];
|
} else {
|
||||||
} else {
|
if (Fluence.defaultClient == null) {
|
||||||
if (!Fluence.defaultClient) {
|
throw new Error(
|
||||||
throw new Error(
|
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
|
||||||
'Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?',
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
peer = Fluence.defaultClient;
|
|
||||||
structuredArgs = args.slice(0, numberOfExpectedArgs);
|
|
||||||
config = args[numberOfExpectedArgs];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (structuredArgs.length !== numberOfExpectedArgs) {
|
peer = Fluence.defaultClient;
|
||||||
throw new Error(`Incorrect number of arguments. Expecting ${numberOfExpectedArgs}`);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const argsRes = argumentNames.reduce((acc, name, index) => ({ ...acc, [name]: structuredArgs[index] }), {});
|
const maybeConfig = args[numberOfExpectedArgs];
|
||||||
|
|
||||||
return {
|
if (isConfig(maybeConfig)) {
|
||||||
client: peer,
|
config = maybeConfig;
|
||||||
config: config,
|
} else {
|
||||||
args: argsRes,
|
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:
|
* Arguments could be passed in one these configurations:
|
||||||
@ -130,48 +160,37 @@ const extractFunctionArgs = async (
|
|||||||
* This function select the appropriate configuration and returns
|
* This function select the appropriate configuration and returns
|
||||||
* arguments in a structured way of: { peer, serviceId, service }
|
* arguments in a structured way of: { peer, serviceId, service }
|
||||||
*/
|
*/
|
||||||
const extractServiceArgs = async (
|
const extractServiceArgs = (
|
||||||
args: any[],
|
args: unknown[],
|
||||||
defaultServiceId?: string,
|
defaultServiceId?: string,
|
||||||
): Promise<{ peer: IFluenceClient; serviceId: string; service: any }> => {
|
): {
|
||||||
let peer: IFluenceClient;
|
peer: FluencePeer;
|
||||||
let serviceId: any;
|
serviceId: string | undefined;
|
||||||
let service: any;
|
} => {
|
||||||
if (isFluencePeer(args[0])) {
|
let peer: FluencePeer;
|
||||||
peer = args[0];
|
let serviceId: string | undefined;
|
||||||
} else {
|
|
||||||
if (!Fluence.defaultClient) {
|
if (isFluencePeer(args[0])) {
|
||||||
throw new Error(
|
peer = args[0];
|
||||||
'Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?',
|
args = args.slice(1);
|
||||||
);
|
} else {
|
||||||
}
|
if (Fluence.defaultClient == null) {
|
||||||
peer = Fluence.defaultClient;
|
throw new Error(
|
||||||
|
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof args[0] === 'string') {
|
peer = Fluence.defaultClient;
|
||||||
serviceId = args[0];
|
}
|
||||||
} else if (typeof args[1] === 'string') {
|
|
||||||
serviceId = args[1];
|
|
||||||
} else {
|
|
||||||
serviceId = defaultServiceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Figuring out which overload is the service.
|
if (typeof args[0] === "string") {
|
||||||
// If the first argument is not Fluence Peer and it is an object, then it can only be the service def
|
serviceId = args[0];
|
||||||
// If the first argument is peer, we are checking further. The second argument might either be
|
} else {
|
||||||
// an object, that it must be the service object
|
serviceId = defaultServiceId;
|
||||||
// or a string, which is the service id. In that case the service is the third argument
|
}
|
||||||
if (!isFluencePeer(args[0]) && typeof args[0] === 'object') {
|
|
||||||
service = args[0];
|
|
||||||
} else if (typeof args[1] === 'object') {
|
|
||||||
service = args[1];
|
|
||||||
} else {
|
|
||||||
service = args[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peer: peer,
|
peer,
|
||||||
serviceId: serviceId,
|
serviceId,
|
||||||
service: service,
|
};
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,111 +13,139 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { ClientConfig, ConnectionState, IFluenceClient, PeerIdB58, RelayOptions } from '@fluencelabs/interfaces';
|
|
||||||
import { RelayConnection, RelayConnectionConfig } from '../connection/RelayConnection.js';
|
|
||||||
import { fromOpts, KeyPair } from '../keypair/index.js';
|
|
||||||
import { FluencePeer, PeerConfig } from '../jsPeer/FluencePeer.js';
|
|
||||||
import { relayOptionToMultiaddr } from '../util/libp2pUtils.js';
|
|
||||||
import { IAvmRunner, IMarineHost } from '../marine/interfaces.js';
|
|
||||||
import { JsServiceHost } from '../jsServiceHost/JsServiceHost.js';
|
|
||||||
import { logger } from '../util/logger.js';
|
|
||||||
|
|
||||||
const log = logger('client');
|
import {
|
||||||
|
ClientConfig,
|
||||||
|
ConnectionState,
|
||||||
|
IFluenceClient,
|
||||||
|
RelayOptions,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
|
import {
|
||||||
|
RelayConnection,
|
||||||
|
RelayConnectionConfig,
|
||||||
|
} from "../connection/RelayConnection.js";
|
||||||
|
import { FluencePeer, PeerConfig } from "../jsPeer/FluencePeer.js";
|
||||||
|
import { JsServiceHost } from "../jsServiceHost/JsServiceHost.js";
|
||||||
|
import { fromOpts, KeyPair } from "../keypair/index.js";
|
||||||
|
import { IMarineHost } from "../marine/interfaces.js";
|
||||||
|
import { relayOptionToMultiaddr } from "../util/libp2pUtils.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
|
const log = logger("client");
|
||||||
|
|
||||||
const DEFAULT_TTL_MS = 7000;
|
const DEFAULT_TTL_MS = 7000;
|
||||||
const MAX_OUTBOUND_STREAMS = 1024;
|
const MAX_OUTBOUND_STREAMS = 1024;
|
||||||
const MAX_INBOUND_STREAMS = 1024;
|
const MAX_INBOUND_STREAMS = 1024;
|
||||||
|
|
||||||
export const makeClientPeerConfig = async (
|
export const makeClientPeerConfig = async (
|
||||||
relay: RelayOptions,
|
relay: RelayOptions,
|
||||||
config: ClientConfig,
|
config: ClientConfig,
|
||||||
): Promise<{ peerConfig: PeerConfig; relayConfig: RelayConnectionConfig; keyPair: KeyPair }> => {
|
): Promise<{
|
||||||
const opts = config?.keyPair || { type: 'Ed25519', source: 'random' };
|
peerConfig: PeerConfig;
|
||||||
const keyPair = await fromOpts(opts);
|
relayConfig: RelayConnectionConfig;
|
||||||
const relayAddress = relayOptionToMultiaddr(relay);
|
keyPair: KeyPair;
|
||||||
|
}> => {
|
||||||
|
const opts = config.keyPair ?? { type: "Ed25519", source: "random" };
|
||||||
|
const keyPair = await fromOpts(opts);
|
||||||
|
const relayAddress = relayOptionToMultiaddr(relay);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peerConfig: {
|
peerConfig: {
|
||||||
debug: {
|
debug: {
|
||||||
printParticleId: config?.debug?.printParticleId || false,
|
printParticleId: config.debug?.printParticleId ?? false,
|
||||||
},
|
},
|
||||||
defaultTtlMs: config?.defaultTtlMs || DEFAULT_TTL_MS,
|
defaultTtlMs: config.defaultTtlMs ?? DEFAULT_TTL_MS,
|
||||||
},
|
},
|
||||||
relayConfig: {
|
relayConfig: {
|
||||||
peerId: keyPair.getLibp2pPeerId(),
|
peerId: keyPair.getLibp2pPeerId(),
|
||||||
relayAddress: relayAddress,
|
relayAddress: relayAddress,
|
||||||
dialTimeoutMs: config?.connectionOptions?.dialTimeoutMs,
|
...(config.connectionOptions?.dialTimeoutMs != null
|
||||||
maxInboundStreams: config?.connectionOptions?.maxInboundStreams || MAX_OUTBOUND_STREAMS,
|
? {
|
||||||
maxOutboundStreams: config?.connectionOptions?.maxOutboundStreams || MAX_INBOUND_STREAMS,
|
dialTimeout: config.connectionOptions.dialTimeoutMs,
|
||||||
},
|
}
|
||||||
keyPair: keyPair,
|
: {}),
|
||||||
};
|
maxInboundStreams:
|
||||||
|
config.connectionOptions?.maxInboundStreams ?? MAX_OUTBOUND_STREAMS,
|
||||||
|
maxOutboundStreams:
|
||||||
|
config.connectionOptions?.maxOutboundStreams ?? MAX_INBOUND_STREAMS,
|
||||||
|
},
|
||||||
|
keyPair: keyPair,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ClientPeer extends FluencePeer implements IFluenceClient {
|
export class ClientPeer extends FluencePeer implements IFluenceClient {
|
||||||
constructor(
|
constructor(
|
||||||
peerConfig: PeerConfig,
|
peerConfig: PeerConfig,
|
||||||
relayConfig: RelayConnectionConfig,
|
relayConfig: RelayConnectionConfig,
|
||||||
keyPair: KeyPair,
|
keyPair: KeyPair,
|
||||||
marine: IMarineHost,
|
marine: IMarineHost,
|
||||||
) {
|
) {
|
||||||
super(peerConfig, keyPair, marine, new JsServiceHost(), new RelayConnection(relayConfig));
|
super(
|
||||||
}
|
peerConfig,
|
||||||
|
keyPair,
|
||||||
|
marine,
|
||||||
|
new JsServiceHost(),
|
||||||
|
new RelayConnection(relayConfig),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getPeerId(): string {
|
getPeerId(): string {
|
||||||
return this.keyPair.getPeerId();
|
return this.keyPair.getPeerId();
|
||||||
}
|
}
|
||||||
|
|
||||||
getPeerSecretKey(): Uint8Array {
|
getPeerSecretKey(): Uint8Array {
|
||||||
return this.keyPair.toEd25519PrivateKey();
|
return this.keyPair.toEd25519PrivateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
connectionState: ConnectionState = 'disconnected';
|
connectionState: ConnectionState = "disconnected";
|
||||||
connectionStateChangeHandler: (state: ConnectionState) => void = () => {};
|
connectionStateChangeHandler: (state: ConnectionState) => void = () => {};
|
||||||
|
|
||||||
getRelayPeerId(): string {
|
getRelayPeerId(): string {
|
||||||
return this.internals.getRelayPeerId();
|
return this.internals.getRelayPeerId();
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnectionStateChange(handler: (state: ConnectionState) => void): ConnectionState {
|
onConnectionStateChange(
|
||||||
this.connectionStateChangeHandler = handler;
|
handler: (state: ConnectionState) => void,
|
||||||
|
): ConnectionState {
|
||||||
|
this.connectionStateChangeHandler = handler;
|
||||||
|
|
||||||
return this.connectionState;
|
return this.connectionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private changeConnectionState(state: ConnectionState) {
|
private changeConnectionState(state: ConnectionState) {
|
||||||
this.connectionState = state;
|
this.connectionState = state;
|
||||||
this.connectionStateChangeHandler(state);
|
this.connectionStateChangeHandler(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to the Fluence network
|
* Connect to the Fluence network
|
||||||
*/
|
*/
|
||||||
async connect(): Promise<void> {
|
async connect(): Promise<void> {
|
||||||
return this.start();
|
return this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Disconnect from the Fluence network
|
// * Disconnect from the Fluence network
|
||||||
// */
|
// */
|
||||||
async disconnect(): Promise<void> {
|
async disconnect(): Promise<void> {
|
||||||
return this.stop();
|
return this.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
override async start(): Promise<void> {
|
||||||
log.trace('connecting to Fluence network');
|
log.trace("connecting to Fluence network");
|
||||||
this.changeConnectionState('connecting');
|
this.changeConnectionState("connecting");
|
||||||
await super.start();
|
await super.start();
|
||||||
// TODO: check connection (`checkConnection` function) here
|
// TODO: check connection (`checkConnection` function) here
|
||||||
this.changeConnectionState('connected');
|
this.changeConnectionState("connected");
|
||||||
log.trace('connected');
|
log.trace("connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop(): Promise<void> {
|
override async stop(): Promise<void> {
|
||||||
log.trace('disconnecting from Fluence network');
|
log.trace("disconnecting from Fluence network");
|
||||||
this.changeConnectionState('disconnecting');
|
this.changeConnectionState("disconnecting");
|
||||||
await super.stop();
|
await super.stop();
|
||||||
this.changeConnectionState('disconnected');
|
this.changeConnectionState("disconnected");
|
||||||
log.trace('disconnected');
|
log.trace("disconnected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,36 @@
|
|||||||
import { it, describe, expect } from 'vitest';
|
/**
|
||||||
import { handleTimeout } from '../../particle/Particle.js';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import { doNothing } from '../../jsServiceHost/serviceUtils.js';
|
*
|
||||||
import { registerHandlersHelper, withClient } from '../../util/testUtils.js';
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
import { checkConnection } from '../checkConnection.js';
|
* you may not use this file except in compliance with the License.
|
||||||
import { nodes, RELAY } from './connection.js';
|
* You may obtain a copy of the License at
|
||||||
import { CallServiceData } from '../../jsServiceHost/interfaces.js';
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
describe('FluenceClient usage test suite', () => {
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
it('should make a call through network', async () => {
|
import { it, describe, expect } from "vitest";
|
||||||
await withClient(RELAY, {}, async (peer) => {
|
|
||||||
// arrange
|
|
||||||
|
|
||||||
const script = `
|
import { CallServiceData } from "../../jsServiceHost/interfaces.js";
|
||||||
|
import { doNothing } from "../../jsServiceHost/serviceUtils.js";
|
||||||
|
import { handleTimeout } from "../../particle/Particle.js";
|
||||||
|
import { registerHandlersHelper, withClient } from "../../util/testUtils.js";
|
||||||
|
import { checkConnection } from "../checkConnection.js";
|
||||||
|
|
||||||
|
import { nodes, RELAY } from "./connection.js";
|
||||||
|
|
||||||
|
describe("FluenceClient usage test suite", () => {
|
||||||
|
it("should make a call through network", async () => {
|
||||||
|
await withClient(RELAY, {}, async (peer) => {
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
const script = `
|
||||||
(xor
|
(xor
|
||||||
(seq
|
(seq
|
||||||
(call %init_peer_id% ("load" "relay") [] init_relay)
|
(call %init_peer_id% ("load" "relay") [] init_relay)
|
||||||
@ -26,162 +45,179 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
)
|
)
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const result = await new Promise<string>((resolve, reject) => {
|
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const result = await new Promise<JSONValue>((resolve, reject) => {
|
||||||
load: {
|
if (particle instanceof Error) {
|
||||||
relay: () => {
|
reject(particle.message);
|
||||||
return peer.getRelayPeerId();
|
return;
|
||||||
},
|
}
|
||||||
},
|
|
||||||
callback: {
|
|
||||||
callback: (args: any) => {
|
|
||||||
const [val] = args;
|
|
||||||
resolve(val);
|
|
||||||
},
|
|
||||||
error: (args: any) => {
|
|
||||||
const [error] = args;
|
|
||||||
reject(error);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
registerHandlersHelper(peer, particle, {
|
||||||
});
|
load: {
|
||||||
|
relay: () => {
|
||||||
expect(result).toBe('hello world!');
|
return peer.getRelayPeerId();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
callback: {
|
||||||
|
callback: (args): undefined => {
|
||||||
|
const [val] = args;
|
||||||
|
resolve(val);
|
||||||
|
},
|
||||||
|
error: (args): undefined => {
|
||||||
|
const [error] = args;
|
||||||
|
reject(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe("hello world!");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('check connection should work', async function () {
|
it("check connection should work", async function () {
|
||||||
await withClient(RELAY, {}, async (peer) => {
|
await withClient(RELAY, {}, async (peer) => {
|
||||||
const isConnected = await checkConnection(peer);
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
expect(isConnected).toEqual(true);
|
expect(isConnected).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("check connection should work with ttl", async function () {
|
||||||
|
await withClient(RELAY, {}, async (peer) => {
|
||||||
|
const isConnected = await checkConnection(peer, 10000);
|
||||||
|
|
||||||
|
expect(isConnected).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("two clients should work inside the same time javascript process", async () => {
|
||||||
|
await withClient(RELAY, {}, async (peer1) => {
|
||||||
|
await withClient(RELAY, {}, async (peer2) => {
|
||||||
|
const res = new Promise((resolve) => {
|
||||||
|
peer2.internals.regHandler.common(
|
||||||
|
"test",
|
||||||
|
"test",
|
||||||
|
(req: CallServiceData) => {
|
||||||
|
resolve(req.args[0]);
|
||||||
|
return {
|
||||||
|
result: {},
|
||||||
|
retCode: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('check connection should work with ttl', async function () {
|
const script = `
|
||||||
await withClient(RELAY, {}, async (peer) => {
|
|
||||||
const isConnected = await checkConnection(peer, 10000);
|
|
||||||
|
|
||||||
expect(isConnected).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('two clients should work inside the same time javascript process', async () => {
|
|
||||||
await withClient(RELAY, {}, async (peer1) => {
|
|
||||||
await withClient(RELAY, {}, async (peer2) => {
|
|
||||||
const res = new Promise((resolve) => {
|
|
||||||
peer2.internals.regHandler.common('test', 'test', (req: CallServiceData) => {
|
|
||||||
resolve(req.args[0]);
|
|
||||||
return {
|
|
||||||
result: {},
|
|
||||||
retCode: 0,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const script = `
|
|
||||||
(seq
|
(seq
|
||||||
(call "${peer1.getRelayPeerId()}" ("op" "identity") [])
|
(call "${peer1.getRelayPeerId()}" ("op" "identity") [])
|
||||||
(call "${peer2.getPeerId()}" ("test" "test") ["test"])
|
(call "${peer2.getPeerId()}" ("test" "test") ["test"])
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
const particle = await peer1.internals.createNewParticle(script);
|
|
||||||
|
|
||||||
if (particle instanceof Error) {
|
const particle = await peer1.internals.createNewParticle(script);
|
||||||
throw particle;
|
|
||||||
}
|
|
||||||
|
|
||||||
peer1.internals.initiateParticle(particle, doNothing);
|
if (particle instanceof Error) {
|
||||||
|
throw particle;
|
||||||
|
}
|
||||||
|
|
||||||
expect(await res).toEqual('test');
|
peer1.internals.initiateParticle(particle, doNothing);
|
||||||
});
|
|
||||||
});
|
expect(await res).toEqual("test");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("should make connection to network", () => {
|
||||||
|
it("address as string", async () => {
|
||||||
|
await withClient(nodes[0].multiaddr, {}, async (peer) => {
|
||||||
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
|
expect(isConnected).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should make connection to network', () => {
|
it("address as node", async () => {
|
||||||
it('address as string', async () => {
|
await withClient(nodes[0], {}, async (peer) => {
|
||||||
await withClient(nodes[0].multiaddr, {}, async (peer) => {
|
const isConnected = await checkConnection(peer);
|
||||||
const isConnected = await checkConnection(peer);
|
|
||||||
|
|
||||||
expect(isConnected).toBeTruthy();
|
expect(isConnected).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('address as node', async () => {
|
|
||||||
await withClient(nodes[0], {}, async (peer) => {
|
|
||||||
const isConnected = await checkConnection(peer);
|
|
||||||
|
|
||||||
expect(isConnected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('With connection options: dialTimeout', async () => {
|
|
||||||
await withClient(RELAY, { connectionOptions: { dialTimeoutMs: 100000 } }, async (peer) => {
|
|
||||||
const isConnected = await checkConnection(peer);
|
|
||||||
|
|
||||||
expect(isConnected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('With connection options: skipCheckConnection', async () => {
|
|
||||||
await withClient(RELAY, { connectionOptions: { skipCheckConnection: true } }, async (peer) => {
|
|
||||||
const isConnected = await checkConnection(peer);
|
|
||||||
|
|
||||||
expect(isConnected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('With connection options: defaultTTL', async () => {
|
|
||||||
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
|
|
||||||
const isConnected = await checkConnection(peer);
|
|
||||||
|
|
||||||
expect(isConnected).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('Should throw correct error when the client tries to send a particle not to the relay', async () => {
|
it("With connection options: dialTimeout", async () => {
|
||||||
await withClient(RELAY, {}, async (peer) => {
|
await withClient(
|
||||||
const script = `
|
RELAY,
|
||||||
|
{ connectionOptions: { dialTimeoutMs: 100000 } },
|
||||||
|
async (peer) => {
|
||||||
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
|
expect(isConnected).toBeTruthy();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("With connection options: skipCheckConnection", async () => {
|
||||||
|
await withClient(
|
||||||
|
RELAY,
|
||||||
|
{ connectionOptions: { skipCheckConnection: true } },
|
||||||
|
async (peer) => {
|
||||||
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
|
expect(isConnected).toBeTruthy();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("With connection options: defaultTTL", async () => {
|
||||||
|
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
|
||||||
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
|
expect(isConnected).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip("Should throw correct error when the client tries to send a particle not to the relay", async () => {
|
||||||
|
await withClient(RELAY, {}, async (peer) => {
|
||||||
|
const script = `
|
||||||
(xor
|
(xor
|
||||||
(call "incorrect_peer_id" ("any" "service") [])
|
(call "incorrect_peer_id" ("any" "service") [])
|
||||||
(call %init_peer_id% ("callback" "error") [%last_error%])
|
(call %init_peer_id% ("callback" "error") [%last_error%])
|
||||||
)`;
|
)`;
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
|
||||||
const promise = new Promise((resolve, reject) => {
|
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
callback: {
|
|
||||||
error: (args: any) => {
|
|
||||||
const [error] = args;
|
|
||||||
reject(error);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, (stage) => {
|
const promise = new Promise((_resolve, reject) => {
|
||||||
if (stage.stage === 'sendingError') {
|
if (particle instanceof Error) {
|
||||||
reject(stage.errorMessage);
|
reject(particle.message);
|
||||||
}
|
return;
|
||||||
});
|
}
|
||||||
});
|
|
||||||
|
|
||||||
await promise;
|
registerHandlersHelper(peer, particle, {
|
||||||
|
callback: {
|
||||||
await expect(promise).rejects.toMatch(
|
error: (args): undefined => {
|
||||||
'Particle is expected to be sent to only the single peer (relay which client is connected to)',
|
const [error] = args;
|
||||||
);
|
reject(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
peer.internals.initiateParticle(particle, (stage) => {
|
||||||
|
if (stage.stage === "sendingError") {
|
||||||
|
reject(stage.errorMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
|
||||||
|
await expect(promise).rejects.toMatch(
|
||||||
|
"Particle is expected to be sent to only the single peer (relay which client is connected to)",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
export const nodes = [
|
export const nodes = [
|
||||||
{
|
{
|
||||||
multiaddr: '/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
multiaddr:
|
||||||
peerId: '12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
},
|
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const RELAY = nodes[0].multiaddr;
|
export const RELAY = nodes[0].multiaddr;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,22 +13,28 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { ClientPeer } from './ClientPeer.js';
|
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
import { WrapFnIntoServiceCall } from '../jsServiceHost/serviceUtils.js';
|
|
||||||
import { handleTimeout } from '../particle/Particle.js';
|
|
||||||
|
|
||||||
const log = logger('connection');
|
import { WrapFnIntoServiceCall } from "../jsServiceHost/serviceUtils.js";
|
||||||
|
import { handleTimeout } from "../particle/Particle.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
|
import { ClientPeer } from "./ClientPeer.js";
|
||||||
|
|
||||||
|
const log = logger("connection");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the network connection by sending a ping-like request to relay node
|
* Checks the network connection by sending a ping-like request to relay node
|
||||||
* @param { ClientPeer } peer - The Fluence Client instance.
|
* @param { ClientPeer } peer - The Fluence Client instance.
|
||||||
*/
|
*/
|
||||||
export const checkConnection = async (peer: ClientPeer, ttl?: number): Promise<boolean> => {
|
export const checkConnection = async (
|
||||||
const msg = Math.random().toString(36).substring(7);
|
peer: ClientPeer,
|
||||||
|
ttl?: number,
|
||||||
|
): Promise<boolean> => {
|
||||||
|
const msg = Math.random().toString(36).substring(7);
|
||||||
|
|
||||||
const script = `
|
const script = `
|
||||||
(xor
|
(xor
|
||||||
(seq
|
(seq
|
||||||
(call %init_peer_id% ("load" "relay") [] init_relay)
|
(call %init_peer_id% ("load" "relay") [] init_relay)
|
||||||
@ -45,73 +51,88 @@ export const checkConnection = async (peer: ClientPeer, ttl?: number): Promise<b
|
|||||||
(call %init_peer_id% ("callback" "error") [%last_error%])
|
(call %init_peer_id% ("callback" "error") [%last_error%])
|
||||||
)
|
)
|
||||||
)`;
|
)`;
|
||||||
const particle = await peer.internals.createNewParticle(script, ttl);
|
|
||||||
|
|
||||||
const promise = new Promise<string>((resolve, reject) => {
|
const particle = await peer.internals.createNewParticle(script, ttl);
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
peer.internals.regHandler.forParticle(
|
const promise = new Promise<JSONValue>((resolve, reject) => {
|
||||||
particle.id,
|
if (particle instanceof Error) {
|
||||||
'load',
|
reject(particle.message);
|
||||||
'relay',
|
return;
|
||||||
WrapFnIntoServiceCall(() => {
|
|
||||||
return peer.getRelayPeerId();
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
peer.internals.regHandler.forParticle(
|
|
||||||
particle.id,
|
|
||||||
'load',
|
|
||||||
'msg',
|
|
||||||
WrapFnIntoServiceCall(() => {
|
|
||||||
return msg;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
peer.internals.regHandler.forParticle(
|
|
||||||
particle.id,
|
|
||||||
'callback',
|
|
||||||
'callback',
|
|
||||||
WrapFnIntoServiceCall((args) => {
|
|
||||||
const [val] = args;
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve(val);
|
|
||||||
}, 0);
|
|
||||||
return {};
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
peer.internals.regHandler.forParticle(
|
|
||||||
particle.id,
|
|
||||||
'callback',
|
|
||||||
'error',
|
|
||||||
WrapFnIntoServiceCall((args) => {
|
|
||||||
const [error] = args;
|
|
||||||
setTimeout(() => {
|
|
||||||
reject(error);
|
|
||||||
}, 0);
|
|
||||||
return {};
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(
|
|
||||||
particle,
|
|
||||||
handleTimeout(() => {
|
|
||||||
reject('particle timed out');
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await promise;
|
|
||||||
if (result != msg) {
|
|
||||||
log.error("unexpected behavior. 'identity' must return the passed arguments.");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
log.error('error on establishing connection. Relay: %s error: %j', peer.getRelayPeerId(), e);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peer.internals.regHandler.forParticle(
|
||||||
|
particle.id,
|
||||||
|
"load",
|
||||||
|
"relay",
|
||||||
|
WrapFnIntoServiceCall(() => {
|
||||||
|
return peer.getRelayPeerId();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
peer.internals.regHandler.forParticle(
|
||||||
|
particle.id,
|
||||||
|
"load",
|
||||||
|
"msg",
|
||||||
|
WrapFnIntoServiceCall(() => {
|
||||||
|
return msg;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
peer.internals.regHandler.forParticle(
|
||||||
|
particle.id,
|
||||||
|
"callback",
|
||||||
|
"callback",
|
||||||
|
WrapFnIntoServiceCall((args) => {
|
||||||
|
const [val] = args;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(val);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
peer.internals.regHandler.forParticle(
|
||||||
|
particle.id,
|
||||||
|
"callback",
|
||||||
|
"error",
|
||||||
|
WrapFnIntoServiceCall((args) => {
|
||||||
|
const [error] = args;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
reject(error);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
peer.internals.initiateParticle(
|
||||||
|
particle,
|
||||||
|
handleTimeout(() => {
|
||||||
|
reject("particle timed out");
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await promise;
|
||||||
|
|
||||||
|
if (result !== msg) {
|
||||||
|
log.error(
|
||||||
|
"unexpected behavior. 'identity' must return the passed arguments.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
log.error(
|
||||||
|
"error on establishing connection. Relay: %s error: %j",
|
||||||
|
peer.getRelayPeerId(),
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,223 +1,248 @@
|
|||||||
import { it, describe, expect, test } from 'vitest';
|
/**
|
||||||
import { aqua2ts, ts2aqua } from '../conversions.js';
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
const i32 = { tag: 'scalar', name: 'i32' } as const;
|
import { JSONValue, NonArrowType } from "@fluencelabs/interfaces";
|
||||||
|
import { it, describe, expect, test } from "vitest";
|
||||||
|
|
||||||
|
import { aqua2ts, ts2aqua } from "../conversions.js";
|
||||||
|
|
||||||
|
const i32 = { tag: "scalar", name: "i32" } as const;
|
||||||
|
|
||||||
const opt_i32 = {
|
const opt_i32 = {
|
||||||
tag: 'option',
|
tag: "option",
|
||||||
type: i32,
|
type: i32,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const array_i32 = { tag: 'array', type: i32 };
|
const array_i32 = { tag: "array", type: i32 };
|
||||||
|
|
||||||
const array_opt_i32 = { tag: 'array', type: opt_i32 };
|
const array_opt_i32 = { tag: "array", type: opt_i32 };
|
||||||
|
|
||||||
const labeledProduct = {
|
const labeledProduct = {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
a: i32,
|
a: i32,
|
||||||
b: opt_i32,
|
b: opt_i32,
|
||||||
c: array_opt_i32,
|
c: array_opt_i32,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct = {
|
const struct = {
|
||||||
tag: 'struct',
|
tag: "struct",
|
||||||
name: 'someStruct',
|
name: "someStruct",
|
||||||
fields: {
|
fields: {
|
||||||
a: i32,
|
a: i32,
|
||||||
b: opt_i32,
|
b: opt_i32,
|
||||||
c: array_opt_i32,
|
c: array_opt_i32,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const structs = [
|
const structs = [
|
||||||
{
|
{
|
||||||
aqua: {
|
aqua: {
|
||||||
a: 1,
|
a: 1,
|
||||||
b: [2],
|
b: [2],
|
||||||
c: [[1], [2]],
|
c: [[1], [2]],
|
||||||
},
|
|
||||||
|
|
||||||
ts: {
|
|
||||||
a: 1,
|
|
||||||
b: 2,
|
|
||||||
c: [1, 2],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
aqua: {
|
|
||||||
a: 1,
|
|
||||||
b: [],
|
|
||||||
c: [[], [2]],
|
|
||||||
},
|
|
||||||
|
|
||||||
ts: {
|
ts: {
|
||||||
a: 1,
|
a: 1,
|
||||||
b: null,
|
b: 2,
|
||||||
c: [null, 2],
|
c: [1, 2],
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
aqua: {
|
||||||
|
a: 1,
|
||||||
|
b: [],
|
||||||
|
c: [[], [2]],
|
||||||
|
},
|
||||||
|
|
||||||
|
ts: {
|
||||||
|
a: 1,
|
||||||
|
b: null,
|
||||||
|
c: [null, 2],
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const labeledProduct2 = {
|
const labeledProduct2 = {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const nestedLabeledProductType = {
|
const nestedLabeledProductType = {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
a: labeledProduct2,
|
a: labeledProduct2,
|
||||||
b: {
|
b: {
|
||||||
tag: 'option',
|
tag: "option",
|
||||||
type: labeledProduct2,
|
type: labeledProduct2,
|
||||||
},
|
|
||||||
c: {
|
|
||||||
tag: 'array',
|
|
||||||
type: labeledProduct2,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
c: {
|
||||||
|
tag: "array",
|
||||||
|
type: labeledProduct2,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const nestedStructs = [
|
const nestedStructs = [
|
||||||
{
|
{
|
||||||
aqua: {
|
aqua: {
|
||||||
a: {
|
a: {
|
||||||
x: 1,
|
x: 1,
|
||||||
y: 2,
|
y: 2,
|
||||||
},
|
},
|
||||||
b: [
|
b: [
|
||||||
{
|
{
|
||||||
x: 1,
|
x: 1,
|
||||||
y: 2,
|
y: 2,
|
||||||
},
|
|
||||||
],
|
|
||||||
c: [
|
|
||||||
{
|
|
||||||
x: 1,
|
|
||||||
y: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: 3,
|
|
||||||
y: 4,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
],
|
||||||
ts: {
|
c: [
|
||||||
a: {
|
{
|
||||||
x: 1,
|
x: 1,
|
||||||
y: 2,
|
y: 2,
|
||||||
},
|
|
||||||
b: {
|
|
||||||
x: 1,
|
|
||||||
y: 2,
|
|
||||||
},
|
|
||||||
|
|
||||||
c: [
|
|
||||||
{
|
|
||||||
x: 1,
|
|
||||||
y: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: 3,
|
|
||||||
y: 4,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
x: 3,
|
||||||
|
y: 4,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
aqua: {
|
|
||||||
a: {
|
|
||||||
x: 1,
|
|
||||||
y: 2,
|
|
||||||
},
|
|
||||||
b: [],
|
|
||||||
c: [],
|
|
||||||
},
|
|
||||||
|
|
||||||
ts: {
|
ts: {
|
||||||
a: {
|
a: {
|
||||||
x: 1,
|
x: 1,
|
||||||
y: 2,
|
y: 2,
|
||||||
},
|
},
|
||||||
b: null,
|
b: {
|
||||||
c: [],
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
},
|
||||||
|
|
||||||
|
c: [
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
x: 3,
|
||||||
|
y: 4,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
aqua: {
|
||||||
|
a: {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
},
|
||||||
|
b: [],
|
||||||
|
c: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
ts: {
|
||||||
|
a: {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
},
|
||||||
|
b: null,
|
||||||
|
c: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
describe('Conversion from aqua to typescript', () => {
|
interface ConversionTestArgs {
|
||||||
test.each`
|
aqua: JSONValue;
|
||||||
aqua | ts | type
|
ts: JSONValue;
|
||||||
${1} | ${1} | ${i32}
|
type: NonArrowType;
|
||||||
${[]} | ${null} | ${opt_i32}
|
}
|
||||||
${[1]} | ${1} | ${opt_i32}
|
|
||||||
${[1, 2, 3]} | ${[1, 2, 3]} | ${array_i32}
|
|
||||||
${[]} | ${[]} | ${array_i32}
|
|
||||||
${[[1]]} | ${[1]} | ${array_opt_i32}
|
|
||||||
${[[]]} | ${[null]} | ${array_opt_i32}
|
|
||||||
${[[1], [2]]} | ${[1, 2]} | ${array_opt_i32}
|
|
||||||
${[[], [2]]} | ${[null, 2]} | ${array_opt_i32}
|
|
||||||
${structs[0].aqua} | ${structs[0].ts} | ${labeledProduct}
|
|
||||||
${structs[1].aqua} | ${structs[1].ts} | ${labeledProduct}
|
|
||||||
${structs[0].aqua} | ${structs[0].ts} | ${struct}
|
|
||||||
${structs[1].aqua} | ${structs[1].ts} | ${struct}
|
|
||||||
${nestedStructs[0].aqua} | ${nestedStructs[0].ts} | ${nestedLabeledProductType}
|
|
||||||
${nestedStructs[1].aqua} | ${nestedStructs[1].ts} | ${nestedLabeledProductType}
|
|
||||||
`(
|
|
||||||
//
|
|
||||||
'aqua: $aqua. ts: $ts. type: $type',
|
|
||||||
async ({ aqua, ts, type }) => {
|
|
||||||
// arrange
|
|
||||||
|
|
||||||
// act
|
describe("Conversion from aqua to typescript", () => {
|
||||||
const tsFromAqua = aqua2ts(aqua, type);
|
test.each`
|
||||||
const aquaFromTs = ts2aqua(ts, type);
|
aqua | ts | type
|
||||||
|
${1} | ${1} | ${i32}
|
||||||
|
${[]} | ${null} | ${opt_i32}
|
||||||
|
${[1]} | ${1} | ${opt_i32}
|
||||||
|
${[1, 2, 3]} | ${[1, 2, 3]} | ${array_i32}
|
||||||
|
${[]} | ${[]} | ${array_i32}
|
||||||
|
${[[1]]} | ${[1]} | ${array_opt_i32}
|
||||||
|
${[[]]} | ${[null]} | ${array_opt_i32}
|
||||||
|
${[[1], [2]]} | ${[1, 2]} | ${array_opt_i32}
|
||||||
|
${[[], [2]]} | ${[null, 2]} | ${array_opt_i32}
|
||||||
|
${structs[0].aqua} | ${structs[0].ts} | ${labeledProduct}
|
||||||
|
${structs[1].aqua} | ${structs[1].ts} | ${labeledProduct}
|
||||||
|
${structs[0].aqua} | ${structs[0].ts} | ${struct}
|
||||||
|
${structs[1].aqua} | ${structs[1].ts} | ${struct}
|
||||||
|
${nestedStructs[0].aqua} | ${nestedStructs[0].ts} | ${nestedLabeledProductType}
|
||||||
|
${nestedStructs[1].aqua} | ${nestedStructs[1].ts} | ${nestedLabeledProductType}
|
||||||
|
`(
|
||||||
|
//
|
||||||
|
"aqua: $aqua. ts: $ts. type: $type",
|
||||||
|
({ aqua, ts, type }: ConversionTestArgs) => {
|
||||||
|
// arrange
|
||||||
|
|
||||||
// assert
|
// act
|
||||||
expect(tsFromAqua).toStrictEqual(ts);
|
const tsFromAqua = aqua2ts(aqua, type);
|
||||||
expect(aquaFromTs).toStrictEqual(aqua);
|
const aquaFromTs = ts2aqua(ts, type);
|
||||||
},
|
|
||||||
);
|
// assert
|
||||||
|
expect(tsFromAqua).toStrictEqual(ts);
|
||||||
|
expect(aquaFromTs).toStrictEqual(aqua);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Conversion corner cases', () => {
|
describe("Conversion corner cases", () => {
|
||||||
it('Should accept undefined in object entry', () => {
|
it("Should accept undefined in object entry", () => {
|
||||||
// arrange
|
// arrange
|
||||||
const type = {
|
const type = {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
x: opt_i32,
|
x: opt_i32,
|
||||||
y: opt_i32,
|
y: opt_i32,
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const valueInTs = {
|
const valueInTs = {
|
||||||
x: 1,
|
x: 1,
|
||||||
};
|
};
|
||||||
const valueInAqua = {
|
|
||||||
x: [1],
|
|
||||||
y: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
// act
|
const valueInAqua = {
|
||||||
const aqua = ts2aqua(valueInTs, type);
|
x: [1],
|
||||||
const ts = aqua2ts(valueInAqua, type);
|
y: [],
|
||||||
|
};
|
||||||
|
|
||||||
// assert
|
// act
|
||||||
expect(aqua).toStrictEqual({
|
const aqua = ts2aqua(valueInTs, type);
|
||||||
x: [1],
|
const ts = aqua2ts(valueInAqua, type);
|
||||||
y: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(ts).toStrictEqual({
|
// assert
|
||||||
x: 1,
|
expect(aqua).toStrictEqual({
|
||||||
y: null,
|
x: [1],
|
||||||
});
|
y: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(ts).toStrictEqual({
|
||||||
|
x: 1,
|
||||||
|
y: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,22 +13,31 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { CallAquaFunctionType, getArgumentTypes, isReturnTypeVoid } from '@fluencelabs/interfaces';
|
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
errorHandlingService,
|
FnConfig,
|
||||||
injectRelayService,
|
FunctionCallDef,
|
||||||
injectValueService,
|
getArgumentTypes,
|
||||||
registerParticleScopeService,
|
isReturnTypeVoid,
|
||||||
responseService,
|
PassedArgs,
|
||||||
ServiceDescription,
|
} from "@fluencelabs/interfaces";
|
||||||
userHandlerService,
|
|
||||||
} from './services.js';
|
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
const log = logger('aqua');
|
import {
|
||||||
|
errorHandlingService,
|
||||||
|
injectRelayService,
|
||||||
|
injectValueService,
|
||||||
|
registerParticleScopeService,
|
||||||
|
responseService,
|
||||||
|
ServiceDescription,
|
||||||
|
userHandlerService,
|
||||||
|
} from "./services.js";
|
||||||
|
|
||||||
|
const log = logger("aqua");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function which does all the internal work of creating particles
|
* Convenience function which does all the internal work of creating particles
|
||||||
@ -41,57 +50,96 @@ const log = logger('aqua');
|
|||||||
* @param args - args in the form of JSON where each key corresponds to the name of the argument
|
* @param args - args in the form of JSON where each key corresponds to the name of the argument
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const callAquaFunction: CallAquaFunctionType = async ({ def, script, config, peer, args }) => {
|
|
||||||
log.trace('calling aqua function %j', { def, script, config, args });
|
|
||||||
const argumentTypes = getArgumentTypes(def);
|
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script, config?.ttl);
|
type CallAquaFunctionArgs = {
|
||||||
|
def: FunctionCallDef;
|
||||||
return new Promise((resolve, reject) => {
|
script: string;
|
||||||
if (particle instanceof Error) {
|
config: FnConfig;
|
||||||
return reject(particle.message);
|
peer: FluencePeer;
|
||||||
}
|
args: PassedArgs;
|
||||||
|
};
|
||||||
for (let [name, argVal] of Object.entries(args)) {
|
|
||||||
const type = argumentTypes[name];
|
export const callAquaFunction = async ({
|
||||||
let service: ServiceDescription;
|
def,
|
||||||
if (type.tag === 'arrow') {
|
script,
|
||||||
service = userHandlerService(def.names.callbackSrv, [name, type], argVal);
|
config,
|
||||||
} else {
|
peer,
|
||||||
service = injectValueService(def.names.getDataSrv, name, type, argVal);
|
args,
|
||||||
}
|
}: CallAquaFunctionArgs) => {
|
||||||
registerParticleScopeService(peer, particle, service);
|
// TODO: this function should be rewritten. We can remove asserts if we wont check definition there
|
||||||
}
|
log.trace("calling aqua function %j", { def, script, config, args });
|
||||||
|
const argumentTypes = getArgumentTypes(def);
|
||||||
registerParticleScopeService(peer, particle, responseService(def, resolve));
|
|
||||||
|
const particle = await peer.internals.createNewParticle(script, config.ttl);
|
||||||
registerParticleScopeService(peer, particle, injectRelayService(def, peer));
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
registerParticleScopeService(peer, particle, errorHandlingService(def, reject));
|
for (const [name, argVal] of Object.entries(args)) {
|
||||||
|
const type = argumentTypes[name];
|
||||||
peer.internals.initiateParticle(particle, (stage: any) => {
|
let service: ServiceDescription;
|
||||||
// If function is void, then it's completed when one of the two conditions is met:
|
|
||||||
// 1. The particle is sent to the network (state 'sent')
|
if (type.tag === "arrow") {
|
||||||
// 2. All CallRequests are executed, e.g., all variable loading and local function calls are completed (state 'localWorkDone')
|
// TODO: Add validation here
|
||||||
if (isReturnTypeVoid(def) && (stage.stage === 'sent' || stage.stage === 'localWorkDone')) {
|
assert(
|
||||||
resolve(undefined);
|
typeof argVal === "function",
|
||||||
}
|
"Should not be possible, bad types",
|
||||||
|
);
|
||||||
if (stage.stage === 'sendingError') {
|
|
||||||
reject(`Could not send particle for ${def.functionName}: not connected (particle id: ${particle.id})`);
|
service = userHandlerService(
|
||||||
}
|
def.names.callbackSrv,
|
||||||
|
[name, type],
|
||||||
if (stage.stage === 'expired') {
|
argVal,
|
||||||
reject(
|
);
|
||||||
`Particle expired after ttl of ${particle.ttl}ms for function ${def.functionName} (particle id: ${particle.id})`,
|
} else {
|
||||||
);
|
// TODO: Add validation here
|
||||||
}
|
assert(
|
||||||
|
typeof argVal !== "function",
|
||||||
if (stage.stage === 'interpreterError') {
|
"Should not be possible, bad types",
|
||||||
reject(
|
);
|
||||||
`Script interpretation failed for ${def.functionName}: ${stage.errorMessage} (particle id: ${particle.id})`,
|
|
||||||
);
|
service = injectValueService(def.names.getDataSrv, name, type, argVal);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
})
|
registerParticleScopeService(peer, particle, service);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerParticleScopeService(peer, particle, responseService(def, resolve));
|
||||||
|
|
||||||
|
registerParticleScopeService(peer, particle, injectRelayService(def, peer));
|
||||||
|
|
||||||
|
registerParticleScopeService(
|
||||||
|
peer,
|
||||||
|
particle,
|
||||||
|
errorHandlingService(def, reject),
|
||||||
|
);
|
||||||
|
|
||||||
|
peer.internals.initiateParticle(particle, (stage) => {
|
||||||
|
// If function is void, then it's completed when one of the two conditions is met:
|
||||||
|
// 1. The particle is sent to the network (state 'sent')
|
||||||
|
// 2. All CallRequests are executed, e.g., all variable loading and local function calls are completed (state 'localWorkDone')
|
||||||
|
if (
|
||||||
|
isReturnTypeVoid(def) &&
|
||||||
|
(stage.stage === "sent" || stage.stage === "localWorkDone")
|
||||||
|
) {
|
||||||
|
resolve(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage.stage === "sendingError") {
|
||||||
|
reject(
|
||||||
|
`Could not send particle for ${def.functionName}: not connected (particle id: ${particle.id})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage.stage === "expired") {
|
||||||
|
reject(
|
||||||
|
`Particle expired after ttl of ${particle.ttl}ms for function ${def.functionName} (particle id: ${particle.id})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage.stage === "interpreterError") {
|
||||||
|
reject(
|
||||||
|
`Script interpretation failed for ${def.functionName}: ${stage.errorMessage} (particle id: ${particle.id})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,10 +13,24 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { jsonify } from '../util/utils.js';
|
|
||||||
import { match } from 'ts-pattern';
|
// TODO: This file is a mess. Need to refactor it later
|
||||||
import type { ArrowType, ArrowWithoutCallbacks, NonArrowType } from '@fluencelabs/interfaces';
|
/* eslint-disable */
|
||||||
import { CallServiceData } from '../jsServiceHost/interfaces.js';
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ArrowType,
|
||||||
|
ArrowWithoutCallbacks,
|
||||||
|
JSONArray,
|
||||||
|
JSONValue,
|
||||||
|
NonArrowType,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
import { match } from "ts-pattern";
|
||||||
|
|
||||||
|
import { CallServiceData } from "../jsServiceHost/interfaces.js";
|
||||||
|
import { jsonify } from "../util/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert value from its representation in aqua language to representation in typescript
|
* Convert value from its representation in aqua language to representation in typescript
|
||||||
@ -24,48 +38,53 @@ import { CallServiceData } from '../jsServiceHost/interfaces.js';
|
|||||||
* @param type - definition of the aqua type
|
* @param type - definition of the aqua type
|
||||||
* @returns value represented in typescript
|
* @returns value represented in typescript
|
||||||
*/
|
*/
|
||||||
export const aqua2ts = (value: any, type: NonArrowType): any => {
|
export const aqua2ts = (value: JSONValue, type: NonArrowType): JSONValue => {
|
||||||
const res = match(type)
|
const res = match(type)
|
||||||
.with({ tag: 'nil' }, () => {
|
.with({ tag: "nil" }, () => {
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.with({ tag: 'option' }, (opt) => {
|
.with({ tag: "option" }, (opt) => {
|
||||||
if (value.length === 0) {
|
assert(Array.isArray(value), "Should not be possible, bad types");
|
||||||
return null;
|
|
||||||
} else {
|
if (value.length === 0) {
|
||||||
return aqua2ts(value[0], opt.type);
|
return null;
|
||||||
}
|
} else {
|
||||||
})
|
return aqua2ts(value[0], opt.type);
|
||||||
// @ts-ignore
|
}
|
||||||
.with({ tag: 'scalar' }, { tag: 'bottomType' }, { tag: 'topType' }, () => {
|
})
|
||||||
return value;
|
.with({ tag: "scalar" }, { tag: "bottomType" }, { tag: "topType" }, () => {
|
||||||
})
|
return value;
|
||||||
.with({ tag: 'array' }, (arr) => {
|
})
|
||||||
return value.map((y: any) => aqua2ts(y, arr.type));
|
.with({ tag: "array" }, (arr) => {
|
||||||
})
|
assert(Array.isArray(value), "Should not be possible, bad types");
|
||||||
.with({ tag: 'struct' }, (x) => {
|
return value.map((y) => {
|
||||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
return aqua2ts(y, arr.type);
|
||||||
const val = aqua2ts(value[key], type);
|
});
|
||||||
return { ...agg, [key]: val };
|
})
|
||||||
}, {});
|
.with({ tag: "struct" }, (x) => {
|
||||||
})
|
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||||
.with({ tag: 'labeledProduct' }, (x) => {
|
const val = aqua2ts(value[key], type);
|
||||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
return { ...agg, [key]: val };
|
||||||
const val = aqua2ts(value[key], type);
|
}, {});
|
||||||
return { ...agg, [key]: val };
|
})
|
||||||
}, {});
|
.with({ tag: "labeledProduct" }, (x) => {
|
||||||
})
|
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
const val = aqua2ts(value[key], type);
|
||||||
return x.items.map((type, index) => {
|
return { ...agg, [key]: val };
|
||||||
return aqua2ts(value[index], type);
|
}, {});
|
||||||
});
|
})
|
||||||
})
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
// uncomment to check that every pattern in matched
|
return x.items.map((type, index) => {
|
||||||
// .exhaustive();
|
return aqua2ts(value[index], type);
|
||||||
.otherwise(() => {
|
});
|
||||||
throw new Error('Unexpected tag: ' + jsonify(type));
|
})
|
||||||
});
|
// uncomment to check that every pattern in matched
|
||||||
return res;
|
// .exhaustive();
|
||||||
|
.otherwise(() => {
|
||||||
|
throw new Error("Unexpected tag: " + jsonify(type));
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,30 +93,35 @@ export const aqua2ts = (value: any, type: NonArrowType): any => {
|
|||||||
* @param arrow - aqua type definition
|
* @param arrow - aqua type definition
|
||||||
* @returns arguments in typescript representation
|
* @returns arguments in typescript representation
|
||||||
*/
|
*/
|
||||||
export const aquaArgs2Ts = (req: CallServiceData, arrow: ArrowWithoutCallbacks) => {
|
export const aquaArgs2Ts = (
|
||||||
const argTypes = match(arrow.domain)
|
req: CallServiceData,
|
||||||
.with({ tag: 'labeledProduct' }, (x) => {
|
arrow: ArrowWithoutCallbacks,
|
||||||
return Object.values(x.fields);
|
): JSONArray => {
|
||||||
})
|
const argTypes = match(arrow.domain)
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
.with({ tag: "labeledProduct" }, (x) => {
|
||||||
return x.items;
|
return Object.values(x.fields);
|
||||||
})
|
})
|
||||||
.with({ tag: 'nil' }, (x) => {
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
return [];
|
return x.items;
|
||||||
})
|
})
|
||||||
// uncomment to check that every pattern in matched
|
.with({ tag: "nil" }, (x) => {
|
||||||
// .exhaustive()
|
return [];
|
||||||
.otherwise(() => {
|
})
|
||||||
throw new Error('Unexpected tag: ' + jsonify(arrow.domain));
|
// uncomment to check that every pattern in matched
|
||||||
});
|
// .exhaustive()
|
||||||
|
.otherwise(() => {
|
||||||
if (req.args.length !== argTypes.length) {
|
throw new Error("Unexpected tag: " + jsonify(arrow.domain));
|
||||||
throw new Error(`incorrect number of arguments, expected: ${argTypes.length}, got: ${req.args.length}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return req.args.map((arg, index) => {
|
|
||||||
return aqua2ts(arg, argTypes[index]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (req.args.length !== argTypes.length) {
|
||||||
|
throw new Error(
|
||||||
|
`incorrect number of arguments, expected: ${argTypes.length}, got: ${req.args.length}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return req.args.map((arg, index) => {
|
||||||
|
return aqua2ts(arg, argTypes[index]);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,49 +130,51 @@ export const aquaArgs2Ts = (req: CallServiceData, arrow: ArrowWithoutCallbacks)
|
|||||||
* @param type - definition of the aqua type
|
* @param type - definition of the aqua type
|
||||||
* @returns value represented in aqua
|
* @returns value represented in aqua
|
||||||
*/
|
*/
|
||||||
export const ts2aqua = (value: any, type: NonArrowType): any => {
|
export const ts2aqua = (value: JSONValue, type: NonArrowType): JSONValue => {
|
||||||
const res = match(type)
|
const res = match(type)
|
||||||
.with({ tag: 'nil' }, () => {
|
.with({ tag: "nil" }, () => {
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.with({ tag: 'option' }, (opt) => {
|
.with({ tag: "option" }, (opt) => {
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
return [];
|
return [];
|
||||||
} else {
|
} else {
|
||||||
return [ts2aqua(value, opt.type)];
|
return [ts2aqua(value, opt.type)];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// @ts-ignore
|
.with({ tag: "scalar" }, { tag: "bottomType" }, { tag: "topType" }, () => {
|
||||||
.with({ tag: 'scalar' }, { tag: 'bottomType' }, { tag: 'topType' }, () => {
|
return value;
|
||||||
return value;
|
})
|
||||||
})
|
.with({ tag: "array" }, (arr) => {
|
||||||
.with({ tag: 'array' }, (arr) => {
|
assert(Array.isArray(value), "Should not be possible, bad types");
|
||||||
return value.map((y: any) => ts2aqua(y, arr.type));
|
return value.map((y) => {
|
||||||
})
|
return ts2aqua(y, arr.type);
|
||||||
.with({ tag: 'struct' }, (x) => {
|
});
|
||||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
})
|
||||||
const val = ts2aqua(value[key], type);
|
.with({ tag: "struct" }, (x) => {
|
||||||
return { ...agg, [key]: val };
|
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||||
}, {});
|
const val = ts2aqua(value[key], type);
|
||||||
})
|
return { ...agg, [key]: val };
|
||||||
.with({ tag: 'labeledProduct' }, (x) => {
|
}, {});
|
||||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
})
|
||||||
const val = ts2aqua(value[key], type);
|
.with({ tag: "labeledProduct" }, (x) => {
|
||||||
return { ...agg, [key]: val };
|
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||||
}, {});
|
const val = ts2aqua(value[key], type);
|
||||||
})
|
return { ...agg, [key]: val };
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
}, {});
|
||||||
return x.items.map((type, index) => {
|
})
|
||||||
return ts2aqua(value[index], type);
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
});
|
return x.items.map((type, index) => {
|
||||||
})
|
return ts2aqua(value[index], type);
|
||||||
// uncomment to check that every pattern in matched
|
});
|
||||||
// .exhaustive()
|
})
|
||||||
.otherwise(() => {
|
// uncomment to check that every pattern in matched
|
||||||
throw new Error('Unexpected tag: ' + jsonify(type));
|
// .exhaustive()
|
||||||
});
|
.otherwise(() => {
|
||||||
|
throw new Error("Unexpected tag: " + jsonify(type));
|
||||||
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,22 +183,25 @@ export const ts2aqua = (value: any, type: NonArrowType): any => {
|
|||||||
* @param arrowType - the arrow type which describes the service
|
* @param arrowType - the arrow type which describes the service
|
||||||
* @returns - value represented in aqua
|
* @returns - value represented in aqua
|
||||||
*/
|
*/
|
||||||
export const returnType2Aqua = (returnValue: any, arrowType: ArrowType<NonArrowType>) => {
|
export const returnType2Aqua = (
|
||||||
if (arrowType.codomain.tag === 'nil') {
|
returnValue: any,
|
||||||
return {};
|
arrowType: ArrowType<NonArrowType>,
|
||||||
}
|
) => {
|
||||||
|
if (arrowType.codomain.tag === "nil") {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (arrowType.codomain.items.length === 0) {
|
if (arrowType.codomain.items.length === 0) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arrowType.codomain.items.length === 1) {
|
if (arrowType.codomain.items.length === 1) {
|
||||||
return ts2aqua(returnValue, arrowType.codomain.items[0]);
|
return ts2aqua(returnValue, arrowType.codomain.items[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return arrowType.codomain.items.map((type, index) => {
|
return arrowType.codomain.items.map((type, index) => {
|
||||||
return ts2aqua(returnValue[index], type);
|
return ts2aqua(returnValue[index], type);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,21 +210,26 @@ export const returnType2Aqua = (returnValue: any, arrowType: ArrowType<NonArrowT
|
|||||||
* @param arrow - aqua type definition
|
* @param arrow - aqua type definition
|
||||||
* @returns response value in typescript representation
|
* @returns response value in typescript representation
|
||||||
*/
|
*/
|
||||||
export const responseServiceValue2ts = (req: CallServiceData, arrow: ArrowType<any>) => {
|
export const responseServiceValue2ts = (
|
||||||
return match(arrow.codomain)
|
req: CallServiceData,
|
||||||
.with({ tag: 'nil' }, () => {
|
arrow: ArrowType<any>,
|
||||||
return undefined;
|
) => {
|
||||||
})
|
return match(arrow.codomain)
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
.with({ tag: "nil" }, () => {
|
||||||
if (x.items.length === 0) {
|
return null;
|
||||||
return undefined;
|
})
|
||||||
}
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
|
if (x.items.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (x.items.length === 1) {
|
if (x.items.length === 1) {
|
||||||
return aqua2ts(req.args[0], x.items[0]);
|
return aqua2ts(req.args[0], x.items[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return req.args.map((y, index) => aqua2ts(y, x.items[index]));
|
return req.args.map((y, index) => {
|
||||||
})
|
return aqua2ts(y, x.items[index]);
|
||||||
.exhaustive();
|
});
|
||||||
|
})
|
||||||
|
.exhaustive();
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,43 +13,72 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { RegisterServiceType } from '@fluencelabs/interfaces';
|
|
||||||
import { registerGlobalService, userHandlerService } from './services.js';
|
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
import type { ServiceDef, ServiceImpl } from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
const log = logger('aqua');
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
export const registerService: RegisterServiceType = ({ peer, def, serviceId, service }) => {
|
import { registerGlobalService, userHandlerService } from "./services.js";
|
||||||
log.trace('registering aqua service %o', { def, serviceId, service });
|
|
||||||
|
|
||||||
// Checking for missing keys
|
const log = logger("aqua");
|
||||||
const requiredKeys = def.functions.tag === 'nil' ? [] : Object.keys(def.functions.fields);
|
|
||||||
const incorrectServiceDefinitions = requiredKeys.filter((f) => !(f in service));
|
|
||||||
if (!!incorrectServiceDefinitions.length) {
|
|
||||||
throw new Error(
|
|
||||||
`Error registering service ${serviceId}: missing functions: ` +
|
|
||||||
incorrectServiceDefinitions.map((d) => "'" + d + "'").join(', '),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!serviceId) {
|
interface RegisterServiceArgs {
|
||||||
serviceId = def.defaultServiceId;
|
peer: FluencePeer;
|
||||||
}
|
def: ServiceDef;
|
||||||
|
serviceId: string | undefined;
|
||||||
|
service: ServiceImpl;
|
||||||
|
}
|
||||||
|
|
||||||
if (!serviceId) {
|
export const registerService = ({
|
||||||
throw new Error('Service ID must be specified');
|
peer,
|
||||||
}
|
def,
|
||||||
|
serviceId = def.defaultServiceId,
|
||||||
|
service,
|
||||||
|
}: RegisterServiceArgs) => {
|
||||||
|
// TODO: Need to refactor this. We can compute function types from service implementation, making func more type safe
|
||||||
|
log.trace("registering aqua service %o", { def, serviceId, service });
|
||||||
|
|
||||||
const singleFunctions = def.functions.tag === 'nil' ? [] : Object.entries(def.functions.fields);
|
// Checking for missing keys
|
||||||
for (let singleFunction of singleFunctions) {
|
const requiredKeys =
|
||||||
let [name, type] = singleFunction;
|
def.functions.tag === "nil" ? [] : Object.keys(def.functions.fields);
|
||||||
// The function has type of (arg1, arg2, arg3, ... , callParams) => CallServiceResultType | void
|
|
||||||
// Account for the fact that user service might be defined as a class - .bind(...)
|
|
||||||
const userDefinedHandler = service[name].bind(service);
|
|
||||||
|
|
||||||
const serviceDescription = userHandlerService(serviceId, singleFunction, userDefinedHandler);
|
const incorrectServiceDefinitions = requiredKeys.filter((f) => {
|
||||||
registerGlobalService(peer, serviceDescription);
|
return !(f in service);
|
||||||
}
|
});
|
||||||
log.trace('aqua service registered %s', serviceId);
|
|
||||||
|
if (serviceId == null) {
|
||||||
|
throw new Error("Service ID must be specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incorrectServiceDefinitions.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Error registering service ${serviceId}: missing functions: ` +
|
||||||
|
incorrectServiceDefinitions
|
||||||
|
.map((d) => {
|
||||||
|
return "'" + d + "'";
|
||||||
|
})
|
||||||
|
.join(", "),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const singleFunctions =
|
||||||
|
def.functions.tag === "nil" ? [] : Object.entries(def.functions.fields);
|
||||||
|
|
||||||
|
for (const singleFunction of singleFunctions) {
|
||||||
|
const [name] = singleFunction;
|
||||||
|
// The function has type of (arg1, arg2, arg3, ... , callParams) => CallServiceResultType | void
|
||||||
|
// Account for the fact that user service might be defined as a class - .bind(...)
|
||||||
|
const userDefinedHandler = service[name].bind(service);
|
||||||
|
|
||||||
|
const serviceDescription = userHandlerService(
|
||||||
|
serviceId,
|
||||||
|
singleFunction,
|
||||||
|
userDefinedHandler,
|
||||||
|
);
|
||||||
|
|
||||||
|
registerGlobalService(peer, serviceDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.trace("aqua service registered %s", serviceId);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,186 +13,216 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { SecurityTetraplet } from '@fluencelabs/avm';
|
|
||||||
import { match } from 'ts-pattern';
|
|
||||||
|
|
||||||
import { Particle } from '../particle/Particle.js';
|
import { SecurityTetraplet } from "@fluencelabs/avm";
|
||||||
|
|
||||||
import { aquaArgs2Ts, responseServiceValue2ts, returnType2Aqua, ts2aqua } from './conversions.js';
|
|
||||||
import {
|
import {
|
||||||
CallParams,
|
CallParams,
|
||||||
ArrowWithoutCallbacks,
|
ArrowWithoutCallbacks,
|
||||||
FunctionCallConstants,
|
FunctionCallDef,
|
||||||
FunctionCallDef,
|
NonArrowType,
|
||||||
NonArrowType,
|
ServiceImpl,
|
||||||
IFluenceInternalApi,
|
JSONValue,
|
||||||
} from '@fluencelabs/interfaces';
|
} from "@fluencelabs/interfaces";
|
||||||
import { CallServiceData, GenericCallServiceHandler, ResultCodes } from '../jsServiceHost/interfaces.js';
|
import { fromUint8Array } from "js-base64";
|
||||||
import { fromUint8Array } from 'js-base64';
|
import { match } from "ts-pattern";
|
||||||
|
|
||||||
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
|
import {
|
||||||
|
CallServiceData,
|
||||||
|
GenericCallServiceHandler,
|
||||||
|
ResultCodes,
|
||||||
|
} from "../jsServiceHost/interfaces.js";
|
||||||
|
import { Particle } from "../particle/Particle.js";
|
||||||
|
|
||||||
|
import {
|
||||||
|
aquaArgs2Ts,
|
||||||
|
responseServiceValue2ts,
|
||||||
|
returnType2Aqua,
|
||||||
|
ts2aqua,
|
||||||
|
} from "./conversions.js";
|
||||||
|
|
||||||
export interface ServiceDescription {
|
export interface ServiceDescription {
|
||||||
serviceId: string;
|
serviceId: string;
|
||||||
fnName: string;
|
fnName: string;
|
||||||
handler: GenericCallServiceHandler;
|
handler: GenericCallServiceHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a service which injects relay's peer id into aqua space
|
* Creates a service which injects relay's peer id into aqua space
|
||||||
*/
|
*/
|
||||||
export const injectRelayService = (def: FunctionCallDef, peer: IFluenceInternalApi) => {
|
export const injectRelayService = (def: FunctionCallDef, peer: FluencePeer) => {
|
||||||
return {
|
return {
|
||||||
serviceId: def.names.getDataSrv,
|
serviceId: def.names.getDataSrv,
|
||||||
fnName: def.names.relay,
|
fnName: def.names.relay,
|
||||||
handler: () => {
|
handler: () => {
|
||||||
return {
|
return {
|
||||||
retCode: ResultCodes.success,
|
retCode: ResultCodes.success,
|
||||||
result: peer.internals.getRelayPeerId(),
|
result: peer.internals.getRelayPeerId(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a service which injects plain value into aqua space
|
* Creates a service which injects plain value into aqua space
|
||||||
*/
|
*/
|
||||||
export const injectValueService = (serviceId: string, fnName: string, valueType: NonArrowType, value: any) => {
|
export const injectValueService = (
|
||||||
return {
|
serviceId: string,
|
||||||
serviceId: serviceId,
|
fnName: string,
|
||||||
fnName: fnName,
|
valueType: NonArrowType,
|
||||||
handler: () => {
|
value: JSONValue,
|
||||||
return {
|
) => {
|
||||||
retCode: ResultCodes.success,
|
return {
|
||||||
result: ts2aqua(value, valueType),
|
serviceId: serviceId,
|
||||||
};
|
fnName: fnName,
|
||||||
},
|
handler: () => {
|
||||||
};
|
return {
|
||||||
|
retCode: ResultCodes.success,
|
||||||
|
result: ts2aqua(value, valueType),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a service which is used to return value from aqua function into typescript space
|
* Creates a service which is used to return value from aqua function into typescript space
|
||||||
*/
|
*/
|
||||||
export const responseService = (def: FunctionCallDef, resolveCallback: Function) => {
|
export const responseService = (
|
||||||
return {
|
def: FunctionCallDef,
|
||||||
serviceId: def.names.responseSrv,
|
resolveCallback: (val: JSONValue) => void,
|
||||||
fnName: def.names.responseFnName,
|
) => {
|
||||||
handler: (req: CallServiceData) => {
|
return {
|
||||||
const userFunctionReturn = responseServiceValue2ts(req, def.arrow);
|
serviceId: def.names.responseSrv,
|
||||||
|
fnName: def.names.responseFnName,
|
||||||
|
handler: (req: CallServiceData) => {
|
||||||
|
const userFunctionReturn = responseServiceValue2ts(req, def.arrow);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolveCallback(userFunctionReturn);
|
resolveCallback(userFunctionReturn);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
retCode: ResultCodes.success,
|
retCode: ResultCodes.success,
|
||||||
result: {},
|
result: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a service which is used to return errors from aqua function into typescript space
|
* Creates a service which is used to return errors from aqua function into typescript space
|
||||||
*/
|
*/
|
||||||
export const errorHandlingService = (def: FunctionCallDef, rejectCallback: Function) => {
|
export const errorHandlingService = (
|
||||||
return {
|
def: FunctionCallDef,
|
||||||
serviceId: def.names.errorHandlingSrv,
|
rejectCallback: (err: JSONValue) => void,
|
||||||
fnName: def.names.errorFnName,
|
) => {
|
||||||
handler: (req: CallServiceData) => {
|
return {
|
||||||
const [err, _] = req.args;
|
serviceId: def.names.errorHandlingSrv,
|
||||||
setTimeout(() => {
|
fnName: def.names.errorFnName,
|
||||||
rejectCallback(err);
|
handler: (req: CallServiceData) => {
|
||||||
}, 0);
|
const [err] = req.args;
|
||||||
return {
|
|
||||||
retCode: ResultCodes.success,
|
setTimeout(() => {
|
||||||
result: {},
|
rejectCallback(err);
|
||||||
};
|
}, 0);
|
||||||
},
|
|
||||||
};
|
return {
|
||||||
|
retCode: ResultCodes.success,
|
||||||
|
result: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a service for user-defined service function handler
|
* Creates a service for user-defined service function handler
|
||||||
*/
|
*/
|
||||||
export const userHandlerService = (
|
export const userHandlerService = (
|
||||||
serviceId: string,
|
serviceId: string,
|
||||||
arrowType: [string, ArrowWithoutCallbacks],
|
arrowType: [string, ArrowWithoutCallbacks],
|
||||||
userHandler: (...args: Array<unknown>) => Promise<unknown>,
|
userHandler: ServiceImpl[string],
|
||||||
) => {
|
) => {
|
||||||
const [fnName, type] = arrowType;
|
const [fnName, type] = arrowType;
|
||||||
return {
|
return {
|
||||||
serviceId,
|
serviceId,
|
||||||
fnName,
|
fnName,
|
||||||
handler: async (req: CallServiceData) => {
|
handler: async (req: CallServiceData) => {
|
||||||
const args = [...aquaArgs2Ts(req, type), extractCallParams(req, type)];
|
const args: [...JSONValue[], CallParams<string>] = [
|
||||||
const rawResult = await userHandler.apply(null, args);
|
...aquaArgs2Ts(req, type),
|
||||||
const result = returnType2Aqua(rawResult, type);
|
extractCallParams(req, type),
|
||||||
|
];
|
||||||
|
|
||||||
return {
|
const rawResult = await userHandler.bind(null)(...args);
|
||||||
retCode: ResultCodes.success,
|
const result = returnType2Aqua(rawResult, type);
|
||||||
result: result,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
return {
|
||||||
* Converts argument of aqua function to a corresponding service.
|
retCode: ResultCodes.success,
|
||||||
* For arguments of non-arrow types the resulting service injects the argument into aqua space.
|
result: result,
|
||||||
* For arguments of arrow types the resulting service calls the corresponding function.
|
};
|
||||||
*/
|
},
|
||||||
export const argToServiceDef = (
|
};
|
||||||
arg: any,
|
|
||||||
argName: string,
|
|
||||||
argType: NonArrowType | ArrowWithoutCallbacks,
|
|
||||||
names: FunctionCallConstants,
|
|
||||||
): ServiceDescription => {
|
|
||||||
if (argType.tag === 'arrow') {
|
|
||||||
return userHandlerService(names.callbackSrv, [argName, argType], arg);
|
|
||||||
} else {
|
|
||||||
return injectValueService(names.getDataSrv, argName, arg, argType);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts call params from from call service data according to aqua type definition
|
* Extracts call params from from call service data according to aqua type definition
|
||||||
*/
|
*/
|
||||||
const extractCallParams = (req: CallServiceData, arrow: ArrowWithoutCallbacks): CallParams<any> => {
|
const extractCallParams = (
|
||||||
const names = match(arrow.domain)
|
req: CallServiceData,
|
||||||
.with({ tag: 'nil' }, () => {
|
arrow: ArrowWithoutCallbacks,
|
||||||
return [] as string[];
|
): CallParams<string> => {
|
||||||
})
|
const names: (string | undefined)[] = match(arrow.domain)
|
||||||
.with({ tag: 'labeledProduct' }, (x) => {
|
.with({ tag: "nil" }, () => {
|
||||||
return Object.keys(x.fields);
|
return [];
|
||||||
})
|
})
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
return x.items.map((_, index) => 'arg' + index);
|
return x.items.map((_, index) => {
|
||||||
})
|
return "arg" + index;
|
||||||
.exhaustive();
|
});
|
||||||
|
})
|
||||||
|
.with({ tag: "labeledProduct" }, (x) => {
|
||||||
|
return Object.keys(x.fields);
|
||||||
|
})
|
||||||
|
.exhaustive();
|
||||||
|
|
||||||
const tetraplets: Record<string, SecurityTetraplet[]> = {};
|
const tetraplets: Record<string, SecurityTetraplet[]> = {};
|
||||||
for (let i = 0; i < req.args.length; i++) {
|
|
||||||
if (names[i]) {
|
for (let i = 0; i < req.args.length; i++) {
|
||||||
tetraplets[names[i]] = req.tetraplets[i];
|
const name = names[i];
|
||||||
}
|
|
||||||
|
if (name != null) {
|
||||||
|
tetraplets[name] = req.tetraplets[i];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const callParams = {
|
const callParams = {
|
||||||
...req.particleContext,
|
...req.particleContext,
|
||||||
signature: fromUint8Array(req.particleContext.signature),
|
signature: fromUint8Array(req.particleContext.signature),
|
||||||
tetraplets,
|
tetraplets,
|
||||||
};
|
};
|
||||||
|
|
||||||
return callParams;
|
return callParams;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const registerParticleScopeService = (
|
export const registerParticleScopeService = (
|
||||||
peer: IFluenceInternalApi,
|
peer: FluencePeer,
|
||||||
particle: Particle,
|
particle: Particle,
|
||||||
service: ServiceDescription,
|
service: ServiceDescription,
|
||||||
) => {
|
) => {
|
||||||
peer.internals.regHandler.forParticle(particle.id, service.serviceId, service.fnName, service.handler);
|
peer.internals.regHandler.forParticle(
|
||||||
|
particle.id,
|
||||||
|
service.serviceId,
|
||||||
|
service.fnName,
|
||||||
|
service.handler,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const registerGlobalService = (peer: IFluenceInternalApi, service: ServiceDescription) => {
|
export const registerGlobalService = (
|
||||||
peer.internals.regHandler.common(service.serviceId, service.fnName, service.handler);
|
peer: FluencePeer,
|
||||||
|
service: ServiceDescription,
|
||||||
|
) => {
|
||||||
|
peer.internals.regHandler.common(
|
||||||
|
service.serviceId,
|
||||||
|
service.fnName,
|
||||||
|
service.handler,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2020 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -13,235 +13,281 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import { pipe } from 'it-pipe';
|
|
||||||
import { decode, encode } from 'it-length-prefixed';
|
|
||||||
import type { PeerId } from '@libp2p/interface/peer-id';
|
|
||||||
import { createLibp2p, Libp2p } from 'libp2p';
|
|
||||||
|
|
||||||
import { noise } from '@chainsafe/libp2p-noise';
|
import { noise } from "@chainsafe/libp2p-noise";
|
||||||
import { yamux } from '@chainsafe/libp2p-yamux';
|
import { yamux } from "@chainsafe/libp2p-yamux";
|
||||||
import { webSockets } from '@libp2p/websockets';
|
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
import { all } from '@libp2p/websockets/filters';
|
import { Stream } from "@libp2p/interface/connection";
|
||||||
import { multiaddr, type Multiaddr } from '@multiformats/multiaddr';
|
import type { PeerId } from "@libp2p/interface/peer-id";
|
||||||
|
import { peerIdFromString } from "@libp2p/peer-id";
|
||||||
|
import { webSockets } from "@libp2p/websockets";
|
||||||
|
import { all } from "@libp2p/websockets/filters";
|
||||||
|
import { multiaddr, type Multiaddr } from "@multiformats/multiaddr";
|
||||||
|
import { decode, encode } from "it-length-prefixed";
|
||||||
|
import map from "it-map";
|
||||||
|
import { pipe } from "it-pipe";
|
||||||
|
import { createLibp2p, Libp2p } from "libp2p";
|
||||||
|
import { identifyService } from "libp2p/identify";
|
||||||
|
import { pingService } from "libp2p/ping";
|
||||||
|
import { Subject } from "rxjs";
|
||||||
|
import { fromString } from "uint8arrays/from-string";
|
||||||
|
import { toString } from "uint8arrays/to-string";
|
||||||
|
|
||||||
import map from 'it-map';
|
import { KeyPair } from "../keypair/index.js";
|
||||||
import { fromString } from 'uint8arrays/from-string';
|
import { IParticle } from "../particle/interfaces.js";
|
||||||
import { toString } from 'uint8arrays/to-string';
|
import {
|
||||||
|
buildParticleMessage,
|
||||||
|
Particle,
|
||||||
|
serializeToString,
|
||||||
|
} from "../particle/Particle.js";
|
||||||
|
import { throwHasNoPeerId } from "../util/libp2pUtils.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
import { IConnection } from "./interfaces.js";
|
||||||
import { Subject } from 'rxjs';
|
|
||||||
import { throwIfHasNoPeerId } from '../util/libp2pUtils.js';
|
|
||||||
import { IConnection } from './interfaces.js';
|
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
|
||||||
import { buildParticleMessage, Particle, serializeToString, verifySignature } from '../particle/Particle.js';
|
|
||||||
import { identifyService } from 'libp2p/identify';
|
|
||||||
import { pingService } from 'libp2p/ping';
|
|
||||||
import { unmarshalPublicKey } from '@libp2p/crypto/keys';
|
|
||||||
import { peerIdFromString } from '@libp2p/peer-id';
|
|
||||||
import { Stream } from '@libp2p/interface/connection';
|
|
||||||
import { KeyPair } from '../keypair/index.js';
|
|
||||||
|
|
||||||
const log = logger('connection');
|
const log = logger("connection");
|
||||||
|
|
||||||
export const PROTOCOL_NAME = '/fluence/particle/2.0.0';
|
export const PROTOCOL_NAME = "/fluence/particle/2.0.0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options to configure fluence relay connection
|
* Options to configure fluence relay connection
|
||||||
*/
|
*/
|
||||||
export interface RelayConnectionConfig {
|
export interface RelayConnectionConfig {
|
||||||
/**
|
/**
|
||||||
* Peer id of the Fluence Peer
|
* Peer id of the Fluence Peer
|
||||||
*/
|
*/
|
||||||
peerId: PeerId;
|
peerId: PeerId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiaddress of the relay to make connection to
|
* Multiaddress of the relay to make connection to
|
||||||
*/
|
*/
|
||||||
relayAddress: Multiaddr;
|
relayAddress: Multiaddr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dialing timeout in milliseconds
|
* The dialing timeout in milliseconds
|
||||||
*/
|
*/
|
||||||
dialTimeoutMs?: number;
|
dialTimeoutMs?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of inbound streams for the libp2p node.
|
* The maximum number of inbound streams for the libp2p node.
|
||||||
* Default: 1024
|
* Default: 1024
|
||||||
*/
|
*/
|
||||||
maxInboundStreams: number;
|
maxInboundStreams: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of outbound streams for the libp2p node.
|
* The maximum number of outbound streams for the libp2p node.
|
||||||
* Default: 1024
|
* Default: 1024
|
||||||
*/
|
*/
|
||||||
maxOutboundStreams: number;
|
maxOutboundStreams: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation for JS peers which connects to Fluence through relay node
|
* Implementation for JS peers which connects to Fluence through relay node
|
||||||
*/
|
*/
|
||||||
export class RelayConnection implements IConnection {
|
export class RelayConnection implements IConnection {
|
||||||
private relayAddress: Multiaddr;
|
private relayAddress: Multiaddr;
|
||||||
private lib2p2Peer: Libp2p | null = null;
|
private lib2p2Peer: Libp2p | null = null;
|
||||||
|
private relayPeerId: string;
|
||||||
|
|
||||||
constructor(private config: RelayConnectionConfig) {
|
constructor(private config: RelayConnectionConfig) {
|
||||||
this.relayAddress = multiaddr(this.config.relayAddress);
|
this.relayAddress = multiaddr(this.config.relayAddress);
|
||||||
throwIfHasNoPeerId(this.relayAddress);
|
const peerId = this.relayAddress.getPeerId();
|
||||||
|
|
||||||
|
if (peerId == null) {
|
||||||
|
throwHasNoPeerId(this.relayAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRelayPeerId(): string {
|
this.relayPeerId = peerId;
|
||||||
// since we check for peer id in constructor, we can safely use ! here
|
}
|
||||||
return this.relayAddress.getPeerId()!;
|
|
||||||
|
getRelayPeerId(): string {
|
||||||
|
return this.relayPeerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
supportsRelay(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
particleSource = new Subject<IParticle>();
|
||||||
|
|
||||||
|
async start(): Promise<void> {
|
||||||
|
// check if already started
|
||||||
|
if (this.lib2p2Peer !== null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
supportsRelay(): boolean {
|
this.lib2p2Peer = await createLibp2p({
|
||||||
return true;
|
peerId: this.config.peerId,
|
||||||
}
|
transports: [
|
||||||
|
webSockets({
|
||||||
particleSource = new Subject<IParticle>();
|
filter: all,
|
||||||
|
}),
|
||||||
async start(): Promise<void> {
|
],
|
||||||
// check if already started
|
streamMuxers: [yamux()],
|
||||||
if (this.lib2p2Peer !== null) {
|
connectionEncryption: [noise()],
|
||||||
return;
|
connectionManager: {
|
||||||
}
|
...(this.config.dialTimeoutMs != null
|
||||||
|
? {
|
||||||
this.lib2p2Peer = await createLibp2p({
|
dialTimeout: this.config.dialTimeoutMs,
|
||||||
peerId: this.config.peerId,
|
|
||||||
transports: [
|
|
||||||
webSockets({
|
|
||||||
filter: all,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
streamMuxers: [yamux()],
|
|
||||||
connectionEncryption: [noise()],
|
|
||||||
connectionManager: {
|
|
||||||
dialTimeout: this.config.dialTimeoutMs,
|
|
||||||
},
|
|
||||||
connectionGater: {
|
|
||||||
// By default, this function forbids connections to private peers. For example multiaddr with ip 127.0.0.1 isn't allowed
|
|
||||||
denyDialMultiaddr: () => Promise.resolve(false),
|
|
||||||
},
|
|
||||||
services: {
|
|
||||||
identify: identifyService(),
|
|
||||||
ping: pingService(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const supportedProtocols = (await this.lib2p2Peer.peerStore.get(this.lib2p2Peer.peerId)).protocols;
|
|
||||||
await this.lib2p2Peer.peerStore.patch(this.lib2p2Peer.peerId, {
|
|
||||||
protocols: [...supportedProtocols, PROTOCOL_NAME],
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
async stop(): Promise<void> {
|
|
||||||
// check if already stopped
|
|
||||||
if (this.lib2p2Peer === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.lib2p2Peer.unhandle(PROTOCOL_NAME);
|
|
||||||
await this.lib2p2Peer.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendParticle(nextPeerIds: PeerIdB58[], particle: IParticle): Promise<void> {
|
|
||||||
if (this.lib2p2Peer === null) {
|
|
||||||
throw new Error('Relay connection is not started');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextPeerIds.length !== 1 && nextPeerIds[0] !== this.getRelayPeerId()) {
|
|
||||||
throw new Error(
|
|
||||||
`Relay connection only accepts peer id of the connected relay. Got: ${JSON.stringify(
|
|
||||||
nextPeerIds,
|
|
||||||
)} instead.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.trace('sending particle...');
|
|
||||||
// Reusing active connection here
|
|
||||||
const stream = await this.lib2p2Peer.dialProtocol(this.relayAddress, PROTOCOL_NAME);
|
|
||||||
log.trace('created stream with id ', stream.id);
|
|
||||||
const sink = stream.sink;
|
|
||||||
|
|
||||||
await pipe([fromString(serializeToString(particle))], encode(), sink);
|
|
||||||
log.trace('data written to sink');
|
|
||||||
}
|
|
||||||
|
|
||||||
private async processIncomingMessage(msg: string, stream: Stream) {
|
|
||||||
let particle: Particle | undefined;
|
|
||||||
try {
|
|
||||||
particle = Particle.fromString(msg);
|
|
||||||
log.trace('got particle from stream with id %s and particle id %s', stream.id, particle.id);
|
|
||||||
const initPeerId = peerIdFromString(particle.initPeerId);
|
|
||||||
|
|
||||||
if (initPeerId.publicKey === undefined) {
|
|
||||||
log.error(
|
|
||||||
'cannot retrieve public key from init_peer_id. particle id: %s. init_peer_id: %s',
|
|
||||||
particle.id,
|
|
||||||
particle.initPeerId,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
: {}),
|
||||||
|
},
|
||||||
|
connectionGater: {
|
||||||
|
// By default, this function forbids connections to private peers. For example multiaddr with ip 127.0.0.1 isn't allowed
|
||||||
|
denyDialMultiaddr: () => {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
services: {
|
||||||
|
identify: identifyService(),
|
||||||
|
ping: pingService(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const isVerified = await KeyPair.verifyWithPublicKey(
|
const supportedProtocols = (
|
||||||
initPeerId.publicKey,
|
await this.lib2p2Peer.peerStore.get(this.lib2p2Peer.peerId)
|
||||||
buildParticleMessage(particle),
|
).protocols;
|
||||||
particle.signature,
|
|
||||||
);
|
await this.lib2p2Peer.peerStore.patch(this.lib2p2Peer.peerId, {
|
||||||
if (isVerified) {
|
protocols: [...supportedProtocols, PROTOCOL_NAME],
|
||||||
this.particleSource.next(particle);
|
});
|
||||||
} else {
|
|
||||||
log.trace('particle signature is incorrect. rejecting particle with id: %s', particle.id);
|
await this.connect();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
const particleId = particle?.id;
|
async stop(): Promise<void> {
|
||||||
const particleIdMessage = typeof particleId === 'string' ? `. particle id: ${particleId}` : '';
|
// check if already stopped
|
||||||
log.error(`error on handling an incoming message: %O%s`, e, particleIdMessage);
|
if (this.lib2p2Peer === null) {
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async connect() {
|
await this.lib2p2Peer.unhandle(PROTOCOL_NAME);
|
||||||
if (this.lib2p2Peer === null) {
|
await this.lib2p2Peer.stop();
|
||||||
throw new Error('Relay connection is not started');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
await this.lib2p2Peer.handle(
|
async sendParticle(
|
||||||
[PROTOCOL_NAME],
|
nextPeerIds: PeerIdB58[],
|
||||||
async ({ connection, stream }) =>
|
particle: IParticle,
|
||||||
pipe(
|
): Promise<void> {
|
||||||
stream.source,
|
if (this.lib2p2Peer === null) {
|
||||||
decode(),
|
throw new Error("Relay connection is not started");
|
||||||
(source) => map(source, (buf) => toString(buf.subarray())),
|
}
|
||||||
async (source) => {
|
|
||||||
try {
|
if (nextPeerIds.length !== 1 && nextPeerIds[0] !== this.getRelayPeerId()) {
|
||||||
for await (const msg of source) {
|
throw new Error(
|
||||||
await this.processIncomingMessage(msg, stream);
|
`Relay connection only accepts peer id of the connected relay. Got: ${JSON.stringify(
|
||||||
}
|
nextPeerIds,
|
||||||
} catch (e) {
|
)} instead.`,
|
||||||
log.error('connection closed: %j', e);
|
);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
),
|
log.trace("sending particle...");
|
||||||
{
|
|
||||||
maxInboundStreams: this.config.maxInboundStreams,
|
// Reusing active connection here
|
||||||
maxOutboundStreams: this.config.maxOutboundStreams,
|
const stream = await this.lib2p2Peer.dialProtocol(
|
||||||
},
|
this.relayAddress,
|
||||||
|
PROTOCOL_NAME,
|
||||||
|
);
|
||||||
|
|
||||||
|
log.trace("created stream with id ", stream.id);
|
||||||
|
const sink = stream.sink;
|
||||||
|
|
||||||
|
await pipe([fromString(serializeToString(particle))], encode(), sink);
|
||||||
|
log.trace("data written to sink");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Await will appear after uncommenting lines in func body
|
||||||
|
// eslint-disable-next-line @typescript-eslint/require-await
|
||||||
|
private async processIncomingMessage(msg: string, stream: Stream) {
|
||||||
|
let particle: Particle | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
particle = Particle.fromString(msg);
|
||||||
|
|
||||||
|
log.trace(
|
||||||
|
"got particle from stream with id %s and particle id %s",
|
||||||
|
stream.id,
|
||||||
|
particle.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
const initPeerId = peerIdFromString(particle.initPeerId);
|
||||||
|
|
||||||
|
if (initPeerId.publicKey === undefined) {
|
||||||
|
log.error(
|
||||||
|
"cannot retrieve public key from init_peer_id. particle id: %s. init_peer_id: %s",
|
||||||
|
particle.id,
|
||||||
|
particle.initPeerId,
|
||||||
);
|
);
|
||||||
|
|
||||||
log.debug("dialing to the node with client's address: %s", this.lib2p2Peer.peerId.toString());
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
const isVerified = await KeyPair.verifyWithPublicKey(
|
||||||
await this.lib2p2Peer.dial(this.relayAddress);
|
initPeerId.publicKey,
|
||||||
} catch (e: any) {
|
buildParticleMessage(particle),
|
||||||
if (e.name === 'AggregateError' && e._errors?.length === 1) {
|
particle.signature,
|
||||||
const error = e._errors[0];
|
);
|
||||||
throw new Error(`Error dialing node ${this.relayAddress}:\n${error.code}\n${error.message}`);
|
|
||||||
} else {
|
if (isVerified) {
|
||||||
throw e;
|
this.particleSource.next(particle);
|
||||||
}
|
} else {
|
||||||
}
|
log.trace(
|
||||||
|
"particle signature is incorrect. rejecting particle with id: %s",
|
||||||
|
particle.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
const particleId = particle?.id;
|
||||||
|
|
||||||
|
const particleIdMessage =
|
||||||
|
typeof particleId === "string" ? `. particle id: ${particleId}` : "";
|
||||||
|
|
||||||
|
log.error(
|
||||||
|
`error on handling an incoming message: %O%s`,
|
||||||
|
e,
|
||||||
|
particleIdMessage,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async connect() {
|
||||||
|
if (this.lib2p2Peer === null) {
|
||||||
|
throw new Error("Relay connection is not started");
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.lib2p2Peer.handle(
|
||||||
|
[PROTOCOL_NAME],
|
||||||
|
({ stream }) => {
|
||||||
|
void pipe(
|
||||||
|
stream.source,
|
||||||
|
decode(),
|
||||||
|
(source) => {
|
||||||
|
return map(source, (buf) => {
|
||||||
|
return toString(buf.subarray());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async (source) => {
|
||||||
|
try {
|
||||||
|
for await (const msg of source) {
|
||||||
|
await this.processIncomingMessage(msg, stream);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.error("connection closed: %j", e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
maxInboundStreams: this.config.maxInboundStreams,
|
||||||
|
maxOutboundStreams: this.config.maxOutboundStreams,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"dialing to the node with client's address: %s",
|
||||||
|
this.lib2p2Peer.peerId.toString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.lib2p2Peer.dial(this.relayAddress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,34 +13,36 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import type { Subscribable } from 'rxjs';
|
import type { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
import type { Subscribable } from "rxjs";
|
||||||
import { IStartable } from '../util/commonTypes.js';
|
|
||||||
|
import { IParticle } from "../particle/interfaces.js";
|
||||||
|
import { IStartable } from "../util/commonTypes.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for connection used in Fluence Peer.
|
* Interface for connection used in Fluence Peer.
|
||||||
*/
|
*/
|
||||||
export interface IConnection extends IStartable {
|
export interface IConnection extends IStartable {
|
||||||
/**
|
/**
|
||||||
* Observable that emits particles received from the connection.
|
* Observable that emits particles received from the connection.
|
||||||
*/
|
*/
|
||||||
particleSource: Subscribable<IParticle>;
|
particleSource: Subscribable<IParticle>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send particle to the network using the connection.
|
* Send particle to the network using the connection.
|
||||||
* @param nextPeerIds - list of peer ids to send the particle to
|
* @param nextPeerIds - list of peer ids to send the particle to
|
||||||
* @param particle - particle to send
|
* @param particle - particle to send
|
||||||
*/
|
*/
|
||||||
sendParticle(nextPeerIds: PeerIdB58[], particle: IParticle): Promise<void>;
|
sendParticle(nextPeerIds: PeerIdB58[], particle: IParticle): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get peer id of the relay peer. Throws an error if the connection doesn't support relay.
|
* Get peer id of the relay peer. Throws an error if the connection doesn't support relay.
|
||||||
*/
|
*/
|
||||||
getRelayPeerId(): PeerIdB58;
|
getRelayPeerId(): PeerIdB58;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the connection supports relay.
|
* Check if the connection supports relay.
|
||||||
*/
|
*/
|
||||||
supportsRelay(): boolean;
|
supportsRelay(): boolean;
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,54 @@
|
|||||||
import { it, describe, expect, beforeEach, afterEach } from 'vitest';
|
/**
|
||||||
import { DEFAULT_CONFIG, FluencePeer } from '../../jsPeer/FluencePeer.js';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import { CallServiceData, ResultCodes } from '../../jsServiceHost/interfaces.js';
|
*
|
||||||
import { KeyPair } from '../../keypair/index.js';
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
import { EphemeralNetworkClient } from '../client.js';
|
* 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 { EphemeralNetwork, defaultConfig } from '../network.js';
|
import { it, describe, expect, beforeEach, afterEach } from "vitest";
|
||||||
|
|
||||||
|
import { DEFAULT_CONFIG, FluencePeer } from "../../jsPeer/FluencePeer.js";
|
||||||
|
import { ResultCodes } from "../../jsServiceHost/interfaces.js";
|
||||||
|
import { KeyPair } from "../../keypair/index.js";
|
||||||
|
import { EphemeralNetworkClient } from "../client.js";
|
||||||
|
import { EphemeralNetwork, defaultConfig } from "../network.js";
|
||||||
|
|
||||||
let en: EphemeralNetwork;
|
let en: EphemeralNetwork;
|
||||||
let client: FluencePeer;
|
let client: FluencePeer;
|
||||||
const relay = defaultConfig.peers[0].peerId;
|
const relay = defaultConfig.peers[0].peerId;
|
||||||
|
|
||||||
// TODO: race condition here. Needs to be fixed
|
// TODO: race condition here. Needs to be fixed
|
||||||
describe.skip('Ephemeral networks tests', () => {
|
describe.skip("Ephemeral networks tests", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
en = new EphemeralNetwork(defaultConfig);
|
en = new EphemeralNetwork(defaultConfig);
|
||||||
await en.up();
|
await en.up();
|
||||||
|
|
||||||
const kp = await KeyPair.randomEd25519();
|
const kp = await KeyPair.randomEd25519();
|
||||||
client = new EphemeralNetworkClient(DEFAULT_CONFIG, kp, en, relay);
|
client = new EphemeralNetworkClient(DEFAULT_CONFIG, kp, en, relay);
|
||||||
await client.start();
|
await client.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await client.stop();
|
||||||
|
await en.down();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("smoke test", async function () {
|
||||||
|
// arrange
|
||||||
|
const peers = defaultConfig.peers.map((x) => {
|
||||||
|
return x.peerId;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
const script = `
|
||||||
if (client) {
|
|
||||||
await client.stop();
|
|
||||||
}
|
|
||||||
if (en) {
|
|
||||||
await en.down();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('smoke test', async function () {
|
|
||||||
// arrange
|
|
||||||
const peers = defaultConfig.peers.map((x) => x.peerId);
|
|
||||||
|
|
||||||
const script = `
|
|
||||||
(seq
|
(seq
|
||||||
(call "${relay}" ("op" "noop") [])
|
(call "${relay}" ("op" "noop") [])
|
||||||
(seq
|
(seq
|
||||||
@ -59,22 +73,27 @@ describe.skip('Ephemeral networks tests', () => {
|
|||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const particle = await client.internals.createNewParticle(script);
|
const particle = await client.internals.createNewParticle(script);
|
||||||
|
|
||||||
const promise = new Promise<string>((resolve) => {
|
const promise = new Promise<string>((resolve) => {
|
||||||
client.internals.regHandler.forParticle(particle.id, 'test', 'test', (req: CallServiceData) => {
|
client.internals.regHandler.forParticle(
|
||||||
resolve('success');
|
particle.id,
|
||||||
return {
|
"test",
|
||||||
result: 'test',
|
"test",
|
||||||
retCode: ResultCodes.success,
|
() => {
|
||||||
};
|
resolve("success");
|
||||||
});
|
return {
|
||||||
});
|
result: "test",
|
||||||
|
retCode: ResultCodes.success,
|
||||||
// act
|
};
|
||||||
client.internals.initiateParticle(particle, () => {});
|
},
|
||||||
|
);
|
||||||
// assert
|
|
||||||
await expect(promise).resolves.toBe('success');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// act
|
||||||
|
client.internals.initiateParticle(particle, () => {});
|
||||||
|
|
||||||
|
// assert
|
||||||
|
await expect(promise).resolves.toBe("success");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,25 +13,47 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import { FluencePeer, PeerConfig } from '../jsPeer/FluencePeer.js';
|
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
import { KeyPair } from '../keypair/index.js';
|
|
||||||
import { WasmLoaderFromNpm } from '../marine/deps-loader/node.js';
|
import { FluencePeer, PeerConfig } from "../jsPeer/FluencePeer.js";
|
||||||
import { WorkerLoader } from '../marine/worker-script/workerLoader.js';
|
import { JsServiceHost } from "../jsServiceHost/JsServiceHost.js";
|
||||||
import { MarineBackgroundRunner } from '../marine/worker/index.js';
|
import { KeyPair } from "../keypair/index.js";
|
||||||
import { EphemeralNetwork } from './network.js';
|
import { WasmLoaderFromNpm } from "../marine/deps-loader/node.js";
|
||||||
import { JsServiceHost } from '../jsServiceHost/JsServiceHost.js';
|
import { MarineBackgroundRunner } from "../marine/worker/index.js";
|
||||||
|
import { WorkerLoader } from "../marine/worker-script/workerLoader.js";
|
||||||
|
|
||||||
|
import { EphemeralNetwork } from "./network.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ephemeral network client is a FluencePeer that connects to a relay peer in an ephemeral network.
|
* Ephemeral network client is a FluencePeer that connects to a relay peer in an ephemeral network.
|
||||||
*/
|
*/
|
||||||
export class EphemeralNetworkClient extends FluencePeer {
|
export class EphemeralNetworkClient extends FluencePeer {
|
||||||
constructor(config: PeerConfig, keyPair: KeyPair, network: EphemeralNetwork, relay: PeerIdB58) {
|
constructor(
|
||||||
const workerLoader = new WorkerLoader();
|
config: PeerConfig,
|
||||||
const controlModuleLoader = new WasmLoaderFromNpm('@fluencelabs/marine-js', 'marine-js.wasm');
|
keyPair: KeyPair,
|
||||||
const avmModuleLoader = new WasmLoaderFromNpm('@fluencelabs/avm', 'avm.wasm');
|
network: EphemeralNetwork,
|
||||||
const marine = new MarineBackgroundRunner(workerLoader, controlModuleLoader, avmModuleLoader);
|
relay: PeerIdB58,
|
||||||
const conn = network.getRelayConnection(keyPair.getPeerId(), relay);
|
) {
|
||||||
super(config, keyPair, marine, new JsServiceHost(), conn);
|
const workerLoader = new WorkerLoader();
|
||||||
}
|
|
||||||
|
const controlModuleLoader = new WasmLoaderFromNpm(
|
||||||
|
"@fluencelabs/marine-js",
|
||||||
|
"marine-js.wasm",
|
||||||
|
);
|
||||||
|
|
||||||
|
const avmModuleLoader = new WasmLoaderFromNpm(
|
||||||
|
"@fluencelabs/avm",
|
||||||
|
"avm.wasm",
|
||||||
|
);
|
||||||
|
|
||||||
|
const marine = new MarineBackgroundRunner(
|
||||||
|
workerLoader,
|
||||||
|
controlModuleLoader,
|
||||||
|
avmModuleLoader,
|
||||||
|
);
|
||||||
|
|
||||||
|
const conn = network.getRelayConnection(keyPair.getPeerId(), relay);
|
||||||
|
super(config, keyPair, marine, new JsServiceHost(), conn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,200 +13,208 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import { fromBase64Sk, KeyPair } from '../keypair/index.js';
|
|
||||||
import { MarineBackgroundRunner } from '../marine/worker/index.js';
|
|
||||||
|
|
||||||
import { WorkerLoaderFromFs } from '../marine/deps-loader/node.js';
|
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
|
import { Subject } from "rxjs";
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
import { IConnection } from "../connection/interfaces.js";
|
||||||
import { Subject } from 'rxjs';
|
import { DEFAULT_CONFIG, FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
import { Particle } from '../particle/Particle.js';
|
import { JsServiceHost } from "../jsServiceHost/JsServiceHost.js";
|
||||||
|
import { fromBase64Sk, KeyPair } from "../keypair/index.js";
|
||||||
|
import {
|
||||||
|
WorkerLoaderFromFs,
|
||||||
|
WasmLoaderFromNpm,
|
||||||
|
} from "../marine/deps-loader/node.js";
|
||||||
|
import { IMarineHost } from "../marine/interfaces.js";
|
||||||
|
import { MarineBackgroundRunner } from "../marine/worker/index.js";
|
||||||
|
import { Particle } from "../particle/Particle.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
import { WasmLoaderFromNpm } from '../marine/deps-loader/node.js';
|
const log = logger("ephemeral");
|
||||||
import { DEFAULT_CONFIG, FluencePeer } from '../jsPeer/FluencePeer.js';
|
|
||||||
import { IConnection } from '../connection/interfaces.js';
|
|
||||||
import { IAvmRunner, IMarineHost } from '../marine/interfaces.js';
|
|
||||||
import { JsServiceHost } from '../jsServiceHost/JsServiceHost.js';
|
|
||||||
|
|
||||||
const log = logger('ephemeral');
|
|
||||||
|
|
||||||
interface EphemeralConfig {
|
interface EphemeralConfig {
|
||||||
peers: Array<{
|
peers: Array<{
|
||||||
peerId: PeerIdB58;
|
peerId: PeerIdB58;
|
||||||
sk: string;
|
sk: string;
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultConfig = {
|
export const defaultConfig = {
|
||||||
peers: [
|
peers: [
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWJankP2PcEDYCZDdJ26JsU8BMRfdGWyGqbtFiWyoKVtmx',
|
peerId: "12D3KooWJankP2PcEDYCZDdJ26JsU8BMRfdGWyGqbtFiWyoKVtmx",
|
||||||
sk: 'dWNAHhDVuFj9bEieILMu6TcCFRxBJdOPIvAWmf4sZQI=',
|
sk: "dWNAHhDVuFj9bEieILMu6TcCFRxBJdOPIvAWmf4sZQI=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWSBTB5sYxdwayUyTnqopBwABsnGFY3p4dTx5hABYDtJjV',
|
peerId: "12D3KooWSBTB5sYxdwayUyTnqopBwABsnGFY3p4dTx5hABYDtJjV",
|
||||||
sk: 'dOmaxAeu4Th+MJ22vRDLMFTNbiDgKNXar9fW9ofAMgQ=',
|
sk: "dOmaxAeu4Th+MJ22vRDLMFTNbiDgKNXar9fW9ofAMgQ=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWQjwf781DJ41moW5RrZXypLdnTbo6aMsoA8QLctGGX8RB',
|
peerId: "12D3KooWQjwf781DJ41moW5RrZXypLdnTbo6aMsoA8QLctGGX8RB",
|
||||||
sk: 'TgzaLlxXuOMDNuuuTKEHUKsW0jM4AmX0gahFvkB1KgE=',
|
sk: "TgzaLlxXuOMDNuuuTKEHUKsW0jM4AmX0gahFvkB1KgE=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWCXWTLFyY1mqKnNAhLQTsjW1zqDzCMbUs8M4a8zdz28HK',
|
peerId: "12D3KooWCXWTLFyY1mqKnNAhLQTsjW1zqDzCMbUs8M4a8zdz28HK",
|
||||||
sk: 'hiO2Ta8g2ibMQ7iu5yj9CfN+qQCwE8oRShjr7ortKww=',
|
sk: "hiO2Ta8g2ibMQ7iu5yj9CfN+qQCwE8oRShjr7ortKww=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWPmZpf4ng6GMS39HLagxsXbjiTPLH5CFJpFAHyN6amw6V',
|
peerId: "12D3KooWPmZpf4ng6GMS39HLagxsXbjiTPLH5CFJpFAHyN6amw6V",
|
||||||
sk: 'LzJtOHTqxfrlHDW40BKiLfjai8JU4yW6/s2zrXLCcQE=',
|
sk: "LzJtOHTqxfrlHDW40BKiLfjai8JU4yW6/s2zrXLCcQE=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWKrx8PZxM1R9A8tp2jmrFf6c6q1ZQiWfD4QkNgh7fWSoF',
|
peerId: "12D3KooWKrx8PZxM1R9A8tp2jmrFf6c6q1ZQiWfD4QkNgh7fWSoF",
|
||||||
sk: 'XMhlk/xr1FPcp7sKQhS18doXlq1x16EMhBC2NGW2LQ4=',
|
sk: "XMhlk/xr1FPcp7sKQhS18doXlq1x16EMhBC2NGW2LQ4=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWCbJHvnzSZEXjR1UJmtSUozuJK13iRiCYHLN1gjvm4TZZ',
|
peerId: "12D3KooWCbJHvnzSZEXjR1UJmtSUozuJK13iRiCYHLN1gjvm4TZZ",
|
||||||
sk: 'KXPAIqxrSHr7v0ngv3qagcqivFvnQ0xd3s1/rKmi8QU=',
|
sk: "KXPAIqxrSHr7v0ngv3qagcqivFvnQ0xd3s1/rKmi8QU=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWEvKe7WQHp42W4xhHRgTAWQjtDWyH38uJbLHAsMuTtYvD',
|
peerId: "12D3KooWEvKe7WQHp42W4xhHRgTAWQjtDWyH38uJbLHAsMuTtYvD",
|
||||||
sk: 'GCYMAshGnsrNtrHhuT7ayzh5uCzX99J03PmAXoOcCgw=',
|
sk: "GCYMAshGnsrNtrHhuT7ayzh5uCzX99J03PmAXoOcCgw=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWSznSHN3BGrSykBXkLkFsqo9SYB73wVauVdqeuRt562cC',
|
peerId: "12D3KooWSznSHN3BGrSykBXkLkFsqo9SYB73wVauVdqeuRt562cC",
|
||||||
sk: 'UP+SEuznS0h259VbFquzyOJAQ4W5iIwhP+hd1PmUQQ0=',
|
sk: "UP+SEuznS0h259VbFquzyOJAQ4W5iIwhP+hd1PmUQQ0=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWF57jwbShfnT3c4dNfRDdGjr6SQ3B71m87UVpEpSWHFwi',
|
peerId: "12D3KooWF57jwbShfnT3c4dNfRDdGjr6SQ3B71m87UVpEpSWHFwi",
|
||||||
sk: '8dl+Crm5RSh0eh+LqLKwX8/Eo4QLpvIjfD8L0wzX4A4=',
|
sk: "8dl+Crm5RSh0eh+LqLKwX8/Eo4QLpvIjfD8L0wzX4A4=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWBWrzpSg9nwMLBCa2cJubUjTv63Mfy6PYg9rHGbetaV5C',
|
peerId: "12D3KooWBWrzpSg9nwMLBCa2cJubUjTv63Mfy6PYg9rHGbetaV5C",
|
||||||
sk: 'qolc1FcpJ+vHDon0HeXdUYnstjV1wiVx2p0mjblrfAg=',
|
sk: "qolc1FcpJ+vHDon0HeXdUYnstjV1wiVx2p0mjblrfAg=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWNkLVU6juM8oyN2SVq5nBd2kp7Rf4uzJH1hET6vj6G5j6',
|
peerId: "12D3KooWNkLVU6juM8oyN2SVq5nBd2kp7Rf4uzJH1hET6vj6G5j6",
|
||||||
sk: 'vN6QzWILTM7hSHp+iGkKxiXcqs8bzlnH3FPaRaDGSQY=',
|
sk: "vN6QzWILTM7hSHp+iGkKxiXcqs8bzlnH3FPaRaDGSQY=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWKo1YwGL5vivPiKJMJS7wjtB6B2nJNdSXPkSABT4NKBUU',
|
peerId: "12D3KooWKo1YwGL5vivPiKJMJS7wjtB6B2nJNdSXPkSABT4NKBUU",
|
||||||
sk: 'YbDQ++bsor2kei7rYAsu2SbyoiOYPRzFRZWnNRUpBgQ=',
|
sk: "YbDQ++bsor2kei7rYAsu2SbyoiOYPRzFRZWnNRUpBgQ=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWLUyBKmmNCyxaPkXoWcUFPcy5qrZsUo2E1tyM6CJmGJvC',
|
peerId: "12D3KooWLUyBKmmNCyxaPkXoWcUFPcy5qrZsUo2E1tyM6CJmGJvC",
|
||||||
sk: 'ptB9eSFMKudAtHaFgDrRK/1oIMrhBujxbMw2Pzwx/wA=',
|
sk: "ptB9eSFMKudAtHaFgDrRK/1oIMrhBujxbMw2Pzwx/wA=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWAEZXME4KMu9FvLezsJWDbYFe2zyujyMnDT1AgcAxgcCk',
|
peerId: "12D3KooWAEZXME4KMu9FvLezsJWDbYFe2zyujyMnDT1AgcAxgcCk",
|
||||||
sk: 'xtwTOKgAbDIgkuPf7RKiR7gYyZ1HY4mOgFMv3sOUcAQ=',
|
sk: "xtwTOKgAbDIgkuPf7RKiR7gYyZ1HY4mOgFMv3sOUcAQ=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWEhXetsFVAD9h2dRz9XgFpfidho1TCZVhFrczX8h8qgzY',
|
peerId: "12D3KooWEhXetsFVAD9h2dRz9XgFpfidho1TCZVhFrczX8h8qgzY",
|
||||||
sk: '1I2MGuiKG1F4FDMiRihVOcOP2mxzOLWJ99MeexK27A4=',
|
sk: "1I2MGuiKG1F4FDMiRihVOcOP2mxzOLWJ99MeexK27A4=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWDBfVNdMyV3hPEF4WLBmx9DwD2t2SYuqZ2mztYmDzZWM1',
|
peerId: "12D3KooWDBfVNdMyV3hPEF4WLBmx9DwD2t2SYuqZ2mztYmDzZWM1",
|
||||||
sk: 'eqJ4Bp7iN4aBXgPH0ezwSg+nVsatkYtfrXv9obI0YQ0=',
|
sk: "eqJ4Bp7iN4aBXgPH0ezwSg+nVsatkYtfrXv9obI0YQ0=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWSyY7wiSiR4vbXa1WtZawi3ackMTqcQhEPrvqtagoWPny',
|
peerId: "12D3KooWSyY7wiSiR4vbXa1WtZawi3ackMTqcQhEPrvqtagoWPny",
|
||||||
sk: 'UVM3SBJhPYIY/gafpnd9/q/Fn9V4BE9zkgrvF1T7Pgc=',
|
sk: "UVM3SBJhPYIY/gafpnd9/q/Fn9V4BE9zkgrvF1T7Pgc=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWFZmBMGG9PxTs9s6ASzkLGKJWMyPheA5ruaYc2FDkDTmv',
|
peerId: "12D3KooWFZmBMGG9PxTs9s6ASzkLGKJWMyPheA5ruaYc2FDkDTmv",
|
||||||
sk: '8RbZfEVpQhPVuhv64uqxENDuSoyJrslQoSQJznxsTQ0=',
|
sk: "8RbZfEVpQhPVuhv64uqxENDuSoyJrslQoSQJznxsTQ0=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWBbhUaqqur6KHPunnKxXjY1daCtqJdy4wRji89LmAkVB4',
|
peerId: "12D3KooWBbhUaqqur6KHPunnKxXjY1daCtqJdy4wRji89LmAkVB4",
|
||||||
sk: 'RbgKmG6soWW9uOi7yRedm+0Qck3f3rw6MSnDP7AcBQs=',
|
sk: "RbgKmG6soWW9uOi7yRedm+0Qck3f3rw6MSnDP7AcBQs=",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IEphemeralConnection extends IConnection {
|
export interface IEphemeralConnection extends IConnection {
|
||||||
readonly selfPeerId: PeerIdB58;
|
readonly selfPeerId: PeerIdB58;
|
||||||
readonly connections: Map<PeerIdB58, IEphemeralConnection>;
|
readonly connections: Map<PeerIdB58, IEphemeralConnection>;
|
||||||
receiveParticle(particle: Particle): void;
|
receiveParticle(particle: Particle): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EphemeralConnection implements IEphemeralConnection {
|
export class EphemeralConnection implements IEphemeralConnection {
|
||||||
readonly selfPeerId: PeerIdB58;
|
readonly selfPeerId: PeerIdB58;
|
||||||
readonly connections: Map<PeerIdB58, IEphemeralConnection> = new Map();
|
readonly connections: Map<PeerIdB58, IEphemeralConnection> = new Map();
|
||||||
|
|
||||||
constructor(selfPeerId: PeerIdB58) {
|
constructor(selfPeerId: PeerIdB58) {
|
||||||
this.selfPeerId = selfPeerId;
|
this.selfPeerId = selfPeerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
start(): Promise<void> {
|
start(): Promise<void> {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(): Promise<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectToOther(other: IEphemeralConnection) {
|
||||||
|
if (other.selfPeerId === this.selfPeerId) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): Promise<void> {
|
this.connections.set(other.selfPeerId, other);
|
||||||
return Promise.resolve();
|
other.connections.set(this.selfPeerId, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectFromOther(other: IEphemeralConnection) {
|
||||||
|
this.connections.delete(other.selfPeerId);
|
||||||
|
other.connections.delete(this.selfPeerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectFromAll() {
|
||||||
|
for (const other of this.connections.values()) {
|
||||||
|
this.disconnectFromOther(other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
particleSource = new Subject<Particle>();
|
||||||
|
|
||||||
|
receiveParticle(particle: Particle): void {
|
||||||
|
this.particleSource.next(particle);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendParticle(nextPeerIds: string[], particle: Particle): Promise<void> {
|
||||||
|
const from = this.selfPeerId;
|
||||||
|
|
||||||
|
for (const to of nextPeerIds) {
|
||||||
|
const destConnection = this.connections.get(to);
|
||||||
|
|
||||||
|
if (destConnection === undefined) {
|
||||||
|
log.error("peer %s has no connection with %s", from, to);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// log.trace(`Sending particle from %s, to %j, particleId %s`, from, to, particle.id);
|
||||||
|
destConnection.receiveParticle(particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
connectToOther(other: IEphemeralConnection) {
|
return Promise.resolve();
|
||||||
if (other.selfPeerId === this.selfPeerId) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.connections.set(other.selfPeerId, other);
|
getRelayPeerId(): string {
|
||||||
other.connections.set(this.selfPeerId, this);
|
const firstMapKey = this.connections.keys().next();
|
||||||
|
|
||||||
|
// Empty map
|
||||||
|
if (firstMapKey.done === true) {
|
||||||
|
throw new Error("relay is not supported in this Ephemeral network peer");
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectFromOther(other: IEphemeralConnection) {
|
return firstMapKey.value;
|
||||||
this.connections.delete(other.selfPeerId);
|
}
|
||||||
other.connections.delete(this.selfPeerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectFromAll() {
|
supportsRelay(): boolean {
|
||||||
for (let other of this.connections.values()) {
|
return this.connections.size === 1;
|
||||||
this.disconnectFromOther(other);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
particleSource = new Subject<Particle>();
|
|
||||||
|
|
||||||
receiveParticle(particle: Particle): void {
|
|
||||||
this.particleSource.next(Particle.fromString(particle.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendParticle(nextPeerIds: string[], particle: Particle): Promise<void> {
|
|
||||||
const from = this.selfPeerId;
|
|
||||||
for (let to of nextPeerIds) {
|
|
||||||
const destConnection = this.connections.get(to);
|
|
||||||
if (destConnection === undefined) {
|
|
||||||
log.error('peer %s has no connection with %s', from, to);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// log.trace(`Sending particle from %s, to %j, particleId %s`, from, to, particle.id);
|
|
||||||
destConnection.receiveParticle(particle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getRelayPeerId(): string {
|
|
||||||
if (this.connections.size === 1) {
|
|
||||||
return this.connections.keys().next().value;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('relay is not supported in this Ephemeral network peer');
|
|
||||||
}
|
|
||||||
|
|
||||||
supportsRelay(): boolean {
|
|
||||||
return this.connections.size === 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EphemeralPeer extends FluencePeer {
|
class EphemeralPeer extends FluencePeer {
|
||||||
ephemeralConnection: EphemeralConnection;
|
ephemeralConnection: EphemeralConnection;
|
||||||
|
|
||||||
constructor(keyPair: KeyPair, marine: IMarineHost) {
|
constructor(keyPair: KeyPair, marine: IMarineHost) {
|
||||||
const conn = new EphemeralConnection(keyPair.getPeerId());
|
const conn = new EphemeralConnection(keyPair.getPeerId());
|
||||||
super(DEFAULT_CONFIG, keyPair, marine, new JsServiceHost(), conn);
|
super(DEFAULT_CONFIG, keyPair, marine, new JsServiceHost(), conn);
|
||||||
|
|
||||||
this.ephemeralConnection = conn;
|
this.ephemeralConnection = conn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,83 +222,107 @@ class EphemeralPeer extends FluencePeer {
|
|||||||
* Ephemeral network is a virtual network which runs locally and focuses on p2p interaction by removing connectivity layer out of the equation.
|
* Ephemeral network is a virtual network which runs locally and focuses on p2p interaction by removing connectivity layer out of the equation.
|
||||||
*/
|
*/
|
||||||
export class EphemeralNetwork {
|
export class EphemeralNetwork {
|
||||||
private peers: Map<PeerIdB58, EphemeralPeer> = new Map();
|
private peers: Map<PeerIdB58, EphemeralPeer> = new Map();
|
||||||
|
|
||||||
workerLoader: WorkerLoaderFromFs;
|
workerLoader: WorkerLoaderFromFs;
|
||||||
controlModuleLoader: WasmLoaderFromNpm;
|
controlModuleLoader: WasmLoaderFromNpm;
|
||||||
avmModuleLoader: WasmLoaderFromNpm;
|
avmModuleLoader: WasmLoaderFromNpm;
|
||||||
|
|
||||||
constructor(public readonly config: EphemeralConfig) {
|
constructor(readonly config: EphemeralConfig) {
|
||||||
// shared worker for all the peers
|
// shared worker for all the peers
|
||||||
this.workerLoader = new WorkerLoaderFromFs('../../marine/worker-script');
|
this.workerLoader = new WorkerLoaderFromFs("../../marine/worker-script");
|
||||||
this.controlModuleLoader = new WasmLoaderFromNpm('@fluencelabs/marine-js', 'marine-js.wasm');
|
|
||||||
this.avmModuleLoader = new WasmLoaderFromNpm('@fluencelabs/avm', 'avm.wasm');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
this.controlModuleLoader = new WasmLoaderFromNpm(
|
||||||
* Starts the Ephemeral network up
|
"@fluencelabs/marine-js",
|
||||||
*/
|
"marine-js.wasm",
|
||||||
async up(): Promise<void> {
|
);
|
||||||
log.trace('starting ephemeral network up...');
|
|
||||||
|
|
||||||
const promises = this.config.peers.map(async (x) => {
|
this.avmModuleLoader = new WasmLoaderFromNpm(
|
||||||
const kp = await fromBase64Sk(x.sk);
|
"@fluencelabs/avm",
|
||||||
const marine = new MarineBackgroundRunner(this.workerLoader, this.controlModuleLoader, this.avmModuleLoader);
|
"avm.wasm",
|
||||||
const peerId = kp.getPeerId();
|
);
|
||||||
if (peerId !== x.peerId) {
|
}
|
||||||
throw new Error(`Invalid config: peer id ${x.peerId} does not match the secret key ${x.sk}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new EphemeralPeer(kp, marine);
|
/**
|
||||||
});
|
* Starts the Ephemeral network up
|
||||||
|
*/
|
||||||
|
async up(): Promise<void> {
|
||||||
|
log.trace("starting ephemeral network up...");
|
||||||
|
|
||||||
const peers = await Promise.all(promises);
|
const promises = this.config.peers.map(async (x) => {
|
||||||
|
const kp = await fromBase64Sk(x.sk);
|
||||||
|
|
||||||
for (let i = 0; i < peers.length; i++) {
|
const marine = new MarineBackgroundRunner(
|
||||||
for (let j = 0; j < i; j++) {
|
this.workerLoader,
|
||||||
if (i === j) {
|
this.controlModuleLoader,
|
||||||
continue;
|
this.avmModuleLoader,
|
||||||
}
|
);
|
||||||
|
|
||||||
peers[i].ephemeralConnection.connectToOther(peers[j].ephemeralConnection);
|
const peerId = kp.getPeerId();
|
||||||
}
|
|
||||||
|
if (peerId !== x.peerId) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid config: peer id ${x.peerId} does not match the secret key ${x.sk}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EphemeralPeer(kp, marine);
|
||||||
|
});
|
||||||
|
|
||||||
|
const peers = await Promise.all(promises);
|
||||||
|
|
||||||
|
for (let i = 0; i < peers.length; i++) {
|
||||||
|
for (let j = 0; j < i; j++) {
|
||||||
|
if (i === j) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const startPromises = peers.map((x) => x.start());
|
peers[i].ephemeralConnection.connectToOther(
|
||||||
await Promise.all(startPromises);
|
peers[j].ephemeralConnection,
|
||||||
|
);
|
||||||
for (let p of peers) {
|
}
|
||||||
this.peers.set(p.keyPair.getPeerId(), p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const startPromises = peers.map((x) => {
|
||||||
* Shuts the ephemeral network down. Will disconnect all connected peers.
|
return x.start();
|
||||||
*/
|
});
|
||||||
async down(): Promise<void> {
|
|
||||||
log.trace('shutting down ephemeral network...');
|
|
||||||
const peers = Array.from(this.peers.entries());
|
|
||||||
const promises = peers.map(async ([k, p]) => {
|
|
||||||
await p.ephemeralConnection.disconnectFromAll();
|
|
||||||
await p.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(startPromises);
|
||||||
this.peers.clear();
|
|
||||||
log.trace('ephemeral network shut down');
|
for (const p of peers) {
|
||||||
|
this.peers.set(p.keyPair.getPeerId(), p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuts the ephemeral network down. Will disconnect all connected peers.
|
||||||
|
*/
|
||||||
|
async down(): Promise<void> {
|
||||||
|
log.trace("shutting down ephemeral network...");
|
||||||
|
const peers = Array.from(this.peers.entries());
|
||||||
|
|
||||||
|
const promises = peers.map(async ([, p]) => {
|
||||||
|
p.ephemeralConnection.disconnectFromAll();
|
||||||
|
await p.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
this.peers.clear();
|
||||||
|
log.trace("ephemeral network shut down");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a relay connection to the specified peer.
|
||||||
|
*/
|
||||||
|
getRelayConnection(peerId: PeerIdB58, relayPeerId: PeerIdB58): IConnection {
|
||||||
|
const relay = this.peers.get(relayPeerId);
|
||||||
|
|
||||||
|
if (relay === undefined) {
|
||||||
|
throw new Error(`Peer ${relayPeerId} is not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const res = new EphemeralConnection(peerId);
|
||||||
* Gets a relay connection to the specified peer.
|
res.connectToOther(relay.ephemeralConnection);
|
||||||
*/
|
return res;
|
||||||
getRelayConnection(peerId: PeerIdB58, relayPeerId: PeerIdB58): IConnection {
|
}
|
||||||
const relay = this.peers.get(relayPeerId);
|
|
||||||
if (relay === undefined) {
|
|
||||||
throw new Error(`Peer ${relayPeerId} is not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = new EphemeralConnection(peerId);
|
|
||||||
res.connectToOther(relay.ephemeralConnection);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -15,26 +15,47 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
interface PackageJsonContent {
|
interface PackageJsonContent {
|
||||||
dependencies: Record<string, string | undefined>;
|
dependencies: Record<string, string | undefined>;
|
||||||
devDependencies: Record<string, string | undefined>;
|
devDependencies: Record<string, string | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be substituted in build phase
|
// This will be substituted in build phase
|
||||||
const packageJsonContentString = `__PACKAGE_JSON_CONTENT__`;
|
const packageJsonContentString = `__PACKAGE_JSON_CONTENT__`;
|
||||||
let parsedPackageJsonContent: PackageJsonContent;
|
let parsedPackageJsonContent: PackageJsonContent | undefined;
|
||||||
|
|
||||||
const PRIMARY_CDN = "https://unpkg.com/";
|
const PRIMARY_CDN = "https://unpkg.com/";
|
||||||
|
|
||||||
export async function fetchResource(pkg: string, assetPath: string) {
|
export async function fetchResource(pkg: string, assetPath: string) {
|
||||||
const packageJsonContent = parsedPackageJsonContent || (parsedPackageJsonContent = JSON.parse(packageJsonContentString));
|
const packageJsonContent =
|
||||||
const version = packageJsonContent.dependencies[pkg] || packageJsonContent.devDependencies[pkg];
|
parsedPackageJsonContent ??
|
||||||
|
// TODO: Should be validated
|
||||||
if (version === undefined) {
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
const availableDeps = [...Object.keys(packageJsonContent.dependencies), ...Object.keys(packageJsonContent.devDependencies)];
|
(parsedPackageJsonContent = JSON.parse(
|
||||||
throw new Error(`Cannot find version of ${pkg} in package.json. Available versions: ${availableDeps.join(',')}`);
|
packageJsonContentString,
|
||||||
}
|
) as PackageJsonContent);
|
||||||
|
|
||||||
const refinedAssetPath = assetPath.startsWith('/') ? assetPath.slice(1) : assetPath;
|
const version =
|
||||||
|
packageJsonContent.dependencies[pkg] ??
|
||||||
return fetch(new globalThis.URL(`${pkg}@${version}/` + refinedAssetPath, PRIMARY_CDN));
|
packageJsonContent.devDependencies[pkg];
|
||||||
|
|
||||||
|
if (version === undefined) {
|
||||||
|
const availableDeps = [
|
||||||
|
...Object.keys(packageJsonContent.dependencies),
|
||||||
|
...Object.keys(packageJsonContent.devDependencies),
|
||||||
|
];
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Cannot find version of ${pkg} in package.json. Available versions: ${availableDeps.join(
|
||||||
|
",",
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const refinedAssetPath = assetPath.startsWith("/")
|
||||||
|
? assetPath.slice(1)
|
||||||
|
: assetPath;
|
||||||
|
|
||||||
|
return fetch(
|
||||||
|
new globalThis.URL(`${pkg}@${version}/` + refinedAssetPath, PRIMARY_CDN),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,17 +14,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fetchResource as fetchResourceBrowser } from './browser.js';
|
import process from "process";
|
||||||
import { fetchResource as fetchResourceNode } from './node.js';
|
|
||||||
import process from 'process';
|
|
||||||
|
|
||||||
const isNode = typeof process !== 'undefined' && process?.release?.name === 'node';
|
import { fetchResource as fetchResourceIsomorphic } from "#fetcher";
|
||||||
|
|
||||||
|
const isNode =
|
||||||
|
// process.release is undefined in browser env
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
typeof process !== "undefined" && process.release?.name === "node";
|
||||||
|
|
||||||
export async function fetchResource(pkg: string, path: string) {
|
export async function fetchResource(pkg: string, path: string) {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case isNode:
|
case isNode:
|
||||||
return fetchResourceNode(pkg, path);
|
return fetchResourceIsomorphic(pkg, path);
|
||||||
default:
|
default:
|
||||||
return fetchResourceBrowser(pkg, path);
|
return fetchResourceIsomorphic(pkg, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,46 +14,46 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import path from 'path';
|
import module from "module";
|
||||||
import module from 'module';
|
import path from "path";
|
||||||
|
|
||||||
export async function fetchResource(pkg: string, assetPath: string) {
|
export async function fetchResource(pkg: string, assetPath: string) {
|
||||||
const require = module.createRequire(import.meta.url);
|
const require = module.createRequire(import.meta.url);
|
||||||
const packagePathIndex = require.resolve(pkg);
|
const packagePathIndex = require.resolve(pkg);
|
||||||
|
|
||||||
// Ensure that windows path is converted to posix path. So we can find a package
|
// Ensure that windows path is converted to posix path. So we can find a package
|
||||||
const posixPath = packagePathIndex.split(path.sep).join(path.posix.sep);
|
const posixPath = packagePathIndex.split(path.sep).join(path.posix.sep);
|
||||||
|
|
||||||
const matches = new RegExp(`(.+${pkg})`).exec(posixPath);
|
const matches = new RegExp(`(.+${pkg})`).exec(posixPath);
|
||||||
|
|
||||||
const packagePath = matches?.[0];
|
const packagePath = matches?.[0];
|
||||||
|
|
||||||
if (!packagePath) {
|
if (packagePath == null) {
|
||||||
throw new Error(`Cannot find dependency ${pkg} in path ${posixPath}`);
|
throw new Error(`Cannot find dependency ${pkg} in path ${posixPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathToResource = path.join(packagePath, assetPath);
|
const pathToResource = path.join(packagePath, assetPath);
|
||||||
|
|
||||||
const file = await new Promise<ArrayBuffer>((resolve, reject) => {
|
const file = await new Promise<ArrayBuffer>((resolve, reject) => {
|
||||||
// Cannot use 'fs/promises' with current vite config. This module is not polyfilled by default.
|
// Cannot use 'fs/promises' with current vite config. This module is not polyfilled by default.
|
||||||
fs.readFile(pathToResource, (err, data) => {
|
fs.readFile(pathToResource, (err, data) => {
|
||||||
if (err) {
|
if (err != null) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve(data);
|
|
||||||
});
|
resolve(data);
|
||||||
});
|
|
||||||
|
|
||||||
return new Response(file, {
|
|
||||||
headers: {
|
|
||||||
'Content-type':
|
|
||||||
assetPath.endsWith('.wasm')
|
|
||||||
? 'application/wasm'
|
|
||||||
: assetPath.endsWith('.js')
|
|
||||||
? 'application/javascript'
|
|
||||||
: 'application/text'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(file, {
|
||||||
|
headers: {
|
||||||
|
"Content-type": assetPath.endsWith(".wasm")
|
||||||
|
? "application/wasm"
|
||||||
|
: assetPath.endsWith(".js")
|
||||||
|
? "application/javascript"
|
||||||
|
: "application/text",
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,157 +13,235 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { ClientConfig, IFluenceClient, RelayOptions, ConnectionState, CallAquaFunctionType, RegisterServiceType } from '@fluencelabs/interfaces';
|
|
||||||
import { ClientPeer, makeClientPeerConfig } from './clientPeer/ClientPeer.js';
|
|
||||||
import { callAquaFunction } from './compilerSupport/callFunction.js';
|
|
||||||
import { registerService } from './compilerSupport/registerService.js';
|
|
||||||
import { MarineBackgroundRunner } from './marine/worker/index.js';
|
|
||||||
// @ts-ignore
|
|
||||||
import { BlobWorker, Worker } from 'threads';
|
|
||||||
import { doRegisterNodeUtils } from './services/NodeUtils.js';
|
|
||||||
import { fetchResource } from './fetchers/index.js';
|
|
||||||
import process from 'process';
|
|
||||||
import path from 'path';
|
|
||||||
import url from 'url';
|
|
||||||
import module from 'module';
|
|
||||||
|
|
||||||
const isNode = typeof process !== 'undefined' && process?.release?.name === 'node';
|
import module from "module";
|
||||||
|
import path from "path";
|
||||||
|
import process from "process";
|
||||||
|
import url from "url";
|
||||||
|
|
||||||
const fetchWorkerCode = () => fetchResource('@fluencelabs/marine-worker', '/dist/browser/marine-worker.umd.cjs').then(res => res.text());
|
import type {
|
||||||
const fetchMarineJsWasm = () => fetchResource('@fluencelabs/marine-js', '/dist/marine-js.wasm').then(res => res.arrayBuffer());
|
ClientConfig,
|
||||||
const fetchAvmWasm = () => fetchResource('@fluencelabs/avm', '/dist/avm.wasm').then(res => res.arrayBuffer());
|
ConnectionState,
|
||||||
|
RelayOptions,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
import { BlobWorker, Worker } from "threads/master";
|
||||||
|
|
||||||
const createClient = async (relay: RelayOptions, config: ClientConfig): Promise<IFluenceClient> => {
|
import { ClientPeer, makeClientPeerConfig } from "./clientPeer/ClientPeer.js";
|
||||||
const marineJsWasm = await fetchMarineJsWasm();
|
import { callAquaFunction } from "./compilerSupport/callFunction.js";
|
||||||
const avmWasm = await fetchAvmWasm();
|
import { registerService } from "./compilerSupport/registerService.js";
|
||||||
|
import { fetchResource } from "./fetchers/index.js";
|
||||||
const marine = new MarineBackgroundRunner({
|
import { MarineBackgroundRunner } from "./marine/worker/index.js";
|
||||||
async getValue() {
|
import { doRegisterNodeUtils } from "./services/NodeUtils.js";
|
||||||
if (isNode) {
|
|
||||||
const require = module.createRequire(import.meta.url);
|
const isNode =
|
||||||
const pathToThisFile = path.dirname(url.fileURLToPath(import.meta.url));
|
// process.release is undefined in browser env
|
||||||
const pathToWorker = require.resolve('@fluencelabs/marine-worker');
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
const relativePathToWorker = path.relative(pathToThisFile, pathToWorker);
|
typeof process !== "undefined" && process.release?.name === "node";
|
||||||
return new Worker(relativePathToWorker);
|
|
||||||
} else {
|
const fetchWorkerCode = async () => {
|
||||||
const workerCode = await fetchWorkerCode();
|
const resource = await fetchResource(
|
||||||
return BlobWorker.fromText(workerCode)
|
"@fluencelabs/marine-worker",
|
||||||
}
|
"/dist/browser/marine-worker.umd.cjs",
|
||||||
},
|
);
|
||||||
start() {
|
|
||||||
return Promise.resolve(undefined);
|
return resource.text();
|
||||||
},
|
};
|
||||||
stop() {
|
|
||||||
return Promise.resolve(undefined);
|
const fetchMarineJsWasm = async () => {
|
||||||
},
|
const resource = await fetchResource(
|
||||||
}, {
|
"@fluencelabs/marine-js",
|
||||||
getValue() {
|
"/dist/marine-js.wasm",
|
||||||
return marineJsWasm;
|
);
|
||||||
}, start(): Promise<void> {
|
|
||||||
return Promise.resolve(undefined);
|
return resource.arrayBuffer();
|
||||||
}, stop(): Promise<void> {
|
};
|
||||||
return Promise.resolve(undefined);
|
|
||||||
|
const fetchAvmWasm = async () => {
|
||||||
|
const resource = await fetchResource("@fluencelabs/avm", "/dist/avm.wasm");
|
||||||
|
return resource.arrayBuffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
const createClient = async (
|
||||||
|
relay: RelayOptions,
|
||||||
|
config: ClientConfig,
|
||||||
|
): Promise<ClientPeer> => {
|
||||||
|
const marineJsWasm = await fetchMarineJsWasm();
|
||||||
|
const avmWasm = await fetchAvmWasm();
|
||||||
|
|
||||||
|
const marine = new MarineBackgroundRunner(
|
||||||
|
{
|
||||||
|
async getValue() {
|
||||||
|
if (isNode) {
|
||||||
|
const require = module.createRequire(import.meta.url);
|
||||||
|
|
||||||
|
const pathToThisFile = path.dirname(
|
||||||
|
url.fileURLToPath(import.meta.url),
|
||||||
|
);
|
||||||
|
|
||||||
|
const pathToWorker = require.resolve("@fluencelabs/marine-worker");
|
||||||
|
|
||||||
|
const relativePathToWorker = path.relative(
|
||||||
|
pathToThisFile,
|
||||||
|
pathToWorker,
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Worker(relativePathToWorker);
|
||||||
|
} else {
|
||||||
|
const workerCode = await fetchWorkerCode();
|
||||||
|
return BlobWorker.fromText(workerCode);
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
getValue() {
|
start() {
|
||||||
return avmWasm;
|
return Promise.resolve(undefined);
|
||||||
}, start(): Promise<void> {
|
},
|
||||||
return Promise.resolve(undefined);
|
stop() {
|
||||||
}, stop(): Promise<void> {
|
return Promise.resolve(undefined);
|
||||||
return Promise.resolve(undefined);
|
},
|
||||||
}
|
},
|
||||||
});
|
{
|
||||||
const { keyPair, peerConfig, relayConfig } = await makeClientPeerConfig(relay, config);
|
getValue() {
|
||||||
const client: IFluenceClient = new ClientPeer(peerConfig, relayConfig, keyPair, marine);
|
return marineJsWasm;
|
||||||
if (isNode) {
|
},
|
||||||
doRegisterNodeUtils(client);
|
start(): Promise<void> {
|
||||||
}
|
return Promise.resolve(undefined);
|
||||||
await client.connect();
|
},
|
||||||
return client;
|
stop(): Promise<void> {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
getValue() {
|
||||||
|
return avmWasm;
|
||||||
|
},
|
||||||
|
start(): Promise<void> {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
},
|
||||||
|
stop(): Promise<void> {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const { keyPair, peerConfig, relayConfig } = await makeClientPeerConfig(
|
||||||
|
relay,
|
||||||
|
config,
|
||||||
|
);
|
||||||
|
|
||||||
|
const client = new ClientPeer(peerConfig, relayConfig, keyPair, marine);
|
||||||
|
|
||||||
|
if (isNode) {
|
||||||
|
doRegisterNodeUtils(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.connect();
|
||||||
|
return client;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public interface to Fluence Network
|
* Public interface to Fluence Network
|
||||||
*/
|
*/
|
||||||
export const Fluence = {
|
interface FluencePublicApi {
|
||||||
defaultClient: undefined as (IFluenceClient | undefined),
|
defaultClient: ClientPeer | undefined;
|
||||||
/**
|
connect: (relay: RelayOptions, config: ClientConfig) => Promise<void>;
|
||||||
* Connect to the Fluence network
|
disconnect: () => Promise<void>;
|
||||||
* @param relay - relay node to connect to
|
onConnectionStateChange: (
|
||||||
* @param config - client configuration
|
handler: (state: ConnectionState) => void,
|
||||||
*/
|
) => ConnectionState;
|
||||||
connect: async function(relay: RelayOptions, config: ClientConfig): Promise<void> {
|
getClient: () => ClientPeer;
|
||||||
const client = await createClient(relay, config);
|
}
|
||||||
this.defaultClient = client;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
export const Fluence: FluencePublicApi = {
|
||||||
* Disconnect from the Fluence network
|
defaultClient: undefined,
|
||||||
*/
|
/**
|
||||||
disconnect: async function(): Promise<void> {
|
* Connect to the Fluence network
|
||||||
await this.defaultClient?.disconnect();
|
* @param relay - relay node to connect to
|
||||||
this.defaultClient = undefined;
|
* @param config - client configuration
|
||||||
},
|
*/
|
||||||
|
connect: async function (relay, config) {
|
||||||
|
this.defaultClient = await createClient(relay, config);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle connection state changes. Immediately returns the current connection state
|
* Disconnect from the Fluence network
|
||||||
*/
|
*/
|
||||||
onConnectionStateChange(handler: (state: ConnectionState) => void): ConnectionState {
|
disconnect: async function (): Promise<void> {
|
||||||
return this.defaultClient?.onConnectionStateChange(handler) || 'disconnected';
|
await this.defaultClient?.disconnect();
|
||||||
},
|
this.defaultClient = undefined;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Low level API. Get the underlying client instance which holds the connection to the network
|
* Handle connection state changes. Immediately returns the current connection state
|
||||||
* @returns IFluenceClient instance
|
*/
|
||||||
*/
|
onConnectionStateChange(handler) {
|
||||||
getClient: async function(): Promise<IFluenceClient> {
|
return (
|
||||||
if (!this.defaultClient) {
|
this.defaultClient?.onConnectionStateChange(handler) ?? "disconnected"
|
||||||
throw new Error('Fluence client is not initialized. Call Fluence.connect() first');
|
);
|
||||||
}
|
},
|
||||||
return this.defaultClient;
|
|
||||||
},
|
/**
|
||||||
|
* Low level API. Get the underlying client instance which holds the connection to the network
|
||||||
|
* @returns IFluenceClient instance
|
||||||
|
*/
|
||||||
|
getClient: function () {
|
||||||
|
if (this.defaultClient == null) {
|
||||||
|
throw new Error(
|
||||||
|
"Fluence client is not initialized. Call Fluence.connect() first",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.defaultClient;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type { IFluenceClient, ClientConfig, CallParams } from '@fluencelabs/interfaces';
|
export type {
|
||||||
|
IFluenceClient,
|
||||||
|
ClientConfig,
|
||||||
|
CallParams,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
ArrayType,
|
ArrayType,
|
||||||
ArrowType,
|
ArrowType,
|
||||||
ArrowWithCallbacks,
|
ArrowWithCallbacks,
|
||||||
ArrowWithoutCallbacks,
|
ArrowWithoutCallbacks,
|
||||||
BottomType,
|
BottomType,
|
||||||
FunctionCallConstants,
|
FunctionCallConstants,
|
||||||
FunctionCallDef,
|
FunctionCallDef,
|
||||||
LabeledProductType,
|
LabeledProductType,
|
||||||
NilType,
|
NilType,
|
||||||
NonArrowType,
|
NonArrowType,
|
||||||
OptionType,
|
OptionType,
|
||||||
ProductType,
|
ProductType,
|
||||||
ScalarNames,
|
ScalarNames,
|
||||||
ScalarType,
|
ScalarType,
|
||||||
ServiceDef,
|
ServiceDef,
|
||||||
StructType,
|
StructType,
|
||||||
TopType,
|
TopType,
|
||||||
UnlabeledProductType,
|
UnlabeledProductType,
|
||||||
CallAquaFunctionType,
|
CallAquaFunctionType,
|
||||||
CallAquaFunctionArgs,
|
CallAquaFunctionArgs,
|
||||||
PassedArgs,
|
PassedArgs,
|
||||||
FnConfig,
|
FnConfig,
|
||||||
RegisterServiceType,
|
RegisterServiceType,
|
||||||
RegisterServiceArgs,
|
RegisterServiceArgs,
|
||||||
} from '@fluencelabs/interfaces';
|
} from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
export { v5_callFunction, v5_registerService } from './api.js';
|
export { v5_callFunction, v5_registerService } from "./api.js";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error Writing to global object like this prohibited by ts
|
||||||
globalThis.new_fluence = Fluence;
|
globalThis.new_fluence = Fluence;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error Writing to global object like this prohibited by ts
|
||||||
globalThis.fluence = {
|
globalThis.fluence = {
|
||||||
clientFactory: createClient,
|
clientFactory: createClient,
|
||||||
callAquaFunction,
|
callAquaFunction,
|
||||||
registerService,
|
registerService,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { createClient, callAquaFunction, registerService };
|
export { createClient, callAquaFunction, registerService };
|
||||||
export { getFluenceInterface, getFluenceInterfaceFromGlobalThis } from './util/loadClient.js';
|
export {
|
||||||
|
KeyPair,
|
||||||
|
fromBase64Sk,
|
||||||
|
fromBase58Sk,
|
||||||
|
fromOpts,
|
||||||
|
} from "./keypair/index.js";
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,39 +1,59 @@
|
|||||||
import { it, describe, expect } from 'vitest';
|
/**
|
||||||
import { registerHandlersHelper, withPeer } from '../../util/testUtils.js';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import { handleTimeout } from '../../particle/Particle.js';
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
describe('Basic AVM functionality in Fluence Peer tests', () => {
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
it('Simple call', async () => {
|
import { it, describe, expect } from "vitest";
|
||||||
await withPeer(async (peer) => {
|
|
||||||
const script = `
|
import { handleTimeout } from "../../particle/Particle.js";
|
||||||
|
import { registerHandlersHelper, withPeer } from "../../util/testUtils.js";
|
||||||
|
|
||||||
|
describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||||
|
it("Simple call", async () => {
|
||||||
|
await withPeer(async (peer) => {
|
||||||
|
const script = `
|
||||||
(call %init_peer_id% ("print" "print") ["1"])
|
(call %init_peer_id% ("print" "print") ["1"])
|
||||||
`;
|
`;
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
|
||||||
|
|
||||||
const res = await new Promise<string>((resolve, reject) => {
|
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
print: {
|
|
||||||
print: (args: Array<string>) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
const res = await new Promise<JSONValue>((resolve, reject) => {
|
||||||
});
|
if (particle instanceof Error) {
|
||||||
|
reject(particle.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
expect(res).toBe('1');
|
registerHandlersHelper(peer, particle, {
|
||||||
|
print: {
|
||||||
|
print: (args): undefined => {
|
||||||
|
const [res] = args;
|
||||||
|
resolve(res);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('Par call', async () => {
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
await withPeer(async (peer) => {
|
});
|
||||||
const script = `
|
|
||||||
|
expect(res).toBe("1");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Par call", async () => {
|
||||||
|
await withPeer(async (peer) => {
|
||||||
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
(par
|
(par
|
||||||
(call %init_peer_id% ("print" "print") ["1"])
|
(call %init_peer_id% ("print" "print") ["1"])
|
||||||
@ -42,36 +62,39 @@ describe('Basic AVM functionality in Fluence Peer tests', () => {
|
|||||||
(call %init_peer_id% ("print" "print") ["2"])
|
(call %init_peer_id% ("print" "print") ["2"])
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
|
||||||
|
|
||||||
const res = await new Promise<string[]>((resolve, reject) => {
|
|
||||||
const res: any[] = [];
|
|
||||||
|
|
||||||
if (particle instanceof Error) {
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const res = await new Promise<JSONValue[]>((resolve, reject) => {
|
||||||
print: {
|
const res: JSONValue[] = [];
|
||||||
print: (args: any) => {
|
|
||||||
res.push(args[0]);
|
|
||||||
if (res.length == 2) {
|
|
||||||
resolve(res);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
if (particle instanceof Error) {
|
||||||
});
|
reject(particle.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
expect(res).toStrictEqual(['1', '2']);
|
registerHandlersHelper(peer, particle, {
|
||||||
|
print: {
|
||||||
|
print: (args): undefined => {
|
||||||
|
res.push(args[0]);
|
||||||
|
|
||||||
|
if (res.length === 2) {
|
||||||
|
resolve(res);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('Timeout in par call: race', async () => {
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
await withPeer(async (peer) => {
|
});
|
||||||
const script = `
|
|
||||||
|
expect(res).toStrictEqual(["1", "2"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Timeout in par call: race", async () => {
|
||||||
|
await withPeer(async (peer) => {
|
||||||
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
(call %init_peer_id% ("op" "identity") ["slow_result"] arg)
|
(call %init_peer_id% ("op" "identity") ["slow_result"] arg)
|
||||||
(seq
|
(seq
|
||||||
@ -86,31 +109,33 @@ describe('Basic AVM functionality in Fluence Peer tests', () => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
|
||||||
|
|
||||||
const res = await new Promise((resolve, reject) => {
|
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
return: {
|
|
||||||
return: (args: any) => {
|
|
||||||
resolve(args[0]);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
const res = await new Promise((resolve, reject) => {
|
||||||
});
|
if (particle instanceof Error) {
|
||||||
|
reject(particle.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
expect(res).toBe('fast_result');
|
registerHandlersHelper(peer, particle, {
|
||||||
|
return: {
|
||||||
|
return: (args): undefined => {
|
||||||
|
resolve(args[0]);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('Timeout in par call: wait', async () => {
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
await withPeer(async (peer) => {
|
});
|
||||||
const script = `
|
|
||||||
|
expect(res).toBe("fast_result");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Timeout in par call: wait", async () => {
|
||||||
|
await withPeer(async (peer) => {
|
||||||
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
(call %init_peer_id% ("op" "identity") ["timeout_msg"] arg)
|
(call %init_peer_id% ("op" "identity") ["timeout_msg"] arg)
|
||||||
(seq
|
(seq
|
||||||
@ -136,25 +161,27 @@ describe('Basic AVM functionality in Fluence Peer tests', () => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
|
||||||
|
|
||||||
const res = await new Promise((resolve, reject) => {
|
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
return: {
|
|
||||||
return: (args: any) => {
|
|
||||||
resolve(args[0]);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
const res = await new Promise((resolve, reject) => {
|
||||||
});
|
if (particle instanceof Error) {
|
||||||
|
reject(particle.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
expect(res).toBe('failed_with_timeout');
|
registerHandlersHelper(peer, particle, {
|
||||||
|
return: {
|
||||||
|
return: (args): undefined => {
|
||||||
|
resolve(args[0]);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe("failed_with_timeout");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,15 +13,23 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { describe, expect, it } from 'vitest';
|
|
||||||
import { registerHandlersHelper, withPeer } from '../../util/testUtils.js';
|
|
||||||
import { handleTimeout } from '../../particle/Particle.js';
|
|
||||||
import { CallServiceData, ResultCodes } from '../../jsServiceHost/interfaces.js';
|
|
||||||
|
|
||||||
describe('FluencePeer flow tests', () => {
|
import assert from "assert";
|
||||||
it('should execute par instruction in parallel', async function () {
|
|
||||||
await withPeer(async (peer) => {
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
const script = `
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CallServiceData,
|
||||||
|
ResultCodes,
|
||||||
|
} from "../../jsServiceHost/interfaces.js";
|
||||||
|
import { handleTimeout } from "../../particle/Particle.js";
|
||||||
|
import { registerHandlersHelper, withPeer } from "../../util/testUtils.js";
|
||||||
|
|
||||||
|
describe("FluencePeer flow tests", () => {
|
||||||
|
it("should execute par instruction in parallel", async function () {
|
||||||
|
await withPeer(async (peer) => {
|
||||||
|
const script = `
|
||||||
(par
|
(par
|
||||||
(seq
|
(seq
|
||||||
(call %init_peer_id% ("flow" "timeout") [1000 "test1"] res1)
|
(call %init_peer_id% ("flow" "timeout") [1000 "test1"] res1)
|
||||||
@ -34,52 +42,62 @@ describe('FluencePeer flow tests', () => {
|
|||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const res = await new Promise<any>((resolve, reject) => {
|
|
||||||
peer.internals.regHandler.forParticle(particle.id, 'flow', 'timeout', (req: CallServiceData) => {
|
|
||||||
const [timeout, message] = req.args;
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
const res = {
|
|
||||||
result: message,
|
|
||||||
retCode: ResultCodes.success,
|
|
||||||
};
|
|
||||||
resolve(res);
|
|
||||||
}, timeout);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (particle instanceof Error) {
|
const res = await new Promise((resolve, reject) => {
|
||||||
return reject(particle.message);
|
peer.internals.regHandler.forParticle(
|
||||||
}
|
particle.id,
|
||||||
|
"flow",
|
||||||
const values: any[] = [];
|
"timeout",
|
||||||
|
(req: CallServiceData) => {
|
||||||
|
const [timeout, message] = req.args;
|
||||||
|
assert(typeof timeout === "number");
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
return new Promise((resolve) => {
|
||||||
callback: {
|
setTimeout(() => {
|
||||||
callback1: (args: any) => {
|
const res = {
|
||||||
const [val] = args;
|
result: message,
|
||||||
values.push(val);
|
retCode: ResultCodes.success,
|
||||||
if (values.length === 2) {
|
};
|
||||||
resolve(values);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
callback2: (args: any) => {
|
|
||||||
const [val] = args;
|
|
||||||
values.push(val);
|
|
||||||
if (values.length === 2) {
|
|
||||||
resolve(values);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
resolve(res);
|
||||||
|
}, timeout);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
await expect(res).toEqual(expect.arrayContaining(["test1", "test1"]));
|
if (particle instanceof Error) {
|
||||||
|
reject(particle.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values: JSONValue[] = [];
|
||||||
|
|
||||||
|
registerHandlersHelper(peer, particle, {
|
||||||
|
callback: {
|
||||||
|
callback1: (args): undefined => {
|
||||||
|
const [val] = args;
|
||||||
|
values.push(val);
|
||||||
|
|
||||||
|
if (values.length === 2) {
|
||||||
|
resolve(values);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callback2: (args): undefined => {
|
||||||
|
const [val] = args;
|
||||||
|
values.push(val);
|
||||||
|
|
||||||
|
if (values.length === 2) {
|
||||||
|
resolve(values);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}, 1500);
|
|
||||||
});
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toEqual(expect.arrayContaining(["test1", "test1"]));
|
||||||
|
});
|
||||||
|
}, 1500);
|
||||||
|
});
|
||||||
|
@ -1,29 +1,46 @@
|
|||||||
import { it, describe, expect } from 'vitest';
|
/**
|
||||||
|
* 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 { withPeer } from '../../util/testUtils.js';
|
import { it, describe, expect } from "vitest";
|
||||||
|
|
||||||
describe('Parse ast tests', () => {
|
import { withPeer } from "../../util/testUtils.js";
|
||||||
it('Correct ast should be parsed correctly', async () => {
|
|
||||||
withPeer(async (peer) => {
|
|
||||||
const air = `(null)`;
|
|
||||||
const res = await peer.internals.parseAst(air);
|
|
||||||
|
|
||||||
expect(res).toStrictEqual({
|
describe("Parse ast tests", () => {
|
||||||
success: true,
|
it("Correct ast should be parsed correctly", async () => {
|
||||||
data: { Null: null },
|
await withPeer(async (peer) => {
|
||||||
});
|
const air = `(null)`;
|
||||||
});
|
const res = await peer.internals.parseAst(air);
|
||||||
|
|
||||||
|
expect(res).toStrictEqual({
|
||||||
|
success: true,
|
||||||
|
data: { Null: null },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Incorrect ast should result in corresponding error', async () => {
|
it("Incorrect ast should result in corresponding error", async () => {
|
||||||
withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const air = `(null`;
|
const air = `(null`;
|
||||||
const res = await peer.internals.parseAst(air);
|
const res = await peer.internals.parseAst(air);
|
||||||
|
|
||||||
expect(res).toStrictEqual({
|
expect(res).toStrictEqual({
|
||||||
success: false,
|
success: false,
|
||||||
data: expect.stringContaining('error'),
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
});
|
data: expect.stringContaining("error"),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,78 +1,101 @@
|
|||||||
import { it, describe, expect } from 'vitest';
|
/**
|
||||||
|
* 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 { isFluencePeer } from '../../api.js';
|
import { it, describe, expect } from "vitest";
|
||||||
import { mkTestPeer, registerHandlersHelper, withPeer } from '../../util/testUtils.js';
|
|
||||||
import { handleTimeout } from '../../particle/Particle.js';
|
|
||||||
import { FluencePeer } from '../FluencePeer.js';
|
|
||||||
|
|
||||||
describe('FluencePeer usage test suite', () => {
|
import { isFluencePeer } from "../../api.js";
|
||||||
it('should perform test for FluencePeer class correctly', async () => {
|
import { handleTimeout } from "../../particle/Particle.js";
|
||||||
// arrange
|
import {
|
||||||
const peer = await mkTestPeer();
|
mkTestPeer,
|
||||||
const number = 1;
|
registerHandlersHelper,
|
||||||
const object = { str: 'Hello!' };
|
withPeer,
|
||||||
const undefinedVal = undefined;
|
} from "../../util/testUtils.js";
|
||||||
|
import { FluencePeer } from "../FluencePeer.js";
|
||||||
|
|
||||||
// act
|
describe("FluencePeer usage test suite", () => {
|
||||||
const isPeerPeer = isFluencePeer(peer);
|
it("should perform test for FluencePeer class correctly", async () => {
|
||||||
const isNumberPeer = isFluencePeer(number);
|
// arrange
|
||||||
const isObjectPeer = isFluencePeer(object);
|
const peer = await mkTestPeer();
|
||||||
const isUndefinedPeer = isFluencePeer(undefinedVal);
|
const number = 1;
|
||||||
|
const object = { str: "Hello!" };
|
||||||
|
const undefinedVal = undefined;
|
||||||
|
|
||||||
// act
|
// act
|
||||||
expect(isPeerPeer).toBe(true);
|
const isPeerPeer = isFluencePeer(peer);
|
||||||
expect(isNumberPeer).toBe(false);
|
const isNumberPeer = isFluencePeer(number);
|
||||||
expect(isObjectPeer).toBe(false);
|
const isObjectPeer = isFluencePeer(object);
|
||||||
expect(isUndefinedPeer).toBe(false);
|
const isUndefinedPeer = isFluencePeer(undefinedVal);
|
||||||
});
|
|
||||||
|
|
||||||
it('Should successfully call identity on local peer', async function () {
|
// act
|
||||||
await withPeer(async (peer) => {
|
expect(isPeerPeer).toBe(true);
|
||||||
const script = `
|
expect(isNumberPeer).toBe(false);
|
||||||
|
expect(isObjectPeer).toBe(false);
|
||||||
|
expect(isUndefinedPeer).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should successfully call identity on local peer", async function () {
|
||||||
|
await withPeer(async (peer) => {
|
||||||
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
(call %init_peer_id% ("op" "identity") ["test"] res)
|
(call %init_peer_id% ("op" "identity") ["test"] res)
|
||||||
(call %init_peer_id% ("callback" "callback") [res])
|
(call %init_peer_id% ("callback" "callback") [res])
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
|
||||||
|
|
||||||
const res = await new Promise<string>((resolve, reject) => {
|
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
callback: {
|
|
||||||
callback: async (args: any) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
const res = await new Promise((resolve, reject) => {
|
||||||
});
|
if (particle instanceof Error) {
|
||||||
|
reject(particle.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
expect(res).toBe('test');
|
registerHandlersHelper(peer, particle, {
|
||||||
|
callback: {
|
||||||
|
callback: (args): undefined => {
|
||||||
|
const [res] = args;
|
||||||
|
resolve(res);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe("test");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Should throw correct message when calling non existing local service', async function () {
|
it("Should throw correct message when calling non existing local service", async function () {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const res = callIncorrectService(peer);
|
const res = callIncorrectService(peer);
|
||||||
|
|
||||||
await expect(res).rejects.toMatchObject({
|
await expect(res).rejects.toMatchObject({
|
||||||
message: expect.stringContaining(
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
`"No service found for service call: serviceId='incorrect', fnName='incorrect' args='[]'"`,
|
message: expect.stringContaining(
|
||||||
),
|
`"No service found for service call: serviceId='incorrect', fnName='incorrect' args='[]'"`,
|
||||||
instruction: 'call %init_peer_id% ("incorrect" "incorrect") [] res',
|
),
|
||||||
});
|
instruction: 'call %init_peer_id% ("incorrect" "incorrect") [] res',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Should not crash if undefined is passed as a variable', async () => {
|
it("Should not crash if undefined is passed as a variable", async () => {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
(call %init_peer_id% ("load" "arg") [] arg)
|
(call %init_peer_id% ("load" "arg") [] arg)
|
||||||
(seq
|
(seq
|
||||||
@ -80,99 +103,108 @@ describe('FluencePeer usage test suite', () => {
|
|||||||
(call %init_peer_id% ("callback" "callback") [res])
|
(call %init_peer_id% ("callback" "callback") [res])
|
||||||
)
|
)
|
||||||
)`;
|
)`;
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
|
||||||
|
|
||||||
const res = await new Promise<any>((resolve, reject) => {
|
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
load: {
|
|
||||||
arg: () => undefined,
|
|
||||||
},
|
|
||||||
callback: {
|
|
||||||
callback: (args: any) => {
|
|
||||||
const [val] = args;
|
|
||||||
resolve(val);
|
|
||||||
},
|
|
||||||
error: (args: any) => {
|
|
||||||
const [error] = args;
|
|
||||||
reject(error);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
const res = await new Promise((resolve, reject) => {
|
||||||
});
|
if (particle instanceof Error) {
|
||||||
|
reject(particle.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
expect(res).toBe(null);
|
registerHandlersHelper(peer, particle, {
|
||||||
|
load: {
|
||||||
|
arg: () => {
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
callback: {
|
||||||
|
callback: (args): undefined => {
|
||||||
|
const [val] = args;
|
||||||
|
resolve(val);
|
||||||
|
},
|
||||||
|
error: (args): undefined => {
|
||||||
|
const [error] = args;
|
||||||
|
reject(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not crash if an error ocurred in user-defined handler', async () => {
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
await withPeer(async (peer) => {
|
});
|
||||||
const script = `
|
|
||||||
|
expect(res).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should not crash if an error ocurred in user-defined handler", async () => {
|
||||||
|
await withPeer(async (peer) => {
|
||||||
|
const script = `
|
||||||
(xor
|
(xor
|
||||||
(call %init_peer_id% ("load" "arg") [] arg)
|
(call %init_peer_id% ("load" "arg") [] arg)
|
||||||
(call %init_peer_id% ("callback" "error") [%last_error%])
|
(call %init_peer_id% ("callback" "error") [%last_error%])
|
||||||
)`;
|
)`;
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
|
||||||
|
|
||||||
const promise = new Promise<any>((_resolve, reject) => {
|
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
load: {
|
|
||||||
arg: () => {
|
|
||||||
throw new Error('my super custom error message');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
callback: {
|
|
||||||
error: (args: any) => {
|
|
||||||
const [error] = args;
|
|
||||||
reject(error);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
const promise = new Promise<never>((_resolve, reject) => {
|
||||||
});
|
if (particle instanceof Error) {
|
||||||
|
reject(particle.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await expect(promise).rejects.toMatchObject({
|
registerHandlersHelper(peer, particle, {
|
||||||
message: expect.stringContaining('my super custom error message'),
|
load: {
|
||||||
});
|
arg: () => {
|
||||||
|
throw new Error("my super custom error message");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
callback: {
|
||||||
|
error: (args): undefined => {
|
||||||
|
const [error] = args;
|
||||||
|
reject(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(promise).rejects.toMatchObject({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
message: expect.stringContaining("my super custom error message"),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function callIncorrectService(peer: FluencePeer): Promise<string[]> {
|
async function callIncorrectService(peer: FluencePeer) {
|
||||||
const script = `
|
const script = `
|
||||||
(xor
|
(xor
|
||||||
(call %init_peer_id% ("incorrect" "incorrect") [] res)
|
(call %init_peer_id% ("incorrect" "incorrect") [] res)
|
||||||
(call %init_peer_id% ("callback" "error") [%last_error%])
|
(call %init_peer_id% ("callback" "error") [%last_error%])
|
||||||
)`;
|
)`;
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
|
||||||
|
|
||||||
return new Promise<any[]>((resolve, reject) => {
|
|
||||||
if (particle instanceof Error) {
|
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
callback: {
|
|
||||||
callback: (args: any) => {
|
|
||||||
resolve(args);
|
|
||||||
},
|
|
||||||
error: (args: any) => {
|
|
||||||
const [error] = args;
|
|
||||||
reject(error);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
return new Promise<unknown[]>((resolve, reject) => {
|
||||||
|
if (particle instanceof Error) {
|
||||||
|
reject(particle.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerHandlersHelper(peer, particle, {
|
||||||
|
callback: {
|
||||||
|
callback: (args): undefined => {
|
||||||
|
resolve(args);
|
||||||
|
},
|
||||||
|
error: (args): undefined => {
|
||||||
|
const [error] = args;
|
||||||
|
reject(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,99 +13,114 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { CallServiceData, CallServiceResult, GenericCallServiceHandler, IJsServiceHost } from './interfaces.js';
|
|
||||||
|
import {
|
||||||
|
CallServiceData,
|
||||||
|
CallServiceResult,
|
||||||
|
GenericCallServiceHandler,
|
||||||
|
IJsServiceHost,
|
||||||
|
} from "./interfaces.js";
|
||||||
|
|
||||||
export class JsServiceHost implements IJsServiceHost {
|
export class JsServiceHost implements IJsServiceHost {
|
||||||
private particleScopeHandlers = new Map<string, Map<string, GenericCallServiceHandler>>();
|
private particleScopeHandlers = new Map<
|
||||||
private commonHandlers = new Map<string, GenericCallServiceHandler>();
|
string,
|
||||||
|
Map<string, GenericCallServiceHandler>
|
||||||
|
>();
|
||||||
|
private commonHandlers = new Map<string, GenericCallServiceHandler>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if any handler for the specified serviceId is registered
|
* Returns true if any handler for the specified serviceId is registered
|
||||||
*/
|
*/
|
||||||
hasService(serviceId: string): boolean {
|
hasService(serviceId: string): boolean {
|
||||||
return this.commonHandlers.has(serviceId) || this.particleScopeHandlers.has(serviceId);
|
return (
|
||||||
|
this.commonHandlers.has(serviceId) ||
|
||||||
|
this.particleScopeHandlers.has(serviceId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all handlers associated with the specified particle scope
|
||||||
|
* @param particleId Particle ID to remove handlers for
|
||||||
|
*/
|
||||||
|
removeParticleScopeHandlers(particleId: string): void {
|
||||||
|
this.particleScopeHandlers.delete(particleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find call service handler for specified particle
|
||||||
|
* @param serviceId Service ID as specified in `call` air instruction
|
||||||
|
* @param fnName Function name as specified in `call` air instruction
|
||||||
|
* @param particleId Particle ID
|
||||||
|
*/
|
||||||
|
getHandler(
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
particleId: string,
|
||||||
|
): GenericCallServiceHandler | null {
|
||||||
|
const key = serviceFnKey(serviceId, fnName);
|
||||||
|
return (
|
||||||
|
this.particleScopeHandlers.get(particleId)?.get(key) ??
|
||||||
|
this.commonHandlers.get(key) ??
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute service call for specified call service data. Return null if no handler was found
|
||||||
|
*/
|
||||||
|
async callService(req: CallServiceData): Promise<CallServiceResult | null> {
|
||||||
|
const handler = this.getHandler(
|
||||||
|
req.serviceId,
|
||||||
|
req.fnName,
|
||||||
|
req.particleContext.particleId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (handler === null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const result = await handler(req);
|
||||||
* Removes all handlers associated with the specified particle scope
|
|
||||||
* @param particleId Particle ID to remove handlers for
|
// Otherwise AVM might break
|
||||||
*/
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
removeParticleScopeHandlers(particleId: string): void {
|
if (result.result === undefined) {
|
||||||
this.particleScopeHandlers.delete(particleId);
|
result.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return result;
|
||||||
* Find call service handler for specified particle
|
}
|
||||||
* @param serviceId Service ID as specified in `call` air instruction
|
|
||||||
* @param fnName Function name as specified in `call` air instruction
|
|
||||||
* @param particleId Particle ID
|
|
||||||
*/
|
|
||||||
getHandler(serviceId: string, fnName: string, particleId: string): GenericCallServiceHandler | null {
|
|
||||||
const key = serviceFnKey(serviceId, fnName);
|
|
||||||
const psh = this.particleScopeHandlers.get(particleId);
|
|
||||||
let handler: GenericCallServiceHandler | undefined = undefined;
|
|
||||||
|
|
||||||
// we should prioritize handler for this particle if there is one
|
/**
|
||||||
// if particle-scoped handler exist for this particle try getting handler there
|
* Register handler for all particles
|
||||||
if (psh !== undefined) {
|
*/
|
||||||
handler = psh.get(key);
|
registerGlobalHandler(
|
||||||
}
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
handler: GenericCallServiceHandler,
|
||||||
|
): void {
|
||||||
|
this.commonHandlers.set(serviceFnKey(serviceId, fnName), handler);
|
||||||
|
}
|
||||||
|
|
||||||
// then try to find a common handler for all particles with this service-fn key
|
/**
|
||||||
// if there is no particle-specific handler, get one from common map
|
* Register handler which will be called only for particle with the specific id
|
||||||
if (handler === undefined) {
|
*/
|
||||||
handler = this.commonHandlers.get(key);
|
registerParticleScopeHandler(
|
||||||
}
|
particleId: string,
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
handler: GenericCallServiceHandler,
|
||||||
|
): void {
|
||||||
|
let psh = this.particleScopeHandlers.get(particleId);
|
||||||
|
|
||||||
return handler || null;
|
if (psh === undefined) {
|
||||||
|
psh = new Map<string, GenericCallServiceHandler>();
|
||||||
|
this.particleScopeHandlers.set(particleId, psh);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
psh.set(serviceFnKey(serviceId, fnName), handler);
|
||||||
* Execute service call for specified call service data. Return null if no handler was found
|
}
|
||||||
*/
|
|
||||||
async callService(req: CallServiceData): Promise<CallServiceResult | null> {
|
|
||||||
const handler = this.getHandler(req.serviceId, req.fnName, req.particleContext.particleId);
|
|
||||||
|
|
||||||
if (handler === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await handler(req);
|
|
||||||
|
|
||||||
// Otherwise AVM might break
|
|
||||||
if (result.result === undefined) {
|
|
||||||
result.result = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register handler for all particles
|
|
||||||
*/
|
|
||||||
registerGlobalHandler(serviceId: string, fnName: string, handler: GenericCallServiceHandler): void {
|
|
||||||
this.commonHandlers.set(serviceFnKey(serviceId, fnName), handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register handler which will be called only for particle with the specific id
|
|
||||||
*/
|
|
||||||
registerParticleScopeHandler(
|
|
||||||
particleId: string,
|
|
||||||
serviceId: string,
|
|
||||||
fnName: string,
|
|
||||||
handler: GenericCallServiceHandler,
|
|
||||||
): void {
|
|
||||||
let psh = this.particleScopeHandlers.get(particleId);
|
|
||||||
if (psh === undefined) {
|
|
||||||
psh = new Map<string, GenericCallServiceHandler>();
|
|
||||||
this.particleScopeHandlers.set(particleId, psh);
|
|
||||||
}
|
|
||||||
|
|
||||||
psh.set(serviceFnKey(serviceId, fnName), handler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function serviceFnKey(serviceId: string, fnName: string) {
|
function serviceFnKey(serviceId: string, fnName: string) {
|
||||||
return `${serviceId}/${fnName}`;
|
return `${serviceId}/${fnName}`;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,118 +13,127 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import type { SecurityTetraplet } from '@fluencelabs/avm';
|
import type { SecurityTetraplet } from "@fluencelabs/avm";
|
||||||
import { JSONValue } from '../util/commonTypes.js';
|
import type { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
|
import { JSONArray, JSONValue } from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JS Service host a low level interface for managing pure javascript services.
|
* JS Service host a low level interface for managing pure javascript services.
|
||||||
* It operates on a notion of Call Service Handlers - functions which are called when a `call` air instruction is executed on the local peer.
|
* It operates on a notion of Call Service Handlers - functions which are called when a `call` air instruction is executed on the local peer.
|
||||||
*/
|
*/
|
||||||
export interface IJsServiceHost {
|
export interface IJsServiceHost {
|
||||||
/**
|
/**
|
||||||
* Returns true if any handler for the specified serviceId is registered
|
* Returns true if any handler for the specified serviceId is registered
|
||||||
*/
|
*/
|
||||||
hasService(serviceId: string): boolean;
|
hasService(serviceId: string): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find call service handler for specified particle
|
* Find call service handler for specified particle
|
||||||
* @param serviceId Service ID as specified in `call` air instruction
|
* @param serviceId Service ID as specified in `call` air instruction
|
||||||
* @param fnName Function name as specified in `call` air instruction
|
* @param fnName Function name as specified in `call` air instruction
|
||||||
* @param particleId Particle ID
|
* @param particleId Particle ID
|
||||||
*/
|
*/
|
||||||
getHandler(serviceId: string, fnName: string, particleId: string): GenericCallServiceHandler | null;
|
getHandler(
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
particleId: string,
|
||||||
|
): GenericCallServiceHandler | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute service call for specified call service data
|
* Execute service call for specified call service data
|
||||||
*/
|
*/
|
||||||
callService(req: CallServiceData): Promise<CallServiceResult | null>;
|
callService(req: CallServiceData): Promise<CallServiceResult | null>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register handler for all particles
|
* Register handler for all particles
|
||||||
*/
|
*/
|
||||||
registerGlobalHandler(serviceId: string, fnName: string, handler: GenericCallServiceHandler): void;
|
registerGlobalHandler(
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
handler: GenericCallServiceHandler,
|
||||||
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register handler which will be called only for particle with the specific id
|
* Register handler which will be called only for particle with the specific id
|
||||||
*/
|
*/
|
||||||
registerParticleScopeHandler(
|
registerParticleScopeHandler(
|
||||||
particleId: string,
|
particleId: string,
|
||||||
serviceId: string,
|
serviceId: string,
|
||||||
fnName: string,
|
fnName: string,
|
||||||
handler: GenericCallServiceHandler,
|
handler: GenericCallServiceHandler,
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all handlers associated with the specified particle scope
|
* Removes all handlers associated with the specified particle scope
|
||||||
* @param particleId Particle ID to remove handlers for
|
* @param particleId Particle ID to remove handlers for
|
||||||
*/
|
*/
|
||||||
removeParticleScopeHandlers(particleId: string): void;
|
removeParticleScopeHandlers(particleId: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ResultCodes {
|
export enum ResultCodes {
|
||||||
success = 0,
|
success = 0,
|
||||||
error = 1,
|
error = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Particle context. Contains additional information about particle which triggered `call` air instruction from AVM
|
* Particle context. Contains additional information about particle which triggered `call` air instruction from AVM
|
||||||
*/
|
*/
|
||||||
export interface ParticleContext {
|
export interface ParticleContext {
|
||||||
/**
|
/**
|
||||||
* The identifier of particle which triggered the call
|
* The identifier of particle which triggered the call
|
||||||
*/
|
*/
|
||||||
particleId: string;
|
particleId: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The peer id which created the particle
|
* The peer id which created the particle
|
||||||
*/
|
*/
|
||||||
initPeerId: PeerIdB58;
|
initPeerId: PeerIdB58;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Particle's timestamp when it was created
|
* Particle's timestamp when it was created
|
||||||
*/
|
*/
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time to live in milliseconds. The time after the particle should be expired
|
* Time to live in milliseconds. The time after the particle should be expired
|
||||||
*/
|
*/
|
||||||
ttl: number;
|
ttl: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Particle's signature
|
* Particle's signature
|
||||||
*/
|
*/
|
||||||
signature: Uint8Array;
|
signature: Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the information passed from AVM when a `call` air instruction is executed on the local peer
|
* Represents the information passed from AVM when a `call` air instruction is executed on the local peer
|
||||||
*/
|
*/
|
||||||
export interface CallServiceData {
|
export interface CallServiceData {
|
||||||
/**
|
/**
|
||||||
* Service ID as specified in `call` air instruction
|
* Service ID as specified in `call` air instruction
|
||||||
*/
|
*/
|
||||||
serviceId: string;
|
serviceId: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function name as specified in `call` air instruction
|
* Function name as specified in `call` air instruction
|
||||||
*/
|
*/
|
||||||
fnName: string;
|
fnName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments as specified in `call` air instruction
|
* Arguments as specified in `call` air instruction
|
||||||
*/
|
*/
|
||||||
args: any[];
|
args: JSONArray;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security Tetraplets received from AVM
|
* Security Tetraplets received from AVM
|
||||||
*/
|
*/
|
||||||
tetraplets: SecurityTetraplet[][];
|
tetraplets: SecurityTetraplet[][];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Particle context, @see {@link ParticleContext}
|
* Particle context, @see {@link ParticleContext}
|
||||||
*/
|
*/
|
||||||
particleContext: ParticleContext;
|
particleContext: ParticleContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,19 +144,21 @@ export type CallServiceResultType = JSONValue;
|
|||||||
/**
|
/**
|
||||||
* Generic call service handler
|
* Generic call service handler
|
||||||
*/
|
*/
|
||||||
export type GenericCallServiceHandler = (req: CallServiceData) => CallServiceResult | Promise<CallServiceResult>;
|
export type GenericCallServiceHandler = (
|
||||||
|
req: CallServiceData,
|
||||||
|
) => CallServiceResult | Promise<CallServiceResult>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the result of the `call` air instruction to be returned into AVM
|
* Represents the result of the `call` air instruction to be returned into AVM
|
||||||
*/
|
*/
|
||||||
export interface CallServiceResult {
|
export interface CallServiceResult {
|
||||||
/**
|
/**
|
||||||
* Return code to be returned to AVM
|
* Return code to be returned to AVM
|
||||||
*/
|
*/
|
||||||
retCode: ResultCodes;
|
retCode: ResultCodes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result object to be returned to AVM
|
* Result object to be returned to AVM
|
||||||
*/
|
*/
|
||||||
result: CallServiceResultType;
|
result: CallServiceResultType;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,48 +13,58 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { FluencePeer } from '../jsPeer/FluencePeer.js';
|
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
import { JSONArray } from "@fluencelabs/interfaces";
|
||||||
import { builtInServices } from '../services/builtins.js';
|
|
||||||
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
|
import { IParticle } from "../particle/interfaces.js";
|
||||||
|
import { builtInServices } from "../services/builtins.js";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CallServiceData,
|
CallServiceData,
|
||||||
CallServiceResult,
|
CallServiceResult,
|
||||||
CallServiceResultType,
|
CallServiceResultType,
|
||||||
ParticleContext,
|
ParticleContext,
|
||||||
ResultCodes,
|
ResultCodes,
|
||||||
} from './interfaces.js';
|
} from "./interfaces.js";
|
||||||
|
|
||||||
export const doNothing = (..._args: Array<unknown>) => undefined;
|
export const doNothing = () => {
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
export const WrapFnIntoServiceCall =
|
export const WrapFnIntoServiceCall = (
|
||||||
(fn: (args: any[]) => CallServiceResultType) =>
|
fn: (args: JSONArray) => CallServiceResultType | undefined,
|
||||||
(req: CallServiceData): CallServiceResult => ({
|
) => {
|
||||||
retCode: ResultCodes.success,
|
return (req: CallServiceData): CallServiceResult => {
|
||||||
result: fn(req.args),
|
return {
|
||||||
});
|
retCode: ResultCodes.success,
|
||||||
|
result: fn(req.args) ?? null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export class ServiceError extends Error {
|
export class ServiceError extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super(message);
|
super(message);
|
||||||
|
|
||||||
Object.setPrototypeOf(this, ServiceError.prototype);
|
Object.setPrototypeOf(this, ServiceError.prototype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getParticleContext = (particle: IParticle): ParticleContext => {
|
export const getParticleContext = (particle: IParticle): ParticleContext => {
|
||||||
return {
|
return {
|
||||||
particleId: particle.id,
|
particleId: particle.id,
|
||||||
initPeerId: particle.initPeerId,
|
initPeerId: particle.initPeerId,
|
||||||
timestamp: particle.timestamp,
|
timestamp: particle.timestamp,
|
||||||
ttl: particle.ttl,
|
ttl: particle.ttl,
|
||||||
signature: particle.signature,
|
signature: particle.signature,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function registerDefaultServices(peer: FluencePeer) {
|
export function registerDefaultServices(peer: FluencePeer) {
|
||||||
Object.entries(builtInServices).forEach(([serviceId, service]) => {
|
Object.entries(builtInServices).forEach(([serviceId, service]) => {
|
||||||
Object.entries(service).forEach(([fnName, fn]) => {
|
Object.entries(service).forEach(([fnName, fn]) => {
|
||||||
peer.internals.regHandler.common(serviceId, fnName, fn);
|
peer.internals.regHandler.common(serviceId, fnName, fn);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import bs58 from "bs58";
|
import bs58 from "bs58";
|
||||||
import { fromUint8Array, toUint8Array } from 'js-base64';
|
import { fromUint8Array, toUint8Array } from "js-base64";
|
||||||
import { it, describe, expect } from "vitest";
|
import { it, describe, expect } from "vitest";
|
||||||
import { fromBase64Sk, KeyPair } from '../index.js';
|
|
||||||
|
|
||||||
import { Particle, serializeToString, buildParticleMessage } from '../../particle/Particle.js';
|
import { Particle, buildParticleMessage } from "../../particle/Particle.js";
|
||||||
|
import { fromBase64Sk, KeyPair } from "../index.js";
|
||||||
|
|
||||||
const key = "+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=";
|
const key = "+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=";
|
||||||
const keyBytes = toUint8Array(key);
|
const keyBytes = toUint8Array(key);
|
||||||
@ -27,9 +27,10 @@ const keyBytes = toUint8Array(key);
|
|||||||
const testData = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9, 10]);
|
const testData = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9, 10]);
|
||||||
|
|
||||||
const testDataSig = Uint8Array.from([
|
const testDataSig = Uint8Array.from([
|
||||||
224, 104, 245, 206, 140, 248, 27, 72, 68, 133, 111, 10, 164, 197, 242, 132, 107, 77, 224, 67, 99, 106, 76, 29, 144,
|
224, 104, 245, 206, 140, 248, 27, 72, 68, 133, 111, 10, 164, 197, 242, 132,
|
||||||
121, 122, 169, 36, 173, 58, 80, 170, 102, 137, 253, 157, 247, 168, 87, 162, 223, 188, 214, 203, 220, 52, 246, 29,
|
107, 77, 224, 67, 99, 106, 76, 29, 144, 121, 122, 169, 36, 173, 58, 80, 170,
|
||||||
86, 77, 71, 224, 248, 16, 213, 254, 75, 78, 239, 243, 222, 241, 15,
|
102, 137, 253, 157, 247, 168, 87, 162, 223, 188, 214, 203, 220, 52, 246, 29,
|
||||||
|
86, 77, 71, 224, 248, 16, 213, 254, 75, 78, 239, 243, 222, 241, 15,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// signature produced by KeyPair created from some random KeyPair
|
// signature produced by KeyPair created from some random KeyPair
|
||||||
@ -112,24 +113,49 @@ describe("KeyPair tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("validates particle signature checks", async function () {
|
it("validates particle signature checks", async function () {
|
||||||
const keyPair = await fromBase64Sk("7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE=");
|
const keyPair = await fromBase64Sk(
|
||||||
expect(bs58.encode(keyPair.getLibp2pPeerId().toBytes())).toBe("12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D");
|
"7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE=",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(bs58.encode(keyPair.getLibp2pPeerId().toBytes())).toBe(
|
||||||
|
"12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D",
|
||||||
|
);
|
||||||
|
|
||||||
const message = toUint8Array(btoa("message"));
|
const message = toUint8Array(btoa("message"));
|
||||||
const signature = await keyPair.signBytes(message);
|
const signature = await keyPair.signBytes(message);
|
||||||
|
|
||||||
const verified = await keyPair.verify(message, signature);
|
const verified = await keyPair.verify(message, signature);
|
||||||
expect(verified).toBe(true);
|
expect(verified).toBe(true);
|
||||||
expect(fromUint8Array(signature)).toBe("sBW7H6/1fwAwF86ldwVm9BDu0YH3w30oFQjTWX0Tiu9yTVZHmxkV2OX4GL5jn0Iz0CrasGcOfozzkZwtJBPMBg==");
|
|
||||||
|
|
||||||
const particle = await Particle.createNew("abc", keyPair.getPeerId(), 7000, keyPair, "2883f959-e9e7-4843-8c37-205d393ca372", 1696934545662);
|
expect(fromUint8Array(signature)).toBe(
|
||||||
|
"sBW7H6/1fwAwF86ldwVm9BDu0YH3w30oFQjTWX0Tiu9yTVZHmxkV2OX4GL5jn0Iz0CrasGcOfozzkZwtJBPMBg==",
|
||||||
|
);
|
||||||
|
|
||||||
|
const particle = await Particle.createNew(
|
||||||
|
"abc",
|
||||||
|
keyPair.getPeerId(),
|
||||||
|
7000,
|
||||||
|
keyPair,
|
||||||
|
"2883f959-e9e7-4843-8c37-205d393ca372",
|
||||||
|
1696934545662,
|
||||||
|
);
|
||||||
|
|
||||||
const particle_bytes = buildParticleMessage(particle);
|
const particle_bytes = buildParticleMessage(particle);
|
||||||
expect(fromUint8Array(particle_bytes)).toBe("Mjg4M2Y5NTktZTllNy00ODQzLThjMzctMjA1ZDM5M2NhMzcy/kguGYsBAABYGwAAYWJj");
|
|
||||||
|
|
||||||
const isParticleVerified = await KeyPair.verifyWithPublicKey(keyPair.getPublicKey(), particle_bytes, particle.signature);
|
expect(fromUint8Array(particle_bytes)).toBe(
|
||||||
|
"Mjg4M2Y5NTktZTllNy00ODQzLThjMzctMjA1ZDM5M2NhMzcy/kguGYsBAABYGwAAYWJj",
|
||||||
|
);
|
||||||
|
|
||||||
|
const isParticleVerified = await KeyPair.verifyWithPublicKey(
|
||||||
|
keyPair.getPublicKey(),
|
||||||
|
particle_bytes,
|
||||||
|
particle.signature,
|
||||||
|
);
|
||||||
|
|
||||||
expect(isParticleVerified).toBe(true);
|
expect(isParticleVerified).toBe(true);
|
||||||
|
|
||||||
expect(fromUint8Array(particle.signature)).toBe("KceXDnOfqe0dOnAxiDsyWBIvUq6WHoT0ge+VMHXOZsjZvCNH7/10oufdlYfcPomfv28On6E87ZhDcHGBZcb7Bw==");
|
expect(fromUint8Array(particle.signature)).toBe(
|
||||||
|
"KceXDnOfqe0dOnAxiDsyWBIvUq6WHoT0ge+VMHXOZsjZvCNH7/10oufdlYfcPomfv28On6E87ZhDcHGBZcb7Bw==",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,31 +1,55 @@
|
|||||||
import { it, describe, expect, beforeAll } from 'vitest';
|
/**
|
||||||
|
* 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 * as fs from 'fs';
|
import * as fs from "fs";
|
||||||
import * as url from 'url';
|
import * as path from "path";
|
||||||
import * as path from 'path';
|
import * as url from "url";
|
||||||
import { compileAqua, withPeer } from '../../util/testUtils.js';
|
|
||||||
|
|
||||||
let aqua: any;
|
import { it, describe, expect, beforeAll } from "vitest";
|
||||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
|
||||||
|
|
||||||
describe('Marine js tests', () => {
|
import { compileAqua, CompiledFnCall, withPeer } from "../../util/testUtils.js";
|
||||||
beforeAll(async () => {
|
|
||||||
const pathToAquaFiles = path.join(__dirname, '../../../aqua_test/marine-js.aqua');
|
let aqua: Record<string, CompiledFnCall>;
|
||||||
const { services, functions } = await compileAqua(pathToAquaFiles);
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
||||||
aqua = functions;
|
|
||||||
});
|
describe("Marine js tests", () => {
|
||||||
|
beforeAll(async () => {
|
||||||
it('should call marine service correctly', async () => {
|
const pathToAquaFiles = path.join(
|
||||||
await withPeer(async (peer) => {
|
__dirname,
|
||||||
// arrange
|
"../../../aqua_test/marine-js.aqua",
|
||||||
const wasm = await fs.promises.readFile(path.join(__dirname, '../../../data_for_test/greeting.wasm'));
|
);
|
||||||
await peer.registerMarineService(wasm, 'greeting');
|
|
||||||
|
const { functions } = await compileAqua(pathToAquaFiles);
|
||||||
// act
|
aqua = functions;
|
||||||
const res = await aqua.call(peer, { arg: 'test' });
|
});
|
||||||
|
|
||||||
// assert
|
it("should call marine service correctly", async () => {
|
||||||
expect(res).toBe('Hi, Hi, Hi, test');
|
await withPeer(async (peer) => {
|
||||||
});
|
// arrange
|
||||||
|
const wasm = await fs.promises.readFile(
|
||||||
|
path.join(__dirname, "../../../data_for_test/greeting.wasm"),
|
||||||
|
);
|
||||||
|
|
||||||
|
await peer.registerMarineService(wasm, "greeting");
|
||||||
|
|
||||||
|
// act
|
||||||
|
const res = await aqua["call"](peer, { arg: "test" });
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(res).toBe("Hi, Hi, Hi, test");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
import { BlobWorker } from 'threads';
|
|
||||||
import { fromBase64, toUint8Array } from 'js-base64';
|
|
||||||
// @ts-ignore
|
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
|
||||||
import { Buffer } from 'buffer';
|
|
||||||
import { LazyLoader } from '../interfaces.js';
|
|
||||||
|
|
||||||
export class InlinedWorkerLoader extends LazyLoader<WorkerImplementation> {
|
|
||||||
constructor(b64script: string) {
|
|
||||||
super(() => {
|
|
||||||
const script = fromBase64(b64script);
|
|
||||||
return BlobWorker.fromText(script);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InlinedWasmLoader extends LazyLoader<Buffer> {
|
|
||||||
constructor(b64wasm: string) {
|
|
||||||
super(() => {
|
|
||||||
const wasm = toUint8Array(b64wasm);
|
|
||||||
return Buffer.from(wasm);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,24 +13,23 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { createRequire } from 'module';
|
|
||||||
|
|
||||||
// @ts-ignore
|
import { Buffer } from "buffer";
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
import fs from "fs";
|
||||||
// @ts-ignore
|
import { createRequire } from "module";
|
||||||
import { Worker } from 'threads';
|
import path from "path";
|
||||||
import { Buffer } from 'buffer';
|
|
||||||
import * as fs from 'fs';
|
import { Worker, type Worker as WorkerImplementation } from "threads/master";
|
||||||
import * as path from 'path';
|
|
||||||
import { LazyLoader } from '../interfaces.js';
|
import { LazyLoader } from "../interfaces.js";
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
const bufferToSharedArrayBuffer = (buffer: Buffer): SharedArrayBuffer => {
|
const bufferToSharedArrayBuffer = (buffer: Buffer): SharedArrayBuffer => {
|
||||||
const sab = new SharedArrayBuffer(buffer.length);
|
const sab = new SharedArrayBuffer(buffer.length);
|
||||||
const tmp = new Uint8Array(sab);
|
const tmp = new Uint8Array(sab);
|
||||||
tmp.set(buffer, 0);
|
tmp.set(buffer, 0);
|
||||||
return sab;
|
return sab;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,10 +38,13 @@ const bufferToSharedArrayBuffer = (buffer: Buffer): SharedArrayBuffer => {
|
|||||||
* @param source - object specifying the source of the file. Consist two fields: package name and file path.
|
* @param source - object specifying the source of the file. Consist two fields: package name and file path.
|
||||||
* @returns SharedArrayBuffer with the wasm file
|
* @returns SharedArrayBuffer with the wasm file
|
||||||
*/
|
*/
|
||||||
export const loadWasmFromNpmPackage = async (source: { package: string; file: string }): Promise<SharedArrayBuffer> => {
|
export const loadWasmFromNpmPackage = async (source: {
|
||||||
const packagePath = require.resolve(source.package);
|
package: string;
|
||||||
const filePath = path.join(path.dirname(packagePath), source.file);
|
file: string;
|
||||||
return loadWasmFromFileSystem(filePath);
|
}): Promise<SharedArrayBuffer> => {
|
||||||
|
const packagePath = require.resolve(source.package);
|
||||||
|
const filePath = path.join(path.dirname(packagePath), source.file);
|
||||||
|
return loadWasmFromFileSystem(filePath);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,35 +53,43 @@ export const loadWasmFromNpmPackage = async (source: { package: string; file: st
|
|||||||
* @param filePath - path to the wasm file
|
* @param filePath - path to the wasm file
|
||||||
* @returns SharedArrayBuffer with the wasm fileWorker
|
* @returns SharedArrayBuffer with the wasm fileWorker
|
||||||
*/
|
*/
|
||||||
export const loadWasmFromFileSystem = async (filePath: string): Promise<SharedArrayBuffer> => {
|
export const loadWasmFromFileSystem = async (
|
||||||
const buffer = await fs.promises.readFile(filePath);
|
filePath: string,
|
||||||
return bufferToSharedArrayBuffer(buffer);
|
): Promise<SharedArrayBuffer> => {
|
||||||
|
const buffer = await fs.promises.readFile(filePath);
|
||||||
|
return bufferToSharedArrayBuffer(buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
export class WasmLoaderFromFs extends LazyLoader<SharedArrayBuffer> {
|
export class WasmLoaderFromFs extends LazyLoader<SharedArrayBuffer> {
|
||||||
constructor(filePath: string) {
|
constructor(filePath: string) {
|
||||||
super(() => loadWasmFromFileSystem(filePath));
|
super(() => {
|
||||||
}
|
return loadWasmFromFileSystem(filePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WasmLoaderFromNpm extends LazyLoader<SharedArrayBuffer> {
|
export class WasmLoaderFromNpm extends LazyLoader<SharedArrayBuffer> {
|
||||||
constructor(pkg: string, file: string) {
|
constructor(pkg: string, file: string) {
|
||||||
super(() => loadWasmFromNpmPackage({ package: pkg, file: file }));
|
super(() => {
|
||||||
}
|
return loadWasmFromNpmPackage({ package: pkg, file: file });
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WorkerLoaderFromFs extends LazyLoader<WorkerImplementation> {
|
export class WorkerLoaderFromFs extends LazyLoader<WorkerImplementation> {
|
||||||
constructor(scriptPath: string) {
|
constructor(scriptPath: string) {
|
||||||
super(() => new Worker(scriptPath));
|
super(() => {
|
||||||
}
|
return new Worker(scriptPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WorkerLoaderFromNpm extends LazyLoader<WorkerImplementation> {
|
export class WorkerLoaderFromNpm extends LazyLoader<WorkerImplementation> {
|
||||||
constructor(pkg: string, file: string) {
|
constructor(pkg: string, file: string) {
|
||||||
super(() => {
|
super(() => {
|
||||||
const packagePath = require.resolve(pkg);
|
const packagePath = require.resolve(pkg);
|
||||||
const scriptPath = path.join(path.dirname(packagePath), file);
|
const scriptPath = path.join(path.dirname(packagePath), file);
|
||||||
return new Worker(scriptPath);
|
return new Worker(scriptPath);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 { Buffer } from 'buffer';
|
|
||||||
import { LazyLoader } from '../interfaces.js';
|
|
||||||
// @ts-ignore
|
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
|
||||||
|
|
||||||
const bufferToSharedArrayBuffer = (buffer: Buffer): SharedArrayBuffer => {
|
|
||||||
const sab = new SharedArrayBuffer(buffer.length);
|
|
||||||
const tmp = new Uint8Array(sab);
|
|
||||||
tmp.set(buffer, 0);
|
|
||||||
return sab;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load wasm file from the server. Only works in browsers.
|
|
||||||
* The function will try load file into SharedArrayBuffer if the site is cross-origin isolated.
|
|
||||||
* Otherwise the return value fallbacks to Buffer which is less performant but is still compatible with FluenceAppService methods.
|
|
||||||
* We strongly recommend to set-up cross-origin headers. For more details see: See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements
|
|
||||||
* Filename is relative to current origin.
|
|
||||||
* @param filePath - path to the wasm file relative to current origin
|
|
||||||
* @returns Either SharedArrayBuffer or Buffer with the wasm file
|
|
||||||
*/
|
|
||||||
export const loadWasmFromUrl = async (filePath: string): Promise<SharedArrayBuffer | Buffer> => {
|
|
||||||
const fullUrl = window.location.origin + '/' + filePath;
|
|
||||||
const res = await fetch(fullUrl);
|
|
||||||
const ab = await res.arrayBuffer();
|
|
||||||
new Uint8Array(ab);
|
|
||||||
const buffer = Buffer.from(ab);
|
|
||||||
|
|
||||||
// only convert to shared buffers if necessary CORS headers have been set:
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements
|
|
||||||
if (crossOriginIsolated) {
|
|
||||||
return bufferToSharedArrayBuffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class WasmLoaderFromUrl extends LazyLoader<SharedArrayBuffer | Buffer> {
|
|
||||||
constructor(filePath: string) {
|
|
||||||
super(() => loadWasmFromUrl(filePath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WorkerLoaderFromUrl extends LazyLoader<WorkerImplementation> {
|
|
||||||
constructor(scriptPath: string) {
|
|
||||||
super(() => new Worker(scriptPath));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,97 +13,112 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { CallResultsArray, InterpreterResult, RunParameters } from '@fluencelabs/avm';
|
|
||||||
import { IStartable, JSONArray, JSONObject, CallParameters } from '../util/commonTypes.js';
|
import {
|
||||||
// @ts-ignore
|
CallResultsArray,
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
InterpreterResult,
|
||||||
|
RunParameters,
|
||||||
|
} from "@fluencelabs/avm";
|
||||||
|
import { JSONObject, JSONValue, JSONArray } from "@fluencelabs/interfaces";
|
||||||
|
import type { Worker as WorkerImplementation } from "threads/master";
|
||||||
|
|
||||||
|
import { IStartable, CallParameters } from "../util/commonTypes.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract for marine host implementations. Marine host is responsible for creating calling and removing marine services
|
* Contract for marine host implementations. Marine host is responsible for creating calling and removing marine services
|
||||||
*/
|
*/
|
||||||
export interface IMarineHost extends IStartable {
|
export interface IMarineHost extends IStartable {
|
||||||
/**
|
/**
|
||||||
* Creates marine service from the given module and service id
|
* Creates marine service from the given module and service id
|
||||||
*/
|
*/
|
||||||
createService(serviceModule: ArrayBuffer | SharedArrayBuffer, serviceId: string): Promise<void>;
|
createService(
|
||||||
|
serviceModule: ArrayBuffer | SharedArrayBuffer,
|
||||||
|
serviceId: string,
|
||||||
|
): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes marine service with the given service id
|
* Removes marine service with the given service id
|
||||||
*/
|
*/
|
||||||
removeService(serviceId: string): Promise<void>;
|
removeService(serviceId: string): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if any service with the specified service id is registered
|
* Returns true if any service with the specified service id is registered
|
||||||
*/
|
*/
|
||||||
hasService(serviceId: string): Promise<boolean>;
|
hasService(serviceId: string): Promise<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the specified function of the specified service with the given arguments
|
* Calls the specified function of the specified service with the given arguments
|
||||||
*/
|
*/
|
||||||
callService(
|
callService(
|
||||||
serviceId: string,
|
serviceId: string,
|
||||||
functionName: string,
|
functionName: string,
|
||||||
args: JSONArray | JSONObject,
|
args: JSONArray | JSONObject,
|
||||||
callParams: CallParameters,
|
callParams: CallParameters,
|
||||||
): Promise<unknown>;
|
): Promise<JSONValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for different implementations of AVM runner
|
* Interface for different implementations of AVM runner
|
||||||
*/
|
*/
|
||||||
export interface IAvmRunner extends IStartable {
|
export interface IAvmRunner extends IStartable {
|
||||||
/**
|
/**
|
||||||
* Run AVM interpreter with the specified parameters
|
* Run AVM interpreter with the specified parameters
|
||||||
*/
|
*/
|
||||||
run(
|
run(
|
||||||
runParams: RunParameters,
|
runParams: RunParameters,
|
||||||
air: string,
|
air: string,
|
||||||
prevData: Uint8Array,
|
prevData: Uint8Array,
|
||||||
data: Uint8Array,
|
data: Uint8Array,
|
||||||
callResults: CallResultsArray,
|
callResults: CallResultsArray,
|
||||||
): Promise<InterpreterResult | Error>;
|
): Promise<InterpreterResult | Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for something which can hold a value
|
* Interface for something which can hold a value
|
||||||
*/
|
*/
|
||||||
export interface IValueLoader<T> {
|
export interface IValueLoader<T> {
|
||||||
getValue(): T;
|
getValue(): T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for something which can load wasm files
|
* Interface for something which can load wasm files
|
||||||
*/
|
*/
|
||||||
export interface IWasmLoader extends IValueLoader<ArrayBuffer | SharedArrayBuffer>, IStartable {}
|
export interface IWasmLoader
|
||||||
|
extends IValueLoader<ArrayBuffer | SharedArrayBuffer>,
|
||||||
|
IStartable {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for something which can thread.js based worker
|
* Interface for something which can thread.js based worker
|
||||||
*/
|
*/
|
||||||
export interface IWorkerLoader extends IValueLoader<WorkerImplementation>, IStartable {}
|
export interface IWorkerLoader
|
||||||
|
extends IValueLoader<WorkerImplementation | Promise<WorkerImplementation>>,
|
||||||
|
IStartable {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazy loader for some value. Value is loaded only when `start` method is called
|
* Lazy loader for some value. Value is loaded only when `start` method is called
|
||||||
*/
|
*/
|
||||||
export class LazyLoader<T> implements IStartable, IValueLoader<T> {
|
export class LazyLoader<T> implements IStartable, IValueLoader<T> {
|
||||||
private value: T | null = null;
|
private value: T | null = null;
|
||||||
|
|
||||||
constructor(private loadValue: () => Promise<T> | T) {}
|
constructor(private loadValue: () => Promise<T> | T) {}
|
||||||
|
|
||||||
getValue(): T {
|
getValue(): T {
|
||||||
if (this.value == null) {
|
if (this.value == null) {
|
||||||
throw new Error('Value has not been loaded. Call `start` method to load the value.');
|
throw new Error(
|
||||||
}
|
"Value has not been loaded. Call `start` method to load the value.",
|
||||||
|
);
|
||||||
return this.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async start() {
|
return this.value;
|
||||||
if (this.value !== null) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.value = await this.loadValue();
|
async start() {
|
||||||
|
if (this.value !== null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop() {}
|
this.value = await this.loadValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop() {}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,14 +13,17 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
// @ts-ignore
|
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
import { Worker, type Worker as WorkerImplementation } from "threads/master";
|
||||||
// @ts-ignore
|
|
||||||
import { Worker } from 'threads';
|
import { LazyLoader } from "../interfaces.js";
|
||||||
import { LazyLoader } from '../interfaces.js';
|
|
||||||
|
|
||||||
export class WorkerLoader extends LazyLoader<WorkerImplementation> {
|
export class WorkerLoader extends LazyLoader<WorkerImplementation> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(() => new Worker('../../../node_modules/@fluencelabs/marine-worker/dist/index.js'));
|
super(() => {
|
||||||
}
|
return new Worker(
|
||||||
|
"../../../node_modules/@fluencelabs/marine-worker/dist/index.js",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2022 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,96 +14,117 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { JSONArray, JSONObject, CallParameters } from '@fluencelabs/marine-js/dist/types';
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
import { LogFunction, logLevelToEnv } from '@fluencelabs/marine-js/dist/types';
|
import type {
|
||||||
import type { MarineBackgroundInterface } from '@fluencelabs/marine-worker';
|
JSONArray,
|
||||||
// @ts-ignore
|
JSONObject,
|
||||||
import { ModuleThread, spawn, Thread } from 'threads';
|
CallParameters,
|
||||||
|
} from "@fluencelabs/marine-js/dist/types";
|
||||||
|
import { LogFunction, logLevelToEnv } from "@fluencelabs/marine-js/dist/types";
|
||||||
|
import type { MarineBackgroundInterface } from "@fluencelabs/marine-worker";
|
||||||
|
import { ModuleThread, Thread, spawn } from "threads/master";
|
||||||
|
|
||||||
import { MarineLogger, marineLogger } from '../../util/logger.js';
|
import { MarineLogger, marineLogger } from "../../util/logger.js";
|
||||||
import { IMarineHost, IWasmLoader, IWorkerLoader } from '../interfaces.js';
|
import { IMarineHost, IWasmLoader, IWorkerLoader } from "../interfaces.js";
|
||||||
|
|
||||||
export class MarineBackgroundRunner implements IMarineHost {
|
export class MarineBackgroundRunner implements IMarineHost {
|
||||||
private workerThread?: MarineBackgroundInterface;
|
private workerThread?: ModuleThread<MarineBackgroundInterface>;
|
||||||
|
|
||||||
private loggers = new Map<string, MarineLogger>();
|
private loggers = new Map<string, MarineLogger>();
|
||||||
|
|
||||||
constructor(private workerLoader: IWorkerLoader, private controlModuleLoader: IWasmLoader, private avmWasmLoader: IWasmLoader) {}
|
constructor(
|
||||||
|
private workerLoader: IWorkerLoader,
|
||||||
|
private controlModuleLoader: IWasmLoader,
|
||||||
|
private avmWasmLoader: IWasmLoader,
|
||||||
|
) {}
|
||||||
|
|
||||||
async hasService(serviceId: string) {
|
async hasService(serviceId: string) {
|
||||||
if (!this.workerThread) {
|
if (this.workerThread == null) {
|
||||||
throw new Error('Worker is not initialized');
|
throw new Error("Worker is not initialized");
|
||||||
}
|
|
||||||
|
|
||||||
return this.workerThread.hasService(serviceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeService(serviceId: string) {
|
return this.workerThread.hasService(serviceId);
|
||||||
if (!this.workerThread) {
|
}
|
||||||
throw new Error('Worker is not initialized');
|
|
||||||
}
|
async removeService(serviceId: string) {
|
||||||
|
if (this.workerThread == null) {
|
||||||
await this.workerThread.removeService(serviceId);
|
throw new Error("Worker is not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
await this.workerThread.removeService(serviceId);
|
||||||
if (this.workerThread) {
|
}
|
||||||
throw new Error('Worker thread already initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.controlModuleLoader.start();
|
|
||||||
const wasm = this.controlModuleLoader.getValue();
|
|
||||||
|
|
||||||
await this.avmWasmLoader.start();
|
|
||||||
|
|
||||||
await this.workerLoader.start();
|
async start(): Promise<void> {
|
||||||
const worker = await this.workerLoader.getValue();
|
if (this.workerThread != null) {
|
||||||
|
throw new Error("Worker thread already initialized");
|
||||||
const workerThread = await spawn<MarineBackgroundInterface>(worker);
|
|
||||||
const logfn: LogFunction = (message) => {
|
|
||||||
const serviceLogger = this.loggers.get(message.service);
|
|
||||||
if (!serviceLogger) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
serviceLogger[message.level](message.message);
|
|
||||||
};
|
|
||||||
workerThread.onLogMessage().subscribe(logfn);
|
|
||||||
await workerThread.init(wasm);
|
|
||||||
this.workerThread = workerThread;
|
|
||||||
await this.createService(this.avmWasmLoader.getValue(), 'avm');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createService(serviceModule: ArrayBuffer | SharedArrayBuffer, serviceId: string): Promise<void> {
|
await this.controlModuleLoader.start();
|
||||||
if (!this.workerThread) {
|
const wasm = this.controlModuleLoader.getValue();
|
||||||
throw new Error('Worker is not initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
// The logging level is controlled by the environment variable passed to enable debug logs.
|
await this.avmWasmLoader.start();
|
||||||
// We enable all possible log levels passing the control for exact printouts to the logger
|
|
||||||
const env = logLevelToEnv('info');
|
await this.workerLoader.start();
|
||||||
this.loggers.set(serviceId, marineLogger(serviceId));
|
const worker = await this.workerLoader.getValue();
|
||||||
await this.workerThread.createService(serviceModule, serviceId, env);
|
|
||||||
|
const workerThread: ModuleThread<MarineBackgroundInterface> =
|
||||||
|
await spawn<MarineBackgroundInterface>(worker);
|
||||||
|
|
||||||
|
const logfn: LogFunction = (message) => {
|
||||||
|
const serviceLogger = this.loggers.get(message.service);
|
||||||
|
|
||||||
|
if (serviceLogger == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceLogger[message.level](message.message);
|
||||||
|
};
|
||||||
|
|
||||||
|
workerThread.onLogMessage().subscribe(logfn);
|
||||||
|
await workerThread.init(wasm);
|
||||||
|
this.workerThread = workerThread;
|
||||||
|
await this.createService(this.avmWasmLoader.getValue(), "avm");
|
||||||
|
}
|
||||||
|
|
||||||
|
async createService(
|
||||||
|
serviceModule: ArrayBuffer | SharedArrayBuffer,
|
||||||
|
serviceId: string,
|
||||||
|
): Promise<void> {
|
||||||
|
if (this.workerThread == null) {
|
||||||
|
throw new Error("Worker is not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
async callService(
|
// The logging level is controlled by the environment variable passed to enable debug logs.
|
||||||
serviceId: string,
|
// We enable all possible log levels passing the control for exact printouts to the logger
|
||||||
functionName: string,
|
const env = logLevelToEnv("info");
|
||||||
args: JSONArray | JSONObject,
|
this.loggers.set(serviceId, marineLogger(serviceId));
|
||||||
callParams: CallParameters,
|
await this.workerThread.createService(serviceModule, serviceId, env);
|
||||||
): Promise<unknown> {
|
}
|
||||||
if (!this.workerThread) {
|
|
||||||
throw 'Worker is not initialized';
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.workerThread.callService(serviceId, functionName, args, callParams);
|
async callService(
|
||||||
|
serviceId: string,
|
||||||
|
functionName: string,
|
||||||
|
args: JSONArray | JSONObject,
|
||||||
|
callParams: CallParameters,
|
||||||
|
): Promise<JSONValue> {
|
||||||
|
if (this.workerThread == null) {
|
||||||
|
throw new Error("Worker is not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop(): Promise<void> {
|
return this.workerThread.callService(
|
||||||
if (!this.workerThread) {
|
serviceId,
|
||||||
return;
|
functionName,
|
||||||
}
|
args,
|
||||||
|
callParams,
|
||||||
await this.workerThread.terminate();
|
);
|
||||||
await Thread.terminate(this.workerThread);
|
}
|
||||||
|
|
||||||
|
async stop(): Promise<void> {
|
||||||
|
if (this.workerThread == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.workerThread.terminate();
|
||||||
|
await Thread.terminate(this.workerThread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2020 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -18,23 +18,34 @@ import { CallResultsArray } from "@fluencelabs/avm";
|
|||||||
import { fromUint8Array, toUint8Array } from "js-base64";
|
import { fromUint8Array, toUint8Array } from "js-base64";
|
||||||
import { concat } from "uint8arrays/concat";
|
import { concat } from "uint8arrays/concat";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
import { KeyPair } from "../keypair/index.js";
|
import { KeyPair } from "../keypair/index.js";
|
||||||
import { numberToLittleEndianBytes } from "../util/bytes.js";
|
import { numberToLittleEndianBytes } from "../util/bytes.js";
|
||||||
|
|
||||||
import { IParticle } from "./interfaces.js";
|
import { IParticle } from "./interfaces.js";
|
||||||
|
|
||||||
|
const particleSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
timestamp: z.number().positive(),
|
||||||
|
script: z.string(),
|
||||||
|
data: z.string(),
|
||||||
|
ttl: z.number().positive(),
|
||||||
|
init_peer_id: z.string(),
|
||||||
|
signature: z.array(z.number()),
|
||||||
|
});
|
||||||
|
|
||||||
export class Particle implements IParticle {
|
export class Particle implements IParticle {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly id: string,
|
readonly id: string,
|
||||||
public readonly timestamp: number,
|
readonly timestamp: number,
|
||||||
public readonly script: string,
|
readonly script: string,
|
||||||
public readonly data: Uint8Array,
|
readonly data: Uint8Array,
|
||||||
public readonly ttl: number,
|
readonly ttl: number,
|
||||||
public readonly initPeerId: string,
|
readonly initPeerId: string,
|
||||||
public readonly signature: Uint8Array
|
readonly signature: Uint8Array,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
static async createNew(
|
static async createNew(
|
||||||
script: string,
|
script: string,
|
||||||
initPeerId: string,
|
initPeerId: string,
|
||||||
@ -60,20 +71,31 @@ export class Particle implements IParticle {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromString(str: string): Particle {
|
static fromString(str: string): Particle {
|
||||||
const json = JSON.parse(str);
|
const json = JSON.parse(str);
|
||||||
const res = new Particle(
|
|
||||||
json.id,
|
|
||||||
json.timestamp,
|
|
||||||
json.script,
|
|
||||||
toUint8Array(json.data),
|
|
||||||
json.ttl,
|
|
||||||
json.init_peer_id,
|
|
||||||
new Uint8Array(json.signature)
|
|
||||||
);
|
|
||||||
|
|
||||||
return res;
|
const res = particleSchema.safeParse(json);
|
||||||
|
|
||||||
|
if (!res.success) {
|
||||||
|
throw new Error(
|
||||||
|
`Particle format invalid. Errors: ${JSON.stringify(
|
||||||
|
res.error.flatten(),
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = res.data;
|
||||||
|
|
||||||
|
return new Particle(
|
||||||
|
data.id,
|
||||||
|
data.timestamp,
|
||||||
|
data.script,
|
||||||
|
toUint8Array(data.data),
|
||||||
|
data.ttl,
|
||||||
|
data.init_peer_id,
|
||||||
|
new Uint8Array(data.signature),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const en = new TextEncoder();
|
const en = new TextEncoder();
|
||||||
@ -109,16 +131,6 @@ export const hasExpired = (particle: IParticle): boolean => {
|
|||||||
return getActualTTL(particle) <= 0;
|
return getActualTTL(particle) <= 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates that particle signature is correct
|
|
||||||
*/
|
|
||||||
export const verifySignature = async (particle: IParticle, publicKey: Uint8Array): Promise<boolean> => {
|
|
||||||
// TODO: Uncomment this when nox roll out particle signatures
|
|
||||||
return true;
|
|
||||||
// const message = buildParticleMessage(particle);
|
|
||||||
// return unmarshalPublicKey(publicKey).verify(message, particle.signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a particle clone with new data
|
* Creates a particle clone with new data
|
||||||
*/
|
*/
|
||||||
@ -179,8 +191,8 @@ export interface ParticleQueueItem {
|
|||||||
*/
|
*/
|
||||||
export const handleTimeout = (fn: () => void) => {
|
export const handleTimeout = (fn: () => void) => {
|
||||||
return (stage: ParticleExecutionStage) => {
|
return (stage: ParticleExecutionStage) => {
|
||||||
if (stage.stage === "expired") {
|
if (stage.stage === "expired") {
|
||||||
fn();
|
fn();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user