mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-05-05 22:32:13 +00:00
Compare commits
54 Commits
js-client-
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
25faa5af30 | ||
|
702ad605a8 | ||
|
3b1371f968 | ||
|
b269cf4100 | ||
|
6b934b4bcb | ||
|
31d82047a7 | ||
|
e8417d069a | ||
|
0edea02701 | ||
|
d7070fd71e | ||
|
0417ba410a | ||
|
2b1d0f7f05 | ||
|
00db991332 | ||
|
514663a4fd | ||
|
fa38328fdd | ||
|
628e60173b | ||
|
8ac029b6d3 | ||
|
fe661dbd2c | ||
|
e21ecc1ede | ||
|
6f0df6425b | ||
|
a8a14735b3 | ||
|
5696e3beba | ||
|
15f96dc5f7 | ||
|
4d90414190 | ||
|
5d7ae85e58 | ||
|
f5425b4746 | ||
|
1e42e58a83 | ||
|
165e96b702 | ||
|
57f8a851eb | ||
|
61964ccb1e | ||
|
fd23160f81 | ||
|
05180c1020 | ||
|
cea65efc1a | ||
|
3610a6cca1 | ||
|
042431238a | ||
|
86a73027e5 | ||
|
9b629eef2e | ||
|
d6008110cf | ||
|
ac407c204d | ||
|
1578b791ac | ||
|
44eb1493b3 | ||
|
bf0ed95dff | ||
|
fdd0ca0ea2 | ||
|
8dd30ea35b | ||
|
59e852878c | ||
|
04c278b783 | ||
|
65a9e44aac | ||
|
b5a6296225 | ||
|
5e917a650f | ||
|
98462bfdf6 | ||
|
f4a550dd22 | ||
|
638da47bc2 | ||
|
f5e9923974 | ||
|
1803d83ce7 | ||
|
b460491fbd |
@ -1,4 +1,5 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2022,
|
||||
@ -20,13 +21,7 @@
|
||||
],
|
||||
"ignorePatterns": ["**/node_modules/", "**/dist/", "**/build/", "**/public/"],
|
||||
"rules": {
|
||||
"eqeqeq": [
|
||||
"error",
|
||||
"always",
|
||||
{
|
||||
"null": "ignore"
|
||||
}
|
||||
],
|
||||
"eqeqeq": ["error", "always"],
|
||||
"no-console": ["error"],
|
||||
"arrow-body-style": ["error", "always"],
|
||||
"no-empty": [
|
||||
|
24
.github/e2e/docker-compose.yml
vendored
24
.github/e2e/docker-compose.yml
vendored
@ -44,8 +44,8 @@ services:
|
||||
- -w=9991
|
||||
- -x=10.50.10.10
|
||||
- --external-maddrs
|
||||
- /dns4/nox-1/tcp/7771
|
||||
- /dns4/nox-1/tcp/9991/ws
|
||||
- /ip4/127.0.0.1/tcp/7771
|
||||
- /ip4/127.0.0.1/tcp/9991/ws
|
||||
- --allow-private-ips
|
||||
- --local
|
||||
# - --bootstraps=/dns/nox-1/tcp/7771
|
||||
@ -75,8 +75,8 @@ services:
|
||||
- -w=9992
|
||||
- -x=10.50.10.20
|
||||
- --external-maddrs
|
||||
- /dns4/nox-2/tcp/7772
|
||||
- /dns4/nox-2/tcp/9992/ws
|
||||
- /ip4/127.0.0.1/tcp/7772
|
||||
- /ip4/127.0.0.1/tcp/9992/ws
|
||||
- --allow-private-ips
|
||||
- --bootstraps=/dns/nox-1/tcp/7771
|
||||
# 12D3KooWQdpukY3p2DhDfUfDgphAqsGu5ZUrmQ4mcHSGrRag6gQK
|
||||
@ -105,8 +105,8 @@ services:
|
||||
- -w=9993
|
||||
- -x=10.50.10.30
|
||||
- --external-maddrs
|
||||
- /dns4/nox-3/tcp/7773
|
||||
- /dns4/nox-3/tcp/9993/ws
|
||||
- /ip4/127.0.0.1/tcp/7773
|
||||
- /ip4/127.0.0.1/tcp/9993/ws
|
||||
- --allow-private-ips
|
||||
- --bootstraps=/dns/nox-1/tcp/7771
|
||||
# 12D3KooWRT8V5awYdEZm6aAV9HWweCEbhWd7df4wehqHZXAB7yMZ
|
||||
@ -135,8 +135,8 @@ services:
|
||||
- -w=9994
|
||||
- -x=10.50.10.40
|
||||
- --external-maddrs
|
||||
- /dns4/nox-4/tcp/7774
|
||||
- /dns4/nox-4/tcp/9994/ws
|
||||
- /ip4/127.0.0.1/tcp/7774
|
||||
- /ip4/127.0.0.1/tcp/9994/ws
|
||||
- --allow-private-ips
|
||||
- --bootstraps=/dns/nox-1/tcp/7771
|
||||
# 12D3KooWBzLSu9RL7wLP6oUowzCbkCj2AGBSXkHSJKuq4wwTfwof
|
||||
@ -165,8 +165,8 @@ services:
|
||||
- -w=9995
|
||||
- -x=10.50.10.50
|
||||
- --external-maddrs
|
||||
- /dns4/nox-5/tcp/7775
|
||||
- /dns4/nox-5/tcp/9995/ws
|
||||
- /ip4/127.0.0.1/tcp/7775
|
||||
- /ip4/127.0.0.1/tcp/9995/ws
|
||||
- --allow-private-ips
|
||||
- --bootstraps=/dns/nox-1/tcp/7771
|
||||
# 12D3KooWBf6hFgrnXwHkBnwPGMysP3b1NJe5HGtAWPYfwmQ2MBiU
|
||||
@ -196,8 +196,8 @@ services:
|
||||
- --bootstraps=/dns/nox-1/tcp/7771
|
||||
- -x=10.50.10.60
|
||||
- --external-maddrs
|
||||
- /dns4/nox-6/tcp/7776
|
||||
- /dns4/nox-6/tcp/9996/ws
|
||||
- /ip4/127.0.0.1/tcp/7776
|
||||
- /ip4/127.0.0.1/tcp/9996/ws
|
||||
- --allow-private-ips
|
||||
# 12D3KooWPisGn7JhooWhggndz25WM7vQ2JmA121EV8jUDQ5xMovJ
|
||||
- -k=5Qh8bB1sF28uLPwr3HTvEksCeC6mAWQvebCfcgv9y6j4qKwSzNKm2tzLUg4nACUEo2KZpBw11gNCnwaAdM7o1pEn
|
||||
|
4
.github/release-please/config.json
vendored
4
.github/release-please/config.json
vendored
@ -11,6 +11,8 @@
|
||||
"packages/core/js-client": {},
|
||||
"packages/core/js-client-isomorphic": {},
|
||||
"packages/core/marine-worker": {},
|
||||
"packages/core/aqua-to-js": {}
|
||||
"packages/core/aqua-to-js": {},
|
||||
"packages/core/interfaces": {},
|
||||
"packages/core/npm-aqua-compiler": {}
|
||||
}
|
||||
}
|
||||
|
10
.github/release-please/manifest.json
vendored
10
.github/release-please/manifest.json
vendored
@ -1,6 +1,8 @@
|
||||
{
|
||||
"packages/core/js-client": "0.4.2",
|
||||
"packages/core/marine-worker": "0.4.1",
|
||||
"packages/core/aqua-to-js": "0.2.0",
|
||||
"packages/core/js-client-isomorphic": "0.2.1"
|
||||
"packages/core/js-client": "0.9.0",
|
||||
"packages/core/marine-worker": "0.6.0",
|
||||
"packages/core/aqua-to-js": "0.3.13",
|
||||
"packages/core/js-client-isomorphic": "0.6.0",
|
||||
"packages/core/interfaces": "0.12.0",
|
||||
"packages/core/npm-aqua-compiler": "0.0.3"
|
||||
}
|
||||
|
6
.github/workflows/e2e.yml
vendored
6
.github/workflows/e2e.yml
vendored
@ -15,7 +15,7 @@ on:
|
||||
- "reopened"
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "main"
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- ".github/**"
|
||||
@ -43,7 +43,7 @@ jobs:
|
||||
uses: fluencelabs/aqua/.github/workflows/tests.yml@main
|
||||
with:
|
||||
js-client-snapshots: "${{ needs.js-client.outputs.js-client-snapshots }}"
|
||||
nox-image: "fluencelabs/nox:unstable_minimal"
|
||||
nox-image: "docker.fluence.dev/nox:renovate-avm_4905_1"
|
||||
flox:
|
||||
needs:
|
||||
- js-client
|
||||
@ -51,4 +51,4 @@ jobs:
|
||||
uses: fluencelabs/flox/.github/workflows/tests.yml@main
|
||||
with:
|
||||
js-client-snapshots: "${{ needs.js-client.outputs.js-client-snapshots }}"
|
||||
nox-image: "fluencelabs/nox:unstable_minimal"
|
||||
nox-image: "docker.fluence.dev/nox:renovate-avm_4905_1"
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -3,7 +3,7 @@ name: "release-please"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "main"
|
||||
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||
|
4
.github/workflows/run-tests.yml
vendored
4
.github/workflows/run-tests.yml
vendored
@ -10,7 +10,7 @@ on:
|
||||
- "!.github/workflows/snapshot.yml"
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "main"
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- ".github/**"
|
||||
@ -28,4 +28,4 @@ jobs:
|
||||
uses: ./.github/workflows/tests.yml
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
nox-image: "fluencelabs/nox:unstable_minimal"
|
||||
nox-image: "docker.fluence.dev/nox:renovate-avm_4905_1"
|
||||
|
2
.github/workflows/snapshot.yml
vendored
2
.github/workflows/snapshot.yml
vendored
@ -14,7 +14,7 @@ on:
|
||||
ref:
|
||||
description: "git ref to checkout to"
|
||||
type: string
|
||||
default: "master"
|
||||
default: "main"
|
||||
outputs:
|
||||
js-client-snapshots:
|
||||
description: "js-client snapshots"
|
||||
|
16
.github/workflows/tests.yml
vendored
16
.github/workflows/tests.yml
vendored
@ -6,7 +6,7 @@ on:
|
||||
nox-image:
|
||||
description: "nox image tag"
|
||||
type: string
|
||||
default: "fluencelabs/nox:minimal_0.2.9"
|
||||
default: "fluencelabs/nox:0.4.2"
|
||||
avm-version:
|
||||
description: "@fluencelabs/avm version"
|
||||
type: string
|
||||
@ -18,7 +18,7 @@ on:
|
||||
ref:
|
||||
description: "git ref to checkout to"
|
||||
type: string
|
||||
default: "master"
|
||||
default: "main"
|
||||
|
||||
env:
|
||||
NOX_IMAGE: "${{ inputs.nox-image }}"
|
||||
@ -34,6 +34,9 @@ jobs:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
env:
|
||||
LATEST_NODE_VERSION: 20.x
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version:
|
||||
@ -88,10 +91,6 @@ jobs:
|
||||
registry-url: "https://npm.fluence.dev"
|
||||
cache: "pnpm"
|
||||
|
||||
- run: pnpm -r i
|
||||
- run: pnpm -r build
|
||||
- run: pnpm lint-check
|
||||
|
||||
- name: Override dependencies
|
||||
uses: fluencelabs/github-actions/pnpm-set-dependency@main
|
||||
with:
|
||||
@ -102,6 +101,11 @@ jobs:
|
||||
}
|
||||
|
||||
- run: pnpm -r --no-frozen-lockfile i
|
||||
- run: pnpm -r build
|
||||
|
||||
- name: Lint code
|
||||
run: pnpm lint-check
|
||||
|
||||
- run: pnpm -r test
|
||||
|
||||
- name: Dump container logs
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -6,6 +6,6 @@
|
||||
node_modules/
|
||||
|
||||
# Build directory
|
||||
**/dist
|
||||
**/public
|
||||
dist/
|
||||
public/
|
||||
.DS_Store
|
||||
|
@ -2,11 +2,15 @@
|
||||
.eslintcache
|
||||
pnpm-lock.yaml
|
||||
|
||||
**/node_modules
|
||||
**/dist
|
||||
**/build
|
||||
**/public
|
||||
node_modules
|
||||
dist
|
||||
build
|
||||
public
|
||||
|
||||
**/CHANGELOG.md
|
||||
# TODO: remove after pnpm-set-deps will add \n symbol at the end of these files
|
||||
**/package.json
|
||||
|
||||
packages/core/js-client/src/versions.ts
|
||||
packages/core/js-client-isomorphic/src/versions.ts
|
||||
__snapshots__
|
||||
packages/@tests/aqua/src/_aqua/**
|
43
README.md
43
README.md
@ -25,7 +25,7 @@ This is the Javascript client for the [Fluence](https://fluence.network) network
|
||||
|
||||
### HTML page
|
||||
|
||||
Add a script tag with the JS Client bundle to your `index.html`. The easiest way to do this is using a CDN (
|
||||
Add a script tag with the JS Client module 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/)).
|
||||
|
||||
Here is an example using the JSDELIVR CDN:
|
||||
@ -33,33 +33,22 @@ Here is an example using the JSDELIVR CDN:
|
||||
```html
|
||||
<head>
|
||||
<title>Cool App</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@fluencelabs/js-client/dist/browser/index.min.js"></script>
|
||||
<script type="module">
|
||||
import {
|
||||
Fluence,
|
||||
randomKras,
|
||||
} from "https://cdn.jsdelivr.net/npm/@fluencelabs/js-client/dist/browser/index.min.js";
|
||||
|
||||
Fluence.connect(randomKras());
|
||||
</script>
|
||||
</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) 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
|
||||
the `/dist/browser` 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.)
|
||||
|
||||
After importing JS-client to HTML page the client is available as `window.Fluence` variable.
|
||||
To get a specific network you can peek at
|
||||
|
||||
```
|
||||
https://cdn.jsdelivr.net/npm/@fluencelabs/js-client/dist/network.js
|
||||
```
|
||||
|
||||
and hardcode selected network. So initialization would look like this
|
||||
|
||||
```javascript
|
||||
// Passing 1 kras network config from ./dist/network.js above
|
||||
window.Fluence.connect({
|
||||
multiaddr:
|
||||
"/dns4/0-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWSD5PToNiLQwKDXsu8JSysCwUt8BVUJEqCHcDe7P5h45e",
|
||||
peerId: "12D3KooWSD5PToNiLQwKDXsu8JSysCwUt8BVUJEqCHcDe7P5h45e",
|
||||
});
|
||||
```
|
||||
|
||||
## Usage in an Application
|
||||
|
||||
Once you've added the client, you can compile [Aqua](https://github.com/fluencelabs/aqua) and run it in your application. To compile Aqua, use [Fluence CLI](https://github.com/fluencelabs/cli).
|
||||
@ -171,6 +160,18 @@ localStorage.debug = "fluence:*";
|
||||
In Chromium-based web browsers (e.g. Brave, Chrome, and Electron), the JavaScript console will be default—only to show
|
||||
messages logged by debug if the "Verbose" log level is enabled.
|
||||
|
||||
## Low level usage
|
||||
|
||||
JS client also has an API for low level interaction with AVM and Marine JS.
|
||||
It could be handy in advanced scenarios when a user fetches AIR dynamically or generates AIR without default Aqua compiler.
|
||||
|
||||
`callAquaFunction` Allows to call aqua function without schema.
|
||||
|
||||
`registerService` Gives an ability to register service without schema. Passed `service` could be
|
||||
|
||||
- Plain object. In this case all function properties will be registered as AIR service functions.
|
||||
- Class instance. All class methods without inherited ones will be registered as AIR service functions.
|
||||
|
||||
## Development
|
||||
|
||||
To hack on the Fluence JS Client itself, please refer to the [development page](./DEVELOPING.md).
|
||||
|
2
ci.cjs
2
ci.cjs
@ -95,7 +95,7 @@ async function checkConsistency(file, versionsMap) {
|
||||
|
||||
for (const [name, versionInDep] of versionsMap) {
|
||||
const check = (x, version) => {
|
||||
if (version.includes("*")) {
|
||||
if (version.includes("*") || version.includes("^")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
21
packages/@tests/aqua/.gitignore
vendored
21
packages/@tests/aqua/.gitignore
vendored
@ -1,21 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
bundle/
|
||||
|
||||
dist
|
||||
esm
|
||||
types
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
.idea
|
51
packages/@tests/aqua/compile-aqua.ts
Normal file
51
packages/@tests/aqua/compile-aqua.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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 { writeFile } from "fs/promises";
|
||||
import { join, dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { compileFromPath } from "@fluencelabs/aqua-api";
|
||||
import aquaToJs from "@fluencelabs/aqua-to-js";
|
||||
|
||||
const files = ["smoke_test", "finalize_particle"];
|
||||
|
||||
for (const file of files) {
|
||||
const cr = await compileFromPath({
|
||||
filePath: join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
"_aqua",
|
||||
file + ".aqua",
|
||||
),
|
||||
targetType: "air",
|
||||
imports: [fileURLToPath(new URL("./node_modules", import.meta.url))],
|
||||
});
|
||||
|
||||
if (cr.errors.length > 0) {
|
||||
throw new Error(cr.errors.join("\n"));
|
||||
}
|
||||
|
||||
const res = await aquaToJs(cr, "ts");
|
||||
|
||||
if (res == null) {
|
||||
throw new Error("AquaToJs gave null value after compilation");
|
||||
}
|
||||
|
||||
await writeFile(
|
||||
fileURLToPath(new URL(join("src", "_aqua", file + ".ts"), import.meta.url)),
|
||||
res.sources,
|
||||
);
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"compile-aqua": "fluence aqua -i ./_aqua -o ./src/_aqua"
|
||||
"compile-aqua": "node --loader ts-node/esm compile-aqua.ts"
|
||||
},
|
||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||
"author": "Fluence Labs",
|
||||
@ -20,10 +20,12 @@
|
||||
"base64-js": "1.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fluencelabs/aqua-api": "0.13.0",
|
||||
"@fluencelabs/aqua-lib": "0.6.0",
|
||||
"@fluencelabs/cli": "0.7.2",
|
||||
"@fluencelabs/js-client": "workspace:^",
|
||||
"@fluencelabs/registry": "0.8.2",
|
||||
"@fluencelabs/trust-graph": "3.1.2"
|
||||
"@fluencelabs/aqua-to-js": "workspace:*",
|
||||
"@fluencelabs/js-client": "workspace:*",
|
||||
"@fluencelabs/registry": "0.9.0",
|
||||
"@fluencelabs/trust-graph": "3.1.2",
|
||||
"ts-node": "10.9.1"
|
||||
}
|
||||
}
|
||||
|
@ -2,71 +2,68 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
*
|
||||
* This file is auto-generated. Do not edit manually: changes may be erased.
|
||||
* Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
|
||||
* If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
||||
* Aqua version: 0.12.0
|
||||
* This file is generated using:
|
||||
* @fluencelabs/aqua-api version: 0.12.4-main-cee4448-2196-1
|
||||
* @fluencelabs/aqua-to-js version: 0.2.0
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
import type {
|
||||
IFluenceClient as IFluenceClient$$,
|
||||
CallParams as CallParams$$,
|
||||
} from "@fluencelabs/js-client";
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$,
|
||||
} from "@fluencelabs/js-client";
|
||||
import type { IFluenceClient as IFluenceClient$$, ParticleContext as ParticleContext$$ } from '@fluencelabs/js-client';
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$
|
||||
} from '@fluencelabs/js-client';
|
||||
|
||||
// Services
|
||||
|
||||
// Functions
|
||||
export const test_script = `
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(xor
|
||||
(xor
|
||||
(call -relay- ("op" "noop") [])
|
||||
(fail %last_error%)
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 0])
|
||||
)
|
||||
)
|
||||
`;
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(xor
|
||||
(call -relay- ("op" "noop") [])
|
||||
(fail :error:)
|
||||
)
|
||||
)
|
||||
(call %init_peer_id% ("callbackSrv" "response") [])
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0])
|
||||
)
|
||||
`;
|
||||
|
||||
export function test(config?: { ttl?: number }): Promise<void>;
|
||||
export type TestParams = [config?: {ttl?: number}] | [peer: IFluenceClient$$, config?: {ttl?: number}];
|
||||
|
||||
export function test(
|
||||
peer: IFluenceClient$$,
|
||||
config?: { ttl?: number },
|
||||
): Promise<void>;
|
||||
export type TestResult = Promise<void>;
|
||||
|
||||
export function test(...args: any) {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
functionName: "test",
|
||||
arrow: {
|
||||
tag: "arrow",
|
||||
domain: {
|
||||
tag: "labeledProduct",
|
||||
fields: {},
|
||||
export function test(...args: TestParams): TestResult {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
"functionName": "test",
|
||||
"arrow": {
|
||||
"domain": {
|
||||
"fields": {},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
codomain: {
|
||||
tag: "nil",
|
||||
"codomain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
},
|
||||
names: {
|
||||
relay: "-relay-",
|
||||
getDataSrv: "getDataSrv",
|
||||
callbackSrv: "callbackSrv",
|
||||
responseSrv: "callbackSrv",
|
||||
responseFnName: "response",
|
||||
errorHandlingSrv: "errorHandlingSrv",
|
||||
errorFnName: "error",
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
test_script,
|
||||
);
|
||||
"names": {
|
||||
"relay": "-relay-",
|
||||
"getDataSrv": "getDataSrv",
|
||||
"callbackSrv": "callbackSrv",
|
||||
"responseSrv": "callbackSrv",
|
||||
"responseFnName": "response",
|
||||
"errorHandlingSrv": "errorHandlingSrv",
|
||||
"errorFnName": "error"
|
||||
}
|
||||
},
|
||||
test_script
|
||||
);
|
||||
}
|
||||
|
||||
/* eslint-enable */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,9 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Fluence } from "@fluencelabs/js-client";
|
||||
import type { ClientConfig } from "@fluencelabs/js-client";
|
||||
import { fromByteArray } from "base64-js";
|
||||
import { Fluence, type ClientConfig } from "@fluencelabs/js-client";
|
||||
|
||||
import { test as particleTest } from "./_aqua/finalize_particle.js";
|
||||
import {
|
||||
@ -57,10 +55,6 @@ export type TestResult =
|
||||
|
||||
export const runTest = async (): Promise<TestResult> => {
|
||||
try {
|
||||
Fluence.onConnectionStateChange((state) => {
|
||||
console.info("connection state changed: ", state);
|
||||
});
|
||||
|
||||
console.log("connecting to Fluence Network...");
|
||||
console.log("multiaddr: ", relay.multiaddr);
|
||||
|
||||
@ -83,7 +77,6 @@ export const runTest = async (): Promise<TestResult> => {
|
||||
const client = Fluence.getClient();
|
||||
|
||||
console.log("my peer id: ", client.getPeerId());
|
||||
console.log("my sk id: ", fromByteArray(client.getPeerSecretKey()));
|
||||
|
||||
console.log("running hello test...");
|
||||
const hello = await helloTest();
|
||||
@ -91,11 +84,11 @@ export const runTest = async (): Promise<TestResult> => {
|
||||
|
||||
console.log("running marine test...");
|
||||
const marine = await marineTest(wasm);
|
||||
console.log("marine test finished, result: ", marine);
|
||||
|
||||
console.log("running particle test...");
|
||||
await particleTest();
|
||||
|
||||
console.log("marine test finished, result: ", marine);
|
||||
await particleTest();
|
||||
|
||||
const returnVal = {
|
||||
hello,
|
||||
|
21
packages/@tests/smoke/node/.gitignore
vendored
21
packages/@tests/smoke/node/.gitignore
vendored
@ -1,21 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
bundle/
|
||||
|
||||
dist
|
||||
esm
|
||||
types
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
.idea
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import "@fluencelabs/js-client";
|
||||
import { runTest } from "@test/aqua_for_test";
|
||||
|
||||
await runTest();
|
||||
|
26
packages/@tests/smoke/web-cra-ts/.gitignore
vendored
26
packages/@tests/smoke/web-cra-ts/.gitignore
vendored
@ -1,25 +1 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
public/js-client.min.js
|
||||
build/
|
@ -7,8 +7,6 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Web site created using create-react-app" />
|
||||
<!-- Ideally we want to use 'async' here. Currently, it's not supported. -->
|
||||
<script src="js-client.min.js"></script>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
|
@ -12,6 +12,18 @@ function App() {
|
||||
setResult(res);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err instanceof Error) {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
name: err.name,
|
||||
message: err.message,
|
||||
stack: err.stack,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
console.log(JSON.stringify(err));
|
||||
}
|
||||
|
||||
setResult({ type: "failure", error: err.toString() });
|
||||
});
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ const publicPath = join(__dirname, "../build/");
|
||||
|
||||
const test = async () => {
|
||||
const localServer = await startContentServer(port, publicPath);
|
||||
await createSymlinkIfNotExists(CDN_PUBLIC_PATH, join(publicPath, "source"));
|
||||
|
||||
await createSymlinkIfNotExists(
|
||||
JS_CLIENT_DEPS_PATH,
|
||||
join(publicPath, "node_modules"),
|
||||
|
21
packages/@tests/smoke/web/.gitignore
vendored
21
packages/@tests/smoke/web/.gitignore
vendored
@ -1,21 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
dist
|
||||
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
.idea
|
||||
|
||||
public/js-client.min.js
|
@ -19,7 +19,7 @@
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fluencelabs/js-client": "workspace:*",
|
||||
"@fluencelabs/js-client-isomorphic": "workspace:*",
|
||||
"@test/test-utils": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -16,8 +16,9 @@
|
||||
|
||||
<div id="res-placeholder"></div>
|
||||
|
||||
<script src="js-client.min.js"></script>
|
||||
<script src="index.js"></script>
|
||||
<!-- Importing js-client from local server that is used instead of the CDN -->
|
||||
<script type="module" src="js-client.min.js"></script>
|
||||
<script type="module" src="index.js"></script>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
const fluence = globalThis.fluence;
|
||||
import { Fluence, callAquaFunction, randomStage } from "./js-client.min.js";
|
||||
|
||||
const relay = {
|
||||
multiaddr:
|
||||
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||
};
|
||||
|
||||
@ -74,18 +74,19 @@ const getRelayTime = () => {
|
||||
const config = {};
|
||||
|
||||
const args = { relayPeerId: relay.peerId };
|
||||
return fluence.callAquaFunction({
|
||||
|
||||
return callAquaFunction({
|
||||
args,
|
||||
def,
|
||||
script,
|
||||
config,
|
||||
peer: fluence.defaultClient,
|
||||
peer: Fluence.defaultClient,
|
||||
fireAndForget: false
|
||||
});
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
console.log("starting fluence...");
|
||||
fluence.defaultClient = await fluence.clientFactory(relay, {
|
||||
await Fluence.connect(relay, {
|
||||
CDNUrl: "http://localhost:3000",
|
||||
});
|
||||
console.log("started fluence");
|
||||
@ -95,7 +96,7 @@ const main = async () => {
|
||||
console.log("got relay time, ", relayTime);
|
||||
|
||||
console.log("stopping fluence...");
|
||||
await fluence.defaultClient.stop();
|
||||
await Fluence.disconnect();
|
||||
console.log("stopped fluence...");
|
||||
|
||||
return relayTime;
|
||||
@ -109,5 +110,11 @@ btn.addEventListener("click", () => {
|
||||
inner.id = "res";
|
||||
inner.innerText = res;
|
||||
document.getElementById("res-placeholder").appendChild(inner);
|
||||
}).catch(err => {
|
||||
if (err instanceof Error) {
|
||||
console.log(JSON.stringify({ name: err.name, message: err.message, stack: err.stack }));
|
||||
return;
|
||||
}
|
||||
console.log(JSON.stringify(err));
|
||||
});
|
||||
});
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import assert from "node:assert";
|
||||
import { dirname, join } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
@ -45,6 +46,8 @@ const test = async () => {
|
||||
const browser = await puppeteer.launch();
|
||||
const page = (await browser.pages())[0];
|
||||
|
||||
assert(page);
|
||||
|
||||
page.on("console", (message) => {
|
||||
console.log(`${message.type().toUpperCase()}: ${message.text()}`);
|
||||
});
|
||||
@ -77,7 +80,7 @@ const test = async () => {
|
||||
await browser.close();
|
||||
await stopServer(localServer);
|
||||
|
||||
if (content == null) {
|
||||
if (content === null || content === undefined) {
|
||||
throw new Error("smoke test failed!");
|
||||
}
|
||||
};
|
||||
|
@ -61,21 +61,16 @@ export const startContentServer = (
|
||||
source: "/js-client.min.js",
|
||||
destination: "/source/index.min.js",
|
||||
},
|
||||
// TODO:
|
||||
// something like this
|
||||
// {
|
||||
// source: "/@fluencelabs/:name(\\w+)@:version([\\d.]+)/:path*",
|
||||
// destination: "/deps/@fluencelabs/:name/:path",
|
||||
// }
|
||||
// not supported for some reason. Need to manually iterate over all possible paths
|
||||
{
|
||||
source: "/@fluencelabs/:name([\\w-]+)@:version([\\d.]+)/dist/:asset",
|
||||
destination: "/node_modules/@fluencelabs/:name/dist/:asset",
|
||||
source: "/@fluencelabs/:name([\\w-]+)@:version([\\w-.]+)/dist/:asset",
|
||||
destination:
|
||||
"/node_modules/@fluencelabs/js-client-isomorphic/node_modules/@fluencelabs/:name/dist/:asset",
|
||||
},
|
||||
{
|
||||
source:
|
||||
"/@fluencelabs/:name([\\w-]+)@:version([\\d.]+)/dist/:prefix/:asset",
|
||||
destination: "/node_modules/@fluencelabs/:name/dist/:prefix/:asset",
|
||||
"/@fluencelabs/:name([\\w-]+)@:version([\\w-.]+)/dist/:prefix/:asset",
|
||||
destination:
|
||||
"/node_modules/@fluencelabs/js-client-isomorphic/node_modules/@fluencelabs/:name/dist/:prefix/:asset",
|
||||
},
|
||||
],
|
||||
headers: [
|
||||
|
@ -1,3 +1,3 @@
|
||||
{
|
||||
"ignorePatterns": ["src/**/__snapshots__/**/*"]
|
||||
"ignorePatterns": ["src/**/__snapshots__/**/*", "src/**/*.js"]
|
||||
}
|
||||
|
@ -18,6 +18,114 @@
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.2.1
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.5.1
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/interfaces bumped to 0.9.0
|
||||
* @fluencelabs/js-client bumped to 0.5.2
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.5.3
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.5.4
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/interfaces bumped to 0.10.0
|
||||
* @fluencelabs/js-client bumped to 0.6.0
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/interfaces bumped to 0.11.0
|
||||
* @fluencelabs/js-client bumped to 0.7.0
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.8.0
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.8.1
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.8.2
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.8.3
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.8.4
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/interfaces bumped to 0.12.0
|
||||
* @fluencelabs/js-client bumped to 0.9.0
|
||||
|
||||
## [0.3.5](https://github.com/fluencelabs/js-client/compare/aqua-to-js-v0.3.4...aqua-to-js-v0.3.5) (2023-12-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **npm-aqua-compiler:** create package ([#401](https://github.com/fluencelabs/js-client/issues/401)) ([d600811](https://github.com/fluencelabs/js-client/commit/d6008110cf0ecaf23a63cfef0bb3f786a6eb0937))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.5.5
|
||||
|
||||
## [0.3.0](https://github.com/fluencelabs/js-client/compare/aqua-to-js-v0.2.0...aqua-to-js-v0.3.0) (2023-11-22)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **js-client:** Segregation of responsibility between js-client packages [fixes DXJ-525] ([#378](https://github.com/fluencelabs/js-client/issues/378))
|
||||
|
||||
### Features
|
||||
|
||||
* **js-client:** Segregation of responsibility between js-client packages [fixes DXJ-525] ([#378](https://github.com/fluencelabs/js-client/issues/378)) ([f4a550d](https://github.com/fluencelabs/js-client/commit/f4a550dd226846dfc2ade1ccc35a286dc3be2fed))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* devDependencies
|
||||
* @fluencelabs/js-client bumped to 0.5.0
|
||||
|
||||
## [0.2.0](https://github.com/fluencelabs/js-client/compare/aqua-to-js-v0.1.0...aqua-to-js-v0.2.0) (2023-10-25)
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@fluencelabs/aqua-to-js",
|
||||
"type": "module",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.13",
|
||||
"description": "Tool for generating aqua wrapper",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
@ -18,12 +18,14 @@
|
||||
"ts-pattern": "5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fluencelabs/aqua-api": "0.12.0",
|
||||
"@fluencelabs/aqua-api": "0.13.0",
|
||||
"@fluencelabs/aqua-lib": "0.7.3",
|
||||
"@fluencelabs/interfaces": "workspace:*",
|
||||
"@fluencelabs/registry": "0.8.7",
|
||||
"@fluencelabs/js-client": "workspace:^",
|
||||
"@fluencelabs/registry": "0.9.0",
|
||||
"@fluencelabs/spell": "0.5.20",
|
||||
"@fluencelabs/trust-graph": "0.4.7",
|
||||
"vitest": "0.34.6"
|
||||
"vitest": "0.34.6",
|
||||
"zod": "3.22.4"
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ArrowWithoutCallbacks, NonArrowType } from "@fluencelabs/interfaces";
|
||||
import { ArrowType, NonArrowType } from "@fluencelabs/interfaces";
|
||||
import { match, P } from "ts-pattern";
|
||||
|
||||
import { getFuncArgs } from "./utils.js";
|
||||
|
||||
export function genTypeName(
|
||||
t: NonArrowType | ArrowWithoutCallbacks,
|
||||
t: NonArrowType | ArrowType,
|
||||
name: string,
|
||||
): readonly [string | undefined, string] {
|
||||
const genType = typeToTs(t);
|
||||
@ -35,7 +35,7 @@ export function genTypeName(
|
||||
const args =
|
||||
item.tag === "labeledProduct" ? Object.values(item.fields) : item.items;
|
||||
|
||||
if (args.length === 1) {
|
||||
if (args.length === 1 && "0" in args) {
|
||||
return genTypeName(args[0], name);
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ export function genTypeName(
|
||||
});
|
||||
}
|
||||
|
||||
export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks): string {
|
||||
export function typeToTs(t: NonArrowType | ArrowType): string {
|
||||
return match(t)
|
||||
.with({ tag: "nil" }, () => {
|
||||
return "null";
|
||||
@ -112,7 +112,7 @@ export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks): string {
|
||||
const retType =
|
||||
codomain.tag === "nil"
|
||||
? "void"
|
||||
: codomain.items.length === 1
|
||||
: codomain.items.length === 1 && "0" in codomain.items
|
||||
? typeToTs(codomain.items[0])
|
||||
: typeToTs(codomain);
|
||||
|
||||
@ -120,16 +120,7 @@ export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks): string {
|
||||
return [name, typeToTs(type)];
|
||||
});
|
||||
|
||||
const generic =
|
||||
args.length === 0
|
||||
? "null"
|
||||
: args
|
||||
.map(([name]) => {
|
||||
return `'${name}'`;
|
||||
})
|
||||
.join(" | ");
|
||||
|
||||
args.push(["callParams", `CallParams$$<${generic}>`]);
|
||||
args.push(["callParams", `ParticleContext$$`]);
|
||||
|
||||
const funcArgs = args
|
||||
.map(([name, type]) => {
|
||||
|
67
packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.d.ts
vendored
Normal file
67
packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.d.ts
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
/**
|
||||
*
|
||||
* This file is generated using:
|
||||
* @fluencelabs/aqua-api version: 0.0.0
|
||||
* @fluencelabs/aqua-to-js version: 0.0.0
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
import type { IFluenceClient as IFluenceClient$$, ParticleContext as ParticleContext$$ } from '@fluencelabs/js-client';
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$
|
||||
} from '@fluencelabs/js-client';
|
||||
|
||||
// Services
|
||||
export interface SrvDef {
|
||||
create: (wasm_b64_content: string, callParams: ParticleContext$$) => { error: string | null; service_id: string | null; success: boolean; } | Promise<{ error: string | null; service_id: string | null; success: boolean; }>;
|
||||
list: (callParams: ParticleContext$$) => string[] | Promise<string[]>;
|
||||
remove: (service_id: string, callParams: ParticleContext$$) => { error: string | null; success: boolean; } | Promise<{ error: string | null; success: boolean; }>;
|
||||
}
|
||||
export function registerSrv(service: SrvDef): void;
|
||||
export function registerSrv(serviceId: string, service: SrvDef): void;
|
||||
export function registerSrv(peer: IFluenceClient$$, service: SrvDef): void;
|
||||
export function registerSrv(peer: IFluenceClient$$, serviceId: string, service: SrvDef): void;
|
||||
export interface CalcServiceDef {
|
||||
divide: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
|
||||
clear_state: (callParams: ParticleContext$$) => void | Promise<void>;
|
||||
test_logs: (callParams: ParticleContext$$) => void | Promise<void>;
|
||||
multiply: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
|
||||
add: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
|
||||
state: (callParams: ParticleContext$$) => number | Promise<number>;
|
||||
subtract: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
|
||||
}
|
||||
export function registerCalcService(serviceId: string, service: CalcServiceDef): void;
|
||||
export function registerCalcService(peer: IFluenceClient$$, serviceId: string, service: CalcServiceDef): void;
|
||||
export interface HelloWorldDef {
|
||||
hello: (str: string, callParams: ParticleContext$$) => string | Promise<string>;
|
||||
}
|
||||
export function registerHelloWorld(service: HelloWorldDef): void;
|
||||
export function registerHelloWorld(serviceId: string, service: HelloWorldDef): void;
|
||||
export function registerHelloWorld(peer: IFluenceClient$$, service: HelloWorldDef): void;
|
||||
export function registerHelloWorld(peer: IFluenceClient$$, serviceId: string, service: HelloWorldDef): void;
|
||||
|
||||
// Functions
|
||||
export type ResourceTestResultType = [string | null, string[]]
|
||||
|
||||
export type ResourceTestParams = [label: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, label: string, config?: {ttl?: number}];
|
||||
|
||||
export type ResourceTestResult = Promise<ResourceTestResultType>;
|
||||
|
||||
export type HelloTestParams = [config?: {ttl?: number}] | [peer: IFluenceClient$$, config?: {ttl?: number}];
|
||||
|
||||
export type HelloTestResult = Promise<string>;
|
||||
|
||||
export type Demo_calculationParams = [service_id: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, service_id: string, config?: {ttl?: number}];
|
||||
|
||||
export type Demo_calculationResult = Promise<number>;
|
||||
|
||||
export type MarineTestParams = [wasm64: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, wasm64: string, config?: {ttl?: number}];
|
||||
|
||||
export type MarineTestResult = Promise<number>;
|
||||
|
@ -0,0 +1,900 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
/**
|
||||
*
|
||||
* This file is generated using:
|
||||
* @fluencelabs/aqua-api version: 0.0.0
|
||||
* @fluencelabs/aqua-to-js version: 0.0.0
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$
|
||||
} from '@fluencelabs/js-client';
|
||||
|
||||
// Services
|
||||
|
||||
export function registerSrv(...args) {
|
||||
registerService$$(
|
||||
args,
|
||||
{
|
||||
"defaultServiceId": "single_module_srv",
|
||||
"functions": {
|
||||
"fields": {
|
||||
"create": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"wasm_b64_content": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "ServiceCreationResult",
|
||||
"fields": {
|
||||
"error": {
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "option"
|
||||
},
|
||||
"service_id": {
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "option"
|
||||
},
|
||||
"success": {
|
||||
"name": "bool",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "struct"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"list": {
|
||||
"domain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "array"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"remove": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"service_id": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "RemoveResult",
|
||||
"fields": {
|
||||
"error": {
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "option"
|
||||
},
|
||||
"success": {
|
||||
"name": "bool",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "struct"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export function registerCalcService(...args) {
|
||||
registerService$$(
|
||||
args,
|
||||
{
|
||||
"functions": {
|
||||
"fields": {
|
||||
"divide": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"num": {
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"clear_state": {
|
||||
"domain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"codomain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"test_logs": {
|
||||
"domain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"codomain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"multiply": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"num": {
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"add": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"num": {
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"state": {
|
||||
"domain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"subtract": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"num": {
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export function registerHelloWorld(...args) {
|
||||
registerService$$(
|
||||
args,
|
||||
{
|
||||
"defaultServiceId": "hello-world",
|
||||
"functions": {
|
||||
"fields": {
|
||||
"hello": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"str": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Functions
|
||||
export const resourceTest_script = `
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(call %init_peer_id% ("getDataSrv" "label") [] -label-arg-)
|
||||
)
|
||||
(new $resource_id
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("peer" "timestamp_sec") [] ret)
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(call -relay- ("registry" "get_key_bytes") [-label-arg- [] ret [] ""] ret-0)
|
||||
(xor
|
||||
(call %init_peer_id% ("sig" "sign") [ret-0] ret-1)
|
||||
(fail :error:)
|
||||
)
|
||||
)
|
||||
(new -if-else-error-
|
||||
(new -else-error-
|
||||
(new -if-error-
|
||||
(xor
|
||||
(match ret-1.$.success false
|
||||
(ap ret-1.$.error.[0] $error)
|
||||
)
|
||||
(seq
|
||||
(ap :error: -if-error-)
|
||||
(xor
|
||||
(match :error:.$.error_code 10001
|
||||
(new $successful
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(ap ret-1.$.signature ret-1_flat)
|
||||
(call -relay- ("registry" "get_key_id") [-label-arg- %init_peer_id%] ret-2)
|
||||
)
|
||||
(call -relay- ("op" "string_to_b58") [ret-2] ret-3)
|
||||
)
|
||||
(call -relay- ("kad" "neighborhood") [ret-3 [] []] ret-4)
|
||||
)
|
||||
(par
|
||||
(fold ret-4 n-0
|
||||
(par
|
||||
(xor
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call n-0 ("peer" "timestamp_sec") [] ret-5)
|
||||
(call n-0 ("trust-graph" "get_weight") [%init_peer_id% ret-5] ret-6)
|
||||
)
|
||||
(call n-0 ("registry" "register_key") [-label-arg- [] ret [] "" ret-1_flat.$.[0] ret-6 ret-5] ret-7)
|
||||
)
|
||||
(new -if-else-error-
|
||||
(new -else-error-
|
||||
(new -if-error-
|
||||
(xor
|
||||
(seq
|
||||
(match ret-7.$.success true
|
||||
(ap true $successful)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(ap :error: -if-error-)
|
||||
(xor
|
||||
(seq
|
||||
(match :error:.$.error_code 10001
|
||||
(ap ret-7.$.error $error)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(ap :error: -else-error-)
|
||||
(xor
|
||||
(seq
|
||||
(match :error:.$.error_code 10001
|
||||
(ap -if-error- -if-else-error-)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(ap -else-error- -if-else-error-)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fail -if-else-error-)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(null)
|
||||
)
|
||||
(fail :error:)
|
||||
)
|
||||
(next n-0)
|
||||
)
|
||||
(never)
|
||||
)
|
||||
(null)
|
||||
)
|
||||
)
|
||||
(new $status
|
||||
(new $result-1
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(par
|
||||
(seq
|
||||
(new $successful_test
|
||||
(seq
|
||||
(seq
|
||||
(fold $successful successful_fold_var
|
||||
(seq
|
||||
(seq
|
||||
(ap successful_fold_var $successful_test)
|
||||
(canon -relay- $successful_test #successful_iter_canon)
|
||||
)
|
||||
(xor
|
||||
(match #successful_iter_canon.length 1
|
||||
(null)
|
||||
)
|
||||
(next successful_fold_var)
|
||||
)
|
||||
)
|
||||
(never)
|
||||
)
|
||||
(canon -relay- $successful_test #successful_result_canon)
|
||||
)
|
||||
(ap #successful_result_canon successful_gate)
|
||||
)
|
||||
)
|
||||
(ap "ok" $status)
|
||||
)
|
||||
(seq
|
||||
(call -relay- ("peer" "timeout") [6000 "timeout"] ret-8)
|
||||
(ap ret-8 $status)
|
||||
)
|
||||
)
|
||||
(new $status_test
|
||||
(seq
|
||||
(seq
|
||||
(fold $status status_fold_var
|
||||
(seq
|
||||
(seq
|
||||
(ap status_fold_var $status_test)
|
||||
(canon -relay- $status_test #status_iter_canon)
|
||||
)
|
||||
(xor
|
||||
(match #status_iter_canon.length 1
|
||||
(null)
|
||||
)
|
||||
(next status_fold_var)
|
||||
)
|
||||
)
|
||||
(never)
|
||||
)
|
||||
(canon -relay- $status_test #status_result_canon)
|
||||
)
|
||||
(ap #status_result_canon status_gate)
|
||||
)
|
||||
)
|
||||
)
|
||||
(new -if-else-error-
|
||||
(new -else-error-
|
||||
(new -if-error-
|
||||
(xor
|
||||
(match status_gate.$.[0] "ok"
|
||||
(ap true $result-1)
|
||||
)
|
||||
(seq
|
||||
(ap :error: -if-error-)
|
||||
(xor
|
||||
(match :error:.$.error_code 10001
|
||||
(ap false $result-1)
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(ap :error: -else-error-)
|
||||
(xor
|
||||
(match :error:.$.error_code 10001
|
||||
(ap -if-error- -if-else-error-)
|
||||
)
|
||||
(ap -else-error- -if-else-error-)
|
||||
)
|
||||
)
|
||||
(fail -if-else-error-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(new $result-1_test
|
||||
(seq
|
||||
(seq
|
||||
(fold $result-1 result-1_fold_var
|
||||
(seq
|
||||
(seq
|
||||
(ap result-1_fold_var $result-1_test)
|
||||
(canon -relay- $result-1_test #result-1_iter_canon)
|
||||
)
|
||||
(xor
|
||||
(match #result-1_iter_canon.length 1
|
||||
(null)
|
||||
)
|
||||
(next result-1_fold_var)
|
||||
)
|
||||
)
|
||||
(never)
|
||||
)
|
||||
(canon -relay- $result-1_test #result-1_result_canon)
|
||||
)
|
||||
(ap #result-1_result_canon result-1_gate)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(new -if-else-error-
|
||||
(new -else-error-
|
||||
(new -if-error-
|
||||
(xor
|
||||
(match result-1_gate.$.[0] false
|
||||
(ap "resource wasn't created: timeout exceeded" $error)
|
||||
)
|
||||
(seq
|
||||
(ap :error: -if-error-)
|
||||
(xor
|
||||
(match :error:.$.error_code 10001
|
||||
(ap ret-2 $resource_id)
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(ap :error: -else-error-)
|
||||
(xor
|
||||
(seq
|
||||
(match :error:.$.error_code 10001
|
||||
(ap -if-error- -if-else-error-)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(ap -else-error- -if-else-error-)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fail -if-else-error-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(ap :error: -else-error-)
|
||||
(xor
|
||||
(seq
|
||||
(match :error:.$.error_code 10001
|
||||
(ap -if-error- -if-else-error-)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(ap -else-error- -if-else-error-)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fail -if-else-error-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fail :error:)
|
||||
)
|
||||
)
|
||||
(canon %init_peer_id% $resource_id #-resource_id-fix-0)
|
||||
)
|
||||
(ap #-resource_id-fix-0 -resource_id-flat-0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(canon %init_peer_id% $error #error_canon)
|
||||
)
|
||||
(call %init_peer_id% ("callbackSrv" "response") [-resource_id-flat-0 #error_canon])
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0])
|
||||
)
|
||||
`;
|
||||
|
||||
|
||||
export function resourceTest(...args) {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
"functionName": "resourceTest",
|
||||
"arrow": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"label": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "option"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "array"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"names": {
|
||||
"relay": "-relay-",
|
||||
"getDataSrv": "getDataSrv",
|
||||
"callbackSrv": "callbackSrv",
|
||||
"responseSrv": "callbackSrv",
|
||||
"responseFnName": "response",
|
||||
"errorHandlingSrv": "errorHandlingSrv",
|
||||
"errorFnName": "error"
|
||||
}
|
||||
},
|
||||
resourceTest_script
|
||||
);
|
||||
}
|
||||
|
||||
export const helloTest_script = `
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(call %init_peer_id% ("hello-world" "hello") ["Fluence user"] ret)
|
||||
)
|
||||
(call %init_peer_id% ("callbackSrv" "response") [ret])
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0])
|
||||
)
|
||||
`;
|
||||
|
||||
|
||||
export function helloTest(...args) {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
"functionName": "helloTest",
|
||||
"arrow": {
|
||||
"domain": {
|
||||
"fields": {},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"names": {
|
||||
"relay": "-relay-",
|
||||
"getDataSrv": "getDataSrv",
|
||||
"callbackSrv": "callbackSrv",
|
||||
"responseSrv": "callbackSrv",
|
||||
"responseFnName": "response",
|
||||
"errorHandlingSrv": "errorHandlingSrv",
|
||||
"errorFnName": "error"
|
||||
}
|
||||
},
|
||||
helloTest_script
|
||||
);
|
||||
}
|
||||
|
||||
export const demo_calculation_script = `
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(call %init_peer_id% ("getDataSrv" "service_id") [] -service_id-arg-)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "test_logs") [])
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "add") [10] ret)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "multiply") [5] ret-0)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "subtract") [8] ret-1)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "divide") [6] ret-2)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "state") [] ret-3)
|
||||
)
|
||||
(call %init_peer_id% ("callbackSrv" "response") [ret-3])
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0])
|
||||
)
|
||||
`;
|
||||
|
||||
|
||||
export function demo_calculation(...args) {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
"functionName": "demo_calculation",
|
||||
"arrow": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"service_id": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"names": {
|
||||
"relay": "-relay-",
|
||||
"getDataSrv": "getDataSrv",
|
||||
"callbackSrv": "callbackSrv",
|
||||
"responseSrv": "callbackSrv",
|
||||
"responseFnName": "response",
|
||||
"errorHandlingSrv": "errorHandlingSrv",
|
||||
"errorFnName": "error"
|
||||
}
|
||||
},
|
||||
demo_calculation_script
|
||||
);
|
||||
}
|
||||
|
||||
export const marineTest_script = `
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(call %init_peer_id% ("getDataSrv" "wasm64") [] -wasm64-arg-)
|
||||
)
|
||||
(call %init_peer_id% ("single_module_srv" "create") [-wasm64-arg-] ret)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "test_logs") [])
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "add") [10] ret-0)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "multiply") [5] ret-1)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "subtract") [8] ret-2)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "divide") [6] ret-3)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "state") [] ret-4)
|
||||
)
|
||||
(call %init_peer_id% ("callbackSrv" "response") [ret-4])
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0])
|
||||
)
|
||||
`;
|
||||
|
||||
|
||||
export function marineTest(...args) {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
"functionName": "marineTest",
|
||||
"arrow": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"wasm64": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"names": {
|
||||
"relay": "-relay-",
|
||||
"getDataSrv": "getDataSrv",
|
||||
"callbackSrv": "callbackSrv",
|
||||
"responseSrv": "callbackSrv",
|
||||
"responseFnName": "response",
|
||||
"errorHandlingSrv": "errorHandlingSrv",
|
||||
"errorFnName": "error"
|
||||
}
|
||||
},
|
||||
marineTest_script
|
||||
);
|
||||
}
|
@ -0,0 +1,938 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
/**
|
||||
*
|
||||
* This file is generated using:
|
||||
* @fluencelabs/aqua-api version: 0.0.0
|
||||
* @fluencelabs/aqua-to-js version: 0.0.0
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
import type { IFluenceClient as IFluenceClient$$, ParticleContext as ParticleContext$$ } from '@fluencelabs/js-client';
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$
|
||||
} from '@fluencelabs/js-client';
|
||||
|
||||
// Services
|
||||
export interface SrvDef {
|
||||
create: (wasm_b64_content: string, callParams: ParticleContext$$) => { error: string | null; service_id: string | null; success: boolean; } | Promise<{ error: string | null; service_id: string | null; success: boolean; }>;
|
||||
list: (callParams: ParticleContext$$) => string[] | Promise<string[]>;
|
||||
remove: (service_id: string, callParams: ParticleContext$$) => { error: string | null; success: boolean; } | Promise<{ error: string | null; success: boolean; }>;
|
||||
}
|
||||
export function registerSrv(service: SrvDef): void;
|
||||
export function registerSrv(serviceId: string, service: SrvDef): void;
|
||||
export function registerSrv(peer: IFluenceClient$$, service: SrvDef): void;
|
||||
export function registerSrv(peer: IFluenceClient$$, serviceId: string, service: SrvDef): void;
|
||||
export function registerSrv(...args: any[]) {
|
||||
registerService$$(
|
||||
args,
|
||||
{
|
||||
"defaultServiceId": "single_module_srv",
|
||||
"functions": {
|
||||
"fields": {
|
||||
"create": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"wasm_b64_content": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "ServiceCreationResult",
|
||||
"fields": {
|
||||
"error": {
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "option"
|
||||
},
|
||||
"service_id": {
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "option"
|
||||
},
|
||||
"success": {
|
||||
"name": "bool",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "struct"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"list": {
|
||||
"domain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "array"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"remove": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"service_id": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "RemoveResult",
|
||||
"fields": {
|
||||
"error": {
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "option"
|
||||
},
|
||||
"success": {
|
||||
"name": "bool",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "struct"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export interface CalcServiceDef {
|
||||
divide: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
|
||||
clear_state: (callParams: ParticleContext$$) => void | Promise<void>;
|
||||
test_logs: (callParams: ParticleContext$$) => void | Promise<void>;
|
||||
multiply: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
|
||||
add: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
|
||||
state: (callParams: ParticleContext$$) => number | Promise<number>;
|
||||
subtract: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
|
||||
}
|
||||
export function registerCalcService(serviceId: string, service: CalcServiceDef): void;
|
||||
export function registerCalcService(peer: IFluenceClient$$, serviceId: string, service: CalcServiceDef): void;
|
||||
export function registerCalcService(...args: any[]) {
|
||||
registerService$$(
|
||||
args,
|
||||
{
|
||||
"functions": {
|
||||
"fields": {
|
||||
"divide": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"num": {
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"clear_state": {
|
||||
"domain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"codomain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"test_logs": {
|
||||
"domain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"codomain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"multiply": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"num": {
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"add": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"num": {
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"state": {
|
||||
"domain": {
|
||||
"tag": "nil"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"subtract": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"num": {
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export interface HelloWorldDef {
|
||||
hello: (str: string, callParams: ParticleContext$$) => string | Promise<string>;
|
||||
}
|
||||
export function registerHelloWorld(service: HelloWorldDef): void;
|
||||
export function registerHelloWorld(serviceId: string, service: HelloWorldDef): void;
|
||||
export function registerHelloWorld(peer: IFluenceClient$$, service: HelloWorldDef): void;
|
||||
export function registerHelloWorld(peer: IFluenceClient$$, serviceId: string, service: HelloWorldDef): void;
|
||||
export function registerHelloWorld(...args: any[]) {
|
||||
registerService$$(
|
||||
args,
|
||||
{
|
||||
"defaultServiceId": "hello-world",
|
||||
"functions": {
|
||||
"fields": {
|
||||
"hello": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"str": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Functions
|
||||
export const resourceTest_script = `
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(call %init_peer_id% ("getDataSrv" "label") [] -label-arg-)
|
||||
)
|
||||
(new $resource_id
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("peer" "timestamp_sec") [] ret)
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(call -relay- ("registry" "get_key_bytes") [-label-arg- [] ret [] ""] ret-0)
|
||||
(xor
|
||||
(call %init_peer_id% ("sig" "sign") [ret-0] ret-1)
|
||||
(fail :error:)
|
||||
)
|
||||
)
|
||||
(new -if-else-error-
|
||||
(new -else-error-
|
||||
(new -if-error-
|
||||
(xor
|
||||
(match ret-1.$.success false
|
||||
(ap ret-1.$.error.[0] $error)
|
||||
)
|
||||
(seq
|
||||
(ap :error: -if-error-)
|
||||
(xor
|
||||
(match :error:.$.error_code 10001
|
||||
(new $successful
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(ap ret-1.$.signature ret-1_flat)
|
||||
(call -relay- ("registry" "get_key_id") [-label-arg- %init_peer_id%] ret-2)
|
||||
)
|
||||
(call -relay- ("op" "string_to_b58") [ret-2] ret-3)
|
||||
)
|
||||
(call -relay- ("kad" "neighborhood") [ret-3 [] []] ret-4)
|
||||
)
|
||||
(par
|
||||
(fold ret-4 n-0
|
||||
(par
|
||||
(xor
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call n-0 ("peer" "timestamp_sec") [] ret-5)
|
||||
(call n-0 ("trust-graph" "get_weight") [%init_peer_id% ret-5] ret-6)
|
||||
)
|
||||
(call n-0 ("registry" "register_key") [-label-arg- [] ret [] "" ret-1_flat.$.[0] ret-6 ret-5] ret-7)
|
||||
)
|
||||
(new -if-else-error-
|
||||
(new -else-error-
|
||||
(new -if-error-
|
||||
(xor
|
||||
(seq
|
||||
(match ret-7.$.success true
|
||||
(ap true $successful)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(ap :error: -if-error-)
|
||||
(xor
|
||||
(seq
|
||||
(match :error:.$.error_code 10001
|
||||
(ap ret-7.$.error $error)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(ap :error: -else-error-)
|
||||
(xor
|
||||
(seq
|
||||
(match :error:.$.error_code 10001
|
||||
(ap -if-error- -if-else-error-)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(ap -else-error- -if-else-error-)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fail -if-else-error-)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(null)
|
||||
)
|
||||
(fail :error:)
|
||||
)
|
||||
(next n-0)
|
||||
)
|
||||
(never)
|
||||
)
|
||||
(null)
|
||||
)
|
||||
)
|
||||
(new $status
|
||||
(new $result-1
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(par
|
||||
(seq
|
||||
(new $successful_test
|
||||
(seq
|
||||
(seq
|
||||
(fold $successful successful_fold_var
|
||||
(seq
|
||||
(seq
|
||||
(ap successful_fold_var $successful_test)
|
||||
(canon -relay- $successful_test #successful_iter_canon)
|
||||
)
|
||||
(xor
|
||||
(match #successful_iter_canon.length 1
|
||||
(null)
|
||||
)
|
||||
(next successful_fold_var)
|
||||
)
|
||||
)
|
||||
(never)
|
||||
)
|
||||
(canon -relay- $successful_test #successful_result_canon)
|
||||
)
|
||||
(ap #successful_result_canon successful_gate)
|
||||
)
|
||||
)
|
||||
(ap "ok" $status)
|
||||
)
|
||||
(seq
|
||||
(call -relay- ("peer" "timeout") [6000 "timeout"] ret-8)
|
||||
(ap ret-8 $status)
|
||||
)
|
||||
)
|
||||
(new $status_test
|
||||
(seq
|
||||
(seq
|
||||
(fold $status status_fold_var
|
||||
(seq
|
||||
(seq
|
||||
(ap status_fold_var $status_test)
|
||||
(canon -relay- $status_test #status_iter_canon)
|
||||
)
|
||||
(xor
|
||||
(match #status_iter_canon.length 1
|
||||
(null)
|
||||
)
|
||||
(next status_fold_var)
|
||||
)
|
||||
)
|
||||
(never)
|
||||
)
|
||||
(canon -relay- $status_test #status_result_canon)
|
||||
)
|
||||
(ap #status_result_canon status_gate)
|
||||
)
|
||||
)
|
||||
)
|
||||
(new -if-else-error-
|
||||
(new -else-error-
|
||||
(new -if-error-
|
||||
(xor
|
||||
(match status_gate.$.[0] "ok"
|
||||
(ap true $result-1)
|
||||
)
|
||||
(seq
|
||||
(ap :error: -if-error-)
|
||||
(xor
|
||||
(match :error:.$.error_code 10001
|
||||
(ap false $result-1)
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(ap :error: -else-error-)
|
||||
(xor
|
||||
(match :error:.$.error_code 10001
|
||||
(ap -if-error- -if-else-error-)
|
||||
)
|
||||
(ap -else-error- -if-else-error-)
|
||||
)
|
||||
)
|
||||
(fail -if-else-error-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(new $result-1_test
|
||||
(seq
|
||||
(seq
|
||||
(fold $result-1 result-1_fold_var
|
||||
(seq
|
||||
(seq
|
||||
(ap result-1_fold_var $result-1_test)
|
||||
(canon -relay- $result-1_test #result-1_iter_canon)
|
||||
)
|
||||
(xor
|
||||
(match #result-1_iter_canon.length 1
|
||||
(null)
|
||||
)
|
||||
(next result-1_fold_var)
|
||||
)
|
||||
)
|
||||
(never)
|
||||
)
|
||||
(canon -relay- $result-1_test #result-1_result_canon)
|
||||
)
|
||||
(ap #result-1_result_canon result-1_gate)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(new -if-else-error-
|
||||
(new -else-error-
|
||||
(new -if-error-
|
||||
(xor
|
||||
(match result-1_gate.$.[0] false
|
||||
(ap "resource wasn't created: timeout exceeded" $error)
|
||||
)
|
||||
(seq
|
||||
(ap :error: -if-error-)
|
||||
(xor
|
||||
(match :error:.$.error_code 10001
|
||||
(ap ret-2 $resource_id)
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(ap :error: -else-error-)
|
||||
(xor
|
||||
(seq
|
||||
(match :error:.$.error_code 10001
|
||||
(ap -if-error- -if-else-error-)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(ap -else-error- -if-else-error-)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fail -if-else-error-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(ap :error: -else-error-)
|
||||
(xor
|
||||
(seq
|
||||
(match :error:.$.error_code 10001
|
||||
(ap -if-error- -if-else-error-)
|
||||
)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(ap -else-error- -if-else-error-)
|
||||
(new $-ephemeral-stream-
|
||||
(new #-ephemeral-canon-
|
||||
(canon -relay- $-ephemeral-stream- #-ephemeral-canon-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fail -if-else-error-)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fail :error:)
|
||||
)
|
||||
)
|
||||
(canon %init_peer_id% $resource_id #-resource_id-fix-0)
|
||||
)
|
||||
(ap #-resource_id-fix-0 -resource_id-flat-0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(canon %init_peer_id% $error #error_canon)
|
||||
)
|
||||
(call %init_peer_id% ("callbackSrv" "response") [-resource_id-flat-0 #error_canon])
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0])
|
||||
)
|
||||
`;
|
||||
|
||||
export type ResourceTestResultType = [string | null, string[]]
|
||||
|
||||
export type ResourceTestParams = [label: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, label: string, config?: {ttl?: number}];
|
||||
|
||||
export type ResourceTestResult = Promise<ResourceTestResultType>;
|
||||
|
||||
export function resourceTest(...args: ResourceTestParams): ResourceTestResult {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
"functionName": "resourceTest",
|
||||
"arrow": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"label": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "option"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
},
|
||||
"tag": "array"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"names": {
|
||||
"relay": "-relay-",
|
||||
"getDataSrv": "getDataSrv",
|
||||
"callbackSrv": "callbackSrv",
|
||||
"responseSrv": "callbackSrv",
|
||||
"responseFnName": "response",
|
||||
"errorHandlingSrv": "errorHandlingSrv",
|
||||
"errorFnName": "error"
|
||||
}
|
||||
},
|
||||
resourceTest_script
|
||||
);
|
||||
}
|
||||
|
||||
export const helloTest_script = `
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(call %init_peer_id% ("hello-world" "hello") ["Fluence user"] ret)
|
||||
)
|
||||
(call %init_peer_id% ("callbackSrv" "response") [ret])
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0])
|
||||
)
|
||||
`;
|
||||
|
||||
export type HelloTestParams = [config?: {ttl?: number}] | [peer: IFluenceClient$$, config?: {ttl?: number}];
|
||||
|
||||
export type HelloTestResult = Promise<string>;
|
||||
|
||||
export function helloTest(...args: HelloTestParams): HelloTestResult {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
"functionName": "helloTest",
|
||||
"arrow": {
|
||||
"domain": {
|
||||
"fields": {},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"names": {
|
||||
"relay": "-relay-",
|
||||
"getDataSrv": "getDataSrv",
|
||||
"callbackSrv": "callbackSrv",
|
||||
"responseSrv": "callbackSrv",
|
||||
"responseFnName": "response",
|
||||
"errorHandlingSrv": "errorHandlingSrv",
|
||||
"errorFnName": "error"
|
||||
}
|
||||
},
|
||||
helloTest_script
|
||||
);
|
||||
}
|
||||
|
||||
export const demo_calculation_script = `
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(call %init_peer_id% ("getDataSrv" "service_id") [] -service_id-arg-)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "test_logs") [])
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "add") [10] ret)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "multiply") [5] ret-0)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "subtract") [8] ret-1)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "divide") [6] ret-2)
|
||||
)
|
||||
(call %init_peer_id% (-service_id-arg- "state") [] ret-3)
|
||||
)
|
||||
(call %init_peer_id% ("callbackSrv" "response") [ret-3])
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0])
|
||||
)
|
||||
`;
|
||||
|
||||
export type Demo_calculationParams = [service_id: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, service_id: string, config?: {ttl?: number}];
|
||||
|
||||
export type Demo_calculationResult = Promise<number>;
|
||||
|
||||
export function demo_calculation(...args: Demo_calculationParams): Demo_calculationResult {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
"functionName": "demo_calculation",
|
||||
"arrow": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"service_id": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"names": {
|
||||
"relay": "-relay-",
|
||||
"getDataSrv": "getDataSrv",
|
||||
"callbackSrv": "callbackSrv",
|
||||
"responseSrv": "callbackSrv",
|
||||
"responseFnName": "response",
|
||||
"errorHandlingSrv": "errorHandlingSrv",
|
||||
"errorFnName": "error"
|
||||
}
|
||||
},
|
||||
demo_calculation_script
|
||||
);
|
||||
}
|
||||
|
||||
export const marineTest_script = `
|
||||
(xor
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(call %init_peer_id% ("getDataSrv" "wasm64") [] -wasm64-arg-)
|
||||
)
|
||||
(call %init_peer_id% ("single_module_srv" "create") [-wasm64-arg-] ret)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "test_logs") [])
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "add") [10] ret-0)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "multiply") [5] ret-1)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "subtract") [8] ret-2)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "divide") [6] ret-3)
|
||||
)
|
||||
(call %init_peer_id% (ret.$.service_id.[0] "state") [] ret-4)
|
||||
)
|
||||
(call %init_peer_id% ("callbackSrv" "response") [ret-4])
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0])
|
||||
)
|
||||
`;
|
||||
|
||||
export type MarineTestParams = [wasm64: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, wasm64: string, config?: {ttl?: number}];
|
||||
|
||||
export type MarineTestResult = Promise<number>;
|
||||
|
||||
export function marineTest(...args: MarineTestParams): MarineTestResult {
|
||||
return callFunction$$(
|
||||
args,
|
||||
{
|
||||
"functionName": "marineTest",
|
||||
"arrow": {
|
||||
"domain": {
|
||||
"fields": {
|
||||
"wasm64": {
|
||||
"name": "string",
|
||||
"tag": "scalar"
|
||||
}
|
||||
},
|
||||
"tag": "labeledProduct"
|
||||
},
|
||||
"codomain": {
|
||||
"items": [
|
||||
{
|
||||
"name": "f64",
|
||||
"tag": "scalar"
|
||||
}
|
||||
],
|
||||
"tag": "unlabeledProduct"
|
||||
},
|
||||
"tag": "arrow"
|
||||
},
|
||||
"names": {
|
||||
"relay": "-relay-",
|
||||
"getDataSrv": "getDataSrv",
|
||||
"callbackSrv": "callbackSrv",
|
||||
"responseSrv": "callbackSrv",
|
||||
"responseFnName": "response",
|
||||
"errorHandlingSrv": "errorHandlingSrv",
|
||||
"errorFnName": "error"
|
||||
}
|
||||
},
|
||||
marineTest_script
|
||||
);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -14,46 +14,55 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import url from "url";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { compileFromPath } from "@fluencelabs/aqua-api";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
|
||||
import { getPackageJsonContent, PackageJson } from "../../utils.js";
|
||||
import { generateTypes, generateSources } from "../index.js";
|
||||
import { CompilationResult } from "../interfaces.js";
|
||||
|
||||
let res: Omit<CompilationResult, "funcCall">;
|
||||
let pkg: PackageJson;
|
||||
|
||||
describe("Aqua to js/ts compiler", () => {
|
||||
it("compiles smoke tests successfully", async () => {
|
||||
const res = await compileFromPath({
|
||||
filePath: url.fileURLToPath(
|
||||
beforeAll(async () => {
|
||||
res = await compileFromPath({
|
||||
filePath: fileURLToPath(
|
||||
new URL("./sources/smoke_test.aqua", import.meta.url),
|
||||
),
|
||||
imports: ["./node_modules"],
|
||||
targetType: "air",
|
||||
});
|
||||
|
||||
const pkg: PackageJson = {
|
||||
pkg = {
|
||||
...(await getPackageJsonContent()),
|
||||
version: "0.0.0",
|
||||
devDependencies: {
|
||||
"@fluencelabs/aqua-api": "0.0.0",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// TODO: see https://github.com/fluencelabs/js-client/pull/366#discussion_r1370567711
|
||||
// @ts-expect-error don't use compileFromPath directly here
|
||||
it("matches js snapshots", async () => {
|
||||
const jsResult = generateSources(res, "js", pkg);
|
||||
// TODO: see https://github.com/fluencelabs/js-client/pull/366#discussion_r1370567711
|
||||
// @ts-expect-error don't use compileFromPath directly here
|
||||
const jsTypes = generateTypes(res, pkg);
|
||||
|
||||
expect(jsResult).toMatchSnapshot();
|
||||
expect(jsTypes).toMatchSnapshot();
|
||||
await expect(jsResult).toMatchFileSnapshot(
|
||||
"./__snapshots__/generate.snap.js",
|
||||
);
|
||||
|
||||
// TODO: see https://github.com/fluencelabs/js-client/pull/366#discussion_r1370567711
|
||||
// @ts-expect-error don't use compileFromPath directly here
|
||||
await expect(jsTypes).toMatchFileSnapshot(
|
||||
"./__snapshots__/generate.snap.d.ts",
|
||||
);
|
||||
});
|
||||
|
||||
it("matches ts snapshots", async () => {
|
||||
const tsResult = generateSources(res, "ts", pkg);
|
||||
|
||||
expect(tsResult).toMatchSnapshot();
|
||||
await expect(tsResult).toMatchFileSnapshot(
|
||||
"./__snapshots__/generate.snap.ts",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { recursiveRenameLaquaProps } from "../utils.js";
|
||||
import { capitalize, recursiveRenameLaquaProps } from "../utils.js";
|
||||
|
||||
import { AquaFunction, TypeGenerator } from "./interfaces.js";
|
||||
|
||||
@ -40,8 +40,11 @@ ${func.script}\`;
|
||||
${typeGenerator.funcType(func)}
|
||||
export function ${func.funcDef.functionName}(${typeGenerator.type(
|
||||
"...args",
|
||||
"any[]",
|
||||
)}) {
|
||||
`${capitalize(func.funcDef.functionName)}Params`,
|
||||
)})${typeGenerator.type(
|
||||
"",
|
||||
`${capitalize(func.funcDef.functionName)}Result`,
|
||||
)} {
|
||||
return callFunction$$(
|
||||
args,
|
||||
${JSON.stringify(recursiveRenameLaquaProps(funcDef), null, 4)},
|
||||
|
@ -35,12 +35,13 @@ export default function generateHeader(
|
||||
*/
|
||||
${
|
||||
outputType === "ts"
|
||||
? "import type { IFluenceClient as IFluenceClient$$, CallParams as CallParams$$ } from '@fluencelabs/js-client';"
|
||||
? "import type { IFluenceClient as IFluenceClient$$, ParticleContext as ParticleContext$$ } from '@fluencelabs/js-client';"
|
||||
: ""
|
||||
}
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$,
|
||||
v5_registerService as registerService$$
|
||||
} from '@fluencelabs/js-client';`;
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ export class TSTypeGenerator implements TypeGenerator {
|
||||
args.push([undefined, `config?: {ttl?: number}`]);
|
||||
|
||||
const argsDefs = args.map(([, def]) => {
|
||||
return " " + def;
|
||||
return def;
|
||||
});
|
||||
|
||||
const argsDesc = args
|
||||
@ -66,28 +66,30 @@ export class TSTypeGenerator implements TypeGenerator {
|
||||
});
|
||||
|
||||
const functionOverloads = [
|
||||
argsDefs.join(",\n"),
|
||||
[` peer: ${CLIENT}`, ...argsDefs].join(",\n"),
|
||||
argsDefs.join(", "),
|
||||
[`peer: ${CLIENT}`, ...argsDefs].join(", "),
|
||||
];
|
||||
|
||||
const [resTypeDesc, resType] = genTypeName(
|
||||
funcDef.arrow.codomain,
|
||||
capitalize(funcDef.functionName) + "Result",
|
||||
capitalize(funcDef.functionName) + "ResultType",
|
||||
);
|
||||
|
||||
const functionOverloadArgsType = functionOverloads
|
||||
.map((overload) => {
|
||||
return `[${overload}]`;
|
||||
})
|
||||
.join(" | ");
|
||||
|
||||
return [
|
||||
argsDesc.join("\n"),
|
||||
resTypeDesc ?? "",
|
||||
functionOverloads
|
||||
.flatMap((fo) => {
|
||||
return [
|
||||
`export function ${funcDef.functionName}(`,
|
||||
fo,
|
||||
`): Promise<${resType}>;`,
|
||||
"",
|
||||
];
|
||||
})
|
||||
.join("\n"),
|
||||
`export type ${capitalize(
|
||||
funcDef.functionName,
|
||||
)}Params = ${functionOverloadArgsType};`,
|
||||
`export type ${capitalize(
|
||||
funcDef.functionName,
|
||||
)}Result = Promise<${resType}>;\n`,
|
||||
]
|
||||
.filter((s) => {
|
||||
return s !== "";
|
||||
@ -117,13 +119,23 @@ export class TSTypeGenerator implements TypeGenerator {
|
||||
const serviceDecl = `service: ${srvName}Def`;
|
||||
const serviceIdDecl = `serviceId: string`;
|
||||
|
||||
const registerServiceArgs = [
|
||||
const functionOverloadsWithDefaultServiceId = [
|
||||
[serviceDecl],
|
||||
[serviceIdDecl, serviceDecl],
|
||||
[peerDecl, serviceDecl],
|
||||
[peerDecl, serviceIdDecl, serviceDecl],
|
||||
];
|
||||
|
||||
const functionOverloadsWithoutDefaultServiceId = [
|
||||
[serviceIdDecl, serviceDecl],
|
||||
[peerDecl, serviceIdDecl, serviceDecl],
|
||||
];
|
||||
|
||||
const registerServiceArgs =
|
||||
srvDef.defaultServiceId === undefined
|
||||
? functionOverloadsWithoutDefaultServiceId
|
||||
: functionOverloadsWithDefaultServiceId;
|
||||
|
||||
return [
|
||||
interfaces,
|
||||
...registerServiceArgs.map((registerServiceArg) => {
|
||||
|
@ -14,16 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ServiceDef } from "@fluencelabs/interfaces";
|
||||
import { JSONValue, ServiceDef } from "@fluencelabs/interfaces";
|
||||
|
||||
import { recursiveRenameLaquaProps } from "../utils.js";
|
||||
|
||||
import { TypeGenerator } from "./interfaces.js";
|
||||
|
||||
interface DefaultServiceId {
|
||||
s_Some__f_value?: string;
|
||||
}
|
||||
|
||||
export function generateServices(
|
||||
typeGenerator: TypeGenerator,
|
||||
services: Record<string, ServiceDef>,
|
||||
@ -67,21 +63,6 @@ function generateRegisterServiceOverload(
|
||||
}
|
||||
|
||||
function serviceToJson(service: ServiceDef): string {
|
||||
return JSON.stringify(
|
||||
{
|
||||
// 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 != null
|
||||
? {
|
||||
defaultServiceId:
|
||||
// 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,
|
||||
);
|
||||
const record: Record<never, JSONValue> = service;
|
||||
return JSON.stringify(recursiveRenameLaquaProps(record), null, 4);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { generateSources, generateTypes } from "./generate/index.js";
|
||||
import { CompilationResult, OutputType } from "./generate/interfaces.js";
|
||||
import { CompilationResult } from "./generate/interfaces.js";
|
||||
import { getPackageJsonContent } from "./utils.js";
|
||||
|
||||
interface JsOutput {
|
||||
@ -27,17 +27,22 @@ interface TsOutput {
|
||||
sources: string;
|
||||
}
|
||||
|
||||
type LanguageOutput = {
|
||||
js: JsOutput;
|
||||
ts: TsOutput;
|
||||
};
|
||||
|
||||
type NothingToGenerate = null;
|
||||
|
||||
export default async function aquaToJs<T extends OutputType>(
|
||||
export default async function aquaToJs(
|
||||
res: CompilationResult,
|
||||
outputType: T,
|
||||
): Promise<LanguageOutput[T] | NothingToGenerate> {
|
||||
outputType: "js",
|
||||
): Promise<JsOutput | NothingToGenerate>;
|
||||
|
||||
export default async function aquaToJs(
|
||||
res: CompilationResult,
|
||||
outputType: "ts",
|
||||
): Promise<TsOutput | NothingToGenerate>;
|
||||
|
||||
export default async function aquaToJs(
|
||||
res: CompilationResult,
|
||||
outputType: "js" | "ts",
|
||||
): Promise<JsOutput | TsOutput | NothingToGenerate> {
|
||||
if (
|
||||
Object.keys(res.services).length === 0 &&
|
||||
Object.keys(res.functions).length === 0
|
||||
@ -47,14 +52,14 @@ export default async function aquaToJs<T extends OutputType>(
|
||||
|
||||
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]);
|
||||
if (outputType === "js") {
|
||||
return {
|
||||
sources: generateSources(res, "js", packageJson),
|
||||
types: generateTypes(res, packageJson),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
sources: generateSources(res, "ts", packageJson),
|
||||
};
|
||||
}
|
||||
|
@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
import { readFile } from "fs/promises";
|
||||
import path from "path";
|
||||
import { join } from "path";
|
||||
|
||||
import {
|
||||
ArrowType,
|
||||
@ -27,24 +26,26 @@ import {
|
||||
SimpleTypes,
|
||||
UnlabeledProductType,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { z } from "zod";
|
||||
|
||||
export interface PackageJson {
|
||||
name: string;
|
||||
version: string;
|
||||
devDependencies: {
|
||||
["@fluencelabs/aqua-api"]: string;
|
||||
};
|
||||
}
|
||||
const packageJsonSchema = z.object({
|
||||
name: z.string(),
|
||||
version: z.string(),
|
||||
devDependencies: z.object({
|
||||
// @fluencelabs/aqua-api version is included as part of the comment at the top of each js and ts file
|
||||
["@fluencelabs/aqua-api"]: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type PackageJson = z.infer<typeof packageJsonSchema>;
|
||||
|
||||
export async function getPackageJsonContent(): Promise<PackageJson> {
|
||||
const content = await readFile(
|
||||
new URL(path.join("..", "package.json"), import.meta.url),
|
||||
new URL(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;
|
||||
return packageJsonSchema.parse(JSON.parse(content));
|
||||
}
|
||||
|
||||
export function getFuncArgs(
|
||||
@ -84,7 +85,7 @@ export function recursiveRenameLaquaProps(obj: JSONValue): unknown {
|
||||
// Last part of the property separated by "_" is a correct name
|
||||
const refinedProperty = prop.split("_").pop();
|
||||
|
||||
if (refinedProperty == null) {
|
||||
if (refinedProperty === undefined) {
|
||||
throw new Error(`Bad property name: ${prop}.`);
|
||||
}
|
||||
|
||||
@ -93,11 +94,15 @@ export function recursiveRenameLaquaProps(obj: JSONValue): unknown {
|
||||
}
|
||||
}
|
||||
|
||||
assert(accessProp in obj);
|
||||
const laquaProp = obj[accessProp];
|
||||
|
||||
if (laquaProp === undefined) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[accessProp]: recursiveRenameLaquaProps(obj[accessProp]),
|
||||
[accessProp]: recursiveRenameLaquaProps(laquaProp),
|
||||
};
|
||||
}, {});
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
|
@ -1,5 +1,71 @@
|
||||
# Changelog
|
||||
|
||||
## [0.12.0](https://github.com/fluencelabs/js-client/compare/interfaces-v0.11.0...interfaces-v0.12.0) (2024-02-23)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **deps:** Update avm to 0.62 ([#438](https://github.com/fluencelabs/js-client/issues/438))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** Update avm to 0.62 ([#438](https://github.com/fluencelabs/js-client/issues/438)) ([702ad60](https://github.com/fluencelabs/js-client/commit/702ad605a8e9217f66d3992f31ae8461283ff0b1))
|
||||
|
||||
## [0.11.0](https://github.com/fluencelabs/js-client/compare/interfaces-v0.10.0...interfaces-v0.11.0) (2024-01-26)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.59.0 #423
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.59.0 [#423](https://github.com/fluencelabs/js-client/issues/423) ([e21ecc1](https://github.com/fluencelabs/js-client/commit/e21ecc1edec5f34f2a56726eb62833774f814fef))
|
||||
|
||||
## [0.10.0](https://github.com/fluencelabs/js-client/compare/interfaces-v0.9.0...interfaces-v0.10.0) (2024-01-19)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.55.0 ([#407](https://github.com/fluencelabs/js-client/issues/407))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.55.0 ([5d7ae85](https://github.com/fluencelabs/js-client/commit/5d7ae85e585b8ce1d89f347a0a31d2212fc5a792))
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.55.0 ([#407](https://github.com/fluencelabs/js-client/issues/407)) ([5d7ae85](https://github.com/fluencelabs/js-client/commit/5d7ae85e585b8ce1d89f347a0a31d2212fc5a792))
|
||||
|
||||
## [0.9.0](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.2...interfaces-v0.9.0) (2023-11-23)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* Force release of interfaces ([#388](https://github.com/fluencelabs/js-client/issues/388))
|
||||
* **js-client:** Segregation of responsibility between js-client packages [fixes DXJ-525] ([#378](https://github.com/fluencelabs/js-client/issues/378))
|
||||
* Bump avm ([#361](https://github.com/fluencelabs/js-client/issues/361))
|
||||
* **js-client:** Adding strictes eslint and ts config to all packages [fixes DXJ-464] ([#355](https://github.com/fluencelabs/js-client/issues/355))
|
||||
|
||||
### 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))
|
||||
* Force release of interfaces ([#388](https://github.com/fluencelabs/js-client/issues/388)) ([04c278b](https://github.com/fluencelabs/js-client/commit/04c278b7830aaae5bd83194511de3f942ddd4955))
|
||||
* **js-client:** Adding strictes eslint and ts config to all packages [fixes DXJ-464] ([#355](https://github.com/fluencelabs/js-client/issues/355)) ([919c7d6](https://github.com/fluencelabs/js-client/commit/919c7d6ea1e9c153ff7a367873c85fb36624125d))
|
||||
* **js-client:** Segregation of responsibility between js-client packages [fixes DXJ-525] ([#378](https://github.com/fluencelabs/js-client/issues/378)) ([f4a550d](https://github.com/fluencelabs/js-client/commit/f4a550dd226846dfc2ade1ccc35a286dc3be2fed))
|
||||
* 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
|
||||
|
||||
* **deps:** Bump avm to 0.54 ([14e91b6](https://github.com/fluencelabs/js-client/commit/14e91b6e00e625792051aee2c82651e5679e3575))
|
||||
* **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.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.48.0 ([#350](https://github.com/fluencelabs/js-client/issues/350)) ([945908a](https://github.com/fluencelabs/js-client/commit/945908a992976f2ad953bcaa3918741f890ffeeb))
|
||||
* **tests:** Repair integration tests [fixes DXJ-506] ([#364](https://github.com/fluencelabs/js-client/issues/364)) ([36c7619](https://github.com/fluencelabs/js-client/commit/36c7619b4a1e8e2426aaf5592a14e96dafefb273))
|
||||
|
||||
|
||||
### Miscellaneous Chores
|
||||
|
||||
* Bump avm ([#361](https://github.com/fluencelabs/js-client/issues/361)) ([29ec812](https://github.com/fluencelabs/js-client/commit/29ec812fc1c5ee812cceb4034776b344e5cadfe5))
|
||||
|
||||
## [0.8.2](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.1...interfaces-v0.8.2) (2023-08-24)
|
||||
|
||||
### Features
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@fluencelabs/interfaces",
|
||||
"type": "module",
|
||||
"version": "0.8.2",
|
||||
"version": "0.12.0",
|
||||
"description": "Interfaces",
|
||||
"main": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
@ -49,8 +49,6 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@multiformats/multiaddr": "11.3.0",
|
||||
"@fluencelabs/avm": "0.54.0",
|
||||
"hotscript": "1.0.13"
|
||||
}
|
||||
}
|
||||
|
@ -14,60 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { SecurityTetraplet } from "@fluencelabs/avm";
|
||||
|
||||
import { InterfaceToType, MaybePromise } from "./utils.js";
|
||||
|
||||
/**
|
||||
* Peer ID's id as a base58 string (multihash/CIDv0).
|
||||
*/
|
||||
export type PeerIdB58 = string;
|
||||
|
||||
/**
|
||||
* Additional information about a service call
|
||||
* @typeparam ArgName
|
||||
*/
|
||||
export type CallParams<ArgName extends string | null> = {
|
||||
/**
|
||||
* The identifier of particle which triggered the call
|
||||
*/
|
||||
particleId: string;
|
||||
|
||||
/**
|
||||
* The peer id which created the particle
|
||||
*/
|
||||
initPeerId: PeerIdB58;
|
||||
|
||||
/**
|
||||
* Particle's timestamp when it was created
|
||||
*/
|
||||
timestamp: number;
|
||||
|
||||
/**
|
||||
* Time to live in milliseconds. The time after the particle should be expired
|
||||
*/
|
||||
ttl: number;
|
||||
|
||||
/**
|
||||
* Particle's signature
|
||||
*/
|
||||
signature?: string;
|
||||
|
||||
/**
|
||||
* Security tetraplets
|
||||
*/
|
||||
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
|
||||
@ -75,5 +26,5 @@ export type JSONValue =
|
||||
| null
|
||||
| { [x: string]: JSONValue }
|
||||
| Array<JSONValue>;
|
||||
export type JSONArray = Array<JSONValue>;
|
||||
export type JSONObject = { [x: string]: JSONValue };
|
||||
export type JSONArray = Array<JSONValue>;
|
||||
|
@ -25,6 +25,11 @@ export type SimpleTypes =
|
||||
|
||||
export type NonArrowType = SimpleTypes | ProductType;
|
||||
|
||||
export type NonArrowSimpleType =
|
||||
| SimpleTypes
|
||||
| UnlabeledProductType
|
||||
| LabeledProductType<SimpleTypes>;
|
||||
|
||||
export type TopType = {
|
||||
/**
|
||||
* Type descriptor. Used for pattern-matching
|
||||
@ -154,7 +159,13 @@ export type ProductType = UnlabeledProductType | LabeledProductType;
|
||||
* ArrowType is a profunctor pointing its domain to codomain.
|
||||
* Profunctor means variance: Arrow is contravariant on domain, and variant on codomain.
|
||||
*/
|
||||
export type ArrowType<T extends LabeledProductType | UnlabeledProductType> = {
|
||||
export type ArrowType<
|
||||
T extends
|
||||
| LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||
| UnlabeledProductType =
|
||||
| LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||
| UnlabeledProductType,
|
||||
> = {
|
||||
/**
|
||||
* Type descriptor. Used for pattern-matching
|
||||
*/
|
||||
@ -174,14 +185,14 @@ export type ArrowType<T extends LabeledProductType | UnlabeledProductType> = {
|
||||
/**
|
||||
* Arrow which domain contains only non-arrow types
|
||||
*/
|
||||
export type ArrowWithoutCallbacks = ArrowType<
|
||||
UnlabeledProductType | LabeledProductType<SimpleTypes>
|
||||
>;
|
||||
export type ArrowWithoutCallbacks = ArrowType<UnlabeledProductType>;
|
||||
|
||||
/**
|
||||
* Arrow which domain does can contain both non-arrow types and arrows (which themselves cannot contain arrows)
|
||||
*/
|
||||
export type ArrowWithCallbacks = ArrowType<LabeledProductType>;
|
||||
export type ArrowWithCallbacks = ArrowType<
|
||||
LabeledProductType<SimpleTypes | ArrowWithoutCallbacks>
|
||||
>;
|
||||
|
||||
export interface FunctionCallConstants {
|
||||
/**
|
||||
@ -232,9 +243,7 @@ export interface FunctionCallDef {
|
||||
/**
|
||||
* Underlying arrow which represents function in aqua
|
||||
*/
|
||||
arrow: ArrowType<
|
||||
LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||
>;
|
||||
arrow: ArrowWithCallbacks;
|
||||
|
||||
/**
|
||||
* Names of the different entities used in generated air script
|
||||
@ -255,37 +264,8 @@ export interface ServiceDef {
|
||||
* List of functions which the service consists of
|
||||
*/
|
||||
functions:
|
||||
| LabeledProductType<ArrowType<LabeledProductType<SimpleTypes>>>
|
||||
| LabeledProductType<
|
||||
ArrowType<LabeledProductType<SimpleTypes> | UnlabeledProductType>
|
||||
>
|
||||
| NilType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options to configure Aqua function execution
|
||||
*/
|
||||
export interface FnConfig {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
ttl?: number;
|
||||
}
|
||||
|
||||
export const getArgumentTypes = (
|
||||
def: FunctionCallDef,
|
||||
): {
|
||||
[key: string]: NonArrowType | ArrowWithoutCallbacks;
|
||||
} => {
|
||||
if (def.arrow.domain.tag !== "labeledProduct") {
|
||||
throw new Error("Should be impossible");
|
||||
}
|
||||
|
||||
return def.arrow.domain.fields;
|
||||
};
|
||||
|
||||
export const isReturnTypeVoid = (def: FunctionCallDef): boolean => {
|
||||
if (def.arrow.codomain.tag === "nil") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return def.arrow.codomain.items.length === 0;
|
||||
};
|
||||
|
@ -1,103 +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 { JSONValue } from "../commonTypes.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
|
||||
*/
|
||||
export type PassedArgs = { [key: string]: JSONValue | ArgCallbackFunction };
|
||||
|
||||
/**
|
||||
* Arguments for callAquaFunction function
|
||||
*/
|
||||
// TODO: move to js-client side
|
||||
export interface CallAquaFunctionArgs {
|
||||
/**
|
||||
* Peer to call the function on
|
||||
*/
|
||||
peer: unknown;
|
||||
|
||||
/**
|
||||
* Function definition
|
||||
*/
|
||||
def: FunctionCallDef;
|
||||
|
||||
/**
|
||||
* Air script used by the aqua function
|
||||
*/
|
||||
script: string;
|
||||
|
||||
/**
|
||||
* Function configuration
|
||||
*/
|
||||
config: FnConfig;
|
||||
|
||||
/**
|
||||
* Arguments to pass to the function
|
||||
*/
|
||||
args: PassedArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a function from Aqua script
|
||||
*/
|
||||
export type CallAquaFunctionType = (
|
||||
args: CallAquaFunctionArgs,
|
||||
) => Promise<unknown>;
|
||||
|
||||
/**
|
||||
* Arguments for registerService function
|
||||
*/
|
||||
export interface RegisterServiceArgs {
|
||||
/**
|
||||
* Peer to register the service on
|
||||
*/
|
||||
peer: unknown;
|
||||
|
||||
/**
|
||||
* Service definition
|
||||
*/
|
||||
def: ServiceDef;
|
||||
|
||||
/**
|
||||
* Service id
|
||||
*/
|
||||
serviceId: string | undefined;
|
||||
|
||||
/**
|
||||
* Service implementation
|
||||
*/
|
||||
service: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a service defined in Aqua on a Fluence peer
|
||||
*/
|
||||
export type RegisterServiceType = (args: RegisterServiceArgs) => void;
|
@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Call, Pipe, Objects, Tuples, Unions, Fn } from "hotscript";
|
||||
|
||||
import {
|
||||
ArrayType,
|
||||
ArrowType,
|
||||
@ -25,8 +27,7 @@ import {
|
||||
StructType,
|
||||
TopType,
|
||||
UnlabeledProductType,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { Call, Pipe, Objects, Tuples, Unions, Fn } from "hotscript";
|
||||
} from "./compilerSupport/aquaTypeDefinitions.js";
|
||||
|
||||
// Type definitions for inferring ts types from air json definition
|
||||
// In the future we may remove string type declaration and move to type inference.
|
||||
|
@ -15,6 +15,5 @@
|
||||
*/
|
||||
|
||||
export * from "./compilerSupport/aquaTypeDefinitions.js";
|
||||
export * from "./compilerSupport/compilerSupportInterface.js";
|
||||
export * from "./commonTypes.js";
|
||||
export * from "./future.js";
|
||||
|
1
packages/core/js-client-isomorphic/.gitignore
vendored
Normal file
1
packages/core/js-client-isomorphic/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
src/versions.ts
|
@ -1,5 +1,93 @@
|
||||
# Changelog
|
||||
|
||||
## [0.6.0](https://github.com/fluencelabs/js-client/compare/js-client-isomorphic-v0.5.0...js-client-isomorphic-v0.6.0) (2024-02-23)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **deps:** Update avm to 0.62 ([#438](https://github.com/fluencelabs/js-client/issues/438))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** Update avm to 0.62 ([#438](https://github.com/fluencelabs/js-client/issues/438)) ([702ad60](https://github.com/fluencelabs/js-client/commit/702ad605a8e9217f66d3992f31ae8461283ff0b1))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/marine-worker bumped from 0.5.1 to 0.6.0
|
||||
|
||||
## [0.5.0](https://github.com/fluencelabs/js-client/compare/js-client-isomorphic-v0.4.0...js-client-isomorphic-v0.5.0) (2024-01-26)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.59.0 #423
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.59.0 [#423](https://github.com/fluencelabs/js-client/issues/423) ([e21ecc1](https://github.com/fluencelabs/js-client/commit/e21ecc1edec5f34f2a56726eb62833774f814fef))
|
||||
|
||||
## [0.4.0](https://github.com/fluencelabs/js-client/compare/js-client-isomorphic-v0.3.1...js-client-isomorphic-v0.4.0) (2024-01-19)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.55.0 ([#407](https://github.com/fluencelabs/js-client/issues/407))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.55.0 ([5d7ae85](https://github.com/fluencelabs/js-client/commit/5d7ae85e585b8ce1d89f347a0a31d2212fc5a792))
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.55.0 ([#407](https://github.com/fluencelabs/js-client/issues/407)) ([5d7ae85](https://github.com/fluencelabs/js-client/commit/5d7ae85e585b8ce1d89f347a0a31d2212fc5a792))
|
||||
* Enable async loading of all dependency resources ([#408](https://github.com/fluencelabs/js-client/issues/408)) ([f5425b4](https://github.com/fluencelabs/js-client/commit/f5425b4746f436f84a41bae6584adb8b200ba33d))
|
||||
|
||||
## [0.3.1](https://github.com/fluencelabs/js-client/compare/js-client-isomorphic-v0.3.0...js-client-isomorphic-v0.3.1) (2023-12-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **npm-aqua-compiler:** create package ([#401](https://github.com/fluencelabs/js-client/issues/401)) ([d600811](https://github.com/fluencelabs/js-client/commit/d6008110cf0ecaf23a63cfef0bb3f786a6eb0937))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/marine-worker bumped from 0.5.0 to 0.5.1
|
||||
|
||||
## [0.3.0](https://github.com/fluencelabs/js-client/compare/js-client-isomorphic-v0.2.2...js-client-isomorphic-v0.3.0) (2023-11-22)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **js-client:** Segregation of responsibility between js-client packages [fixes DXJ-525] ([#378](https://github.com/fluencelabs/js-client/issues/378))
|
||||
|
||||
### Features
|
||||
|
||||
* **js-client:** Segregation of responsibility between js-client packages [fixes DXJ-525] ([#378](https://github.com/fluencelabs/js-client/issues/378)) ([f4a550d](https://github.com/fluencelabs/js-client/commit/f4a550dd226846dfc2ade1ccc35a286dc3be2fed))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/marine-worker bumped from 0.4.2 to 0.5.0
|
||||
|
||||
## [0.2.2](https://github.com/fluencelabs/js-client/compare/js-client-isomorphic-v0.2.1...js-client-isomorphic-v0.2.2) (2023-11-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* JS-client bugs and tech debt [fixes DXJ-520] ([#374](https://github.com/fluencelabs/js-client/issues/374)) ([b460491](https://github.com/fluencelabs/js-client/commit/b460491fbd0d07e3507a6c70f162014580c6d6da))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/marine-worker bumped from 0.4.1 to 0.4.2
|
||||
|
||||
## [0.2.1](https://github.com/fluencelabs/js-client/compare/js-client-isomorphic-v0.2.0...js-client-isomorphic-v0.2.1) (2023-10-30)
|
||||
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@fluencelabs/js-client-isomorphic",
|
||||
"version": "0.2.1",
|
||||
"version": "0.6.0",
|
||||
"description": "Isomorphic entities for js-client",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
"build": "tsc",
|
||||
"prepare": "node createVersionFile.js"
|
||||
},
|
||||
"exports": {
|
||||
".": "./dist/types.js",
|
||||
@ -22,9 +22,9 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluencelabs/avm": "0.54.0",
|
||||
"@fluencelabs/marine-js": "0.7.2",
|
||||
"@fluencelabs/marine-worker": "workspace:*",
|
||||
"@fluencelabs/avm": "0.62.0",
|
||||
"@fluencelabs/marine-js": "0.13.0",
|
||||
"@fluencelabs/marine-worker": "0.6.0",
|
||||
"@fluencelabs/threads": "^2.0.0"
|
||||
},
|
||||
"keywords": [],
|
||||
|
@ -14,25 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { VersionedPackage } from "../types.js";
|
||||
import { FetchResourceFn, getVersionedPackage } from "../types.js";
|
||||
|
||||
/**
|
||||
* @param pkg name of package with version
|
||||
* @param assetPath path of required asset in given package
|
||||
* @param root CDN domain in browser or file system root in node
|
||||
*/
|
||||
export async function fetchResource(
|
||||
pkg: VersionedPackage,
|
||||
assetPath: string,
|
||||
root: string,
|
||||
) {
|
||||
export const fetchResource: FetchResourceFn = async (pkg, assetPath, root) => {
|
||||
const refinedAssetPath = assetPath.startsWith("/")
|
||||
? assetPath.slice(1)
|
||||
: assetPath;
|
||||
|
||||
const url = new URL(`${pkg.name}@${pkg.version}/` + refinedAssetPath, root);
|
||||
const { name, version } = getVersionedPackage(pkg);
|
||||
const url = new URL(`${name}@${version}/` + refinedAssetPath, root);
|
||||
|
||||
return fetch(url).catch(() => {
|
||||
throw new Error(`Cannot fetch from ${url.toString()}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -18,35 +18,29 @@ import { readFile } from "fs/promises";
|
||||
import { createRequire } from "module";
|
||||
import { sep, posix, join } from "path";
|
||||
|
||||
import type { VersionedPackage } from "../types.js";
|
||||
import { FetchResourceFn, getVersionedPackage } from "../types.js";
|
||||
|
||||
/**
|
||||
* @param pkg name of package with version
|
||||
* @param assetPath path of required asset in given package
|
||||
* @param root CDN domain in browser or js-client itself in node
|
||||
*/
|
||||
export async function fetchResource(
|
||||
pkg: VersionedPackage,
|
||||
assetPath: string,
|
||||
root: string,
|
||||
) {
|
||||
// TODO: `root` will be handled somehow in the future. For now, we use filesystem root where js-client is running;
|
||||
root = "/";
|
||||
export const fetchResource: FetchResourceFn = async (pkg, assetPath) => {
|
||||
const { name } = getVersionedPackage(pkg);
|
||||
const require = createRequire(import.meta.url);
|
||||
const packagePathIndex = require.resolve(pkg.name);
|
||||
const packagePathIndex = require.resolve(name);
|
||||
|
||||
// Ensure that windows path is converted to posix path. So we can find a package
|
||||
const posixPath = packagePathIndex.split(sep).join(posix.sep);
|
||||
|
||||
const matches = new RegExp(`(.+${pkg.name})`).exec(posixPath);
|
||||
const matches = new RegExp(`(.+${name})`).exec(posixPath);
|
||||
|
||||
const packagePath = matches?.[0];
|
||||
|
||||
if (packagePath == null) {
|
||||
throw new Error(`Cannot find dependency ${pkg.name} in path ${posixPath}`);
|
||||
if (packagePath === undefined) {
|
||||
throw new Error(`Cannot find dependency ${name} in path ${posixPath}`);
|
||||
}
|
||||
|
||||
const pathToResource = join(root, packagePath, assetPath);
|
||||
const pathToResource = join(packagePath, assetPath);
|
||||
|
||||
const file = await readFile(pathToResource);
|
||||
|
||||
@ -59,4 +53,4 @@ export async function fetchResource(
|
||||
: "application/text",
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -14,10 +14,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Worker } from "@fluencelabs/threads/master";
|
||||
import type { MarineBackgroundInterface } from "@fluencelabs/marine-worker";
|
||||
import { ModuleThread } from "@fluencelabs/threads/master";
|
||||
|
||||
export type VersionedPackage = { name: string; version: string };
|
||||
export type GetWorker = (
|
||||
pkg: VersionedPackage,
|
||||
import versions from "./versions.js";
|
||||
|
||||
export type FetchedPackages = keyof typeof versions;
|
||||
type VersionedPackage = { name: string; version: string };
|
||||
export type GetWorkerFn = (
|
||||
pkg: FetchedPackages,
|
||||
CDNUrl: string,
|
||||
) => Promise<Worker>;
|
||||
) => Promise<ModuleThread<MarineBackgroundInterface>>;
|
||||
|
||||
export const getVersionedPackage = (pkg: FetchedPackages): VersionedPackage => {
|
||||
return {
|
||||
name: pkg,
|
||||
version: versions[pkg],
|
||||
};
|
||||
};
|
||||
|
||||
export type FetchResourceFn = (
|
||||
pkg: FetchedPackages,
|
||||
assetPath: string,
|
||||
root: string,
|
||||
) => Promise<Response>;
|
||||
|
@ -14,13 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BlobWorker } from "@fluencelabs/threads/master";
|
||||
import type { MarineBackgroundInterface } from "@fluencelabs/marine-worker";
|
||||
import { BlobWorker, ModuleThread, spawn } from "@fluencelabs/threads/master";
|
||||
|
||||
import { fetchResource } from "../fetchers/browser.js";
|
||||
import type { GetWorker, VersionedPackage } from "../types.js";
|
||||
import type { FetchedPackages, GetWorkerFn } from "../types.js";
|
||||
|
||||
export const getWorker: GetWorker = async (
|
||||
pkg: VersionedPackage,
|
||||
export const getWorker: GetWorkerFn = async (
|
||||
pkg: FetchedPackages,
|
||||
CDNUrl: string,
|
||||
) => {
|
||||
const fetchWorkerCode = async () => {
|
||||
@ -34,5 +35,9 @@ export const getWorker: GetWorker = async (
|
||||
};
|
||||
|
||||
const workerCode = await fetchWorkerCode();
|
||||
return BlobWorker.fromText(workerCode);
|
||||
|
||||
const workerThread: ModuleThread<MarineBackgroundInterface> =
|
||||
await spawn<MarineBackgroundInterface>(BlobWorker.fromText(workerCode));
|
||||
|
||||
return workerThread;
|
||||
};
|
||||
|
@ -18,18 +18,24 @@ import { createRequire } from "module";
|
||||
import { dirname, relative } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { Worker } from "@fluencelabs/threads/master";
|
||||
import type { MarineBackgroundInterface } from "@fluencelabs/marine-worker";
|
||||
import { ModuleThread, spawn, Worker } from "@fluencelabs/threads/master";
|
||||
|
||||
import type { GetWorker, VersionedPackage } from "../types.js";
|
||||
import type { FetchedPackages, GetWorkerFn } from "../types.js";
|
||||
import { getVersionedPackage } from "../types.js";
|
||||
|
||||
export const getWorker: GetWorker = (pkg: VersionedPackage) => {
|
||||
export const getWorker: GetWorkerFn = async (pkg: FetchedPackages) => {
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const pathToThisFile = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const pathToWorker = require.resolve(pkg.name);
|
||||
const { name } = getVersionedPackage(pkg);
|
||||
const pathToWorker = require.resolve(name);
|
||||
|
||||
const relativePathToWorker = relative(pathToThisFile, pathToWorker);
|
||||
|
||||
return Promise.resolve(new Worker(relativePathToWorker));
|
||||
const workerThread: ModuleThread<MarineBackgroundInterface> =
|
||||
await spawn<MarineBackgroundInterface>(new Worker(relativePathToWorker));
|
||||
|
||||
return workerThread;
|
||||
};
|
||||
|
26
packages/core/js-client/.gitignore
vendored
26
packages/core/js-client/.gitignore
vendored
@ -1,26 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
bundle/
|
||||
|
||||
dist
|
||||
esm
|
||||
types
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
.idea
|
||||
|
||||
# workaround to make integration tests work
|
||||
src/marine/worker-script/index.js
|
||||
|
||||
src/versions.ts
|
@ -6,6 +6,204 @@
|
||||
* dependencies
|
||||
* @fluencelabs/js-client-isomorphic bumped to 0.2.0
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/interfaces bumped to 0.9.0
|
||||
|
||||
## [0.9.0](https://github.com/fluencelabs/js-client/compare/js-client-v0.8.4...js-client-v0.9.0) (2024-02-23)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **deps:** Update avm to 0.62 ([#438](https://github.com/fluencelabs/js-client/issues/438))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** Update avm to 0.62 ([#438](https://github.com/fluencelabs/js-client/issues/438)) ([702ad60](https://github.com/fluencelabs/js-client/commit/702ad605a8e9217f66d3992f31ae8461283ff0b1))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/interfaces bumped to 0.12.0
|
||||
* @fluencelabs/js-client-isomorphic bumped to 0.6.0
|
||||
* @fluencelabs/marine-worker bumped from 0.5.1 to 0.6.0
|
||||
|
||||
## [0.8.4](https://github.com/fluencelabs/js-client/compare/js-client-v0.8.3...js-client-v0.8.4) (2024-02-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Rename testnet to dar ([#433](https://github.com/fluencelabs/js-client/issues/433)) ([e8417d0](https://github.com/fluencelabs/js-client/commit/e8417d069a8cc2244f392b028e7464c9917ec063))
|
||||
|
||||
## [0.8.3](https://github.com/fluencelabs/js-client/compare/js-client-v0.8.2...js-client-v0.8.3) (2024-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **js-client:** Handle null as user input value ([#431](https://github.com/fluencelabs/js-client/issues/431)) ([d7070fd](https://github.com/fluencelabs/js-client/commit/d7070fd71ef8524bdc47c0c84afe0a62ae80dca6))
|
||||
|
||||
## [0.8.2](https://github.com/fluencelabs/js-client/compare/js-client-v0.8.1...js-client-v0.8.2) (2024-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **js-client:** Improve logging of conversion API ([#429](https://github.com/fluencelabs/js-client/issues/429)) ([2b1d0f7](https://github.com/fluencelabs/js-client/commit/2b1d0f7f05de237733ad6f6c5aeb1ba787313dd7))
|
||||
|
||||
## [0.8.1](https://github.com/fluencelabs/js-client/compare/js-client-v0.8.0...js-client-v0.8.1) (2024-01-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Dial interval ([#421](https://github.com/fluencelabs/js-client/issues/421)) ([fa38328](https://github.com/fluencelabs/js-client/commit/fa38328fddac076831ecd0a352d802a0281c4ab4))
|
||||
|
||||
## [0.8.0](https://github.com/fluencelabs/js-client/compare/js-client-v0.7.0...js-client-v0.8.0) (2024-01-29)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **js-client:** Multiformat MsgPack for particle data ([#422](https://github.com/fluencelabs/js-client/issues/422))
|
||||
|
||||
### Features
|
||||
|
||||
* **js-client:** Multiformat MsgPack for particle data ([#422](https://github.com/fluencelabs/js-client/issues/422)) ([8ac029b](https://github.com/fluencelabs/js-client/commit/8ac029b6d336114a90559fb3fee18f7493beb1e0))
|
||||
|
||||
## [0.7.0](https://github.com/fluencelabs/js-client/compare/js-client-v0.6.0...js-client-v0.7.0) (2024-01-26)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.59.0 #423
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.59.0 [#423](https://github.com/fluencelabs/js-client/issues/423) ([e21ecc1](https://github.com/fluencelabs/js-client/commit/e21ecc1edec5f34f2a56726eb62833774f814fef))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/interfaces bumped to 0.11.0
|
||||
* @fluencelabs/js-client-isomorphic bumped to 0.5.0
|
||||
|
||||
## [0.6.0](https://github.com/fluencelabs/js-client/compare/js-client-v0.5.5...js-client-v0.6.0) (2024-01-19)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **js-client:** Remove getter for secret key ([#416](https://github.com/fluencelabs/js-client/issues/416))
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.55.0 ([#407](https://github.com/fluencelabs/js-client/issues/407))
|
||||
|
||||
### Features
|
||||
|
||||
* **js-client:** Remove getter for secret key ([#416](https://github.com/fluencelabs/js-client/issues/416)) ([15f96dc](https://github.com/fluencelabs/js-client/commit/15f96dc5f78693ac9bb86f21b79ed035ffd97977))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.55.0 ([5d7ae85](https://github.com/fluencelabs/js-client/commit/5d7ae85e585b8ce1d89f347a0a31d2212fc5a792))
|
||||
* **deps:** update dependency @fluencelabs/avm to v0.55.0 ([#407](https://github.com/fluencelabs/js-client/issues/407)) ([5d7ae85](https://github.com/fluencelabs/js-client/commit/5d7ae85e585b8ce1d89f347a0a31d2212fc5a792))
|
||||
* Enable async loading of all dependency resources ([#408](https://github.com/fluencelabs/js-client/issues/408)) ([f5425b4](https://github.com/fluencelabs/js-client/commit/f5425b4746f436f84a41bae6584adb8b200ba33d))
|
||||
* **js-client:** Improve logging ([#418](https://github.com/fluencelabs/js-client/issues/418)) ([5696e3b](https://github.com/fluencelabs/js-client/commit/5696e3beba9453e5981a599bab2662d87dc1ddd2))
|
||||
* **js-client:** Remove union with undefined of methods for getting random peer ([#417](https://github.com/fluencelabs/js-client/issues/417)) ([4d90414](https://github.com/fluencelabs/js-client/commit/4d90414190feb6772c7afe0869dee7636616f4c4))
|
||||
* Update libp2p deps ([#419](https://github.com/fluencelabs/js-client/issues/419)) ([a8a1473](https://github.com/fluencelabs/js-client/commit/a8a14735b34495d2426b1f59f53c75d69c7faf66))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/interfaces bumped to 0.10.0
|
||||
* @fluencelabs/js-client-isomorphic bumped to 0.4.0
|
||||
|
||||
## [0.5.5](https://github.com/fluencelabs/js-client/compare/js-client-v0.5.4...js-client-v0.5.5) (2023-12-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **js-client:** Add fire-and-forget flag [DXJ-562] ([#400](https://github.com/fluencelabs/js-client/issues/400)) ([86a7302](https://github.com/fluencelabs/js-client/commit/86a73027e523cf3db4fc9cf58fc7625e44638d0a))
|
||||
* **npm-aqua-compiler:** create package ([#401](https://github.com/fluencelabs/js-client/issues/401)) ([d600811](https://github.com/fluencelabs/js-client/commit/d6008110cf0ecaf23a63cfef0bb3f786a6eb0937))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **js-client:** Remove log truncation. ([#403](https://github.com/fluencelabs/js-client/issues/403)) ([9b629ee](https://github.com/fluencelabs/js-client/commit/9b629eef2e188331cfb338efe775f20dac9bc2fb))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/js-client-isomorphic bumped to 0.3.1
|
||||
* @fluencelabs/marine-worker bumped from 0.5.0 to 0.5.1
|
||||
|
||||
## [0.5.4](https://github.com/fluencelabs/js-client/compare/js-client-v0.5.3...js-client-v0.5.4) (2023-12-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **js-client:** Update libp2p ecosystem [fixes DXJ-551] ([#393](https://github.com/fluencelabs/js-client/issues/393)) ([44eb149](https://github.com/fluencelabs/js-client/commit/44eb1493b3a40fea56cfe726f7e18cd646ca6f92))
|
||||
* Support instance context [fixes DXJ-541] ([#392](https://github.com/fluencelabs/js-client/issues/392)) ([1578b79](https://github.com/fluencelabs/js-client/commit/1578b791ac4ff483dc73aa3fd5083b1eeef79be8))
|
||||
|
||||
## [0.5.3](https://github.com/fluencelabs/js-client/compare/js-client-v0.5.2...js-client-v0.5.3) (2023-11-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Additional export from js-client ([fdd0ca0](https://github.com/fluencelabs/js-client/commit/fdd0ca0ea26407dbfd94d43e2a5ec1bbea7b96ff))
|
||||
|
||||
## [0.5.1](https://github.com/fluencelabs/js-client/compare/js-client-v0.5.0...js-client-v0.5.1) (2023-11-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Temporarily expose internal js client API ([#386](https://github.com/fluencelabs/js-client/issues/386)) ([b5a6296](https://github.com/fluencelabs/js-client/commit/b5a6296225563dc6039b3f7c83e11075d9893045))
|
||||
|
||||
## [0.5.0](https://github.com/fluencelabs/js-client/compare/js-client-v0.4.4...js-client-v0.5.0) (2023-11-22)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **js-client:** Segregation of responsibility between js-client packages [fixes DXJ-525] ([#378](https://github.com/fluencelabs/js-client/issues/378))
|
||||
|
||||
### Features
|
||||
|
||||
* **js-client:** Segregation of responsibility between js-client packages [fixes DXJ-525] ([#378](https://github.com/fluencelabs/js-client/issues/378)) ([f4a550d](https://github.com/fluencelabs/js-client/commit/f4a550dd226846dfc2ade1ccc35a286dc3be2fed))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/js-client-isomorphic bumped to 0.3.0
|
||||
* @fluencelabs/marine-worker bumped from 0.4.2 to 0.5.0
|
||||
|
||||
## [0.4.4](https://github.com/fluencelabs/js-client/compare/js-client-v0.4.3...js-client-v0.4.4) (2023-11-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **js-client:** Fix CDN flow and move it in modules [fixes DXJ-527] ([#376](https://github.com/fluencelabs/js-client/issues/376)) ([f5e9923](https://github.com/fluencelabs/js-client/commit/f5e99239741bc8907c3b4febdc53e46d7a43e46e))
|
||||
|
||||
## [0.4.3](https://github.com/fluencelabs/js-client/compare/js-client-v0.4.2...js-client-v0.4.3) (2023-11-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* JS-client bugs and tech debt [fixes DXJ-520] ([#374](https://github.com/fluencelabs/js-client/issues/374)) ([b460491](https://github.com/fluencelabs/js-client/commit/b460491fbd0d07e3507a6c70f162014580c6d6da))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* The following workspace dependencies were updated
|
||||
* dependencies
|
||||
* @fluencelabs/js-client-isomorphic bumped to 0.2.2
|
||||
* @fluencelabs/marine-worker bumped from 0.4.1 to 0.4.2
|
||||
|
||||
## [0.4.2](https://github.com/fluencelabs/js-client/compare/js-client-v0.4.1...js-client-v0.4.2) (2023-10-30)
|
||||
|
||||
|
||||
|
16
packages/core/js-client/aqua/calc.aqua
Normal file
16
packages/core/js-client/aqua/calc.aqua
Normal file
@ -0,0 +1,16 @@
|
||||
service Calc("calc"):
|
||||
add(n: f32)
|
||||
subtract(n: f32)
|
||||
multiply(n: f32)
|
||||
divide(n: f32)
|
||||
reset()
|
||||
getResult() -> f32
|
||||
|
||||
|
||||
func demoCalc() -> f32:
|
||||
Calc.add(10)
|
||||
Calc.multiply(5)
|
||||
Calc.subtract(8)
|
||||
Calc.divide(6)
|
||||
res <- Calc.getResult()
|
||||
<- res
|
@ -1,10 +1,10 @@
|
||||
data GreetingRecord:
|
||||
data GreetingRecordData:
|
||||
str: string
|
||||
num: i32
|
||||
|
||||
service Greeting("greeting"):
|
||||
greeting(name: string) -> string
|
||||
greeting_record() -> GreetingRecord
|
||||
greeting_record() -> GreetingRecordData
|
||||
|
||||
func call(arg: string) -> string:
|
||||
res1 <- Greeting.greeting(arg)
|
||||
@ -13,7 +13,7 @@ func call(arg: string) -> string:
|
||||
<- res3
|
||||
|
||||
service GreetingRecord:
|
||||
greeting_record() -> GreetingRecord
|
||||
greeting_record() -> GreetingRecordData
|
||||
log_debug()
|
||||
log_error()
|
||||
log_info()
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@fluencelabs/js-client",
|
||||
"version": "0.4.2",
|
||||
"version": "0.9.0",
|
||||
"description": "Client for interacting with Fluence network",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
@ -24,51 +24,50 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc && vite build",
|
||||
"test": "vitest --threads false run",
|
||||
"prepare": "node createVersionFile.js"
|
||||
"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.54.0",
|
||||
"@chainsafe/libp2p-noise": "14.0.0",
|
||||
"@chainsafe/libp2p-yamux": "6.0.1",
|
||||
"@fluencelabs/avm": "0.62.0",
|
||||
"@fluencelabs/interfaces": "workspace:*",
|
||||
"@fluencelabs/js-client-isomorphic": "workspace:*",
|
||||
"@fluencelabs/marine-worker": "0.4.1",
|
||||
"@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",
|
||||
"@fluencelabs/marine-worker": "0.6.0",
|
||||
"@fluencelabs/threads": "^2.0.0",
|
||||
"@libp2p/crypto": "4.0.1",
|
||||
"@libp2p/identify": "1.0.11",
|
||||
"@libp2p/interface": "1.1.2",
|
||||
"@libp2p/peer-id": "4.0.5",
|
||||
"@libp2p/peer-id-factory": "4.0.5",
|
||||
"@libp2p/ping": "1.0.10",
|
||||
"@libp2p/utils": "5.2.2",
|
||||
"@libp2p/websockets": "8.0.12",
|
||||
"@multiformats/multiaddr": "12.1.12",
|
||||
"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",
|
||||
"int64-buffer": "1.0.1",
|
||||
"it-length-prefixed": "9.0.3",
|
||||
"it-map": "3.0.5",
|
||||
"it-pipe": "3.0.1",
|
||||
"js-base64": "3.7.5",
|
||||
"libp2p": "0.46.6",
|
||||
"libp2p": "1.2.0",
|
||||
"multiformats": "11.0.1",
|
||||
"rxjs": "7.5.5",
|
||||
"@fluencelabs/threads": "^2.0.0",
|
||||
"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",
|
||||
"@fluencelabs/aqua-api": "0.13.0",
|
||||
"@rollup/plugin-inject": "5.0.5",
|
||||
"@types/bs58": "4.0.1",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/node": "20.7.0",
|
||||
"@types/uuid": "8.3.2",
|
||||
"esbuild": "0.19.5",
|
||||
"hotscript": "1.0.13",
|
||||
"vite": "4.4.11",
|
||||
"vite-tsconfig-paths": "4.0.3",
|
||||
|
89
packages/core/js-client/src/api.spec.ts
Normal file
89
packages/core/js-client/src/api.spec.ts
Normal file
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* 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 { fileURLToPath } from "url";
|
||||
|
||||
import { compileFromPath } from "@fluencelabs/aqua-api";
|
||||
import { ServiceDef } from "@fluencelabs/interfaces";
|
||||
import { assert, describe, expect, it } from "vitest";
|
||||
|
||||
import { v5_registerService } from "./api.js";
|
||||
import { callAquaFunction } from "./compilerSupport/callFunction.js";
|
||||
import { withPeer } from "./util/testUtils.js";
|
||||
|
||||
class CalcParent {
|
||||
protected _state: number = 0;
|
||||
|
||||
add(n: number) {
|
||||
this._state += n;
|
||||
}
|
||||
|
||||
subtract(n: number) {
|
||||
this._state -= n;
|
||||
}
|
||||
}
|
||||
|
||||
class Calc extends CalcParent {
|
||||
multiply(n: number) {
|
||||
this._state *= n;
|
||||
}
|
||||
|
||||
divide(n: number) {
|
||||
this._state /= n;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._state = 0;
|
||||
}
|
||||
|
||||
getResult() {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
describe("User API methods", () => {
|
||||
it("registers user class service and calls own and inherited methods correctly", async () => {
|
||||
await withPeer(async (peer) => {
|
||||
const calcService: Record<never, unknown> = new Calc();
|
||||
|
||||
const { functions, services } = await compileFromPath({
|
||||
filePath: fileURLToPath(new URL("../aqua/calc.aqua", import.meta.url)),
|
||||
});
|
||||
|
||||
const typedServices: Record<string, ServiceDef> = services;
|
||||
|
||||
assert("demoCalc" in functions);
|
||||
|
||||
const { script } = functions["demoCalc"];
|
||||
|
||||
assert("Calc" in typedServices);
|
||||
|
||||
v5_registerService([peer, "calc", calcService], {
|
||||
defaultServiceId: "calc",
|
||||
functions: typedServices["Calc"].functions,
|
||||
});
|
||||
|
||||
const res = await callAquaFunction({
|
||||
args: {},
|
||||
peer,
|
||||
script,
|
||||
fireAndForget: false,
|
||||
});
|
||||
|
||||
expect(res).toBe(7);
|
||||
});
|
||||
});
|
||||
});
|
@ -15,182 +15,220 @@
|
||||
*/
|
||||
|
||||
import type {
|
||||
FnConfig,
|
||||
ArrowWithoutCallbacks,
|
||||
FunctionCallDef,
|
||||
JSONValue,
|
||||
ServiceDef,
|
||||
PassedArgs,
|
||||
ServiceImpl,
|
||||
SimpleTypes,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { getArgumentTypes } from "@fluencelabs/interfaces";
|
||||
import { z } from "zod";
|
||||
|
||||
import { CallAquaFunctionConfig } from "./compilerSupport/callFunction.js";
|
||||
import {
|
||||
aqua2js,
|
||||
js2aqua,
|
||||
SchemaValidationError,
|
||||
wrapJsFunction,
|
||||
} from "./compilerSupport/conversions.js";
|
||||
import { ServiceImpl, UserServiceImpl } from "./compilerSupport/types.js";
|
||||
import { FluencePeer } from "./jsPeer/FluencePeer.js";
|
||||
import { zip } from "./util/utils.js";
|
||||
|
||||
import { callAquaFunction, Fluence, registerService } from "./index.js";
|
||||
|
||||
export const isFluencePeer = (
|
||||
fluencePeerCandidate: unknown,
|
||||
): fluencePeerCandidate is FluencePeer => {
|
||||
return fluencePeerCandidate instanceof FluencePeer;
|
||||
};
|
||||
function validateAquaConfig(
|
||||
config: unknown,
|
||||
): asserts config is CallAquaFunctionConfig | undefined {
|
||||
z.union([
|
||||
z.object({
|
||||
ttl: z.number().optional(),
|
||||
}),
|
||||
z.undefined(),
|
||||
]).parse(config);
|
||||
}
|
||||
|
||||
// TODO: remove v5 prefix from functions
|
||||
|
||||
/**
|
||||
* Convenience function to support Aqua `func` generation backend
|
||||
* The compiler only need to generate a call the function and provide the corresponding definitions and the air script
|
||||
*
|
||||
* @param rawFnArgs - raw arguments passed by user to the generated function
|
||||
* @param args - raw arguments passed by user to the generated function
|
||||
* @param def - function definition generated by the Aqua compiler
|
||||
* @param script - air script with function execution logic generated by the Aqua compiler
|
||||
*/
|
||||
export const v5_callFunction = async (
|
||||
rawFnArgs: unknown[],
|
||||
args: [
|
||||
client: FluencePeer | (JSONValue | UserServiceImpl[string]),
|
||||
...args: (JSONValue | UserServiceImpl[string])[],
|
||||
],
|
||||
def: FunctionCallDef,
|
||||
script: string,
|
||||
): Promise<unknown> => {
|
||||
const { args, client: peer, config } = extractFunctionArgs(rawFnArgs, def);
|
||||
): Promise<JSONValue> => {
|
||||
const [peerOrArg, ...rest] = args;
|
||||
|
||||
return callAquaFunction({
|
||||
args,
|
||||
def,
|
||||
if (!(peerOrArg instanceof FluencePeer)) {
|
||||
return await v5_callFunction(
|
||||
[getDefaultPeer(), peerOrArg, ...rest],
|
||||
def,
|
||||
script,
|
||||
);
|
||||
}
|
||||
|
||||
const schemaFunctionArgs: Record<string, FunctionArg> =
|
||||
def.arrow.domain.tag === "nil" ? {} : def.arrow.domain.fields;
|
||||
|
||||
const schemaFunctionArgEntries = Object.entries(schemaFunctionArgs);
|
||||
|
||||
const schemaArgCount = schemaFunctionArgEntries.length;
|
||||
|
||||
type FunctionArg = SimpleTypes | ArrowWithoutCallbacks;
|
||||
|
||||
// if there are more args than expected in schema (schemaArgCount) then last arg is config
|
||||
const config = schemaArgCount < rest.length ? rest.pop() : undefined;
|
||||
|
||||
validateAquaConfig(config);
|
||||
|
||||
const callArgs = Object.fromEntries<JSONValue | ServiceImpl[string]>(
|
||||
zip(rest.slice(0, schemaArgCount), schemaFunctionArgEntries).map(
|
||||
([arg, [argName, argType]]) => {
|
||||
if (argType.tag === "arrow") {
|
||||
if (typeof arg !== "function") {
|
||||
throw new SchemaValidationError(
|
||||
[argName],
|
||||
argType,
|
||||
"function",
|
||||
arg,
|
||||
);
|
||||
}
|
||||
|
||||
return [argName, wrapJsFunction(arg, argType, argName)];
|
||||
}
|
||||
|
||||
if (typeof arg === "function") {
|
||||
throw new SchemaValidationError(
|
||||
[argName],
|
||||
argType,
|
||||
"non-function value",
|
||||
arg,
|
||||
);
|
||||
}
|
||||
|
||||
return [argName, js2aqua(arg, argType, { path: [argName] })];
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const returnTypeVoid =
|
||||
def.arrow.codomain.tag === "nil" || def.arrow.codomain.items.length === 0;
|
||||
|
||||
const returnSchema =
|
||||
def.arrow.codomain.tag === "unlabeledProduct" &&
|
||||
def.arrow.codomain.items.length === 1 &&
|
||||
"0" in def.arrow.codomain.items
|
||||
? def.arrow.codomain.items[0]
|
||||
: def.arrow.codomain;
|
||||
|
||||
let result = await callAquaFunction({
|
||||
script,
|
||||
peer: peerOrArg,
|
||||
args: callArgs,
|
||||
config,
|
||||
peer,
|
||||
fireAndForget: returnTypeVoid,
|
||||
});
|
||||
|
||||
if (returnTypeVoid) {
|
||||
result = null;
|
||||
}
|
||||
|
||||
return aqua2js(result, returnSchema, {
|
||||
path: [`${def.functionName}ReturnValue`],
|
||||
});
|
||||
};
|
||||
|
||||
const getDefaultPeer = (): FluencePeer => {
|
||||
if (Fluence.defaultClient === undefined) {
|
||||
throw new Error(
|
||||
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
|
||||
);
|
||||
}
|
||||
|
||||
return Fluence.defaultClient;
|
||||
};
|
||||
|
||||
const getDefaultServiceId = (def: ServiceDef) => {
|
||||
if (def.defaultServiceId === undefined) {
|
||||
throw new Error("Service ID is not provided");
|
||||
}
|
||||
|
||||
return def.defaultServiceId;
|
||||
};
|
||||
|
||||
type RegisterServiceType =
|
||||
| [UserServiceImpl]
|
||||
| [string, UserServiceImpl]
|
||||
| [FluencePeer, UserServiceImpl]
|
||||
| [FluencePeer, string, UserServiceImpl];
|
||||
|
||||
/**
|
||||
* Convenience function to support Aqua `service` generation backend
|
||||
* The compiler only need to generate a call the function and provide the corresponding definitions and the air script
|
||||
* @param args - raw arguments passed by user to the generated function
|
||||
* TODO: dont forget to add jsdoc for new arg
|
||||
* @param def - service definition generated by the Aqua compiler
|
||||
*/
|
||||
export const v5_registerService = (args: unknown[], def: ServiceDef): void => {
|
||||
// TODO: Support this in aqua-to-js package
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const service: ServiceImpl = args.pop() as ServiceImpl;
|
||||
export const v5_registerService = (
|
||||
args: RegisterServiceType,
|
||||
def: ServiceDef,
|
||||
): void => {
|
||||
if (args.length === 1) {
|
||||
v5_registerService(
|
||||
[getDefaultPeer(), getDefaultServiceId(def), args[0]],
|
||||
def,
|
||||
);
|
||||
|
||||
const { peer, serviceId } = extractServiceArgs(args, def.defaultServiceId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length === 2) {
|
||||
if (args[0] instanceof FluencePeer) {
|
||||
v5_registerService([args[0], getDefaultServiceId(def), args[1]], def);
|
||||
return;
|
||||
}
|
||||
|
||||
v5_registerService([getDefaultPeer(), args[0], args[1]], def);
|
||||
return;
|
||||
}
|
||||
|
||||
const [peer, serviceId, serviceImpl] = args;
|
||||
|
||||
// Schema for every function in service
|
||||
const serviceSchema = def.functions.tag === "nil" ? {} : def.functions.fields;
|
||||
|
||||
// Wrapping service functions, selecting only those listed in schema, to convert their args js -> aqua and backwards
|
||||
const wrappedServiceImpl = Object.fromEntries(
|
||||
Object.entries(serviceSchema).map(([schemaKey, schemaValue]) => {
|
||||
const serviceImplValue = serviceImpl[schemaKey];
|
||||
|
||||
if (serviceImplValue === undefined) {
|
||||
throw new Error(
|
||||
`Service function ${schemaKey} listed in Aqua schema but wasn't provided in schema implementation object or class instance. Check that your Aqua service definition matches passed service implementation`,
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
schemaKey,
|
||||
wrapJsFunction(
|
||||
serviceImplValue.bind(serviceImpl),
|
||||
schemaValue,
|
||||
schemaKey,
|
||||
),
|
||||
] as const;
|
||||
}),
|
||||
);
|
||||
|
||||
registerService({
|
||||
def,
|
||||
service,
|
||||
serviceId,
|
||||
service: wrappedServiceImpl,
|
||||
peer,
|
||||
serviceId,
|
||||
});
|
||||
};
|
||||
|
||||
function isConfig(arg: unknown): arg is FnConfig {
|
||||
return typeof arg === "object" && arg !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments could be passed in one these configurations:
|
||||
* [...actualArgs]
|
||||
* [peer, ...actualArgs]
|
||||
* [...actualArgs, config]
|
||||
* [peer, ...actualArgs, config]
|
||||
*
|
||||
* This function select the appropriate configuration and returns
|
||||
* arguments in a structured way of: { peer, config, args }
|
||||
*/
|
||||
function extractFunctionArgs(
|
||||
args: unknown[],
|
||||
def: FunctionCallDef,
|
||||
): {
|
||||
client: FluencePeer;
|
||||
config: FnConfig;
|
||||
args: PassedArgs;
|
||||
} {
|
||||
const argumentTypes = getArgumentTypes(def);
|
||||
const argumentNames = Object.keys(argumentTypes);
|
||||
const numberOfExpectedArgs = argumentNames.length;
|
||||
|
||||
let peer: FluencePeer;
|
||||
let config: FnConfig;
|
||||
|
||||
if (isFluencePeer(args[0])) {
|
||||
peer = args[0];
|
||||
args = args.slice(1);
|
||||
} else {
|
||||
if (Fluence.defaultClient == null) {
|
||||
throw new Error(
|
||||
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
|
||||
);
|
||||
}
|
||||
|
||||
peer = Fluence.defaultClient;
|
||||
}
|
||||
|
||||
const maybeConfig = args[numberOfExpectedArgs];
|
||||
|
||||
if (isConfig(maybeConfig)) {
|
||||
config = maybeConfig;
|
||||
} else {
|
||||
config = {};
|
||||
}
|
||||
|
||||
const structuredArgs = args.slice(0, numberOfExpectedArgs);
|
||||
|
||||
if (structuredArgs.length !== numberOfExpectedArgs) {
|
||||
throw new Error(
|
||||
`Incorrect number of arguments. Expecting ${numberOfExpectedArgs}`,
|
||||
);
|
||||
}
|
||||
|
||||
const argsRes = argumentNames.reduce((acc, name, index) => {
|
||||
return { ...acc, [name]: structuredArgs[index] };
|
||||
}, {});
|
||||
|
||||
return {
|
||||
client: peer,
|
||||
args: argsRes,
|
||||
config: config,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments could be passed in one these configurations:
|
||||
* [serviceObject]
|
||||
* [peer, serviceObject]
|
||||
* [defaultId, serviceObject]
|
||||
* [peer, defaultId, serviceObject]
|
||||
*
|
||||
* Where serviceObject is the raw object with function definitions passed by user
|
||||
*
|
||||
* This function select the appropriate configuration and returns
|
||||
* arguments in a structured way of: { peer, serviceId, service }
|
||||
*/
|
||||
const extractServiceArgs = (
|
||||
args: unknown[],
|
||||
defaultServiceId?: string,
|
||||
): {
|
||||
peer: FluencePeer;
|
||||
serviceId: string | undefined;
|
||||
} => {
|
||||
let peer: FluencePeer;
|
||||
let serviceId: string | undefined;
|
||||
|
||||
if (isFluencePeer(args[0])) {
|
||||
peer = args[0];
|
||||
args = args.slice(1);
|
||||
} else {
|
||||
if (Fluence.defaultClient == null) {
|
||||
throw new Error(
|
||||
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
|
||||
);
|
||||
}
|
||||
|
||||
peer = Fluence.defaultClient;
|
||||
}
|
||||
|
||||
if (typeof args[0] === "string") {
|
||||
serviceId = args[0];
|
||||
} else {
|
||||
serviceId = defaultServiceId;
|
||||
}
|
||||
|
||||
return {
|
||||
peer,
|
||||
serviceId,
|
||||
};
|
||||
};
|
||||
|
@ -60,7 +60,7 @@ export const makeClientPeerConfig = async (
|
||||
relayConfig: {
|
||||
peerId: keyPair.getLibp2pPeerId(),
|
||||
relayAddress: relayAddress,
|
||||
...(config.connectionOptions?.dialTimeoutMs != null
|
||||
...(config.connectionOptions?.dialTimeoutMs !== undefined
|
||||
? {
|
||||
dialTimeout: config.connectionOptions.dialTimeoutMs,
|
||||
}
|
||||
@ -90,14 +90,6 @@ export class ClientPeer extends FluencePeer implements IFluenceClient {
|
||||
);
|
||||
}
|
||||
|
||||
getPeerId(): string {
|
||||
return this.keyPair.getPeerId();
|
||||
}
|
||||
|
||||
getPeerSecretKey(): Uint8Array {
|
||||
return this.keyPair.toEd25519PrivateKey();
|
||||
}
|
||||
|
||||
connectionState: ConnectionState = "disconnected";
|
||||
connectionStateChangeHandler: (state: ConnectionState) => void = () => {};
|
||||
|
||||
|
@ -15,17 +15,58 @@
|
||||
*/
|
||||
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { it, describe, expect } from "vitest";
|
||||
import { it, describe, expect, assert } from "vitest";
|
||||
|
||||
import { ExpirationError } from "../../jsPeer/errors.js";
|
||||
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";
|
||||
|
||||
const ONE_SECOND = 1000;
|
||||
|
||||
describe("FluenceClient usage test suite", () => {
|
||||
it("Should stop particle processing after TTL is reached", async () => {
|
||||
await withClient(RELAY, { defaultTtlMs: 600 }, async (peer) => {
|
||||
const script = `
|
||||
(seq
|
||||
(call %init_peer_id% ("load" "relay") [] init_relay)
|
||||
(call init_relay ("peer" "timeout") [60000 "Do you really want to wait for so long?"])
|
||||
)`;
|
||||
|
||||
const particle = await peer.internals.createNewParticle(script);
|
||||
|
||||
const start = Date.now();
|
||||
|
||||
const promise = new Promise<JSONValue>((resolve, reject) => {
|
||||
registerHandlersHelper(peer, particle, {
|
||||
load: {
|
||||
relay: () => {
|
||||
return peer.getRelayPeerId();
|
||||
},
|
||||
},
|
||||
callbackSrv: {
|
||||
response: () => {
|
||||
resolve({});
|
||||
return "";
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, resolve, reject, false);
|
||||
});
|
||||
|
||||
await expect(promise).rejects.toThrow(ExpirationError);
|
||||
|
||||
expect(
|
||||
Date.now() - 500,
|
||||
"Particle processing didn't stop after TTL is reached",
|
||||
).toBeGreaterThanOrEqual(start);
|
||||
});
|
||||
});
|
||||
|
||||
it("should make a call through network", async () => {
|
||||
await withClient(RELAY, {}, async (peer) => {
|
||||
// arrange
|
||||
@ -62,6 +103,7 @@ describe("FluenceClient usage test suite", () => {
|
||||
callback: {
|
||||
callback: (args): undefined => {
|
||||
const [val] = args;
|
||||
assert(val);
|
||||
resolve(val);
|
||||
},
|
||||
error: (args): undefined => {
|
||||
@ -71,7 +113,11 @@ describe("FluenceClient usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(result).toBe("hello world!");
|
||||
@ -124,7 +170,11 @@ describe("FluenceClient usage test suite", () => {
|
||||
throw particle;
|
||||
}
|
||||
|
||||
peer1.internals.initiateParticle(particle, doNothing);
|
||||
peer1.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
|
||||
expect(await res).toEqual("test");
|
||||
});
|
||||
@ -172,13 +222,17 @@ describe("FluenceClient usage test suite", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("With connection options: defaultTTL", async () => {
|
||||
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
|
||||
const isConnected = await checkConnection(peer);
|
||||
it(
|
||||
"With connection options: defaultTTL",
|
||||
async () => {
|
||||
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
|
||||
const isConnected = await checkConnection(peer);
|
||||
|
||||
expect(isConnected).toBeFalsy();
|
||||
});
|
||||
});
|
||||
expect(isConnected).toBeFalsy();
|
||||
});
|
||||
},
|
||||
ONE_SECOND,
|
||||
);
|
||||
});
|
||||
|
||||
it.skip("Should throw correct error when the client tries to send a particle not to the relay", async () => {
|
||||
@ -206,15 +260,15 @@ describe("FluenceClient usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, (stage) => {
|
||||
if (stage.stage === "sendingError") {
|
||||
reject(stage.errorMessage);
|
||||
}
|
||||
});
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
(error: Error) => {
|
||||
reject(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
await promise;
|
||||
|
||||
await expect(promise).rejects.toMatch(
|
||||
"Particle is expected to be sent to only the single peer (relay which client is connected to)",
|
||||
);
|
||||
|
@ -20,6 +20,6 @@ export const nodes = [
|
||||
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
export const RELAY = nodes[0].multiaddr;
|
||||
|
@ -86,7 +86,7 @@ export const checkConnection = async (
|
||||
const [val] = args;
|
||||
|
||||
setTimeout(() => {
|
||||
resolve(val);
|
||||
resolve(val ?? null);
|
||||
}, 0);
|
||||
|
||||
return {};
|
||||
@ -110,6 +110,7 @@ export const checkConnection = async (
|
||||
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(() => {
|
||||
reject("particle timed out");
|
||||
}),
|
||||
|
@ -14,39 +14,53 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Peer ID's id as a base58 string (multihash/CIDv0).
|
||||
*/
|
||||
export type PeerIdB58 = string;
|
||||
|
||||
/**
|
||||
* Node of the Fluence network specified as a pair of node's multiaddr and it's peer id
|
||||
*/
|
||||
export type Node = {
|
||||
peerId: PeerIdB58;
|
||||
multiaddr: string;
|
||||
};
|
||||
const relaySchema = z.object({
|
||||
peerId: z.string(),
|
||||
multiaddr: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* A node in Fluence network a client can connect to.
|
||||
* Relay of the Fluence network specified as a pair of node's multiaddr and it's peer id
|
||||
*/
|
||||
export type Relay = z.infer<typeof relaySchema>;
|
||||
|
||||
export const relayOptionsSchema = z.union([z.string(), relaySchema]);
|
||||
|
||||
/**
|
||||
* A relay in Fluence network a client can connect to.
|
||||
* Can be in the form of:
|
||||
* - string: multiaddr in string format
|
||||
* - Node: node structure, @see Node
|
||||
* - Relay: relay structure, @see Relay
|
||||
*/
|
||||
export type RelayOptions = string | Node;
|
||||
export type RelayOptions = z.infer<typeof relayOptionsSchema>;
|
||||
|
||||
/**
|
||||
* Fluence Peer's key pair types
|
||||
*/
|
||||
export type KeyTypes = "RSA" | "Ed25519" | "secp256k1";
|
||||
|
||||
const keyPairOptionsSchema = z.object({
|
||||
/**
|
||||
* Key pair type. Only Ed25519 is supported for now.
|
||||
*/
|
||||
type: z.literal("Ed25519"),
|
||||
/**
|
||||
* Key pair source. Could be byte array or generated randomly.
|
||||
*/
|
||||
source: z.union([z.literal("random"), z.instanceof(Uint8Array)]),
|
||||
});
|
||||
|
||||
/**
|
||||
* Options to specify key pair used in Fluence Peer
|
||||
*/
|
||||
export type KeyPairOptions = {
|
||||
type: "Ed25519";
|
||||
source: "random" | Uint8Array;
|
||||
};
|
||||
export type KeyPairOptions = z.infer<typeof keyPairOptionsSchema>;
|
||||
|
||||
/**
|
||||
* Fluence JS Client connection states as string literals
|
||||
@ -63,17 +77,10 @@ export const ConnectionStates = [
|
||||
*/
|
||||
export type ConnectionState = (typeof ConnectionStates)[number];
|
||||
|
||||
export interface IFluenceInternalApi {
|
||||
/**
|
||||
* Internal API
|
||||
*/
|
||||
internals: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API of Fluence JS Client
|
||||
*/
|
||||
export interface IFluenceClient extends IFluenceInternalApi {
|
||||
export interface IFluenceClient {
|
||||
/**
|
||||
* Connect to the Fluence network
|
||||
*/
|
||||
@ -91,11 +98,6 @@ export interface IFluenceClient extends IFluenceInternalApi {
|
||||
handler: (state: ConnectionState) => void,
|
||||
): ConnectionState;
|
||||
|
||||
/**
|
||||
* Return peer's secret key as byte array.
|
||||
*/
|
||||
getPeerSecretKey(): Uint8Array;
|
||||
|
||||
/**
|
||||
* Return peer's public key as a base58 string (multihash/CIDv0).
|
||||
*/
|
||||
@ -107,65 +109,66 @@ export interface IFluenceClient extends IFluenceInternalApi {
|
||||
getRelayPeerId(): string;
|
||||
}
|
||||
|
||||
export const configSchema = z
|
||||
.object({
|
||||
/**
|
||||
* Specify the KeyPair to be used to identify the Fluence Peer.
|
||||
* Will be generated randomly if not specified
|
||||
*/
|
||||
keyPair: keyPairOptionsSchema,
|
||||
/**
|
||||
* Options to configure the connection to the Fluence network
|
||||
*/
|
||||
connectionOptions: z
|
||||
.object({
|
||||
/**
|
||||
* 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: z.boolean(),
|
||||
/**
|
||||
* The dialing timeout in milliseconds
|
||||
*/
|
||||
dialTimeoutMs: z.number(),
|
||||
/**
|
||||
* The maximum number of inbound streams for the libp2p node.
|
||||
* Default: 1024
|
||||
*/
|
||||
maxInboundStreams: z.number(),
|
||||
/**
|
||||
* The maximum number of outbound streams for the libp2p node.
|
||||
* Default: 1024
|
||||
*/
|
||||
maxOutboundStreams: z.number(),
|
||||
})
|
||||
.partial(),
|
||||
/**
|
||||
* Sets the default TTL for all particles originating from the peer with no TTL specified.
|
||||
* 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: z.number(),
|
||||
/**
|
||||
* Property for passing custom CDN Url to load dependencies from browser. https://unpkg.com used by default
|
||||
*/
|
||||
CDNUrl: z.string(),
|
||||
/**
|
||||
* Enables\disabled various debugging features
|
||||
*/
|
||||
debug: z
|
||||
.object({
|
||||
/**
|
||||
* 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: z.boolean(),
|
||||
})
|
||||
.partial(),
|
||||
})
|
||||
.partial();
|
||||
|
||||
/**
|
||||
* Configuration used when initiating Fluence Client
|
||||
*/
|
||||
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?: {
|
||||
/**
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Property for passing custom CDN Url to load dependencies from browser. https://unpkg.com used by default
|
||||
*/
|
||||
CDNUrl?: string;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
}
|
||||
export type ClientConfig = z.infer<typeof configSchema>;
|
||||
|
@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { JSONValue, NonArrowType } from "@fluencelabs/interfaces";
|
||||
import { JSONValue, NonArrowSimpleType } from "@fluencelabs/interfaces";
|
||||
import { it, describe, expect, test } from "vitest";
|
||||
|
||||
import { aqua2ts, ts2aqua } from "../conversions.js";
|
||||
import { aqua2js, js2aqua } from "../conversions.js";
|
||||
|
||||
const i32 = { tag: "scalar", name: "i32" } as const;
|
||||
|
||||
@ -76,7 +76,7 @@ const structs = [
|
||||
c: [null, 2],
|
||||
},
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
const labeledProduct2 = {
|
||||
tag: "labeledProduct",
|
||||
@ -167,18 +167,19 @@ const nestedStructs = [
|
||||
c: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
interface ConversionTestArgs {
|
||||
aqua: JSONValue;
|
||||
ts: JSONValue;
|
||||
type: NonArrowType;
|
||||
type: NonArrowSimpleType;
|
||||
}
|
||||
|
||||
describe("Conversion from aqua to typescript", () => {
|
||||
test.each`
|
||||
aqua | ts | type
|
||||
${1} | ${1} | ${i32}
|
||||
${null} | ${null} | ${opt_i32}
|
||||
${[]} | ${null} | ${opt_i32}
|
||||
${[1]} | ${1} | ${opt_i32}
|
||||
${[1, 2, 3]} | ${[1, 2, 3]} | ${array_i32}
|
||||
@ -200,12 +201,16 @@ describe("Conversion from aqua to typescript", () => {
|
||||
// arrange
|
||||
|
||||
// act
|
||||
const tsFromAqua = aqua2ts(aqua, type);
|
||||
const aquaFromTs = ts2aqua(ts, type);
|
||||
const tsFromAqua = aqua2js(aqua, type, { path: [] });
|
||||
const aquaFromTs = js2aqua(ts, type, { path: [] });
|
||||
|
||||
// assert
|
||||
expect(tsFromAqua).toStrictEqual(ts);
|
||||
expect(aquaFromTs).toStrictEqual(aqua);
|
||||
|
||||
// 'null' -> 'null' -> [] ; 'null' not equal []
|
||||
if (aqua !== null || ts !== null) {
|
||||
expect(aquaFromTs).toStrictEqual(aqua);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
@ -231,8 +236,8 @@ describe("Conversion corner cases", () => {
|
||||
};
|
||||
|
||||
// act
|
||||
const aqua = ts2aqua(valueInTs, type);
|
||||
const ts = aqua2ts(valueInAqua, type);
|
||||
const aqua = js2aqua(valueInTs, type, { path: [] });
|
||||
const ts = aqua2js(valueInAqua, type, { path: [] });
|
||||
|
||||
// assert
|
||||
expect(aqua).toStrictEqual({
|
@ -14,18 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
|
||||
import {
|
||||
FnConfig,
|
||||
FunctionCallDef,
|
||||
getArgumentTypes,
|
||||
isReturnTypeVoid,
|
||||
PassedArgs,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
import { logger } from "../util/logger.js";
|
||||
import { ArgCallbackFunction } from "../util/testUtils.js";
|
||||
|
||||
import {
|
||||
errorHandlingService,
|
||||
@ -51,95 +44,49 @@ const log = logger("aqua");
|
||||
* @returns
|
||||
*/
|
||||
|
||||
type CallAquaFunctionArgs = {
|
||||
def: FunctionCallDef;
|
||||
export type CallAquaFunctionArgs = {
|
||||
script: string;
|
||||
config: FnConfig;
|
||||
config?: CallAquaFunctionConfig | undefined;
|
||||
peer: FluencePeer;
|
||||
args: PassedArgs;
|
||||
args: { [key: string]: JSONValue | ArgCallbackFunction };
|
||||
fireAndForget: boolean;
|
||||
};
|
||||
|
||||
export type CallAquaFunctionConfig = {
|
||||
ttl?: number;
|
||||
};
|
||||
|
||||
export const callAquaFunction = async ({
|
||||
def,
|
||||
script,
|
||||
config,
|
||||
config = {},
|
||||
peer,
|
||||
args,
|
||||
fireAndForget,
|
||||
}: CallAquaFunctionArgs) => {
|
||||
// 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);
|
||||
log.trace("calling aqua function %j", { script, config, args });
|
||||
|
||||
const particle = await peer.internals.createNewParticle(script, config.ttl);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise<JSONValue>((resolve, reject) => {
|
||||
// Registering function args as a services
|
||||
for (const [name, argVal] of Object.entries(args)) {
|
||||
const type = argumentTypes[name];
|
||||
let service: ServiceDescription;
|
||||
|
||||
if (type.tag === "arrow") {
|
||||
// TODO: Add validation here
|
||||
assert(
|
||||
typeof argVal === "function",
|
||||
"Should not be possible, bad types",
|
||||
);
|
||||
|
||||
service = userHandlerService(
|
||||
def.names.callbackSrv,
|
||||
[name, type],
|
||||
argVal,
|
||||
);
|
||||
if (typeof argVal === "function") {
|
||||
service = userHandlerService("callbackSrv", name, argVal);
|
||||
} else {
|
||||
// TODO: Add validation here
|
||||
assert(
|
||||
typeof argVal !== "function",
|
||||
"Should not be possible, bad types",
|
||||
);
|
||||
|
||||
service = injectValueService(def.names.getDataSrv, name, type, argVal);
|
||||
service = injectValueService("getDataSrv", name, argVal);
|
||||
}
|
||||
|
||||
registerParticleScopeService(peer, particle, service);
|
||||
}
|
||||
|
||||
registerParticleScopeService(peer, particle, responseService(def, resolve));
|
||||
registerParticleScopeService(peer, particle, responseService(resolve));
|
||||
|
||||
registerParticleScopeService(peer, particle, injectRelayService(def, peer));
|
||||
registerParticleScopeService(peer, particle, injectRelayService(peer));
|
||||
|
||||
registerParticleScopeService(
|
||||
peer,
|
||||
particle,
|
||||
errorHandlingService(def, reject),
|
||||
);
|
||||
registerParticleScopeService(peer, particle, errorHandlingService(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})`,
|
||||
);
|
||||
}
|
||||
});
|
||||
peer.internals.initiateParticle(particle, resolve, reject, fireAndForget);
|
||||
});
|
||||
};
|
||||
|
@ -14,222 +14,252 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// TODO: This file is a mess. Need to refactor it later
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import assert from "assert";
|
||||
|
||||
import type {
|
||||
import {
|
||||
ArrowType,
|
||||
ArrowWithoutCallbacks,
|
||||
JSONArray,
|
||||
JSONValue,
|
||||
NonArrowType,
|
||||
LabeledProductType,
|
||||
NonArrowSimpleType,
|
||||
ScalarType,
|
||||
SimpleTypes,
|
||||
UnlabeledProductType,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { match } from "ts-pattern";
|
||||
|
||||
import { CallServiceData } from "../jsServiceHost/interfaces.js";
|
||||
import { jsonify } from "../util/utils.js";
|
||||
import { zip } from "../util/utils.js";
|
||||
|
||||
/**
|
||||
* Convert value from its representation in aqua language to representation in typescript
|
||||
* @param value - value as represented in aqua
|
||||
* @param type - definition of the aqua type
|
||||
* @returns value represented in typescript
|
||||
*/
|
||||
export const aqua2ts = (value: JSONValue, type: NonArrowType): JSONValue => {
|
||||
const res = match(type)
|
||||
.with({ tag: "nil" }, () => {
|
||||
import { ServiceImpl, UserServiceImpl } from "./types.js";
|
||||
|
||||
export class SchemaValidationError extends Error {
|
||||
constructor(
|
||||
public path: string[],
|
||||
schema: NonArrowSimpleType | ArrowWithoutCallbacks,
|
||||
expected: string,
|
||||
provided: JSONValue | UserServiceImpl[string],
|
||||
) {
|
||||
const given =
|
||||
provided === null
|
||||
? "null"
|
||||
: Array.isArray(provided)
|
||||
? "array"
|
||||
: typeof provided;
|
||||
|
||||
const message = `Aqua type mismatch. Path: ${path.join(
|
||||
".",
|
||||
)}; Expected: ${expected}; Given: ${given}; \nSchema: ${JSON.stringify(
|
||||
schema,
|
||||
)}; \nTry recompiling rust services and aqua. Make sure you are using up-to-date versions of aqua libraries`;
|
||||
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
interface ValidationContext {
|
||||
path: string[];
|
||||
}
|
||||
|
||||
const numberTypes = [
|
||||
"u8",
|
||||
"u16",
|
||||
"u32",
|
||||
"u64",
|
||||
"i8",
|
||||
"i16",
|
||||
"i32",
|
||||
"i64",
|
||||
"f32",
|
||||
"f64",
|
||||
] as const;
|
||||
|
||||
function isScalar(
|
||||
schema: ScalarType,
|
||||
arg: JSONValue,
|
||||
{ path }: ValidationContext,
|
||||
) {
|
||||
if (numberTypes.includes(schema.name)) {
|
||||
if (typeof arg !== "number") {
|
||||
throw new SchemaValidationError(path, schema, "number", arg);
|
||||
}
|
||||
} else if (schema.name === "bool") {
|
||||
if (typeof arg !== "boolean") {
|
||||
throw new SchemaValidationError(path, schema, "boolean", arg);
|
||||
}
|
||||
} else if (schema.name === "string") {
|
||||
if (typeof arg !== "string") {
|
||||
throw new SchemaValidationError(path, schema, "string", arg);
|
||||
}
|
||||
} else {
|
||||
throw new SchemaValidationError(path, schema, schema.name, arg);
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
export function aqua2js(
|
||||
value: JSONValue,
|
||||
schema: NonArrowSimpleType,
|
||||
{ path }: ValidationContext,
|
||||
): JSONValue {
|
||||
if (schema.tag === "nil") {
|
||||
return null;
|
||||
} else if (schema.tag === "option") {
|
||||
if (!Array.isArray(value) && value !== null) {
|
||||
throw new SchemaValidationError(path, schema, "array or null", value);
|
||||
}
|
||||
|
||||
if (value !== null && "0" in value) {
|
||||
return aqua2js(value[0], schema.type, { path: [...path, "?"] });
|
||||
} else {
|
||||
return null;
|
||||
})
|
||||
.with({ tag: "option" }, (opt) => {
|
||||
assert(Array.isArray(value), "Should not be possible, bad types");
|
||||
}
|
||||
} else if (
|
||||
schema.tag === "scalar" ||
|
||||
schema.tag === "bottomType" ||
|
||||
schema.tag === "topType"
|
||||
) {
|
||||
return value;
|
||||
} else if (schema.tag === "array") {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new SchemaValidationError([], schema, "array", value);
|
||||
}
|
||||
|
||||
if (value.length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
return aqua2ts(value[0], opt.type);
|
||||
}
|
||||
})
|
||||
.with({ tag: "scalar" }, { tag: "bottomType" }, { tag: "topType" }, () => {
|
||||
return value;
|
||||
})
|
||||
.with({ tag: "array" }, (arr) => {
|
||||
assert(Array.isArray(value), "Should not be possible, bad types");
|
||||
return value.map((y) => {
|
||||
return aqua2ts(y, arr.type);
|
||||
});
|
||||
})
|
||||
.with({ tag: "struct" }, (x) => {
|
||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||
const val = aqua2ts(value[key], type);
|
||||
return { ...agg, [key]: val };
|
||||
}, {});
|
||||
})
|
||||
.with({ tag: "labeledProduct" }, (x) => {
|
||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||
const val = aqua2ts(value[key], type);
|
||||
return { ...agg, [key]: val };
|
||||
}, {});
|
||||
})
|
||||
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||
return x.items.map((type, index) => {
|
||||
return aqua2ts(value[index], type);
|
||||
});
|
||||
})
|
||||
// uncomment to check that every pattern in matched
|
||||
// .exhaustive();
|
||||
.otherwise(() => {
|
||||
throw new Error("Unexpected tag: " + jsonify(type));
|
||||
return value.map((y, i) => {
|
||||
return aqua2js(y, schema.type, { path: [...path, `[${i}]`] });
|
||||
});
|
||||
} else if (schema.tag === "unlabeledProduct") {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new SchemaValidationError([], schema, "array", value);
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert call service arguments list from their aqua representation to representation in typescript
|
||||
* @param req - call service data
|
||||
* @param arrow - aqua type definition
|
||||
* @returns arguments in typescript representation
|
||||
*/
|
||||
export const aquaArgs2Ts = (
|
||||
req: CallServiceData,
|
||||
arrow: ArrowWithoutCallbacks,
|
||||
): JSONArray => {
|
||||
const argTypes = match(arrow.domain)
|
||||
.with({ tag: "labeledProduct" }, (x) => {
|
||||
return Object.values(x.fields);
|
||||
})
|
||||
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||
return x.items;
|
||||
})
|
||||
.with({ tag: "nil" }, (x) => {
|
||||
return [];
|
||||
})
|
||||
// uncomment to check that every pattern in matched
|
||||
// .exhaustive()
|
||||
.otherwise(() => {
|
||||
throw new Error("Unexpected tag: " + jsonify(arrow.domain));
|
||||
return zip(value, schema.items).map(([v, s], i) => {
|
||||
return aqua2js(v, s, { path: [...path, `[${i}]`] });
|
||||
});
|
||||
} else if (["labeledProduct", "struct"].includes(schema.tag)) {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
||||
throw new SchemaValidationError([], schema, "object", value);
|
||||
}
|
||||
|
||||
if (req.args.length !== argTypes.length) {
|
||||
throw new Error(
|
||||
`incorrect number of arguments, expected: ${argTypes.length}, got: ${req.args.length}`,
|
||||
return Object.fromEntries(
|
||||
Object.entries(schema.fields).map(([key, type]) => {
|
||||
let v = value[key];
|
||||
|
||||
if (v === undefined) {
|
||||
v = null;
|
||||
}
|
||||
|
||||
const val = aqua2js(v, type, { path: [...path, key] });
|
||||
return [key, val];
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
throw new SchemaValidationError([], schema, "never", value);
|
||||
}
|
||||
}
|
||||
|
||||
return req.args.map((arg, index) => {
|
||||
return aqua2ts(arg, argTypes[index]);
|
||||
});
|
||||
};
|
||||
export function js2aqua(
|
||||
value: JSONValue,
|
||||
schema: NonArrowSimpleType,
|
||||
{ path }: ValidationContext,
|
||||
): JSONValue {
|
||||
if (schema.tag === "nil") {
|
||||
if (value !== null) {
|
||||
throw new SchemaValidationError(path, schema, "null", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert value from its typescript representation to representation in aqua
|
||||
* @param value - the value as represented in typescript
|
||||
* @param type - definition of the aqua type
|
||||
* @returns value represented in aqua
|
||||
*/
|
||||
export const ts2aqua = (value: JSONValue, type: NonArrowType): JSONValue => {
|
||||
const res = match(type)
|
||||
.with({ tag: "nil" }, () => {
|
||||
return null;
|
||||
})
|
||||
.with({ tag: "option" }, (opt) => {
|
||||
if (value === null || value === undefined) {
|
||||
return [];
|
||||
} else {
|
||||
return [ts2aqua(value, opt.type)];
|
||||
}
|
||||
})
|
||||
.with({ tag: "scalar" }, { tag: "bottomType" }, { tag: "topType" }, () => {
|
||||
return value;
|
||||
})
|
||||
.with({ tag: "array" }, (arr) => {
|
||||
assert(Array.isArray(value), "Should not be possible, bad types");
|
||||
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);
|
||||
return { ...agg, [key]: val };
|
||||
}, {});
|
||||
})
|
||||
.with({ tag: "labeledProduct" }, (x) => {
|
||||
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);
|
||||
});
|
||||
})
|
||||
// uncomment to check that every pattern in matched
|
||||
// .exhaustive()
|
||||
.otherwise(() => {
|
||||
throw new Error("Unexpected tag: " + jsonify(type));
|
||||
return value;
|
||||
} else if (schema.tag === "option") {
|
||||
// option means 'type | null'
|
||||
return value === null
|
||||
? []
|
||||
: [js2aqua(value, schema.type, { path: [...path, "?"] })];
|
||||
} else if (schema.tag === "topType") {
|
||||
// topType equals to 'any'
|
||||
return value;
|
||||
} else if (schema.tag === "bottomType") {
|
||||
// bottomType equals to 'never'
|
||||
throw new SchemaValidationError(path, schema, "never", value);
|
||||
} else if (schema.tag === "scalar") {
|
||||
return isScalar(schema, value, { path });
|
||||
} else if (schema.tag === "array") {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new SchemaValidationError(path, schema, "array", value);
|
||||
}
|
||||
|
||||
return value.map((y, i) => {
|
||||
return js2aqua(y, schema.type, { path: [...path, `[${i}]`] });
|
||||
});
|
||||
} else if (schema.tag === "unlabeledProduct") {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new SchemaValidationError(path, schema, "array", value);
|
||||
}
|
||||
|
||||
return zip(value, schema.items).map(([v, s], i) => {
|
||||
return js2aqua(v, s, { path: [...path, `[${i}]`] });
|
||||
});
|
||||
} else if (["labeledProduct", "struct"].includes(schema.tag)) {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
||||
throw new SchemaValidationError(path, schema, "object", value);
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(schema.fields).map(([key, type]) => {
|
||||
let v = value[key];
|
||||
|
||||
if (v === undefined) {
|
||||
v = null;
|
||||
}
|
||||
|
||||
const val = js2aqua(v, type, { path: [...path, key] });
|
||||
return [key, val];
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
throw new SchemaValidationError(path, schema, "never", value);
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapping function, converting its arguments to aqua before call and back to js after call.
|
||||
// It makes callbacks and service functions defined by user operate on js types seamlessly
|
||||
export const wrapJsFunction = (
|
||||
func: UserServiceImpl[string],
|
||||
schema:
|
||||
| ArrowWithoutCallbacks
|
||||
| ArrowType<LabeledProductType<SimpleTypes> | UnlabeledProductType>,
|
||||
funcName: string,
|
||||
): ServiceImpl[string] => {
|
||||
return async ({ args, context }) => {
|
||||
const schemaArgs =
|
||||
schema.domain.tag === "nil"
|
||||
? []
|
||||
: schema.domain.tag === "unlabeledProduct"
|
||||
? schema.domain.items
|
||||
: Object.values(schema.domain.fields);
|
||||
|
||||
if (schemaArgs.length !== args.length) {
|
||||
throw new Error(
|
||||
`Schema and generated air doesn't match. Air has been called with ${args.length} args and schema contains ${schemaArgs.length} args`,
|
||||
);
|
||||
}
|
||||
|
||||
const jsArgs = zip(args, schemaArgs).map(([arg, schemaArg], i) => {
|
||||
return aqua2js(arg, schemaArg, { path: [`${funcName}Args`, `[${i}]`] });
|
||||
});
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert return type of the service from it's typescript representation to representation in aqua
|
||||
* @param returnValue - the value as represented in typescript
|
||||
* @param arrowType - the arrow type which describes the service
|
||||
* @returns - value represented in aqua
|
||||
*/
|
||||
export const returnType2Aqua = (
|
||||
returnValue: any,
|
||||
arrowType: ArrowType<NonArrowType>,
|
||||
) => {
|
||||
if (arrowType.codomain.tag === "nil") {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (arrowType.codomain.items.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (arrowType.codomain.items.length === 1) {
|
||||
return ts2aqua(returnValue, arrowType.codomain.items[0]);
|
||||
}
|
||||
|
||||
return arrowType.codomain.items.map((type, index) => {
|
||||
return ts2aqua(returnValue[index], type);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts response value from aqua its representation to representation in typescript
|
||||
* @param req - call service data
|
||||
* @param arrow - aqua type definition
|
||||
* @returns response value in typescript representation
|
||||
*/
|
||||
export const responseServiceValue2ts = (
|
||||
req: CallServiceData,
|
||||
arrow: ArrowType<any>,
|
||||
) => {
|
||||
return match(arrow.codomain)
|
||||
.with({ tag: "nil" }, () => {
|
||||
return null;
|
||||
})
|
||||
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||
if (x.items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (x.items.length === 1) {
|
||||
return aqua2ts(req.args[0], x.items[0]);
|
||||
}
|
||||
|
||||
return req.args.map((y, index) => {
|
||||
return aqua2ts(y, x.items[index]);
|
||||
});
|
||||
})
|
||||
.exhaustive();
|
||||
const returnTypeVoid =
|
||||
schema.codomain.tag === "nil" || schema.codomain.items.length === 0;
|
||||
|
||||
const resultSchema =
|
||||
schema.codomain.tag === "unlabeledProduct" &&
|
||||
schema.codomain.items.length === 1 &&
|
||||
"0" in schema.codomain.items
|
||||
? schema.codomain.items[0]
|
||||
: schema.codomain;
|
||||
|
||||
let result = await func(...jsArgs, context);
|
||||
|
||||
if (returnTypeVoid) {
|
||||
result = null;
|
||||
}
|
||||
|
||||
return js2aqua(result, resultSchema, { path: [`${funcName}ReturnValue`] });
|
||||
};
|
||||
};
|
||||
|
@ -14,66 +14,60 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ServiceDef, ServiceImpl } from "@fluencelabs/interfaces";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
import { logger } from "../util/logger.js";
|
||||
|
||||
import { registerGlobalService, userHandlerService } from "./services.js";
|
||||
import { ServiceImpl } from "./types.js";
|
||||
|
||||
const log = logger("aqua");
|
||||
|
||||
interface RegisterServiceArgs {
|
||||
peer: FluencePeer;
|
||||
def: ServiceDef;
|
||||
serviceId: string | undefined;
|
||||
serviceId: string;
|
||||
service: ServiceImpl;
|
||||
}
|
||||
|
||||
type ServiceFunctionPair = [key: string, value: ServiceImpl[string]];
|
||||
|
||||
// This function iterates on plain object or class instance functions ignoring inherited functions and prototype chain.
|
||||
const findAllPossibleRegisteredServiceFunctions = (
|
||||
service: ServiceImpl,
|
||||
): Array<ServiceFunctionPair> => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const prototype = Object.getPrototypeOf(service) as ServiceImpl;
|
||||
|
||||
const isClassInstance = prototype.constructor !== Object;
|
||||
|
||||
if (isClassInstance) {
|
||||
service = prototype;
|
||||
}
|
||||
|
||||
return Object.getOwnPropertyNames(service)
|
||||
.map((prop) => {
|
||||
return [prop, service[prop]];
|
||||
})
|
||||
.filter((entry): entry is ServiceFunctionPair => {
|
||||
const [prop, value] = entry;
|
||||
return typeof value === "function" && prop !== "constructor";
|
||||
});
|
||||
};
|
||||
|
||||
export const registerService = ({
|
||||
peer,
|
||||
def,
|
||||
serviceId = def.defaultServiceId,
|
||||
serviceId,
|
||||
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 });
|
||||
log.trace("registering aqua service %o", { serviceId, service });
|
||||
|
||||
// Checking for missing keys
|
||||
const requiredKeys =
|
||||
def.functions.tag === "nil" ? [] : Object.keys(def.functions.fields);
|
||||
const serviceFunctions = findAllPossibleRegisteredServiceFunctions(service);
|
||||
|
||||
const incorrectServiceDefinitions = requiredKeys.filter((f) => {
|
||||
return !(f in service);
|
||||
});
|
||||
|
||||
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);
|
||||
for (const [serviceFunction, handler] of serviceFunctions) {
|
||||
const userDefinedHandler = handler.bind(service);
|
||||
|
||||
const serviceDescription = userHandlerService(
|
||||
serviceId,
|
||||
singleFunction,
|
||||
serviceFunction,
|
||||
userDefinedHandler,
|
||||
);
|
||||
|
||||
|
@ -14,17 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SecurityTetraplet } from "@fluencelabs/avm";
|
||||
import {
|
||||
CallParams,
|
||||
ArrowWithoutCallbacks,
|
||||
FunctionCallDef,
|
||||
NonArrowType,
|
||||
ServiceImpl,
|
||||
JSONValue,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { fromUint8Array } from "js-base64";
|
||||
import { match } from "ts-pattern";
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
import {
|
||||
@ -34,12 +24,7 @@ import {
|
||||
} from "../jsServiceHost/interfaces.js";
|
||||
import { Particle } from "../particle/Particle.js";
|
||||
|
||||
import {
|
||||
aquaArgs2Ts,
|
||||
responseServiceValue2ts,
|
||||
returnType2Aqua,
|
||||
ts2aqua,
|
||||
} from "./conversions.js";
|
||||
import { ServiceImpl } from "./types.js";
|
||||
|
||||
export interface ServiceDescription {
|
||||
serviceId: string;
|
||||
@ -50,10 +35,10 @@ export interface ServiceDescription {
|
||||
/**
|
||||
* Creates a service which injects relay's peer id into aqua space
|
||||
*/
|
||||
export const injectRelayService = (def: FunctionCallDef, peer: FluencePeer) => {
|
||||
export const injectRelayService = (peer: FluencePeer) => {
|
||||
return {
|
||||
serviceId: def.names.getDataSrv,
|
||||
fnName: def.names.relay,
|
||||
serviceId: "getDataSrv",
|
||||
fnName: "-relay-",
|
||||
handler: () => {
|
||||
return {
|
||||
retCode: ResultCodes.success,
|
||||
@ -69,7 +54,6 @@ export const injectRelayService = (def: FunctionCallDef, peer: FluencePeer) => {
|
||||
export const injectValueService = (
|
||||
serviceId: string,
|
||||
fnName: string,
|
||||
valueType: NonArrowType,
|
||||
value: JSONValue,
|
||||
) => {
|
||||
return {
|
||||
@ -78,7 +62,7 @@ export const injectValueService = (
|
||||
handler: () => {
|
||||
return {
|
||||
retCode: ResultCodes.success,
|
||||
result: ts2aqua(value, valueType),
|
||||
result: value,
|
||||
};
|
||||
},
|
||||
};
|
||||
@ -87,15 +71,17 @@ export const injectValueService = (
|
||||
/**
|
||||
* Creates a service which is used to return value from aqua function into typescript space
|
||||
*/
|
||||
export const responseService = (
|
||||
def: FunctionCallDef,
|
||||
resolveCallback: (val: JSONValue) => void,
|
||||
) => {
|
||||
export const responseService = (resolveCallback: (val: JSONValue) => void) => {
|
||||
return {
|
||||
serviceId: def.names.responseSrv,
|
||||
fnName: def.names.responseFnName,
|
||||
serviceId: "callbackSrv",
|
||||
fnName: "response",
|
||||
handler: (req: CallServiceData) => {
|
||||
const userFunctionReturn = responseServiceValue2ts(req, def.arrow);
|
||||
const userFunctionReturn =
|
||||
req.args.length === 0
|
||||
? null
|
||||
: req.args.length === 1 && "0" in req.args
|
||||
? req.args[0]
|
||||
: req.args;
|
||||
|
||||
setTimeout(() => {
|
||||
resolveCallback(userFunctionReturn);
|
||||
@ -113,17 +99,19 @@ export const responseService = (
|
||||
* Creates a service which is used to return errors from aqua function into typescript space
|
||||
*/
|
||||
export const errorHandlingService = (
|
||||
def: FunctionCallDef,
|
||||
rejectCallback: (err: JSONValue) => void,
|
||||
) => {
|
||||
return {
|
||||
serviceId: def.names.errorHandlingSrv,
|
||||
fnName: def.names.errorFnName,
|
||||
serviceId: "errorHandlingSrv",
|
||||
fnName: "error",
|
||||
handler: (req: CallServiceData) => {
|
||||
const [err] = req.args;
|
||||
|
||||
setTimeout(() => {
|
||||
rejectCallback(err);
|
||||
rejectCallback(
|
||||
err ??
|
||||
"Unknown error happened when executing aqua code. No error text was passed to 'errorHandlingSrv.error' function, probably because AIR code was modified or aqua compiler didn't produce the correct call",
|
||||
);
|
||||
}, 0);
|
||||
|
||||
return {
|
||||
@ -139,21 +127,19 @@ export const errorHandlingService = (
|
||||
*/
|
||||
export const userHandlerService = (
|
||||
serviceId: string,
|
||||
arrowType: [string, ArrowWithoutCallbacks],
|
||||
fnName: string,
|
||||
userHandler: ServiceImpl[string],
|
||||
) => {
|
||||
const [fnName, type] = arrowType;
|
||||
return {
|
||||
serviceId,
|
||||
fnName,
|
||||
handler: async (req: CallServiceData) => {
|
||||
const args: [...JSONValue[], CallParams<string>] = [
|
||||
...aquaArgs2Ts(req, type),
|
||||
extractCallParams(req, type),
|
||||
];
|
||||
const { args, particleContext: context } = req;
|
||||
|
||||
const rawResult = await userHandler.bind(null)(...args);
|
||||
const result = returnType2Aqua(rawResult, type);
|
||||
const result = await userHandler.bind(null)({
|
||||
args,
|
||||
context,
|
||||
});
|
||||
|
||||
return {
|
||||
retCode: ResultCodes.success,
|
||||
@ -163,46 +149,6 @@ export const userHandlerService = (
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts call params from from call service data according to aqua type definition
|
||||
*/
|
||||
const extractCallParams = (
|
||||
req: CallServiceData,
|
||||
arrow: ArrowWithoutCallbacks,
|
||||
): CallParams<string> => {
|
||||
const names: (string | undefined)[] = match(arrow.domain)
|
||||
.with({ tag: "nil" }, () => {
|
||||
return [];
|
||||
})
|
||||
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||
return x.items.map((_, index) => {
|
||||
return "arg" + index;
|
||||
});
|
||||
})
|
||||
.with({ tag: "labeledProduct" }, (x) => {
|
||||
return Object.keys(x.fields);
|
||||
})
|
||||
.exhaustive();
|
||||
|
||||
const tetraplets: Record<string, SecurityTetraplet[]> = {};
|
||||
|
||||
for (let i = 0; i < req.args.length; i++) {
|
||||
const name = names[i];
|
||||
|
||||
if (name != null) {
|
||||
tetraplets[name] = req.tetraplets[i];
|
||||
}
|
||||
}
|
||||
|
||||
const callParams = {
|
||||
...req.particleContext,
|
||||
signature: fromUint8Array(req.particleContext.signature),
|
||||
tetraplets,
|
||||
};
|
||||
|
||||
return callParams;
|
||||
};
|
||||
|
||||
export const registerParticleScopeService = (
|
||||
peer: FluencePeer,
|
||||
particle: Particle,
|
||||
|
@ -14,8 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type InterfaceToType<T extends object> = {
|
||||
[K in keyof T]: T[K];
|
||||
};
|
||||
import { JSONArray, JSONValue } from "@fluencelabs/interfaces";
|
||||
|
||||
import { ParticleContext } from "../jsServiceHost/interfaces.js";
|
||||
|
||||
export type MaybePromise<T> = T | Promise<T>;
|
||||
|
||||
export type ServiceImpl = Record<
|
||||
string,
|
||||
(args: {
|
||||
args: JSONArray;
|
||||
context: ParticleContext;
|
||||
}) => MaybePromise<JSONValue>
|
||||
>;
|
||||
|
||||
export type UserServiceImpl = Record<
|
||||
string,
|
||||
(...args: [...JSONArray, ParticleContext]) => MaybePromise<JSONValue>
|
||||
>;
|
||||
|
||||
export type ServiceFnArgs<T> = { args: T; context: ParticleContext };
|
@ -17,9 +17,10 @@
|
||||
import { noise } from "@chainsafe/libp2p-noise";
|
||||
import { yamux } from "@chainsafe/libp2p-yamux";
|
||||
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||
import { Stream } from "@libp2p/interface/connection";
|
||||
import type { PeerId } from "@libp2p/interface/peer-id";
|
||||
import { identify } from "@libp2p/identify";
|
||||
import type { PeerId } from "@libp2p/interface";
|
||||
import { peerIdFromString } from "@libp2p/peer-id";
|
||||
import { ping } from "@libp2p/ping";
|
||||
import { webSockets } from "@libp2p/websockets";
|
||||
import { all } from "@libp2p/websockets/filters";
|
||||
import { multiaddr, type Multiaddr } from "@multiformats/multiaddr";
|
||||
@ -27,18 +28,14 @@ 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 { KeyPair } from "../keypair/index.js";
|
||||
import { IParticle } from "../particle/interfaces.js";
|
||||
import {
|
||||
buildParticleMessage,
|
||||
Particle,
|
||||
serializeToString,
|
||||
serializeParticle,
|
||||
} from "../particle/Particle.js";
|
||||
import { throwHasNoPeerId } from "../util/libp2pUtils.js";
|
||||
import { logger } from "../util/logger.js";
|
||||
@ -81,6 +78,23 @@ export interface RelayConnectionConfig {
|
||||
maxOutboundStreams: number;
|
||||
}
|
||||
|
||||
type DenyCondition = (ma: Multiaddr) => boolean;
|
||||
|
||||
const dockerNoxDenyCondition: DenyCondition = (ma) => {
|
||||
const [routingProtocol] = ma.stringTuples();
|
||||
const host = routingProtocol?.[1];
|
||||
|
||||
// Nox proposes 3 multiaddr to discover when used inside docker network,
|
||||
// e.g.: [/dns/nox-1, /ip4/10.50.10.10, /ip4/127.0.0.1]
|
||||
// First 2 of them are unreachable outside the docker network
|
||||
// Libp2p cannot handle these scenarios correctly, creating interruptions which affect e2e tests execution.
|
||||
return (
|
||||
host === undefined || host.startsWith("nox-") || host.startsWith("10.50.10")
|
||||
);
|
||||
};
|
||||
|
||||
const denyConditions = [dockerNoxDenyCondition];
|
||||
|
||||
/**
|
||||
* Implementation for JS peers which connects to Fluence through relay node
|
||||
*/
|
||||
@ -93,7 +107,7 @@ export class RelayConnection implements IConnection {
|
||||
this.relayAddress = multiaddr(this.config.relayAddress);
|
||||
const peerId = this.relayAddress.getPeerId();
|
||||
|
||||
if (peerId == null) {
|
||||
if (peerId === null) {
|
||||
throwHasNoPeerId(this.relayAddress);
|
||||
}
|
||||
|
||||
@ -126,21 +140,25 @@ export class RelayConnection implements IConnection {
|
||||
streamMuxers: [yamux()],
|
||||
connectionEncryption: [noise()],
|
||||
connectionManager: {
|
||||
...(this.config.dialTimeoutMs != null
|
||||
...(this.config.dialTimeoutMs !== undefined
|
||||
? {
|
||||
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: () => {
|
||||
return Promise.resolve(false);
|
||||
// By default, this function forbids connections to private peers. For example, multiaddr with ip 127.0.0.1 isn't allowed
|
||||
denyDialMultiaddr: (ma: Multiaddr) => {
|
||||
return Promise.resolve(
|
||||
denyConditions.some((dc) => {
|
||||
return dc(ma);
|
||||
}),
|
||||
);
|
||||
},
|
||||
},
|
||||
services: {
|
||||
identify: identifyService(),
|
||||
ping: pingService(),
|
||||
identify: identify(),
|
||||
ping: ping(),
|
||||
},
|
||||
});
|
||||
|
||||
@ -181,33 +199,39 @@ export class RelayConnection implements IConnection {
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
log.trace(
|
||||
"sending particle %s to %s",
|
||||
particle.id,
|
||||
this.relayAddress.toString(),
|
||||
);
|
||||
|
||||
const sink = stream.sink;
|
||||
|
||||
await pipe([fromString(serializeToString(particle))], encode(), sink);
|
||||
log.trace("data written to sink");
|
||||
await pipe([serializeParticle(particle)], encode, sink);
|
||||
|
||||
log.trace(
|
||||
"particle %s sent to %s",
|
||||
particle.id,
|
||||
this.relayAddress.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
// Await will appear after uncommenting lines in func body
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
private async processIncomingMessage(msg: string, stream: Stream) {
|
||||
private async processIncomingMessage(msg: Uint8Array) {
|
||||
let particle: Particle | undefined;
|
||||
|
||||
try {
|
||||
particle = Particle.fromString(msg);
|
||||
particle = Particle.deserialize(msg);
|
||||
|
||||
log.trace(
|
||||
"got particle from stream with id %s and particle id %s",
|
||||
stream.id,
|
||||
"received particle %s from %s",
|
||||
particle.id,
|
||||
this.relayAddress.toString(),
|
||||
);
|
||||
|
||||
const initPeerId = peerIdFromString(particle.initPeerId);
|
||||
@ -260,16 +284,16 @@ export class RelayConnection implements IConnection {
|
||||
({ stream }) => {
|
||||
void pipe(
|
||||
stream.source,
|
||||
decode(),
|
||||
decode,
|
||||
(source) => {
|
||||
return map(source, (buf) => {
|
||||
return toString(buf.subarray());
|
||||
return buf.subarray();
|
||||
});
|
||||
},
|
||||
async (source) => {
|
||||
try {
|
||||
for await (const msg of source) {
|
||||
await this.processIncomingMessage(msg, stream);
|
||||
await this.processIncomingMessage(msg);
|
||||
}
|
||||
} catch (e) {
|
||||
log.error("connection closed: %j", e);
|
||||
|
@ -14,13 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, describe, expect, beforeEach, afterEach } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
import { DEFAULT_CONFIG, FluencePeer } from "../../jsPeer/FluencePeer.js";
|
||||
import { ResultCodes } from "../../jsServiceHost/interfaces.js";
|
||||
import { KeyPair } from "../../keypair/index.js";
|
||||
import { loadMarineDeps } from "../../marine/loader.js";
|
||||
import { MarineBackgroundRunner } from "../../marine/worker/index.js";
|
||||
import { EphemeralNetworkClient } from "../client.js";
|
||||
import { EphemeralNetwork, defaultConfig } from "../network.js";
|
||||
import { defaultConfig, EphemeralNetwork } from "../network.js";
|
||||
|
||||
let en: EphemeralNetwork;
|
||||
let client: FluencePeer;
|
||||
@ -33,7 +35,11 @@ describe.skip("Ephemeral networks tests", () => {
|
||||
await en.up();
|
||||
|
||||
const kp = await KeyPair.randomEd25519();
|
||||
client = new EphemeralNetworkClient(DEFAULT_CONFIG, kp, en, relay);
|
||||
|
||||
const marineDeps = await loadMarineDeps("/");
|
||||
const marine = new MarineBackgroundRunner(...marineDeps);
|
||||
|
||||
client = new EphemeralNetworkClient(DEFAULT_CONFIG, kp, marine, en, relay);
|
||||
await client.start();
|
||||
});
|
||||
|
||||
@ -91,7 +97,11 @@ describe.skip("Ephemeral networks tests", () => {
|
||||
});
|
||||
|
||||
// act
|
||||
client.internals.initiateParticle(particle, () => {});
|
||||
client.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
|
||||
// assert
|
||||
await expect(promise).resolves.toBe("success");
|
||||
|
@ -19,9 +19,7 @@ import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||
import { FluencePeer, PeerConfig } from "../jsPeer/FluencePeer.js";
|
||||
import { JsServiceHost } from "../jsServiceHost/JsServiceHost.js";
|
||||
import { KeyPair } from "../keypair/index.js";
|
||||
import { WasmLoaderFromNpm } from "../marine/deps-loader/node.js";
|
||||
import { MarineBackgroundRunner } from "../marine/worker/index.js";
|
||||
import { WorkerLoader } from "../marine/worker-script/workerLoader.js";
|
||||
import { IMarineHost } from "../marine/interfaces.js";
|
||||
|
||||
import { EphemeralNetwork } from "./network.js";
|
||||
|
||||
@ -32,28 +30,12 @@ export class EphemeralNetworkClient extends FluencePeer {
|
||||
constructor(
|
||||
config: PeerConfig,
|
||||
keyPair: KeyPair,
|
||||
marine: IMarineHost,
|
||||
network: EphemeralNetwork,
|
||||
relay: PeerIdB58,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,8 @@ import { IConnection } from "../connection/interfaces.js";
|
||||
import { DEFAULT_CONFIG, FluencePeer } from "../jsPeer/FluencePeer.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 { loadMarineDeps } from "../marine/loader.js";
|
||||
import { MarineBackgroundRunner } from "../marine/worker/index.js";
|
||||
import { Particle } from "../particle/Particle.js";
|
||||
import { logger } from "../util/logger.js";
|
||||
@ -33,10 +30,12 @@ import { logger } from "../util/logger.js";
|
||||
const log = logger("ephemeral");
|
||||
|
||||
interface EphemeralConfig {
|
||||
peers: Array<{
|
||||
peerId: PeerIdB58;
|
||||
sk: string;
|
||||
}>;
|
||||
peers: Readonly<
|
||||
Array<{
|
||||
peerId: PeerIdB58;
|
||||
sk: string;
|
||||
}>
|
||||
>;
|
||||
}
|
||||
|
||||
export const defaultConfig = {
|
||||
@ -122,7 +121,7 @@ export const defaultConfig = {
|
||||
sk: "RbgKmG6soWW9uOi7yRedm+0Qck3f3rw6MSnDP7AcBQs=",
|
||||
},
|
||||
],
|
||||
};
|
||||
} as const;
|
||||
|
||||
export interface IEphemeralConnection extends IConnection {
|
||||
readonly selfPeerId: PeerIdB58;
|
||||
@ -224,24 +223,7 @@ class EphemeralPeer extends FluencePeer {
|
||||
export class EphemeralNetwork {
|
||||
private peers: Map<PeerIdB58, EphemeralPeer> = new Map();
|
||||
|
||||
workerLoader: WorkerLoaderFromFs;
|
||||
controlModuleLoader: WasmLoaderFromNpm;
|
||||
avmModuleLoader: WasmLoaderFromNpm;
|
||||
|
||||
constructor(readonly config: EphemeralConfig) {
|
||||
// shared worker for all the peers
|
||||
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",
|
||||
);
|
||||
}
|
||||
constructor(readonly config: EphemeralConfig) {}
|
||||
|
||||
/**
|
||||
* Starts the Ephemeral network up
|
||||
@ -252,11 +234,8 @@ export class EphemeralNetwork {
|
||||
const promises = this.config.peers.map(async (x) => {
|
||||
const kp = await fromBase64Sk(x.sk);
|
||||
|
||||
const marine = new MarineBackgroundRunner(
|
||||
this.workerLoader,
|
||||
this.controlModuleLoader,
|
||||
this.avmModuleLoader,
|
||||
);
|
||||
const marineDeps = await loadMarineDeps("/");
|
||||
const marine = new MarineBackgroundRunner(...marineDeps);
|
||||
|
||||
const peerId = kp.getPeerId();
|
||||
|
||||
@ -271,17 +250,17 @@ export class EphemeralNetwork {
|
||||
|
||||
const peers = await Promise.all(promises);
|
||||
|
||||
for (let i = 0; i < peers.length; i++) {
|
||||
for (let j = 0; j < i; j++) {
|
||||
peers.forEach((peer, i) => {
|
||||
const otherPeers = peers.slice(0, i);
|
||||
|
||||
otherPeers.forEach((otherPeer, j) => {
|
||||
if (i === j) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
peers[i].ephemeralConnection.connectToOther(
|
||||
peers[j].ephemeralConnection,
|
||||
);
|
||||
}
|
||||
}
|
||||
peer.ephemeralConnection.connectToOther(otherPeer.ephemeralConnection);
|
||||
});
|
||||
});
|
||||
|
||||
const startPromises = peers.map((x) => {
|
||||
return x.start();
|
||||
@ -290,7 +269,7 @@ export class EphemeralNetwork {
|
||||
await Promise.all(startPromises);
|
||||
|
||||
for (const p of peers) {
|
||||
this.peers.set(p.keyPair.getPeerId(), p);
|
||||
this.peers.set(p.getPeerId(), p);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,97 +14,40 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { VersionedPackage } from "@fluencelabs/js-client-isomorphic";
|
||||
import { fetchResource } from "@fluencelabs/js-client-isomorphic/fetcher";
|
||||
import { getWorker } from "@fluencelabs/js-client-isomorphic/worker-resolver";
|
||||
import { ZodError } from "zod";
|
||||
|
||||
import { ClientPeer, makeClientPeerConfig } from "./clientPeer/ClientPeer.js";
|
||||
import {
|
||||
ClientConfig,
|
||||
configSchema,
|
||||
ConnectionState,
|
||||
RelayOptions,
|
||||
relayOptionsSchema,
|
||||
} from "./clientPeer/types.js";
|
||||
import { callAquaFunction } from "./compilerSupport/callFunction.js";
|
||||
import { registerService } from "./compilerSupport/registerService.js";
|
||||
import { loadMarineDeps } from "./marine/loader.js";
|
||||
import { MarineBackgroundRunner } from "./marine/worker/index.js";
|
||||
import versions from "./versions.js";
|
||||
|
||||
const DEFAULT_CDN_URL = "https://unpkg.com";
|
||||
|
||||
const getVersionedPackage = (pkg: keyof typeof versions): VersionedPackage => {
|
||||
return {
|
||||
name: pkg,
|
||||
version: versions[pkg],
|
||||
};
|
||||
};
|
||||
|
||||
const createClient = async (
|
||||
relay: RelayOptions,
|
||||
config: ClientConfig = {},
|
||||
): Promise<ClientPeer> => {
|
||||
try {
|
||||
relay = relayOptionsSchema.parse(relay);
|
||||
config = configSchema.parse(config);
|
||||
} catch (e) {
|
||||
if (e instanceof ZodError) {
|
||||
throw new Error(JSON.stringify(e.format()));
|
||||
}
|
||||
}
|
||||
|
||||
const CDNUrl = config.CDNUrl ?? DEFAULT_CDN_URL;
|
||||
|
||||
const fetchMarineJsWasm = async () => {
|
||||
const resource = await fetchResource(
|
||||
getVersionedPackage("@fluencelabs/marine-js"),
|
||||
"/dist/marine-js.wasm",
|
||||
CDNUrl,
|
||||
);
|
||||
|
||||
return resource.arrayBuffer();
|
||||
};
|
||||
|
||||
const fetchAvmWasm = async () => {
|
||||
const resource = await fetchResource(
|
||||
getVersionedPackage("@fluencelabs/avm"),
|
||||
"/dist/avm.wasm",
|
||||
CDNUrl,
|
||||
);
|
||||
|
||||
return resource.arrayBuffer();
|
||||
};
|
||||
|
||||
const marineJsWasm = await fetchMarineJsWasm();
|
||||
const avmWasm = await fetchAvmWasm();
|
||||
|
||||
const marine = new MarineBackgroundRunner(
|
||||
{
|
||||
async getValue() {
|
||||
return getWorker(
|
||||
getVersionedPackage("@fluencelabs/marine-worker"),
|
||||
CDNUrl,
|
||||
);
|
||||
},
|
||||
start() {
|
||||
return Promise.resolve(undefined);
|
||||
},
|
||||
stop() {
|
||||
return Promise.resolve(undefined);
|
||||
},
|
||||
},
|
||||
{
|
||||
getValue() {
|
||||
return marineJsWasm;
|
||||
},
|
||||
start(): Promise<void> {
|
||||
return Promise.resolve(undefined);
|
||||
},
|
||||
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 marineDeps = await loadMarineDeps(CDNUrl);
|
||||
const marine = new MarineBackgroundRunner(...marineDeps);
|
||||
|
||||
const { keyPair, peerConfig, relayConfig } = await makeClientPeerConfig(
|
||||
relay,
|
||||
@ -168,7 +111,7 @@ export const Fluence: FluencePublicApi = {
|
||||
* @returns IFluenceClient instance
|
||||
*/
|
||||
getClient: function () {
|
||||
if (this.defaultClient == null) {
|
||||
if (this.defaultClient === undefined) {
|
||||
throw new Error(
|
||||
"Fluence client is not initialized. Call Fluence.connect() first",
|
||||
);
|
||||
@ -178,22 +121,23 @@ export const Fluence: FluencePublicApi = {
|
||||
},
|
||||
};
|
||||
|
||||
export type { ClientConfig, IFluenceClient } from "./clientPeer/types.js";
|
||||
export type {
|
||||
ClientConfig,
|
||||
IFluenceClient,
|
||||
ConnectionState,
|
||||
Relay,
|
||||
RelayOptions,
|
||||
KeyPairOptions,
|
||||
} from "./clientPeer/types.js";
|
||||
|
||||
export type { ParticleContext } from "./jsServiceHost/interfaces.js";
|
||||
|
||||
export { v5_callFunction, v5_registerService } from "./api.js";
|
||||
|
||||
// @ts-expect-error Writing to global object like this prohibited by ts
|
||||
globalThis.new_fluence = Fluence;
|
||||
|
||||
// @ts-expect-error Writing to global object like this prohibited by ts
|
||||
globalThis.fluence = {
|
||||
clientFactory: createClient,
|
||||
callAquaFunction,
|
||||
registerService,
|
||||
};
|
||||
|
||||
export { createClient, callAquaFunction, registerService };
|
||||
|
||||
export { ClientPeer } from "./clientPeer/ClientPeer.js";
|
||||
|
||||
// Deprecated exports. Later they will be exposed only under js-client/keypair path
|
||||
export {
|
||||
KeyPair,
|
||||
@ -203,3 +147,6 @@ export {
|
||||
} from "./keypair/index.js";
|
||||
|
||||
export * from "./network.js";
|
||||
|
||||
// TODO: Remove this export after DXJ-535 is done!
|
||||
export { js2aqua, aqua2js } from "./compilerSupport/conversions.js";
|
||||
|
@ -14,20 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
import {
|
||||
deserializeAvmResult,
|
||||
InterpreterResult,
|
||||
KeyPairFormat,
|
||||
serializeAvmArgs,
|
||||
} from "@fluencelabs/avm";
|
||||
import { defaultCallParameters } from "@fluencelabs/marine-js/dist/types";
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { fromUint8Array } from "js-base64";
|
||||
import {
|
||||
concatMap,
|
||||
filter,
|
||||
groupBy,
|
||||
GroupedObservable,
|
||||
lastValueFrom,
|
||||
mergeMap,
|
||||
pipe,
|
||||
@ -56,7 +55,6 @@ import {
|
||||
getActualTTL,
|
||||
hasExpired,
|
||||
Particle,
|
||||
ParticleExecutionStage,
|
||||
ParticleQueueItem,
|
||||
} from "../particle/Particle.js";
|
||||
import { registerSig } from "../services/_aqua/services.js";
|
||||
@ -66,9 +64,12 @@ import { defaultSigGuard, Sig } from "../services/Sig.js";
|
||||
import { Srv } from "../services/SingleModuleSrv.js";
|
||||
import { Tracing } from "../services/Tracing.js";
|
||||
import { logger } from "../util/logger.js";
|
||||
import { jsonify, isString, getErrorMessage } from "../util/utils.js";
|
||||
import { getErrorMessage, isString, jsonify } from "../util/utils.js";
|
||||
|
||||
import { ExpirationError, InterpreterError, SendError } from "./errors.js";
|
||||
|
||||
const log_particle = logger("particle");
|
||||
const log_particle_data = logger("particle:data");
|
||||
const log_peer = logger("peer");
|
||||
|
||||
export type PeerConfig = {
|
||||
@ -105,7 +106,7 @@ export const DEFAULT_CONFIG: PeerConfig = {
|
||||
export abstract class FluencePeer {
|
||||
constructor(
|
||||
protected readonly config: PeerConfig,
|
||||
readonly keyPair: KeyPair,
|
||||
protected readonly keyPair: KeyPair,
|
||||
protected readonly marineHost: IMarineHost,
|
||||
protected readonly jsServiceHost: IJsServiceHost,
|
||||
protected readonly connection: IConnection,
|
||||
@ -113,6 +114,10 @@ export abstract class FluencePeer {
|
||||
this._initServices();
|
||||
}
|
||||
|
||||
getPeerId(): string {
|
||||
return this.keyPair.getPeerId();
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
log_peer.trace("starting Fluence peer");
|
||||
|
||||
@ -122,7 +127,7 @@ export abstract class FluencePeer {
|
||||
|
||||
await this.marineHost.start();
|
||||
|
||||
this._startParticleProcessing();
|
||||
this.startParticleProcessing();
|
||||
this.isInitialized = true;
|
||||
await this.connection.start();
|
||||
log_peer.trace("started Fluence peer");
|
||||
@ -142,7 +147,7 @@ export abstract class FluencePeer {
|
||||
await this._incomingParticlePromise;
|
||||
log_peer.trace("All particles finished execution");
|
||||
|
||||
this._stopParticleProcessing();
|
||||
this.stopParticleProcessing();
|
||||
await this.marineHost.stop();
|
||||
await this.connection.stop();
|
||||
this.isInitialized = false;
|
||||
@ -159,7 +164,7 @@ export abstract class FluencePeer {
|
||||
* @param serviceId - the service id by which the service can be accessed in aqua
|
||||
*/
|
||||
async registerMarineService(
|
||||
wasm: SharedArrayBuffer | Buffer,
|
||||
wasm: ArrayBuffer | SharedArrayBuffer,
|
||||
serviceId: string,
|
||||
): Promise<void> {
|
||||
if (this.jsServiceHost.hasService(serviceId)) {
|
||||
@ -201,12 +206,7 @@ export abstract class FluencePeer {
|
||||
new Error("Can't use avm: peer is not initialized");
|
||||
}
|
||||
|
||||
const res = await this.marineHost.callService(
|
||||
"avm",
|
||||
"ast",
|
||||
[air],
|
||||
defaultCallParameters,
|
||||
);
|
||||
const res = await this.marineHost.callService("avm", "ast", [air]);
|
||||
|
||||
if (!isString(res)) {
|
||||
throw new Error(
|
||||
@ -253,11 +253,15 @@ export abstract class FluencePeer {
|
||||
/**
|
||||
* Initiates a new particle execution starting from local peer
|
||||
* @param particle - particle to start execution of
|
||||
* @param onStageChange - callback for reacting on particle state changes
|
||||
* @param onSuccess - callback which is called when particle execution succeed
|
||||
* @param onError - callback which is called when particle execution fails
|
||||
* @param fireAndForget - determines whether particle has fire-and-forget behavior
|
||||
*/
|
||||
initiateParticle: (
|
||||
particle: IParticle,
|
||||
onStageChange: (stage: ParticleExecutionStage) => void,
|
||||
onSuccess: (result: JSONValue) => void,
|
||||
onError: (error: Error) => void,
|
||||
fireAndForget: boolean = true,
|
||||
): void => {
|
||||
if (!this.isInitialized) {
|
||||
throw new Error(
|
||||
@ -274,7 +278,9 @@ export abstract class FluencePeer {
|
||||
this._incomingParticles.next({
|
||||
particle: particle,
|
||||
callResults: [],
|
||||
onStageChange: onStageChange,
|
||||
onSuccess,
|
||||
onError,
|
||||
fireAndForget,
|
||||
});
|
||||
},
|
||||
|
||||
@ -301,7 +307,7 @@ export abstract class FluencePeer {
|
||||
// Queues for incoming and outgoing particles
|
||||
|
||||
private _incomingParticles = new Subject<ParticleQueueItem>();
|
||||
private _timeouts: Array<NodeJS.Timeout> = [];
|
||||
private timeouts: Record<string, NodeJS.Timeout> = {};
|
||||
private _particleSourceSubscription?: Unsubscribable;
|
||||
private _incomingParticlePromise?: Promise<void>;
|
||||
|
||||
@ -335,315 +341,96 @@ export abstract class FluencePeer {
|
||||
registerTracing(this, "tracingSrv", this._classServices.tracing);
|
||||
}
|
||||
|
||||
private _startParticleProcessing() {
|
||||
this._particleSourceSubscription = this.connection.particleSource.subscribe(
|
||||
{
|
||||
next: (p) => {
|
||||
this._incomingParticles.next({
|
||||
particle: p,
|
||||
callResults: [],
|
||||
onStageChange: () => {},
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
this._incomingParticlePromise = lastValueFrom(
|
||||
this._incomingParticles.pipe(
|
||||
tap((item) => {
|
||||
log_particle.debug("id %s. received:", item.particle.id);
|
||||
|
||||
log_particle.trace("id %s. data: %j", item.particle.id, {
|
||||
initPeerId: item.particle.initPeerId,
|
||||
timestamp: item.particle.timestamp,
|
||||
ttl: item.particle.ttl,
|
||||
signature: item.particle.signature,
|
||||
});
|
||||
|
||||
log_particle.trace(
|
||||
"id %s. script: %s",
|
||||
item.particle.id,
|
||||
item.particle.script,
|
||||
);
|
||||
|
||||
log_particle.trace(
|
||||
"id %s. call results: %j",
|
||||
item.particle.id,
|
||||
item.callResults,
|
||||
);
|
||||
}),
|
||||
filterExpiredParticles(this._expireParticle.bind(this)),
|
||||
groupBy((item) => {
|
||||
return fromUint8Array(item.particle.signature);
|
||||
}),
|
||||
mergeMap((group$) => {
|
||||
let prevData: Uint8Array = Buffer.from([]);
|
||||
let firstRun = true;
|
||||
|
||||
return group$.pipe(
|
||||
concatMap(async (item) => {
|
||||
if (firstRun) {
|
||||
const timeout = setTimeout(() => {
|
||||
this._expireParticle(item);
|
||||
}, getActualTTL(item.particle));
|
||||
|
||||
this._timeouts.push(timeout);
|
||||
firstRun = false;
|
||||
}
|
||||
|
||||
if (!this.isInitialized) {
|
||||
// If `.stop()` was called return null to stop particle processing immediately
|
||||
return null;
|
||||
}
|
||||
|
||||
// IMPORTANT!
|
||||
// AVM runner execution and prevData <-> newData swapping
|
||||
// MUST happen sequentially (in a critical section).
|
||||
// Otherwise the race might occur corrupting the prevData
|
||||
|
||||
log_particle.debug(
|
||||
"id %s. sending particle to interpreter",
|
||||
item.particle.id,
|
||||
);
|
||||
|
||||
log_particle.trace(
|
||||
"id %s. prevData: %s",
|
||||
item.particle.id,
|
||||
this.decodeAvmData(prevData).slice(0, 50),
|
||||
);
|
||||
|
||||
const args = serializeAvmArgs(
|
||||
{
|
||||
initPeerId: item.particle.initPeerId,
|
||||
currentPeerId: this.keyPair.getPeerId(),
|
||||
timestamp: item.particle.timestamp,
|
||||
ttl: item.particle.ttl,
|
||||
keyFormat: KeyPairFormat.Ed25519,
|
||||
particleId: item.particle.id,
|
||||
secretKeyBytes: this.keyPair.toEd25519PrivateKey(),
|
||||
},
|
||||
item.particle.script,
|
||||
prevData,
|
||||
item.particle.data,
|
||||
item.callResults,
|
||||
);
|
||||
|
||||
let avmCallResult: InterpreterResult | Error;
|
||||
|
||||
try {
|
||||
const res = await this.marineHost.callService(
|
||||
"avm",
|
||||
"invoke",
|
||||
args,
|
||||
defaultCallParameters,
|
||||
);
|
||||
|
||||
avmCallResult = deserializeAvmResult(res);
|
||||
} catch (e) {
|
||||
avmCallResult = e instanceof Error ? e : new Error(String(e));
|
||||
}
|
||||
|
||||
if (
|
||||
!(avmCallResult instanceof Error) &&
|
||||
avmCallResult.retCode === 0
|
||||
) {
|
||||
const newData = Buffer.from(avmCallResult.data);
|
||||
prevData = newData;
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
result: avmCallResult,
|
||||
};
|
||||
}),
|
||||
filter((item): item is NonNullable<typeof item> => {
|
||||
return item !== null;
|
||||
}),
|
||||
filterExpiredParticles<
|
||||
ParticleQueueItem & {
|
||||
result: Error | InterpreterResult;
|
||||
}
|
||||
>(this._expireParticle.bind(this)),
|
||||
mergeMap(async (item) => {
|
||||
// If peer was stopped, do not proceed further
|
||||
if (!this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not continue if there was an error in particle interpretation
|
||||
if (item.result instanceof Error) {
|
||||
log_particle.error(
|
||||
"id %s. interpreter failed: %s",
|
||||
item.particle.id,
|
||||
item.result.message,
|
||||
);
|
||||
|
||||
item.onStageChange({
|
||||
stage: "interpreterError",
|
||||
errorMessage: item.result.message,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.result.retCode !== 0) {
|
||||
log_particle.error(
|
||||
"id %s. interpreter failed: retCode: %d, message: %s",
|
||||
item.particle.id,
|
||||
item.result.retCode,
|
||||
item.result.errorMessage,
|
||||
);
|
||||
|
||||
log_particle.trace(
|
||||
"id %s. avm data: %s",
|
||||
item.particle.id,
|
||||
this.decodeAvmData(item.result.data),
|
||||
);
|
||||
|
||||
item.onStageChange({
|
||||
stage: "interpreterError",
|
||||
errorMessage: item.result.errorMessage,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
log_particle.trace(
|
||||
"id %s. interpreter result: retCode: %d, avm data: %s",
|
||||
item.particle.id,
|
||||
item.result.retCode,
|
||||
this.decodeAvmData(item.result.data),
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
item.onStageChange({ stage: "interpreted" });
|
||||
}, 0);
|
||||
|
||||
let connectionPromise: Promise<void> = Promise.resolve();
|
||||
|
||||
// send particle further if requested
|
||||
if (item.result.nextPeerPks.length > 0) {
|
||||
const newParticle = cloneWithNewData(
|
||||
item.particle,
|
||||
Buffer.from(item.result.data),
|
||||
);
|
||||
|
||||
log_particle.debug(
|
||||
"id %s. sending particle into network. Next peer ids: %s",
|
||||
newParticle.id,
|
||||
item.result.nextPeerPks.toString(),
|
||||
);
|
||||
|
||||
connectionPromise = this.connection
|
||||
.sendParticle(item.result.nextPeerPks, newParticle)
|
||||
.then(() => {
|
||||
log_particle.trace(
|
||||
"id %s. send successful",
|
||||
newParticle.id,
|
||||
);
|
||||
|
||||
item.onStageChange({ stage: "sent" });
|
||||
})
|
||||
.catch((e: unknown) => {
|
||||
log_particle.error(
|
||||
"id %s. send failed %j",
|
||||
newParticle.id,
|
||||
e,
|
||||
);
|
||||
|
||||
item.onStageChange({
|
||||
stage: "sendingError",
|
||||
errorMessage: getErrorMessage(e),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// execute call requests if needed
|
||||
// and put particle with the results back to queue
|
||||
if (item.result.callRequests.length > 0) {
|
||||
for (const [key, cr] of item.result.callRequests) {
|
||||
const req = {
|
||||
fnName: cr.functionName,
|
||||
args: cr.arguments,
|
||||
serviceId: cr.serviceId,
|
||||
tetraplets: cr.tetraplets,
|
||||
particleContext: getParticleContext(item.particle),
|
||||
};
|
||||
|
||||
void this._execSingleCallRequest(req)
|
||||
.catch((err): CallServiceResult => {
|
||||
if (err instanceof ServiceError) {
|
||||
return {
|
||||
retCode: ResultCodes.error,
|
||||
result: err.message,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
retCode: ResultCodes.error,
|
||||
result: `Service call failed. fnName="${
|
||||
req.fnName
|
||||
}" serviceId="${
|
||||
req.serviceId
|
||||
}" error: ${getErrorMessage(err)}`,
|
||||
};
|
||||
})
|
||||
.then((res) => {
|
||||
const serviceResult = {
|
||||
result: jsonify(res.result),
|
||||
retCode: res.retCode,
|
||||
};
|
||||
|
||||
const newParticle = cloneWithNewData(
|
||||
item.particle,
|
||||
Buffer.from([]),
|
||||
);
|
||||
|
||||
this._incomingParticles.next({
|
||||
...item,
|
||||
particle: newParticle,
|
||||
callResults: [[key, serviceResult]],
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
item.onStageChange({ stage: "localWorkDone" });
|
||||
}
|
||||
|
||||
return connectionPromise;
|
||||
}),
|
||||
);
|
||||
}),
|
||||
),
|
||||
{ defaultValue: undefined },
|
||||
);
|
||||
}
|
||||
|
||||
private _expireParticle(item: ParticleQueueItem) {
|
||||
const particleId = item.particle.id;
|
||||
private async sendParticleToRelay(
|
||||
item: ParticleQueueItem & { result: InterpreterResult },
|
||||
) {
|
||||
const newParticle = cloneWithNewData(item.particle, item.result.data);
|
||||
|
||||
log_particle.debug(
|
||||
"id %s. particle has expired after %d. Deleting particle-related queues and handlers",
|
||||
item.particle.id,
|
||||
item.particle.ttl,
|
||||
"id %s. sending particle into network. Next peer ids: %s",
|
||||
newParticle.id,
|
||||
item.result.nextPeerPks.toString(),
|
||||
);
|
||||
|
||||
this.jsServiceHost.removeParticleScopeHandlers(particleId);
|
||||
try {
|
||||
await this.connection.sendParticle(item.result.nextPeerPks, newParticle);
|
||||
log_particle.trace("id %s. send successful", newParticle.id);
|
||||
} catch (e) {
|
||||
const message = getErrorMessage(e);
|
||||
log_particle.error("id %s. send failed %s", newParticle.id, message);
|
||||
|
||||
item.onStageChange({ stage: "expired" });
|
||||
item.onError(
|
||||
new SendError(
|
||||
`Could not send particle: (particle id: ${item.particle.id}, message: ${message})`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private decodeAvmData(data: Uint8Array) {
|
||||
return new TextDecoder().decode(data.buffer);
|
||||
private async execCallRequests(
|
||||
item: ParticleQueueItem & { result: InterpreterResult },
|
||||
) {
|
||||
return Promise.all(
|
||||
item.result.callRequests.map(async ([key, cr]) => {
|
||||
const req = {
|
||||
fnName: cr.functionName,
|
||||
args: cr.arguments,
|
||||
serviceId: cr.serviceId,
|
||||
tetraplets: cr.tetraplets,
|
||||
particleContext: getParticleContext(item.particle, cr.tetraplets),
|
||||
};
|
||||
|
||||
let res: CallServiceResult;
|
||||
|
||||
try {
|
||||
res = await this.execCallRequest(req);
|
||||
} catch (err) {
|
||||
if (err instanceof ServiceError) {
|
||||
res = {
|
||||
retCode: ResultCodes.error,
|
||||
result: err.message,
|
||||
};
|
||||
}
|
||||
|
||||
res = {
|
||||
retCode: ResultCodes.error,
|
||||
result: `Service call failed. fnName="${req.fnName}" serviceId="${
|
||||
req.serviceId
|
||||
}" error: ${getErrorMessage(err)}`,
|
||||
};
|
||||
}
|
||||
|
||||
const serviceResult = {
|
||||
result: jsonify(res.result),
|
||||
retCode: res.retCode,
|
||||
};
|
||||
|
||||
const newParticle = cloneWithNewData(
|
||||
item.particle,
|
||||
Uint8Array.from([]),
|
||||
);
|
||||
|
||||
this._incomingParticles.next({
|
||||
...item,
|
||||
particle: newParticle,
|
||||
callResults: [[key, serviceResult]],
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private async _execSingleCallRequest(
|
||||
private async execCallRequest(
|
||||
req: CallServiceData,
|
||||
): Promise<CallServiceResult> {
|
||||
const particleId = req.particleContext.particleId;
|
||||
|
||||
log_particle.debug("id %s. executing call service handler %j", particleId, {
|
||||
serviceId: req.serviceId,
|
||||
functionName: req.fnName,
|
||||
});
|
||||
|
||||
log_particle.trace(
|
||||
"id %s. executing call service handler %j",
|
||||
"id %s. executing call service handler detailed %j",
|
||||
particleId,
|
||||
req,
|
||||
);
|
||||
@ -654,7 +441,6 @@ export abstract class FluencePeer {
|
||||
req.serviceId,
|
||||
req.fnName,
|
||||
req.args,
|
||||
defaultCallParameters,
|
||||
);
|
||||
|
||||
return {
|
||||
@ -684,9 +470,242 @@ export abstract class FluencePeer {
|
||||
return res;
|
||||
}
|
||||
|
||||
private _stopParticleProcessing() {
|
||||
private mapParticleGroup(
|
||||
group$: GroupedObservable<string, ParticleQueueItem>,
|
||||
) {
|
||||
let prevData = Uint8Array.from([]);
|
||||
|
||||
return group$.pipe(
|
||||
concatMap(async (item) => {
|
||||
if (!(group$.key in this.timeouts)) {
|
||||
this.timeouts[group$.key] = setTimeout(() => {
|
||||
this.expireParticle(item);
|
||||
}, getActualTTL(item.particle));
|
||||
}
|
||||
|
||||
// IMPORTANT!
|
||||
// AVM runner execution and prevData <-> newData swapping
|
||||
// MUST happen sequentially (in a critical section).
|
||||
// Otherwise the race might occur corrupting the prevData
|
||||
|
||||
log_particle.debug(
|
||||
"id %s. sending particle to interpreter",
|
||||
item.particle.id,
|
||||
);
|
||||
|
||||
log_particle_data.trace(
|
||||
"id %s. prevData: %s",
|
||||
item.particle.id,
|
||||
this.decodeAvmData(prevData),
|
||||
);
|
||||
|
||||
const args = serializeAvmArgs(
|
||||
{
|
||||
initPeerId: item.particle.initPeerId,
|
||||
currentPeerId: this.keyPair.getPeerId(),
|
||||
timestamp: item.particle.timestamp,
|
||||
ttl: item.particle.ttl,
|
||||
keyFormat: KeyPairFormat.Ed25519,
|
||||
particleId: item.particle.id,
|
||||
secretKeyBytes: this.keyPair.toEd25519PrivateKey(),
|
||||
},
|
||||
item.particle.script,
|
||||
prevData,
|
||||
item.particle.data,
|
||||
item.callResults,
|
||||
);
|
||||
|
||||
const [, , , forthParam] = args;
|
||||
|
||||
if (
|
||||
typeof forthParam === "object" &&
|
||||
"hard_limit_enabled" in forthParam
|
||||
) {
|
||||
forthParam["hard_limit_enabled"] = false;
|
||||
}
|
||||
|
||||
let avmCallResult: InterpreterResult | Error;
|
||||
|
||||
try {
|
||||
const res = await this.marineHost.callService("avm", "invoke", args);
|
||||
|
||||
avmCallResult = deserializeAvmResult(res);
|
||||
// TODO: This is bug in @fluencelabs/avm package. 'avmCallResult.data' actually number array, not Uint8Array as stated in type.
|
||||
avmCallResult.data = Uint8Array.from(avmCallResult.data);
|
||||
} catch (e) {
|
||||
avmCallResult = e instanceof Error ? e : new Error(String(e));
|
||||
}
|
||||
|
||||
if (!(avmCallResult instanceof Error) && avmCallResult.retCode === 0) {
|
||||
prevData = avmCallResult.data;
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
result: avmCallResult,
|
||||
};
|
||||
}),
|
||||
filterExpiredParticles<
|
||||
ParticleQueueItem & {
|
||||
result: Error | InterpreterResult;
|
||||
}
|
||||
>(this.expireParticle.bind(this)),
|
||||
filter(() => {
|
||||
// If peer was stopped, do not proceed further
|
||||
return this.isInitialized;
|
||||
}),
|
||||
mergeMap(async (item) => {
|
||||
// Do not continue if there was an error in particle interpretation
|
||||
if (item.result instanceof Error) {
|
||||
log_particle.error(
|
||||
"id %s. interpreter failed: %s",
|
||||
item.particle.id,
|
||||
item.result.message,
|
||||
);
|
||||
|
||||
item.onError(
|
||||
new InterpreterError(
|
||||
`Script interpretation failed: ${item.result.message} (particle id: ${item.particle.id})`,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.result.retCode !== 0) {
|
||||
log_particle.error(
|
||||
"id %s. interpreter failed: retCode: %d, message: %s",
|
||||
item.particle.id,
|
||||
item.result.retCode,
|
||||
item.result.errorMessage,
|
||||
);
|
||||
|
||||
log_particle_data.trace(
|
||||
"id %s. avm data: %s",
|
||||
item.particle.id,
|
||||
this.decodeAvmData(item.result.data),
|
||||
);
|
||||
|
||||
item.onError(
|
||||
new InterpreterError(
|
||||
`Script interpretation failed: ${item.result.errorMessage} (particle id: ${item.particle.id})`,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
log_particle.trace(
|
||||
"id %s. interpreter result: retCode: %d",
|
||||
item.particle.id,
|
||||
item.result.retCode,
|
||||
);
|
||||
|
||||
log_particle_data.trace(
|
||||
"id %s. avm data: %s",
|
||||
item.particle.id,
|
||||
this.decodeAvmData(item.result.data),
|
||||
);
|
||||
|
||||
let connectionPromise: Promise<void> = Promise.resolve();
|
||||
|
||||
// send particle further if requested
|
||||
if (item.result.nextPeerPks.length > 0) {
|
||||
// TS doesn't allow to pass just 'item'
|
||||
connectionPromise = this.sendParticleToRelay({
|
||||
...item,
|
||||
result: item.result,
|
||||
});
|
||||
}
|
||||
|
||||
// execute call requests if needed
|
||||
// and put particle with the results back to queue
|
||||
if (item.result.callRequests.length > 0) {
|
||||
// TS doesn't allow to pass just 'item'
|
||||
void this.execCallRequests({ ...item, result: item.result });
|
||||
} else if (item.fireAndForget === true) {
|
||||
// Local work done.
|
||||
item.onSuccess(null);
|
||||
}
|
||||
|
||||
return connectionPromise;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private startParticleProcessing() {
|
||||
this._particleSourceSubscription = this.connection.particleSource.subscribe(
|
||||
{
|
||||
next: (particle) => {
|
||||
this._incomingParticles.next({
|
||||
particle,
|
||||
callResults: [],
|
||||
onSuccess: () => {},
|
||||
onError: () => {},
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
this._incomingParticlePromise = lastValueFrom(
|
||||
this._incomingParticles.pipe(
|
||||
tap((item) => {
|
||||
log_particle.debug("id %s. received:", item.particle.id);
|
||||
|
||||
log_particle.trace("id %s. data: %j", item.particle.id, {
|
||||
initPeerId: item.particle.initPeerId,
|
||||
timestamp: item.particle.timestamp,
|
||||
ttl: item.particle.ttl,
|
||||
signature: item.particle.signature,
|
||||
});
|
||||
|
||||
log_particle.trace(
|
||||
"id %s. script: %s",
|
||||
item.particle.id,
|
||||
item.particle.script,
|
||||
);
|
||||
|
||||
log_particle.trace(
|
||||
"id %s. call results: %j",
|
||||
item.particle.id,
|
||||
item.callResults,
|
||||
);
|
||||
}),
|
||||
filterExpiredParticles(this.expireParticle.bind(this)),
|
||||
groupBy((item) => {
|
||||
return fromUint8Array(item.particle.signature);
|
||||
}),
|
||||
mergeMap(this.mapParticleGroup.bind(this)),
|
||||
),
|
||||
{ defaultValue: undefined },
|
||||
);
|
||||
}
|
||||
|
||||
private expireParticle(item: ParticleQueueItem) {
|
||||
const particleId = item.particle.id;
|
||||
|
||||
log_particle.debug(
|
||||
"id %s. particle has expired after %d. Deleting particle-related queues and handlers",
|
||||
item.particle.id,
|
||||
item.particle.ttl,
|
||||
);
|
||||
|
||||
this.jsServiceHost.removeParticleScopeHandlers(particleId);
|
||||
|
||||
item.onError(
|
||||
new ExpirationError(
|
||||
`Particle expired after ttl of ${item.particle.ttl}ms (particle id: ${item.particle.id})`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private decodeAvmData(data: Uint8Array) {
|
||||
return new TextDecoder().decode(data.buffer);
|
||||
}
|
||||
|
||||
private stopParticleProcessing() {
|
||||
// do not hang if the peer has been stopped while some of the timeouts are still being executed
|
||||
this._timeouts.forEach((timeout) => {
|
||||
Object.values(this.timeouts).forEach((timeout) => {
|
||||
clearTimeout(timeout);
|
||||
});
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { it, describe, expect } from "vitest";
|
||||
import { it, describe, expect, assert } from "vitest";
|
||||
|
||||
import { handleTimeout } from "../../particle/Particle.js";
|
||||
import { registerHandlersHelper, withPeer } from "../../util/testUtils.js";
|
||||
@ -39,12 +39,17 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
print: {
|
||||
print: (args): undefined => {
|
||||
const [res] = args;
|
||||
assert(res);
|
||||
resolve(res);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe("1");
|
||||
@ -76,6 +81,7 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
registerHandlersHelper(peer, particle, {
|
||||
print: {
|
||||
print: (args): undefined => {
|
||||
assert(args[0]);
|
||||
res.push(args[0]);
|
||||
|
||||
if (res.length === 2) {
|
||||
@ -85,7 +91,11 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toStrictEqual(["1", "2"]);
|
||||
@ -126,7 +136,11 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe("fast_result");
|
||||
@ -178,7 +192,11 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe("failed_with_timeout");
|
||||
|
@ -14,10 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, assert } from "vitest";
|
||||
|
||||
import {
|
||||
CallServiceData,
|
||||
@ -52,6 +50,7 @@ describe("FluencePeer flow tests", () => {
|
||||
(req: CallServiceData) => {
|
||||
const [timeout, message] = req.args;
|
||||
assert(typeof timeout === "number");
|
||||
assert(message);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
@ -77,6 +76,7 @@ describe("FluencePeer flow tests", () => {
|
||||
callback: {
|
||||
callback1: (args): undefined => {
|
||||
const [val] = args;
|
||||
assert(val);
|
||||
values.push(val);
|
||||
|
||||
if (values.length === 2) {
|
||||
@ -85,6 +85,7 @@ describe("FluencePeer flow tests", () => {
|
||||
},
|
||||
callback2: (args): undefined => {
|
||||
const [val] = args;
|
||||
assert(val);
|
||||
values.push(val);
|
||||
|
||||
if (values.length === 2) {
|
||||
@ -94,10 +95,14 @@ describe("FluencePeer flow tests", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toEqual(expect.arrayContaining(["test1", "test1"]));
|
||||
});
|
||||
}, 1500);
|
||||
}, 1800);
|
||||
});
|
||||
|
@ -16,36 +16,11 @@
|
||||
|
||||
import { it, describe, expect } from "vitest";
|
||||
|
||||
import { isFluencePeer } from "../../api.js";
|
||||
import { handleTimeout } from "../../particle/Particle.js";
|
||||
import {
|
||||
mkTestPeer,
|
||||
registerHandlersHelper,
|
||||
withPeer,
|
||||
} from "../../util/testUtils.js";
|
||||
import { registerHandlersHelper, withPeer } from "../../util/testUtils.js";
|
||||
import { FluencePeer } from "../FluencePeer.js";
|
||||
|
||||
describe("FluencePeer usage test suite", () => {
|
||||
it("should perform test for FluencePeer class correctly", async () => {
|
||||
// arrange
|
||||
const peer = await mkTestPeer();
|
||||
const number = 1;
|
||||
const object = { str: "Hello!" };
|
||||
const undefinedVal = undefined;
|
||||
|
||||
// act
|
||||
const isPeerPeer = isFluencePeer(peer);
|
||||
const isNumberPeer = isFluencePeer(number);
|
||||
const isObjectPeer = isFluencePeer(object);
|
||||
const isUndefinedPeer = isFluencePeer(undefinedVal);
|
||||
|
||||
// act
|
||||
expect(isPeerPeer).toBe(true);
|
||||
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 = `
|
||||
@ -72,7 +47,11 @@ describe("FluencePeer usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe("test");
|
||||
@ -130,7 +109,11 @@ describe("FluencePeer usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe(null);
|
||||
@ -167,7 +150,11 @@ describe("FluencePeer usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
await expect(promise).rejects.toMatchObject({
|
||||
@ -205,6 +192,6 @@ async function callIncorrectService(peer: FluencePeer) {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(particle, () => {}, handleTimeout(reject));
|
||||
});
|
||||
}
|
||||
|
@ -14,19 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Worker,
|
||||
type Worker as WorkerImplementation,
|
||||
} from "@fluencelabs/threads/master";
|
||||
/**
|
||||
* Throw when particle times out, e.g. haven't been resolved after TTL is expired
|
||||
*/
|
||||
export class ExpirationError extends Error {}
|
||||
|
||||
import { LazyLoader } from "../interfaces.js";
|
||||
/**
|
||||
* Throws when AquaVM interpreter returns an error while executing air script. It could be badly written air or internal bug.
|
||||
*/
|
||||
export class InterpreterError extends Error {}
|
||||
|
||||
export class WorkerLoader extends LazyLoader<WorkerImplementation> {
|
||||
constructor() {
|
||||
super(() => {
|
||||
return new Worker(
|
||||
"../../../node_modules/@fluencelabs/marine-worker/dist/index.js",
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Throws when network error occurs while sending particle to relay peer.
|
||||
*/
|
||||
export class SendError extends Error {}
|
@ -79,7 +79,7 @@ export enum ResultCodes {
|
||||
/**
|
||||
* Particle context. Contains additional information about particle which triggered `call` air instruction from AVM
|
||||
*/
|
||||
export interface ParticleContext {
|
||||
export type ParticleContext = {
|
||||
/**
|
||||
* The identifier of particle which triggered the call
|
||||
*/
|
||||
@ -104,7 +104,12 @@ export interface ParticleContext {
|
||||
* Particle's signature
|
||||
*/
|
||||
signature: Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Security Tetraplets received from AVM and copied here
|
||||
*/
|
||||
tetraplets: SecurityTetraplet[][];
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the information passed from AVM when a `call` air instruction is executed on the local peer
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SecurityTetraplet } from "@fluencelabs/avm";
|
||||
import { JSONArray } from "@fluencelabs/interfaces";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
@ -28,10 +29,6 @@ import {
|
||||
ResultCodes,
|
||||
} from "./interfaces.js";
|
||||
|
||||
export const doNothing = () => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const WrapFnIntoServiceCall = (
|
||||
fn: (args: JSONArray) => CallServiceResultType | undefined,
|
||||
) => {
|
||||
@ -51,20 +48,40 @@ export class ServiceError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export const getParticleContext = (particle: IParticle): ParticleContext => {
|
||||
export const getParticleContext = (
|
||||
particle: IParticle,
|
||||
tetraplets: SecurityTetraplet[][],
|
||||
): ParticleContext => {
|
||||
return {
|
||||
particleId: particle.id,
|
||||
initPeerId: particle.initPeerId,
|
||||
timestamp: particle.timestamp,
|
||||
ttl: particle.ttl,
|
||||
signature: particle.signature,
|
||||
tetraplets,
|
||||
};
|
||||
};
|
||||
|
||||
export function registerDefaultServices(peer: FluencePeer) {
|
||||
Object.entries(builtInServices).forEach(([serviceId, service]) => {
|
||||
Object.entries(service).forEach(([fnName, fn]) => {
|
||||
peer.internals.regHandler.common(serviceId, fnName, fn);
|
||||
const wrapped = async (req: CallServiceData) => {
|
||||
const res = await fn(req);
|
||||
|
||||
if (
|
||||
res.retCode === ResultCodes.error &&
|
||||
typeof res.result === "string"
|
||||
) {
|
||||
return {
|
||||
retCode: ResultCodes.error,
|
||||
result: `("${serviceId}" "${fnName}") ${res.result}`,
|
||||
};
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
peer.internals.regHandler.common(serviceId, fnName, wrapped);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -19,8 +19,7 @@ import {
|
||||
generateKeyPair,
|
||||
unmarshalPublicKey,
|
||||
} from "@libp2p/crypto/keys";
|
||||
import type { PrivateKey, PublicKey } from "@libp2p/interface/keys";
|
||||
import type { PeerId } from "@libp2p/interface/peer-id";
|
||||
import type { PrivateKey, PublicKey, PeerId } from "@libp2p/interface";
|
||||
import { createFromPrivKey } from "@libp2p/peer-id-factory";
|
||||
import bs58 from "bs58";
|
||||
import { toUint8Array } from "js-base64";
|
||||
@ -91,11 +90,11 @@ export class KeyPair {
|
||||
return this.privateKey.marshal().subarray(0, 32);
|
||||
}
|
||||
|
||||
signBytes(data: Uint8Array): Promise<Uint8Array> {
|
||||
async signBytes(data: Uint8Array): Promise<Uint8Array> {
|
||||
return this.privateKey.sign(data);
|
||||
}
|
||||
|
||||
verify(data: Uint8Array, signature: Uint8Array): Promise<boolean> {
|
||||
async verify(data: Uint8Array, signature: Uint8Array): Promise<boolean> {
|
||||
return this.publicKey.verify(data, signature);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import * as url from "url";
|
||||
|
||||
import { it, describe, expect, beforeAll } from "vitest";
|
||||
import { it, describe, expect, beforeAll, assert } from "vitest";
|
||||
|
||||
import { compileAqua, CompiledFnCall, withPeer } from "../../util/testUtils.js";
|
||||
|
||||
@ -46,6 +46,7 @@ describe("Marine js tests", () => {
|
||||
await peer.registerMarineService(wasm, "greeting");
|
||||
|
||||
// act
|
||||
assert(aqua["call"]);
|
||||
const res = await aqua["call"](peer, { arg: "test" });
|
||||
|
||||
// assert
|
||||
|
@ -1,98 +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 fs from "fs";
|
||||
import { createRequire } from "module";
|
||||
import path from "path";
|
||||
|
||||
import {
|
||||
Worker,
|
||||
type Worker as WorkerImplementation,
|
||||
} from "@fluencelabs/threads/master";
|
||||
|
||||
import { LazyLoader } from "../interfaces.js";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
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 npm package. Only works in nodejs environment.
|
||||
* The function returns SharedArrayBuffer compatible with FluenceAppService methods.
|
||||
* @param source - object specifying the source of the file. Consist two fields: package name and file path.
|
||||
* @returns SharedArrayBuffer with the wasm file
|
||||
*/
|
||||
export const loadWasmFromNpmPackage = async (source: {
|
||||
package: string;
|
||||
file: string;
|
||||
}): Promise<SharedArrayBuffer> => {
|
||||
const packagePath = require.resolve(source.package);
|
||||
const filePath = path.join(path.dirname(packagePath), source.file);
|
||||
return loadWasmFromFileSystem(filePath);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load wasm file from the file system. Only works in nodejs environment.
|
||||
* The functions returns SharedArrayBuffer compatible with FluenceAppService methods.
|
||||
* @param filePath - path to the wasm file
|
||||
* @returns SharedArrayBuffer with the wasm fileWorker
|
||||
*/
|
||||
export const loadWasmFromFileSystem = async (
|
||||
filePath: string,
|
||||
): Promise<SharedArrayBuffer> => {
|
||||
const buffer = await fs.promises.readFile(filePath);
|
||||
return bufferToSharedArrayBuffer(buffer);
|
||||
};
|
||||
|
||||
export class WasmLoaderFromFs extends LazyLoader<SharedArrayBuffer> {
|
||||
constructor(filePath: string) {
|
||||
super(() => {
|
||||
return loadWasmFromFileSystem(filePath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class WasmLoaderFromNpm extends LazyLoader<SharedArrayBuffer> {
|
||||
constructor(pkg: string, file: string) {
|
||||
super(() => {
|
||||
return loadWasmFromNpmPackage({ package: pkg, file: file });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkerLoaderFromFs extends LazyLoader<WorkerImplementation> {
|
||||
constructor(scriptPath: string) {
|
||||
super(() => {
|
||||
return new Worker(scriptPath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkerLoaderFromNpm extends LazyLoader<WorkerImplementation> {
|
||||
constructor(pkg: string, file: string) {
|
||||
super(() => {
|
||||
const packagePath = require.resolve(pkg);
|
||||
const scriptPath = path.join(path.dirname(packagePath), file);
|
||||
return new Worker(scriptPath);
|
||||
});
|
||||
}
|
||||
}
|
@ -14,15 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
CallResultsArray,
|
||||
InterpreterResult,
|
||||
RunParameters,
|
||||
} from "@fluencelabs/avm";
|
||||
import { JSONObject, JSONValue, JSONArray } from "@fluencelabs/interfaces";
|
||||
import type { Worker as WorkerImplementation } from "@fluencelabs/threads/master";
|
||||
import { CallParameters } from "@fluencelabs/marine-worker";
|
||||
|
||||
import { IStartable, CallParameters } from "../util/commonTypes.js";
|
||||
import { IStartable } from "../util/commonTypes.js";
|
||||
|
||||
/**
|
||||
* Contract for marine host implementations. Marine host is responsible for creating calling and removing marine services
|
||||
@ -53,72 +48,6 @@ export interface IMarineHost extends IStartable {
|
||||
serviceId: string,
|
||||
functionName: string,
|
||||
args: JSONArray | JSONObject,
|
||||
callParams: CallParameters,
|
||||
callParams?: CallParameters,
|
||||
): Promise<JSONValue>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for different implementations of AVM runner
|
||||
*/
|
||||
export interface IAvmRunner extends IStartable {
|
||||
/**
|
||||
* Run AVM interpreter with the specified parameters
|
||||
*/
|
||||
run(
|
||||
runParams: RunParameters,
|
||||
air: string,
|
||||
prevData: Uint8Array,
|
||||
data: Uint8Array,
|
||||
callResults: CallResultsArray,
|
||||
): Promise<InterpreterResult | Error>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for something which can hold a value
|
||||
*/
|
||||
export interface IValueLoader<T> {
|
||||
getValue(): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for something which can load wasm files
|
||||
*/
|
||||
export interface IWasmLoader
|
||||
extends IValueLoader<ArrayBuffer | SharedArrayBuffer>,
|
||||
IStartable {}
|
||||
|
||||
/**
|
||||
* Interface for something which can thread.js based worker
|
||||
*/
|
||||
export interface IWorkerLoader
|
||||
extends IValueLoader<WorkerImplementation | Promise<WorkerImplementation>>,
|
||||
IStartable {}
|
||||
|
||||
/**
|
||||
* Lazy loader for some value. Value is loaded only when `start` method is called
|
||||
*/
|
||||
export class LazyLoader<T> implements IStartable, IValueLoader<T> {
|
||||
private value: T | null = null;
|
||||
|
||||
constructor(private loadValue: () => Promise<T> | T) {}
|
||||
|
||||
getValue(): T {
|
||||
if (this.value == null) {
|
||||
throw new Error(
|
||||
"Value has not been loaded. Call `start` method to load the value.",
|
||||
);
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
async start() {
|
||||
if (this.value !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.value = await this.loadValue();
|
||||
}
|
||||
|
||||
async stop() {}
|
||||
}
|
||||
|
46
packages/core/js-client/src/marine/loader.ts
Normal file
46
packages/core/js-client/src/marine/loader.ts
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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 { fetchResource } from "@fluencelabs/js-client-isomorphic/fetcher";
|
||||
import { getWorker } from "@fluencelabs/js-client-isomorphic/worker-resolver";
|
||||
import type { MarineBackgroundInterface } from "@fluencelabs/marine-worker";
|
||||
import type { ModuleThread } from "@fluencelabs/threads/master";
|
||||
|
||||
type StrategyReturnType = [
|
||||
marineJsWasm: ArrayBuffer,
|
||||
avmWasm: ArrayBuffer,
|
||||
worker: ModuleThread<MarineBackgroundInterface>,
|
||||
];
|
||||
|
||||
export const loadMarineDeps = async (
|
||||
CDNUrl: string,
|
||||
): Promise<StrategyReturnType> => {
|
||||
const [marineJsWasm, avmWasm, worker] = await Promise.all([
|
||||
fetchResource(
|
||||
"@fluencelabs/marine-js",
|
||||
"/dist/marine-js.wasm",
|
||||
CDNUrl,
|
||||
).then((res) => {
|
||||
return res.arrayBuffer();
|
||||
}),
|
||||
fetchResource("@fluencelabs/avm", "/dist/avm.wasm", CDNUrl).then((res) => {
|
||||
return res.arrayBuffer();
|
||||
}),
|
||||
getWorker("@fluencelabs/marine-worker", CDNUrl),
|
||||
]);
|
||||
|
||||
return [marineJsWasm, avmWasm, worker];
|
||||
};
|
@ -16,101 +16,63 @@
|
||||
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import type {
|
||||
JSONArray,
|
||||
JSONObject,
|
||||
MarineBackgroundInterface,
|
||||
LogFunction,
|
||||
JSONValueNonNullable,
|
||||
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 "@fluencelabs/threads/master";
|
||||
} from "@fluencelabs/marine-worker";
|
||||
import { ModuleThread, Thread } from "@fluencelabs/threads/master";
|
||||
|
||||
import { MarineLogger, marineLogger } from "../../util/logger.js";
|
||||
import { IMarineHost, IWasmLoader, IWorkerLoader } from "../interfaces.js";
|
||||
import { IMarineHost } from "../interfaces.js";
|
||||
|
||||
export class MarineBackgroundRunner implements IMarineHost {
|
||||
private workerThread?: ModuleThread<MarineBackgroundInterface>;
|
||||
|
||||
private loggers = new Map<string, MarineLogger>();
|
||||
|
||||
constructor(
|
||||
private workerLoader: IWorkerLoader,
|
||||
private controlModuleLoader: IWasmLoader,
|
||||
private avmWasmLoader: IWasmLoader,
|
||||
private marineJsWasm: ArrayBuffer,
|
||||
private avmWasm: ArrayBuffer,
|
||||
private workerThread: ModuleThread<MarineBackgroundInterface>,
|
||||
) {}
|
||||
|
||||
async hasService(serviceId: string) {
|
||||
if (this.workerThread == null) {
|
||||
throw new Error("Worker is not initialized");
|
||||
}
|
||||
|
||||
return this.workerThread.hasService(serviceId);
|
||||
}
|
||||
|
||||
async removeService(serviceId: string) {
|
||||
if (this.workerThread == null) {
|
||||
throw new Error("Worker is not initialized");
|
||||
}
|
||||
|
||||
await this.workerThread.removeService(serviceId);
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
if (this.workerThread != null) {
|
||||
throw new Error("Worker thread already initialized");
|
||||
}
|
||||
|
||||
await this.controlModuleLoader.start();
|
||||
const wasm = this.controlModuleLoader.getValue();
|
||||
|
||||
await this.avmWasmLoader.start();
|
||||
|
||||
await this.workerLoader.start();
|
||||
const worker = await this.workerLoader.getValue();
|
||||
|
||||
const workerThread: ModuleThread<MarineBackgroundInterface> =
|
||||
await spawn<MarineBackgroundInterface>(worker);
|
||||
|
||||
const logfn: LogFunction = (message) => {
|
||||
const serviceLogger = this.loggers.get(message.service);
|
||||
|
||||
if (serviceLogger == null) {
|
||||
if (serviceLogger === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
serviceLogger[message.level](message.message);
|
||||
};
|
||||
|
||||
workerThread.onLogMessage().subscribe(logfn);
|
||||
await workerThread.init(wasm);
|
||||
this.workerThread = workerThread;
|
||||
await this.createService(this.avmWasmLoader.getValue(), "avm");
|
||||
this.workerThread.onLogMessage().subscribe(logfn);
|
||||
await this.workerThread.init(this.marineJsWasm);
|
||||
await this.createService(this.avmWasm, "avm");
|
||||
}
|
||||
|
||||
async createService(
|
||||
serviceModule: ArrayBuffer | SharedArrayBuffer,
|
||||
serviceId: string,
|
||||
): Promise<void> {
|
||||
if (this.workerThread == null) {
|
||||
throw new Error("Worker is not initialized");
|
||||
}
|
||||
|
||||
// The logging level is controlled by the environment variable passed to enable debug logs.
|
||||
// We enable all possible log levels passing the control for exact printouts to the logger
|
||||
const env = logLevelToEnv("info");
|
||||
this.loggers.set(serviceId, marineLogger(serviceId));
|
||||
await this.workerThread.createService(serviceModule, serviceId, env);
|
||||
await this.workerThread.createService(serviceModule, serviceId);
|
||||
}
|
||||
|
||||
async callService(
|
||||
serviceId: string,
|
||||
functionName: string,
|
||||
args: JSONArray | JSONObject,
|
||||
callParams: CallParameters,
|
||||
args: Array<JSONValueNonNullable> | Record<string, JSONValueNonNullable>,
|
||||
callParams?: CallParameters,
|
||||
): Promise<JSONValue> {
|
||||
if (this.workerThread == null) {
|
||||
throw new Error("Worker is not initialized");
|
||||
}
|
||||
|
||||
return this.workerThread.callService(
|
||||
serviceId,
|
||||
functionName,
|
||||
@ -120,10 +82,6 @@ export class MarineBackgroundRunner implements IMarineHost {
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (this.workerThread == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.workerThread.terminate();
|
||||
await Thread.terminate(this.workerThread);
|
||||
}
|
||||
|
@ -52,57 +52,57 @@ export const stage: Relay[] = [
|
||||
export const testNet: Relay[] = [
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/0-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz",
|
||||
"/dns4/0-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz",
|
||||
peerId: "12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/1-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9",
|
||||
"/dns4/1-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9",
|
||||
peerId: "12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/2-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er",
|
||||
"/dns4/2-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er",
|
||||
peerId: "12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/3-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb",
|
||||
"/dns4/3-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb",
|
||||
peerId: "12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/4-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB",
|
||||
"/dns4/4-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB",
|
||||
peerId: "12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/5-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWCKCeqLPSgMnDjyFsJuWqREDtKNHx1JEBiwaMXhCLNTRb",
|
||||
"/dns4/5-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWCKCeqLPSgMnDjyFsJuWqREDtKNHx1JEBiwaMXhCLNTRb",
|
||||
peerId: "12D3KooWCKCeqLPSgMnDjyFsJuWqREDtKNHx1JEBiwaMXhCLNTRb",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/6-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH",
|
||||
"/dns4/6-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH",
|
||||
peerId: "12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/7-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWBSdm6TkqnEFrgBuSkpVE3dR1kr6952DsWQRNwJZjFZBv",
|
||||
"/dns4/7-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWBSdm6TkqnEFrgBuSkpVE3dR1kr6952DsWQRNwJZjFZBv",
|
||||
peerId: "12D3KooWBSdm6TkqnEFrgBuSkpVE3dR1kr6952DsWQRNwJZjFZBv",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/8-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H",
|
||||
"/dns4/8-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H",
|
||||
peerId: "12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/9-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWF7gjXhQ4LaKj6j7ntxsPpGk34psdQicN2KNfBi9bFKXg",
|
||||
"/dns4/9-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWF7gjXhQ4LaKj6j7ntxsPpGk34psdQicN2KNfBi9bFKXg",
|
||||
peerId: "12D3KooWF7gjXhQ4LaKj6j7ntxsPpGk34psdQicN2KNfBi9bFKXg",
|
||||
},
|
||||
{
|
||||
multiaddr:
|
||||
"/dns4/10-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWB9P1xmV3c7ZPpBemovbwCiRRTKd3Kq2jsVPQN4ZukDfy",
|
||||
"/dns4/10-dar.fluence.dev/tcp/9000/wss/p2p/12D3KooWB9P1xmV3c7ZPpBemovbwCiRRTKd3Kq2jsVPQN4ZukDfy",
|
||||
peerId: "12D3KooWB9P1xmV3c7ZPpBemovbwCiRRTKd3Kq2jsVPQN4ZukDfy",
|
||||
},
|
||||
];
|
||||
@ -165,21 +165,23 @@ export const kras: Relay[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const randomKras = () => {
|
||||
export const randomKras = (): Relay => {
|
||||
return randomItem(kras);
|
||||
};
|
||||
|
||||
export const randomTestNet = () => {
|
||||
export const randomTestNet = (): Relay => {
|
||||
return randomItem(testNet);
|
||||
};
|
||||
|
||||
export const randomStage = () => {
|
||||
export const randomStage = (): Relay => {
|
||||
return randomItem(stage);
|
||||
};
|
||||
|
||||
function randomItem(arr: Relay[]) {
|
||||
function randomItem(arr: Relay[]): Relay {
|
||||
const index = randomInt(0, arr.length);
|
||||
return arr[index];
|
||||
// This array access always defined
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
return arr[index] as Relay;
|
||||
}
|
||||
|
||||
function randomInt(min: number, max: number) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user