mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2024-12-12 13:35:32 +00:00
feat(js-client)!: Adding strictes eslint and ts config to all packages [fixes DXJ-464] (#355)
* introduce eslint * Fix all eslint errors * Eslint fix and some touches * Fix tests * Fix misc errors * change semver * change semver #2 * Fix path * Fix path #2 * freeze lock file in CI * fix package install * Fix formatting of surrounding files * Add empty prettier config * Fix formatting * Fix build errors * Remove unused deps * remove changelog from formatting * deps cleanup * make resource importers async * Refactor * Fix error message * remove comment * more refactoring * Update packages/core/js-client/src/compilerSupport/registerService.ts Co-authored-by: shamsartem <shamsartem@gmail.com> * refactoring * refactoring fix * optimize import * Update packages/@tests/smoke/node/src/index.ts Co-authored-by: shamsartem <shamsartem@gmail.com> * Revert package * Fix pnpm lock * Lint-fix * Fix CI * Update tests * Fix build * Fix import * Use forked threads dep * Use fixed version * Update threads * Fix lint * Fix test * Fix test * Add polyfill for assert * Add subpath import * Fix tests * Fix deps --------- Co-authored-by: shamsartem <shamsartem@gmail.com>
This commit is contained in:
parent
b46933252a
commit
919c7d6ea1
@ -1,12 +0,0 @@
|
|||||||
# EditorConfig is awesome: https://EditorConfig.org
|
|
||||||
|
|
||||||
# top-most EditorConfig file
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
insert_final_newline = false
|
|
133
.eslintrc.json
Normal file
133
.eslintrc.json
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2022,
|
||||||
|
"project": ["./tsconfig.eslint.json"],
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/strict-type-checked",
|
||||||
|
"plugin:import/recommended",
|
||||||
|
"plugin:import/typescript",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint",
|
||||||
|
"import",
|
||||||
|
"license-header",
|
||||||
|
"unused-imports"
|
||||||
|
],
|
||||||
|
"ignorePatterns": ["**/node_modules/**/*", "**/dist/**/*"],
|
||||||
|
"rules": {
|
||||||
|
"eqeqeq": [
|
||||||
|
"error",
|
||||||
|
"always",
|
||||||
|
{
|
||||||
|
"null": "ignore"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-console": ["error"],
|
||||||
|
"arrow-body-style": ["error", "always"],
|
||||||
|
"no-empty": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allowEmptyCatch": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"curly": ["error", "all"],
|
||||||
|
"no-unused-expressions": ["error"],
|
||||||
|
"dot-notation": ["off"],
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
"padding-line-between-statements": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "multiline-expression",
|
||||||
|
"next": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "*",
|
||||||
|
"next": "multiline-expression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "multiline-block-like",
|
||||||
|
"next": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "*",
|
||||||
|
"next": "multiline-block-like"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "multiline-const",
|
||||||
|
"next": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "*",
|
||||||
|
"next": "multiline-const"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "multiline-let",
|
||||||
|
"next": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "always",
|
||||||
|
"prev": "*",
|
||||||
|
"next": "multiline-let"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blankLine": "any",
|
||||||
|
"prev": "case",
|
||||||
|
"next": "case"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"import/extensions": ["error", "ignorePackages"],
|
||||||
|
"import/no-unresolved": "off",
|
||||||
|
"import/no-cycle": ["error"],
|
||||||
|
"import/order": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"newlines-between": "always",
|
||||||
|
"alphabetize": {
|
||||||
|
"order": "asc",
|
||||||
|
"caseInsensitive": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"node/no-unsupported-features/es-syntax": "off",
|
||||||
|
"node/no-unpublished-import": "off",
|
||||||
|
"node/no-missing-import": "off",
|
||||||
|
"@typescript-eslint/explicit-member-accessibility": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"accessibility": "no-public"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/strict-boolean-expressions": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allowString": false,
|
||||||
|
"allowNumber": false,
|
||||||
|
"allowNullableObject": false,
|
||||||
|
"allowNullableBoolean": false,
|
||||||
|
"allowNullableString": false,
|
||||||
|
"allowNullableNumber": false,
|
||||||
|
"allowAny": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/consistent-type-assertions": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"assertionStyle": "never"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
"license-header/header": ["error", "./resources/license-header.js"]
|
||||||
|
}
|
||||||
|
}
|
9
.github/workflows/tests.yml
vendored
9
.github/workflows/tests.yml
vendored
@ -88,6 +88,11 @@ jobs:
|
|||||||
registry-url: "https://npm.fluence.dev"
|
registry-url: "https://npm.fluence.dev"
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- run: pnpm -r i
|
||||||
|
- run: pnpm -r build
|
||||||
|
- run: pnpm lint-check
|
||||||
|
|
||||||
|
|
||||||
- name: Override dependencies
|
- name: Override dependencies
|
||||||
uses: fluencelabs/github-actions/pnpm-set-dependency@main
|
uses: fluencelabs/github-actions/pnpm-set-dependency@main
|
||||||
with:
|
with:
|
||||||
@ -97,10 +102,8 @@ jobs:
|
|||||||
"@fluencelabs/marine-js": "${{ inputs.marine-js-version }}"
|
"@fluencelabs/marine-js": "${{ inputs.marine-js-version }}"
|
||||||
}
|
}
|
||||||
|
|
||||||
- run: pnpm -r --no-frozen-lockfile i
|
- run: pnpm -r i
|
||||||
- run: pnpm -r build
|
|
||||||
- run: pnpm -r test
|
- run: pnpm -r test
|
||||||
|
|
||||||
- name: Dump rust-peer logs
|
- name: Dump rust-peer logs
|
||||||
if: always()
|
if: always()
|
||||||
uses: jwalton/gh-docker-logs@v2
|
uses: jwalton/gh-docker-logs@v2
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
*.log
|
*.log
|
||||||
.idea
|
.idea
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
# Dependency directories
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
|
@ -1 +1,10 @@
|
|||||||
.github
|
.github
|
||||||
|
.eslintcache
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
**/node_modules
|
||||||
|
**/dist
|
||||||
|
**/build
|
||||||
|
**/public
|
||||||
|
|
||||||
|
**/CHANGELOG.md
|
1
.prettierrc
Normal file
1
.prettierrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
@ -1,8 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
semi: true,
|
|
||||||
trailingComma: 'all',
|
|
||||||
singleQuote: true,
|
|
||||||
printWidth: 120,
|
|
||||||
tabWidth: 4,
|
|
||||||
useTabs: false,
|
|
||||||
};
|
|
17
ci.cjs
17
ci.cjs
@ -5,7 +5,7 @@ const path = require("path");
|
|||||||
|
|
||||||
function printUsage() {
|
function printUsage() {
|
||||||
console.log(
|
console.log(
|
||||||
`Usage: "ci check-consistency" or "ci bump-version %postfix%" or "ci get-version"`
|
`Usage: "ci check-consistency" or "ci bump-version %postfix%" or "ci get-version"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,18 +43,17 @@ async function getPackageJsonsRecursive(currentPath) {
|
|||||||
(await fs.readdir(currentPath, { withFileTypes: true }))
|
(await fs.readdir(currentPath, { withFileTypes: true }))
|
||||||
.filter(
|
.filter(
|
||||||
(file) =>
|
(file) =>
|
||||||
file.name !== "node_modules" && file.name !== "@tests" &&
|
file.name !== "node_modules" &&
|
||||||
(file.isDirectory() || file.name === "package.json")
|
file.name !== "@tests" &&
|
||||||
|
(file.isDirectory() || file.name === "package.json"),
|
||||||
)
|
)
|
||||||
.map((file) =>
|
.map((file) =>
|
||||||
file.isDirectory()
|
file.isDirectory()
|
||||||
? getPackageJsonsRecursive(
|
? getPackageJsonsRecursive(path.join(currentPath, file.name))
|
||||||
path.join(currentPath, file.name)
|
|
||||||
)
|
|
||||||
: Promise.resolve([
|
: Promise.resolve([
|
||||||
path.join(process.cwd(), currentPath, file.name),
|
path.join(process.cwd(), currentPath, file.name),
|
||||||
])
|
]),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
).flat();
|
).flat();
|
||||||
}
|
}
|
||||||
@ -103,7 +102,7 @@ async function checkConsistency(file, versionsMap) {
|
|||||||
if (versionInDep !== version) {
|
if (versionInDep !== version) {
|
||||||
console.log(
|
console.log(
|
||||||
`Error, versions don't match: ${name}:${version} !== ${versionInDep}`,
|
`Error, versions don't match: ${name}:${version} !== ${versionInDep}`,
|
||||||
file
|
file,
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
24
package.json
24
package.json
@ -8,15 +8,29 @@
|
|||||||
"node": ">=10",
|
"node": ">=10",
|
||||||
"pnpm": ">=3"
|
"pnpm": ">=3"
|
||||||
},
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint-check": "pnpm run prettier --check && pnpm run eslint",
|
||||||
|
"lint-fix": "pnpm run prettier --write && pnpm run eslint --fix",
|
||||||
|
"prettier": "prettier .",
|
||||||
|
"eslint": "eslint --cache \"**/src/**/*.{js,ts}\""
|
||||||
|
},
|
||||||
"author": "Fluence Labs",
|
"author": "Fluence Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"http-server": "14.1.1",
|
"@total-typescript/ts-reset": "0.5.1",
|
||||||
"puppeteer": "19.7.2",
|
"@tsconfig/strictest": "2.0.2",
|
||||||
"@types/node": "18.13.0",
|
"@types/node": "18.13.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "6.7.3",
|
||||||
|
"@typescript-eslint/parser": "6.7.3",
|
||||||
|
"eslint": "8.50.0",
|
||||||
|
"eslint-config-prettier": "9.0.0",
|
||||||
|
"eslint-plugin-import": "2.28.1",
|
||||||
|
"eslint-plugin-license-header": "0.6.0",
|
||||||
|
"eslint-plugin-unused-imports": "3.0.0",
|
||||||
|
"http-server": "14.1.1",
|
||||||
|
"prettier": "3.0.3",
|
||||||
|
"puppeteer": "19.7.2",
|
||||||
"ts-node": "10.9.1",
|
"ts-node": "10.9.1",
|
||||||
"typescript": "4.7",
|
"typescript": "5.1.6"
|
||||||
"@fluencelabs/aqua-lib": "0.6.0",
|
|
||||||
"@fluencelabs/aqua": "0.9.1-374"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
6
packages/@tests/.eslintrc.json
Normal file
6
packages/@tests/.eslintrc.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ignorePatterns": ["**/*.css"],
|
||||||
|
"rules": {
|
||||||
|
"no-console": "off"
|
||||||
|
}
|
||||||
|
}
|
@ -53,3 +53,6 @@ func marineTest(wasm64: string) -> f64:
|
|||||||
<- res
|
<- res
|
||||||
|
|
||||||
|
|
||||||
|
func callHappy(a: string, b: f64, c: f64, d: string -> f64) -> f64:
|
||||||
|
res <- d("abc")
|
||||||
|
<- res
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
"author": "Fluence Labs",
|
"author": "Fluence Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluencelabs/js-client": "workspace:^",
|
|
||||||
"base64-js": "1.5.1"
|
"base64-js": "1.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fluencelabs/cli": "0.7.2",
|
|
||||||
"@fluencelabs/registry": "0.8.2",
|
|
||||||
"@fluencelabs/aqua-lib": "0.6.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/trust-graph": "3.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,14 @@
|
|||||||
* Aqua version: 0.12.0
|
* Aqua version: 0.12.0
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import type { IFluenceClient as IFluenceClient$$, CallParams as CallParams$$ } from '@fluencelabs/js-client';
|
import type {
|
||||||
|
IFluenceClient as IFluenceClient$$,
|
||||||
|
CallParams as CallParams$$,
|
||||||
|
} from "@fluencelabs/js-client";
|
||||||
import {
|
import {
|
||||||
v5_callFunction as callFunction$$,
|
v5_callFunction as callFunction$$,
|
||||||
v5_registerService as registerService$$,
|
v5_registerService as registerService$$,
|
||||||
} from '@fluencelabs/js-client';
|
} from "@fluencelabs/js-client";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
|
|
||||||
@ -30,49 +31,42 @@ export const test_script = `
|
|||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 0])
|
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 0])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
`
|
`;
|
||||||
|
|
||||||
|
export function test(config?: { ttl?: number }): Promise<void>;
|
||||||
export function test(
|
|
||||||
config?: {ttl?: number}
|
|
||||||
): Promise<void>;
|
|
||||||
|
|
||||||
export function test(
|
export function test(
|
||||||
peer: IFluenceClient$$,
|
peer: IFluenceClient$$,
|
||||||
config?: {ttl?: number}
|
config?: { ttl?: number },
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
|
||||||
export function test(...args: any) {
|
export function test(...args: any) {
|
||||||
|
|
||||||
|
|
||||||
return callFunction$$(
|
return callFunction$$(
|
||||||
args,
|
args,
|
||||||
{
|
{
|
||||||
"functionName" : "test",
|
functionName: "test",
|
||||||
"arrow" : {
|
arrow: {
|
||||||
"tag" : "arrow",
|
tag: "arrow",
|
||||||
"domain" : {
|
domain: {
|
||||||
"tag" : "labeledProduct",
|
tag: "labeledProduct",
|
||||||
"fields" : {
|
fields: {},
|
||||||
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"codomain" : {
|
codomain: {
|
||||||
"tag" : "nil"
|
tag: "nil",
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"names" : {
|
},
|
||||||
"relay" : "-relay-",
|
names: {
|
||||||
"getDataSrv" : "getDataSrv",
|
relay: "-relay-",
|
||||||
"callbackSrv" : "callbackSrv",
|
getDataSrv: "getDataSrv",
|
||||||
"responseSrv" : "callbackSrv",
|
callbackSrv: "callbackSrv",
|
||||||
"responseFnName" : "response",
|
responseSrv: "callbackSrv",
|
||||||
"errorHandlingSrv" : "errorHandlingSrv",
|
responseFnName: "response",
|
||||||
"errorFnName" : "error"
|
errorHandlingSrv: "errorHandlingSrv",
|
||||||
}
|
errorFnName: "error",
|
||||||
},
|
},
|
||||||
test_script
|
},
|
||||||
)
|
test_script,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
File diff suppressed because it is too large
Load Diff
@ -1,84 +1,117 @@
|
|||||||
import { fromByteArray } from 'base64-js';
|
/**
|
||||||
import { Fluence } from '@fluencelabs/js-client';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import type { ClientConfig } from '@fluencelabs/js-client';
|
*
|
||||||
import { registerHelloWorld, helloTest, marineTest, resourceTest } from './_aqua/smoke_test.js';
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
import { test as particleTest } from './_aqua/finalize_particle.js';
|
* you may not use this file except in compliance with the License.
|
||||||
import { wasm } from './wasmb64.js';
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Fluence } from "@fluencelabs/js-client";
|
||||||
|
import type { ClientConfig } from "@fluencelabs/js-client";
|
||||||
|
import { fromByteArray } from "base64-js";
|
||||||
|
|
||||||
|
import { test as particleTest } from "./_aqua/finalize_particle.js";
|
||||||
|
import {
|
||||||
|
registerHelloWorld,
|
||||||
|
helloTest,
|
||||||
|
marineTest,
|
||||||
|
} from "./_aqua/smoke_test.js";
|
||||||
|
import { wasm } from "./wasmb64.js";
|
||||||
|
|
||||||
const relay = {
|
const relay = {
|
||||||
multiaddr: '/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
multiaddr:
|
||||||
peerId: '12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
|
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
};
|
};
|
||||||
|
|
||||||
function generateRandomUint8Array() {
|
function generateRandomUint8Array() {
|
||||||
const uint8Array = new Uint8Array(32);
|
const uint8Array = new Uint8Array(32);
|
||||||
|
|
||||||
for (let i = 0; i < uint8Array.length; i++) {
|
for (let i = 0; i < uint8Array.length; i++) {
|
||||||
uint8Array[i] = Math.floor(Math.random() * 256);
|
uint8Array[i] = Math.floor(Math.random() * 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
return uint8Array;
|
return uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
const optsWithRandomKeyPair = (): ClientConfig => {
|
const optsWithRandomKeyPair = (): ClientConfig => {
|
||||||
return {
|
return {
|
||||||
keyPair: {
|
keyPair: {
|
||||||
type: 'Ed25519',
|
type: "Ed25519",
|
||||||
source: generateRandomUint8Array(),
|
source: generateRandomUint8Array(),
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TestResult = { type: 'success'; data: string } | { type: 'failure'; error: string };
|
export type TestResult =
|
||||||
|
| { type: "success"; data: string }
|
||||||
|
| { type: "failure"; error: string };
|
||||||
|
|
||||||
export const runTest = async (): Promise<TestResult> => {
|
export const runTest = async (): Promise<TestResult> => {
|
||||||
try {
|
try {
|
||||||
Fluence.onConnectionStateChange((state) => console.info('connection state changed: ', state));
|
Fluence.onConnectionStateChange((state) => {
|
||||||
|
console.info("connection state changed: ", state);
|
||||||
|
});
|
||||||
|
|
||||||
console.log('connecting to Fluence Network...');
|
console.log("connecting to Fluence Network...");
|
||||||
console.log('multiaddr: ', relay.multiaddr);
|
console.log("multiaddr: ", relay.multiaddr);
|
||||||
await Fluence.connect(relay, optsWithRandomKeyPair());
|
await Fluence.connect(relay, optsWithRandomKeyPair());
|
||||||
|
|
||||||
console.log('connected');
|
console.log("connected");
|
||||||
|
|
||||||
const relayPeerId = (await Fluence.getClient()).getRelayPeerId();
|
const relayPeerId = Fluence.getClient().getRelayPeerId();
|
||||||
console.log('relay:', relayPeerId);
|
console.log("relay:", relayPeerId);
|
||||||
|
|
||||||
await registerHelloWorld({
|
registerHelloWorld({
|
||||||
hello(str) {
|
hello(str) {
|
||||||
return 'Hello, ' + str + '!';
|
return "Hello, " + str + "!";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const client = await Fluence.getClient();
|
const client = Fluence.getClient();
|
||||||
|
|
||||||
console.log('my peer id: ', client.getPeerId());
|
console.log("my peer id: ", client.getPeerId());
|
||||||
console.log('my sk id: ', fromByteArray(client.getPeerSecretKey()));
|
console.log("my sk id: ", fromByteArray(client.getPeerSecretKey()));
|
||||||
|
|
||||||
console.log('running hello test...');
|
console.log("running hello test...");
|
||||||
const hello = await helloTest();
|
const hello = await helloTest();
|
||||||
console.log('hello test finished, result: ', hello);
|
console.log("hello test finished, result: ", hello);
|
||||||
|
|
||||||
console.log('running marine test...');
|
console.log("running marine test...");
|
||||||
const marine = await marineTest(wasm);
|
const marine = await marineTest(wasm);
|
||||||
|
|
||||||
console.log('running particle test...');
|
console.log("running particle test...");
|
||||||
await particleTest();
|
await particleTest();
|
||||||
|
|
||||||
console.log('marine test finished, result: ', marine);
|
console.log("marine test finished, result: ", marine);
|
||||||
|
|
||||||
const returnVal = {
|
const returnVal = {
|
||||||
hello,
|
hello,
|
||||||
marine,
|
marine,
|
||||||
};
|
};
|
||||||
return { type: 'success', data: JSON.stringify(returnVal) };
|
|
||||||
|
return { type: "success", data: JSON.stringify(returnVal) };
|
||||||
} finally {
|
} finally {
|
||||||
console.log('disconnecting from Fluence Network...');
|
console.log("disconnecting from Fluence Network...");
|
||||||
await Fluence.disconnect();
|
await Fluence.disconnect();
|
||||||
console.log('disconnected');
|
console.log("disconnected");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const runMain = () => {
|
export const runMain = () => {
|
||||||
runTest()
|
runTest()
|
||||||
.then(() => console.log('done!'))
|
.then(() => {
|
||||||
.catch((err) => console.error('error: ', err));
|
console.log("done!");
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("error: ", err);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
File diff suppressed because one or more lines are too long
@ -4,5 +4,6 @@
|
|||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"module": "NodeNext"
|
"module": "NodeNext"
|
||||||
},
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,21 @@
|
|||||||
import '@fluencelabs/js-client';
|
/**
|
||||||
import { runTest } from '@test/aqua_for_test';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
runTest().then(() => console.log('Smoke tests succeed!'));
|
import "@fluencelabs/js-client";
|
||||||
|
import { runTest } from "@test/aqua_for_test";
|
||||||
|
|
||||||
|
await runTest();
|
||||||
|
console.log("Smoke tests succeed!");
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@test/aqua_for_test": "workspace:^",
|
"@test/aqua_for_test": "workspace:*",
|
||||||
"@testing-library/jest-dom": "5.16.5",
|
"@testing-library/jest-dom": "5.16.5",
|
||||||
"@testing-library/react": "13.4.0",
|
"@testing-library/react": "13.4.0",
|
||||||
"@testing-library/user-event": "13.5.0",
|
"@testing-library/user-event": "13.5.0",
|
||||||
@ -15,11 +15,10 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"typescript": "4.9.5",
|
|
||||||
"web-vitals": "2.1.4"
|
"web-vitals": "2.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@test/test-utils": "workspace:^",
|
"@test/test-utils": "workspace:*",
|
||||||
"puppeteer": "19.7.2"
|
"puppeteer": "19.7.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { runTest, TestResult } from '@test/aqua_for_test';
|
import { runTest, TestResult } from "@test/aqua_for_test";
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import logo from './logo.svg';
|
import logo from "./logo.svg";
|
||||||
import './App.css';
|
import "./App.css";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [result, setResult] = React.useState<TestResult | null>(null);
|
const [result, setResult] = React.useState<TestResult | null>(null);
|
||||||
@ -12,7 +12,7 @@ function App() {
|
|||||||
setResult(res);
|
setResult(res);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setResult({ type: 'failure', error: err.toString() });
|
setResult({ type: "failure", error: err.toString() });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -27,9 +27,18 @@ function App() {
|
|||||||
Click to run test
|
Click to run test
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{result && result.type === 'success' && <div id="res">{result.data}</div>}
|
{result && result.type === "success" && (
|
||||||
{result && result.type === 'failure' && <div id="error">{result.error}</div>}
|
<div id="res">{result.data}</div>
|
||||||
<a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer">
|
)}
|
||||||
|
{result && result.type === "failure" && (
|
||||||
|
<div id="error">{result.error}</div>
|
||||||
|
)}
|
||||||
|
<a
|
||||||
|
className="App-link"
|
||||||
|
href="https://reactjs.org"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
Learn React
|
Learn React
|
||||||
</a>
|
</a>
|
||||||
</header>
|
</header>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||||
sans-serif;
|
sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||||
monospace;
|
monospace;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from "react-dom/client";
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from "./reportWebVitals";
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
const root = ReactDOM.createRoot(
|
||||||
|
document.getElementById("root") as HTMLElement,
|
||||||
|
);
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ReportHandler } from 'web-vitals';
|
import { ReportHandler } from "web-vitals";
|
||||||
|
|
||||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
getCLS(onPerfEntry);
|
getCLS(onPerfEntry);
|
||||||
getFID(onPerfEntry);
|
getFID(onPerfEntry);
|
||||||
getFCP(onPerfEntry);
|
getFCP(onPerfEntry);
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
// allows you to do things like:
|
// allows you to do things like:
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
import '@testing-library/jest-dom';
|
import "@testing-library/jest-dom";
|
||||||
|
@ -1,49 +1,53 @@
|
|||||||
import puppeteer from 'puppeteer';
|
import puppeteer from "puppeteer";
|
||||||
import { dirname, join } from 'path';
|
import { dirname, join } from "path";
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
import { CDN_PUBLIC_PATH, startContentServer, stopServer } from '@test/test-utils';
|
import {
|
||||||
import { access, symlink } from 'fs/promises';
|
CDN_PUBLIC_PATH,
|
||||||
|
startContentServer,
|
||||||
|
stopServer,
|
||||||
|
} from "@test/test-utils";
|
||||||
|
import { access, symlink } from "fs/promises";
|
||||||
|
|
||||||
const port = 3001;
|
const port = 3001;
|
||||||
const uri = `http://localhost:${port}/`;
|
const uri = `http://localhost:${port}/`;
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const publicPath = join(__dirname, '../build/');
|
const publicPath = join(__dirname, "../build/");
|
||||||
|
|
||||||
const test = async () => {
|
const test = async () => {
|
||||||
const localServer = await startContentServer(port, publicPath);
|
const localServer = await startContentServer(port, publicPath);
|
||||||
try {
|
try {
|
||||||
await access(join(publicPath, 'source'))
|
await access(join(publicPath, "source"));
|
||||||
} catch {
|
} catch {
|
||||||
await symlink(CDN_PUBLIC_PATH, join(publicPath, 'source'));
|
await symlink(CDN_PUBLIC_PATH, join(publicPath, "source"));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('starting puppeteer...');
|
console.log("starting puppeteer...");
|
||||||
const browser = await puppeteer.launch();
|
const browser = await puppeteer.launch();
|
||||||
const page = (await browser.pages())[0];
|
const page = (await browser.pages())[0];
|
||||||
|
|
||||||
// uncomment to debug what's happening inside the browser
|
// uncomment to debug what's happening inside the browser
|
||||||
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
|
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
|
||||||
|
|
||||||
console.log('going to the page in browser...');
|
console.log("going to the page in browser...");
|
||||||
await page.goto(uri);
|
await page.goto(uri);
|
||||||
|
|
||||||
console.log('clicking button...');
|
console.log("clicking button...");
|
||||||
await page.click('#btn');
|
await page.click("#btn");
|
||||||
|
|
||||||
console.log('waiting for result to appear...');
|
console.log("waiting for result to appear...");
|
||||||
const elem = await page.waitForSelector('#res');
|
const elem = await page.waitForSelector("#res");
|
||||||
|
|
||||||
console.log('getting the content of result div...');
|
console.log("getting the content of result div...");
|
||||||
const content = await elem?.evaluate((x) => x.textContent);
|
const content = await elem?.evaluate((x) => x.textContent);
|
||||||
console.log('raw result: ', content);
|
console.log("raw result: ", content);
|
||||||
|
|
||||||
await browser.close();
|
await browser.close();
|
||||||
await stopServer(localServer);
|
await stopServer(localServer);
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
throw new Error('smoke test failed!');
|
throw new Error("smoke test failed!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test().then(() => console.log('smoke tests succeed!'));
|
test().then(() => console.log("smoke tests succeed!"));
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
"author": "Fluence Labs",
|
"author": "Fluence Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluencelabs/js-client": "workspace:^",
|
"@fluencelabs/js-client": "workspace:*",
|
||||||
"@test/test-utils": "workspace:../../test-utils"
|
"@test/test-utils": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"puppeteer": "19.7.2"
|
"puppeteer": "19.7.2"
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
const fluence = globalThis.fluence;
|
const fluence = globalThis.fluence;
|
||||||
|
|
||||||
const relay = {
|
const relay = {
|
||||||
multiaddr: '/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
multiaddr:
|
||||||
peerId: '12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
|
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRelayTime = () => {
|
const getRelayTime = () => {
|
||||||
@ -37,36 +38,36 @@ const getRelayTime = () => {
|
|||||||
)`;
|
)`;
|
||||||
|
|
||||||
const def = {
|
const def = {
|
||||||
functionName: 'getRelayTime',
|
functionName: "getRelayTime",
|
||||||
arrow: {
|
arrow: {
|
||||||
tag: 'arrow',
|
tag: "arrow",
|
||||||
domain: {
|
domain: {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
relayPeerId: {
|
relayPeerId: {
|
||||||
tag: 'scalar',
|
tag: "scalar",
|
||||||
name: 'string',
|
name: "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
codomain: {
|
codomain: {
|
||||||
tag: 'unlabeledProduct',
|
tag: "unlabeledProduct",
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
tag: 'scalar',
|
tag: "scalar",
|
||||||
name: 'u64',
|
name: "u64",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
names: {
|
names: {
|
||||||
relay: '-relay-',
|
relay: "-relay-",
|
||||||
getDataSrv: 'getDataSrv',
|
getDataSrv: "getDataSrv",
|
||||||
callbackSrv: 'callbackSrv',
|
callbackSrv: "callbackSrv",
|
||||||
responseSrv: 'callbackSrv',
|
responseSrv: "callbackSrv",
|
||||||
responseFnName: 'response',
|
responseFnName: "response",
|
||||||
errorHandlingSrv: 'errorHandlingSrv',
|
errorHandlingSrv: "errorHandlingSrv",
|
||||||
errorFnName: 'error',
|
errorFnName: "error",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,28 +84,28 @@ const getRelayTime = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
console.log('starting fluence...');
|
console.log("starting fluence...");
|
||||||
fluence.defaultClient = await fluence.clientFactory(relay);
|
fluence.defaultClient = await fluence.clientFactory(relay, {});
|
||||||
console.log('started fluence');
|
console.log("started fluence");
|
||||||
|
|
||||||
console.log('getting relay time...');
|
console.log("getting relay time...");
|
||||||
const relayTime = await getRelayTime();
|
const relayTime = await getRelayTime();
|
||||||
console.log('got relay time, ', relayTime);
|
console.log("got relay time, ", relayTime);
|
||||||
|
|
||||||
console.log('stopping fluence...');
|
console.log("stopping fluence...");
|
||||||
await fluence.defaultClient.stop();
|
await fluence.defaultClient.stop();
|
||||||
console.log('stopped fluence...');
|
console.log("stopped fluence...");
|
||||||
|
|
||||||
return relayTime;
|
return relayTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
const btn = document.getElementById('btn');
|
const btn = document.getElementById("btn");
|
||||||
|
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener("click", () => {
|
||||||
main().then((res) => {
|
main().then((res) => {
|
||||||
const inner = document.createElement('div');
|
const inner = document.createElement("div");
|
||||||
inner.id = 'res';
|
inner.id = "res";
|
||||||
inner.innerText = res;
|
inner.innerText = res;
|
||||||
document.getElementById('res-placeholder').appendChild(inner);
|
document.getElementById("res-placeholder").appendChild(inner);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,49 +1,76 @@
|
|||||||
import puppeteer from 'puppeteer';
|
/**
|
||||||
import { dirname, join } from 'path';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import { fileURLToPath } from 'url';
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import { CDN_PUBLIC_PATH, startCdn, startContentServer, stopServer } from '@test/test-utils';
|
import { symlink, access } from "fs/promises";
|
||||||
import { symlink, access } from 'fs/promises';
|
import { dirname, join } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CDN_PUBLIC_PATH,
|
||||||
|
startContentServer,
|
||||||
|
stopServer,
|
||||||
|
} from "@test/test-utils";
|
||||||
|
import puppeteer from "puppeteer";
|
||||||
|
|
||||||
const port = 3000;
|
const port = 3000;
|
||||||
const uri = `http://localhost:${port}/`;
|
const uri = `http://localhost:${port}/`;
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const publicPath = join(__dirname, '../public/');
|
const publicPath = join(__dirname, "../public/");
|
||||||
|
|
||||||
const test = async () => {
|
const test = async () => {
|
||||||
const localServer = await startContentServer(port, publicPath);
|
const localServer = await startContentServer(port, publicPath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await access(join(publicPath, 'source'))
|
await access(join(publicPath, "source"));
|
||||||
} catch {
|
} catch {
|
||||||
await symlink(CDN_PUBLIC_PATH, join(publicPath, 'source'));
|
await symlink(CDN_PUBLIC_PATH, join(publicPath, "source"));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('starting puppeteer...');
|
console.log("starting puppeteer...");
|
||||||
const browser = await puppeteer.launch();
|
const browser = await puppeteer.launch();
|
||||||
const page = (await browser.pages())[0];
|
const page = (await browser.pages())[0];
|
||||||
|
|
||||||
// uncomment to debug what's happening inside the browser
|
// uncomment to debug what's happening inside the browser
|
||||||
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
|
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
|
||||||
|
|
||||||
console.log('going to the page in browser...');
|
console.log("going to the page in browser...");
|
||||||
await page.goto(uri);
|
await page.goto(uri);
|
||||||
|
|
||||||
console.log('clicking button...');
|
console.log("clicking button...");
|
||||||
await page.click('#btn');
|
await page.click("#btn");
|
||||||
|
|
||||||
console.log('waiting for result to appear...');
|
console.log("waiting for result to appear...");
|
||||||
const elem = await page.waitForSelector('#res');
|
const elem = await page.waitForSelector("#res");
|
||||||
|
|
||||||
console.log('getting the content of result div...');
|
console.log("getting the content of result div...");
|
||||||
const content = await elem?.evaluate((x) => x.textContent);
|
|
||||||
console.log('raw result: ', content);
|
const content = await elem?.evaluate((x) => {
|
||||||
|
return x.textContent;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("raw result: ", content);
|
||||||
|
|
||||||
await browser.close();
|
await browser.close();
|
||||||
await stopServer(localServer);
|
await stopServer(localServer);
|
||||||
|
|
||||||
if (!content) {
|
if (content == null) {
|
||||||
throw new Error('smoke test failed!');
|
throw new Error("smoke test failed!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test().then(() => console.log('smoke tests succeed!'));
|
void test().then(() => {
|
||||||
|
console.log("smoke tests succeed!");
|
||||||
|
});
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist", "public"]
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,65 @@
|
|||||||
import handler from 'serve-handler';
|
/**
|
||||||
import { createServer } from 'http';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import type { Server } from 'http';
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import { dirname, join } from 'path';
|
import { createServer } from "http";
|
||||||
import { fileURLToPath } from 'url';
|
import type { Server } from "http";
|
||||||
|
import { dirname, join } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
import handler from "serve-handler";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export const CDN_PUBLIC_PATH = join(__dirname, '../../../core/js-client/dist/browser');
|
export const CDN_PUBLIC_PATH = join(
|
||||||
|
__dirname,
|
||||||
|
"../../../core/js-client/dist/browser",
|
||||||
|
);
|
||||||
|
|
||||||
export const startCdn = (port: number) => startContentServer(port, CDN_PUBLIC_PATH);
|
export const startCdn = (port: number) => {
|
||||||
|
return startContentServer(port, CDN_PUBLIC_PATH);
|
||||||
|
};
|
||||||
|
|
||||||
export const startContentServer = (port: number, publicDir: string): Promise<Server> => {
|
export const startContentServer = (
|
||||||
|
port: number,
|
||||||
|
publicDir: string,
|
||||||
|
): Promise<Server> => {
|
||||||
const server = createServer((request, response) => {
|
const server = createServer((request, response) => {
|
||||||
return handler(request, response, {
|
void handler(request, response, {
|
||||||
public: publicDir,
|
public: publicDir,
|
||||||
rewrites: [{
|
rewrites: [
|
||||||
source: '/js-client.min.js',
|
{
|
||||||
destination: '/source/index.umd.cjs'
|
source: "/js-client.min.js",
|
||||||
}],
|
destination: "/source/index.umd.cjs",
|
||||||
headers: [{
|
},
|
||||||
source: '**/*',
|
],
|
||||||
headers: [
|
headers: [
|
||||||
{ key: 'Cross-Origin-Opener-Policy', value: 'same-origin' },
|
{
|
||||||
{ key: 'Cross-Origin-Embedder-Policy', value: 'require-corp' }
|
source: "**/*",
|
||||||
]
|
headers: [
|
||||||
}]
|
{
|
||||||
|
key: "Cross-Origin-Opener-Policy",
|
||||||
|
value: "same-origin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Cross-Origin-Embedder-Policy",
|
||||||
|
value: "require-corp",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -41,7 +75,7 @@ export const startContentServer = (port: number, publicDir: string): Promise<Ser
|
|||||||
export const stopServer = (app: Server): Promise<void> => {
|
export const stopServer = (app: Server): Promise<void> => {
|
||||||
return new Promise<void>((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
app.close(() => {
|
app.close(() => {
|
||||||
console.log('server stopped');
|
console.log("server stopped");
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,5 +3,6 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
|
"include": ["src"],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
3
packages/core/aqua-to-js/.eslintrc.json
Normal file
3
packages/core/aqua-to-js/.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignorePatterns": ["src/**/__snapshots__/**/*"]
|
||||||
|
}
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/js-client bumped to 0.1.7
|
- @fluencelabs/js-client bumped to 0.1.7
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
## 0.0.1 (2023-09-22)
|
## 0.0.1 (2023-09-22)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **aqua-compiler:** JS-client aqua wrapper [fixes DXJ-461] ([#347](https://github.com/fluencelabs/js-client/issues/347)) ([7fff3b1](https://github.com/fluencelabs/js-client/commit/7fff3b1c0374eef76ab4e665b13cf97b5c50ff70))
|
- **aqua-compiler:** JS-client aqua wrapper [fixes DXJ-461] ([#347](https://github.com/fluencelabs/js-client/issues/347)) ([7fff3b1](https://github.com/fluencelabs/js-client/commit/7fff3b1c0374eef76ab4e665b13cf97b5c50ff70))
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
"@fluencelabs/registry": "0.8.7",
|
"@fluencelabs/registry": "0.8.7",
|
||||||
"@fluencelabs/spell": "0.5.20",
|
"@fluencelabs/spell": "0.5.20",
|
||||||
"@fluencelabs/trust-graph": "0.4.7",
|
"@fluencelabs/trust-graph": "0.4.7",
|
||||||
"typescript": "5.1.6",
|
"vitest": "0.34.6"
|
||||||
"vitest": "0.29.7"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,85 +14,136 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ArrowType, ArrowWithoutCallbacks, NonArrowType, ProductType } from '@fluencelabs/interfaces';
|
import { ArrowWithoutCallbacks, NonArrowType } from "@fluencelabs/interfaces";
|
||||||
import { match, P } from 'ts-pattern';
|
import { match, P } from "ts-pattern";
|
||||||
import { getFuncArgs } from './utils.js';
|
|
||||||
|
|
||||||
export function genTypeName(t: NonArrowType | ProductType<NonArrowType> | ArrowWithoutCallbacks, name: string): readonly [string | undefined, string] {
|
import { getFuncArgs } from "./utils.js";
|
||||||
|
|
||||||
|
export function genTypeName(
|
||||||
|
t: NonArrowType | ArrowWithoutCallbacks,
|
||||||
|
name: string,
|
||||||
|
): readonly [string | undefined, string] {
|
||||||
const genType = typeToTs(t);
|
const genType = typeToTs(t);
|
||||||
return match(t)
|
return match(t)
|
||||||
.with(
|
.with({ tag: "nil" }, () => {
|
||||||
{ tag: 'nil' },
|
return [undefined, "void"] as const;
|
||||||
() => [undefined, 'void'] as const
|
})
|
||||||
).with(
|
.with({ tag: "struct" }, () => {
|
||||||
{ tag: 'struct' },
|
return [`export type ${name} = ${genType}`, name] as const;
|
||||||
() => [`export type ${name} = ${genType}`, name] as const
|
})
|
||||||
).with(
|
.with({ tag: P.union("labeledProduct", "unlabeledProduct") }, (item) => {
|
||||||
{ tag: P.union('labeledProduct', 'unlabeledProduct') },
|
const args =
|
||||||
(item) => {
|
item.tag === "labeledProduct" ? Object.values(item.fields) : item.items;
|
||||||
const args = item.tag === 'labeledProduct'
|
|
||||||
? Object.values(item.fields)
|
|
||||||
: item.items;
|
|
||||||
|
|
||||||
if (args.length === 1) {
|
if (args.length === 1) {
|
||||||
return genTypeName(args[0], name);
|
return genTypeName(args[0], name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [`export type ${name} = ${genType}`, name] as const;
|
return [`export type ${name} = ${genType}`, name] as const;
|
||||||
},
|
})
|
||||||
).otherwise(() => [undefined, genType] as const);
|
.otherwise(() => {
|
||||||
|
return [undefined, genType] as const;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks | ProductType<NonArrowType>): string {
|
export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks): string {
|
||||||
return match(t)
|
return match(t)
|
||||||
|
.with({ tag: "nil" }, () => {
|
||||||
|
return "null";
|
||||||
|
})
|
||||||
|
.with({ tag: "option" }, ({ type }) => {
|
||||||
|
return typeToTs(type) + " | null";
|
||||||
|
})
|
||||||
|
.with({ tag: "scalar" }, ({ name }) => {
|
||||||
|
return match(name)
|
||||||
.with(
|
.with(
|
||||||
{ tag: 'nil' },
|
P.union(
|
||||||
() => 'null'
|
"u8",
|
||||||
).with(
|
"u16",
|
||||||
{ tag: 'option' },
|
"u32",
|
||||||
({ type }) => typeToTs(type) + ' | null'
|
"u64",
|
||||||
).with(
|
"i8",
|
||||||
{ tag: 'scalar' },
|
"i16",
|
||||||
({ name }) => match(name)
|
"i32",
|
||||||
.with(P.union('u8', 'u16', 'u32', 'u64', 'i8', 'i16', 'i32', 'i64', 'f32', 'f64'), () => 'number')
|
"i64",
|
||||||
.with('bool', () => 'boolean')
|
"f32",
|
||||||
.with('string', () => 'string')
|
"f64",
|
||||||
.with(P._, () => 'any').exhaustive()
|
),
|
||||||
).with(
|
() => {
|
||||||
{ tag: 'array' },
|
return "number";
|
||||||
({ type }) => typeToTs(type) + '[]'
|
},
|
||||||
).with(
|
)
|
||||||
{ tag: 'struct' },
|
.with("bool", () => {
|
||||||
({ fields }) => `{ ${Object.entries(fields).map(([field, type]) => `${field}: ${typeToTs(type)};`).join(' ')} }`
|
return "boolean";
|
||||||
).with(
|
})
|
||||||
{ tag: 'labeledProduct' },
|
.with("string", () => {
|
||||||
({ fields }) => `{ ${Object.entries(fields).map(([field, type]) => `${field}: ${typeToTs(type)};`).join(' ')} }`
|
return "string";
|
||||||
).with(
|
})
|
||||||
{ tag: 'unlabeledProduct' },
|
.with(P._, () => {
|
||||||
({ items }) => `[${items.map(item => typeToTs(item)).join(', ')}]`
|
return "any";
|
||||||
).with(
|
})
|
||||||
{ tag: 'arrow' },
|
.exhaustive();
|
||||||
({ tag, domain, codomain }) => {
|
})
|
||||||
const retType = codomain.tag === 'nil'
|
.with({ tag: "array" }, ({ type }) => {
|
||||||
? 'void'
|
return typeToTs(type) + "[]";
|
||||||
|
})
|
||||||
|
.with({ tag: "struct" }, ({ fields }) => {
|
||||||
|
return `{ ${Object.entries(fields)
|
||||||
|
.map(([field, type]) => {
|
||||||
|
return `${field}: ${typeToTs(type)};`;
|
||||||
|
})
|
||||||
|
.join(" ")} }`;
|
||||||
|
})
|
||||||
|
.with({ tag: "labeledProduct" }, ({ fields }) => {
|
||||||
|
return `{ ${Object.entries(fields)
|
||||||
|
.map(([field, type]) => {
|
||||||
|
return `${field}: ${typeToTs(type)};`;
|
||||||
|
})
|
||||||
|
.join(" ")} }`;
|
||||||
|
})
|
||||||
|
.with({ tag: "unlabeledProduct" }, ({ items }) => {
|
||||||
|
return `[${items
|
||||||
|
.map((item) => {
|
||||||
|
return typeToTs(item);
|
||||||
|
})
|
||||||
|
.join(", ")}]`;
|
||||||
|
})
|
||||||
|
.with({ tag: "arrow" }, ({ domain, codomain }) => {
|
||||||
|
const retType =
|
||||||
|
codomain.tag === "nil"
|
||||||
|
? "void"
|
||||||
: codomain.items.length === 1
|
: codomain.items.length === 1
|
||||||
? typeToTs(codomain.items[0])
|
? typeToTs(codomain.items[0])
|
||||||
: typeToTs(codomain);
|
: typeToTs(codomain);
|
||||||
|
|
||||||
const args = getFuncArgs(domain).map(([name, type]) => ([name, typeToTs(type)]));
|
const args = getFuncArgs(domain).map(([name, type]) => {
|
||||||
|
return [name, typeToTs(type)];
|
||||||
|
});
|
||||||
|
|
||||||
const generic = args.length === 0 ? 'null' : args.map(([name]) => `'${name}'`).join(' | ');
|
const generic =
|
||||||
args.push(['callParams', `CallParams$$<${generic}>`]);
|
args.length === 0
|
||||||
|
? "null"
|
||||||
|
: args
|
||||||
|
.map(([name]) => {
|
||||||
|
return `'${name}'`;
|
||||||
|
})
|
||||||
|
.join(" | ");
|
||||||
|
|
||||||
const funcArgs = args.map(([name, type]) => `${name}: ${type}`).join(', ');
|
args.push(["callParams", `CallParams$$<${generic}>`]);
|
||||||
|
|
||||||
|
const funcArgs = args
|
||||||
|
.map(([name, type]) => {
|
||||||
|
return `${name}: ${type}`;
|
||||||
|
})
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
return `(${funcArgs}) => ${retType} | Promise<${retType}>`;
|
return `(${funcArgs}) => ${retType} | Promise<${retType}>`;
|
||||||
}
|
})
|
||||||
).with(
|
.with({ tag: "topType" }, () => {
|
||||||
{ tag: 'topType' },
|
return "unknown";
|
||||||
() => 'unknown'
|
})
|
||||||
).with(
|
.with({ tag: "bottomType" }, () => {
|
||||||
{ tag: 'bottomType' },
|
return "never";
|
||||||
() => 'never'
|
})
|
||||||
).exhaustive();
|
.exhaustive();
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,4 +14,4 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const CLIENT = 'IFluenceClient$$';
|
export const CLIENT = "IFluenceClient$$";
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
ArrayType,
|
|
||||||
BottomType, LabeledProductType,
|
|
||||||
NilType,
|
|
||||||
NonArrowType,
|
|
||||||
OptionType,
|
|
||||||
ScalarType,
|
|
||||||
StructType,
|
|
||||||
TopType, UnlabeledProductType
|
|
||||||
} from '@fluencelabs/interfaces';
|
|
||||||
|
|
||||||
// Type definitions for inferring ts types from air json definition
|
|
||||||
// In the future we may remove string type declaration and move to type inference.
|
|
||||||
|
|
||||||
type GetTsTypeFromScalar<T extends ScalarType> = T['name'] extends 'u8' | 'u16' | 'u32' | 'u64' | 'i8' | 'i16' | 'i32' | 'i64' | 'f32' | 'f64'
|
|
||||||
? number
|
|
||||||
: T['name'] extends 'bool'
|
|
||||||
? boolean
|
|
||||||
: T['name'] extends 'string'
|
|
||||||
? string
|
|
||||||
: never;
|
|
||||||
|
|
||||||
type MapTuple<T> = { [K in keyof T]: T[K] extends NonArrowType ? GetTsType<T[K]> : never }
|
|
||||||
|
|
||||||
type GetTsType<T extends NonArrowType> = T extends NilType
|
|
||||||
? null
|
|
||||||
: T extends ArrayType
|
|
||||||
? GetTsType<T['type']>[]
|
|
||||||
: T extends StructType
|
|
||||||
? { [K in keyof T]: GetTsType<T> }
|
|
||||||
: T extends OptionType
|
|
||||||
? GetTsType<T['type']> | null
|
|
||||||
: T extends ScalarType
|
|
||||||
? GetTsTypeFromScalar<T>
|
|
||||||
: T extends TopType
|
|
||||||
? unknown
|
|
||||||
: T extends BottomType
|
|
||||||
? never
|
|
||||||
: T extends Exclude<UnlabeledProductType<infer H>, NilType>
|
|
||||||
? MapTuple<H>
|
|
||||||
: T extends Exclude<LabeledProductType<infer H>, NilType>
|
|
||||||
? H extends NonArrowType
|
|
||||||
? { [K in keyof T['fields']]: GetTsType<H> }
|
|
||||||
: never
|
|
||||||
: never;
|
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,35 +14,39 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, expect, it } from 'vitest';
|
import url from "url";
|
||||||
import { generateTypes, generateSources } from '../index.js';
|
|
||||||
import { compileFromPath } from '@fluencelabs/aqua-api';
|
|
||||||
import url from 'url';
|
|
||||||
import { getPackageJsonContent, PackageJson } from '../../utils.js';
|
|
||||||
|
|
||||||
describe('Aqua to js/ts compiler', () => {
|
import { compileFromPath } from "@fluencelabs/aqua-api";
|
||||||
it('compiles smoke tests successfully', async () => {
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { getPackageJsonContent, PackageJson } from "../../utils.js";
|
||||||
|
import { generateTypes, generateSources } from "../index.js";
|
||||||
|
|
||||||
|
describe("Aqua to js/ts compiler", () => {
|
||||||
|
it("compiles smoke tests successfully", async () => {
|
||||||
const res = await compileFromPath({
|
const res = await compileFromPath({
|
||||||
filePath: url.fileURLToPath(new URL('./sources/smoke_test.aqua', import.meta.url)),
|
filePath: url.fileURLToPath(
|
||||||
imports: ['./node_modules'],
|
new URL("./sources/smoke_test.aqua", import.meta.url),
|
||||||
targetType: 'air'
|
),
|
||||||
|
imports: ["./node_modules"],
|
||||||
|
targetType: "air",
|
||||||
});
|
});
|
||||||
|
|
||||||
const pkg: PackageJson = {
|
const pkg: PackageJson = {
|
||||||
...(await getPackageJsonContent()),
|
...(await getPackageJsonContent()),
|
||||||
version: '0.0.0',
|
version: "0.0.0",
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
'@fluencelabs/aqua-api': '0.0.0'
|
"@fluencelabs/aqua-api": "0.0.0",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const jsResult = await generateSources(res, 'js', pkg);
|
const jsResult = generateSources(res, "js", pkg);
|
||||||
const jsTypes = await generateTypes(res, pkg);
|
const jsTypes = generateTypes(res, pkg);
|
||||||
|
|
||||||
expect(jsResult).toMatchSnapshot();
|
expect(jsResult).toMatchSnapshot();
|
||||||
expect(jsTypes).toMatchSnapshot();
|
expect(jsTypes).toMatchSnapshot();
|
||||||
|
|
||||||
const tsResult = await generateSources(res, 'ts', pkg);
|
const tsResult = generateSources(res, "ts", pkg);
|
||||||
|
|
||||||
expect(tsResult).toMatchSnapshot();
|
expect(tsResult).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,24 +14,38 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { recursiveRenameLaquaProps } from '../utils.js';
|
import { recursiveRenameLaquaProps } from "../utils.js";
|
||||||
import { AquaFunction, TypeGenerator } from './interfaces.js';
|
|
||||||
|
|
||||||
export function generateFunctions(typeGenerator: TypeGenerator, functions: Record<string, AquaFunction>) {
|
import { AquaFunction, TypeGenerator } from "./interfaces.js";
|
||||||
return Object.values(functions).map(func => generateFunction(typeGenerator, func)).join('\n\n');
|
|
||||||
|
export function generateFunctions(
|
||||||
|
typeGenerator: TypeGenerator,
|
||||||
|
functions: Record<string, AquaFunction>,
|
||||||
|
) {
|
||||||
|
return Object.values(functions)
|
||||||
|
.map((func) => {
|
||||||
|
return generateFunction(typeGenerator, func);
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeepToType<T> = { [K in keyof T]: DeepToType<T[K]> };
|
||||||
|
|
||||||
function generateFunction(typeGenerator: TypeGenerator, func: AquaFunction) {
|
function generateFunction(typeGenerator: TypeGenerator, func: AquaFunction) {
|
||||||
const scriptConstName = func.funcDef.functionName + '_script';
|
const funcDef: DeepToType<typeof func.funcDef> = func.funcDef;
|
||||||
|
const scriptConstName = func.funcDef.functionName + "_script";
|
||||||
return `export const ${scriptConstName} = \`
|
return `export const ${scriptConstName} = \`
|
||||||
${func.script}\`;
|
${func.script}\`;
|
||||||
|
|
||||||
${typeGenerator.funcType(func)}
|
${typeGenerator.funcType(func)}
|
||||||
export function ${func.funcDef.functionName}(${typeGenerator.type('...args', 'any[]')}) {
|
export function ${func.funcDef.functionName}(${typeGenerator.type(
|
||||||
|
"...args",
|
||||||
|
"any[]",
|
||||||
|
)}) {
|
||||||
return callFunction$$(
|
return callFunction$$(
|
||||||
args,
|
args,
|
||||||
${JSON.stringify(recursiveRenameLaquaProps(func.funcDef), null, 4)},
|
${JSON.stringify(recursiveRenameLaquaProps(funcDef), null, 4)},
|
||||||
${scriptConstName}
|
${scriptConstName}
|
||||||
);
|
);
|
||||||
}`
|
}`;
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,22 +14,30 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { OutputType } from './interfaces.js';
|
import { PackageJson } from "../utils.js";
|
||||||
import { PackageJson } from '../utils.js';
|
|
||||||
|
|
||||||
export default function generateHeader({ version, devDependencies }: PackageJson, outputType: OutputType) {
|
import { OutputType } from "./interfaces.js";
|
||||||
|
|
||||||
|
export default function generateHeader(
|
||||||
|
{ version, devDependencies }: PackageJson,
|
||||||
|
outputType: OutputType,
|
||||||
|
) {
|
||||||
return `/* eslint-disable */
|
return `/* eslint-disable */
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* This file is generated using:
|
* This file is generated using:
|
||||||
* @fluencelabs/aqua-api version: ${devDependencies['@fluencelabs/aqua-api']}
|
* @fluencelabs/aqua-api version: ${devDependencies["@fluencelabs/aqua-api"]}
|
||||||
* @fluencelabs/aqua-to-js version: ${version}
|
* @fluencelabs/aqua-to-js version: ${version}
|
||||||
* If you find any bugs in generated AIR, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
* If you find any bugs in generated AIR, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
||||||
* If you find any bugs in generated JS/TS, please write an issue on GitHub: https://github.com/fluencelabs/js-client/issues
|
* If you find any bugs in generated JS/TS, please write an issue on GitHub: https://github.com/fluencelabs/js-client/issues
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
${outputType === 'ts' ? 'import type { IFluenceClient as IFluenceClient$$, CallParams as CallParams$$ } from \'@fluencelabs/js-client\';' : ''}
|
${
|
||||||
|
outputType === "ts"
|
||||||
|
? "import type { IFluenceClient as IFluenceClient$$, CallParams as CallParams$$ } from '@fluencelabs/js-client';"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
|
||||||
import {
|
import {
|
||||||
v5_callFunction as callFunction$$,
|
v5_callFunction as callFunction$$,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,46 +14,80 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CompilationResult, JSTypeGenerator, OutputType, TSTypeGenerator, TypeGenerator } from './interfaces.js';
|
import { PackageJson } from "../utils.js";
|
||||||
import { PackageJson } from '../utils.js';
|
|
||||||
import { generateServices } from './service.js';
|
import { generateFunctions } from "./function.js";
|
||||||
import { generateFunctions } from './function.js';
|
import header from "./header.js";
|
||||||
import header from './header.js';
|
import {
|
||||||
|
CompilationResult,
|
||||||
|
JSTypeGenerator,
|
||||||
|
OutputType,
|
||||||
|
TSTypeGenerator,
|
||||||
|
TypeGenerator,
|
||||||
|
} from "./interfaces.js";
|
||||||
|
import { generateServices } from "./service.js";
|
||||||
|
|
||||||
const typeGenerators: Record<OutputType, TypeGenerator> = {
|
const typeGenerators: Record<OutputType, TypeGenerator> = {
|
||||||
'js': new JSTypeGenerator(),
|
js: new JSTypeGenerator(),
|
||||||
'ts': new TSTypeGenerator()
|
ts: new TSTypeGenerator(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function generateSources({ services, functions }: CompilationResult, outputType: OutputType, packageJson: PackageJson) {
|
export function generateSources(
|
||||||
|
{ services, functions }: CompilationResult,
|
||||||
|
outputType: OutputType,
|
||||||
|
packageJson: PackageJson,
|
||||||
|
) {
|
||||||
const typeGenerator = typeGenerators[outputType];
|
const typeGenerator = typeGenerators[outputType];
|
||||||
return `${header(packageJson, outputType)}
|
return `${header(packageJson, outputType)}
|
||||||
|
|
||||||
${Object.entries(services).length > 0 ? `// Services
|
${
|
||||||
|
Object.entries(services).length > 0
|
||||||
|
? `// Services
|
||||||
${generateServices(typeGenerator, services)}
|
${generateServices(typeGenerator, services)}
|
||||||
` : ''}
|
`
|
||||||
${Object.entries(functions).length > 0 ? `// Functions
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
Object.entries(functions).length > 0
|
||||||
|
? `// Functions
|
||||||
${generateFunctions(typeGenerator, functions)}
|
${generateFunctions(typeGenerator, functions)}
|
||||||
`: ''}`
|
`
|
||||||
|
: ""
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateTypes({ services, functions }: CompilationResult, packageJson: PackageJson) {
|
export function generateTypes(
|
||||||
const typeGenerator = typeGenerators['ts'];
|
{ services, functions }: CompilationResult,
|
||||||
|
packageJson: PackageJson,
|
||||||
|
) {
|
||||||
|
const typeGenerator = typeGenerators["ts"];
|
||||||
|
|
||||||
const generatedServices = Object.entries(services)
|
const generatedServices = Object.entries(services)
|
||||||
.map(([srvName, srvDef]) => typeGenerator.serviceType(srvName, srvDef))
|
.map(([srvName, srvDef]) => {
|
||||||
.join('\n');
|
return typeGenerator.serviceType(srvName, srvDef);
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
const generatedFunctions = Object.entries(functions)
|
const generatedFunctions = Object.entries(functions)
|
||||||
.map(([funcName, funcDef]) => typeGenerator.funcType(funcDef))
|
.map(([, funcDef]) => {
|
||||||
.join('\n');
|
return typeGenerator.funcType(funcDef);
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
return `${header(packageJson, 'ts')}
|
return `${header(packageJson, "ts")}
|
||||||
|
|
||||||
${Object.entries(services).length > 0 ? `// Services
|
${
|
||||||
|
Object.entries(services).length > 0
|
||||||
|
? `// Services
|
||||||
${generatedServices}
|
${generatedServices}
|
||||||
` : ''}
|
`
|
||||||
${Object.entries(functions).length > 0 ? `// Functions
|
: ""
|
||||||
${generatedFunctions}
|
}
|
||||||
`: ''}`
|
${
|
||||||
|
Object.entries(functions).length > 0
|
||||||
|
? `// Functions
|
||||||
|
${generatedFunctions}
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}`;
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,10 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CLIENT } from '../constants.js';
|
import { FunctionCallDef, ServiceDef } from "@fluencelabs/interfaces";
|
||||||
import { FunctionCallDef, ServiceDef } from '@fluencelabs/interfaces';
|
|
||||||
import { genTypeName, typeToTs } from '../common.js';
|
import { genTypeName, typeToTs } from "../common.js";
|
||||||
import { capitalize, getFuncArgs } from '../utils.js';
|
import { CLIENT } from "../constants.js";
|
||||||
|
import { capitalize, getFuncArgs } from "../utils.js";
|
||||||
|
|
||||||
export interface TypeGenerator {
|
export interface TypeGenerator {
|
||||||
type(field: string, type: string): string;
|
type(field: string, type: string): string;
|
||||||
@ -42,58 +43,94 @@ export class TSTypeGenerator implements TypeGenerator {
|
|||||||
|
|
||||||
funcType({ funcDef }: AquaFunction): string {
|
funcType({ funcDef }: AquaFunction): string {
|
||||||
const args = getFuncArgs(funcDef.arrow.domain).map(([name, type]) => {
|
const args = getFuncArgs(funcDef.arrow.domain).map(([name, type]) => {
|
||||||
const [typeDesc, t] = genTypeName(type, capitalize(funcDef.functionName) + 'Arg' + capitalize(name));
|
const [typeDesc, t] = genTypeName(
|
||||||
|
type,
|
||||||
|
capitalize(funcDef.functionName) + "Arg" + capitalize(name),
|
||||||
|
);
|
||||||
|
|
||||||
return [typeDesc, `${name}: ${t}`] as const;
|
return [typeDesc, `${name}: ${t}`] as const;
|
||||||
});
|
});
|
||||||
|
|
||||||
args.push([undefined, `config?: {ttl?: number}`]);
|
args.push([undefined, `config?: {ttl?: number}`]);
|
||||||
|
|
||||||
const argsDefs = args.map(([, def]) => " " + def);
|
const argsDefs = args.map(([, def]) => {
|
||||||
const argsDesc = args.filter(([desc]) => desc !== undefined).map(([desc]) => desc);
|
return " " + def;
|
||||||
|
});
|
||||||
|
|
||||||
|
const argsDesc = args
|
||||||
|
.filter(([desc]) => {
|
||||||
|
return desc !== undefined;
|
||||||
|
})
|
||||||
|
.map(([desc]) => {
|
||||||
|
return desc;
|
||||||
|
});
|
||||||
|
|
||||||
const functionOverloads = [
|
const functionOverloads = [
|
||||||
argsDefs.join(',\n'),
|
argsDefs.join(",\n"),
|
||||||
[` peer: ${CLIENT}`, ...argsDefs].join(',\n')
|
[` peer: ${CLIENT}`, ...argsDefs].join(",\n"),
|
||||||
];
|
];
|
||||||
|
|
||||||
const [resTypeDesc, resType] = genTypeName(funcDef.arrow.codomain, capitalize(funcDef.functionName) + "Result");
|
const [resTypeDesc, resType] = genTypeName(
|
||||||
|
funcDef.arrow.codomain,
|
||||||
|
capitalize(funcDef.functionName) + "Result",
|
||||||
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
argsDesc.join('\n'),
|
argsDesc.join("\n"),
|
||||||
resTypeDesc || "",
|
resTypeDesc ?? "",
|
||||||
functionOverloads.flatMap(fo => [
|
functionOverloads
|
||||||
|
.flatMap((fo) => {
|
||||||
|
return [
|
||||||
`export function ${funcDef.functionName}(`,
|
`export function ${funcDef.functionName}(`,
|
||||||
fo,
|
fo,
|
||||||
`): Promise<${resType}>;`,
|
`): Promise<${resType}>;`,
|
||||||
''
|
"",
|
||||||
]).join('\n')
|
];
|
||||||
].filter(s => s !== '').join('\n\n');
|
})
|
||||||
|
.join("\n"),
|
||||||
|
]
|
||||||
|
.filter((s) => {
|
||||||
|
return s !== "";
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceType(srvName: string, srvDef: ServiceDef): string {
|
serviceType(srvName: string, srvDef: ServiceDef): string {
|
||||||
const members = srvDef.functions.tag === 'nil' ? [] : Object.entries(srvDef.functions.fields);
|
const members =
|
||||||
|
srvDef.functions.tag === "nil"
|
||||||
|
? []
|
||||||
|
: Object.entries(srvDef.functions.fields);
|
||||||
|
|
||||||
const interfaceDefs = members
|
const interfaceDefs = members
|
||||||
.map(([name, arrow]) => {
|
.map(([name, arrow]) => {
|
||||||
return ` ${name}: ${typeToTs(arrow)};`;
|
return ` ${name}: ${typeToTs(arrow)};`;
|
||||||
})
|
})
|
||||||
.join('\n');
|
.join("\n");
|
||||||
|
|
||||||
const interfaces = [`export interface ${srvName}Def {`, interfaceDefs, '}'].join('\n');
|
const interfaces = [
|
||||||
|
`export interface ${srvName}Def {`,
|
||||||
|
interfaceDefs,
|
||||||
|
"}",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
const peerDecl = `peer: ${CLIENT}`;
|
const peerDecl = `peer: ${CLIENT}`;
|
||||||
const serviceDecl = `service: ${srvName}Def`;
|
const serviceDecl = `service: ${srvName}Def`;
|
||||||
const serviceIdDecl = `serviceId: string`;
|
const serviceIdDecl = `serviceId: string`;
|
||||||
|
|
||||||
const registerServiceArgs = [
|
const registerServiceArgs = [
|
||||||
[serviceDecl],
|
[serviceDecl],
|
||||||
[serviceIdDecl, serviceDecl],
|
[serviceIdDecl, serviceDecl],
|
||||||
[peerDecl, serviceDecl],
|
[peerDecl, serviceDecl],
|
||||||
[peerDecl, serviceIdDecl, serviceDecl]
|
[peerDecl, serviceIdDecl, serviceDecl],
|
||||||
];
|
];
|
||||||
|
|
||||||
return [interfaces, ...registerServiceArgs.map(registerServiceArg => {
|
return [
|
||||||
const args = registerServiceArg.join(', ');
|
interfaces,
|
||||||
return `export function register${srvName}(${args}): void;`
|
...registerServiceArgs.map((registerServiceArg) => {
|
||||||
})].join('\n');
|
const args = registerServiceArg.join(", ");
|
||||||
|
return `export function register${srvName}(${args}): void;`;
|
||||||
|
}),
|
||||||
|
].join("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,20 +139,20 @@ export class JSTypeGenerator implements TypeGenerator {
|
|||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
generic(field: string, type: string): string {
|
generic(field: string): string {
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
type(field: string, type: string): string {
|
type(field: string): string {
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
funcType(): string {
|
funcType(): string {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceType(): string {
|
serviceType(): string {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,4 +170,4 @@ export interface EntityGenerator {
|
|||||||
generate(compilationResult: CompilationResult): string;
|
generate(compilationResult: CompilationResult): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OutputType = 'js' | 'ts';
|
export type OutputType = "js" | "ts";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,45 +14,74 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ServiceDef } from '@fluencelabs/interfaces';
|
import { ServiceDef } from "@fluencelabs/interfaces";
|
||||||
import { recursiveRenameLaquaProps } from '../utils.js';
|
|
||||||
import { TypeGenerator } from './interfaces.js';
|
import { recursiveRenameLaquaProps } from "../utils.js";
|
||||||
|
|
||||||
|
import { TypeGenerator } from "./interfaces.js";
|
||||||
|
|
||||||
interface DefaultServiceId {
|
interface DefaultServiceId {
|
||||||
s_Some__f_value?: string
|
s_Some__f_value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateServices(typeGenerator: TypeGenerator, services: Record<string, ServiceDef>) {
|
export function generateServices(
|
||||||
const generated = Object.entries(services).map(([srvName, srvDef]) => generateService(typeGenerator, srvName, srvDef)).join('\n\n');
|
typeGenerator: TypeGenerator,
|
||||||
|
services: Record<string, ServiceDef>,
|
||||||
|
) {
|
||||||
|
const generated = Object.entries(services)
|
||||||
|
.map(([srvName, srvDef]) => {
|
||||||
|
return generateService(typeGenerator, srvName, srvDef);
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
|
|
||||||
return generated + '\n';
|
return generated + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateService(typeGenerator: TypeGenerator, srvName: string, srvDef: ServiceDef) {
|
function generateService(
|
||||||
|
typeGenerator: TypeGenerator,
|
||||||
|
srvName: string,
|
||||||
|
srvDef: ServiceDef,
|
||||||
|
) {
|
||||||
return [
|
return [
|
||||||
typeGenerator.serviceType(srvName, srvDef),
|
typeGenerator.serviceType(srvName, srvDef),
|
||||||
generateRegisterServiceOverload(typeGenerator, srvName, srvDef)
|
generateRegisterServiceOverload(typeGenerator, srvName, srvDef),
|
||||||
].join('\n');
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateRegisterServiceOverload(typeGenerator: TypeGenerator, srvName: string, srvDef: ServiceDef) {
|
function generateRegisterServiceOverload(
|
||||||
|
typeGenerator: TypeGenerator,
|
||||||
|
srvName: string,
|
||||||
|
srvDef: ServiceDef,
|
||||||
|
) {
|
||||||
return [
|
return [
|
||||||
`export function register${srvName}(${typeGenerator.type('...args', 'any[]')}) {`,
|
`export function register${srvName}(${typeGenerator.type(
|
||||||
' registerService$$(',
|
"...args",
|
||||||
' args,',
|
"any[]",
|
||||||
|
)}) {`,
|
||||||
|
" registerService$$(",
|
||||||
|
" args,",
|
||||||
` ${serviceToJson(srvDef)}`,
|
` ${serviceToJson(srvDef)}`,
|
||||||
' );',
|
" );",
|
||||||
'}'
|
"}",
|
||||||
].join('\n');
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function serviceToJson(service: ServiceDef): string {
|
function serviceToJson(service: ServiceDef): string {
|
||||||
return JSON.stringify({
|
return JSON.stringify(
|
||||||
...(
|
{
|
||||||
(service.defaultServiceId as DefaultServiceId)?.s_Some__f_value
|
// This assertion is required because aqua-api gives bad types
|
||||||
? { defaultServiceId: (service.defaultServiceId as DefaultServiceId).s_Some__f_value }
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
: {}
|
...((service.defaultServiceId as DefaultServiceId).s_Some__f_value != null
|
||||||
),
|
? {
|
||||||
functions: recursiveRenameLaquaProps(service.functions)
|
defaultServiceId:
|
||||||
}, null, 4);
|
// This assertion is required because aqua-api gives bad types
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
(service.defaultServiceId as DefaultServiceId).s_Some__f_value,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
functions: recursiveRenameLaquaProps(service.functions),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
4,
|
||||||
|
);
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,12 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { generateSources, generateTypes } from "./generate/index.js";
|
||||||
generateSources,
|
import { CompilationResult, OutputType } from "./generate/interfaces.js";
|
||||||
generateTypes,
|
import { getPackageJsonContent } from "./utils.js";
|
||||||
} from './generate/index.js';
|
|
||||||
import { CompilationResult, OutputType } from './generate/interfaces.js';
|
|
||||||
import { getPackageJsonContent } from './utils.js';
|
|
||||||
|
|
||||||
interface JsOutput {
|
interface JsOutput {
|
||||||
sources: string;
|
sources: string;
|
||||||
@ -31,17 +28,33 @@ interface TsOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LanguageOutput = {
|
type LanguageOutput = {
|
||||||
"js": JsOutput,
|
js: JsOutput;
|
||||||
"ts": TsOutput
|
ts: TsOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function aquaToJs<T extends OutputType>(res: CompilationResult, outputType: T): Promise<LanguageOutput[T]> {
|
type NothingToGenerate = null;
|
||||||
|
|
||||||
|
export default async function aquaToJs<T extends OutputType>(
|
||||||
|
res: CompilationResult,
|
||||||
|
outputType: T,
|
||||||
|
): Promise<LanguageOutput[T] | NothingToGenerate> {
|
||||||
|
if (
|
||||||
|
Object.keys(res.services).length === 0 &&
|
||||||
|
Object.keys(res.functions).length === 0
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const packageJson = await getPackageJsonContent();
|
const packageJson = await getPackageJsonContent();
|
||||||
|
|
||||||
return outputType === 'js' ? {
|
return outputType === "js"
|
||||||
sources: await generateSources(res, 'js', packageJson),
|
? {
|
||||||
types: await generateTypes(res, packageJson)
|
sources: generateSources(res, "js", packageJson),
|
||||||
} : {
|
types: generateTypes(res, packageJson),
|
||||||
sources: await generateSources(res, 'ts', packageJson),
|
}
|
||||||
} as LanguageOutput[T];
|
: // TODO: probably there is a way to remove this type assert
|
||||||
};
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
({
|
||||||
|
sources: generateSources(res, "ts", packageJson),
|
||||||
|
} as LanguageOutput[T]);
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,53 +14,90 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ArrowWithoutCallbacks, NonArrowType, ProductType } from '@fluencelabs/interfaces';
|
import assert from "assert";
|
||||||
import { readFile } from 'fs/promises';
|
import { readFile } from "fs/promises";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
|
|
||||||
|
import {
|
||||||
|
ArrowType,
|
||||||
|
ArrowWithoutCallbacks,
|
||||||
|
JSONValue,
|
||||||
|
LabeledProductType,
|
||||||
|
NilType,
|
||||||
|
SimpleTypes,
|
||||||
|
UnlabeledProductType,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
export interface PackageJson {
|
export interface PackageJson {
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
['@fluencelabs/aqua-api']: string
|
["@fluencelabs/aqua-api"]: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPackageJsonContent(): Promise<PackageJson> {
|
export async function getPackageJsonContent(): Promise<PackageJson> {
|
||||||
const content = await readFile(new URL(path.join('..', 'package.json'), import.meta.url), 'utf-8');
|
const content = await readFile(
|
||||||
return JSON.parse(content);
|
new URL(path.join("..", "package.json"), import.meta.url),
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Add validation here
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
return JSON.parse(content) as PackageJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFuncArgs(domain: ProductType<NonArrowType | ArrowWithoutCallbacks>): [string, NonArrowType | ArrowWithoutCallbacks][] {
|
export function getFuncArgs(
|
||||||
if (domain.tag === 'labeledProduct') {
|
domain:
|
||||||
return Object.entries(domain.fields).map(([label, type]) => [label, type]);
|
| LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||||
} else if (domain.tag === 'unlabeledProduct') {
|
| UnlabeledProductType
|
||||||
return domain.items.map((type, index) => ['arg' + index, type]);
|
| NilType,
|
||||||
|
): [string, SimpleTypes | ArrowWithoutCallbacks][] {
|
||||||
|
if (domain.tag === "labeledProduct") {
|
||||||
|
return Object.entries(domain.fields).map(([label, type]) => {
|
||||||
|
return [label, type];
|
||||||
|
});
|
||||||
|
} else if (domain.tag === "unlabeledProduct") {
|
||||||
|
return domain.items.map((type, index) => {
|
||||||
|
return ["arg" + index, type];
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function recursiveRenameLaquaProps(obj: unknown): unknown {
|
export function recursiveRenameLaquaProps(obj: JSONValue): unknown {
|
||||||
if (typeof obj !== 'object' || obj === null) return obj;
|
if (typeof obj !== "object" || obj === null) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(obj)) {
|
if (Array.isArray(obj)) {
|
||||||
return obj.map(item => recursiveRenameLaquaProps(item));
|
return obj.map((item) => {
|
||||||
|
return recursiveRenameLaquaProps(item);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.getOwnPropertyNames(obj).reduce((acc, prop) => {
|
return Object.getOwnPropertyNames(obj).reduce((acc, prop) => {
|
||||||
let accessProp = prop;
|
let accessProp = prop;
|
||||||
if (prop.includes('Laqua_js')) {
|
|
||||||
|
if (prop.includes("Laqua_js")) {
|
||||||
// Last part of the property separated by "_" is a correct name
|
// Last part of the property separated by "_" is a correct name
|
||||||
const refinedProperty = prop.split('_').pop()!;
|
const refinedProperty = prop.split("_").pop();
|
||||||
|
|
||||||
|
if (refinedProperty == null) {
|
||||||
|
throw new Error(`Bad property name: ${prop}.`);
|
||||||
|
}
|
||||||
|
|
||||||
if (refinedProperty in obj) {
|
if (refinedProperty in obj) {
|
||||||
accessProp = refinedProperty;
|
accessProp = refinedProperty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(accessProp in obj);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
[accessProp]: recursiveRenameLaquaProps(obj[accessProp as keyof typeof obj])
|
[accessProp]: recursiveRenameLaquaProps(obj[accessProp]),
|
||||||
};
|
};
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist", "src/**/__test__"],
|
"exclude": ["node_modules", "dist", "src/**/__test__"]
|
||||||
}
|
}
|
||||||
|
@ -2,85 +2,74 @@
|
|||||||
|
|
||||||
## [0.8.2](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.1...interfaces-v0.8.2) (2023-08-24)
|
## [0.8.2](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.1...interfaces-v0.8.2) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* use marine-js 0.7.2 ([#321](https://github.com/fluencelabs/js-client/issues/321)) ([c99a509](https://github.com/fluencelabs/js-client/commit/c99a509c8743471856b0beb25696ffe7357d5399))
|
- use marine-js 0.7.2 ([#321](https://github.com/fluencelabs/js-client/issues/321)) ([c99a509](https://github.com/fluencelabs/js-client/commit/c99a509c8743471856b0beb25696ffe7357d5399))
|
||||||
|
|
||||||
## [0.8.1](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.0...interfaces-v0.8.1) (2023-08-08)
|
## [0.8.1](https://github.com/fluencelabs/js-client/compare/interfaces-v0.8.0...interfaces-v0.8.1) (2023-08-08)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.43.1 ([#322](https://github.com/fluencelabs/js-client/issues/322)) ([c1d1fa6](https://github.com/fluencelabs/js-client/commit/c1d1fa6659b6dc2c6707786748b3410fab7f1bcd))
|
- **deps:** update dependency @fluencelabs/avm to v0.43.1 ([#322](https://github.com/fluencelabs/js-client/issues/322)) ([c1d1fa6](https://github.com/fluencelabs/js-client/commit/c1d1fa6659b6dc2c6707786748b3410fab7f1bcd))
|
||||||
|
|
||||||
## [0.8.0](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.6...interfaces-v0.8.0) (2023-06-29)
|
## [0.8.0](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.6...interfaces-v0.8.0) (2023-06-29)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315)
|
- **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315) ([8bae6e2](https://github.com/fluencelabs/js-client/commit/8bae6e24e62153b567f320ccecc7bce76bc826d1))
|
- **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315) ([8bae6e2](https://github.com/fluencelabs/js-client/commit/8bae6e24e62153b567f320ccecc7bce76bc826d1))
|
||||||
|
|
||||||
## [0.7.6](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.5...interfaces-v0.7.6) (2023-06-20)
|
## [0.7.6](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.5...interfaces-v0.7.6) (2023-06-20)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* support signatures [fixes DXJ-389] ([#310](https://github.com/fluencelabs/js-client/issues/310)) ([a60dfe0](https://github.com/fluencelabs/js-client/commit/a60dfe0d680b4d9ac5092dec64e2ebf478bf80eb))
|
- support signatures [fixes DXJ-389] ([#310](https://github.com/fluencelabs/js-client/issues/310)) ([a60dfe0](https://github.com/fluencelabs/js-client/commit/a60dfe0d680b4d9ac5092dec64e2ebf478bf80eb))
|
||||||
|
|
||||||
## [0.7.5](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.4...interfaces-v0.7.5) (2023-04-04)
|
## [0.7.5](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.4...interfaces-v0.7.5) (2023-04-04)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Cleaning up technical debts ([#295](https://github.com/fluencelabs/js-client/issues/295)) ([0b2f12d](https://github.com/fluencelabs/js-client/commit/0b2f12d8ac223db341d6c30ff403166b3eae2e56))
|
- Cleaning up technical debts ([#295](https://github.com/fluencelabs/js-client/issues/295)) ([0b2f12d](https://github.com/fluencelabs/js-client/commit/0b2f12d8ac223db341d6c30ff403166b3eae2e56))
|
||||||
|
|
||||||
## [0.7.4](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.3...interfaces-v0.7.4) (2023-03-31)
|
## [0.7.4](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.3...interfaces-v0.7.4) (2023-03-31)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **logs:** Use `debug.js` library for logging [DXJ-327] ([#285](https://github.com/fluencelabs/js-client/issues/285)) ([e95c34a](https://github.com/fluencelabs/js-client/commit/e95c34a79220bd8ecdcee806802ac3d69a2af0cb))
|
- **logs:** Use `debug.js` library for logging [DXJ-327] ([#285](https://github.com/fluencelabs/js-client/issues/285)) ([e95c34a](https://github.com/fluencelabs/js-client/commit/e95c34a79220bd8ecdcee806802ac3d69a2af0cb))
|
||||||
|
|
||||||
## [0.7.3](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.2...interfaces-v0.7.3) (2023-02-16)
|
## [0.7.3](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.2...interfaces-v0.7.3) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Trigger release to publish packages that were built ([#262](https://github.com/fluencelabs/js-client/issues/262)) ([47abf38](https://github.com/fluencelabs/js-client/commit/47abf3882956ffbdc52df372db26ba6252e8306b))
|
- Trigger release to publish packages that were built ([#262](https://github.com/fluencelabs/js-client/issues/262)) ([47abf38](https://github.com/fluencelabs/js-client/commit/47abf3882956ffbdc52df372db26ba6252e8306b))
|
||||||
|
|
||||||
## [0.7.2](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.1...interfaces-v0.7.2) (2023-02-16)
|
## [0.7.2](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.1...interfaces-v0.7.2) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add `getRelayPeerId` method for `IFluenceClient` ([#260](https://github.com/fluencelabs/js-client/issues/260)) ([a10278a](https://github.com/fluencelabs/js-client/commit/a10278afaa782a307feb10c4eac060094c101230))
|
- Add `getRelayPeerId` method for `IFluenceClient` ([#260](https://github.com/fluencelabs/js-client/issues/260)) ([a10278a](https://github.com/fluencelabs/js-client/commit/a10278afaa782a307feb10c4eac060094c101230))
|
||||||
|
|
||||||
## [0.7.1](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.0...interfaces-v0.7.1) (2023-02-16)
|
## [0.7.1](https://github.com/fluencelabs/js-client/compare/interfaces-v0.7.0...interfaces-v0.7.1) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Simplify JS Client public API ([#257](https://github.com/fluencelabs/js-client/issues/257)) ([9daaf41](https://github.com/fluencelabs/js-client/commit/9daaf410964d43228192c829c7ff785db6e88081))
|
- Simplify JS Client public API ([#257](https://github.com/fluencelabs/js-client/issues/257)) ([9daaf41](https://github.com/fluencelabs/js-client/commit/9daaf410964d43228192c829c7ff785db6e88081))
|
||||||
|
|
||||||
## [0.7.0](https://github.com/fluencelabs/fluence-js/compare/interfaces-v0.6.0...interfaces-v0.7.0) (2023-02-15)
|
## [0.7.0](https://github.com/fluencelabs/fluence-js/compare/interfaces-v0.6.0...interfaces-v0.7.0) (2023-02-15)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246))
|
- Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246))
|
||||||
* Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243))
|
- Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243))
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246)) ([d4bb8fb](https://github.com/fluencelabs/fluence-js/commit/d4bb8fb42964b3ba25154232980b9ae82c21e627))
|
- Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246)) ([d4bb8fb](https://github.com/fluencelabs/fluence-js/commit/d4bb8fb42964b3ba25154232980b9ae82c21e627))
|
||||||
* Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243)) ([9667c4f](https://github.com/fluencelabs/fluence-js/commit/9667c4fec6868f984bba13249f3c47d293396406))
|
- Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243)) ([9667c4f](https://github.com/fluencelabs/fluence-js/commit/9667c4fec6868f984bba13249f3c47d293396406))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* NodeJS package building ([#248](https://github.com/fluencelabs/fluence-js/issues/248)) ([0d05e51](https://github.com/fluencelabs/fluence-js/commit/0d05e517d89529af513fcb96cfa6c722ccc357a7))
|
- NodeJS package building ([#248](https://github.com/fluencelabs/fluence-js/issues/248)) ([0d05e51](https://github.com/fluencelabs/fluence-js/commit/0d05e517d89529af513fcb96cfa6c722ccc357a7))
|
||||||
|
@ -51,6 +51,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@multiformats/multiaddr": "11.3.0",
|
"@multiformats/multiaddr": "11.3.0",
|
||||||
"@fluencelabs/avm": "0.48.0",
|
"@fluencelabs/avm": "0.48.0",
|
||||||
"@fluencelabs/marine-js": "0.7.2"
|
"hotscript": "1.0.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,7 +13,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { SecurityTetraplet } from '@fluencelabs/avm';
|
|
||||||
|
import type { SecurityTetraplet } from "@fluencelabs/avm";
|
||||||
|
|
||||||
|
import { InterfaceToType, MaybePromise } from "./utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peer ID's id as a base58 string (multihash/CIDv0).
|
* Peer ID's id as a base58 string (multihash/CIDv0).
|
||||||
@ -32,7 +35,7 @@ export type Node = {
|
|||||||
* Additional information about a service call
|
* Additional information about a service call
|
||||||
* @typeparam ArgName
|
* @typeparam ArgName
|
||||||
*/
|
*/
|
||||||
export interface CallParams<ArgName extends string | null> {
|
export type CallParams<ArgName extends string | null> = {
|
||||||
/**
|
/**
|
||||||
* The identifier of particle which triggered the call
|
* The identifier of particle which triggered the call
|
||||||
*/
|
*/
|
||||||
@ -61,5 +64,24 @@ export interface CallParams<ArgName extends string | null> {
|
|||||||
/**
|
/**
|
||||||
* Security tetraplets
|
* Security tetraplets
|
||||||
*/
|
*/
|
||||||
tetraplets: ArgName extends string ? Record<ArgName, SecurityTetraplet[]> : Record<string, never>;
|
tetraplets: ArgName extends string
|
||||||
}
|
? Record<ArgName, InterfaceToType<SecurityTetraplet>[]>
|
||||||
|
: Record<string, never>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServiceImpl = Record<
|
||||||
|
string,
|
||||||
|
(
|
||||||
|
...args: [...JSONArray, CallParams<string>]
|
||||||
|
) => MaybePromise<JSONValue | undefined>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type JSONValue =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| { [x: string]: JSONValue }
|
||||||
|
| Array<JSONValue>;
|
||||||
|
export type JSONArray = Array<JSONValue>;
|
||||||
|
export type JSONObject = { [x: string]: JSONValue };
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,77 +13,85 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
type SimpleTypes = ScalarType | OptionType | ArrayType | StructType | TopType | BottomType | NilType;
|
|
||||||
|
|
||||||
export type NonArrowType = SimpleTypes | ProductType<SimpleTypes>;
|
export type SimpleTypes =
|
||||||
|
| ScalarType
|
||||||
|
| OptionType
|
||||||
|
| ArrayType
|
||||||
|
| StructType
|
||||||
|
| TopType
|
||||||
|
| BottomType
|
||||||
|
| NilType;
|
||||||
|
|
||||||
|
export type NonArrowType = SimpleTypes | ProductType;
|
||||||
|
|
||||||
export type TopType = {
|
export type TopType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'topType';
|
tag: "topType";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BottomType = {
|
export type BottomType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'bottomType';
|
tag: "bottomType";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OptionType = {
|
export type OptionType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'option';
|
tag: "option";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Underlying type of the option
|
* Underlying type of the option
|
||||||
*/
|
*/
|
||||||
type: NonArrowType;
|
type: SimpleTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NilType = {
|
export type NilType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'nil';
|
tag: "nil";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ArrayType = {
|
export type ArrayType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'array';
|
tag: "array";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of array elements
|
* Type of array elements
|
||||||
*/
|
*/
|
||||||
type: NonArrowType;
|
type: SimpleTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All possible scalar type names
|
* All possible scalar type names
|
||||||
*/
|
*/
|
||||||
export type ScalarNames =
|
export type ScalarNames =
|
||||||
| 'u8'
|
| "u8"
|
||||||
| 'u16'
|
| "u16"
|
||||||
| 'u32'
|
| "u32"
|
||||||
| 'u64'
|
| "u64"
|
||||||
| 'i8'
|
| "i8"
|
||||||
| 'i16'
|
| "i16"
|
||||||
| 'i32'
|
| "i32"
|
||||||
| 'i64'
|
| "i64"
|
||||||
| 'f32'
|
| "f32"
|
||||||
| 'f64'
|
| "f64"
|
||||||
| 'bool'
|
| "bool"
|
||||||
| 'string';
|
| "string";
|
||||||
|
|
||||||
export type ScalarType = {
|
export type ScalarType = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'scalar';
|
tag: "scalar";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the scalar type
|
* Name of the scalar type
|
||||||
@ -95,7 +103,7 @@ export type StructType = {
|
|||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'struct';
|
tag: "struct";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Struct name
|
* Struct name
|
||||||
@ -105,65 +113,75 @@ export type StructType = {
|
|||||||
/**
|
/**
|
||||||
* Struct fields
|
* Struct fields
|
||||||
*/
|
*/
|
||||||
fields: { [key: string]: NonArrowType };
|
fields: { [key: string]: SimpleTypes };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LabeledProductType<T> = {
|
export type LabeledProductType<
|
||||||
|
T extends
|
||||||
|
| SimpleTypes
|
||||||
|
| ArrowType<LabeledProductType<SimpleTypes> | UnlabeledProductType> =
|
||||||
|
| SimpleTypes
|
||||||
|
| ArrowType<LabeledProductType<SimpleTypes> | UnlabeledProductType>,
|
||||||
|
K extends { [key: string]: T } = { [key: string]: T },
|
||||||
|
> = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'labeledProduct';
|
tag: "labeledProduct";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Labelled product fields
|
* Labelled product fields
|
||||||
*/
|
*/
|
||||||
fields: { [key: string]: T };
|
fields: K;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UnlabeledProductType<T> = {
|
export type UnlabeledProductType<T extends Array<SimpleTypes> = SimpleTypes[]> =
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'unlabeledProduct';
|
tag: "unlabeledProduct";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items in unlabelled product
|
* Items in unlabelled product
|
||||||
*/
|
*/
|
||||||
items: Array<T>;
|
items: T;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProductType<T> = UnlabeledProductType<T> | LabeledProductType<T> | NilType;
|
export type ProductType = UnlabeledProductType | LabeledProductType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ArrowType is a profunctor pointing its domain to codomain.
|
* ArrowType is a profunctor pointing its domain to codomain.
|
||||||
* Profunctor means variance: Arrow is contravariant on domain, and variant on codomain.
|
* Profunctor means variance: Arrow is contravariant on domain, and variant on codomain.
|
||||||
*/
|
*/
|
||||||
export type ArrowType<T> = {
|
export type ArrowType<T extends LabeledProductType | UnlabeledProductType> = {
|
||||||
/**
|
/**
|
||||||
* Type descriptor. Used for pattern-matching
|
* Type descriptor. Used for pattern-matching
|
||||||
*/
|
*/
|
||||||
tag: 'arrow';
|
tag: "arrow";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where this Arrow is defined
|
* Where this Arrow is defined
|
||||||
*/
|
*/
|
||||||
domain: ProductType<T>;
|
domain: T | NilType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where this Arrow points to
|
* Where this Arrow points to
|
||||||
*/
|
*/
|
||||||
codomain: UnlabeledProductType<NonArrowType> | NilType;
|
codomain: UnlabeledProductType | NilType;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrow which domain contains only non-arrow types
|
* Arrow which domain contains only non-arrow types
|
||||||
*/
|
*/
|
||||||
export type ArrowWithoutCallbacks = ArrowType<NonArrowType>;
|
export type ArrowWithoutCallbacks = ArrowType<
|
||||||
|
UnlabeledProductType | LabeledProductType<SimpleTypes>
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrow which domain does can contain both non-arrow types and arrows (which themselves cannot contain arrows)
|
* Arrow which domain does can contain both non-arrow types and arrows (which themselves cannot contain arrows)
|
||||||
*/
|
*/
|
||||||
export type ArrowWithCallbacks = ArrowType<NonArrowType | ArrowWithoutCallbacks>;
|
export type ArrowWithCallbacks = ArrowType<LabeledProductType>;
|
||||||
|
|
||||||
export interface FunctionCallConstants {
|
export interface FunctionCallConstants {
|
||||||
/**
|
/**
|
||||||
@ -214,7 +232,9 @@ export interface FunctionCallDef {
|
|||||||
/**
|
/**
|
||||||
* Underlying arrow which represents function in aqua
|
* Underlying arrow which represents function in aqua
|
||||||
*/
|
*/
|
||||||
arrow: ArrowWithCallbacks;
|
arrow: ArrowType<
|
||||||
|
LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Names of the different entities used in generated air script
|
* Names of the different entities used in generated air script
|
||||||
@ -234,7 +254,9 @@ export interface ServiceDef {
|
|||||||
/**
|
/**
|
||||||
* List of functions which the service consists of
|
* List of functions which the service consists of
|
||||||
*/
|
*/
|
||||||
functions: LabeledProductType<ArrowWithoutCallbacks> | NilType;
|
functions:
|
||||||
|
| LabeledProductType<ArrowType<LabeledProductType<SimpleTypes>>>
|
||||||
|
| NilType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -253,17 +275,17 @@ export const getArgumentTypes = (
|
|||||||
): {
|
): {
|
||||||
[key: string]: NonArrowType | ArrowWithoutCallbacks;
|
[key: string]: NonArrowType | ArrowWithoutCallbacks;
|
||||||
} => {
|
} => {
|
||||||
if (def.arrow.domain.tag !== 'labeledProduct') {
|
if (def.arrow.domain.tag !== "labeledProduct") {
|
||||||
throw new Error('Should be impossible');
|
throw new Error("Should be impossible");
|
||||||
}
|
}
|
||||||
|
|
||||||
return def.arrow.domain.fields;
|
return def.arrow.domain.fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isReturnTypeVoid = (def: FunctionCallDef): boolean => {
|
export const isReturnTypeVoid = (def: FunctionCallDef): boolean => {
|
||||||
if (def.arrow.codomain.tag === 'nil') {
|
if (def.arrow.codomain.tag === "nil") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return def.arrow.codomain.items.length == 0;
|
return def.arrow.codomain.items.length === 0;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,13 +13,27 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { IFluenceInternalApi } from '../fluenceClient.js';
|
|
||||||
import { FnConfig, FunctionCallDef, ServiceDef } from './aquaTypeDefinitions.js';
|
import { JSONValue } from "../commonTypes.js";
|
||||||
|
import { IFluenceInternalApi } from "../fluenceClient.js";
|
||||||
|
|
||||||
|
import {
|
||||||
|
FnConfig,
|
||||||
|
FunctionCallDef,
|
||||||
|
ServiceDef,
|
||||||
|
} from "./aquaTypeDefinitions.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for callback passed as aqua function argument
|
||||||
|
*/
|
||||||
|
export type ArgCallbackFunction = (
|
||||||
|
...args: JSONValue[]
|
||||||
|
) => JSONValue | Promise<JSONValue>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments passed to Aqua function
|
* Arguments passed to Aqua function
|
||||||
*/
|
*/
|
||||||
export type PassedArgs = { [key: string]: any };
|
export type PassedArgs = { [key: string]: JSONValue | ArgCallbackFunction };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments for callAquaFunction function
|
* Arguments for callAquaFunction function
|
||||||
@ -54,7 +68,9 @@ export interface CallAquaFunctionArgs {
|
|||||||
/**
|
/**
|
||||||
* Call a function from Aqua script
|
* Call a function from Aqua script
|
||||||
*/
|
*/
|
||||||
export type CallAquaFunctionType = (args: CallAquaFunctionArgs) => Promise<unknown>;
|
export type CallAquaFunctionType = (
|
||||||
|
args: CallAquaFunctionArgs,
|
||||||
|
) => Promise<unknown>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments for registerService function
|
* Arguments for registerService function
|
||||||
@ -78,7 +94,7 @@ export interface RegisterServiceArgs {
|
|||||||
/**
|
/**
|
||||||
* Service implementation
|
* Service implementation
|
||||||
*/
|
*/
|
||||||
service: any;
|
service: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,7 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { Node } from './commonTypes.js';
|
|
||||||
|
import type { Node } from "./commonTypes.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A node in Fluence network a client can connect to.
|
* A node in Fluence network a client can connect to.
|
||||||
@ -26,14 +27,14 @@ export type RelayOptions = string | Node;
|
|||||||
/**
|
/**
|
||||||
* Fluence Peer's key pair types
|
* Fluence Peer's key pair types
|
||||||
*/
|
*/
|
||||||
export type KeyTypes = 'RSA' | 'Ed25519' | 'secp256k1';
|
export type KeyTypes = "RSA" | "Ed25519" | "secp256k1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options to specify key pair used in Fluence Peer
|
* Options to specify key pair used in Fluence Peer
|
||||||
*/
|
*/
|
||||||
export type KeyPairOptions = {
|
export type KeyPairOptions = {
|
||||||
type: 'Ed25519';
|
type: "Ed25519";
|
||||||
source: 'random' | Uint8Array;
|
source: "random" | Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,7 +98,12 @@ export interface ClientConfig {
|
|||||||
/**
|
/**
|
||||||
* Fluence JS Client connection states as string literals
|
* Fluence JS Client connection states as string literals
|
||||||
*/
|
*/
|
||||||
export const ConnectionStates = ['disconnected', 'connecting', 'connected', 'disconnecting'] as const;
|
export const ConnectionStates = [
|
||||||
|
"disconnected",
|
||||||
|
"connecting",
|
||||||
|
"connected",
|
||||||
|
"disconnecting",
|
||||||
|
] as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fluence JS Client connection states
|
* Fluence JS Client connection states
|
||||||
@ -108,7 +114,7 @@ export interface IFluenceInternalApi {
|
|||||||
/**
|
/**
|
||||||
* Internal API
|
* Internal API
|
||||||
*/
|
*/
|
||||||
internals: any;
|
internals: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,7 +134,9 @@ export interface IFluenceClient extends IFluenceInternalApi {
|
|||||||
/**
|
/**
|
||||||
* Handle connection state changes. Immediately returns current connection state
|
* Handle connection state changes. Immediately returns current connection state
|
||||||
*/
|
*/
|
||||||
onConnectionStateChange(handler: (state: ConnectionState) => void): ConnectionState;
|
onConnectionStateChange(
|
||||||
|
handler: (state: ConnectionState) => void,
|
||||||
|
): ConnectionState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return peer's secret key as byte array.
|
* Return peer's secret key as byte array.
|
||||||
@ -145,7 +153,3 @@ export interface IFluenceClient extends IFluenceInternalApi {
|
|||||||
*/
|
*/
|
||||||
getRelayPeerId(): string;
|
getRelayPeerId(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
90
packages/core/interfaces/src/future.ts
Normal file
90
packages/core/interfaces/src/future.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2023 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
ArrayType,
|
||||||
|
ArrowType,
|
||||||
|
LabeledProductType,
|
||||||
|
NilType,
|
||||||
|
OptionType,
|
||||||
|
ScalarType,
|
||||||
|
SimpleTypes,
|
||||||
|
StructType,
|
||||||
|
TopType,
|
||||||
|
UnlabeledProductType,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
import { Call, Pipe, Objects, Tuples, Unions, Fn } from "hotscript";
|
||||||
|
|
||||||
|
// Type definitions for inferring ts types from air json definition
|
||||||
|
// In the future we may remove string type declaration and move to type inference.
|
||||||
|
|
||||||
|
type GetTsTypeFromScalar<T extends ScalarType> = [T["name"]] extends [
|
||||||
|
"u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64",
|
||||||
|
]
|
||||||
|
? number
|
||||||
|
: [T["name"]] extends ["bool"]
|
||||||
|
? boolean
|
||||||
|
: [T["name"]] extends ["string"]
|
||||||
|
? string
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type MapTuple<T> = {
|
||||||
|
[K in keyof T]: [T[K]] extends [SimpleTypes] ? GetSimpleType<T[K]> : never;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UnpackIfSingle<T> = [T] extends [[infer R]] ? R : T;
|
||||||
|
|
||||||
|
type GetSimpleType<T> = [T] extends [NilType]
|
||||||
|
? null
|
||||||
|
: [T] extends [ArrayType]
|
||||||
|
? GetSimpleType<T["type"]>[]
|
||||||
|
: [T] extends [StructType]
|
||||||
|
? { [K in keyof T["fields"]]: GetSimpleType<T["fields"][K]> }
|
||||||
|
: [T] extends [OptionType]
|
||||||
|
? GetSimpleType<T["type"]> | null
|
||||||
|
: [T] extends [ScalarType]
|
||||||
|
? GetTsTypeFromScalar<T>
|
||||||
|
: [T] extends [TopType]
|
||||||
|
? unknown
|
||||||
|
: never;
|
||||||
|
|
||||||
|
interface Access<T> extends Fn {
|
||||||
|
return: __GetTsType<Call<Objects.Get<this["arg0"]>, T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type __GetTsType<T> = [T] extends [SimpleTypes]
|
||||||
|
? GetSimpleType<T>
|
||||||
|
: [T] extends [UnlabeledProductType]
|
||||||
|
? MapTuple<T["items"]>
|
||||||
|
: [T] extends [LabeledProductType]
|
||||||
|
? { [K in keyof T["fields"]]: __GetTsType<T["fields"][K]> }
|
||||||
|
: [T] extends [ArrowType<infer H>]
|
||||||
|
? (
|
||||||
|
...t: [H] extends [UnlabeledProductType<infer K>]
|
||||||
|
? MapTuple<K>
|
||||||
|
: [H] extends [LabeledProductType<infer _V, infer K>]
|
||||||
|
? Pipe<K, [Objects.Keys, Unions.ToTuple, Tuples.Map<Access<K>>]>
|
||||||
|
: []
|
||||||
|
) => [T["codomain"]] extends [UnlabeledProductType]
|
||||||
|
? UnpackIfSingle<MapTuple<T["codomain"]["items"]>>
|
||||||
|
: undefined
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type DeepMutable<T> = {
|
||||||
|
-readonly [K in keyof T]: DeepMutable<T[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetTsType<T> = __GetTsType<DeepMutable<T>>;
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,7 +13,9 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
export * from './compilerSupport/aquaTypeDefinitions.js';
|
|
||||||
export * from './compilerSupport/compilerSupportInterface.js';
|
export * from "./compilerSupport/aquaTypeDefinitions.js";
|
||||||
export * from './commonTypes.js';
|
export * from "./compilerSupport/compilerSupportInterface.js";
|
||||||
export * from './fluenceClient.js';
|
export * from "./commonTypes.js";
|
||||||
|
export * from "./fluenceClient.js";
|
||||||
|
export * from "./future.js";
|
||||||
|
21
packages/core/interfaces/src/utils.ts
Normal file
21
packages/core/interfaces/src/utils.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2023 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type InterfaceToType<T extends object> = {
|
||||||
|
[K in keyof T]: T[K];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MaybePromise<T> = T | Promise<T>;
|
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../../tsconfig.json",
|
"extends": "../../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist"
|
"outDir": "./dist",
|
||||||
|
"rootDir": "src"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
|
@ -25,271 +25,234 @@
|
|||||||
|
|
||||||
## [0.1.7](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.6...js-client-v0.1.7) (2023-09-22)
|
## [0.1.7](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.6...js-client-v0.1.7) (2023-09-22)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.48.0 ([#350](https://github.com/fluencelabs/js-client/issues/350)) ([945908a](https://github.com/fluencelabs/js-client/commit/945908a992976f2ad953bcaa3918741f890ffeeb))
|
- **deps:** update dependency @fluencelabs/avm to v0.48.0 ([#350](https://github.com/fluencelabs/js-client/issues/350)) ([945908a](https://github.com/fluencelabs/js-client/commit/945908a992976f2ad953bcaa3918741f890ffeeb))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/marine-worker bumped to 0.3.3
|
- @fluencelabs/marine-worker bumped to 0.3.3
|
||||||
|
|
||||||
## [0.1.6](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.5...js-client-v0.1.6) (2023-09-15)
|
## [0.1.6](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.5...js-client-v0.1.6) (2023-09-15)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.47.0 ([#341](https://github.com/fluencelabs/js-client/issues/341)) ([f186f20](https://github.com/fluencelabs/js-client/commit/f186f209366c29f12e6677e03564ee2fa14b51ae))
|
- **deps:** update dependency @fluencelabs/avm to v0.47.0 ([#341](https://github.com/fluencelabs/js-client/issues/341)) ([f186f20](https://github.com/fluencelabs/js-client/commit/f186f209366c29f12e6677e03564ee2fa14b51ae))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/marine-worker bumped to 0.3.2
|
- @fluencelabs/marine-worker bumped to 0.3.2
|
||||||
|
|
||||||
## [0.1.5](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.4...js-client-v0.1.5) (2023-09-14)
|
## [0.1.5](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.4...js-client-v0.1.5) (2023-09-14)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **libp2p:** Add fluence protocol to local peer store protocols [fixes DXJ-471] ([#343](https://github.com/fluencelabs/js-client/issues/343)) ([88fcf02](https://github.com/fluencelabs/js-client/commit/88fcf02d5fd3d28db619427c31b38154646f7ad2))
|
- **libp2p:** Add fluence protocol to local peer store protocols [fixes DXJ-471] ([#343](https://github.com/fluencelabs/js-client/issues/343)) ([88fcf02](https://github.com/fluencelabs/js-client/commit/88fcf02d5fd3d28db619427c31b38154646f7ad2))
|
||||||
|
|
||||||
## [0.1.4](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.3...js-client-v0.1.4) (2023-09-14)
|
## [0.1.4](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.3...js-client-v0.1.4) (2023-09-14)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Fire and forget [fixes DXJ-446] ([#336](https://github.com/fluencelabs/js-client/issues/336)) ([e0a970d](https://github.com/fluencelabs/js-client/commit/e0a970d86a13f1617778a461c1c4d558d7dbafcb))
|
- Fire and forget [fixes DXJ-446] ([#336](https://github.com/fluencelabs/js-client/issues/336)) ([e0a970d](https://github.com/fluencelabs/js-client/commit/e0a970d86a13f1617778a461c1c4d558d7dbafcb))
|
||||||
|
|
||||||
## [0.1.3](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.2...js-client-v0.1.3) (2023-09-07)
|
## [0.1.3](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.2...js-client-v0.1.3) (2023-09-07)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.46.0 ([#338](https://github.com/fluencelabs/js-client/issues/338)) ([8e6918c](https://github.com/fluencelabs/js-client/commit/8e6918c4da5bc4cdfe1c840312f477d782d9ca20))
|
- **deps:** update dependency @fluencelabs/avm to v0.46.0 ([#338](https://github.com/fluencelabs/js-client/issues/338)) ([8e6918c](https://github.com/fluencelabs/js-client/commit/8e6918c4da5bc4cdfe1c840312f477d782d9ca20))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/marine-worker bumped to 0.3.1
|
- @fluencelabs/marine-worker bumped to 0.3.1
|
||||||
|
|
||||||
## [0.1.2](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.1...js-client-v0.1.2) (2023-09-05)
|
## [0.1.2](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.1...js-client-v0.1.2) (2023-09-05)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* remove obsolete packages [fixes DXJ-462] ([#337](https://github.com/fluencelabs/js-client/issues/337)) ([e7e6176](https://github.com/fluencelabs/js-client/commit/e7e617661f39e1df36a703d5dad93ba52a338919))
|
- remove obsolete packages [fixes DXJ-462] ([#337](https://github.com/fluencelabs/js-client/issues/337)) ([e7e6176](https://github.com/fluencelabs/js-client/commit/e7e617661f39e1df36a703d5dad93ba52a338919))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **logger:** Change formatter that collides with new libp2p version [fixes DXJ-459] ([#334](https://github.com/fluencelabs/js-client/issues/334)) ([18a972b](https://github.com/fluencelabs/js-client/commit/18a972b573559d0717ec93a95b8c63dd1cbcd93b))
|
- **logger:** Change formatter that collides with new libp2p version [fixes DXJ-459] ([#334](https://github.com/fluencelabs/js-client/issues/334)) ([18a972b](https://github.com/fluencelabs/js-client/commit/18a972b573559d0717ec93a95b8c63dd1cbcd93b))
|
||||||
|
|
||||||
## [0.1.1](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.0...js-client-v0.1.1) (2023-08-25)
|
## [0.1.1](https://github.com/fluencelabs/js-client/compare/js-client-v0.1.0...js-client-v0.1.1) (2023-08-25)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Use info log level instead trace [Fixes DXJ-457] ([#328](https://github.com/fluencelabs/js-client/issues/328)) ([477c6f0](https://github.com/fluencelabs/js-client/commit/477c6f0c151ef6759aaa2802c5e9907065d58e17))
|
- Use info log level instead trace [Fixes DXJ-457] ([#328](https://github.com/fluencelabs/js-client/issues/328)) ([477c6f0](https://github.com/fluencelabs/js-client/commit/477c6f0c151ef6759aaa2802c5e9907065d58e17))
|
||||||
|
|
||||||
## [0.1.0](https://github.com/fluencelabs/js-client/compare/js-client-v0.0.10...js-client-v0.1.0) (2023-08-24)
|
## [0.1.0](https://github.com/fluencelabs/js-client/compare/js-client-v0.0.10...js-client-v0.1.0) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* Unify all packages ([#327](https://github.com/fluencelabs/js-client/issues/327))
|
- Unify all packages ([#327](https://github.com/fluencelabs/js-client/issues/327))
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Unify all packages ([#327](https://github.com/fluencelabs/js-client/issues/327)) ([97c2491](https://github.com/fluencelabs/js-client/commit/97c24918d84b34e7ac58337838dc8343cbd44b19))
|
- Unify all packages ([#327](https://github.com/fluencelabs/js-client/issues/327)) ([97c2491](https://github.com/fluencelabs/js-client/commit/97c24918d84b34e7ac58337838dc8343cbd44b19))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.8.1 to 0.8.2
|
- @fluencelabs/interfaces bumped from 0.8.1 to 0.8.2
|
||||||
* devDependencies
|
- devDependencies
|
||||||
* @fluencelabs/marine-worker bumped to 0.3.0
|
- @fluencelabs/marine-worker bumped to 0.3.0
|
||||||
|
|
||||||
## [0.9.1](https://github.com/fluencelabs/js-client/compare/js-peer-v0.9.0...js-peer-v0.9.1) (2023-08-08)
|
## [0.9.1](https://github.com/fluencelabs/js-client/compare/js-peer-v0.9.0...js-peer-v0.9.1) (2023-08-08)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **deps:** update dependency @fluencelabs/avm to v0.43.1 ([#322](https://github.com/fluencelabs/js-client/issues/322)) ([c1d1fa6](https://github.com/fluencelabs/js-client/commit/c1d1fa6659b6dc2c6707786748b3410fab7f1bcd))
|
- **deps:** update dependency @fluencelabs/avm to v0.43.1 ([#322](https://github.com/fluencelabs/js-client/issues/322)) ([c1d1fa6](https://github.com/fluencelabs/js-client/commit/c1d1fa6659b6dc2c6707786748b3410fab7f1bcd))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.8.0 to 0.8.1
|
- @fluencelabs/interfaces bumped from 0.8.0 to 0.8.1
|
||||||
|
|
||||||
## [0.9.0](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.10...js-peer-v0.9.0) (2023-06-29)
|
## [0.9.0](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.10...js-peer-v0.9.0) (2023-06-29)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315)
|
- **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315) ([8bae6e2](https://github.com/fluencelabs/js-client/commit/8bae6e24e62153b567f320ccecc7bce76bc826d1))
|
- **avm:** avm 0.40.0 (https://github.com/fluencelabs/js-client/pull/315) ([8bae6e2](https://github.com/fluencelabs/js-client/commit/8bae6e24e62153b567f320ccecc7bce76bc826d1))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.6 to 0.8.0
|
- @fluencelabs/interfaces bumped from 0.7.6 to 0.8.0
|
||||||
|
|
||||||
## [0.8.10](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.9...js-peer-v0.8.10) (2023-06-20)
|
## [0.8.10](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.9...js-peer-v0.8.10) (2023-06-20)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* support signatures [fixes DXJ-389] ([#310](https://github.com/fluencelabs/js-client/issues/310)) ([a60dfe0](https://github.com/fluencelabs/js-client/commit/a60dfe0d680b4d9ac5092dec64e2ebf478bf80eb))
|
- support signatures [fixes DXJ-389] ([#310](https://github.com/fluencelabs/js-client/issues/310)) ([a60dfe0](https://github.com/fluencelabs/js-client/commit/a60dfe0d680b4d9ac5092dec64e2ebf478bf80eb))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.5 to 0.7.6
|
- @fluencelabs/interfaces bumped from 0.7.5 to 0.7.6
|
||||||
|
|
||||||
## [0.8.9](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.8...js-peer-v0.8.9) (2023-06-14)
|
## [0.8.9](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.8...js-peer-v0.8.9) (2023-06-14)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add tracing service [fixes DXJ-388] ([#307](https://github.com/fluencelabs/js-client/issues/307)) ([771086f](https://github.com/fluencelabs/js-client/commit/771086fddf52b7a5a1280894c7238e409cdf6a64))
|
- Add tracing service [fixes DXJ-388] ([#307](https://github.com/fluencelabs/js-client/issues/307)) ([771086f](https://github.com/fluencelabs/js-client/commit/771086fddf52b7a5a1280894c7238e409cdf6a64))
|
||||||
* improve ttl error message ([#300](https://github.com/fluencelabs/js-client/issues/300)) ([9821183](https://github.com/fluencelabs/js-client/commit/9821183d53870240cb5700be67cb8d57533b954b))
|
- improve ttl error message ([#300](https://github.com/fluencelabs/js-client/issues/300)) ([9821183](https://github.com/fluencelabs/js-client/commit/9821183d53870240cb5700be67cb8d57533b954b))
|
||||||
|
|
||||||
## [0.8.8](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.7...js-peer-v0.8.8) (2023-05-30)
|
## [0.8.8](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.7...js-peer-v0.8.8) (2023-05-30)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add run-console ([#305](https://github.com/fluencelabs/js-client/issues/305)) ([cf1f029](https://github.com/fluencelabs/js-client/commit/cf1f02963c1d7e1a17866f5798901a0f61b8bc31))
|
- add run-console ([#305](https://github.com/fluencelabs/js-client/issues/305)) ([cf1f029](https://github.com/fluencelabs/js-client/commit/cf1f02963c1d7e1a17866f5798901a0f61b8bc31))
|
||||||
|
|
||||||
## [0.8.7](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.6...js-peer-v0.8.7) (2023-04-04)
|
## [0.8.7](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.6...js-peer-v0.8.7) (2023-04-04)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Cleaning up technical debts ([#295](https://github.com/fluencelabs/js-client/issues/295)) ([0b2f12d](https://github.com/fluencelabs/js-client/commit/0b2f12d8ac223db341d6c30ff403166b3eae2e56))
|
- Cleaning up technical debts ([#295](https://github.com/fluencelabs/js-client/issues/295)) ([0b2f12d](https://github.com/fluencelabs/js-client/commit/0b2f12d8ac223db341d6c30ff403166b3eae2e56))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.4 to 0.7.5
|
- @fluencelabs/interfaces bumped from 0.7.4 to 0.7.5
|
||||||
|
|
||||||
## [0.8.6](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.5...js-peer-v0.8.6) (2023-03-31)
|
## [0.8.6](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.5...js-peer-v0.8.6) (2023-03-31)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **logs:** Use `debug.js` library for logging [DXJ-327] ([#285](https://github.com/fluencelabs/js-client/issues/285)) ([e95c34a](https://github.com/fluencelabs/js-client/commit/e95c34a79220bd8ecdcee806802ac3d69a2af0cb))
|
- **logs:** Use `debug.js` library for logging [DXJ-327] ([#285](https://github.com/fluencelabs/js-client/issues/285)) ([e95c34a](https://github.com/fluencelabs/js-client/commit/e95c34a79220bd8ecdcee806802ac3d69a2af0cb))
|
||||||
* **test:** Automate smoke tests for JS Client [DXJ-293] ([#282](https://github.com/fluencelabs/js-client/issues/282)) ([10d7eae](https://github.com/fluencelabs/js-client/commit/10d7eaed809dde721b582d4b3228a48bbec50884))
|
- **test:** Automate smoke tests for JS Client [DXJ-293] ([#282](https://github.com/fluencelabs/js-client/issues/282)) ([10d7eae](https://github.com/fluencelabs/js-client/commit/10d7eaed809dde721b582d4b3228a48bbec50884))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **test:** All tests are working with vitest [DXJ-306] ([#291](https://github.com/fluencelabs/js-client/issues/291)) ([58ad3ca](https://github.com/fluencelabs/js-client/commit/58ad3ca6f666e8580997bb47609947645903436d))
|
- **test:** All tests are working with vitest [DXJ-306] ([#291](https://github.com/fluencelabs/js-client/issues/291)) ([58ad3ca](https://github.com/fluencelabs/js-client/commit/58ad3ca6f666e8580997bb47609947645903436d))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.3 to 0.7.4
|
- @fluencelabs/interfaces bumped from 0.7.3 to 0.7.4
|
||||||
|
|
||||||
## [0.8.5](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.4...js-peer-v0.8.5) (2023-03-03)
|
## [0.8.5](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.4...js-peer-v0.8.5) (2023-03-03)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Increase number of inbound and outbound streams to 1024 ([#280](https://github.com/fluencelabs/js-client/issues/280)) ([1ccc483](https://github.com/fluencelabs/js-client/commit/1ccc4835328426b546f31e1646d3a49ed042fdf9))
|
- Increase number of inbound and outbound streams to 1024 ([#280](https://github.com/fluencelabs/js-client/issues/280)) ([1ccc483](https://github.com/fluencelabs/js-client/commit/1ccc4835328426b546f31e1646d3a49ed042fdf9))
|
||||||
|
|
||||||
## [0.8.4](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.3...js-peer-v0.8.4) (2023-02-22)
|
## [0.8.4](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.3...js-peer-v0.8.4) (2023-02-22)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* `nodenext` moduleResolution for js peer ([#271](https://github.com/fluencelabs/js-client/issues/271)) ([78d98f1](https://github.com/fluencelabs/js-client/commit/78d98f15c12431dee9fdd7b9869d57760503f8c7))
|
- `nodenext` moduleResolution for js peer ([#271](https://github.com/fluencelabs/js-client/issues/271)) ([78d98f1](https://github.com/fluencelabs/js-client/commit/78d98f15c12431dee9fdd7b9869d57760503f8c7))
|
||||||
|
|
||||||
## [0.8.3](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.2...js-peer-v0.8.3) (2023-02-16)
|
## [0.8.3](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.2...js-peer-v0.8.3) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Trigger release to publish packages that were built ([#262](https://github.com/fluencelabs/js-client/issues/262)) ([47abf38](https://github.com/fluencelabs/js-client/commit/47abf3882956ffbdc52df372db26ba6252e8306b))
|
- Trigger release to publish packages that were built ([#262](https://github.com/fluencelabs/js-client/issues/262)) ([47abf38](https://github.com/fluencelabs/js-client/commit/47abf3882956ffbdc52df372db26ba6252e8306b))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.2 to 0.7.3
|
- @fluencelabs/interfaces bumped from 0.7.2 to 0.7.3
|
||||||
|
|
||||||
## [0.8.2](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.1...js-peer-v0.8.2) (2023-02-16)
|
## [0.8.2](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.1...js-peer-v0.8.2) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add `getRelayPeerId` method for `IFluenceClient` ([#260](https://github.com/fluencelabs/js-client/issues/260)) ([a10278a](https://github.com/fluencelabs/js-client/commit/a10278afaa782a307feb10c4eac060094c101230))
|
- Add `getRelayPeerId` method for `IFluenceClient` ([#260](https://github.com/fluencelabs/js-client/issues/260)) ([a10278a](https://github.com/fluencelabs/js-client/commit/a10278afaa782a307feb10c4eac060094c101230))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.1 to 0.7.2
|
- @fluencelabs/interfaces bumped from 0.7.1 to 0.7.2
|
||||||
|
|
||||||
## [0.8.1](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.0...js-peer-v0.8.1) (2023-02-16)
|
## [0.8.1](https://github.com/fluencelabs/js-client/compare/js-peer-v0.8.0...js-peer-v0.8.1) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Simplify JS Client public API ([#257](https://github.com/fluencelabs/js-client/issues/257)) ([9daaf41](https://github.com/fluencelabs/js-client/commit/9daaf410964d43228192c829c7ff785db6e88081))
|
- Simplify JS Client public API ([#257](https://github.com/fluencelabs/js-client/issues/257)) ([9daaf41](https://github.com/fluencelabs/js-client/commit/9daaf410964d43228192c829c7ff785db6e88081))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.7.0 to 0.7.1
|
- @fluencelabs/interfaces bumped from 0.7.0 to 0.7.1
|
||||||
|
|
||||||
## [0.8.0](https://github.com/fluencelabs/fluence-js/compare/js-peer-v0.7.0...js-peer-v0.8.0) (2023-02-15)
|
## [0.8.0](https://github.com/fluencelabs/fluence-js/compare/js-peer-v0.7.0...js-peer-v0.8.0) (2023-02-15)
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
* Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246))
|
- Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246))
|
||||||
* Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243))
|
- Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243))
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246)) ([d4bb8fb](https://github.com/fluencelabs/fluence-js/commit/d4bb8fb42964b3ba25154232980b9ae82c21e627))
|
- Expose updated JS Client API via `js-client.api` package ([#246](https://github.com/fluencelabs/fluence-js/issues/246)) ([d4bb8fb](https://github.com/fluencelabs/fluence-js/commit/d4bb8fb42964b3ba25154232980b9ae82c21e627))
|
||||||
* Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243)) ([9667c4f](https://github.com/fluencelabs/fluence-js/commit/9667c4fec6868f984bba13249f3c47d293396406))
|
- Standalone web JS Client ([#243](https://github.com/fluencelabs/fluence-js/issues/243)) ([9667c4f](https://github.com/fluencelabs/fluence-js/commit/9667c4fec6868f984bba13249f3c47d293396406))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* NodeJS package building ([#248](https://github.com/fluencelabs/fluence-js/issues/248)) ([0d05e51](https://github.com/fluencelabs/fluence-js/commit/0d05e517d89529af513fcb96cfa6c722ccc357a7))
|
- NodeJS package building ([#248](https://github.com/fluencelabs/fluence-js/issues/248)) ([0d05e51](https://github.com/fluencelabs/fluence-js/commit/0d05e517d89529af513fcb96cfa6c722ccc357a7))
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* The following workspace dependencies were updated
|
- The following workspace dependencies were updated
|
||||||
* dependencies
|
- dependencies
|
||||||
* @fluencelabs/interfaces bumped from 0.6.0 to 0.7.0
|
- @fluencelabs/interfaces bumped from 0.6.0 to 0.7.0
|
||||||
|
@ -17,6 +17,12 @@
|
|||||||
"node": "./dist/index.js",
|
"node": "./dist/index.js",
|
||||||
"default": "./dist/browser/index.js"
|
"default": "./dist/browser/index.js"
|
||||||
},
|
},
|
||||||
|
"imports": {
|
||||||
|
"#fetcher": {
|
||||||
|
"node": "./dist/fetchers/node.js",
|
||||||
|
"default": "./dist/fetchers/browser.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
@ -28,6 +34,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chainsafe/libp2p-noise": "13.0.0",
|
"@chainsafe/libp2p-noise": "13.0.0",
|
||||||
"@chainsafe/libp2p-yamux": "5.0.0",
|
"@chainsafe/libp2p-yamux": "5.0.0",
|
||||||
|
"@fluencelabs/avm": "0.48.0",
|
||||||
"@fluencelabs/interfaces": "workspace:*",
|
"@fluencelabs/interfaces": "workspace:*",
|
||||||
"@fluencelabs/marine-worker": "0.3.3",
|
"@fluencelabs/marine-worker": "0.3.3",
|
||||||
"@libp2p/crypto": "2.0.3",
|
"@libp2p/crypto": "2.0.3",
|
||||||
@ -36,6 +43,7 @@
|
|||||||
"@libp2p/peer-id-factory": "3.0.3",
|
"@libp2p/peer-id-factory": "3.0.3",
|
||||||
"@libp2p/websockets": "7.0.4",
|
"@libp2p/websockets": "7.0.4",
|
||||||
"@multiformats/multiaddr": "11.3.0",
|
"@multiformats/multiaddr": "11.3.0",
|
||||||
|
"assert": "2.1.0",
|
||||||
"async": "3.2.4",
|
"async": "3.2.4",
|
||||||
"bs58": "5.0.0",
|
"bs58": "5.0.0",
|
||||||
"buffer": "6.0.3",
|
"buffer": "6.0.3",
|
||||||
@ -47,22 +55,23 @@
|
|||||||
"libp2p": "0.46.6",
|
"libp2p": "0.46.6",
|
||||||
"multiformats": "11.0.1",
|
"multiformats": "11.0.1",
|
||||||
"rxjs": "7.5.5",
|
"rxjs": "7.5.5",
|
||||||
"threads": "1.7.0",
|
"threads": "fluencelabs/threads.js#b00a5342380b0278d3ae56dcfb170effb3cad7cd",
|
||||||
"ts-pattern": "3.3.3",
|
"ts-pattern": "3.3.3",
|
||||||
"uint8arrays": "4.0.3",
|
"uint8arrays": "4.0.3",
|
||||||
"uuid": "8.3.2"
|
"uuid": "8.3.2",
|
||||||
|
"zod": "3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fluencelabs/aqua-api": "0.9.3",
|
"@fluencelabs/aqua-api": "0.9.3",
|
||||||
"@fluencelabs/avm": "0.48.0",
|
|
||||||
"@fluencelabs/marine-js": "0.7.2",
|
"@fluencelabs/marine-js": "0.7.2",
|
||||||
"@rollup/plugin-inject": "5.0.3",
|
"@rollup/plugin-inject": "5.0.3",
|
||||||
"@types/bs58": "4.0.1",
|
"@types/bs58": "4.0.1",
|
||||||
"@types/debug": "4.1.7",
|
"@types/debug": "4.1.7",
|
||||||
"@types/node": "20.7.0",
|
"@types/node": "20.7.0",
|
||||||
"@types/uuid": "8.3.2",
|
"@types/uuid": "8.3.2",
|
||||||
"vite": "4.0.4",
|
"hotscript": "1.0.13",
|
||||||
|
"vite": "4.4.11",
|
||||||
"vite-tsconfig-paths": "4.0.3",
|
"vite-tsconfig-paths": "4.0.3",
|
||||||
"vitest": "0.29.7"
|
"vitest": "0.34.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,13 +14,22 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { FnConfig, FunctionCallDef, ServiceDef } from '@fluencelabs/interfaces';
|
import type {
|
||||||
import type { IFluenceClient } from '@fluencelabs/interfaces';
|
FnConfig,
|
||||||
import { getArgumentTypes } from '@fluencelabs/interfaces';
|
FunctionCallDef,
|
||||||
import { callAquaFunction, Fluence, registerService } from './index.js';
|
ServiceDef,
|
||||||
import { FluencePeer } from './jsPeer/FluencePeer.js';
|
PassedArgs,
|
||||||
|
ServiceImpl,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
import { getArgumentTypes } from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
export const isFluencePeer = (fluencePeerCandidate: unknown): fluencePeerCandidate is IFluenceClient => {
|
import { FluencePeer } from "./jsPeer/FluencePeer.js";
|
||||||
|
|
||||||
|
import { callAquaFunction, Fluence, registerService } from "./index.js";
|
||||||
|
|
||||||
|
export const isFluencePeer = (
|
||||||
|
fluencePeerCandidate: unknown,
|
||||||
|
): fluencePeerCandidate is FluencePeer => {
|
||||||
return fluencePeerCandidate instanceof FluencePeer;
|
return fluencePeerCandidate instanceof FluencePeer;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,18 +42,18 @@ export const isFluencePeer = (fluencePeerCandidate: unknown): fluencePeerCandida
|
|||||||
* @param script - air script with function execution logic generated by the Aqua compiler
|
* @param script - air script with function execution logic generated by the Aqua compiler
|
||||||
*/
|
*/
|
||||||
export const v5_callFunction = async (
|
export const v5_callFunction = async (
|
||||||
rawFnArgs: Array<any>,
|
rawFnArgs: unknown[],
|
||||||
def: FunctionCallDef,
|
def: FunctionCallDef,
|
||||||
script: string,
|
script: string,
|
||||||
): Promise<unknown> => {
|
): Promise<unknown> => {
|
||||||
const { args, client: peer, config } = await extractFunctionArgs(rawFnArgs, def);
|
const { args, client: peer, config } = extractFunctionArgs(rawFnArgs, def);
|
||||||
|
|
||||||
return callAquaFunction({
|
return callAquaFunction({
|
||||||
args,
|
args,
|
||||||
def,
|
def,
|
||||||
script,
|
script,
|
||||||
config: config || {},
|
config,
|
||||||
peer: peer,
|
peer,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,12 +61,17 @@ export const v5_callFunction = async (
|
|||||||
* Convenience function to support Aqua `service` generation backend
|
* Convenience function to support Aqua `service` generation backend
|
||||||
* The compiler only need to generate a call the function and provide the corresponding definitions and the air script
|
* The compiler only need to generate a call the function and provide the corresponding definitions and the air script
|
||||||
* @param args - raw arguments passed by user to the generated function
|
* @param args - raw arguments passed by user to the generated function
|
||||||
|
* TODO: dont forget to add jsdoc for new arg
|
||||||
* @param def - service definition generated by the Aqua compiler
|
* @param def - service definition generated by the Aqua compiler
|
||||||
*/
|
*/
|
||||||
export const v5_registerService = async (args: any[], def: ServiceDef): Promise<unknown> => {
|
export const v5_registerService = (args: unknown[], def: ServiceDef): void => {
|
||||||
const { peer, service, serviceId } = await extractServiceArgs(args, def.defaultServiceId);
|
// TODO: Support this in aqua-to-js package
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
const service: ServiceImpl = args.pop() as ServiceImpl;
|
||||||
|
|
||||||
return registerService({
|
const { peer, serviceId } = extractServiceArgs(args, def.defaultServiceId);
|
||||||
|
|
||||||
|
registerService({
|
||||||
def,
|
def,
|
||||||
service,
|
service,
|
||||||
serviceId,
|
serviceId,
|
||||||
@ -65,6 +79,10 @@ export const v5_registerService = async (args: any[], def: ServiceDef): Promise<
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isConfig(arg: unknown): arg is FnConfig {
|
||||||
|
return typeof arg === "object" && arg !== null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments could be passed in one these configurations:
|
* Arguments could be passed in one these configurations:
|
||||||
* [...actualArgs]
|
* [...actualArgs]
|
||||||
@ -75,48 +93,60 @@ export const v5_registerService = async (args: any[], def: ServiceDef): Promise<
|
|||||||
* This function select the appropriate configuration and returns
|
* This function select the appropriate configuration and returns
|
||||||
* arguments in a structured way of: { peer, config, args }
|
* arguments in a structured way of: { peer, config, args }
|
||||||
*/
|
*/
|
||||||
const extractFunctionArgs = async (
|
function extractFunctionArgs(
|
||||||
args: any[],
|
args: unknown[],
|
||||||
def: FunctionCallDef,
|
def: FunctionCallDef,
|
||||||
): Promise<{
|
): {
|
||||||
client: IFluenceClient;
|
client: FluencePeer;
|
||||||
config?: FnConfig;
|
config: FnConfig;
|
||||||
args: { [key: string]: any };
|
args: PassedArgs;
|
||||||
}> => {
|
} {
|
||||||
const argumentTypes = getArgumentTypes(def);
|
const argumentTypes = getArgumentTypes(def);
|
||||||
const argumentNames = Object.keys(argumentTypes);
|
const argumentNames = Object.keys(argumentTypes);
|
||||||
const numberOfExpectedArgs = argumentNames.length;
|
const numberOfExpectedArgs = argumentNames.length;
|
||||||
|
|
||||||
let peer: IFluenceClient;
|
let peer: FluencePeer;
|
||||||
let structuredArgs: any[];
|
|
||||||
let config: FnConfig;
|
let config: FnConfig;
|
||||||
|
|
||||||
if (isFluencePeer(args[0])) {
|
if (isFluencePeer(args[0])) {
|
||||||
peer = args[0];
|
peer = args[0];
|
||||||
structuredArgs = args.slice(1, numberOfExpectedArgs + 1);
|
args = args.slice(1);
|
||||||
config = args[numberOfExpectedArgs + 1];
|
|
||||||
} else {
|
} else {
|
||||||
if (!Fluence.defaultClient) {
|
if (Fluence.defaultClient == null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?',
|
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
peer = Fluence.defaultClient;
|
peer = Fluence.defaultClient;
|
||||||
structuredArgs = args.slice(0, numberOfExpectedArgs);
|
|
||||||
config = args[numberOfExpectedArgs];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maybeConfig = args[numberOfExpectedArgs];
|
||||||
|
|
||||||
|
if (isConfig(maybeConfig)) {
|
||||||
|
config = maybeConfig;
|
||||||
|
} else {
|
||||||
|
config = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const structuredArgs = args.slice(0, numberOfExpectedArgs);
|
||||||
|
|
||||||
if (structuredArgs.length !== numberOfExpectedArgs) {
|
if (structuredArgs.length !== numberOfExpectedArgs) {
|
||||||
throw new Error(`Incorrect number of arguments. Expecting ${numberOfExpectedArgs}`);
|
throw new Error(
|
||||||
|
`Incorrect number of arguments. Expecting ${numberOfExpectedArgs}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const argsRes = argumentNames.reduce((acc, name, index) => ({ ...acc, [name]: structuredArgs[index] }), {});
|
const argsRes = argumentNames.reduce((acc, name, index) => {
|
||||||
|
return { ...acc, [name]: structuredArgs[index] };
|
||||||
|
}, {});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
client: peer,
|
client: peer,
|
||||||
config: config,
|
|
||||||
args: argsRes,
|
args: argsRes,
|
||||||
|
config: config,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments could be passed in one these configurations:
|
* Arguments could be passed in one these configurations:
|
||||||
@ -130,48 +160,37 @@ const extractFunctionArgs = async (
|
|||||||
* This function select the appropriate configuration and returns
|
* This function select the appropriate configuration and returns
|
||||||
* arguments in a structured way of: { peer, serviceId, service }
|
* arguments in a structured way of: { peer, serviceId, service }
|
||||||
*/
|
*/
|
||||||
const extractServiceArgs = async (
|
const extractServiceArgs = (
|
||||||
args: any[],
|
args: unknown[],
|
||||||
defaultServiceId?: string,
|
defaultServiceId?: string,
|
||||||
): Promise<{ peer: IFluenceClient; serviceId: string; service: any }> => {
|
): {
|
||||||
let peer: IFluenceClient;
|
peer: FluencePeer;
|
||||||
let serviceId: any;
|
serviceId: string | undefined;
|
||||||
let service: any;
|
} => {
|
||||||
|
let peer: FluencePeer;
|
||||||
|
let serviceId: string | undefined;
|
||||||
|
|
||||||
if (isFluencePeer(args[0])) {
|
if (isFluencePeer(args[0])) {
|
||||||
peer = args[0];
|
peer = args[0];
|
||||||
|
args = args.slice(1);
|
||||||
} else {
|
} else {
|
||||||
if (!Fluence.defaultClient) {
|
if (Fluence.defaultClient == null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?',
|
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
peer = Fluence.defaultClient;
|
peer = Fluence.defaultClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof args[0] === 'string') {
|
if (typeof args[0] === "string") {
|
||||||
serviceId = args[0];
|
serviceId = args[0];
|
||||||
} else if (typeof args[1] === 'string') {
|
|
||||||
serviceId = args[1];
|
|
||||||
} else {
|
} else {
|
||||||
serviceId = defaultServiceId;
|
serviceId = defaultServiceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figuring out which overload is the service.
|
|
||||||
// If the first argument is not Fluence Peer and it is an object, then it can only be the service def
|
|
||||||
// If the first argument is peer, we are checking further. The second argument might either be
|
|
||||||
// an object, that it must be the service object
|
|
||||||
// or a string, which is the service id. In that case the service is the third argument
|
|
||||||
if (!isFluencePeer(args[0]) && typeof args[0] === 'object') {
|
|
||||||
service = args[0];
|
|
||||||
} else if (typeof args[1] === 'object') {
|
|
||||||
service = args[1];
|
|
||||||
} else {
|
|
||||||
service = args[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peer: peer,
|
peer,
|
||||||
serviceId: serviceId,
|
serviceId,
|
||||||
service: service,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,16 +13,26 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { ClientConfig, ConnectionState, IFluenceClient, PeerIdB58, RelayOptions } from '@fluencelabs/interfaces';
|
|
||||||
import { RelayConnection, RelayConnectionConfig } from '../connection/RelayConnection.js';
|
|
||||||
import { fromOpts, KeyPair } from '../keypair/index.js';
|
|
||||||
import { FluencePeer, PeerConfig } from '../jsPeer/FluencePeer.js';
|
|
||||||
import { relayOptionToMultiaddr } from '../util/libp2pUtils.js';
|
|
||||||
import { IAvmRunner, IMarineHost } from '../marine/interfaces.js';
|
|
||||||
import { JsServiceHost } from '../jsServiceHost/JsServiceHost.js';
|
|
||||||
import { logger } from '../util/logger.js';
|
|
||||||
|
|
||||||
const log = logger('client');
|
import {
|
||||||
|
ClientConfig,
|
||||||
|
ConnectionState,
|
||||||
|
IFluenceClient,
|
||||||
|
RelayOptions,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
|
import {
|
||||||
|
RelayConnection,
|
||||||
|
RelayConnectionConfig,
|
||||||
|
} from "../connection/RelayConnection.js";
|
||||||
|
import { FluencePeer, PeerConfig } from "../jsPeer/FluencePeer.js";
|
||||||
|
import { JsServiceHost } from "../jsServiceHost/JsServiceHost.js";
|
||||||
|
import { fromOpts, KeyPair } from "../keypair/index.js";
|
||||||
|
import { IMarineHost } from "../marine/interfaces.js";
|
||||||
|
import { relayOptionToMultiaddr } from "../util/libp2pUtils.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
|
const log = logger("client");
|
||||||
|
|
||||||
const DEFAULT_TTL_MS = 7000;
|
const DEFAULT_TTL_MS = 7000;
|
||||||
const MAX_OUTBOUND_STREAMS = 1024;
|
const MAX_OUTBOUND_STREAMS = 1024;
|
||||||
@ -31,24 +41,34 @@ const MAX_INBOUND_STREAMS = 1024;
|
|||||||
export const makeClientPeerConfig = async (
|
export const makeClientPeerConfig = async (
|
||||||
relay: RelayOptions,
|
relay: RelayOptions,
|
||||||
config: ClientConfig,
|
config: ClientConfig,
|
||||||
): Promise<{ peerConfig: PeerConfig; relayConfig: RelayConnectionConfig; keyPair: KeyPair }> => {
|
): Promise<{
|
||||||
const opts = config?.keyPair || { type: 'Ed25519', source: 'random' };
|
peerConfig: PeerConfig;
|
||||||
|
relayConfig: RelayConnectionConfig;
|
||||||
|
keyPair: KeyPair;
|
||||||
|
}> => {
|
||||||
|
const opts = config.keyPair ?? { type: "Ed25519", source: "random" };
|
||||||
const keyPair = await fromOpts(opts);
|
const keyPair = await fromOpts(opts);
|
||||||
const relayAddress = relayOptionToMultiaddr(relay);
|
const relayAddress = relayOptionToMultiaddr(relay);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peerConfig: {
|
peerConfig: {
|
||||||
debug: {
|
debug: {
|
||||||
printParticleId: config?.debug?.printParticleId || false,
|
printParticleId: config.debug?.printParticleId ?? false,
|
||||||
},
|
},
|
||||||
defaultTtlMs: config?.defaultTtlMs || DEFAULT_TTL_MS,
|
defaultTtlMs: config.defaultTtlMs ?? DEFAULT_TTL_MS,
|
||||||
},
|
},
|
||||||
relayConfig: {
|
relayConfig: {
|
||||||
peerId: keyPair.getLibp2pPeerId(),
|
peerId: keyPair.getLibp2pPeerId(),
|
||||||
relayAddress: relayAddress,
|
relayAddress: relayAddress,
|
||||||
dialTimeoutMs: config?.connectionOptions?.dialTimeoutMs,
|
...(config.connectionOptions?.dialTimeoutMs != null
|
||||||
maxInboundStreams: config?.connectionOptions?.maxInboundStreams || MAX_OUTBOUND_STREAMS,
|
? {
|
||||||
maxOutboundStreams: config?.connectionOptions?.maxOutboundStreams || MAX_INBOUND_STREAMS,
|
dialTimeout: config.connectionOptions.dialTimeoutMs,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
maxInboundStreams:
|
||||||
|
config.connectionOptions?.maxInboundStreams ?? MAX_OUTBOUND_STREAMS,
|
||||||
|
maxOutboundStreams:
|
||||||
|
config.connectionOptions?.maxOutboundStreams ?? MAX_INBOUND_STREAMS,
|
||||||
},
|
},
|
||||||
keyPair: keyPair,
|
keyPair: keyPair,
|
||||||
};
|
};
|
||||||
@ -61,7 +81,13 @@ export class ClientPeer extends FluencePeer implements IFluenceClient {
|
|||||||
keyPair: KeyPair,
|
keyPair: KeyPair,
|
||||||
marine: IMarineHost,
|
marine: IMarineHost,
|
||||||
) {
|
) {
|
||||||
super(peerConfig, keyPair, marine, new JsServiceHost(), new RelayConnection(relayConfig));
|
super(
|
||||||
|
peerConfig,
|
||||||
|
keyPair,
|
||||||
|
marine,
|
||||||
|
new JsServiceHost(),
|
||||||
|
new RelayConnection(relayConfig),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPeerId(): string {
|
getPeerId(): string {
|
||||||
@ -72,14 +98,16 @@ export class ClientPeer extends FluencePeer implements IFluenceClient {
|
|||||||
return this.keyPair.toEd25519PrivateKey();
|
return this.keyPair.toEd25519PrivateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
connectionState: ConnectionState = 'disconnected';
|
connectionState: ConnectionState = "disconnected";
|
||||||
connectionStateChangeHandler: (state: ConnectionState) => void = () => {};
|
connectionStateChangeHandler: (state: ConnectionState) => void = () => {};
|
||||||
|
|
||||||
getRelayPeerId(): string {
|
getRelayPeerId(): string {
|
||||||
return this.internals.getRelayPeerId();
|
return this.internals.getRelayPeerId();
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnectionStateChange(handler: (state: ConnectionState) => void): ConnectionState {
|
onConnectionStateChange(
|
||||||
|
handler: (state: ConnectionState) => void,
|
||||||
|
): ConnectionState {
|
||||||
this.connectionStateChangeHandler = handler;
|
this.connectionStateChangeHandler = handler;
|
||||||
|
|
||||||
return this.connectionState;
|
return this.connectionState;
|
||||||
@ -104,20 +132,20 @@ export class ClientPeer extends FluencePeer implements IFluenceClient {
|
|||||||
return this.stop();
|
return this.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
override async start(): Promise<void> {
|
||||||
log.trace('connecting to Fluence network');
|
log.trace("connecting to Fluence network");
|
||||||
this.changeConnectionState('connecting');
|
this.changeConnectionState("connecting");
|
||||||
await super.start();
|
await super.start();
|
||||||
// TODO: check connection (`checkConnection` function) here
|
// TODO: check connection (`checkConnection` function) here
|
||||||
this.changeConnectionState('connected');
|
this.changeConnectionState("connected");
|
||||||
log.trace('connected');
|
log.trace("connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop(): Promise<void> {
|
override async stop(): Promise<void> {
|
||||||
log.trace('disconnecting from Fluence network');
|
log.trace("disconnecting from Fluence network");
|
||||||
this.changeConnectionState('disconnecting');
|
this.changeConnectionState("disconnecting");
|
||||||
await super.stop();
|
await super.stop();
|
||||||
this.changeConnectionState('disconnected');
|
this.changeConnectionState("disconnected");
|
||||||
log.trace('disconnected');
|
log.trace("disconnected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,32 @@
|
|||||||
import { it, describe, expect } from 'vitest';
|
/**
|
||||||
import { handleTimeout } from '../../particle/Particle.js';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import { doNothing } from '../../jsServiceHost/serviceUtils.js';
|
*
|
||||||
import { registerHandlersHelper, withClient } from '../../util/testUtils.js';
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
import { checkConnection } from '../checkConnection.js';
|
* you may not use this file except in compliance with the License.
|
||||||
import { nodes, RELAY } from './connection.js';
|
* You may obtain a copy of the License at
|
||||||
import { CallServiceData } from '../../jsServiceHost/interfaces.js';
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
describe('FluenceClient usage test suite', () => {
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
it('should make a call through network', async () => {
|
import { it, describe, expect } from "vitest";
|
||||||
|
|
||||||
|
import { CallServiceData } from "../../jsServiceHost/interfaces.js";
|
||||||
|
import { doNothing } from "../../jsServiceHost/serviceUtils.js";
|
||||||
|
import { handleTimeout } from "../../particle/Particle.js";
|
||||||
|
import { registerHandlersHelper, withClient } from "../../util/testUtils.js";
|
||||||
|
import { checkConnection } from "../checkConnection.js";
|
||||||
|
|
||||||
|
import { nodes, RELAY } from "./connection.js";
|
||||||
|
|
||||||
|
describe("FluenceClient usage test suite", () => {
|
||||||
|
it("should make a call through network", async () => {
|
||||||
await withClient(RELAY, {}, async (peer) => {
|
await withClient(RELAY, {}, async (peer) => {
|
||||||
// arrange
|
// arrange
|
||||||
|
|
||||||
@ -28,9 +47,10 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const result = await new Promise<string>((resolve, reject) => {
|
const result = await new Promise<JSONValue>((resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
@ -40,11 +60,11 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
callback: {
|
callback: {
|
||||||
callback: (args: any) => {
|
callback: (args): undefined => {
|
||||||
const [val] = args;
|
const [val] = args;
|
||||||
resolve(val);
|
resolve(val);
|
||||||
},
|
},
|
||||||
error: (args: any) => {
|
error: (args): undefined => {
|
||||||
const [error] = args;
|
const [error] = args;
|
||||||
reject(error);
|
reject(error);
|
||||||
},
|
},
|
||||||
@ -54,11 +74,11 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBe('hello world!');
|
expect(result).toBe("hello world!");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('check connection should work', async function () {
|
it("check connection should work", async function () {
|
||||||
await withClient(RELAY, {}, async (peer) => {
|
await withClient(RELAY, {}, async (peer) => {
|
||||||
const isConnected = await checkConnection(peer);
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
@ -66,7 +86,7 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('check connection should work with ttl', async function () {
|
it("check connection should work with ttl", async function () {
|
||||||
await withClient(RELAY, {}, async (peer) => {
|
await withClient(RELAY, {}, async (peer) => {
|
||||||
const isConnected = await checkConnection(peer, 10000);
|
const isConnected = await checkConnection(peer, 10000);
|
||||||
|
|
||||||
@ -74,17 +94,21 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('two clients should work inside the same time javascript process', async () => {
|
it("two clients should work inside the same time javascript process", async () => {
|
||||||
await withClient(RELAY, {}, async (peer1) => {
|
await withClient(RELAY, {}, async (peer1) => {
|
||||||
await withClient(RELAY, {}, async (peer2) => {
|
await withClient(RELAY, {}, async (peer2) => {
|
||||||
const res = new Promise((resolve) => {
|
const res = new Promise((resolve) => {
|
||||||
peer2.internals.regHandler.common('test', 'test', (req: CallServiceData) => {
|
peer2.internals.regHandler.common(
|
||||||
|
"test",
|
||||||
|
"test",
|
||||||
|
(req: CallServiceData) => {
|
||||||
resolve(req.args[0]);
|
resolve(req.args[0]);
|
||||||
return {
|
return {
|
||||||
result: {},
|
result: {},
|
||||||
retCode: 0,
|
retCode: 0,
|
||||||
};
|
};
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const script = `
|
const script = `
|
||||||
@ -93,6 +117,7 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
(call "${peer2.getPeerId()}" ("test" "test") ["test"])
|
(call "${peer2.getPeerId()}" ("test" "test") ["test"])
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const particle = await peer1.internals.createNewParticle(script);
|
const particle = await peer1.internals.createNewParticle(script);
|
||||||
|
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
@ -101,13 +126,13 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
|
|
||||||
peer1.internals.initiateParticle(particle, doNothing);
|
peer1.internals.initiateParticle(particle, doNothing);
|
||||||
|
|
||||||
expect(await res).toEqual('test');
|
expect(await res).toEqual("test");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should make connection to network', () => {
|
describe("should make connection to network", () => {
|
||||||
it('address as string', async () => {
|
it("address as string", async () => {
|
||||||
await withClient(nodes[0].multiaddr, {}, async (peer) => {
|
await withClient(nodes[0].multiaddr, {}, async (peer) => {
|
||||||
const isConnected = await checkConnection(peer);
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
@ -115,7 +140,7 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('address as node', async () => {
|
it("address as node", async () => {
|
||||||
await withClient(nodes[0], {}, async (peer) => {
|
await withClient(nodes[0], {}, async (peer) => {
|
||||||
const isConnected = await checkConnection(peer);
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
@ -123,23 +148,31 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('With connection options: dialTimeout', async () => {
|
it("With connection options: dialTimeout", async () => {
|
||||||
await withClient(RELAY, { connectionOptions: { dialTimeoutMs: 100000 } }, async (peer) => {
|
await withClient(
|
||||||
|
RELAY,
|
||||||
|
{ connectionOptions: { dialTimeoutMs: 100000 } },
|
||||||
|
async (peer) => {
|
||||||
const isConnected = await checkConnection(peer);
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
expect(isConnected).toBeTruthy();
|
expect(isConnected).toBeTruthy();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('With connection options: skipCheckConnection', async () => {
|
it("With connection options: skipCheckConnection", async () => {
|
||||||
await withClient(RELAY, { connectionOptions: { skipCheckConnection: true } }, async (peer) => {
|
await withClient(
|
||||||
|
RELAY,
|
||||||
|
{ connectionOptions: { skipCheckConnection: true } },
|
||||||
|
async (peer) => {
|
||||||
const isConnected = await checkConnection(peer);
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
expect(isConnected).toBeTruthy();
|
expect(isConnected).toBeTruthy();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('With connection options: defaultTTL', async () => {
|
it("With connection options: defaultTTL", async () => {
|
||||||
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
|
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
|
||||||
const isConnected = await checkConnection(peer);
|
const isConnected = await checkConnection(peer);
|
||||||
|
|
||||||
@ -148,22 +181,25 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('Should throw correct error when the client tries to send a particle not to the relay', async () => {
|
it.skip("Should throw correct error when the client tries to send a particle not to the relay", async () => {
|
||||||
await withClient(RELAY, {}, async (peer) => {
|
await withClient(RELAY, {}, async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(xor
|
(xor
|
||||||
(call "incorrect_peer_id" ("any" "service") [])
|
(call "incorrect_peer_id" ("any" "service") [])
|
||||||
(call %init_peer_id% ("callback" "error") [%last_error%])
|
(call %init_peer_id% ("callback" "error") [%last_error%])
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
const promise = new Promise((resolve, reject) => {
|
|
||||||
|
const promise = new Promise((_resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
callback: {
|
callback: {
|
||||||
error: (args: any) => {
|
error: (args): undefined => {
|
||||||
const [error] = args;
|
const [error] = args;
|
||||||
reject(error);
|
reject(error);
|
||||||
},
|
},
|
||||||
@ -171,7 +207,7 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, (stage) => {
|
peer.internals.initiateParticle(particle, (stage) => {
|
||||||
if (stage.stage === 'sendingError') {
|
if (stage.stage === "sendingError") {
|
||||||
reject(stage.errorMessage);
|
reject(stage.errorMessage);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -180,7 +216,7 @@ describe('FluenceClient usage test suite', () => {
|
|||||||
await promise;
|
await promise;
|
||||||
|
|
||||||
await expect(promise).rejects.toMatch(
|
await expect(promise).rejects.toMatch(
|
||||||
'Particle is expected to be sent to only the single peer (relay which client is connected to)',
|
"Particle is expected to be sent to only the single peer (relay which client is connected to)",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2023 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
export const nodes = [
|
export const nodes = [
|
||||||
{
|
{
|
||||||
multiaddr: '/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
multiaddr:
|
||||||
peerId: '12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR',
|
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
|
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,19 +13,25 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { ClientPeer } from './ClientPeer.js';
|
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
import { WrapFnIntoServiceCall } from '../jsServiceHost/serviceUtils.js';
|
|
||||||
import { handleTimeout } from '../particle/Particle.js';
|
|
||||||
|
|
||||||
const log = logger('connection');
|
import { WrapFnIntoServiceCall } from "../jsServiceHost/serviceUtils.js";
|
||||||
|
import { handleTimeout } from "../particle/Particle.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
|
import { ClientPeer } from "./ClientPeer.js";
|
||||||
|
|
||||||
|
const log = logger("connection");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the network connection by sending a ping-like request to relay node
|
* Checks the network connection by sending a ping-like request to relay node
|
||||||
* @param { ClientPeer } peer - The Fluence Client instance.
|
* @param { ClientPeer } peer - The Fluence Client instance.
|
||||||
*/
|
*/
|
||||||
export const checkConnection = async (peer: ClientPeer, ttl?: number): Promise<boolean> => {
|
export const checkConnection = async (
|
||||||
|
peer: ClientPeer,
|
||||||
|
ttl?: number,
|
||||||
|
): Promise<boolean> => {
|
||||||
const msg = Math.random().toString(36).substring(7);
|
const msg = Math.random().toString(36).substring(7);
|
||||||
|
|
||||||
const script = `
|
const script = `
|
||||||
@ -45,17 +51,19 @@ export const checkConnection = async (peer: ClientPeer, ttl?: number): Promise<b
|
|||||||
(call %init_peer_id% ("callback" "error") [%last_error%])
|
(call %init_peer_id% ("callback" "error") [%last_error%])
|
||||||
)
|
)
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script, ttl);
|
const particle = await peer.internals.createNewParticle(script, ttl);
|
||||||
|
|
||||||
const promise = new Promise<string>((resolve, reject) => {
|
const promise = new Promise<JSONValue>((resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.internals.regHandler.forParticle(
|
peer.internals.regHandler.forParticle(
|
||||||
particle.id,
|
particle.id,
|
||||||
'load',
|
"load",
|
||||||
'relay',
|
"relay",
|
||||||
WrapFnIntoServiceCall(() => {
|
WrapFnIntoServiceCall(() => {
|
||||||
return peer.getRelayPeerId();
|
return peer.getRelayPeerId();
|
||||||
}),
|
}),
|
||||||
@ -63,8 +71,8 @@ export const checkConnection = async (peer: ClientPeer, ttl?: number): Promise<b
|
|||||||
|
|
||||||
peer.internals.regHandler.forParticle(
|
peer.internals.regHandler.forParticle(
|
||||||
particle.id,
|
particle.id,
|
||||||
'load',
|
"load",
|
||||||
'msg',
|
"msg",
|
||||||
WrapFnIntoServiceCall(() => {
|
WrapFnIntoServiceCall(() => {
|
||||||
return msg;
|
return msg;
|
||||||
}),
|
}),
|
||||||
@ -72,26 +80,30 @@ export const checkConnection = async (peer: ClientPeer, ttl?: number): Promise<b
|
|||||||
|
|
||||||
peer.internals.regHandler.forParticle(
|
peer.internals.regHandler.forParticle(
|
||||||
particle.id,
|
particle.id,
|
||||||
'callback',
|
"callback",
|
||||||
'callback',
|
"callback",
|
||||||
WrapFnIntoServiceCall((args) => {
|
WrapFnIntoServiceCall((args) => {
|
||||||
const [val] = args;
|
const [val] = args;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve(val);
|
resolve(val);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
peer.internals.regHandler.forParticle(
|
peer.internals.regHandler.forParticle(
|
||||||
particle.id,
|
particle.id,
|
||||||
'callback',
|
"callback",
|
||||||
'error',
|
"error",
|
||||||
WrapFnIntoServiceCall((args) => {
|
WrapFnIntoServiceCall((args) => {
|
||||||
const [error] = args;
|
const [error] = args;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
reject(error);
|
reject(error);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -99,19 +111,28 @@ export const checkConnection = async (peer: ClientPeer, ttl?: number): Promise<b
|
|||||||
peer.internals.initiateParticle(
|
peer.internals.initiateParticle(
|
||||||
particle,
|
particle,
|
||||||
handleTimeout(() => {
|
handleTimeout(() => {
|
||||||
reject('particle timed out');
|
reject("particle timed out");
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await promise;
|
const result = await promise;
|
||||||
if (result != msg) {
|
|
||||||
log.error("unexpected behavior. 'identity' must return the passed arguments.");
|
if (result !== msg) {
|
||||||
|
log.error(
|
||||||
|
"unexpected behavior. 'identity' must return the passed arguments.",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('error on establishing connection. Relay: %s error: %j', peer.getRelayPeerId(), e);
|
log.error(
|
||||||
|
"error on establishing connection. Relay: %s error: %j",
|
||||||
|
peer.getRelayPeerId(),
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,19 +1,37 @@
|
|||||||
import { it, describe, expect, test } from 'vitest';
|
/**
|
||||||
import { aqua2ts, ts2aqua } from '../conversions.js';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
const i32 = { tag: 'scalar', name: 'i32' } as const;
|
import { JSONValue, NonArrowType } from "@fluencelabs/interfaces";
|
||||||
|
import { it, describe, expect, test } from "vitest";
|
||||||
|
|
||||||
|
import { aqua2ts, ts2aqua } from "../conversions.js";
|
||||||
|
|
||||||
|
const i32 = { tag: "scalar", name: "i32" } as const;
|
||||||
|
|
||||||
const opt_i32 = {
|
const opt_i32 = {
|
||||||
tag: 'option',
|
tag: "option",
|
||||||
type: i32,
|
type: i32,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const array_i32 = { tag: 'array', type: i32 };
|
const array_i32 = { tag: "array", type: i32 };
|
||||||
|
|
||||||
const array_opt_i32 = { tag: 'array', type: opt_i32 };
|
const array_opt_i32 = { tag: "array", type: opt_i32 };
|
||||||
|
|
||||||
const labeledProduct = {
|
const labeledProduct = {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
a: i32,
|
a: i32,
|
||||||
b: opt_i32,
|
b: opt_i32,
|
||||||
@ -22,8 +40,8 @@ const labeledProduct = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const struct = {
|
const struct = {
|
||||||
tag: 'struct',
|
tag: "struct",
|
||||||
name: 'someStruct',
|
name: "someStruct",
|
||||||
fields: {
|
fields: {
|
||||||
a: i32,
|
a: i32,
|
||||||
b: opt_i32,
|
b: opt_i32,
|
||||||
@ -61,7 +79,7 @@ const structs = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const labeledProduct2 = {
|
const labeledProduct2 = {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
@ -69,15 +87,15 @@ const labeledProduct2 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nestedLabeledProductType = {
|
const nestedLabeledProductType = {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
a: labeledProduct2,
|
a: labeledProduct2,
|
||||||
b: {
|
b: {
|
||||||
tag: 'option',
|
tag: "option",
|
||||||
type: labeledProduct2,
|
type: labeledProduct2,
|
||||||
},
|
},
|
||||||
c: {
|
c: {
|
||||||
tag: 'array',
|
tag: "array",
|
||||||
type: labeledProduct2,
|
type: labeledProduct2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -151,7 +169,13 @@ const nestedStructs = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
describe('Conversion from aqua to typescript', () => {
|
interface ConversionTestArgs {
|
||||||
|
aqua: JSONValue;
|
||||||
|
ts: JSONValue;
|
||||||
|
type: NonArrowType;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Conversion from aqua to typescript", () => {
|
||||||
test.each`
|
test.each`
|
||||||
aqua | ts | type
|
aqua | ts | type
|
||||||
${1} | ${1} | ${i32}
|
${1} | ${1} | ${i32}
|
||||||
@ -171,8 +195,8 @@ describe('Conversion from aqua to typescript', () => {
|
|||||||
${nestedStructs[1].aqua} | ${nestedStructs[1].ts} | ${nestedLabeledProductType}
|
${nestedStructs[1].aqua} | ${nestedStructs[1].ts} | ${nestedLabeledProductType}
|
||||||
`(
|
`(
|
||||||
//
|
//
|
||||||
'aqua: $aqua. ts: $ts. type: $type',
|
"aqua: $aqua. ts: $ts. type: $type",
|
||||||
async ({ aqua, ts, type }) => {
|
({ aqua, ts, type }: ConversionTestArgs) => {
|
||||||
// arrange
|
// arrange
|
||||||
|
|
||||||
// act
|
// act
|
||||||
@ -186,11 +210,11 @@ describe('Conversion from aqua to typescript', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Conversion corner cases', () => {
|
describe("Conversion corner cases", () => {
|
||||||
it('Should accept undefined in object entry', () => {
|
it("Should accept undefined in object entry", () => {
|
||||||
// arrange
|
// arrange
|
||||||
const type = {
|
const type = {
|
||||||
tag: 'labeledProduct',
|
tag: "labeledProduct",
|
||||||
fields: {
|
fields: {
|
||||||
x: opt_i32,
|
x: opt_i32,
|
||||||
y: opt_i32,
|
y: opt_i32,
|
||||||
@ -200,6 +224,7 @@ describe('Conversion corner cases', () => {
|
|||||||
const valueInTs = {
|
const valueInTs = {
|
||||||
x: 1,
|
x: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const valueInAqua = {
|
const valueInAqua = {
|
||||||
x: [1],
|
x: [1],
|
||||||
y: [],
|
y: [],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,7 +13,19 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { CallAquaFunctionType, getArgumentTypes, isReturnTypeVoid } from '@fluencelabs/interfaces';
|
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
|
import {
|
||||||
|
FnConfig,
|
||||||
|
FunctionCallDef,
|
||||||
|
getArgumentTypes,
|
||||||
|
isReturnTypeVoid,
|
||||||
|
PassedArgs,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
errorHandlingService,
|
errorHandlingService,
|
||||||
@ -23,12 +35,9 @@ import {
|
|||||||
responseService,
|
responseService,
|
||||||
ServiceDescription,
|
ServiceDescription,
|
||||||
userHandlerService,
|
userHandlerService,
|
||||||
} from './services.js';
|
} from "./services.js";
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
const log = logger("aqua");
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
|
||||||
|
|
||||||
const log = logger('aqua');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function which does all the internal work of creating particles
|
* Convenience function which does all the internal work of creating particles
|
||||||
@ -41,25 +50,55 @@ const log = logger('aqua');
|
|||||||
* @param args - args in the form of JSON where each key corresponds to the name of the argument
|
* @param args - args in the form of JSON where each key corresponds to the name of the argument
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const callAquaFunction: CallAquaFunctionType = async ({ def, script, config, peer, args }) => {
|
|
||||||
log.trace('calling aqua function %j', { def, script, config, args });
|
type CallAquaFunctionArgs = {
|
||||||
|
def: FunctionCallDef;
|
||||||
|
script: string;
|
||||||
|
config: FnConfig;
|
||||||
|
peer: FluencePeer;
|
||||||
|
args: PassedArgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const callAquaFunction = async ({
|
||||||
|
def,
|
||||||
|
script,
|
||||||
|
config,
|
||||||
|
peer,
|
||||||
|
args,
|
||||||
|
}: 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);
|
const argumentTypes = getArgumentTypes(def);
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script, config?.ttl);
|
const particle = await peer.internals.createNewParticle(script, config.ttl);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
for (const [name, argVal] of Object.entries(args)) {
|
||||||
return reject(particle.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let [name, argVal] of Object.entries(args)) {
|
|
||||||
const type = argumentTypes[name];
|
const type = argumentTypes[name];
|
||||||
let service: ServiceDescription;
|
let service: ServiceDescription;
|
||||||
if (type.tag === 'arrow') {
|
|
||||||
service = userHandlerService(def.names.callbackSrv, [name, type], argVal);
|
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,
|
||||||
|
);
|
||||||
} else {
|
} 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(def.names.getDataSrv, name, type, argVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerParticleScopeService(peer, particle, service);
|
registerParticleScopeService(peer, particle, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,31 +106,40 @@ export const callAquaFunction: CallAquaFunctionType = async ({ def, script, conf
|
|||||||
|
|
||||||
registerParticleScopeService(peer, particle, injectRelayService(def, peer));
|
registerParticleScopeService(peer, particle, injectRelayService(def, peer));
|
||||||
|
|
||||||
registerParticleScopeService(peer, particle, errorHandlingService(def, reject));
|
registerParticleScopeService(
|
||||||
|
peer,
|
||||||
|
particle,
|
||||||
|
errorHandlingService(def, reject),
|
||||||
|
);
|
||||||
|
|
||||||
peer.internals.initiateParticle(particle, (stage: any) => {
|
peer.internals.initiateParticle(particle, (stage) => {
|
||||||
// If function is void, then it's completed when one of the two conditions is met:
|
// 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')
|
// 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')
|
// 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')) {
|
if (
|
||||||
|
isReturnTypeVoid(def) &&
|
||||||
|
(stage.stage === "sent" || stage.stage === "localWorkDone")
|
||||||
|
) {
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stage.stage === 'sendingError') {
|
if (stage.stage === "sendingError") {
|
||||||
reject(`Could not send particle for ${def.functionName}: not connected (particle id: ${particle.id})`);
|
reject(
|
||||||
|
`Could not send particle for ${def.functionName}: not connected (particle id: ${particle.id})`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stage.stage === 'expired') {
|
if (stage.stage === "expired") {
|
||||||
reject(
|
reject(
|
||||||
`Particle expired after ttl of ${particle.ttl}ms for function ${def.functionName} (particle id: ${particle.id})`,
|
`Particle expired after ttl of ${particle.ttl}ms for function ${def.functionName} (particle id: ${particle.id})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stage.stage === 'interpreterError') {
|
if (stage.stage === "interpreterError") {
|
||||||
reject(
|
reject(
|
||||||
`Script interpretation failed for ${def.functionName}: ${stage.errorMessage} (particle id: ${particle.id})`,
|
`Script interpretation failed for ${def.functionName}: ${stage.errorMessage} (particle id: ${particle.id})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,10 +13,24 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { jsonify } from '../util/utils.js';
|
|
||||||
import { match } from 'ts-pattern';
|
// TODO: This file is a mess. Need to refactor it later
|
||||||
import type { ArrowType, ArrowWithoutCallbacks, NonArrowType } from '@fluencelabs/interfaces';
|
/* eslint-disable */
|
||||||
import { CallServiceData } from '../jsServiceHost/interfaces.js';
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ArrowType,
|
||||||
|
ArrowWithoutCallbacks,
|
||||||
|
JSONArray,
|
||||||
|
JSONValue,
|
||||||
|
NonArrowType,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
import { match } from "ts-pattern";
|
||||||
|
|
||||||
|
import { CallServiceData } from "../jsServiceHost/interfaces.js";
|
||||||
|
import { jsonify } from "../util/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert value from its representation in aqua language to representation in typescript
|
* Convert value from its representation in aqua language to representation in typescript
|
||||||
@ -24,38 +38,42 @@ import { CallServiceData } from '../jsServiceHost/interfaces.js';
|
|||||||
* @param type - definition of the aqua type
|
* @param type - definition of the aqua type
|
||||||
* @returns value represented in typescript
|
* @returns value represented in typescript
|
||||||
*/
|
*/
|
||||||
export const aqua2ts = (value: any, type: NonArrowType): any => {
|
export const aqua2ts = (value: JSONValue, type: NonArrowType): JSONValue => {
|
||||||
const res = match(type)
|
const res = match(type)
|
||||||
.with({ tag: 'nil' }, () => {
|
.with({ tag: "nil" }, () => {
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.with({ tag: 'option' }, (opt) => {
|
.with({ tag: "option" }, (opt) => {
|
||||||
|
assert(Array.isArray(value), "Should not be possible, bad types");
|
||||||
|
|
||||||
if (value.length === 0) {
|
if (value.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return aqua2ts(value[0], opt.type);
|
return aqua2ts(value[0], opt.type);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// @ts-ignore
|
.with({ tag: "scalar" }, { tag: "bottomType" }, { tag: "topType" }, () => {
|
||||||
.with({ tag: 'scalar' }, { tag: 'bottomType' }, { tag: 'topType' }, () => {
|
|
||||||
return value;
|
return value;
|
||||||
})
|
})
|
||||||
.with({ tag: 'array' }, (arr) => {
|
.with({ tag: "array" }, (arr) => {
|
||||||
return value.map((y: any) => aqua2ts(y, arr.type));
|
assert(Array.isArray(value), "Should not be possible, bad types");
|
||||||
|
return value.map((y) => {
|
||||||
|
return aqua2ts(y, arr.type);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.with({ tag: 'struct' }, (x) => {
|
.with({ tag: "struct" }, (x) => {
|
||||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||||
const val = aqua2ts(value[key], type);
|
const val = aqua2ts(value[key], type);
|
||||||
return { ...agg, [key]: val };
|
return { ...agg, [key]: val };
|
||||||
}, {});
|
}, {});
|
||||||
})
|
})
|
||||||
.with({ tag: 'labeledProduct' }, (x) => {
|
.with({ tag: "labeledProduct" }, (x) => {
|
||||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||||
const val = aqua2ts(value[key], type);
|
const val = aqua2ts(value[key], type);
|
||||||
return { ...agg, [key]: val };
|
return { ...agg, [key]: val };
|
||||||
}, {});
|
}, {});
|
||||||
})
|
})
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
return x.items.map((type, index) => {
|
return x.items.map((type, index) => {
|
||||||
return aqua2ts(value[index], type);
|
return aqua2ts(value[index], type);
|
||||||
});
|
});
|
||||||
@ -63,8 +81,9 @@ export const aqua2ts = (value: any, type: NonArrowType): any => {
|
|||||||
// uncomment to check that every pattern in matched
|
// uncomment to check that every pattern in matched
|
||||||
// .exhaustive();
|
// .exhaustive();
|
||||||
.otherwise(() => {
|
.otherwise(() => {
|
||||||
throw new Error('Unexpected tag: ' + jsonify(type));
|
throw new Error("Unexpected tag: " + jsonify(type));
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -74,25 +93,30 @@ export const aqua2ts = (value: any, type: NonArrowType): any => {
|
|||||||
* @param arrow - aqua type definition
|
* @param arrow - aqua type definition
|
||||||
* @returns arguments in typescript representation
|
* @returns arguments in typescript representation
|
||||||
*/
|
*/
|
||||||
export const aquaArgs2Ts = (req: CallServiceData, arrow: ArrowWithoutCallbacks) => {
|
export const aquaArgs2Ts = (
|
||||||
|
req: CallServiceData,
|
||||||
|
arrow: ArrowWithoutCallbacks,
|
||||||
|
): JSONArray => {
|
||||||
const argTypes = match(arrow.domain)
|
const argTypes = match(arrow.domain)
|
||||||
.with({ tag: 'labeledProduct' }, (x) => {
|
.with({ tag: "labeledProduct" }, (x) => {
|
||||||
return Object.values(x.fields);
|
return Object.values(x.fields);
|
||||||
})
|
})
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
return x.items;
|
return x.items;
|
||||||
})
|
})
|
||||||
.with({ tag: 'nil' }, (x) => {
|
.with({ tag: "nil" }, (x) => {
|
||||||
return [];
|
return [];
|
||||||
})
|
})
|
||||||
// uncomment to check that every pattern in matched
|
// uncomment to check that every pattern in matched
|
||||||
// .exhaustive()
|
// .exhaustive()
|
||||||
.otherwise(() => {
|
.otherwise(() => {
|
||||||
throw new Error('Unexpected tag: ' + jsonify(arrow.domain));
|
throw new Error("Unexpected tag: " + jsonify(arrow.domain));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (req.args.length !== argTypes.length) {
|
if (req.args.length !== argTypes.length) {
|
||||||
throw new Error(`incorrect number of arguments, expected: ${argTypes.length}, got: ${req.args.length}`);
|
throw new Error(
|
||||||
|
`incorrect number of arguments, expected: ${argTypes.length}, got: ${req.args.length}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return req.args.map((arg, index) => {
|
return req.args.map((arg, index) => {
|
||||||
@ -106,38 +130,40 @@ export const aquaArgs2Ts = (req: CallServiceData, arrow: ArrowWithoutCallbacks)
|
|||||||
* @param type - definition of the aqua type
|
* @param type - definition of the aqua type
|
||||||
* @returns value represented in aqua
|
* @returns value represented in aqua
|
||||||
*/
|
*/
|
||||||
export const ts2aqua = (value: any, type: NonArrowType): any => {
|
export const ts2aqua = (value: JSONValue, type: NonArrowType): JSONValue => {
|
||||||
const res = match(type)
|
const res = match(type)
|
||||||
.with({ tag: 'nil' }, () => {
|
.with({ tag: "nil" }, () => {
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.with({ tag: 'option' }, (opt) => {
|
.with({ tag: "option" }, (opt) => {
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
return [];
|
return [];
|
||||||
} else {
|
} else {
|
||||||
return [ts2aqua(value, opt.type)];
|
return [ts2aqua(value, opt.type)];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// @ts-ignore
|
.with({ tag: "scalar" }, { tag: "bottomType" }, { tag: "topType" }, () => {
|
||||||
.with({ tag: 'scalar' }, { tag: 'bottomType' }, { tag: 'topType' }, () => {
|
|
||||||
return value;
|
return value;
|
||||||
})
|
})
|
||||||
.with({ tag: 'array' }, (arr) => {
|
.with({ tag: "array" }, (arr) => {
|
||||||
return value.map((y: any) => ts2aqua(y, arr.type));
|
assert(Array.isArray(value), "Should not be possible, bad types");
|
||||||
|
return value.map((y) => {
|
||||||
|
return ts2aqua(y, arr.type);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.with({ tag: 'struct' }, (x) => {
|
.with({ tag: "struct" }, (x) => {
|
||||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||||
const val = ts2aqua(value[key], type);
|
const val = ts2aqua(value[key], type);
|
||||||
return { ...agg, [key]: val };
|
return { ...agg, [key]: val };
|
||||||
}, {});
|
}, {});
|
||||||
})
|
})
|
||||||
.with({ tag: 'labeledProduct' }, (x) => {
|
.with({ tag: "labeledProduct" }, (x) => {
|
||||||
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
return Object.entries(x.fields).reduce((agg, [key, type]) => {
|
||||||
const val = ts2aqua(value[key], type);
|
const val = ts2aqua(value[key], type);
|
||||||
return { ...agg, [key]: val };
|
return { ...agg, [key]: val };
|
||||||
}, {});
|
}, {});
|
||||||
})
|
})
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
return x.items.map((type, index) => {
|
return x.items.map((type, index) => {
|
||||||
return ts2aqua(value[index], type);
|
return ts2aqua(value[index], type);
|
||||||
});
|
});
|
||||||
@ -145,7 +171,7 @@ export const ts2aqua = (value: any, type: NonArrowType): any => {
|
|||||||
// uncomment to check that every pattern in matched
|
// uncomment to check that every pattern in matched
|
||||||
// .exhaustive()
|
// .exhaustive()
|
||||||
.otherwise(() => {
|
.otherwise(() => {
|
||||||
throw new Error('Unexpected tag: ' + jsonify(type));
|
throw new Error("Unexpected tag: " + jsonify(type));
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -157,8 +183,11 @@ export const ts2aqua = (value: any, type: NonArrowType): any => {
|
|||||||
* @param arrowType - the arrow type which describes the service
|
* @param arrowType - the arrow type which describes the service
|
||||||
* @returns - value represented in aqua
|
* @returns - value represented in aqua
|
||||||
*/
|
*/
|
||||||
export const returnType2Aqua = (returnValue: any, arrowType: ArrowType<NonArrowType>) => {
|
export const returnType2Aqua = (
|
||||||
if (arrowType.codomain.tag === 'nil') {
|
returnValue: any,
|
||||||
|
arrowType: ArrowType<NonArrowType>,
|
||||||
|
) => {
|
||||||
|
if (arrowType.codomain.tag === "nil") {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,21 +210,26 @@ export const returnType2Aqua = (returnValue: any, arrowType: ArrowType<NonArrowT
|
|||||||
* @param arrow - aqua type definition
|
* @param arrow - aqua type definition
|
||||||
* @returns response value in typescript representation
|
* @returns response value in typescript representation
|
||||||
*/
|
*/
|
||||||
export const responseServiceValue2ts = (req: CallServiceData, arrow: ArrowType<any>) => {
|
export const responseServiceValue2ts = (
|
||||||
|
req: CallServiceData,
|
||||||
|
arrow: ArrowType<any>,
|
||||||
|
) => {
|
||||||
return match(arrow.codomain)
|
return match(arrow.codomain)
|
||||||
.with({ tag: 'nil' }, () => {
|
.with({ tag: "nil" }, () => {
|
||||||
return undefined;
|
return null;
|
||||||
})
|
})
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
if (x.items.length === 0) {
|
if (x.items.length === 0) {
|
||||||
return undefined;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x.items.length === 1) {
|
if (x.items.length === 1) {
|
||||||
return aqua2ts(req.args[0], x.items[0]);
|
return aqua2ts(req.args[0], x.items[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return req.args.map((y, index) => aqua2ts(y, x.items[index]));
|
return req.args.map((y, index) => {
|
||||||
|
return aqua2ts(y, x.items[index]);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.exhaustive();
|
.exhaustive();
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,43 +13,72 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { RegisterServiceType } from '@fluencelabs/interfaces';
|
|
||||||
import { registerGlobalService, userHandlerService } from './services.js';
|
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
import type { ServiceDef, ServiceImpl } from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
const log = logger('aqua');
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
export const registerService: RegisterServiceType = ({ peer, def, serviceId, service }) => {
|
import { registerGlobalService, userHandlerService } from "./services.js";
|
||||||
log.trace('registering aqua service %o', { def, serviceId, service });
|
|
||||||
|
const log = logger("aqua");
|
||||||
|
|
||||||
|
interface RegisterServiceArgs {
|
||||||
|
peer: FluencePeer;
|
||||||
|
def: ServiceDef;
|
||||||
|
serviceId: string | undefined;
|
||||||
|
service: ServiceImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const registerService = ({
|
||||||
|
peer,
|
||||||
|
def,
|
||||||
|
serviceId = def.defaultServiceId,
|
||||||
|
service,
|
||||||
|
}: RegisterServiceArgs) => {
|
||||||
|
// TODO: Need to refactor this. We can compute function types from service implementation, making func more type safe
|
||||||
|
log.trace("registering aqua service %o", { def, serviceId, service });
|
||||||
|
|
||||||
// Checking for missing keys
|
// Checking for missing keys
|
||||||
const requiredKeys = def.functions.tag === 'nil' ? [] : Object.keys(def.functions.fields);
|
const requiredKeys =
|
||||||
const incorrectServiceDefinitions = requiredKeys.filter((f) => !(f in service));
|
def.functions.tag === "nil" ? [] : Object.keys(def.functions.fields);
|
||||||
if (!!incorrectServiceDefinitions.length) {
|
|
||||||
|
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(
|
throw new Error(
|
||||||
`Error registering service ${serviceId}: missing functions: ` +
|
`Error registering service ${serviceId}: missing functions: ` +
|
||||||
incorrectServiceDefinitions.map((d) => "'" + d + "'").join(', '),
|
incorrectServiceDefinitions
|
||||||
|
.map((d) => {
|
||||||
|
return "'" + d + "'";
|
||||||
|
})
|
||||||
|
.join(", "),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!serviceId) {
|
const singleFunctions =
|
||||||
serviceId = def.defaultServiceId;
|
def.functions.tag === "nil" ? [] : Object.entries(def.functions.fields);
|
||||||
}
|
|
||||||
|
|
||||||
if (!serviceId) {
|
for (const singleFunction of singleFunctions) {
|
||||||
throw new Error('Service ID must be specified');
|
const [name] = singleFunction;
|
||||||
}
|
|
||||||
|
|
||||||
const singleFunctions = def.functions.tag === 'nil' ? [] : Object.entries(def.functions.fields);
|
|
||||||
for (let singleFunction of singleFunctions) {
|
|
||||||
let [name, type] = singleFunction;
|
|
||||||
// The function has type of (arg1, arg2, arg3, ... , callParams) => CallServiceResultType | void
|
// 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(...)
|
// Account for the fact that user service might be defined as a class - .bind(...)
|
||||||
const userDefinedHandler = service[name].bind(service);
|
const userDefinedHandler = service[name].bind(service);
|
||||||
|
|
||||||
const serviceDescription = userHandlerService(serviceId, singleFunction, userDefinedHandler);
|
const serviceDescription = userHandlerService(
|
||||||
|
serviceId,
|
||||||
|
singleFunction,
|
||||||
|
userDefinedHandler,
|
||||||
|
);
|
||||||
|
|
||||||
registerGlobalService(peer, serviceDescription);
|
registerGlobalService(peer, serviceDescription);
|
||||||
}
|
}
|
||||||
log.trace('aqua service registered %s', serviceId);
|
|
||||||
|
log.trace("aqua service registered %s", serviceId);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,22 +13,33 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { SecurityTetraplet } from '@fluencelabs/avm';
|
|
||||||
import { match } from 'ts-pattern';
|
|
||||||
|
|
||||||
import { Particle } from '../particle/Particle.js';
|
import { SecurityTetraplet } from "@fluencelabs/avm";
|
||||||
|
|
||||||
import { aquaArgs2Ts, responseServiceValue2ts, returnType2Aqua, ts2aqua } from './conversions.js';
|
|
||||||
import {
|
import {
|
||||||
CallParams,
|
CallParams,
|
||||||
ArrowWithoutCallbacks,
|
ArrowWithoutCallbacks,
|
||||||
FunctionCallConstants,
|
|
||||||
FunctionCallDef,
|
FunctionCallDef,
|
||||||
NonArrowType,
|
NonArrowType,
|
||||||
IFluenceInternalApi,
|
ServiceImpl,
|
||||||
} from '@fluencelabs/interfaces';
|
JSONValue,
|
||||||
import { CallServiceData, GenericCallServiceHandler, ResultCodes } from '../jsServiceHost/interfaces.js';
|
} from "@fluencelabs/interfaces";
|
||||||
import { fromUint8Array } from 'js-base64';
|
import { fromUint8Array } from "js-base64";
|
||||||
|
import { match } from "ts-pattern";
|
||||||
|
|
||||||
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
|
import {
|
||||||
|
CallServiceData,
|
||||||
|
GenericCallServiceHandler,
|
||||||
|
ResultCodes,
|
||||||
|
} from "../jsServiceHost/interfaces.js";
|
||||||
|
import { Particle } from "../particle/Particle.js";
|
||||||
|
|
||||||
|
import {
|
||||||
|
aquaArgs2Ts,
|
||||||
|
responseServiceValue2ts,
|
||||||
|
returnType2Aqua,
|
||||||
|
ts2aqua,
|
||||||
|
} from "./conversions.js";
|
||||||
|
|
||||||
export interface ServiceDescription {
|
export interface ServiceDescription {
|
||||||
serviceId: string;
|
serviceId: string;
|
||||||
@ -39,7 +50,7 @@ export interface ServiceDescription {
|
|||||||
/**
|
/**
|
||||||
* Creates a service which injects relay's peer id into aqua space
|
* Creates a service which injects relay's peer id into aqua space
|
||||||
*/
|
*/
|
||||||
export const injectRelayService = (def: FunctionCallDef, peer: IFluenceInternalApi) => {
|
export const injectRelayService = (def: FunctionCallDef, peer: FluencePeer) => {
|
||||||
return {
|
return {
|
||||||
serviceId: def.names.getDataSrv,
|
serviceId: def.names.getDataSrv,
|
||||||
fnName: def.names.relay,
|
fnName: def.names.relay,
|
||||||
@ -55,7 +66,12 @@ export const injectRelayService = (def: FunctionCallDef, peer: IFluenceInternalA
|
|||||||
/**
|
/**
|
||||||
* Creates a service which injects plain value into aqua space
|
* Creates a service which injects plain value into aqua space
|
||||||
*/
|
*/
|
||||||
export const injectValueService = (serviceId: string, fnName: string, valueType: NonArrowType, value: any) => {
|
export const injectValueService = (
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
valueType: NonArrowType,
|
||||||
|
value: JSONValue,
|
||||||
|
) => {
|
||||||
return {
|
return {
|
||||||
serviceId: serviceId,
|
serviceId: serviceId,
|
||||||
fnName: fnName,
|
fnName: fnName,
|
||||||
@ -71,7 +87,10 @@ export const injectValueService = (serviceId: string, fnName: string, valueType:
|
|||||||
/**
|
/**
|
||||||
* Creates a service which is used to return value from aqua function into typescript space
|
* Creates a service which is used to return value from aqua function into typescript space
|
||||||
*/
|
*/
|
||||||
export const responseService = (def: FunctionCallDef, resolveCallback: Function) => {
|
export const responseService = (
|
||||||
|
def: FunctionCallDef,
|
||||||
|
resolveCallback: (val: JSONValue) => void,
|
||||||
|
) => {
|
||||||
return {
|
return {
|
||||||
serviceId: def.names.responseSrv,
|
serviceId: def.names.responseSrv,
|
||||||
fnName: def.names.responseFnName,
|
fnName: def.names.responseFnName,
|
||||||
@ -93,15 +112,20 @@ export const responseService = (def: FunctionCallDef, resolveCallback: Function)
|
|||||||
/**
|
/**
|
||||||
* Creates a service which is used to return errors from aqua function into typescript space
|
* Creates a service which is used to return errors from aqua function into typescript space
|
||||||
*/
|
*/
|
||||||
export const errorHandlingService = (def: FunctionCallDef, rejectCallback: Function) => {
|
export const errorHandlingService = (
|
||||||
|
def: FunctionCallDef,
|
||||||
|
rejectCallback: (err: JSONValue) => void,
|
||||||
|
) => {
|
||||||
return {
|
return {
|
||||||
serviceId: def.names.errorHandlingSrv,
|
serviceId: def.names.errorHandlingSrv,
|
||||||
fnName: def.names.errorFnName,
|
fnName: def.names.errorFnName,
|
||||||
handler: (req: CallServiceData) => {
|
handler: (req: CallServiceData) => {
|
||||||
const [err, _] = req.args;
|
const [err] = req.args;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
rejectCallback(err);
|
rejectCallback(err);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
retCode: ResultCodes.success,
|
retCode: ResultCodes.success,
|
||||||
result: {},
|
result: {},
|
||||||
@ -116,15 +140,19 @@ export const errorHandlingService = (def: FunctionCallDef, rejectCallback: Funct
|
|||||||
export const userHandlerService = (
|
export const userHandlerService = (
|
||||||
serviceId: string,
|
serviceId: string,
|
||||||
arrowType: [string, ArrowWithoutCallbacks],
|
arrowType: [string, ArrowWithoutCallbacks],
|
||||||
userHandler: (...args: Array<unknown>) => Promise<unknown>,
|
userHandler: ServiceImpl[string],
|
||||||
) => {
|
) => {
|
||||||
const [fnName, type] = arrowType;
|
const [fnName, type] = arrowType;
|
||||||
return {
|
return {
|
||||||
serviceId,
|
serviceId,
|
||||||
fnName,
|
fnName,
|
||||||
handler: async (req: CallServiceData) => {
|
handler: async (req: CallServiceData) => {
|
||||||
const args = [...aquaArgs2Ts(req, type), extractCallParams(req, type)];
|
const args: [...JSONValue[], CallParams<string>] = [
|
||||||
const rawResult = await userHandler.apply(null, args);
|
...aquaArgs2Ts(req, type),
|
||||||
|
extractCallParams(req, type),
|
||||||
|
];
|
||||||
|
|
||||||
|
const rawResult = await userHandler.bind(null)(...args);
|
||||||
const result = returnType2Aqua(rawResult, type);
|
const result = returnType2Aqua(rawResult, type);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -135,44 +163,34 @@ export const userHandlerService = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts argument of aqua function to a corresponding service.
|
|
||||||
* For arguments of non-arrow types the resulting service injects the argument into aqua space.
|
|
||||||
* For arguments of arrow types the resulting service calls the corresponding function.
|
|
||||||
*/
|
|
||||||
export const argToServiceDef = (
|
|
||||||
arg: any,
|
|
||||||
argName: string,
|
|
||||||
argType: NonArrowType | ArrowWithoutCallbacks,
|
|
||||||
names: FunctionCallConstants,
|
|
||||||
): ServiceDescription => {
|
|
||||||
if (argType.tag === 'arrow') {
|
|
||||||
return userHandlerService(names.callbackSrv, [argName, argType], arg);
|
|
||||||
} else {
|
|
||||||
return injectValueService(names.getDataSrv, argName, arg, argType);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts call params from from call service data according to aqua type definition
|
* Extracts call params from from call service data according to aqua type definition
|
||||||
*/
|
*/
|
||||||
const extractCallParams = (req: CallServiceData, arrow: ArrowWithoutCallbacks): CallParams<any> => {
|
const extractCallParams = (
|
||||||
const names = match(arrow.domain)
|
req: CallServiceData,
|
||||||
.with({ tag: 'nil' }, () => {
|
arrow: ArrowWithoutCallbacks,
|
||||||
return [] as string[];
|
): CallParams<string> => {
|
||||||
|
const names: (string | undefined)[] = match(arrow.domain)
|
||||||
|
.with({ tag: "nil" }, () => {
|
||||||
|
return [];
|
||||||
})
|
})
|
||||||
.with({ tag: 'labeledProduct' }, (x) => {
|
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||||
|
return x.items.map((_, index) => {
|
||||||
|
return "arg" + index;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.with({ tag: "labeledProduct" }, (x) => {
|
||||||
return Object.keys(x.fields);
|
return Object.keys(x.fields);
|
||||||
})
|
})
|
||||||
.with({ tag: 'unlabeledProduct' }, (x) => {
|
|
||||||
return x.items.map((_, index) => 'arg' + index);
|
|
||||||
})
|
|
||||||
.exhaustive();
|
.exhaustive();
|
||||||
|
|
||||||
const tetraplets: Record<string, SecurityTetraplet[]> = {};
|
const tetraplets: Record<string, SecurityTetraplet[]> = {};
|
||||||
|
|
||||||
for (let i = 0; i < req.args.length; i++) {
|
for (let i = 0; i < req.args.length; i++) {
|
||||||
if (names[i]) {
|
const name = names[i];
|
||||||
tetraplets[names[i]] = req.tetraplets[i];
|
|
||||||
|
if (name != null) {
|
||||||
|
tetraplets[name] = req.tetraplets[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,13 +204,25 @@ const extractCallParams = (req: CallServiceData, arrow: ArrowWithoutCallbacks):
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const registerParticleScopeService = (
|
export const registerParticleScopeService = (
|
||||||
peer: IFluenceInternalApi,
|
peer: FluencePeer,
|
||||||
particle: Particle,
|
particle: Particle,
|
||||||
service: ServiceDescription,
|
service: ServiceDescription,
|
||||||
) => {
|
) => {
|
||||||
peer.internals.regHandler.forParticle(particle.id, service.serviceId, service.fnName, service.handler);
|
peer.internals.regHandler.forParticle(
|
||||||
|
particle.id,
|
||||||
|
service.serviceId,
|
||||||
|
service.fnName,
|
||||||
|
service.handler,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const registerGlobalService = (peer: IFluenceInternalApi, service: ServiceDescription) => {
|
export const registerGlobalService = (
|
||||||
peer.internals.regHandler.common(service.serviceId, service.fnName, service.handler);
|
peer: FluencePeer,
|
||||||
|
service: ServiceDescription,
|
||||||
|
) => {
|
||||||
|
peer.internals.regHandler.common(
|
||||||
|
service.serviceId,
|
||||||
|
service.fnName,
|
||||||
|
service.handler,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2020 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -13,38 +13,41 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import { pipe } from 'it-pipe';
|
|
||||||
import { decode, encode } from 'it-length-prefixed';
|
|
||||||
import type { PeerId } from '@libp2p/interface/peer-id';
|
|
||||||
import { createLibp2p, Libp2p } from 'libp2p';
|
|
||||||
|
|
||||||
import { noise } from '@chainsafe/libp2p-noise';
|
import { noise } from "@chainsafe/libp2p-noise";
|
||||||
import { yamux } from '@chainsafe/libp2p-yamux';
|
import { yamux } from "@chainsafe/libp2p-yamux";
|
||||||
import { webSockets } from '@libp2p/websockets';
|
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
import { all } from '@libp2p/websockets/filters';
|
import { Stream } from "@libp2p/interface/connection";
|
||||||
import { multiaddr, type Multiaddr } from '@multiformats/multiaddr';
|
import type { PeerId } from "@libp2p/interface/peer-id";
|
||||||
|
import { peerIdFromString } from "@libp2p/peer-id";
|
||||||
|
import { webSockets } from "@libp2p/websockets";
|
||||||
|
import { all } from "@libp2p/websockets/filters";
|
||||||
|
import { multiaddr, type Multiaddr } from "@multiformats/multiaddr";
|
||||||
|
import { decode, encode } from "it-length-prefixed";
|
||||||
|
import map from "it-map";
|
||||||
|
import { pipe } from "it-pipe";
|
||||||
|
import { createLibp2p, Libp2p } from "libp2p";
|
||||||
|
import { identifyService } from "libp2p/identify";
|
||||||
|
import { pingService } from "libp2p/ping";
|
||||||
|
import { Subject } from "rxjs";
|
||||||
|
import { fromString } from "uint8arrays/from-string";
|
||||||
|
import { toString } from "uint8arrays/to-string";
|
||||||
|
|
||||||
import map from 'it-map';
|
import { KeyPair } from "../keypair/index.js";
|
||||||
import { fromString } from 'uint8arrays/from-string';
|
import { IParticle } from "../particle/interfaces.js";
|
||||||
import { toString } from 'uint8arrays/to-string';
|
import {
|
||||||
|
buildParticleMessage,
|
||||||
|
Particle,
|
||||||
|
serializeToString,
|
||||||
|
} from "../particle/Particle.js";
|
||||||
|
import { throwHasNoPeerId } from "../util/libp2pUtils.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
import { IConnection } from "./interfaces.js";
|
||||||
import { Subject } from 'rxjs';
|
|
||||||
import { throwIfHasNoPeerId } from '../util/libp2pUtils.js';
|
|
||||||
import { IConnection } from './interfaces.js';
|
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
|
||||||
import { buildParticleMessage, Particle, serializeToString, verifySignature } from '../particle/Particle.js';
|
|
||||||
import { identifyService } from 'libp2p/identify';
|
|
||||||
import { pingService } from 'libp2p/ping';
|
|
||||||
import { unmarshalPublicKey } from '@libp2p/crypto/keys';
|
|
||||||
import { peerIdFromString } from '@libp2p/peer-id';
|
|
||||||
import { Stream } from '@libp2p/interface/connection';
|
|
||||||
import { KeyPair } from '../keypair/index.js';
|
|
||||||
|
|
||||||
const log = logger('connection');
|
const log = logger("connection");
|
||||||
|
|
||||||
export const PROTOCOL_NAME = '/fluence/particle/2.0.0';
|
export const PROTOCOL_NAME = "/fluence/particle/2.0.0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options to configure fluence relay connection
|
* Options to configure fluence relay connection
|
||||||
@ -84,15 +87,21 @@ export interface RelayConnectionConfig {
|
|||||||
export class RelayConnection implements IConnection {
|
export class RelayConnection implements IConnection {
|
||||||
private relayAddress: Multiaddr;
|
private relayAddress: Multiaddr;
|
||||||
private lib2p2Peer: Libp2p | null = null;
|
private lib2p2Peer: Libp2p | null = null;
|
||||||
|
private relayPeerId: string;
|
||||||
|
|
||||||
constructor(private config: RelayConnectionConfig) {
|
constructor(private config: RelayConnectionConfig) {
|
||||||
this.relayAddress = multiaddr(this.config.relayAddress);
|
this.relayAddress = multiaddr(this.config.relayAddress);
|
||||||
throwIfHasNoPeerId(this.relayAddress);
|
const peerId = this.relayAddress.getPeerId();
|
||||||
|
|
||||||
|
if (peerId == null) {
|
||||||
|
throwHasNoPeerId(this.relayAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.relayPeerId = peerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRelayPeerId(): string {
|
getRelayPeerId(): string {
|
||||||
// since we check for peer id in constructor, we can safely use ! here
|
return this.relayPeerId;
|
||||||
return this.relayAddress.getPeerId()!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
supportsRelay(): boolean {
|
supportsRelay(): boolean {
|
||||||
@ -117,11 +126,17 @@ export class RelayConnection implements IConnection {
|
|||||||
streamMuxers: [yamux()],
|
streamMuxers: [yamux()],
|
||||||
connectionEncryption: [noise()],
|
connectionEncryption: [noise()],
|
||||||
connectionManager: {
|
connectionManager: {
|
||||||
|
...(this.config.dialTimeoutMs != null
|
||||||
|
? {
|
||||||
dialTimeout: this.config.dialTimeoutMs,
|
dialTimeout: this.config.dialTimeoutMs,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
},
|
},
|
||||||
connectionGater: {
|
connectionGater: {
|
||||||
// By default, this function forbids connections to private peers. For example multiaddr with ip 127.0.0.1 isn't allowed
|
// By default, this function forbids connections to private peers. For example multiaddr with ip 127.0.0.1 isn't allowed
|
||||||
denyDialMultiaddr: () => Promise.resolve(false),
|
denyDialMultiaddr: () => {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
services: {
|
services: {
|
||||||
identify: identifyService(),
|
identify: identifyService(),
|
||||||
@ -129,7 +144,10 @@ export class RelayConnection implements IConnection {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const supportedProtocols = (await this.lib2p2Peer.peerStore.get(this.lib2p2Peer.peerId)).protocols;
|
const supportedProtocols = (
|
||||||
|
await this.lib2p2Peer.peerStore.get(this.lib2p2Peer.peerId)
|
||||||
|
).protocols;
|
||||||
|
|
||||||
await this.lib2p2Peer.peerStore.patch(this.lib2p2Peer.peerId, {
|
await this.lib2p2Peer.peerStore.patch(this.lib2p2Peer.peerId, {
|
||||||
protocols: [...supportedProtocols, PROTOCOL_NAME],
|
protocols: [...supportedProtocols, PROTOCOL_NAME],
|
||||||
});
|
});
|
||||||
@ -147,9 +165,12 @@ export class RelayConnection implements IConnection {
|
|||||||
await this.lib2p2Peer.stop();
|
await this.lib2p2Peer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendParticle(nextPeerIds: PeerIdB58[], particle: IParticle): Promise<void> {
|
async sendParticle(
|
||||||
|
nextPeerIds: PeerIdB58[],
|
||||||
|
particle: IParticle,
|
||||||
|
): Promise<void> {
|
||||||
if (this.lib2p2Peer === null) {
|
if (this.lib2p2Peer === null) {
|
||||||
throw new Error('Relay connection is not started');
|
throw new Error("Relay connection is not started");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextPeerIds.length !== 1 && nextPeerIds[0] !== this.getRelayPeerId()) {
|
if (nextPeerIds.length !== 1 && nextPeerIds[0] !== this.getRelayPeerId()) {
|
||||||
@ -160,29 +181,44 @@ export class RelayConnection implements IConnection {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.trace('sending particle...');
|
log.trace("sending particle...");
|
||||||
|
|
||||||
// Reusing active connection here
|
// Reusing active connection here
|
||||||
const stream = await this.lib2p2Peer.dialProtocol(this.relayAddress, PROTOCOL_NAME);
|
const stream = await this.lib2p2Peer.dialProtocol(
|
||||||
log.trace('created stream with id ', stream.id);
|
this.relayAddress,
|
||||||
|
PROTOCOL_NAME,
|
||||||
|
);
|
||||||
|
|
||||||
|
log.trace("created stream with id ", stream.id);
|
||||||
const sink = stream.sink;
|
const sink = stream.sink;
|
||||||
|
|
||||||
await pipe([fromString(serializeToString(particle))], encode(), sink);
|
await pipe([fromString(serializeToString(particle))], encode(), sink);
|
||||||
log.trace('data written to sink');
|
log.trace("data written to sink");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Await will appear after uncommenting lines in func body
|
||||||
|
// eslint-disable-next-line @typescript-eslint/require-await
|
||||||
private async processIncomingMessage(msg: string, stream: Stream) {
|
private async processIncomingMessage(msg: string, stream: Stream) {
|
||||||
let particle: Particle | undefined;
|
let particle: Particle | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
particle = Particle.fromString(msg);
|
particle = Particle.fromString(msg);
|
||||||
log.trace('got particle from stream with id %s and particle id %s', stream.id, particle.id);
|
|
||||||
|
log.trace(
|
||||||
|
"got particle from stream with id %s and particle id %s",
|
||||||
|
stream.id,
|
||||||
|
particle.id,
|
||||||
|
);
|
||||||
|
|
||||||
const initPeerId = peerIdFromString(particle.initPeerId);
|
const initPeerId = peerIdFromString(particle.initPeerId);
|
||||||
|
|
||||||
if (initPeerId.publicKey === undefined) {
|
if (initPeerId.publicKey === undefined) {
|
||||||
log.error(
|
log.error(
|
||||||
'cannot retrieve public key from init_peer_id. particle id: %s. init_peer_id: %s',
|
"cannot retrieve public key from init_peer_id. particle id: %s. init_peer_id: %s",
|
||||||
particle.id,
|
particle.id,
|
||||||
particle.initPeerId,
|
particle.initPeerId,
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,57 +227,67 @@ export class RelayConnection implements IConnection {
|
|||||||
buildParticleMessage(particle),
|
buildParticleMessage(particle),
|
||||||
particle.signature,
|
particle.signature,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isVerified) {
|
if (isVerified) {
|
||||||
this.particleSource.next(particle);
|
this.particleSource.next(particle);
|
||||||
} else {
|
} else {
|
||||||
log.trace('particle signature is incorrect. rejecting particle with id: %s', particle.id);
|
log.trace(
|
||||||
|
"particle signature is incorrect. rejecting particle with id: %s",
|
||||||
|
particle.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const particleId = particle?.id;
|
const particleId = particle?.id;
|
||||||
const particleIdMessage = typeof particleId === 'string' ? `. particle id: ${particleId}` : '';
|
|
||||||
log.error(`error on handling an incoming message: %O%s`, e, particleIdMessage);
|
const particleIdMessage =
|
||||||
|
typeof particleId === "string" ? `. particle id: ${particleId}` : "";
|
||||||
|
|
||||||
|
log.error(
|
||||||
|
`error on handling an incoming message: %O%s`,
|
||||||
|
e,
|
||||||
|
particleIdMessage,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async connect() {
|
private async connect() {
|
||||||
if (this.lib2p2Peer === null) {
|
if (this.lib2p2Peer === null) {
|
||||||
throw new Error('Relay connection is not started');
|
throw new Error("Relay connection is not started");
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.lib2p2Peer.handle(
|
await this.lib2p2Peer.handle(
|
||||||
[PROTOCOL_NAME],
|
[PROTOCOL_NAME],
|
||||||
async ({ connection, stream }) =>
|
({ stream }) => {
|
||||||
pipe(
|
void pipe(
|
||||||
stream.source,
|
stream.source,
|
||||||
decode(),
|
decode(),
|
||||||
(source) => map(source, (buf) => toString(buf.subarray())),
|
(source) => {
|
||||||
|
return map(source, (buf) => {
|
||||||
|
return toString(buf.subarray());
|
||||||
|
});
|
||||||
|
},
|
||||||
async (source) => {
|
async (source) => {
|
||||||
try {
|
try {
|
||||||
for await (const msg of source) {
|
for await (const msg of source) {
|
||||||
await this.processIncomingMessage(msg, stream);
|
await this.processIncomingMessage(msg, stream);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('connection closed: %j', e);
|
log.error("connection closed: %j", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
);
|
||||||
|
},
|
||||||
{
|
{
|
||||||
maxInboundStreams: this.config.maxInboundStreams,
|
maxInboundStreams: this.config.maxInboundStreams,
|
||||||
maxOutboundStreams: this.config.maxOutboundStreams,
|
maxOutboundStreams: this.config.maxOutboundStreams,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
log.debug("dialing to the node with client's address: %s", this.lib2p2Peer.peerId.toString());
|
log.debug(
|
||||||
|
"dialing to the node with client's address: %s",
|
||||||
|
this.lib2p2Peer.peerId.toString(),
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
|
||||||
await this.lib2p2Peer.dial(this.relayAddress);
|
await this.lib2p2Peer.dial(this.relayAddress);
|
||||||
} catch (e: any) {
|
|
||||||
if (e.name === 'AggregateError' && e._errors?.length === 1) {
|
|
||||||
const error = e._errors[0];
|
|
||||||
throw new Error(`Error dialing node ${this.relayAddress}:\n${error.code}\n${error.message}`);
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,10 +13,12 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import type { Subscribable } from 'rxjs';
|
import type { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
import type { Subscribable } from "rxjs";
|
||||||
import { IStartable } from '../util/commonTypes.js';
|
|
||||||
|
import { IParticle } from "../particle/interfaces.js";
|
||||||
|
import { IStartable } from "../util/commonTypes.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for connection used in Fluence Peer.
|
* Interface for connection used in Fluence Peer.
|
||||||
|
@ -1,17 +1,33 @@
|
|||||||
import { it, describe, expect, beforeEach, afterEach } from 'vitest';
|
/**
|
||||||
import { DEFAULT_CONFIG, FluencePeer } from '../../jsPeer/FluencePeer.js';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import { CallServiceData, ResultCodes } from '../../jsServiceHost/interfaces.js';
|
*
|
||||||
import { KeyPair } from '../../keypair/index.js';
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
import { EphemeralNetworkClient } from '../client.js';
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import { EphemeralNetwork, defaultConfig } from '../network.js';
|
import { it, describe, expect, beforeEach, afterEach } from "vitest";
|
||||||
|
|
||||||
|
import { DEFAULT_CONFIG, FluencePeer } from "../../jsPeer/FluencePeer.js";
|
||||||
|
import { ResultCodes } from "../../jsServiceHost/interfaces.js";
|
||||||
|
import { KeyPair } from "../../keypair/index.js";
|
||||||
|
import { EphemeralNetworkClient } from "../client.js";
|
||||||
|
import { EphemeralNetwork, defaultConfig } from "../network.js";
|
||||||
|
|
||||||
let en: EphemeralNetwork;
|
let en: EphemeralNetwork;
|
||||||
let client: FluencePeer;
|
let client: FluencePeer;
|
||||||
const relay = defaultConfig.peers[0].peerId;
|
const relay = defaultConfig.peers[0].peerId;
|
||||||
|
|
||||||
// TODO: race condition here. Needs to be fixed
|
// TODO: race condition here. Needs to be fixed
|
||||||
describe.skip('Ephemeral networks tests', () => {
|
describe.skip("Ephemeral networks tests", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
en = new EphemeralNetwork(defaultConfig);
|
en = new EphemeralNetwork(defaultConfig);
|
||||||
await en.up();
|
await en.up();
|
||||||
@ -22,17 +38,15 @@ describe.skip('Ephemeral networks tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
if (client) {
|
|
||||||
await client.stop();
|
await client.stop();
|
||||||
}
|
|
||||||
if (en) {
|
|
||||||
await en.down();
|
await en.down();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('smoke test', async function () {
|
it("smoke test", async function () {
|
||||||
// arrange
|
// arrange
|
||||||
const peers = defaultConfig.peers.map((x) => x.peerId);
|
const peers = defaultConfig.peers.map((x) => {
|
||||||
|
return x.peerId;
|
||||||
|
});
|
||||||
|
|
||||||
const script = `
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
@ -62,19 +76,24 @@ describe.skip('Ephemeral networks tests', () => {
|
|||||||
const particle = await client.internals.createNewParticle(script);
|
const particle = await client.internals.createNewParticle(script);
|
||||||
|
|
||||||
const promise = new Promise<string>((resolve) => {
|
const promise = new Promise<string>((resolve) => {
|
||||||
client.internals.regHandler.forParticle(particle.id, 'test', 'test', (req: CallServiceData) => {
|
client.internals.regHandler.forParticle(
|
||||||
resolve('success');
|
particle.id,
|
||||||
|
"test",
|
||||||
|
"test",
|
||||||
|
() => {
|
||||||
|
resolve("success");
|
||||||
return {
|
return {
|
||||||
result: 'test',
|
result: "test",
|
||||||
retCode: ResultCodes.success,
|
retCode: ResultCodes.success,
|
||||||
};
|
};
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// act
|
// act
|
||||||
client.internals.initiateParticle(particle, () => {});
|
client.internals.initiateParticle(particle, () => {});
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
await expect(promise).resolves.toBe('success');
|
await expect(promise).resolves.toBe("success");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,24 +13,46 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import { FluencePeer, PeerConfig } from '../jsPeer/FluencePeer.js';
|
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
import { KeyPair } from '../keypair/index.js';
|
|
||||||
import { WasmLoaderFromNpm } from '../marine/deps-loader/node.js';
|
import { FluencePeer, PeerConfig } from "../jsPeer/FluencePeer.js";
|
||||||
import { WorkerLoader } from '../marine/worker-script/workerLoader.js';
|
import { JsServiceHost } from "../jsServiceHost/JsServiceHost.js";
|
||||||
import { MarineBackgroundRunner } from '../marine/worker/index.js';
|
import { KeyPair } from "../keypair/index.js";
|
||||||
import { EphemeralNetwork } from './network.js';
|
import { WasmLoaderFromNpm } from "../marine/deps-loader/node.js";
|
||||||
import { JsServiceHost } from '../jsServiceHost/JsServiceHost.js';
|
import { MarineBackgroundRunner } from "../marine/worker/index.js";
|
||||||
|
import { WorkerLoader } from "../marine/worker-script/workerLoader.js";
|
||||||
|
|
||||||
|
import { EphemeralNetwork } from "./network.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ephemeral network client is a FluencePeer that connects to a relay peer in an ephemeral network.
|
* Ephemeral network client is a FluencePeer that connects to a relay peer in an ephemeral network.
|
||||||
*/
|
*/
|
||||||
export class EphemeralNetworkClient extends FluencePeer {
|
export class EphemeralNetworkClient extends FluencePeer {
|
||||||
constructor(config: PeerConfig, keyPair: KeyPair, network: EphemeralNetwork, relay: PeerIdB58) {
|
constructor(
|
||||||
|
config: PeerConfig,
|
||||||
|
keyPair: KeyPair,
|
||||||
|
network: EphemeralNetwork,
|
||||||
|
relay: PeerIdB58,
|
||||||
|
) {
|
||||||
const workerLoader = new WorkerLoader();
|
const workerLoader = new WorkerLoader();
|
||||||
const controlModuleLoader = new WasmLoaderFromNpm('@fluencelabs/marine-js', 'marine-js.wasm');
|
|
||||||
const avmModuleLoader = new WasmLoaderFromNpm('@fluencelabs/avm', 'avm.wasm');
|
const controlModuleLoader = new WasmLoaderFromNpm(
|
||||||
const marine = new MarineBackgroundRunner(workerLoader, controlModuleLoader, avmModuleLoader);
|
"@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);
|
const conn = network.getRelayConnection(keyPair.getPeerId(), relay);
|
||||||
super(config, keyPair, marine, new JsServiceHost(), conn);
|
super(config, keyPair, marine, new JsServiceHost(), conn);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,23 +13,24 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import { fromBase64Sk, KeyPair } from '../keypair/index.js';
|
|
||||||
import { MarineBackgroundRunner } from '../marine/worker/index.js';
|
|
||||||
|
|
||||||
import { WorkerLoaderFromFs } from '../marine/deps-loader/node.js';
|
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
|
import { Subject } from "rxjs";
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
import { IConnection } from "../connection/interfaces.js";
|
||||||
import { Subject } from 'rxjs';
|
import { DEFAULT_CONFIG, FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
import { Particle } from '../particle/Particle.js';
|
import { JsServiceHost } from "../jsServiceHost/JsServiceHost.js";
|
||||||
|
import { fromBase64Sk, KeyPair } from "../keypair/index.js";
|
||||||
|
import {
|
||||||
|
WorkerLoaderFromFs,
|
||||||
|
WasmLoaderFromNpm,
|
||||||
|
} from "../marine/deps-loader/node.js";
|
||||||
|
import { IMarineHost } from "../marine/interfaces.js";
|
||||||
|
import { MarineBackgroundRunner } from "../marine/worker/index.js";
|
||||||
|
import { Particle } from "../particle/Particle.js";
|
||||||
|
import { logger } from "../util/logger.js";
|
||||||
|
|
||||||
import { WasmLoaderFromNpm } from '../marine/deps-loader/node.js';
|
const log = logger("ephemeral");
|
||||||
import { DEFAULT_CONFIG, FluencePeer } from '../jsPeer/FluencePeer.js';
|
|
||||||
import { IConnection } from '../connection/interfaces.js';
|
|
||||||
import { IAvmRunner, IMarineHost } from '../marine/interfaces.js';
|
|
||||||
import { JsServiceHost } from '../jsServiceHost/JsServiceHost.js';
|
|
||||||
|
|
||||||
const log = logger('ephemeral');
|
|
||||||
|
|
||||||
interface EphemeralConfig {
|
interface EphemeralConfig {
|
||||||
peers: Array<{
|
peers: Array<{
|
||||||
@ -41,84 +42,84 @@ interface EphemeralConfig {
|
|||||||
export const defaultConfig = {
|
export const defaultConfig = {
|
||||||
peers: [
|
peers: [
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWJankP2PcEDYCZDdJ26JsU8BMRfdGWyGqbtFiWyoKVtmx',
|
peerId: "12D3KooWJankP2PcEDYCZDdJ26JsU8BMRfdGWyGqbtFiWyoKVtmx",
|
||||||
sk: 'dWNAHhDVuFj9bEieILMu6TcCFRxBJdOPIvAWmf4sZQI=',
|
sk: "dWNAHhDVuFj9bEieILMu6TcCFRxBJdOPIvAWmf4sZQI=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWSBTB5sYxdwayUyTnqopBwABsnGFY3p4dTx5hABYDtJjV',
|
peerId: "12D3KooWSBTB5sYxdwayUyTnqopBwABsnGFY3p4dTx5hABYDtJjV",
|
||||||
sk: 'dOmaxAeu4Th+MJ22vRDLMFTNbiDgKNXar9fW9ofAMgQ=',
|
sk: "dOmaxAeu4Th+MJ22vRDLMFTNbiDgKNXar9fW9ofAMgQ=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWQjwf781DJ41moW5RrZXypLdnTbo6aMsoA8QLctGGX8RB',
|
peerId: "12D3KooWQjwf781DJ41moW5RrZXypLdnTbo6aMsoA8QLctGGX8RB",
|
||||||
sk: 'TgzaLlxXuOMDNuuuTKEHUKsW0jM4AmX0gahFvkB1KgE=',
|
sk: "TgzaLlxXuOMDNuuuTKEHUKsW0jM4AmX0gahFvkB1KgE=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWCXWTLFyY1mqKnNAhLQTsjW1zqDzCMbUs8M4a8zdz28HK',
|
peerId: "12D3KooWCXWTLFyY1mqKnNAhLQTsjW1zqDzCMbUs8M4a8zdz28HK",
|
||||||
sk: 'hiO2Ta8g2ibMQ7iu5yj9CfN+qQCwE8oRShjr7ortKww=',
|
sk: "hiO2Ta8g2ibMQ7iu5yj9CfN+qQCwE8oRShjr7ortKww=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWPmZpf4ng6GMS39HLagxsXbjiTPLH5CFJpFAHyN6amw6V',
|
peerId: "12D3KooWPmZpf4ng6GMS39HLagxsXbjiTPLH5CFJpFAHyN6amw6V",
|
||||||
sk: 'LzJtOHTqxfrlHDW40BKiLfjai8JU4yW6/s2zrXLCcQE=',
|
sk: "LzJtOHTqxfrlHDW40BKiLfjai8JU4yW6/s2zrXLCcQE=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWKrx8PZxM1R9A8tp2jmrFf6c6q1ZQiWfD4QkNgh7fWSoF',
|
peerId: "12D3KooWKrx8PZxM1R9A8tp2jmrFf6c6q1ZQiWfD4QkNgh7fWSoF",
|
||||||
sk: 'XMhlk/xr1FPcp7sKQhS18doXlq1x16EMhBC2NGW2LQ4=',
|
sk: "XMhlk/xr1FPcp7sKQhS18doXlq1x16EMhBC2NGW2LQ4=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWCbJHvnzSZEXjR1UJmtSUozuJK13iRiCYHLN1gjvm4TZZ',
|
peerId: "12D3KooWCbJHvnzSZEXjR1UJmtSUozuJK13iRiCYHLN1gjvm4TZZ",
|
||||||
sk: 'KXPAIqxrSHr7v0ngv3qagcqivFvnQ0xd3s1/rKmi8QU=',
|
sk: "KXPAIqxrSHr7v0ngv3qagcqivFvnQ0xd3s1/rKmi8QU=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWEvKe7WQHp42W4xhHRgTAWQjtDWyH38uJbLHAsMuTtYvD',
|
peerId: "12D3KooWEvKe7WQHp42W4xhHRgTAWQjtDWyH38uJbLHAsMuTtYvD",
|
||||||
sk: 'GCYMAshGnsrNtrHhuT7ayzh5uCzX99J03PmAXoOcCgw=',
|
sk: "GCYMAshGnsrNtrHhuT7ayzh5uCzX99J03PmAXoOcCgw=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWSznSHN3BGrSykBXkLkFsqo9SYB73wVauVdqeuRt562cC',
|
peerId: "12D3KooWSznSHN3BGrSykBXkLkFsqo9SYB73wVauVdqeuRt562cC",
|
||||||
sk: 'UP+SEuznS0h259VbFquzyOJAQ4W5iIwhP+hd1PmUQQ0=',
|
sk: "UP+SEuznS0h259VbFquzyOJAQ4W5iIwhP+hd1PmUQQ0=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWF57jwbShfnT3c4dNfRDdGjr6SQ3B71m87UVpEpSWHFwi',
|
peerId: "12D3KooWF57jwbShfnT3c4dNfRDdGjr6SQ3B71m87UVpEpSWHFwi",
|
||||||
sk: '8dl+Crm5RSh0eh+LqLKwX8/Eo4QLpvIjfD8L0wzX4A4=',
|
sk: "8dl+Crm5RSh0eh+LqLKwX8/Eo4QLpvIjfD8L0wzX4A4=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWBWrzpSg9nwMLBCa2cJubUjTv63Mfy6PYg9rHGbetaV5C',
|
peerId: "12D3KooWBWrzpSg9nwMLBCa2cJubUjTv63Mfy6PYg9rHGbetaV5C",
|
||||||
sk: 'qolc1FcpJ+vHDon0HeXdUYnstjV1wiVx2p0mjblrfAg=',
|
sk: "qolc1FcpJ+vHDon0HeXdUYnstjV1wiVx2p0mjblrfAg=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWNkLVU6juM8oyN2SVq5nBd2kp7Rf4uzJH1hET6vj6G5j6',
|
peerId: "12D3KooWNkLVU6juM8oyN2SVq5nBd2kp7Rf4uzJH1hET6vj6G5j6",
|
||||||
sk: 'vN6QzWILTM7hSHp+iGkKxiXcqs8bzlnH3FPaRaDGSQY=',
|
sk: "vN6QzWILTM7hSHp+iGkKxiXcqs8bzlnH3FPaRaDGSQY=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWKo1YwGL5vivPiKJMJS7wjtB6B2nJNdSXPkSABT4NKBUU',
|
peerId: "12D3KooWKo1YwGL5vivPiKJMJS7wjtB6B2nJNdSXPkSABT4NKBUU",
|
||||||
sk: 'YbDQ++bsor2kei7rYAsu2SbyoiOYPRzFRZWnNRUpBgQ=',
|
sk: "YbDQ++bsor2kei7rYAsu2SbyoiOYPRzFRZWnNRUpBgQ=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWLUyBKmmNCyxaPkXoWcUFPcy5qrZsUo2E1tyM6CJmGJvC',
|
peerId: "12D3KooWLUyBKmmNCyxaPkXoWcUFPcy5qrZsUo2E1tyM6CJmGJvC",
|
||||||
sk: 'ptB9eSFMKudAtHaFgDrRK/1oIMrhBujxbMw2Pzwx/wA=',
|
sk: "ptB9eSFMKudAtHaFgDrRK/1oIMrhBujxbMw2Pzwx/wA=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWAEZXME4KMu9FvLezsJWDbYFe2zyujyMnDT1AgcAxgcCk',
|
peerId: "12D3KooWAEZXME4KMu9FvLezsJWDbYFe2zyujyMnDT1AgcAxgcCk",
|
||||||
sk: 'xtwTOKgAbDIgkuPf7RKiR7gYyZ1HY4mOgFMv3sOUcAQ=',
|
sk: "xtwTOKgAbDIgkuPf7RKiR7gYyZ1HY4mOgFMv3sOUcAQ=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWEhXetsFVAD9h2dRz9XgFpfidho1TCZVhFrczX8h8qgzY',
|
peerId: "12D3KooWEhXetsFVAD9h2dRz9XgFpfidho1TCZVhFrczX8h8qgzY",
|
||||||
sk: '1I2MGuiKG1F4FDMiRihVOcOP2mxzOLWJ99MeexK27A4=',
|
sk: "1I2MGuiKG1F4FDMiRihVOcOP2mxzOLWJ99MeexK27A4=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWDBfVNdMyV3hPEF4WLBmx9DwD2t2SYuqZ2mztYmDzZWM1',
|
peerId: "12D3KooWDBfVNdMyV3hPEF4WLBmx9DwD2t2SYuqZ2mztYmDzZWM1",
|
||||||
sk: 'eqJ4Bp7iN4aBXgPH0ezwSg+nVsatkYtfrXv9obI0YQ0=',
|
sk: "eqJ4Bp7iN4aBXgPH0ezwSg+nVsatkYtfrXv9obI0YQ0=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWSyY7wiSiR4vbXa1WtZawi3ackMTqcQhEPrvqtagoWPny',
|
peerId: "12D3KooWSyY7wiSiR4vbXa1WtZawi3ackMTqcQhEPrvqtagoWPny",
|
||||||
sk: 'UVM3SBJhPYIY/gafpnd9/q/Fn9V4BE9zkgrvF1T7Pgc=',
|
sk: "UVM3SBJhPYIY/gafpnd9/q/Fn9V4BE9zkgrvF1T7Pgc=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWFZmBMGG9PxTs9s6ASzkLGKJWMyPheA5ruaYc2FDkDTmv',
|
peerId: "12D3KooWFZmBMGG9PxTs9s6ASzkLGKJWMyPheA5ruaYc2FDkDTmv",
|
||||||
sk: '8RbZfEVpQhPVuhv64uqxENDuSoyJrslQoSQJznxsTQ0=',
|
sk: "8RbZfEVpQhPVuhv64uqxENDuSoyJrslQoSQJznxsTQ0=",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
peerId: '12D3KooWBbhUaqqur6KHPunnKxXjY1daCtqJdy4wRji89LmAkVB4',
|
peerId: "12D3KooWBbhUaqqur6KHPunnKxXjY1daCtqJdy4wRji89LmAkVB4",
|
||||||
sk: 'RbgKmG6soWW9uOi7yRedm+0Qck3f3rw6MSnDP7AcBQs=',
|
sk: "RbgKmG6soWW9uOi7yRedm+0Qck3f3rw6MSnDP7AcBQs=",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -160,7 +161,7 @@ export class EphemeralConnection implements IEphemeralConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
disconnectFromAll() {
|
disconnectFromAll() {
|
||||||
for (let other of this.connections.values()) {
|
for (const other of this.connections.values()) {
|
||||||
this.disconnectFromOther(other);
|
this.disconnectFromOther(other);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,29 +169,36 @@ export class EphemeralConnection implements IEphemeralConnection {
|
|||||||
particleSource = new Subject<Particle>();
|
particleSource = new Subject<Particle>();
|
||||||
|
|
||||||
receiveParticle(particle: Particle): void {
|
receiveParticle(particle: Particle): void {
|
||||||
this.particleSource.next(Particle.fromString(particle.toString()));
|
this.particleSource.next(particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendParticle(nextPeerIds: string[], particle: Particle): Promise<void> {
|
sendParticle(nextPeerIds: string[], particle: Particle): Promise<void> {
|
||||||
const from = this.selfPeerId;
|
const from = this.selfPeerId;
|
||||||
for (let to of nextPeerIds) {
|
|
||||||
|
for (const to of nextPeerIds) {
|
||||||
const destConnection = this.connections.get(to);
|
const destConnection = this.connections.get(to);
|
||||||
|
|
||||||
if (destConnection === undefined) {
|
if (destConnection === undefined) {
|
||||||
log.error('peer %s has no connection with %s', from, to);
|
log.error("peer %s has no connection with %s", from, to);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// log.trace(`Sending particle from %s, to %j, particleId %s`, from, to, particle.id);
|
// log.trace(`Sending particle from %s, to %j, particleId %s`, from, to, particle.id);
|
||||||
destConnection.receiveParticle(particle);
|
destConnection.receiveParticle(particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
getRelayPeerId(): string {
|
getRelayPeerId(): string {
|
||||||
if (this.connections.size === 1) {
|
const firstMapKey = this.connections.keys().next();
|
||||||
return this.connections.keys().next().value;
|
|
||||||
|
// Empty map
|
||||||
|
if (firstMapKey.done === true) {
|
||||||
|
throw new Error("relay is not supported in this Ephemeral network peer");
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('relay is not supported in this Ephemeral network peer');
|
return firstMapKey.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
supportsRelay(): boolean {
|
supportsRelay(): boolean {
|
||||||
@ -220,25 +228,42 @@ export class EphemeralNetwork {
|
|||||||
controlModuleLoader: WasmLoaderFromNpm;
|
controlModuleLoader: WasmLoaderFromNpm;
|
||||||
avmModuleLoader: WasmLoaderFromNpm;
|
avmModuleLoader: WasmLoaderFromNpm;
|
||||||
|
|
||||||
constructor(public readonly config: EphemeralConfig) {
|
constructor(readonly config: EphemeralConfig) {
|
||||||
// shared worker for all the peers
|
// shared worker for all the peers
|
||||||
this.workerLoader = new WorkerLoaderFromFs('../../marine/worker-script');
|
this.workerLoader = new WorkerLoaderFromFs("../../marine/worker-script");
|
||||||
this.controlModuleLoader = new WasmLoaderFromNpm('@fluencelabs/marine-js', 'marine-js.wasm');
|
|
||||||
this.avmModuleLoader = new WasmLoaderFromNpm('@fluencelabs/avm', 'avm.wasm');
|
this.controlModuleLoader = new WasmLoaderFromNpm(
|
||||||
|
"@fluencelabs/marine-js",
|
||||||
|
"marine-js.wasm",
|
||||||
|
);
|
||||||
|
|
||||||
|
this.avmModuleLoader = new WasmLoaderFromNpm(
|
||||||
|
"@fluencelabs/avm",
|
||||||
|
"avm.wasm",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the Ephemeral network up
|
* Starts the Ephemeral network up
|
||||||
*/
|
*/
|
||||||
async up(): Promise<void> {
|
async up(): Promise<void> {
|
||||||
log.trace('starting ephemeral network up...');
|
log.trace("starting ephemeral network up...");
|
||||||
|
|
||||||
const promises = this.config.peers.map(async (x) => {
|
const promises = this.config.peers.map(async (x) => {
|
||||||
const kp = await fromBase64Sk(x.sk);
|
const kp = await fromBase64Sk(x.sk);
|
||||||
const marine = new MarineBackgroundRunner(this.workerLoader, this.controlModuleLoader, this.avmModuleLoader);
|
|
||||||
|
const marine = new MarineBackgroundRunner(
|
||||||
|
this.workerLoader,
|
||||||
|
this.controlModuleLoader,
|
||||||
|
this.avmModuleLoader,
|
||||||
|
);
|
||||||
|
|
||||||
const peerId = kp.getPeerId();
|
const peerId = kp.getPeerId();
|
||||||
|
|
||||||
if (peerId !== x.peerId) {
|
if (peerId !== x.peerId) {
|
||||||
throw new Error(`Invalid config: peer id ${x.peerId} does not match the secret key ${x.sk}`);
|
throw new Error(
|
||||||
|
`Invalid config: peer id ${x.peerId} does not match the secret key ${x.sk}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new EphemeralPeer(kp, marine);
|
return new EphemeralPeer(kp, marine);
|
||||||
@ -252,14 +277,19 @@ export class EphemeralNetwork {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
peers[i].ephemeralConnection.connectToOther(peers[j].ephemeralConnection);
|
peers[i].ephemeralConnection.connectToOther(
|
||||||
|
peers[j].ephemeralConnection,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const startPromises = peers.map((x) => x.start());
|
const startPromises = peers.map((x) => {
|
||||||
|
return x.start();
|
||||||
|
});
|
||||||
|
|
||||||
await Promise.all(startPromises);
|
await Promise.all(startPromises);
|
||||||
|
|
||||||
for (let p of peers) {
|
for (const p of peers) {
|
||||||
this.peers.set(p.keyPair.getPeerId(), p);
|
this.peers.set(p.keyPair.getPeerId(), p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,16 +298,17 @@ export class EphemeralNetwork {
|
|||||||
* Shuts the ephemeral network down. Will disconnect all connected peers.
|
* Shuts the ephemeral network down. Will disconnect all connected peers.
|
||||||
*/
|
*/
|
||||||
async down(): Promise<void> {
|
async down(): Promise<void> {
|
||||||
log.trace('shutting down ephemeral network...');
|
log.trace("shutting down ephemeral network...");
|
||||||
const peers = Array.from(this.peers.entries());
|
const peers = Array.from(this.peers.entries());
|
||||||
const promises = peers.map(async ([k, p]) => {
|
|
||||||
await p.ephemeralConnection.disconnectFromAll();
|
const promises = peers.map(async ([, p]) => {
|
||||||
|
p.ephemeralConnection.disconnectFromAll();
|
||||||
await p.stop();
|
await p.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
this.peers.clear();
|
this.peers.clear();
|
||||||
log.trace('ephemeral network shut down');
|
log.trace("ephemeral network shut down");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -285,6 +316,7 @@ export class EphemeralNetwork {
|
|||||||
*/
|
*/
|
||||||
getRelayConnection(peerId: PeerIdB58, relayPeerId: PeerIdB58): IConnection {
|
getRelayConnection(peerId: PeerIdB58, relayPeerId: PeerIdB58): IConnection {
|
||||||
const relay = this.peers.get(relayPeerId);
|
const relay = this.peers.get(relayPeerId);
|
||||||
|
|
||||||
if (relay === undefined) {
|
if (relay === undefined) {
|
||||||
throw new Error(`Peer ${relayPeerId} is not found`);
|
throw new Error(`Peer ${relayPeerId} is not found`);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -21,20 +21,41 @@ interface PackageJsonContent {
|
|||||||
|
|
||||||
// This will be substituted in build phase
|
// This will be substituted in build phase
|
||||||
const packageJsonContentString = `__PACKAGE_JSON_CONTENT__`;
|
const packageJsonContentString = `__PACKAGE_JSON_CONTENT__`;
|
||||||
let parsedPackageJsonContent: PackageJsonContent;
|
let parsedPackageJsonContent: PackageJsonContent | undefined;
|
||||||
|
|
||||||
const PRIMARY_CDN = "https://unpkg.com/";
|
const PRIMARY_CDN = "https://unpkg.com/";
|
||||||
|
|
||||||
export async function fetchResource(pkg: string, assetPath: string) {
|
export async function fetchResource(pkg: string, assetPath: string) {
|
||||||
const packageJsonContent = parsedPackageJsonContent || (parsedPackageJsonContent = JSON.parse(packageJsonContentString));
|
const packageJsonContent =
|
||||||
const version = packageJsonContent.dependencies[pkg] || packageJsonContent.devDependencies[pkg];
|
parsedPackageJsonContent ??
|
||||||
|
// TODO: Should be validated
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
(parsedPackageJsonContent = JSON.parse(
|
||||||
|
packageJsonContentString,
|
||||||
|
) as PackageJsonContent);
|
||||||
|
|
||||||
|
const version =
|
||||||
|
packageJsonContent.dependencies[pkg] ??
|
||||||
|
packageJsonContent.devDependencies[pkg];
|
||||||
|
|
||||||
if (version === undefined) {
|
if (version === undefined) {
|
||||||
const availableDeps = [...Object.keys(packageJsonContent.dependencies), ...Object.keys(packageJsonContent.devDependencies)];
|
const availableDeps = [
|
||||||
throw new Error(`Cannot find version of ${pkg} in package.json. Available versions: ${availableDeps.join(',')}`);
|
...Object.keys(packageJsonContent.dependencies),
|
||||||
|
...Object.keys(packageJsonContent.devDependencies),
|
||||||
|
];
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Cannot find version of ${pkg} in package.json. Available versions: ${availableDeps.join(
|
||||||
|
",",
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const refinedAssetPath = assetPath.startsWith('/') ? assetPath.slice(1) : assetPath;
|
const refinedAssetPath = assetPath.startsWith("/")
|
||||||
|
? assetPath.slice(1)
|
||||||
|
: assetPath;
|
||||||
|
|
||||||
return fetch(new globalThis.URL(`${pkg}@${version}/` + refinedAssetPath, PRIMARY_CDN));
|
return fetch(
|
||||||
|
new globalThis.URL(`${pkg}@${version}/` + refinedAssetPath, PRIMARY_CDN),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,17 +14,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fetchResource as fetchResourceBrowser } from './browser.js';
|
import process from "process";
|
||||||
import { fetchResource as fetchResourceNode } from './node.js';
|
|
||||||
import process from 'process';
|
|
||||||
|
|
||||||
const isNode = typeof process !== 'undefined' && process?.release?.name === 'node';
|
import { fetchResource as fetchResourceIsomorphic } from "#fetcher";
|
||||||
|
|
||||||
|
const isNode =
|
||||||
|
// process.release is undefined in browser env
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
typeof process !== "undefined" && process.release?.name === "node";
|
||||||
|
|
||||||
export async function fetchResource(pkg: string, path: string) {
|
export async function fetchResource(pkg: string, path: string) {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case isNode:
|
case isNode:
|
||||||
return fetchResourceNode(pkg, path);
|
return fetchResourceIsomorphic(pkg, path);
|
||||||
default:
|
default:
|
||||||
return fetchResourceBrowser(pkg, path);
|
return fetchResourceIsomorphic(pkg, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,9 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import path from 'path';
|
import module from "module";
|
||||||
import module from 'module';
|
import path from "path";
|
||||||
|
|
||||||
export async function fetchResource(pkg: string, assetPath: string) {
|
export async function fetchResource(pkg: string, assetPath: string) {
|
||||||
const require = module.createRequire(import.meta.url);
|
const require = module.createRequire(import.meta.url);
|
||||||
@ -29,7 +29,7 @@ export async function fetchResource(pkg: string, assetPath: string) {
|
|||||||
|
|
||||||
const packagePath = matches?.[0];
|
const packagePath = matches?.[0];
|
||||||
|
|
||||||
if (!packagePath) {
|
if (packagePath == null) {
|
||||||
throw new Error(`Cannot find dependency ${pkg} in path ${posixPath}`);
|
throw new Error(`Cannot find dependency ${pkg} in path ${posixPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,22 +38,22 @@ export async function fetchResource(pkg: string, assetPath: string) {
|
|||||||
const file = await new Promise<ArrayBuffer>((resolve, reject) => {
|
const file = await new Promise<ArrayBuffer>((resolve, reject) => {
|
||||||
// Cannot use 'fs/promises' with current vite config. This module is not polyfilled by default.
|
// Cannot use 'fs/promises' with current vite config. This module is not polyfilled by default.
|
||||||
fs.readFile(pathToResource, (err, data) => {
|
fs.readFile(pathToResource, (err, data) => {
|
||||||
if (err) {
|
if (err != null) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(data);
|
resolve(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Response(file, {
|
return new Response(file, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-type':
|
"Content-type": assetPath.endsWith(".wasm")
|
||||||
assetPath.endsWith('.wasm')
|
? "application/wasm"
|
||||||
? 'application/wasm'
|
: assetPath.endsWith(".js")
|
||||||
: assetPath.endsWith('.js')
|
? "application/javascript"
|
||||||
? 'application/javascript'
|
: "application/text",
|
||||||
: 'application/text'
|
},
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,41 +13,82 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { ClientConfig, IFluenceClient, RelayOptions, ConnectionState, CallAquaFunctionType, RegisterServiceType } from '@fluencelabs/interfaces';
|
|
||||||
import { ClientPeer, makeClientPeerConfig } from './clientPeer/ClientPeer.js';
|
|
||||||
import { callAquaFunction } from './compilerSupport/callFunction.js';
|
|
||||||
import { registerService } from './compilerSupport/registerService.js';
|
|
||||||
import { MarineBackgroundRunner } from './marine/worker/index.js';
|
|
||||||
// @ts-ignore
|
|
||||||
import { BlobWorker, Worker } from 'threads';
|
|
||||||
import { doRegisterNodeUtils } from './services/NodeUtils.js';
|
|
||||||
import { fetchResource } from './fetchers/index.js';
|
|
||||||
import process from 'process';
|
|
||||||
import path from 'path';
|
|
||||||
import url from 'url';
|
|
||||||
import module from 'module';
|
|
||||||
|
|
||||||
const isNode = typeof process !== 'undefined' && process?.release?.name === 'node';
|
import module from "module";
|
||||||
|
import path from "path";
|
||||||
|
import process from "process";
|
||||||
|
import url from "url";
|
||||||
|
|
||||||
const fetchWorkerCode = () => fetchResource('@fluencelabs/marine-worker', '/dist/browser/marine-worker.umd.cjs').then(res => res.text());
|
import type {
|
||||||
const fetchMarineJsWasm = () => fetchResource('@fluencelabs/marine-js', '/dist/marine-js.wasm').then(res => res.arrayBuffer());
|
ClientConfig,
|
||||||
const fetchAvmWasm = () => fetchResource('@fluencelabs/avm', '/dist/avm.wasm').then(res => res.arrayBuffer());
|
ConnectionState,
|
||||||
|
RelayOptions,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
import { BlobWorker, Worker } from "threads/master";
|
||||||
|
|
||||||
const createClient = async (relay: RelayOptions, config: ClientConfig): Promise<IFluenceClient> => {
|
import { ClientPeer, makeClientPeerConfig } from "./clientPeer/ClientPeer.js";
|
||||||
|
import { callAquaFunction } from "./compilerSupport/callFunction.js";
|
||||||
|
import { registerService } from "./compilerSupport/registerService.js";
|
||||||
|
import { fetchResource } from "./fetchers/index.js";
|
||||||
|
import { MarineBackgroundRunner } from "./marine/worker/index.js";
|
||||||
|
import { doRegisterNodeUtils } from "./services/NodeUtils.js";
|
||||||
|
|
||||||
|
const isNode =
|
||||||
|
// process.release is undefined in browser env
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
typeof process !== "undefined" && process.release?.name === "node";
|
||||||
|
|
||||||
|
const fetchWorkerCode = async () => {
|
||||||
|
const resource = await fetchResource(
|
||||||
|
"@fluencelabs/marine-worker",
|
||||||
|
"/dist/browser/marine-worker.umd.cjs",
|
||||||
|
);
|
||||||
|
|
||||||
|
return resource.text();
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchMarineJsWasm = async () => {
|
||||||
|
const resource = await fetchResource(
|
||||||
|
"@fluencelabs/marine-js",
|
||||||
|
"/dist/marine-js.wasm",
|
||||||
|
);
|
||||||
|
|
||||||
|
return resource.arrayBuffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchAvmWasm = async () => {
|
||||||
|
const resource = await fetchResource("@fluencelabs/avm", "/dist/avm.wasm");
|
||||||
|
return resource.arrayBuffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
const createClient = async (
|
||||||
|
relay: RelayOptions,
|
||||||
|
config: ClientConfig,
|
||||||
|
): Promise<ClientPeer> => {
|
||||||
const marineJsWasm = await fetchMarineJsWasm();
|
const marineJsWasm = await fetchMarineJsWasm();
|
||||||
const avmWasm = await fetchAvmWasm();
|
const avmWasm = await fetchAvmWasm();
|
||||||
|
|
||||||
const marine = new MarineBackgroundRunner({
|
const marine = new MarineBackgroundRunner(
|
||||||
|
{
|
||||||
async getValue() {
|
async getValue() {
|
||||||
if (isNode) {
|
if (isNode) {
|
||||||
const require = module.createRequire(import.meta.url);
|
const require = module.createRequire(import.meta.url);
|
||||||
const pathToThisFile = path.dirname(url.fileURLToPath(import.meta.url));
|
|
||||||
const pathToWorker = require.resolve('@fluencelabs/marine-worker');
|
const pathToThisFile = path.dirname(
|
||||||
const relativePathToWorker = path.relative(pathToThisFile, pathToWorker);
|
url.fileURLToPath(import.meta.url),
|
||||||
|
);
|
||||||
|
|
||||||
|
const pathToWorker = require.resolve("@fluencelabs/marine-worker");
|
||||||
|
|
||||||
|
const relativePathToWorker = path.relative(
|
||||||
|
pathToThisFile,
|
||||||
|
pathToWorker,
|
||||||
|
);
|
||||||
|
|
||||||
return new Worker(relativePathToWorker);
|
return new Worker(relativePathToWorker);
|
||||||
} else {
|
} else {
|
||||||
const workerCode = await fetchWorkerCode();
|
const workerCode = await fetchWorkerCode();
|
||||||
return BlobWorker.fromText(workerCode)
|
return BlobWorker.fromText(workerCode);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
start() {
|
start() {
|
||||||
@ -56,28 +97,42 @@ const createClient = async (relay: RelayOptions, config: ClientConfig): Promise<
|
|||||||
stop() {
|
stop() {
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
getValue() {
|
getValue() {
|
||||||
return marineJsWasm;
|
return marineJsWasm;
|
||||||
}, start(): Promise<void> {
|
},
|
||||||
|
start(): Promise<void> {
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}, stop(): Promise<void> {
|
},
|
||||||
|
stop(): Promise<void> {
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
getValue() {
|
getValue() {
|
||||||
return avmWasm;
|
return avmWasm;
|
||||||
}, start(): Promise<void> {
|
},
|
||||||
|
start(): Promise<void> {
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}, stop(): Promise<void> {
|
},
|
||||||
|
stop(): Promise<void> {
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
},
|
||||||
});
|
},
|
||||||
const { keyPair, peerConfig, relayConfig } = await makeClientPeerConfig(relay, config);
|
);
|
||||||
const client: IFluenceClient = new ClientPeer(peerConfig, relayConfig, keyPair, marine);
|
|
||||||
|
const { keyPair, peerConfig, relayConfig } = await makeClientPeerConfig(
|
||||||
|
relay,
|
||||||
|
config,
|
||||||
|
);
|
||||||
|
|
||||||
|
const client = new ClientPeer(peerConfig, relayConfig, keyPair, marine);
|
||||||
|
|
||||||
if (isNode) {
|
if (isNode) {
|
||||||
doRegisterNodeUtils(client);
|
doRegisterNodeUtils(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.connect();
|
await client.connect();
|
||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
@ -85,22 +140,31 @@ const createClient = async (relay: RelayOptions, config: ClientConfig): Promise<
|
|||||||
/**
|
/**
|
||||||
* Public interface to Fluence Network
|
* Public interface to Fluence Network
|
||||||
*/
|
*/
|
||||||
export const Fluence = {
|
interface FluencePublicApi {
|
||||||
defaultClient: undefined as (IFluenceClient | undefined),
|
defaultClient: ClientPeer | undefined;
|
||||||
|
connect: (relay: RelayOptions, config: ClientConfig) => Promise<void>;
|
||||||
|
disconnect: () => Promise<void>;
|
||||||
|
onConnectionStateChange: (
|
||||||
|
handler: (state: ConnectionState) => void,
|
||||||
|
) => ConnectionState;
|
||||||
|
getClient: () => ClientPeer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Fluence: FluencePublicApi = {
|
||||||
|
defaultClient: undefined,
|
||||||
/**
|
/**
|
||||||
* Connect to the Fluence network
|
* Connect to the Fluence network
|
||||||
* @param relay - relay node to connect to
|
* @param relay - relay node to connect to
|
||||||
* @param config - client configuration
|
* @param config - client configuration
|
||||||
*/
|
*/
|
||||||
connect: async function(relay: RelayOptions, config: ClientConfig): Promise<void> {
|
connect: async function (relay, config) {
|
||||||
const client = await createClient(relay, config);
|
this.defaultClient = await createClient(relay, config);
|
||||||
this.defaultClient = client;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnect from the Fluence network
|
* Disconnect from the Fluence network
|
||||||
*/
|
*/
|
||||||
disconnect: async function(): Promise<void> {
|
disconnect: async function (): Promise<void> {
|
||||||
await this.defaultClient?.disconnect();
|
await this.defaultClient?.disconnect();
|
||||||
this.defaultClient = undefined;
|
this.defaultClient = undefined;
|
||||||
},
|
},
|
||||||
@ -108,23 +172,32 @@ export const Fluence = {
|
|||||||
/**
|
/**
|
||||||
* Handle connection state changes. Immediately returns the current connection state
|
* Handle connection state changes. Immediately returns the current connection state
|
||||||
*/
|
*/
|
||||||
onConnectionStateChange(handler: (state: ConnectionState) => void): ConnectionState {
|
onConnectionStateChange(handler) {
|
||||||
return this.defaultClient?.onConnectionStateChange(handler) || 'disconnected';
|
return (
|
||||||
|
this.defaultClient?.onConnectionStateChange(handler) ?? "disconnected"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Low level API. Get the underlying client instance which holds the connection to the network
|
* Low level API. Get the underlying client instance which holds the connection to the network
|
||||||
* @returns IFluenceClient instance
|
* @returns IFluenceClient instance
|
||||||
*/
|
*/
|
||||||
getClient: async function(): Promise<IFluenceClient> {
|
getClient: function () {
|
||||||
if (!this.defaultClient) {
|
if (this.defaultClient == null) {
|
||||||
throw new Error('Fluence client is not initialized. Call Fluence.connect() first');
|
throw new Error(
|
||||||
|
"Fluence client is not initialized. Call Fluence.connect() first",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.defaultClient;
|
return this.defaultClient;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type { IFluenceClient, ClientConfig, CallParams } from '@fluencelabs/interfaces';
|
export type {
|
||||||
|
IFluenceClient,
|
||||||
|
ClientConfig,
|
||||||
|
CallParams,
|
||||||
|
} from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
ArrayType,
|
ArrayType,
|
||||||
@ -151,14 +224,14 @@ export type {
|
|||||||
FnConfig,
|
FnConfig,
|
||||||
RegisterServiceType,
|
RegisterServiceType,
|
||||||
RegisterServiceArgs,
|
RegisterServiceArgs,
|
||||||
} from '@fluencelabs/interfaces';
|
} from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
export { v5_callFunction, v5_registerService } from './api.js';
|
export { v5_callFunction, v5_registerService } from "./api.js";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error Writing to global object like this prohibited by ts
|
||||||
globalThis.new_fluence = Fluence;
|
globalThis.new_fluence = Fluence;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error Writing to global object like this prohibited by ts
|
||||||
globalThis.fluence = {
|
globalThis.fluence = {
|
||||||
clientFactory: createClient,
|
clientFactory: createClient,
|
||||||
callAquaFunction,
|
callAquaFunction,
|
||||||
@ -166,4 +239,9 @@ globalThis.fluence = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export { createClient, callAquaFunction, registerService };
|
export { createClient, callAquaFunction, registerService };
|
||||||
export { getFluenceInterface, getFluenceInterfaceFromGlobalThis } from './util/loadClient.js';
|
export {
|
||||||
|
KeyPair,
|
||||||
|
fromBase64Sk,
|
||||||
|
fromBase58Sk,
|
||||||
|
fromOpts,
|
||||||
|
} from "./keypair/index.js";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2021 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -13,10 +13,44 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { KeyPair } from '../keypair/index.js';
|
|
||||||
|
|
||||||
import type { PeerIdB58 } from '@fluencelabs/interfaces';
|
import { Buffer } from "buffer";
|
||||||
import { deserializeAvmResult, InterpreterResult, KeyPairFormat, serializeAvmArgs } from '@fluencelabs/avm';
|
|
||||||
|
import {
|
||||||
|
deserializeAvmResult,
|
||||||
|
InterpreterResult,
|
||||||
|
KeyPairFormat,
|
||||||
|
serializeAvmArgs,
|
||||||
|
} from "@fluencelabs/avm";
|
||||||
|
import { defaultCallParameters } from "@fluencelabs/marine-js/dist/types";
|
||||||
|
import { fromUint8Array } from "js-base64";
|
||||||
|
import {
|
||||||
|
concatMap,
|
||||||
|
filter,
|
||||||
|
groupBy,
|
||||||
|
lastValueFrom,
|
||||||
|
mergeMap,
|
||||||
|
pipe,
|
||||||
|
Subject,
|
||||||
|
tap,
|
||||||
|
Unsubscribable,
|
||||||
|
} from "rxjs";
|
||||||
|
|
||||||
|
import { IConnection } from "../connection/interfaces.js";
|
||||||
|
import {
|
||||||
|
CallServiceData,
|
||||||
|
CallServiceResult,
|
||||||
|
IJsServiceHost,
|
||||||
|
ResultCodes,
|
||||||
|
} from "../jsServiceHost/interfaces.js";
|
||||||
|
import {
|
||||||
|
getParticleContext,
|
||||||
|
registerDefaultServices,
|
||||||
|
ServiceError,
|
||||||
|
} from "../jsServiceHost/serviceUtils.js";
|
||||||
|
import { KeyPair } from "../keypair/index.js";
|
||||||
|
import { IMarineHost } from "../marine/interfaces.js";
|
||||||
|
import { IParticle } from "../particle/interfaces.js";
|
||||||
import {
|
import {
|
||||||
cloneWithNewData,
|
cloneWithNewData,
|
||||||
getActualTTL,
|
getActualTTL,
|
||||||
@ -24,50 +58,18 @@ import {
|
|||||||
Particle,
|
Particle,
|
||||||
ParticleExecutionStage,
|
ParticleExecutionStage,
|
||||||
ParticleQueueItem,
|
ParticleQueueItem,
|
||||||
} from '../particle/Particle.js';
|
} from "../particle/Particle.js";
|
||||||
import { defaultCallParameters } from '@fluencelabs/marine-js/dist/types'
|
import { registerSig } from "../services/_aqua/services.js";
|
||||||
import { jsonify, isString } from '../util/utils.js';
|
import { registerSrv } from "../services/_aqua/single-module-srv.js";
|
||||||
import {
|
import { registerTracing } from "../services/_aqua/tracing.js";
|
||||||
concatAll,
|
import { defaultSigGuard, Sig } from "../services/Sig.js";
|
||||||
concatMap,
|
import { Srv } from "../services/SingleModuleSrv.js";
|
||||||
filter,
|
import { Tracing } from "../services/Tracing.js";
|
||||||
from,
|
import { logger } from "../util/logger.js";
|
||||||
groupBy,
|
import { jsonify, isString, getErrorMessage } from "../util/utils.js";
|
||||||
lastValueFrom,
|
|
||||||
mergeAll,
|
|
||||||
mergeMap,
|
|
||||||
Observable,
|
|
||||||
pipe,
|
|
||||||
Subject,
|
|
||||||
tap,
|
|
||||||
Unsubscribable
|
|
||||||
} from 'rxjs';
|
|
||||||
import { defaultSigGuard, Sig } from '../services/Sig.js';
|
|
||||||
import { registerSig } from '../services/_aqua/services.js';
|
|
||||||
import { registerSrv } from '../services/_aqua/single-module-srv.js';
|
|
||||||
import { registerTracing } from '../services/_aqua/tracing.js';
|
|
||||||
import { Buffer } from 'buffer';
|
|
||||||
|
|
||||||
import { Srv } from '../services/SingleModuleSrv.js';
|
const log_particle = logger("particle");
|
||||||
import { Tracing } from '../services/Tracing.js';
|
const log_peer = logger("peer");
|
||||||
|
|
||||||
import { logger } from '../util/logger.js';
|
|
||||||
import { getParticleContext, registerDefaultServices, ServiceError } from '../jsServiceHost/serviceUtils.js';
|
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
|
||||||
import { IConnection } from '../connection/interfaces.js';
|
|
||||||
import { IAvmRunner, IMarineHost } from '../marine/interfaces.js';
|
|
||||||
import {
|
|
||||||
CallServiceData,
|
|
||||||
CallServiceResult,
|
|
||||||
GenericCallServiceHandler,
|
|
||||||
IJsServiceHost,
|
|
||||||
ResultCodes,
|
|
||||||
} from '../jsServiceHost/interfaces.js';
|
|
||||||
import { JSONValue } from '../util/commonTypes.js';
|
|
||||||
import { fromUint8Array } from 'js-base64';
|
|
||||||
|
|
||||||
const log_particle = logger('particle');
|
|
||||||
const log_peer = logger('peer');
|
|
||||||
|
|
||||||
export type PeerConfig = {
|
export type PeerConfig = {
|
||||||
/**
|
/**
|
||||||
@ -103,7 +105,7 @@ export const DEFAULT_CONFIG: PeerConfig = {
|
|||||||
export abstract class FluencePeer {
|
export abstract class FluencePeer {
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly config: PeerConfig,
|
protected readonly config: PeerConfig,
|
||||||
public readonly keyPair: KeyPair,
|
readonly keyPair: KeyPair,
|
||||||
protected readonly marineHost: IMarineHost,
|
protected readonly marineHost: IMarineHost,
|
||||||
protected readonly jsServiceHost: IJsServiceHost,
|
protected readonly jsServiceHost: IJsServiceHost,
|
||||||
protected readonly connection: IConnection,
|
protected readonly connection: IConnection,
|
||||||
@ -112,16 +114,18 @@ export abstract class FluencePeer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
log_peer.trace('starting Fluence peer');
|
log_peer.trace("starting Fluence peer");
|
||||||
if (this.config?.debug?.printParticleId) {
|
|
||||||
|
if (this.config.debug.printParticleId) {
|
||||||
this.printParticleId = true;
|
this.printParticleId = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.marineHost.start();
|
await this.marineHost.start();
|
||||||
|
|
||||||
this._startParticleProcessing();
|
this._startParticleProcessing();
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
await this.connection.start();
|
await this.connection.start();
|
||||||
log_peer.trace('started Fluence peer');
|
log_peer.trace("started Fluence peer");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,20 +133,20 @@ export abstract class FluencePeer {
|
|||||||
* and disconnects from the Fluence network
|
* and disconnects from the Fluence network
|
||||||
*/
|
*/
|
||||||
async stop() {
|
async stop() {
|
||||||
log_peer.trace('stopping Fluence peer');
|
log_peer.trace("stopping Fluence peer");
|
||||||
|
|
||||||
this._particleSourceSubscription?.unsubscribe();
|
this._particleSourceSubscription?.unsubscribe();
|
||||||
|
|
||||||
log_peer.trace('Waiting for all particles to finish execution');
|
log_peer.trace("Waiting for all particles to finish execution");
|
||||||
this._incomingParticles.complete();
|
this._incomingParticles.complete();
|
||||||
await this._incomingParticlePromise;
|
await this._incomingParticlePromise;
|
||||||
log_peer.trace('All particles finished execution');
|
log_peer.trace("All particles finished execution");
|
||||||
|
|
||||||
this._stopParticleProcessing();
|
this._stopParticleProcessing();
|
||||||
await this.marineHost.stop();
|
await this.marineHost.stop();
|
||||||
await this.connection.stop();
|
await this.connection.stop();
|
||||||
this.isInitialized = false;
|
this.isInitialized = false;
|
||||||
log_peer.trace('stopped Fluence peer');
|
log_peer.trace("stopped Fluence peer");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -154,11 +158,10 @@ export abstract class FluencePeer {
|
|||||||
* @param wasm - buffer with the wasm file for service
|
* @param wasm - buffer with the wasm file for service
|
||||||
* @param serviceId - the service id by which the service can be accessed in aqua
|
* @param serviceId - the service id by which the service can be accessed in aqua
|
||||||
*/
|
*/
|
||||||
async registerMarineService(wasm: SharedArrayBuffer | Buffer, serviceId: string): Promise<void> {
|
async registerMarineService(
|
||||||
if (!this.marineHost) {
|
wasm: SharedArrayBuffer | Buffer,
|
||||||
throw new Error("Can't register marine service: peer is not initialized");
|
serviceId: string,
|
||||||
}
|
): Promise<void> {
|
||||||
|
|
||||||
if (this.jsServiceHost.hasService(serviceId)) {
|
if (this.jsServiceHost.hasService(serviceId)) {
|
||||||
throw new Error(`Service with '${serviceId}' id already exists`);
|
throw new Error(`Service with '${serviceId}' id already exists`);
|
||||||
}
|
}
|
||||||
@ -174,35 +177,47 @@ export abstract class FluencePeer {
|
|||||||
await this.marineHost.removeService(serviceId);
|
await this.marineHost.removeService(serviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal api
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private Is not intended to be used manually. Subject to change
|
* @private Is not intended to be used manually. Subject to change
|
||||||
*/
|
*/
|
||||||
get internals() {
|
get internals() {
|
||||||
return {
|
return {
|
||||||
getServices: () => this._classServices,
|
getServices: () => {
|
||||||
|
return this._classServices;
|
||||||
|
},
|
||||||
|
|
||||||
getRelayPeerId: () => {
|
getRelayPeerId: () => {
|
||||||
if (this.connection.supportsRelay()) {
|
if (this.connection.supportsRelay()) {
|
||||||
return this.connection.getRelayPeerId();
|
return this.connection.getRelayPeerId();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Relay is not supported by the current connection');
|
throw new Error("Relay is not supported by the current connection");
|
||||||
},
|
},
|
||||||
|
|
||||||
parseAst: async (air: string): Promise<{ success: boolean; data: any }> => {
|
parseAst: async (
|
||||||
|
air: string,
|
||||||
|
): Promise<{ success: boolean; data: unknown }> => {
|
||||||
if (!this.isInitialized) {
|
if (!this.isInitialized) {
|
||||||
new Error("Can't use avm: peer is not initialized");
|
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],
|
||||||
|
defaultCallParameters,
|
||||||
|
);
|
||||||
|
|
||||||
if (!isString(res)) {
|
if (!isString(res)) {
|
||||||
throw new Error(`Call to avm:ast expected to return string. Actual return: ${res}`);
|
throw new Error(
|
||||||
|
`Call to avm:ast expected to return string. Actual return: ${JSON.stringify(
|
||||||
|
res,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (res.startsWith('error')) {
|
if (res.startsWith("error")) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
data: res,
|
data: res,
|
||||||
@ -214,12 +229,25 @@ export abstract class FluencePeer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error('Failed to call avm. Result: ' + res + '. Error: ' + err);
|
throw new Error(
|
||||||
|
"Failed to call avm. Result: " +
|
||||||
|
res +
|
||||||
|
". Error: " +
|
||||||
|
getErrorMessage(err),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createNewParticle: (script: string, ttl: number = this.config.defaultTtlMs): Promise<IParticle> => {
|
createNewParticle: (
|
||||||
return Particle.createNew(script, this.keyPair.getPeerId(), ttl, this.keyPair);
|
script: string,
|
||||||
|
ttl: number = this.config.defaultTtlMs,
|
||||||
|
): Promise<IParticle> => {
|
||||||
|
return Particle.createNew(
|
||||||
|
script,
|
||||||
|
this.keyPair.getPeerId(),
|
||||||
|
ttl,
|
||||||
|
this.keyPair,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,13 +255,20 @@ export abstract class FluencePeer {
|
|||||||
* @param particle - particle to start execution of
|
* @param particle - particle to start execution of
|
||||||
* @param onStageChange - callback for reacting on particle state changes
|
* @param onStageChange - callback for reacting on particle state changes
|
||||||
*/
|
*/
|
||||||
initiateParticle: (particle: IParticle, onStageChange: (stage: ParticleExecutionStage) => void): void => {
|
initiateParticle: (
|
||||||
|
particle: IParticle,
|
||||||
|
onStageChange: (stage: ParticleExecutionStage) => void,
|
||||||
|
): void => {
|
||||||
if (!this.isInitialized) {
|
if (!this.isInitialized) {
|
||||||
throw new Error('Cannot initiate new particle: peer is not initialized');
|
throw new Error(
|
||||||
|
"Cannot initiate new particle: peer is not initialized",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.printParticleId) {
|
if (this.printParticleId) {
|
||||||
console.log('Particle id: ', particle.id);
|
// This is intended console-log
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log("Particle id: ", particle.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._incomingParticles.next({
|
this._incomingParticles.next({
|
||||||
@ -250,11 +285,15 @@ export abstract class FluencePeer {
|
|||||||
/**
|
/**
|
||||||
* Register handler for all particles
|
* Register handler for all particles
|
||||||
*/
|
*/
|
||||||
common: this.jsServiceHost.registerGlobalHandler.bind(this.jsServiceHost),
|
common: this.jsServiceHost.registerGlobalHandler.bind(
|
||||||
|
this.jsServiceHost,
|
||||||
|
),
|
||||||
/**
|
/**
|
||||||
* Register handler which will be called only for particle with the specific id
|
* Register handler which will be called only for particle with the specific id
|
||||||
*/
|
*/
|
||||||
forParticle: this.jsServiceHost.registerParticleScopeHandler.bind(this.jsServiceHost),
|
forParticle: this.jsServiceHost.registerParticleScopeHandler.bind(
|
||||||
|
this.jsServiceHost,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -290,36 +329,54 @@ export abstract class FluencePeer {
|
|||||||
registerDefaultServices(this);
|
registerDefaultServices(this);
|
||||||
|
|
||||||
this._classServices.sig.securityGuard = defaultSigGuard(peerId);
|
this._classServices.sig.securityGuard = defaultSigGuard(peerId);
|
||||||
registerSig(this, 'sig', this._classServices.sig);
|
registerSig(this, "sig", this._classServices.sig);
|
||||||
registerSig(this, peerId, this._classServices.sig);
|
registerSig(this, peerId, this._classServices.sig);
|
||||||
registerSrv(this, 'single_module_srv', this._classServices.srv);
|
registerSrv(this, "single_module_srv", this._classServices.srv);
|
||||||
registerTracing(this, 'tracingSrv', this._classServices.tracing);
|
registerTracing(this, "tracingSrv", this._classServices.tracing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _startParticleProcessing() {
|
private _startParticleProcessing() {
|
||||||
this._particleSourceSubscription = this.connection.particleSource.subscribe({
|
this._particleSourceSubscription = this.connection.particleSource.subscribe(
|
||||||
|
{
|
||||||
next: (p) => {
|
next: (p) => {
|
||||||
this._incomingParticles.next({ particle: p, callResults: [], onStageChange: () => {} });
|
this._incomingParticles.next({
|
||||||
},
|
particle: p,
|
||||||
|
callResults: [],
|
||||||
|
onStageChange: () => {},
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
this._incomingParticlePromise = lastValueFrom(this._incomingParticles
|
this._incomingParticlePromise = lastValueFrom(
|
||||||
.pipe(
|
this._incomingParticles.pipe(
|
||||||
tap((item) => {
|
tap((item) => {
|
||||||
log_particle.debug('id %s. received:', item.particle.id);
|
log_particle.debug("id %s. received:", item.particle.id);
|
||||||
log_particle.trace('id %s. data: %j', item.particle.id, {
|
|
||||||
|
log_particle.trace("id %s. data: %j", item.particle.id, {
|
||||||
initPeerId: item.particle.initPeerId,
|
initPeerId: item.particle.initPeerId,
|
||||||
timestamp: item.particle.timestamp,
|
timestamp: item.particle.timestamp,
|
||||||
ttl: item.particle.ttl,
|
ttl: item.particle.ttl,
|
||||||
signature: item.particle.signature,
|
signature: item.particle.signature,
|
||||||
});
|
});
|
||||||
|
|
||||||
log_particle.trace('id %s. script: %s', item.particle.id, item.particle.script);
|
log_particle.trace(
|
||||||
log_particle.trace('id %s. call results: %j', item.particle.id, item.callResults);
|
"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)),
|
filterExpiredParticles(this._expireParticle.bind(this)),
|
||||||
groupBy(item => fromUint8Array(item.particle.signature)),
|
groupBy((item) => {
|
||||||
mergeMap(group$ => {
|
return fromUint8Array(item.particle.signature);
|
||||||
|
}),
|
||||||
|
mergeMap((group$) => {
|
||||||
let prevData: Uint8Array = Buffer.from([]);
|
let prevData: Uint8Array = Buffer.from([]);
|
||||||
let firstRun = true;
|
let firstRun = true;
|
||||||
|
|
||||||
@ -334,7 +391,7 @@ export abstract class FluencePeer {
|
|||||||
firstRun = false;
|
firstRun = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isInitialized || this.marineHost === undefined) {
|
if (!this.isInitialized) {
|
||||||
// If `.stop()` was called return null to stop particle processing immediately
|
// If `.stop()` was called return null to stop particle processing immediately
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -344,8 +401,16 @@ export abstract class FluencePeer {
|
|||||||
// MUST happen sequentially (in a critical section).
|
// MUST happen sequentially (in a critical section).
|
||||||
// Otherwise the race might occur corrupting the prevData
|
// Otherwise the race might occur corrupting the prevData
|
||||||
|
|
||||||
log_particle.debug('id %s. sending particle to interpreter', item.particle.id);
|
log_particle.debug(
|
||||||
log_particle.trace('id %s. prevData: %s', item.particle.id, this.decodeAvmData(prevData).slice(0, 50));
|
"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(
|
const args = serializeAvmArgs(
|
||||||
{
|
{
|
||||||
@ -364,14 +429,24 @@ export abstract class FluencePeer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let avmCallResult: InterpreterResult | Error;
|
let avmCallResult: InterpreterResult | Error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await this.marineHost.callService('avm', 'invoke', args, defaultCallParameters);
|
const res = await this.marineHost.callService(
|
||||||
|
"avm",
|
||||||
|
"invoke",
|
||||||
|
args,
|
||||||
|
defaultCallParameters,
|
||||||
|
);
|
||||||
|
|
||||||
avmCallResult = deserializeAvmResult(res);
|
avmCallResult = deserializeAvmResult(res);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
avmCallResult = e instanceof Error ? e : new Error(String(e));
|
avmCallResult = e instanceof Error ? e : new Error(String(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(avmCallResult instanceof Error) && avmCallResult.retCode === 0) {
|
if (
|
||||||
|
!(avmCallResult instanceof Error) &&
|
||||||
|
avmCallResult.retCode === 0
|
||||||
|
) {
|
||||||
const newData = Buffer.from(avmCallResult.data);
|
const newData = Buffer.from(avmCallResult.data);
|
||||||
prevData = newData;
|
prevData = newData;
|
||||||
}
|
}
|
||||||
@ -381,8 +456,14 @@ export abstract class FluencePeer {
|
|||||||
result: avmCallResult,
|
result: avmCallResult,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
filter((item): item is NonNullable<typeof item> => item !== null),
|
filter((item): item is NonNullable<typeof item> => {
|
||||||
filterExpiredParticles<ParticleQueueItem & {result: Error | InterpreterResult }>(this._expireParticle.bind(this)),
|
return item !== null;
|
||||||
|
}),
|
||||||
|
filterExpiredParticles<
|
||||||
|
ParticleQueueItem & {
|
||||||
|
result: Error | InterpreterResult;
|
||||||
|
}
|
||||||
|
>(this._expireParticle.bind(this)),
|
||||||
mergeMap(async (item) => {
|
mergeMap(async (item) => {
|
||||||
// If peer was stopped, do not proceed further
|
// If peer was stopped, do not proceed further
|
||||||
if (!this.isInitialized) {
|
if (!this.isInitialized) {
|
||||||
@ -391,60 +472,89 @@ export abstract class FluencePeer {
|
|||||||
|
|
||||||
// Do not continue if there was an error in particle interpretation
|
// Do not continue if there was an error in particle interpretation
|
||||||
if (item.result instanceof Error) {
|
if (item.result instanceof Error) {
|
||||||
log_particle.error('id %s. interpreter failed: %s', item.particle.id, item.result.message);
|
log_particle.error(
|
||||||
item.onStageChange({ stage: 'interpreterError', errorMessage: item.result.message });
|
"id %s. interpreter failed: %s",
|
||||||
|
item.particle.id,
|
||||||
|
item.result.message,
|
||||||
|
);
|
||||||
|
|
||||||
|
item.onStageChange({
|
||||||
|
stage: "interpreterError",
|
||||||
|
errorMessage: item.result.message,
|
||||||
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.result.retCode !== 0) {
|
if (item.result.retCode !== 0) {
|
||||||
log_particle.error(
|
log_particle.error(
|
||||||
'id %s. interpreter failed: retCode: %d, message: %s',
|
"id %s. interpreter failed: retCode: %d, message: %s",
|
||||||
item.particle.id,
|
item.particle.id,
|
||||||
item.result.retCode,
|
item.result.retCode,
|
||||||
item.result.errorMessage,
|
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 });
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_particle.trace(
|
log_particle.trace(
|
||||||
'id %s. interpreter result: retCode: %d, avm data: %s',
|
"id %s. interpreter result: retCode: %d, avm data: %s",
|
||||||
item.particle.id,
|
item.particle.id,
|
||||||
item.result.retCode,
|
item.result.retCode,
|
||||||
this.decodeAvmData(item.result.data)
|
this.decodeAvmData(item.result.data),
|
||||||
);
|
);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
item.onStageChange({ stage: 'interpreted' });
|
item.onStageChange({ stage: "interpreted" });
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
let connectionPromise: Promise<void> = Promise.resolve();
|
let connectionPromise: Promise<void> = Promise.resolve();
|
||||||
|
|
||||||
// send particle further if requested
|
// send particle further if requested
|
||||||
if (item.result.nextPeerPks.length > 0) {
|
if (item.result.nextPeerPks.length > 0) {
|
||||||
const newParticle = cloneWithNewData(item.particle, Buffer.from(item.result.data));
|
const newParticle = cloneWithNewData(
|
||||||
|
item.particle,
|
||||||
// Do not send particle after the peer has been stopped
|
Buffer.from(item.result.data),
|
||||||
if (!this.isInitialized) {
|
);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_particle.debug(
|
log_particle.debug(
|
||||||
'id %s. sending particle into network. Next peer ids: %s',
|
"id %s. sending particle into network. Next peer ids: %s",
|
||||||
newParticle.id,
|
newParticle.id,
|
||||||
item.result.nextPeerPks.toString(),
|
item.result.nextPeerPks.toString(),
|
||||||
);
|
);
|
||||||
|
|
||||||
connectionPromise = this.connection
|
connectionPromise = this.connection
|
||||||
?.sendParticle(item.result.nextPeerPks, newParticle)
|
.sendParticle(item.result.nextPeerPks, newParticle)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
log_particle.trace('id %s. send successful', newParticle.id);
|
log_particle.trace(
|
||||||
item.onStageChange({ stage: 'sent' });
|
"id %s. send successful",
|
||||||
|
newParticle.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
item.onStageChange({ stage: "sent" });
|
||||||
})
|
})
|
||||||
.catch((e: any) => {
|
.catch((e: unknown) => {
|
||||||
log_particle.error('id %s. send failed %j', newParticle.id, e);
|
log_particle.error(
|
||||||
item.onStageChange({ stage: 'sendingError', errorMessage: e.toString() });
|
"id %s. send failed %j",
|
||||||
|
newParticle.id,
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
|
||||||
|
item.onStageChange({
|
||||||
|
stage: "sendingError",
|
||||||
|
errorMessage: getErrorMessage(e),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,7 +570,7 @@ export abstract class FluencePeer {
|
|||||||
particleContext: getParticleContext(item.particle),
|
particleContext: getParticleContext(item.particle),
|
||||||
};
|
};
|
||||||
|
|
||||||
this._execSingleCallRequest(req)
|
void this._execSingleCallRequest(req)
|
||||||
.catch((err): CallServiceResult => {
|
.catch((err): CallServiceResult => {
|
||||||
if (err instanceof ServiceError) {
|
if (err instanceof ServiceError) {
|
||||||
return {
|
return {
|
||||||
@ -471,9 +581,11 @@ export abstract class FluencePeer {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
retCode: ResultCodes.error,
|
retCode: ResultCodes.error,
|
||||||
result: `Service call failed. fnName="${req.fnName}" serviceId="${
|
result: `Service call failed. fnName="${
|
||||||
|
req.fnName
|
||||||
|
}" serviceId="${
|
||||||
req.serviceId
|
req.serviceId
|
||||||
}" error: ${err.toString()}`,
|
}" error: ${getErrorMessage(err)}`,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@ -482,7 +594,11 @@ export abstract class FluencePeer {
|
|||||||
retCode: res.retCode,
|
retCode: res.retCode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const newParticle = cloneWithNewData(item.particle, Buffer.from([]));
|
const newParticle = cloneWithNewData(
|
||||||
|
item.particle,
|
||||||
|
Buffer.from([]),
|
||||||
|
);
|
||||||
|
|
||||||
this._incomingParticles.next({
|
this._incomingParticles.next({
|
||||||
...item,
|
...item,
|
||||||
particle: newParticle,
|
particle: newParticle,
|
||||||
@ -491,45 +607,59 @@ export abstract class FluencePeer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item.onStageChange({ stage: 'localWorkDone' });
|
item.onStageChange({ stage: "localWorkDone" });
|
||||||
}
|
}
|
||||||
|
|
||||||
return connectionPromise;
|
return connectionPromise;
|
||||||
}),
|
}),
|
||||||
|
);
|
||||||
)
|
}),
|
||||||
})
|
),
|
||||||
), { defaultValue: undefined });
|
{ defaultValue: undefined },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _expireParticle(item: ParticleQueueItem) {
|
private _expireParticle(item: ParticleQueueItem) {
|
||||||
const particleId = item.particle.id;
|
const particleId = item.particle.id;
|
||||||
|
|
||||||
log_particle.debug(
|
log_particle.debug(
|
||||||
'id %s. particle has expired after %d. Deleting particle-related queues and handlers',
|
"id %s. particle has expired after %d. Deleting particle-related queues and handlers",
|
||||||
item.particle.id,
|
item.particle.id,
|
||||||
item.particle.ttl,
|
item.particle.ttl,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.jsServiceHost.removeParticleScopeHandlers(particleId);
|
this.jsServiceHost.removeParticleScopeHandlers(particleId);
|
||||||
|
|
||||||
item.onStageChange({ stage: 'expired' });
|
item.onStageChange({ stage: "expired" });
|
||||||
}
|
}
|
||||||
|
|
||||||
private decodeAvmData(data: Uint8Array) {
|
private decodeAvmData(data: Uint8Array) {
|
||||||
return new TextDecoder().decode(data.buffer);
|
return new TextDecoder().decode(data.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _execSingleCallRequest(req: CallServiceData): Promise<CallServiceResult> {
|
private async _execSingleCallRequest(
|
||||||
|
req: CallServiceData,
|
||||||
|
): Promise<CallServiceResult> {
|
||||||
const particleId = req.particleContext.particleId;
|
const particleId = req.particleContext.particleId;
|
||||||
log_particle.trace('id %s. executing call service handler %j', particleId, req);
|
|
||||||
|
|
||||||
if (this.marineHost && await this.marineHost.hasService(req.serviceId)) {
|
log_particle.trace(
|
||||||
|
"id %s. executing call service handler %j",
|
||||||
|
particleId,
|
||||||
|
req,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (await this.marineHost.hasService(req.serviceId)) {
|
||||||
// TODO build correct CallParameters instead of default ones
|
// TODO build correct CallParameters instead of default ones
|
||||||
const result = await this.marineHost.callService(req.serviceId, req.fnName, req.args, defaultCallParameters);
|
const result = await this.marineHost.callService(
|
||||||
|
req.serviceId,
|
||||||
|
req.fnName,
|
||||||
|
req.args,
|
||||||
|
defaultCallParameters,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
retCode: ResultCodes.success,
|
retCode: ResultCodes.success,
|
||||||
result: result as JSONValue,
|
result: result,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,13 +668,19 @@ export abstract class FluencePeer {
|
|||||||
if (res === null) {
|
if (res === null) {
|
||||||
res = {
|
res = {
|
||||||
retCode: ResultCodes.error,
|
retCode: ResultCodes.error,
|
||||||
result: `No service found for service call: serviceId='${req.serviceId}', fnName='${
|
result: `No service found for service call: serviceId='${
|
||||||
req.fnName
|
req.serviceId
|
||||||
}' args='${jsonify(req.args)}'`,
|
}', fnName='${req.fnName}' args='${jsonify(req.args)}'`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
log_particle.trace('id %s. executed call service handler, req: %j, res: %j ', particleId, req, res);
|
log_particle.trace(
|
||||||
|
"id %s. executed call service handler, req: %j, res: %j ",
|
||||||
|
particleId,
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,13 +692,17 @@ export abstract class FluencePeer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterExpiredParticles<T extends ParticleQueueItem>(onParticleExpiration: (item: T) => void) {
|
function filterExpiredParticles<T extends ParticleQueueItem>(
|
||||||
|
onParticleExpiration: (item: T) => void,
|
||||||
|
) {
|
||||||
return pipe(
|
return pipe(
|
||||||
tap((item: T) => {
|
tap((item: T) => {
|
||||||
if (hasExpired(item.particle)) {
|
if (hasExpired(item.particle)) {
|
||||||
onParticleExpiration(item);
|
onParticleExpiration(item);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
filter((x) => !hasExpired(x.particle)),
|
filter((x) => {
|
||||||
|
return !hasExpired(x.particle);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,43 @@
|
|||||||
import { it, describe, expect } from 'vitest';
|
/**
|
||||||
import { registerHandlersHelper, withPeer } from '../../util/testUtils.js';
|
* Copyright 2023 Fluence Labs Limited
|
||||||
import { handleTimeout } from '../../particle/Particle.js';
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
describe('Basic AVM functionality in Fluence Peer tests', () => {
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
it('Simple call', async () => {
|
import { it, describe, expect } from "vitest";
|
||||||
|
|
||||||
|
import { handleTimeout } from "../../particle/Particle.js";
|
||||||
|
import { registerHandlersHelper, withPeer } from "../../util/testUtils.js";
|
||||||
|
|
||||||
|
describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||||
|
it("Simple call", async () => {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(call %init_peer_id% ("print" "print") ["1"])
|
(call %init_peer_id% ("print" "print") ["1"])
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const res = await new Promise<string>((resolve, reject) => {
|
const res = await new Promise<JSONValue>((resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
print: {
|
print: {
|
||||||
print: (args: Array<string>) => {
|
print: (args): undefined => {
|
||||||
const [res] = args;
|
const [res] = args;
|
||||||
resolve(res);
|
resolve(res);
|
||||||
},
|
},
|
||||||
@ -27,11 +47,11 @@ describe('Basic AVM functionality in Fluence Peer tests', () => {
|
|||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res).toBe('1');
|
expect(res).toBe("1");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Par call', async () => {
|
it("Par call", async () => {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
@ -42,20 +62,23 @@ describe('Basic AVM functionality in Fluence Peer tests', () => {
|
|||||||
(call %init_peer_id% ("print" "print") ["2"])
|
(call %init_peer_id% ("print" "print") ["2"])
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const res = await new Promise<string[]>((resolve, reject) => {
|
const res = await new Promise<JSONValue[]>((resolve, reject) => {
|
||||||
const res: any[] = [];
|
const res: JSONValue[] = [];
|
||||||
|
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
print: {
|
print: {
|
||||||
print: (args: any) => {
|
print: (args): undefined => {
|
||||||
res.push(args[0]);
|
res.push(args[0]);
|
||||||
if (res.length == 2) {
|
|
||||||
|
if (res.length === 2) {
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -65,11 +88,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']);
|
expect(res).toStrictEqual(["1", "2"]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Timeout in par call: race', async () => {
|
it("Timeout in par call: race", async () => {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
@ -86,16 +109,18 @@ describe('Basic AVM functionality in Fluence Peer tests', () => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const res = await new Promise((resolve, reject) => {
|
const res = await new Promise((resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
return: {
|
return: {
|
||||||
return: (args: any) => {
|
return: (args): undefined => {
|
||||||
resolve(args[0]);
|
resolve(args[0]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -104,11 +129,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');
|
expect(res).toBe("fast_result");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Timeout in par call: wait', async () => {
|
it("Timeout in par call: wait", async () => {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
@ -136,16 +161,18 @@ describe('Basic AVM functionality in Fluence Peer tests', () => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const res = await new Promise((resolve, reject) => {
|
const res = await new Promise((resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
return: {
|
return: {
|
||||||
return: (args: any) => {
|
return: (args): undefined => {
|
||||||
resolve(args[0]);
|
resolve(args[0]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -154,7 +181,7 @@ 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');
|
expect(res).toBe("failed_with_timeout");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,13 +13,21 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { describe, expect, it } from 'vitest';
|
|
||||||
import { registerHandlersHelper, withPeer } from '../../util/testUtils.js';
|
|
||||||
import { handleTimeout } from '../../particle/Particle.js';
|
|
||||||
import { CallServiceData, ResultCodes } from '../../jsServiceHost/interfaces.js';
|
|
||||||
|
|
||||||
describe('FluencePeer flow tests', () => {
|
import assert from "assert";
|
||||||
it('should execute par instruction in parallel', async function () {
|
|
||||||
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CallServiceData,
|
||||||
|
ResultCodes,
|
||||||
|
} from "../../jsServiceHost/interfaces.js";
|
||||||
|
import { handleTimeout } from "../../particle/Particle.js";
|
||||||
|
import { registerHandlersHelper, withPeer } from "../../util/testUtils.js";
|
||||||
|
|
||||||
|
describe("FluencePeer flow tests", () => {
|
||||||
|
it("should execute par instruction in parallel", async function () {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(par
|
(par
|
||||||
@ -36,9 +44,14 @@ describe('FluencePeer flow tests', () => {
|
|||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const res = await new Promise<any>((resolve, reject) => {
|
const res = await new Promise((resolve, reject) => {
|
||||||
peer.internals.regHandler.forParticle(particle.id, 'flow', 'timeout', (req: CallServiceData) => {
|
peer.internals.regHandler.forParticle(
|
||||||
|
particle.id,
|
||||||
|
"flow",
|
||||||
|
"timeout",
|
||||||
|
(req: CallServiceData) => {
|
||||||
const [timeout, message] = req.args;
|
const [timeout, message] = req.args;
|
||||||
|
assert(typeof timeout === "number");
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -46,29 +59,34 @@ describe('FluencePeer flow tests', () => {
|
|||||||
result: message,
|
result: message,
|
||||||
retCode: ResultCodes.success,
|
retCode: ResultCodes.success,
|
||||||
};
|
};
|
||||||
|
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}, timeout);
|
}, timeout);
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const values: any[] = [];
|
const values: JSONValue[] = [];
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
callback: {
|
callback: {
|
||||||
callback1: (args: any) => {
|
callback1: (args): undefined => {
|
||||||
const [val] = args;
|
const [val] = args;
|
||||||
values.push(val);
|
values.push(val);
|
||||||
|
|
||||||
if (values.length === 2) {
|
if (values.length === 2) {
|
||||||
resolve(values);
|
resolve(values);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
callback2: (args: any) => {
|
callback2: (args): undefined => {
|
||||||
const [val] = args;
|
const [val] = args;
|
||||||
values.push(val);
|
values.push(val);
|
||||||
|
|
||||||
if (values.length === 2) {
|
if (values.length === 2) {
|
||||||
resolve(values);
|
resolve(values);
|
||||||
}
|
}
|
||||||
@ -79,7 +97,7 @@ describe('FluencePeer flow tests', () => {
|
|||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(res).toEqual(expect.arrayContaining(["test1", "test1"]));
|
expect(res).toEqual(expect.arrayContaining(["test1", "test1"]));
|
||||||
});
|
});
|
||||||
}, 1500);
|
}, 1500);
|
||||||
});
|
});
|
@ -1,10 +1,26 @@
|
|||||||
import { it, describe, expect } from 'vitest';
|
/**
|
||||||
|
* Copyright 2023 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import { withPeer } from '../../util/testUtils.js';
|
import { it, describe, expect } from "vitest";
|
||||||
|
|
||||||
describe('Parse ast tests', () => {
|
import { withPeer } from "../../util/testUtils.js";
|
||||||
it('Correct ast should be parsed correctly', async () => {
|
|
||||||
withPeer(async (peer) => {
|
describe("Parse ast tests", () => {
|
||||||
|
it("Correct ast should be parsed correctly", async () => {
|
||||||
|
await withPeer(async (peer) => {
|
||||||
const air = `(null)`;
|
const air = `(null)`;
|
||||||
const res = await peer.internals.parseAst(air);
|
const res = await peer.internals.parseAst(air);
|
||||||
|
|
||||||
@ -15,14 +31,15 @@ describe('Parse ast tests', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Incorrect ast should result in corresponding error', async () => {
|
it("Incorrect ast should result in corresponding error", async () => {
|
||||||
withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const air = `(null`;
|
const air = `(null`;
|
||||||
const res = await peer.internals.parseAst(air);
|
const res = await peer.internals.parseAst(air);
|
||||||
|
|
||||||
expect(res).toStrictEqual({
|
expect(res).toStrictEqual({
|
||||||
success: false,
|
success: false,
|
||||||
data: expect.stringContaining('error'),
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
data: expect.stringContaining("error"),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,36 @@
|
|||||||
import { it, describe, expect } from 'vitest';
|
/**
|
||||||
|
* Copyright 2023 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import { isFluencePeer } from '../../api.js';
|
import { it, describe, expect } from "vitest";
|
||||||
import { mkTestPeer, registerHandlersHelper, withPeer } from '../../util/testUtils.js';
|
|
||||||
import { handleTimeout } from '../../particle/Particle.js';
|
|
||||||
import { FluencePeer } from '../FluencePeer.js';
|
|
||||||
|
|
||||||
describe('FluencePeer usage test suite', () => {
|
import { isFluencePeer } from "../../api.js";
|
||||||
it('should perform test for FluencePeer class correctly', async () => {
|
import { handleTimeout } from "../../particle/Particle.js";
|
||||||
|
import {
|
||||||
|
mkTestPeer,
|
||||||
|
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
|
// arrange
|
||||||
const peer = await mkTestPeer();
|
const peer = await mkTestPeer();
|
||||||
const number = 1;
|
const number = 1;
|
||||||
const object = { str: 'Hello!' };
|
const object = { str: "Hello!" };
|
||||||
const undefinedVal = undefined;
|
const undefinedVal = undefined;
|
||||||
|
|
||||||
// act
|
// act
|
||||||
@ -26,7 +46,7 @@ describe('FluencePeer usage test suite', () => {
|
|||||||
expect(isUndefinedPeer).toBe(false);
|
expect(isUndefinedPeer).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should successfully call identity on local peer', async function () {
|
it("Should successfully call identity on local peer", async function () {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
@ -34,16 +54,18 @@ describe('FluencePeer usage test suite', () => {
|
|||||||
(call %init_peer_id% ("callback" "callback") [res])
|
(call %init_peer_id% ("callback" "callback") [res])
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const res = await new Promise<string>((resolve, reject) => {
|
const res = await new Promise((resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
callback: {
|
callback: {
|
||||||
callback: async (args: any) => {
|
callback: (args): undefined => {
|
||||||
const [res] = args;
|
const [res] = args;
|
||||||
resolve(res);
|
resolve(res);
|
||||||
},
|
},
|
||||||
@ -53,15 +75,16 @@ describe('FluencePeer usage test suite', () => {
|
|||||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res).toBe('test');
|
expect(res).toBe("test");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should throw correct message when calling non existing local service', async function () {
|
it("Should throw correct message when calling non existing local service", async function () {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const res = callIncorrectService(peer);
|
const res = callIncorrectService(peer);
|
||||||
|
|
||||||
await expect(res).rejects.toMatchObject({
|
await expect(res).rejects.toMatchObject({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
message: expect.stringContaining(
|
message: expect.stringContaining(
|
||||||
`"No service found for service call: serviceId='incorrect', fnName='incorrect' args='[]'"`,
|
`"No service found for service call: serviceId='incorrect', fnName='incorrect' args='[]'"`,
|
||||||
),
|
),
|
||||||
@ -70,7 +93,7 @@ describe('FluencePeer usage test suite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should not crash if undefined is passed as a variable', async () => {
|
it("Should not crash if undefined is passed as a variable", async () => {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(seq
|
(seq
|
||||||
@ -80,23 +103,27 @@ describe('FluencePeer usage test suite', () => {
|
|||||||
(call %init_peer_id% ("callback" "callback") [res])
|
(call %init_peer_id% ("callback" "callback") [res])
|
||||||
)
|
)
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const res = await new Promise<any>((resolve, reject) => {
|
const res = await new Promise((resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
load: {
|
load: {
|
||||||
arg: () => undefined,
|
arg: () => {
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
callback: {
|
callback: {
|
||||||
callback: (args: any) => {
|
callback: (args): undefined => {
|
||||||
const [val] = args;
|
const [val] = args;
|
||||||
resolve(val);
|
resolve(val);
|
||||||
},
|
},
|
||||||
error: (args: any) => {
|
error: (args): undefined => {
|
||||||
const [error] = args;
|
const [error] = args;
|
||||||
reject(error);
|
reject(error);
|
||||||
},
|
},
|
||||||
@ -110,28 +137,30 @@ describe('FluencePeer usage test suite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should not crash if an error ocurred in user-defined handler', async () => {
|
it("Should not crash if an error ocurred in user-defined handler", async () => {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
const script = `
|
const script = `
|
||||||
(xor
|
(xor
|
||||||
(call %init_peer_id% ("load" "arg") [] arg)
|
(call %init_peer_id% ("load" "arg") [] arg)
|
||||||
(call %init_peer_id% ("callback" "error") [%last_error%])
|
(call %init_peer_id% ("callback" "error") [%last_error%])
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
const promise = new Promise<any>((_resolve, reject) => {
|
const promise = new Promise<never>((_resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
load: {
|
load: {
|
||||||
arg: () => {
|
arg: () => {
|
||||||
throw new Error('my super custom error message');
|
throw new Error("my super custom error message");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
callback: {
|
callback: {
|
||||||
error: (args: any) => {
|
error: (args): undefined => {
|
||||||
const [error] = args;
|
const [error] = args;
|
||||||
reject(error);
|
reject(error);
|
||||||
},
|
},
|
||||||
@ -142,31 +171,34 @@ describe('FluencePeer usage test suite', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await expect(promise).rejects.toMatchObject({
|
await expect(promise).rejects.toMatchObject({
|
||||||
message: expect.stringContaining('my super custom error message'),
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
message: expect.stringContaining("my super custom error message"),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function callIncorrectService(peer: FluencePeer): Promise<string[]> {
|
async function callIncorrectService(peer: FluencePeer) {
|
||||||
const script = `
|
const script = `
|
||||||
(xor
|
(xor
|
||||||
(call %init_peer_id% ("incorrect" "incorrect") [] res)
|
(call %init_peer_id% ("incorrect" "incorrect") [] res)
|
||||||
(call %init_peer_id% ("callback" "error") [%last_error%])
|
(call %init_peer_id% ("callback" "error") [%last_error%])
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const particle = await peer.internals.createNewParticle(script);
|
const particle = await peer.internals.createNewParticle(script);
|
||||||
|
|
||||||
return new Promise<any[]>((resolve, reject) => {
|
return new Promise<unknown[]>((resolve, reject) => {
|
||||||
if (particle instanceof Error) {
|
if (particle instanceof Error) {
|
||||||
return reject(particle.message);
|
reject(particle.message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandlersHelper(peer, particle, {
|
registerHandlersHelper(peer, particle, {
|
||||||
callback: {
|
callback: {
|
||||||
callback: (args: any) => {
|
callback: (args): undefined => {
|
||||||
resolve(args);
|
resolve(args);
|
||||||
},
|
},
|
||||||
error: (args: any) => {
|
error: (args): undefined => {
|
||||||
const [error] = args;
|
const [error] = args;
|
||||||
reject(error);
|
reject(error);
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,17 +13,29 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { CallServiceData, CallServiceResult, GenericCallServiceHandler, IJsServiceHost } from './interfaces.js';
|
|
||||||
|
import {
|
||||||
|
CallServiceData,
|
||||||
|
CallServiceResult,
|
||||||
|
GenericCallServiceHandler,
|
||||||
|
IJsServiceHost,
|
||||||
|
} from "./interfaces.js";
|
||||||
|
|
||||||
export class JsServiceHost implements IJsServiceHost {
|
export class JsServiceHost implements IJsServiceHost {
|
||||||
private particleScopeHandlers = new Map<string, Map<string, GenericCallServiceHandler>>();
|
private particleScopeHandlers = new Map<
|
||||||
|
string,
|
||||||
|
Map<string, GenericCallServiceHandler>
|
||||||
|
>();
|
||||||
private commonHandlers = new Map<string, GenericCallServiceHandler>();
|
private commonHandlers = new Map<string, GenericCallServiceHandler>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if any handler for the specified serviceId is registered
|
* Returns true if any handler for the specified serviceId is registered
|
||||||
*/
|
*/
|
||||||
hasService(serviceId: string): boolean {
|
hasService(serviceId: string): boolean {
|
||||||
return this.commonHandlers.has(serviceId) || this.particleScopeHandlers.has(serviceId);
|
return (
|
||||||
|
this.commonHandlers.has(serviceId) ||
|
||||||
|
this.particleScopeHandlers.has(serviceId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,31 +52,28 @@ export class JsServiceHost implements IJsServiceHost {
|
|||||||
* @param fnName Function name as specified in `call` air instruction
|
* @param fnName Function name as specified in `call` air instruction
|
||||||
* @param particleId Particle ID
|
* @param particleId Particle ID
|
||||||
*/
|
*/
|
||||||
getHandler(serviceId: string, fnName: string, particleId: string): GenericCallServiceHandler | null {
|
getHandler(
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
particleId: string,
|
||||||
|
): GenericCallServiceHandler | null {
|
||||||
const key = serviceFnKey(serviceId, fnName);
|
const key = serviceFnKey(serviceId, fnName);
|
||||||
const psh = this.particleScopeHandlers.get(particleId);
|
return (
|
||||||
let handler: GenericCallServiceHandler | undefined = undefined;
|
this.particleScopeHandlers.get(particleId)?.get(key) ??
|
||||||
|
this.commonHandlers.get(key) ??
|
||||||
// we should prioritize handler for this particle if there is one
|
null
|
||||||
// if particle-scoped handler exist for this particle try getting handler there
|
);
|
||||||
if (psh !== undefined) {
|
|
||||||
handler = psh.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// then try to find a common handler for all particles with this service-fn key
|
|
||||||
// if there is no particle-specific handler, get one from common map
|
|
||||||
if (handler === undefined) {
|
|
||||||
handler = this.commonHandlers.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler || null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute service call for specified call service data. Return null if no handler was found
|
* Execute service call for specified call service data. Return null if no handler was found
|
||||||
*/
|
*/
|
||||||
async callService(req: CallServiceData): Promise<CallServiceResult | null> {
|
async callService(req: CallServiceData): Promise<CallServiceResult | null> {
|
||||||
const handler = this.getHandler(req.serviceId, req.fnName, req.particleContext.particleId);
|
const handler = this.getHandler(
|
||||||
|
req.serviceId,
|
||||||
|
req.fnName,
|
||||||
|
req.particleContext.particleId,
|
||||||
|
);
|
||||||
|
|
||||||
if (handler === null) {
|
if (handler === null) {
|
||||||
return null;
|
return null;
|
||||||
@ -73,6 +82,7 @@ export class JsServiceHost implements IJsServiceHost {
|
|||||||
const result = await handler(req);
|
const result = await handler(req);
|
||||||
|
|
||||||
// Otherwise AVM might break
|
// Otherwise AVM might break
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
if (result.result === undefined) {
|
if (result.result === undefined) {
|
||||||
result.result = null;
|
result.result = null;
|
||||||
}
|
}
|
||||||
@ -83,7 +93,11 @@ export class JsServiceHost implements IJsServiceHost {
|
|||||||
/**
|
/**
|
||||||
* Register handler for all particles
|
* Register handler for all particles
|
||||||
*/
|
*/
|
||||||
registerGlobalHandler(serviceId: string, fnName: string, handler: GenericCallServiceHandler): void {
|
registerGlobalHandler(
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
handler: GenericCallServiceHandler,
|
||||||
|
): void {
|
||||||
this.commonHandlers.set(serviceFnKey(serviceId, fnName), handler);
|
this.commonHandlers.set(serviceFnKey(serviceId, fnName), handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +111,7 @@ export class JsServiceHost implements IJsServiceHost {
|
|||||||
handler: GenericCallServiceHandler,
|
handler: GenericCallServiceHandler,
|
||||||
): void {
|
): void {
|
||||||
let psh = this.particleScopeHandlers.get(particleId);
|
let psh = this.particleScopeHandlers.get(particleId);
|
||||||
|
|
||||||
if (psh === undefined) {
|
if (psh === undefined) {
|
||||||
psh = new Map<string, GenericCallServiceHandler>();
|
psh = new Map<string, GenericCallServiceHandler>();
|
||||||
this.particleScopeHandlers.set(particleId, psh);
|
this.particleScopeHandlers.set(particleId, psh);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,9 +13,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
import type { SecurityTetraplet } from '@fluencelabs/avm';
|
import type { SecurityTetraplet } from "@fluencelabs/avm";
|
||||||
import { JSONValue } from '../util/commonTypes.js';
|
import type { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
|
import { JSONArray, JSONValue } from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JS Service host a low level interface for managing pure javascript services.
|
* JS Service host a low level interface for managing pure javascript services.
|
||||||
@ -33,7 +34,11 @@ export interface IJsServiceHost {
|
|||||||
* @param fnName Function name as specified in `call` air instruction
|
* @param fnName Function name as specified in `call` air instruction
|
||||||
* @param particleId Particle ID
|
* @param particleId Particle ID
|
||||||
*/
|
*/
|
||||||
getHandler(serviceId: string, fnName: string, particleId: string): GenericCallServiceHandler | null;
|
getHandler(
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
particleId: string,
|
||||||
|
): GenericCallServiceHandler | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute service call for specified call service data
|
* Execute service call for specified call service data
|
||||||
@ -43,7 +48,11 @@ export interface IJsServiceHost {
|
|||||||
/**
|
/**
|
||||||
* Register handler for all particles
|
* Register handler for all particles
|
||||||
*/
|
*/
|
||||||
registerGlobalHandler(serviceId: string, fnName: string, handler: GenericCallServiceHandler): void;
|
registerGlobalHandler(
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
handler: GenericCallServiceHandler,
|
||||||
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register handler which will be called only for particle with the specific id
|
* Register handler which will be called only for particle with the specific id
|
||||||
@ -114,7 +123,7 @@ export interface CallServiceData {
|
|||||||
/**
|
/**
|
||||||
* Arguments as specified in `call` air instruction
|
* Arguments as specified in `call` air instruction
|
||||||
*/
|
*/
|
||||||
args: any[];
|
args: JSONArray;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security Tetraplets received from AVM
|
* Security Tetraplets received from AVM
|
||||||
@ -135,7 +144,9 @@ export type CallServiceResultType = JSONValue;
|
|||||||
/**
|
/**
|
||||||
* Generic call service handler
|
* Generic call service handler
|
||||||
*/
|
*/
|
||||||
export type GenericCallServiceHandler = (req: CallServiceData) => CallServiceResult | Promise<CallServiceResult>;
|
export type GenericCallServiceHandler = (
|
||||||
|
req: CallServiceData,
|
||||||
|
) => CallServiceResult | Promise<CallServiceResult>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the result of the `call` air instruction to be returned into AVM
|
* Represents the result of the `call` air instruction to be returned into AVM
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,25 +13,35 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { FluencePeer } from '../jsPeer/FluencePeer.js';
|
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
import { JSONArray } from "@fluencelabs/interfaces";
|
||||||
import { builtInServices } from '../services/builtins.js';
|
|
||||||
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
|
import { IParticle } from "../particle/interfaces.js";
|
||||||
|
import { builtInServices } from "../services/builtins.js";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CallServiceData,
|
CallServiceData,
|
||||||
CallServiceResult,
|
CallServiceResult,
|
||||||
CallServiceResultType,
|
CallServiceResultType,
|
||||||
ParticleContext,
|
ParticleContext,
|
||||||
ResultCodes,
|
ResultCodes,
|
||||||
} from './interfaces.js';
|
} from "./interfaces.js";
|
||||||
|
|
||||||
export const doNothing = (..._args: Array<unknown>) => undefined;
|
export const doNothing = () => {
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
export const WrapFnIntoServiceCall =
|
export const WrapFnIntoServiceCall = (
|
||||||
(fn: (args: any[]) => CallServiceResultType) =>
|
fn: (args: JSONArray) => CallServiceResultType | undefined,
|
||||||
(req: CallServiceData): CallServiceResult => ({
|
) => {
|
||||||
|
return (req: CallServiceData): CallServiceResult => {
|
||||||
|
return {
|
||||||
retCode: ResultCodes.success,
|
retCode: ResultCodes.success,
|
||||||
result: fn(req.args),
|
result: fn(req.args) ?? null,
|
||||||
});
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export class ServiceError extends Error {
|
export class ServiceError extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import bs58 from "bs58";
|
import bs58 from "bs58";
|
||||||
import { fromUint8Array, toUint8Array } from 'js-base64';
|
import { fromUint8Array, toUint8Array } from "js-base64";
|
||||||
import { it, describe, expect } from "vitest";
|
import { it, describe, expect } from "vitest";
|
||||||
import { fromBase64Sk, KeyPair } from '../index.js';
|
|
||||||
|
|
||||||
import { Particle, serializeToString, buildParticleMessage } from '../../particle/Particle.js';
|
import { Particle, buildParticleMessage } from "../../particle/Particle.js";
|
||||||
|
import { fromBase64Sk, KeyPair } from "../index.js";
|
||||||
|
|
||||||
const key = "+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=";
|
const key = "+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=";
|
||||||
const keyBytes = toUint8Array(key);
|
const keyBytes = toUint8Array(key);
|
||||||
@ -27,8 +27,9 @@ const keyBytes = toUint8Array(key);
|
|||||||
const testData = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9, 10]);
|
const testData = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9, 10]);
|
||||||
|
|
||||||
const testDataSig = Uint8Array.from([
|
const testDataSig = Uint8Array.from([
|
||||||
224, 104, 245, 206, 140, 248, 27, 72, 68, 133, 111, 10, 164, 197, 242, 132, 107, 77, 224, 67, 99, 106, 76, 29, 144,
|
224, 104, 245, 206, 140, 248, 27, 72, 68, 133, 111, 10, 164, 197, 242, 132,
|
||||||
121, 122, 169, 36, 173, 58, 80, 170, 102, 137, 253, 157, 247, 168, 87, 162, 223, 188, 214, 203, 220, 52, 246, 29,
|
107, 77, 224, 67, 99, 106, 76, 29, 144, 121, 122, 169, 36, 173, 58, 80, 170,
|
||||||
|
102, 137, 253, 157, 247, 168, 87, 162, 223, 188, 214, 203, 220, 52, 246, 29,
|
||||||
86, 77, 71, 224, 248, 16, 213, 254, 75, 78, 239, 243, 222, 241, 15,
|
86, 77, 71, 224, 248, 16, 213, 254, 75, 78, 239, 243, 222, 241, 15,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -112,24 +113,49 @@ describe("KeyPair tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("validates particle signature checks", async function () {
|
it("validates particle signature checks", async function () {
|
||||||
const keyPair = await fromBase64Sk("7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE=");
|
const keyPair = await fromBase64Sk(
|
||||||
expect(bs58.encode(keyPair.getLibp2pPeerId().toBytes())).toBe("12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D");
|
"7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE=",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(bs58.encode(keyPair.getLibp2pPeerId().toBytes())).toBe(
|
||||||
|
"12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D",
|
||||||
|
);
|
||||||
|
|
||||||
const message = toUint8Array(btoa("message"));
|
const message = toUint8Array(btoa("message"));
|
||||||
const signature = await keyPair.signBytes(message);
|
const signature = await keyPair.signBytes(message);
|
||||||
|
|
||||||
const verified = await keyPair.verify(message, signature);
|
const verified = await keyPair.verify(message, signature);
|
||||||
expect(verified).toBe(true);
|
expect(verified).toBe(true);
|
||||||
expect(fromUint8Array(signature)).toBe("sBW7H6/1fwAwF86ldwVm9BDu0YH3w30oFQjTWX0Tiu9yTVZHmxkV2OX4GL5jn0Iz0CrasGcOfozzkZwtJBPMBg==");
|
|
||||||
|
|
||||||
const particle = await Particle.createNew("abc", keyPair.getPeerId(), 7000, keyPair, "2883f959-e9e7-4843-8c37-205d393ca372", 1696934545662);
|
expect(fromUint8Array(signature)).toBe(
|
||||||
|
"sBW7H6/1fwAwF86ldwVm9BDu0YH3w30oFQjTWX0Tiu9yTVZHmxkV2OX4GL5jn0Iz0CrasGcOfozzkZwtJBPMBg==",
|
||||||
|
);
|
||||||
|
|
||||||
|
const particle = await Particle.createNew(
|
||||||
|
"abc",
|
||||||
|
keyPair.getPeerId(),
|
||||||
|
7000,
|
||||||
|
keyPair,
|
||||||
|
"2883f959-e9e7-4843-8c37-205d393ca372",
|
||||||
|
1696934545662,
|
||||||
|
);
|
||||||
|
|
||||||
const particle_bytes = buildParticleMessage(particle);
|
const particle_bytes = buildParticleMessage(particle);
|
||||||
expect(fromUint8Array(particle_bytes)).toBe("Mjg4M2Y5NTktZTllNy00ODQzLThjMzctMjA1ZDM5M2NhMzcy/kguGYsBAABYGwAAYWJj");
|
|
||||||
|
|
||||||
const isParticleVerified = await KeyPair.verifyWithPublicKey(keyPair.getPublicKey(), particle_bytes, particle.signature);
|
expect(fromUint8Array(particle_bytes)).toBe(
|
||||||
|
"Mjg4M2Y5NTktZTllNy00ODQzLThjMzctMjA1ZDM5M2NhMzcy/kguGYsBAABYGwAAYWJj",
|
||||||
|
);
|
||||||
|
|
||||||
|
const isParticleVerified = await KeyPair.verifyWithPublicKey(
|
||||||
|
keyPair.getPublicKey(),
|
||||||
|
particle_bytes,
|
||||||
|
particle.signature,
|
||||||
|
);
|
||||||
|
|
||||||
expect(isParticleVerified).toBe(true);
|
expect(isParticleVerified).toBe(true);
|
||||||
|
|
||||||
expect(fromUint8Array(particle.signature)).toBe("KceXDnOfqe0dOnAxiDsyWBIvUq6WHoT0ge+VMHXOZsjZvCNH7/10oufdlYfcPomfv28On6E87ZhDcHGBZcb7Bw==");
|
expect(fromUint8Array(particle.signature)).toBe(
|
||||||
|
"KceXDnOfqe0dOnAxiDsyWBIvUq6WHoT0ge+VMHXOZsjZvCNH7/10oufdlYfcPomfv28On6E87ZhDcHGBZcb7Bw==",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,31 +1,55 @@
|
|||||||
import { it, describe, expect, beforeAll } from 'vitest';
|
/**
|
||||||
|
* Copyright 2023 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import * as fs from 'fs';
|
import * as fs from "fs";
|
||||||
import * as url from 'url';
|
import * as path from "path";
|
||||||
import * as path from 'path';
|
import * as url from "url";
|
||||||
import { compileAqua, withPeer } from '../../util/testUtils.js';
|
|
||||||
|
|
||||||
let aqua: any;
|
import { it, describe, expect, beforeAll } from "vitest";
|
||||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
|
||||||
|
|
||||||
describe('Marine js tests', () => {
|
import { compileAqua, CompiledFnCall, withPeer } from "../../util/testUtils.js";
|
||||||
|
|
||||||
|
let aqua: Record<string, CompiledFnCall>;
|
||||||
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
||||||
|
|
||||||
|
describe("Marine js tests", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const pathToAquaFiles = path.join(__dirname, '../../../aqua_test/marine-js.aqua');
|
const pathToAquaFiles = path.join(
|
||||||
const { services, functions } = await compileAqua(pathToAquaFiles);
|
__dirname,
|
||||||
|
"../../../aqua_test/marine-js.aqua",
|
||||||
|
);
|
||||||
|
|
||||||
|
const { functions } = await compileAqua(pathToAquaFiles);
|
||||||
aqua = functions;
|
aqua = functions;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call marine service correctly', async () => {
|
it("should call marine service correctly", async () => {
|
||||||
await withPeer(async (peer) => {
|
await withPeer(async (peer) => {
|
||||||
// arrange
|
// arrange
|
||||||
const wasm = await fs.promises.readFile(path.join(__dirname, '../../../data_for_test/greeting.wasm'));
|
const wasm = await fs.promises.readFile(
|
||||||
await peer.registerMarineService(wasm, 'greeting');
|
path.join(__dirname, "../../../data_for_test/greeting.wasm"),
|
||||||
|
);
|
||||||
|
|
||||||
|
await peer.registerMarineService(wasm, "greeting");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const res = await aqua.call(peer, { arg: 'test' });
|
const res = await aqua["call"](peer, { arg: "test" });
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(res).toBe('Hi, Hi, Hi, test');
|
expect(res).toBe("Hi, Hi, Hi, test");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
// @ts-ignore
|
|
||||||
import { BlobWorker } from 'threads';
|
|
||||||
import { fromBase64, toUint8Array } from 'js-base64';
|
|
||||||
// @ts-ignore
|
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
|
||||||
import { Buffer } from 'buffer';
|
|
||||||
import { LazyLoader } from '../interfaces.js';
|
|
||||||
|
|
||||||
export class InlinedWorkerLoader extends LazyLoader<WorkerImplementation> {
|
|
||||||
constructor(b64script: string) {
|
|
||||||
super(() => {
|
|
||||||
const script = fromBase64(b64script);
|
|
||||||
return BlobWorker.fromText(script);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InlinedWasmLoader extends LazyLoader<Buffer> {
|
|
||||||
constructor(b64wasm: string) {
|
|
||||||
super(() => {
|
|
||||||
const wasm = toUint8Array(b64wasm);
|
|
||||||
return Buffer.from(wasm);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,16 +13,15 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { createRequire } from 'module';
|
|
||||||
|
|
||||||
// @ts-ignore
|
import { Buffer } from "buffer";
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
import fs from "fs";
|
||||||
// @ts-ignore
|
import { createRequire } from "module";
|
||||||
import { Worker } from 'threads';
|
import path from "path";
|
||||||
import { Buffer } from 'buffer';
|
|
||||||
import * as fs from 'fs';
|
import { Worker, type Worker as WorkerImplementation } from "threads/master";
|
||||||
import * as path from 'path';
|
|
||||||
import { LazyLoader } from '../interfaces.js';
|
import { LazyLoader } from "../interfaces.js";
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
@ -39,7 +38,10 @@ const bufferToSharedArrayBuffer = (buffer: Buffer): SharedArrayBuffer => {
|
|||||||
* @param source - object specifying the source of the file. Consist two fields: package name and file path.
|
* @param source - object specifying the source of the file. Consist two fields: package name and file path.
|
||||||
* @returns SharedArrayBuffer with the wasm file
|
* @returns SharedArrayBuffer with the wasm file
|
||||||
*/
|
*/
|
||||||
export const loadWasmFromNpmPackage = async (source: { package: string; file: string }): Promise<SharedArrayBuffer> => {
|
export const loadWasmFromNpmPackage = async (source: {
|
||||||
|
package: string;
|
||||||
|
file: string;
|
||||||
|
}): Promise<SharedArrayBuffer> => {
|
||||||
const packagePath = require.resolve(source.package);
|
const packagePath = require.resolve(source.package);
|
||||||
const filePath = path.join(path.dirname(packagePath), source.file);
|
const filePath = path.join(path.dirname(packagePath), source.file);
|
||||||
return loadWasmFromFileSystem(filePath);
|
return loadWasmFromFileSystem(filePath);
|
||||||
@ -51,26 +53,34 @@ export const loadWasmFromNpmPackage = async (source: { package: string; file: st
|
|||||||
* @param filePath - path to the wasm file
|
* @param filePath - path to the wasm file
|
||||||
* @returns SharedArrayBuffer with the wasm fileWorker
|
* @returns SharedArrayBuffer with the wasm fileWorker
|
||||||
*/
|
*/
|
||||||
export const loadWasmFromFileSystem = async (filePath: string): Promise<SharedArrayBuffer> => {
|
export const loadWasmFromFileSystem = async (
|
||||||
|
filePath: string,
|
||||||
|
): Promise<SharedArrayBuffer> => {
|
||||||
const buffer = await fs.promises.readFile(filePath);
|
const buffer = await fs.promises.readFile(filePath);
|
||||||
return bufferToSharedArrayBuffer(buffer);
|
return bufferToSharedArrayBuffer(buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
export class WasmLoaderFromFs extends LazyLoader<SharedArrayBuffer> {
|
export class WasmLoaderFromFs extends LazyLoader<SharedArrayBuffer> {
|
||||||
constructor(filePath: string) {
|
constructor(filePath: string) {
|
||||||
super(() => loadWasmFromFileSystem(filePath));
|
super(() => {
|
||||||
|
return loadWasmFromFileSystem(filePath);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WasmLoaderFromNpm extends LazyLoader<SharedArrayBuffer> {
|
export class WasmLoaderFromNpm extends LazyLoader<SharedArrayBuffer> {
|
||||||
constructor(pkg: string, file: string) {
|
constructor(pkg: string, file: string) {
|
||||||
super(() => loadWasmFromNpmPackage({ package: pkg, file: file }));
|
super(() => {
|
||||||
|
return loadWasmFromNpmPackage({ package: pkg, file: file });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WorkerLoaderFromFs extends LazyLoader<WorkerImplementation> {
|
export class WorkerLoaderFromFs extends LazyLoader<WorkerImplementation> {
|
||||||
constructor(scriptPath: string) {
|
constructor(scriptPath: string) {
|
||||||
super(() => new Worker(scriptPath));
|
super(() => {
|
||||||
|
return new Worker(scriptPath);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
import { Buffer } from 'buffer';
|
|
||||||
import { LazyLoader } from '../interfaces.js';
|
|
||||||
// @ts-ignore
|
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
|
||||||
|
|
||||||
const bufferToSharedArrayBuffer = (buffer: Buffer): SharedArrayBuffer => {
|
|
||||||
const sab = new SharedArrayBuffer(buffer.length);
|
|
||||||
const tmp = new Uint8Array(sab);
|
|
||||||
tmp.set(buffer, 0);
|
|
||||||
return sab;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load wasm file from the server. Only works in browsers.
|
|
||||||
* The function will try load file into SharedArrayBuffer if the site is cross-origin isolated.
|
|
||||||
* Otherwise the return value fallbacks to Buffer which is less performant but is still compatible with FluenceAppService methods.
|
|
||||||
* We strongly recommend to set-up cross-origin headers. For more details see: See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements
|
|
||||||
* Filename is relative to current origin.
|
|
||||||
* @param filePath - path to the wasm file relative to current origin
|
|
||||||
* @returns Either SharedArrayBuffer or Buffer with the wasm file
|
|
||||||
*/
|
|
||||||
export const loadWasmFromUrl = async (filePath: string): Promise<SharedArrayBuffer | Buffer> => {
|
|
||||||
const fullUrl = window.location.origin + '/' + filePath;
|
|
||||||
const res = await fetch(fullUrl);
|
|
||||||
const ab = await res.arrayBuffer();
|
|
||||||
new Uint8Array(ab);
|
|
||||||
const buffer = Buffer.from(ab);
|
|
||||||
|
|
||||||
// only convert to shared buffers if necessary CORS headers have been set:
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements
|
|
||||||
if (crossOriginIsolated) {
|
|
||||||
return bufferToSharedArrayBuffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class WasmLoaderFromUrl extends LazyLoader<SharedArrayBuffer | Buffer> {
|
|
||||||
constructor(filePath: string) {
|
|
||||||
super(() => loadWasmFromUrl(filePath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WorkerLoaderFromUrl extends LazyLoader<WorkerImplementation> {
|
|
||||||
constructor(scriptPath: string) {
|
|
||||||
super(() => new Worker(scriptPath));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,10 +13,16 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { CallResultsArray, InterpreterResult, RunParameters } from '@fluencelabs/avm';
|
|
||||||
import { IStartable, JSONArray, JSONObject, CallParameters } from '../util/commonTypes.js';
|
import {
|
||||||
// @ts-ignore
|
CallResultsArray,
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
InterpreterResult,
|
||||||
|
RunParameters,
|
||||||
|
} from "@fluencelabs/avm";
|
||||||
|
import { JSONObject, JSONValue, JSONArray } from "@fluencelabs/interfaces";
|
||||||
|
import type { Worker as WorkerImplementation } from "threads/master";
|
||||||
|
|
||||||
|
import { IStartable, CallParameters } from "../util/commonTypes.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract for marine host implementations. Marine host is responsible for creating calling and removing marine services
|
* Contract for marine host implementations. Marine host is responsible for creating calling and removing marine services
|
||||||
@ -25,7 +31,10 @@ export interface IMarineHost extends IStartable {
|
|||||||
/**
|
/**
|
||||||
* Creates marine service from the given module and service id
|
* Creates marine service from the given module and service id
|
||||||
*/
|
*/
|
||||||
createService(serviceModule: ArrayBuffer | SharedArrayBuffer, serviceId: string): Promise<void>;
|
createService(
|
||||||
|
serviceModule: ArrayBuffer | SharedArrayBuffer,
|
||||||
|
serviceId: string,
|
||||||
|
): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes marine service with the given service id
|
* Removes marine service with the given service id
|
||||||
@ -45,7 +54,7 @@ export interface IMarineHost extends IStartable {
|
|||||||
functionName: string,
|
functionName: string,
|
||||||
args: JSONArray | JSONObject,
|
args: JSONArray | JSONObject,
|
||||||
callParams: CallParameters,
|
callParams: CallParameters,
|
||||||
): Promise<unknown>;
|
): Promise<JSONValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,12 +83,16 @@ export interface IValueLoader<T> {
|
|||||||
/**
|
/**
|
||||||
* Interface for something which can load wasm files
|
* Interface for something which can load wasm files
|
||||||
*/
|
*/
|
||||||
export interface IWasmLoader extends IValueLoader<ArrayBuffer | SharedArrayBuffer>, IStartable {}
|
export interface IWasmLoader
|
||||||
|
extends IValueLoader<ArrayBuffer | SharedArrayBuffer>,
|
||||||
|
IStartable {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for something which can thread.js based worker
|
* Interface for something which can thread.js based worker
|
||||||
*/
|
*/
|
||||||
export interface IWorkerLoader extends IValueLoader<WorkerImplementation>, IStartable {}
|
export interface IWorkerLoader
|
||||||
|
extends IValueLoader<WorkerImplementation | Promise<WorkerImplementation>>,
|
||||||
|
IStartable {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazy loader for some value. Value is loaded only when `start` method is called
|
* Lazy loader for some value. Value is loaded only when `start` method is called
|
||||||
@ -91,7 +104,9 @@ export class LazyLoader<T> implements IStartable, IValueLoader<T> {
|
|||||||
|
|
||||||
getValue(): T {
|
getValue(): T {
|
||||||
if (this.value == null) {
|
if (this.value == null) {
|
||||||
throw new Error('Value has not been loaded. Call `start` method to load the value.');
|
throw new Error(
|
||||||
|
"Value has not been loaded. Call `start` method to load the value.",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.value;
|
return this.value;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,14 +13,17 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
// @ts-ignore
|
|
||||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
import { Worker, type Worker as WorkerImplementation } from "threads/master";
|
||||||
// @ts-ignore
|
|
||||||
import { Worker } from 'threads';
|
import { LazyLoader } from "../interfaces.js";
|
||||||
import { LazyLoader } from '../interfaces.js';
|
|
||||||
|
|
||||||
export class WorkerLoader extends LazyLoader<WorkerImplementation> {
|
export class WorkerLoader extends LazyLoader<WorkerImplementation> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(() => new Worker('../../../node_modules/@fluencelabs/marine-worker/dist/index.js'));
|
super(() => {
|
||||||
|
return new Worker(
|
||||||
|
"../../../node_modules/@fluencelabs/marine-worker/dist/index.js",
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2022 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,41 +14,49 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { JSONArray, JSONObject, CallParameters } from '@fluencelabs/marine-js/dist/types';
|
import { JSONValue } from "@fluencelabs/interfaces";
|
||||||
import { LogFunction, logLevelToEnv } from '@fluencelabs/marine-js/dist/types';
|
import type {
|
||||||
import type { MarineBackgroundInterface } from '@fluencelabs/marine-worker';
|
JSONArray,
|
||||||
// @ts-ignore
|
JSONObject,
|
||||||
import { ModuleThread, spawn, Thread } from 'threads';
|
CallParameters,
|
||||||
|
} from "@fluencelabs/marine-js/dist/types";
|
||||||
|
import { LogFunction, logLevelToEnv } from "@fluencelabs/marine-js/dist/types";
|
||||||
|
import type { MarineBackgroundInterface } from "@fluencelabs/marine-worker";
|
||||||
|
import { ModuleThread, Thread, spawn } from "threads/master";
|
||||||
|
|
||||||
import { MarineLogger, marineLogger } from '../../util/logger.js';
|
import { MarineLogger, marineLogger } from "../../util/logger.js";
|
||||||
import { IMarineHost, IWasmLoader, IWorkerLoader } from '../interfaces.js';
|
import { IMarineHost, IWasmLoader, IWorkerLoader } from "../interfaces.js";
|
||||||
|
|
||||||
export class MarineBackgroundRunner implements IMarineHost {
|
export class MarineBackgroundRunner implements IMarineHost {
|
||||||
private workerThread?: MarineBackgroundInterface;
|
private workerThread?: ModuleThread<MarineBackgroundInterface>;
|
||||||
|
|
||||||
private loggers = new Map<string, MarineLogger>();
|
private loggers = new Map<string, MarineLogger>();
|
||||||
|
|
||||||
constructor(private workerLoader: IWorkerLoader, private controlModuleLoader: IWasmLoader, private avmWasmLoader: IWasmLoader) {}
|
constructor(
|
||||||
|
private workerLoader: IWorkerLoader,
|
||||||
|
private controlModuleLoader: IWasmLoader,
|
||||||
|
private avmWasmLoader: IWasmLoader,
|
||||||
|
) {}
|
||||||
|
|
||||||
async hasService(serviceId: string) {
|
async hasService(serviceId: string) {
|
||||||
if (!this.workerThread) {
|
if (this.workerThread == null) {
|
||||||
throw new Error('Worker is not initialized');
|
throw new Error("Worker is not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.workerThread.hasService(serviceId);
|
return this.workerThread.hasService(serviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeService(serviceId: string) {
|
async removeService(serviceId: string) {
|
||||||
if (!this.workerThread) {
|
if (this.workerThread == null) {
|
||||||
throw new Error('Worker is not initialized');
|
throw new Error("Worker is not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.workerThread.removeService(serviceId);
|
await this.workerThread.removeService(serviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
if (this.workerThread) {
|
if (this.workerThread != null) {
|
||||||
throw new Error('Worker thread already initialized');
|
throw new Error("Worker thread already initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.controlModuleLoader.start();
|
await this.controlModuleLoader.start();
|
||||||
@ -59,28 +67,36 @@ export class MarineBackgroundRunner implements IMarineHost {
|
|||||||
await this.workerLoader.start();
|
await this.workerLoader.start();
|
||||||
const worker = await this.workerLoader.getValue();
|
const worker = await this.workerLoader.getValue();
|
||||||
|
|
||||||
const workerThread = await spawn<MarineBackgroundInterface>(worker);
|
const workerThread: ModuleThread<MarineBackgroundInterface> =
|
||||||
|
await spawn<MarineBackgroundInterface>(worker);
|
||||||
|
|
||||||
const logfn: LogFunction = (message) => {
|
const logfn: LogFunction = (message) => {
|
||||||
const serviceLogger = this.loggers.get(message.service);
|
const serviceLogger = this.loggers.get(message.service);
|
||||||
if (!serviceLogger) {
|
|
||||||
|
if (serviceLogger == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceLogger[message.level](message.message);
|
serviceLogger[message.level](message.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
workerThread.onLogMessage().subscribe(logfn);
|
workerThread.onLogMessage().subscribe(logfn);
|
||||||
await workerThread.init(wasm);
|
await workerThread.init(wasm);
|
||||||
this.workerThread = workerThread;
|
this.workerThread = workerThread;
|
||||||
await this.createService(this.avmWasmLoader.getValue(), 'avm');
|
await this.createService(this.avmWasmLoader.getValue(), "avm");
|
||||||
}
|
}
|
||||||
|
|
||||||
async createService(serviceModule: ArrayBuffer | SharedArrayBuffer, serviceId: string): Promise<void> {
|
async createService(
|
||||||
if (!this.workerThread) {
|
serviceModule: ArrayBuffer | SharedArrayBuffer,
|
||||||
throw new Error('Worker is not initialized');
|
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.
|
// 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
|
// We enable all possible log levels passing the control for exact printouts to the logger
|
||||||
const env = logLevelToEnv('info');
|
const env = logLevelToEnv("info");
|
||||||
this.loggers.set(serviceId, marineLogger(serviceId));
|
this.loggers.set(serviceId, marineLogger(serviceId));
|
||||||
await this.workerThread.createService(serviceModule, serviceId, env);
|
await this.workerThread.createService(serviceModule, serviceId, env);
|
||||||
}
|
}
|
||||||
@ -90,16 +106,21 @@ export class MarineBackgroundRunner implements IMarineHost {
|
|||||||
functionName: string,
|
functionName: string,
|
||||||
args: JSONArray | JSONObject,
|
args: JSONArray | JSONObject,
|
||||||
callParams: CallParameters,
|
callParams: CallParameters,
|
||||||
): Promise<unknown> {
|
): Promise<JSONValue> {
|
||||||
if (!this.workerThread) {
|
if (this.workerThread == null) {
|
||||||
throw 'Worker is not initialized';
|
throw new Error("Worker is not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.workerThread.callService(serviceId, functionName, args, callParams);
|
return this.workerThread.callService(
|
||||||
|
serviceId,
|
||||||
|
functionName,
|
||||||
|
args,
|
||||||
|
callParams,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop(): Promise<void> {
|
async stop(): Promise<void> {
|
||||||
if (!this.workerThread) {
|
if (this.workerThread == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2020 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -18,21 +18,32 @@ import { CallResultsArray } from "@fluencelabs/avm";
|
|||||||
import { fromUint8Array, toUint8Array } from "js-base64";
|
import { fromUint8Array, toUint8Array } from "js-base64";
|
||||||
import { concat } from "uint8arrays/concat";
|
import { concat } from "uint8arrays/concat";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
import { KeyPair } from "../keypair/index.js";
|
import { KeyPair } from "../keypair/index.js";
|
||||||
import { numberToLittleEndianBytes } from "../util/bytes.js";
|
import { numberToLittleEndianBytes } from "../util/bytes.js";
|
||||||
|
|
||||||
import { IParticle } from "./interfaces.js";
|
import { IParticle } from "./interfaces.js";
|
||||||
|
|
||||||
|
const particleSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
timestamp: z.number().positive(),
|
||||||
|
script: z.string(),
|
||||||
|
data: z.string(),
|
||||||
|
ttl: z.number().positive(),
|
||||||
|
init_peer_id: z.string(),
|
||||||
|
signature: z.array(z.number()),
|
||||||
|
});
|
||||||
|
|
||||||
export class Particle implements IParticle {
|
export class Particle implements IParticle {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly id: string,
|
readonly id: string,
|
||||||
public readonly timestamp: number,
|
readonly timestamp: number,
|
||||||
public readonly script: string,
|
readonly script: string,
|
||||||
public readonly data: Uint8Array,
|
readonly data: Uint8Array,
|
||||||
public readonly ttl: number,
|
readonly ttl: number,
|
||||||
public readonly initPeerId: string,
|
readonly initPeerId: string,
|
||||||
public readonly signature: Uint8Array
|
readonly signature: Uint8Array,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
static async createNew(
|
static async createNew(
|
||||||
@ -62,17 +73,28 @@ export class Particle implements IParticle {
|
|||||||
|
|
||||||
static fromString(str: string): Particle {
|
static fromString(str: string): Particle {
|
||||||
const json = JSON.parse(str);
|
const json = JSON.parse(str);
|
||||||
const res = new Particle(
|
|
||||||
json.id,
|
|
||||||
json.timestamp,
|
|
||||||
json.script,
|
|
||||||
toUint8Array(json.data),
|
|
||||||
json.ttl,
|
|
||||||
json.init_peer_id,
|
|
||||||
new Uint8Array(json.signature)
|
|
||||||
);
|
|
||||||
|
|
||||||
return res;
|
const res = particleSchema.safeParse(json);
|
||||||
|
|
||||||
|
if (!res.success) {
|
||||||
|
throw new Error(
|
||||||
|
`Particle format invalid. Errors: ${JSON.stringify(
|
||||||
|
res.error.flatten(),
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = res.data;
|
||||||
|
|
||||||
|
return new Particle(
|
||||||
|
data.id,
|
||||||
|
data.timestamp,
|
||||||
|
data.script,
|
||||||
|
toUint8Array(data.data),
|
||||||
|
data.ttl,
|
||||||
|
data.init_peer_id,
|
||||||
|
new Uint8Array(data.signature),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,16 +131,6 @@ export const hasExpired = (particle: IParticle): boolean => {
|
|||||||
return getActualTTL(particle) <= 0;
|
return getActualTTL(particle) <= 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates that particle signature is correct
|
|
||||||
*/
|
|
||||||
export const verifySignature = async (particle: IParticle, publicKey: Uint8Array): Promise<boolean> => {
|
|
||||||
// TODO: Uncomment this when nox roll out particle signatures
|
|
||||||
return true;
|
|
||||||
// const message = buildParticleMessage(particle);
|
|
||||||
// return unmarshalPublicKey(publicKey).verify(message, particle.signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a particle clone with new data
|
* Creates a particle clone with new data
|
||||||
*/
|
*/
|
||||||
@ -182,5 +194,5 @@ export const handleTimeout = (fn: () => void) => {
|
|||||||
if (stage.stage === "expired") {
|
if (stage.stage === "expired") {
|
||||||
fn();
|
fn();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,7 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { PeerIdB58 } from '@fluencelabs/interfaces';
|
|
||||||
|
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable part of the particle.
|
* Immutable part of the particle.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,26 +14,30 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CallParams, IFluenceInternalApi } from '@fluencelabs/interfaces';
|
import { Buffer } from "buffer";
|
||||||
import { defaultGuard } from './SingleModuleSrv.js';
|
import * as fs from "fs";
|
||||||
import { NodeUtilsDef, registerNodeUtils } from './_aqua/node-utils.js';
|
|
||||||
import { SecurityGuard } from './securityGuard.js';
|
import { CallParams } from "@fluencelabs/interfaces";
|
||||||
import * as fs from 'fs';
|
|
||||||
import { FluencePeer } from '../jsPeer/FluencePeer.js';
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
import { Buffer } from 'buffer';
|
import { getErrorMessage } from "../util/utils.js";
|
||||||
|
|
||||||
|
import { NodeUtilsDef, registerNodeUtils } from "./_aqua/node-utils.js";
|
||||||
|
import { SecurityGuard } from "./securityGuard.js";
|
||||||
|
import { defaultGuard } from "./SingleModuleSrv.js";
|
||||||
|
|
||||||
export class NodeUtils implements NodeUtilsDef {
|
export class NodeUtils implements NodeUtilsDef {
|
||||||
constructor(private peer: FluencePeer) {
|
constructor(private peer: FluencePeer) {
|
||||||
this.securityGuard_readFile = defaultGuard(this.peer);
|
this.securityGuard_readFile = defaultGuard(this.peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
securityGuard_readFile: SecurityGuard<'path'>;
|
securityGuard_readFile: SecurityGuard<"path">;
|
||||||
|
|
||||||
async read_file(path: string, callParams: CallParams<'path'>) {
|
async read_file(path: string, callParams: CallParams<"path">) {
|
||||||
if (!this.securityGuard_readFile(callParams)) {
|
if (!this.securityGuard_readFile(callParams)) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Security guard validation failed',
|
error: "Security guard validation failed",
|
||||||
content: null,
|
content: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -42,22 +46,26 @@ export class NodeUtils implements NodeUtilsDef {
|
|||||||
// Strange enough, but Buffer type works here, while reading with encoding 'utf-8' doesn't
|
// Strange enough, but Buffer type works here, while reading with encoding 'utf-8' doesn't
|
||||||
const data = await new Promise<Buffer>((resolve, reject) => {
|
const data = await new Promise<Buffer>((resolve, reject) => {
|
||||||
fs.readFile(path, (err, data) => {
|
fs.readFile(path, (err, data) => {
|
||||||
if (err) {
|
if (err != null) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(data);
|
resolve(data);
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
// TODO: this is strange bug.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
content: data as unknown as string,
|
content: data as unknown as string,
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: err.message,
|
error: getErrorMessage(err),
|
||||||
content: null,
|
content: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -65,6 +73,6 @@ export class NodeUtils implements NodeUtilsDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HACK:: security guard functions must be ported to user API
|
// HACK:: security guard functions must be ported to user API
|
||||||
export const doRegisterNodeUtils = (peer: any) => {
|
export const doRegisterNodeUtils = (peer: FluencePeer) => {
|
||||||
registerNodeUtils(peer, 'node_utils', new NodeUtils(peer));
|
registerNodeUtils(peer, "node_utils", new NodeUtils(peer));
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,33 +14,52 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CallParams, PeerIdB58 } from '@fluencelabs/interfaces';
|
import { CallParams, PeerIdB58 } from "@fluencelabs/interfaces";
|
||||||
import { KeyPair } from '../keypair/index.js';
|
|
||||||
import { FluencePeer } from '../jsPeer/FluencePeer.js';
|
import { KeyPair } from "../keypair/index.js";
|
||||||
import { SigDef } from './_aqua/services.js';
|
|
||||||
import { allowOnlyParticleOriginatedAt, allowServiceFn, and, or, SecurityGuard } from './securityGuard.js';
|
import { SigDef } from "./_aqua/services.js";
|
||||||
|
import {
|
||||||
|
allowOnlyParticleOriginatedAt,
|
||||||
|
allowServiceFn,
|
||||||
|
and,
|
||||||
|
or,
|
||||||
|
SecurityGuard,
|
||||||
|
} from "./securityGuard.js";
|
||||||
|
|
||||||
export const defaultSigGuard = (peerId: PeerIdB58) => {
|
export const defaultSigGuard = (peerId: PeerIdB58) => {
|
||||||
return and<'data'>(
|
return and<"data">(
|
||||||
allowOnlyParticleOriginatedAt(peerId),
|
allowOnlyParticleOriginatedAt(peerId),
|
||||||
or(
|
or(
|
||||||
allowServiceFn('trust-graph', 'get_trust_bytes'),
|
allowServiceFn("trust-graph", "get_trust_bytes"),
|
||||||
allowServiceFn('trust-graph', 'get_revocation_bytes'),
|
allowServiceFn("trust-graph", "get_revocation_bytes"),
|
||||||
allowServiceFn('registry', 'get_key_bytes'),
|
allowServiceFn("registry", "get_key_bytes"),
|
||||||
allowServiceFn('registry', 'get_record_bytes'),
|
allowServiceFn("registry", "get_record_bytes"),
|
||||||
allowServiceFn('registry', 'get_record_metadata_bytes'),
|
allowServiceFn("registry", "get_record_metadata_bytes"),
|
||||||
allowServiceFn('registry', 'get_tombstone_bytes'),
|
allowServiceFn("registry", "get_tombstone_bytes"),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SignReturnType =
|
||||||
|
| {
|
||||||
|
error: null;
|
||||||
|
signature: number[];
|
||||||
|
success: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
error: string;
|
||||||
|
signature: null;
|
||||||
|
success: false;
|
||||||
|
};
|
||||||
|
|
||||||
export class Sig implements SigDef {
|
export class Sig implements SigDef {
|
||||||
constructor(private keyPair: KeyPair) {}
|
constructor(private keyPair: KeyPair) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configurable security guard for sign method
|
* Configurable security guard for sign method
|
||||||
*/
|
*/
|
||||||
securityGuard: SecurityGuard<'data'> = (params) => {
|
securityGuard: SecurityGuard<"data"> = () => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,12 +75,12 @@ export class Sig implements SigDef {
|
|||||||
*/
|
*/
|
||||||
async sign(
|
async sign(
|
||||||
data: number[],
|
data: number[],
|
||||||
callParams: CallParams<'data'>,
|
callParams: CallParams<"data">,
|
||||||
): Promise<{ error: string | null; signature: number[] | null; success: boolean }> {
|
): Promise<SignReturnType> {
|
||||||
if (!this.securityGuard(callParams)) {
|
if (!this.securityGuard(callParams)) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Security guard validation failed',
|
error: "Security guard validation failed",
|
||||||
signature: null,
|
signature: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -79,10 +98,9 @@ export class Sig implements SigDef {
|
|||||||
* Verifies the signature. Required by aqua
|
* Verifies the signature. Required by aqua
|
||||||
*/
|
*/
|
||||||
verify(signature: number[], data: number[]): Promise<boolean> {
|
verify(signature: number[], data: number[]): Promise<boolean> {
|
||||||
return this.keyPair.verify(Uint8Array.from(data), Uint8Array.from(signature))
|
return this.keyPair.verify(
|
||||||
|
Uint8Array.from(data),
|
||||||
|
Uint8Array.from(signature),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDefaultSig = (peer: FluencePeer) => {
|
|
||||||
peer.registerMarineService;
|
|
||||||
};
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,15 +14,22 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { Buffer } from "buffer";
|
||||||
import { SrvDef } from './_aqua/single-module-srv.js';
|
|
||||||
import { FluencePeer } from '../jsPeer/FluencePeer.js';
|
import { CallParams } from "@fluencelabs/interfaces";
|
||||||
import { CallParams } from '@fluencelabs/interfaces';
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { Buffer } from 'buffer';
|
|
||||||
import { allowOnlyParticleOriginatedAt, SecurityGuard } from './securityGuard.js';
|
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||||
|
import { getErrorMessage } from "../util/utils.js";
|
||||||
|
|
||||||
|
import { SrvDef } from "./_aqua/single-module-srv.js";
|
||||||
|
import {
|
||||||
|
allowOnlyParticleOriginatedAt,
|
||||||
|
SecurityGuard,
|
||||||
|
} from "./securityGuard.js";
|
||||||
|
|
||||||
export const defaultGuard = (peer: FluencePeer) => {
|
export const defaultGuard = (peer: FluencePeer) => {
|
||||||
return allowOnlyParticleOriginatedAt<any>(peer.keyPair.getPeerId());
|
return allowOnlyParticleOriginatedAt(peer.keyPair.getPeerId());
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Srv implements SrvDef {
|
export class Srv implements SrvDef {
|
||||||
@ -33,20 +40,23 @@ export class Srv implements SrvDef {
|
|||||||
this.securityGuard_remove = defaultGuard(this.peer);
|
this.securityGuard_remove = defaultGuard(this.peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
securityGuard_create: SecurityGuard<'wasm_b64_content'>;
|
securityGuard_create: SecurityGuard<"wasm_b64_content">;
|
||||||
|
|
||||||
async create(wasm_b64_content: string, callParams: CallParams<'wasm_b64_content'>) {
|
async create(
|
||||||
|
wasm_b64_content: string,
|
||||||
|
callParams: CallParams<"wasm_b64_content">,
|
||||||
|
) {
|
||||||
if (!this.securityGuard_create(callParams)) {
|
if (!this.securityGuard_create(callParams)) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Security guard validation failed',
|
error: "Security guard validation failed",
|
||||||
service_id: null,
|
service_id: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newServiceId = uuidv4();
|
const newServiceId = uuidv4();
|
||||||
const buffer = Buffer.from(wasm_b64_content, 'base64');
|
const buffer = Buffer.from(wasm_b64_content, "base64");
|
||||||
// TODO:: figure out why SharedArrayBuffer is not working here
|
// TODO:: figure out why SharedArrayBuffer is not working here
|
||||||
// const sab = new SharedArrayBuffer(buffer.length);
|
// const sab = new SharedArrayBuffer(buffer.length);
|
||||||
// const tmp = new Uint8Array(sab);
|
// const tmp = new Uint8Array(sab);
|
||||||
@ -59,22 +69,22 @@ export class Srv implements SrvDef {
|
|||||||
service_id: newServiceId,
|
service_id: newServiceId,
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
service_id: null,
|
service_id: null,
|
||||||
error: err.message,
|
error: getErrorMessage(err),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
securityGuard_remove: SecurityGuard<'service_id'>;
|
securityGuard_remove: SecurityGuard<"service_id">;
|
||||||
|
|
||||||
async remove(service_id: string, callParams: CallParams<'service_id'>) {
|
async remove(service_id: string, callParams: CallParams<"service_id">) {
|
||||||
if (!this.securityGuard_remove(callParams)) {
|
if (!this.securityGuard_remove(callParams)) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Security guard validation failed',
|
error: "Security guard validation failed",
|
||||||
service_id: null,
|
service_id: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -14,11 +14,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CallParams } from '@fluencelabs/interfaces';
|
import { CallParams } from "@fluencelabs/interfaces";
|
||||||
import { TracingDef } from './_aqua/tracing.js';
|
|
||||||
|
import { TracingDef } from "./_aqua/tracing.js";
|
||||||
|
|
||||||
export class Tracing implements TracingDef {
|
export class Tracing implements TracingDef {
|
||||||
tracingEvent(arrowName: string, event: string, callParams: CallParams<'arrowName' | 'event'>): void {
|
tracingEvent(
|
||||||
console.log('[%s] (%s) %s', callParams.particleId, arrowName, event);
|
arrowName: string,
|
||||||
|
event: string,
|
||||||
|
callParams: CallParams<"arrowName" | "event">,
|
||||||
|
): void {
|
||||||
|
// This console log is intentional
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log("[%s] (%s) %s", callParams.particleId, arrowName, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,30 @@
|
|||||||
import { it, describe, expect, test } from 'vitest';
|
/**
|
||||||
|
* Copyright 2023 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
import { CallParams } from '@fluencelabs/interfaces';
|
import assert from "assert";
|
||||||
import { toUint8Array } from 'js-base64';
|
|
||||||
import { KeyPair } from '../../keypair/index.js';
|
import { CallParams, JSONArray } from "@fluencelabs/interfaces";
|
||||||
import { Sig, defaultSigGuard } from '../Sig.js';
|
import { toUint8Array } from "js-base64";
|
||||||
import { allowServiceFn } from '../securityGuard.js';
|
import { it, describe, expect, test } from "vitest";
|
||||||
import { builtInServices } from '../builtins.js';
|
|
||||||
import { CallServiceData } from '../../jsServiceHost/interfaces.js';
|
import { CallServiceData } from "../../jsServiceHost/interfaces.js";
|
||||||
|
import { KeyPair } from "../../keypair/index.js";
|
||||||
|
import { builtInServices } from "../builtins.js";
|
||||||
|
import { allowServiceFn } from "../securityGuard.js";
|
||||||
|
import { Sig, defaultSigGuard } from "../Sig.js";
|
||||||
|
|
||||||
const a10b20 = `{
|
const a10b20 = `{
|
||||||
"a": 10,
|
"a": 10,
|
||||||
@ -20,97 +38,105 @@ const oneTwoThreeFour = `[
|
|||||||
4
|
4
|
||||||
]`;
|
]`;
|
||||||
|
|
||||||
describe('Tests for default handler', () => {
|
interface ServiceCallType {
|
||||||
|
serviceId: string;
|
||||||
|
fnName: string;
|
||||||
|
args: JSONArray;
|
||||||
|
retCode: 0 | 1;
|
||||||
|
result: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Tests for default handler", () => {
|
||||||
test.each`
|
test.each`
|
||||||
serviceId | fnName | args | retCode | result
|
serviceId | fnName | args | retCode | result
|
||||||
${'op'} | ${'identity'} | ${[]} | ${0} | ${{}}
|
${"op"} | ${"identity"} | ${[]} | ${0} | ${{}}
|
||||||
${'op'} | ${'identity'} | ${[1]} | ${0} | ${1}
|
${"op"} | ${"identity"} | ${[1]} | ${0} | ${1}
|
||||||
${'op'} | ${'identity'} | ${[1, 2]} | ${1} | ${'identity accepts up to 1 arguments, received 2 arguments'}
|
${"op"} | ${"identity"} | ${[1, 2]} | ${1} | ${"identity accepts up to 1 arguments, received 2 arguments"}
|
||||||
${'op'} | ${'noop'} | ${[1, 2]} | ${0} | ${{}}
|
${"op"} | ${"noop"} | ${[1, 2]} | ${0} | ${{}}
|
||||||
${'op'} | ${'array'} | ${[1, 2, 3]} | ${0} | ${[1, 2, 3]}
|
${"op"} | ${"array"} | ${[1, 2, 3]} | ${0} | ${[1, 2, 3]}
|
||||||
${'op'} | ${'array_length'} | ${[[1, 2, 3]]} | ${0} | ${3}
|
${"op"} | ${"array_length"} | ${[[1, 2, 3]]} | ${0} | ${3}
|
||||||
${'op'} | ${'array_length'} | ${[]} | ${1} | ${'array_length accepts exactly one argument, found: 0'}
|
${"op"} | ${"array_length"} | ${[]} | ${1} | ${"array_length accepts exactly one argument, found: 0"}
|
||||||
${'op'} | ${'concat'} | ${[[1, 2], [3, 4], [5, 6]]} | ${0} | ${[1, 2, 3, 4, 5, 6]}
|
${"op"} | ${"concat"} | ${[[1, 2], [3, 4], [5, 6]]} | ${0} | ${[1, 2, 3, 4, 5, 6]}
|
||||||
${'op'} | ${'concat'} | ${[[1, 2]]} | ${0} | ${[1, 2]}
|
${"op"} | ${"concat"} | ${[[1, 2]]} | ${0} | ${[1, 2]}
|
||||||
${'op'} | ${'concat'} | ${[]} | ${0} | ${[]}
|
${"op"} | ${"concat"} | ${[]} | ${0} | ${[]}
|
||||||
${'op'} | ${'concat'} | ${[1, [1, 2], 1]} | ${1} | ${"All arguments of 'concat' must be arrays: arguments 0, 2 are not"}
|
${"op"} | ${"concat"} | ${[1, [1, 2], 1]} | ${1} | ${"All arguments of 'concat' must be arrays: arguments 0, 2 are not"}
|
||||||
${'op'} | ${'string_to_b58'} | ${['test']} | ${0} | ${'3yZe7d'}
|
${"op"} | ${"string_to_b58"} | ${["test"]} | ${0} | ${"3yZe7d"}
|
||||||
${'op'} | ${'string_to_b58'} | ${['test', 1]} | ${1} | ${'string_to_b58 accepts only one string argument'}
|
${"op"} | ${"string_to_b58"} | ${["test", 1]} | ${1} | ${"string_to_b58 accepts only one string argument"}
|
||||||
${'op'} | ${'string_from_b58'} | ${['3yZe7d']} | ${0} | ${'test'}
|
${"op"} | ${"string_from_b58"} | ${["3yZe7d"]} | ${0} | ${"test"}
|
||||||
${'op'} | ${'string_from_b58'} | ${['3yZe7d', 1]} | ${1} | ${'string_from_b58 accepts only one string argument'}
|
${"op"} | ${"string_from_b58"} | ${["3yZe7d", 1]} | ${1} | ${"string_from_b58 accepts only one string argument"}
|
||||||
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116]]} | ${0} | ${'3yZe7d'}
|
${"op"} | ${"bytes_to_b58"} | ${[[116, 101, 115, 116]]} | ${0} | ${"3yZe7d"}
|
||||||
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116], 1]} | ${1} | ${'bytes_to_b58 accepts only single argument: array of numbers'}
|
${"op"} | ${"bytes_to_b58"} | ${[[116, 101, 115, 116], 1]} | ${1} | ${"bytes_to_b58 accepts only single argument: array of numbers"}
|
||||||
${'op'} | ${'bytes_from_b58'} | ${['3yZe7d']} | ${0} | ${[116, 101, 115, 116]}
|
${"op"} | ${"bytes_from_b58"} | ${["3yZe7d"]} | ${0} | ${[116, 101, 115, 116]}
|
||||||
${'op'} | ${'bytes_from_b58'} | ${['3yZe7d', 1]} | ${1} | ${'bytes_from_b58 accepts only one string argument'}
|
${"op"} | ${"bytes_from_b58"} | ${["3yZe7d", 1]} | ${1} | ${"bytes_from_b58 accepts only one string argument"}
|
||||||
${'op'} | ${'sha256_string'} | ${['hello, world!']} | ${0} | ${'QmVQ8pg6L1tpoWYeq6dpoWqnzZoSLCh7E96fCFXKvfKD3u'}
|
${"op"} | ${"sha256_string"} | ${["hello, world!"]} | ${0} | ${"QmVQ8pg6L1tpoWYeq6dpoWqnzZoSLCh7E96fCFXKvfKD3u"}
|
||||||
${'op'} | ${'sha256_string'} | ${['hello, world!', true]} | ${0} | ${'84V7ZxLW7qKsx1Qvbd63BdGaHxUc3TfT2MBPqAXM7Wyu'}
|
${"op"} | ${"sha256_string"} | ${["hello, world!", true]} | ${1} | ${"sha256_string accepts 1 argument, found: 2"}
|
||||||
${'op'} | ${'sha256_string'} | ${[]} | ${1} | ${'sha256_string accepts 1-3 arguments, found: 0'}
|
${"op"} | ${"sha256_string"} | ${[]} | ${1} | ${"sha256_string accepts 1 argument, found: 0"}
|
||||||
${'op'} | ${'concat_strings'} | ${[]} | ${0} | ${''}
|
${"op"} | ${"concat_strings"} | ${[]} | ${0} | ${""}
|
||||||
${'op'} | ${'concat_strings'} | ${['a', 'b', 'c']} | ${0} | ${'abc'}
|
${"op"} | ${"concat_strings"} | ${["a", "b", "c"]} | ${0} | ${"abc"}
|
||||||
${'peer'} | ${'timeout'} | ${[200, []]} | ${0} | ${[]}
|
${"peer"} | ${"timeout"} | ${[200, []]} | ${1} | ${"timeout accepts exactly two arguments: timeout duration in ms and a message string"}
|
||||||
${'peer'} | ${'timeout'} | ${[200, ['test']]} | ${0} | ${['test']}
|
${"peer"} | ${"timeout"} | ${[200, "test"]} | ${0} | ${"test"}
|
||||||
${'peer'} | ${'timeout'} | ${[]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}
|
${"peer"} | ${"timeout"} | ${[]} | ${1} | ${"timeout accepts exactly two arguments: timeout duration in ms and a message string"}
|
||||||
${'peer'} | ${'timeout'} | ${[200, 'test', 1]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}
|
${"peer"} | ${"timeout"} | ${[200, "test", 1]} | ${1} | ${"timeout accepts exactly two arguments: timeout duration in ms and a message string"}
|
||||||
${'debug'} | ${'stringify'} | ${[]} | ${0} | ${'"<empty argument list>"'}
|
${"debug"} | ${"stringify"} | ${[]} | ${0} | ${'"<empty argument list>"'}
|
||||||
${'debug'} | ${'stringify'} | ${[{ a: 10, b: 20 }]} | ${0} | ${a10b20}
|
${"debug"} | ${"stringify"} | ${[{ a: 10, b: 20 }]} | ${0} | ${a10b20}
|
||||||
${'debug'} | ${'stringify'} | ${[1, 2, 3, 4]} | ${0} | ${oneTwoThreeFour}
|
${"debug"} | ${"stringify"} | ${[1, 2, 3, 4]} | ${0} | ${oneTwoThreeFour}
|
||||||
${'math'} | ${'add'} | ${[2, 2]} | ${0} | ${4}
|
${"math"} | ${"add"} | ${[2, 2]} | ${0} | ${4}
|
||||||
${'math'} | ${'add'} | ${[2]} | ${1} | ${'Expected 2 argument(s). Got 1'}
|
${"math"} | ${"add"} | ${[2]} | ${1} | ${"Expected 2 argument(s). Got 1"}
|
||||||
${'math'} | ${'sub'} | ${[2, 2]} | ${0} | ${0}
|
${"math"} | ${"sub"} | ${[2, 2]} | ${0} | ${0}
|
||||||
${'math'} | ${'sub'} | ${[2, 3]} | ${0} | ${-1}
|
${"math"} | ${"sub"} | ${[2, 3]} | ${0} | ${-1}
|
||||||
${'math'} | ${'mul'} | ${[2, 2]} | ${0} | ${4}
|
${"math"} | ${"mul"} | ${[2, 2]} | ${0} | ${4}
|
||||||
${'math'} | ${'mul'} | ${[2, 0]} | ${0} | ${0}
|
${"math"} | ${"mul"} | ${[2, 0]} | ${0} | ${0}
|
||||||
${'math'} | ${'mul'} | ${[2, -1]} | ${0} | ${-2}
|
${"math"} | ${"mul"} | ${[2, -1]} | ${0} | ${-2}
|
||||||
${'math'} | ${'fmul'} | ${[10, 0.66]} | ${0} | ${6}
|
${"math"} | ${"fmul"} | ${[10, 0.66]} | ${0} | ${6}
|
||||||
${'math'} | ${'fmul'} | ${[0.5, 0.5]} | ${0} | ${0}
|
${"math"} | ${"fmul"} | ${[0.5, 0.5]} | ${0} | ${0}
|
||||||
${'math'} | ${'fmul'} | ${[100.5, 0.5]} | ${0} | ${50}
|
${"math"} | ${"fmul"} | ${[100.5, 0.5]} | ${0} | ${50}
|
||||||
${'math'} | ${'div'} | ${[2, 2]} | ${0} | ${1}
|
${"math"} | ${"div"} | ${[2, 2]} | ${0} | ${1}
|
||||||
${'math'} | ${'div'} | ${[2, 3]} | ${0} | ${0}
|
${"math"} | ${"div"} | ${[2, 3]} | ${0} | ${0}
|
||||||
${'math'} | ${'div'} | ${[10, 5]} | ${0} | ${2}
|
${"math"} | ${"div"} | ${[10, 5]} | ${0} | ${2}
|
||||||
${'math'} | ${'rem'} | ${[10, 3]} | ${0} | ${1}
|
${"math"} | ${"rem"} | ${[10, 3]} | ${0} | ${1}
|
||||||
${'math'} | ${'pow'} | ${[2, 2]} | ${0} | ${4}
|
${"math"} | ${"pow"} | ${[2, 2]} | ${0} | ${4}
|
||||||
${'math'} | ${'pow'} | ${[2, 0]} | ${0} | ${1}
|
${"math"} | ${"pow"} | ${[2, 0]} | ${0} | ${1}
|
||||||
${'math'} | ${'log'} | ${[2, 2]} | ${0} | ${1}
|
${"math"} | ${"log"} | ${[2, 2]} | ${0} | ${1}
|
||||||
${'math'} | ${'log'} | ${[2, 4]} | ${0} | ${2}
|
${"math"} | ${"log"} | ${[2, 4]} | ${0} | ${2}
|
||||||
${'cmp'} | ${'gt'} | ${[2, 4]} | ${0} | ${false}
|
${"cmp"} | ${"gt"} | ${[2, 4]} | ${0} | ${false}
|
||||||
${'cmp'} | ${'gte'} | ${[2, 4]} | ${0} | ${false}
|
${"cmp"} | ${"gte"} | ${[2, 4]} | ${0} | ${false}
|
||||||
${'cmp'} | ${'gte'} | ${[4, 2]} | ${0} | ${true}
|
${"cmp"} | ${"gte"} | ${[4, 2]} | ${0} | ${true}
|
||||||
${'cmp'} | ${'gte'} | ${[2, 2]} | ${0} | ${true}
|
${"cmp"} | ${"gte"} | ${[2, 2]} | ${0} | ${true}
|
||||||
${'cmp'} | ${'lt'} | ${[2, 4]} | ${0} | ${true}
|
${"cmp"} | ${"lt"} | ${[2, 4]} | ${0} | ${true}
|
||||||
${'cmp'} | ${'lte'} | ${[2, 4]} | ${0} | ${true}
|
${"cmp"} | ${"lte"} | ${[2, 4]} | ${0} | ${true}
|
||||||
${'cmp'} | ${'lte'} | ${[4, 2]} | ${0} | ${false}
|
${"cmp"} | ${"lte"} | ${[4, 2]} | ${0} | ${false}
|
||||||
${'cmp'} | ${'lte'} | ${[2, 2]} | ${0} | ${true}
|
${"cmp"} | ${"lte"} | ${[2, 2]} | ${0} | ${true}
|
||||||
${'cmp'} | ${'cmp'} | ${[2, 4]} | ${0} | ${-1}
|
${"cmp"} | ${"cmp"} | ${[2, 4]} | ${0} | ${-1}
|
||||||
${'cmp'} | ${'cmp'} | ${[2, -4]} | ${0} | ${1}
|
${"cmp"} | ${"cmp"} | ${[2, -4]} | ${0} | ${1}
|
||||||
${'cmp'} | ${'cmp'} | ${[2, 2]} | ${0} | ${0}
|
${"cmp"} | ${"cmp"} | ${[2, 2]} | ${0} | ${0}
|
||||||
${'array'} | ${'sum'} | ${[[1, 2, 3]]} | ${0} | ${6}
|
${"array"} | ${"sum"} | ${[[1, 2, 3]]} | ${0} | ${6}
|
||||||
${'array'} | ${'dedup'} | ${[['a', 'a', 'b', 'c', 'a', 'b', 'c']]} | ${0} | ${['a', 'b', 'c']}
|
${"array"} | ${"dedup"} | ${[["a", "a", "b", "c", "a", "b", "c"]]} | ${0} | ${["a", "b", "c"]}
|
||||||
${'array'} | ${'intersect'} | ${[['a', 'b', 'c'], ['c', 'b', 'd']]} | ${0} | ${['b', 'c']}
|
${"array"} | ${"intersect"} | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["b", "c"]}
|
||||||
${'array'} | ${'diff'} | ${[['a', 'b', 'c'], ['c', 'b', 'd']]} | ${0} | ${['a']}
|
${"array"} | ${"diff"} | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a"]}
|
||||||
${'array'} | ${'sdiff'} | ${[['a', 'b', 'c'], ['c', 'b', 'd']]} | ${0} | ${['a', 'd']}
|
${"array"} | ${"sdiff"} | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a", "d"]}
|
||||||
${'json'} | ${'obj'} | ${['a', 10, 'b', 'string', 'c', null]} | ${0} | ${{ a: 10, b: 'string', c: null }}
|
${"json"} | ${"obj"} | ${["a", 10, "b", "string", "c", null]} | ${0} | ${{ a: 10, b: "string", c: null }}
|
||||||
${'json'} | ${'obj'} | ${['a', 10, 'b', 'string', 'c']} | ${1} | ${'Expected even number of argument(s). Got 5'}
|
${"json"} | ${"obj"} | ${["a", 10, "b", "string", "c"]} | ${1} | ${"Expected even number of argument(s). Got 5"}
|
||||||
${'json'} | ${'obj'} | ${[]} | ${0} | ${{}}
|
${"json"} | ${"obj"} | ${[]} | ${0} | ${{}}
|
||||||
${'json'} | ${'put'} | ${[{}, 'a', 10]} | ${0} | ${{ a: 10 }}
|
${"json"} | ${"put"} | ${[{}, "a", 10]} | ${0} | ${{ a: 10 }}
|
||||||
${'json'} | ${'put'} | ${[{ b: 11 }, 'a', 10]} | ${0} | ${{ a: 10, b: 11 }}
|
${"json"} | ${"put"} | ${[{ b: 11 }, "a", 10]} | ${0} | ${{ a: 10, b: 11 }}
|
||||||
${'json'} | ${'put'} | ${['a', 'a', 11]} | ${1} | ${'Argument 0 expected to be of type object, Got string'}
|
${"json"} | ${"put"} | ${["a", "a", 11]} | ${1} | ${"Argument 0 expected to be of type object, Got string"}
|
||||||
${'json'} | ${'put'} | ${[{}, 'a', 10, 'b', 20]} | ${1} | ${'Expected 3 argument(s). Got 5'}
|
${"json"} | ${"put"} | ${[{}, "a", 10, "b", 20]} | ${1} | ${"Expected 3 argument(s). Got 5"}
|
||||||
${'json'} | ${'put'} | ${[{}]} | ${1} | ${'Expected 3 argument(s). Got 1'}
|
${"json"} | ${"put"} | ${[{}]} | ${1} | ${"Expected 3 argument(s). Got 1"}
|
||||||
${'json'} | ${'puts'} | ${[{}, 'a', 10]} | ${0} | ${{ a: 10 }}
|
${"json"} | ${"puts"} | ${[{}, "a", 10]} | ${0} | ${{ a: 10 }}
|
||||||
${'json'} | ${'puts'} | ${[{ b: 11 }, 'a', 10]} | ${0} | ${{ a: 10, b: 11 }}
|
${"json"} | ${"puts"} | ${[{ b: 11 }, "a", 10]} | ${0} | ${{ a: 10, b: 11 }}
|
||||||
${'json'} | ${'puts'} | ${[{}, 'a', 10, 'b', 'string', 'c', null]} | ${0} | ${{ a: 10, b: 'string', c: null }}
|
${"json"} | ${"puts"} | ${[{}, "a", 10, "b", "string", "c", null]} | ${0} | ${{ a: 10, b: "string", c: null }}
|
||||||
${'json'} | ${'puts'} | ${[{ x: 'text' }, 'a', 10, 'b', 'string']} | ${0} | ${{ a: 10, b: 'string', x: 'text' }}
|
${"json"} | ${"puts"} | ${[{ x: "text" }, "a", 10, "b", "string"]} | ${0} | ${{ a: 10, b: "string", x: "text" }}
|
||||||
${'json'} | ${'puts'} | ${[{}]} | ${1} | ${'Expected more than 3 argument(s). Got 1'}
|
${"json"} | ${"puts"} | ${[{}]} | ${1} | ${"Expected more than 3 argument(s). Got 1"}
|
||||||
${'json'} | ${'puts'} | ${['a', 'a', 11]} | ${1} | ${'Argument 0 expected to be of type object, Got string'}
|
${"json"} | ${"puts"} | ${["a", "a", 11]} | ${1} | ${"Argument 0 expected to be of type object, Got string"}
|
||||||
${'json'} | ${'stringify'} | ${[{ a: 10, b: 'string', c: null }]} | ${0} | ${'{"a":10,"b":"string","c":null}'}
|
${"json"} | ${"stringify"} | ${[{ a: 10, b: "string", c: null }]} | ${0} | ${'{"a":10,"b":"string","c":null}'}
|
||||||
${'json'} | ${'stringify'} | ${[1]} | ${1} | ${'Argument 0 expected to be of type object, Got number'}
|
${"json"} | ${"stringify"} | ${[1]} | ${1} | ${"Argument 0 expected to be of type object, Got number"}
|
||||||
${'json'} | ${'parse'} | ${['{"a":10,"b":"string","c":null}']} | ${0} | ${{ a: 10, b: 'string', c: null }}
|
${"json"} | ${"parse"} | ${['{"a":10,"b":"string","c":null}']} | ${0} | ${{ a: 10, b: "string", c: null }}
|
||||||
${'json'} | ${'parse'} | ${['incorrect']} | ${1} | ${'Unexpected token i in JSON at position 0'}
|
${"json"} | ${"parse"} | ${["incorrect"]} | ${1} | ${"Unexpected token i in JSON at position 0"}
|
||||||
${'json'} | ${'parse'} | ${[10]} | ${1} | ${'Argument 0 expected to be of type string, Got number'}
|
${"json"} | ${"parse"} | ${[10]} | ${1} | ${"Argument 0 expected to be of type string, Got number"}
|
||||||
`(
|
`(
|
||||||
//
|
//
|
||||||
'$fnName with $args expected retcode: $retCode and result: $result',
|
"$fnName with $args expected retcode: $retCode and result: $result",
|
||||||
async ({ serviceId, fnName, args, retCode, result }) => {
|
async ({ serviceId, fnName, args, retCode, result }: ServiceCallType) => {
|
||||||
// arrange
|
// arrange
|
||||||
const req: CallServiceData = {
|
const req: CallServiceData = {
|
||||||
serviceId: serviceId,
|
serviceId: serviceId,
|
||||||
@ -118,8 +144,8 @@ describe('Tests for default handler', () => {
|
|||||||
args: args,
|
args: args,
|
||||||
tetraplets: [],
|
tetraplets: [],
|
||||||
particleContext: {
|
particleContext: {
|
||||||
particleId: 'some',
|
particleId: "some",
|
||||||
initPeerId: 'init peer id',
|
initPeerId: "init peer id",
|
||||||
timestamp: 595951200,
|
timestamp: 595951200,
|
||||||
ttl: 595961200,
|
ttl: 595961200,
|
||||||
signature: new Uint8Array([]),
|
signature: new Uint8Array([]),
|
||||||
@ -132,8 +158,10 @@ describe('Tests for default handler', () => {
|
|||||||
|
|
||||||
// Our test cases above depend on node error message. In node 20 error message for JSON.parse has changed.
|
// Our test cases above depend on node error message. In node 20 error message for JSON.parse has changed.
|
||||||
// Simple and fast solution for this specific case is to unify both variations into node 18 version error format.
|
// Simple and fast solution for this specific case is to unify both variations into node 18 version error format.
|
||||||
if (res.result === 'Unexpected token \'i\', "incorrect" is not valid JSON') {
|
if (
|
||||||
res.result = 'Unexpected token i in JSON at position 0';
|
res.result === "Unexpected token 'i', \"incorrect\" is not valid JSON"
|
||||||
|
) {
|
||||||
|
res.result = "Unexpected token i in JSON at position 0";
|
||||||
}
|
}
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
@ -144,16 +172,16 @@ describe('Tests for default handler', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
it('should return correct error message for identiy service', async () => {
|
it("should return correct error message for identiy service", async () => {
|
||||||
// arrange
|
// arrange
|
||||||
const req: CallServiceData = {
|
const req: CallServiceData = {
|
||||||
serviceId: 'peer',
|
serviceId: "peer",
|
||||||
fnName: 'identify',
|
fnName: "identify",
|
||||||
args: [],
|
args: [],
|
||||||
tetraplets: [],
|
tetraplets: [],
|
||||||
particleContext: {
|
particleContext: {
|
||||||
particleId: 'some',
|
particleId: "some",
|
||||||
initPeerId: 'init peer id',
|
initPeerId: "init peer id",
|
||||||
timestamp: 595951200,
|
timestamp: 595951200,
|
||||||
ttl: 595961200,
|
ttl: 595961200,
|
||||||
signature: new Uint8Array([]),
|
signature: new Uint8Array([]),
|
||||||
@ -169,22 +197,27 @@ describe('Tests for default handler', () => {
|
|||||||
retCode: 0,
|
retCode: 0,
|
||||||
result: {
|
result: {
|
||||||
external_addresses: [],
|
external_addresses: [],
|
||||||
node_version: expect.stringContaining('js'),
|
// stringContaining method returns any for some reason
|
||||||
air_version: expect.stringContaining('js'),
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
node_version: expect.stringContaining("js"),
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
air_version: expect.stringContaining("js"),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const key = '+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=';
|
const key = "+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=";
|
||||||
|
|
||||||
const context = (async () => {
|
const context = (async () => {
|
||||||
const keyBytes = toUint8Array(key);
|
const keyBytes = toUint8Array(key);
|
||||||
const kp = await KeyPair.fromEd25519SK(keyBytes);
|
const kp = await KeyPair.fromEd25519SK(keyBytes);
|
||||||
|
|
||||||
const res = {
|
const res = {
|
||||||
peerKeyPair: kp,
|
peerKeyPair: kp,
|
||||||
peerId: kp.getPeerId(),
|
peerId: kp.getPeerId(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -192,44 +225,58 @@ const testData = [1, 2, 3, 4, 5, 6, 7, 9, 10];
|
|||||||
|
|
||||||
// signature produced by KeyPair created from key above (`key` variable)
|
// signature produced by KeyPair created from key above (`key` variable)
|
||||||
const testDataSig = [
|
const testDataSig = [
|
||||||
224, 104, 245, 206, 140, 248, 27, 72, 68, 133, 111, 10, 164, 197, 242, 132, 107, 77, 224, 67, 99, 106, 76, 29, 144,
|
224, 104, 245, 206, 140, 248, 27, 72, 68, 133, 111, 10, 164, 197, 242, 132,
|
||||||
121, 122, 169, 36, 173, 58, 80, 170, 102, 137, 253, 157, 247, 168, 87, 162, 223, 188, 214, 203, 220, 52, 246, 29,
|
107, 77, 224, 67, 99, 106, 76, 29, 144, 121, 122, 169, 36, 173, 58, 80, 170,
|
||||||
|
102, 137, 253, 157, 247, 168, 87, 162, 223, 188, 214, 203, 220, 52, 246, 29,
|
||||||
86, 77, 71, 224, 248, 16, 213, 254, 75, 78, 239, 243, 222, 241, 15,
|
86, 77, 71, 224, 248, 16, 213, 254, 75, 78, 239, 243, 222, 241, 15,
|
||||||
];
|
];
|
||||||
|
|
||||||
// signature produced by KeyPair created from some random KeyPair
|
// signature produced by KeyPair created from some random KeyPair
|
||||||
const testDataWrongSig = [
|
const testDataWrongSig = [
|
||||||
116, 247, 189, 118, 236, 53, 147, 123, 219, 75, 176, 105, 101, 108, 233, 137, 97, 14, 146, 132, 252, 70, 51, 153,
|
116, 247, 189, 118, 236, 53, 147, 123, 219, 75, 176, 105, 101, 108, 233, 137,
|
||||||
237, 167, 156, 150, 36, 90, 229, 108, 166, 231, 255, 137, 8, 246, 125, 0, 213, 150, 83, 196, 237, 221, 131, 159,
|
97, 14, 146, 132, 252, 70, 51, 153, 237, 167, 156, 150, 36, 90, 229, 108, 166,
|
||||||
157, 159, 25, 109, 95, 160, 181, 65, 254, 238, 47, 156, 240, 151, 58, 14,
|
231, 255, 137, 8, 246, 125, 0, 213, 150, 83, 196, 237, 221, 131, 159, 157,
|
||||||
|
159, 25, 109, 95, 160, 181, 65, 254, 238, 47, 156, 240, 151, 58, 14,
|
||||||
];
|
];
|
||||||
|
|
||||||
const makeTetraplet = (initPeerId: string, serviceId?: string, fnName?: string): CallParams<'data'> => {
|
const makeTestTetraplet = (
|
||||||
|
initPeerId: string,
|
||||||
|
serviceId: string,
|
||||||
|
fnName: string,
|
||||||
|
): CallParams<"data"> => {
|
||||||
return {
|
return {
|
||||||
|
particleId: "",
|
||||||
|
timestamp: 0,
|
||||||
|
ttl: 0,
|
||||||
initPeerId: initPeerId,
|
initPeerId: initPeerId,
|
||||||
tetraplets: {
|
tetraplets: {
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
|
peer_pk: initPeerId,
|
||||||
function_name: fnName,
|
function_name: fnName,
|
||||||
service_id: serviceId,
|
service_id: serviceId,
|
||||||
|
json_path: "",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
} as any;
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Sig service tests', () => {
|
describe("Sig service tests", () => {
|
||||||
it('sig.sign should create the correct signature', async () => {
|
it("sig.sign should create the correct signature", async () => {
|
||||||
const ctx = await context;
|
const ctx = await context;
|
||||||
const sig = new Sig(ctx.peerKeyPair);
|
const sig = new Sig(ctx.peerKeyPair);
|
||||||
|
|
||||||
const res = await sig.sign(testData, makeTetraplet(ctx.peerId));
|
const res = await sig.sign(
|
||||||
|
testData,
|
||||||
|
makeTestTetraplet(ctx.peerId, "any_service", "any_func"),
|
||||||
|
);
|
||||||
|
|
||||||
expect(res.success).toBe(true);
|
expect(res.success).toBe(true);
|
||||||
expect(res.signature).toStrictEqual(testDataSig);
|
expect(res.signature).toStrictEqual(testDataSig);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sig.verify should return true for the correct signature', async () => {
|
it("sig.verify should return true for the correct signature", async () => {
|
||||||
const ctx = await context;
|
const ctx = await context;
|
||||||
const sig = new Sig(ctx.peerKeyPair);
|
const sig = new Sig(ctx.peerKeyPair);
|
||||||
|
|
||||||
@ -238,7 +285,7 @@ describe('Sig service tests', () => {
|
|||||||
expect(res).toBe(true);
|
expect(res).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sig.verify should return false for the incorrect signature', async () => {
|
it("sig.verify should return false for the incorrect signature", async () => {
|
||||||
const ctx = await context;
|
const ctx = await context;
|
||||||
const sig = new Sig(ctx.peerKeyPair);
|
const sig = new Sig(ctx.peerKeyPair);
|
||||||
|
|
||||||
@ -247,63 +294,93 @@ describe('Sig service tests', () => {
|
|||||||
expect(res).toBe(false);
|
expect(res).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sign-verify call chain should work', async () => {
|
it("sign-verify call chain should work", async () => {
|
||||||
const ctx = await context;
|
const ctx = await context;
|
||||||
const sig = new Sig(ctx.peerKeyPair);
|
const sig = new Sig(ctx.peerKeyPair);
|
||||||
|
|
||||||
const signature = await sig.sign(testData, makeTetraplet(ctx.peerId));
|
const signature = await sig.sign(
|
||||||
const res = await sig.verify(signature.signature as number[], testData);
|
testData,
|
||||||
|
makeTestTetraplet(ctx.peerId, "any_service", "any_func"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(signature.success).toBe(true);
|
||||||
|
assert(signature.success);
|
||||||
|
const res = await sig.verify(signature.signature, testData);
|
||||||
|
|
||||||
expect(res).toBe(true);
|
expect(res).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sig.sign with defaultSigGuard should work for correct callParams', async () => {
|
it("sig.sign with defaultSigGuard should work for correct callParams", async () => {
|
||||||
const ctx = await context;
|
const ctx = await context;
|
||||||
const sig = new Sig(ctx.peerKeyPair);
|
const sig = new Sig(ctx.peerKeyPair);
|
||||||
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
||||||
|
|
||||||
const signature = await sig.sign(testData, makeTetraplet(ctx.peerId, 'registry', 'get_route_bytes'));
|
const signature = await sig.sign(
|
||||||
|
testData,
|
||||||
|
makeTestTetraplet(ctx.peerId, "registry", "get_route_bytes"),
|
||||||
|
);
|
||||||
|
|
||||||
await expect(signature).toBeDefined();
|
expect(signature).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sig.sign with defaultSigGuard should not allow particles initiated from incorrect service', async () => {
|
it("sig.sign with defaultSigGuard should not allow particles initiated from incorrect service", async () => {
|
||||||
const ctx = await context;
|
|
||||||
const sig = new Sig(ctx.peerKeyPair);
|
|
||||||
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
|
||||||
|
|
||||||
const res = await sig.sign(testData, makeTetraplet(ctx.peerId, 'other_service', 'other_fn'));
|
|
||||||
|
|
||||||
await expect(res.success).toBe(false);
|
|
||||||
await expect(res.error).toBe('Security guard validation failed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sig.sign with defaultSigGuard should not allow particles initiated from other peers', async () => {
|
|
||||||
const ctx = await context;
|
const ctx = await context;
|
||||||
const sig = new Sig(ctx.peerKeyPair);
|
const sig = new Sig(ctx.peerKeyPair);
|
||||||
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
||||||
|
|
||||||
const res = await sig.sign(
|
const res = await sig.sign(
|
||||||
testData,
|
testData,
|
||||||
makeTetraplet((await KeyPair.randomEd25519()).getPeerId(), 'registry', 'get_key_bytes'),
|
makeTestTetraplet(ctx.peerId, "other_service", "other_fn"),
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(res.success).toBe(false);
|
expect(res.success).toBe(false);
|
||||||
await expect(res.error).toBe('Security guard validation failed');
|
expect(res.error).toBe("Security guard validation failed");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changing securityGuard should work', async () => {
|
it("sig.sign with defaultSigGuard should not allow particles initiated from other peers", async () => {
|
||||||
const ctx = await context;
|
const ctx = await context;
|
||||||
const sig = new Sig(ctx.peerKeyPair);
|
const sig = new Sig(ctx.peerKeyPair);
|
||||||
sig.securityGuard = allowServiceFn('test', 'test');
|
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
||||||
|
|
||||||
const successful1 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'test', 'test'));
|
const res = await sig.sign(
|
||||||
const unSuccessful1 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'wrong', 'wrong'));
|
testData,
|
||||||
|
makeTestTetraplet(
|
||||||
|
(await KeyPair.randomEd25519()).getPeerId(),
|
||||||
|
"registry",
|
||||||
|
"get_key_bytes",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
sig.securityGuard = allowServiceFn('wrong', 'wrong');
|
expect(res.success).toBe(false);
|
||||||
|
expect(res.error).toBe("Security guard validation failed");
|
||||||
|
});
|
||||||
|
|
||||||
const successful2 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'wrong', 'wrong'));
|
it("changing securityGuard should work", async () => {
|
||||||
const unSuccessful2 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'test', 'test'));
|
const ctx = await context;
|
||||||
|
const sig = new Sig(ctx.peerKeyPair);
|
||||||
|
sig.securityGuard = allowServiceFn("test", "test");
|
||||||
|
|
||||||
|
const successful1 = await sig.sign(
|
||||||
|
testData,
|
||||||
|
makeTestTetraplet(ctx.peerId, "test", "test"),
|
||||||
|
);
|
||||||
|
|
||||||
|
const unSuccessful1 = await sig.sign(
|
||||||
|
testData,
|
||||||
|
makeTestTetraplet(ctx.peerId, "wrong", "wrong"),
|
||||||
|
);
|
||||||
|
|
||||||
|
sig.securityGuard = allowServiceFn("wrong", "wrong");
|
||||||
|
|
||||||
|
const successful2 = await sig.sign(
|
||||||
|
testData,
|
||||||
|
makeTestTetraplet(ctx.peerId, "wrong", "wrong"),
|
||||||
|
);
|
||||||
|
|
||||||
|
const unSuccessful2 = await sig.sign(
|
||||||
|
testData,
|
||||||
|
makeTestTetraplet(ctx.peerId, "test", "test"),
|
||||||
|
);
|
||||||
|
|
||||||
expect(successful1.success).toBe(true);
|
expect(successful1.success).toBe(true);
|
||||||
expect(successful2.success).toBe(true);
|
expect(successful2.success).toBe(true);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user