mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-04-24 06:22:13 +00:00
chore: Remove aqua cli (#874)
* Remove aqua cli * fix * Fix --------- Co-authored-by: DieMyst <dmitry.shakhtarin@fluence.ai>
This commit is contained in:
parent
9f863eae43
commit
1e2e35ee88
70
.github/workflows/binary.yml
vendored
70
.github/workflows/binary.yml
vendored
@ -1,70 +0,0 @@
|
||||
name: "Publish binary aqua"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
runs-on:
|
||||
type: string
|
||||
required: true
|
||||
arch:
|
||||
type: string
|
||||
required: true
|
||||
os:
|
||||
type: string
|
||||
required: true
|
||||
static:
|
||||
type: boolean
|
||||
required: true
|
||||
workflow_call:
|
||||
inputs:
|
||||
runs-on:
|
||||
type: string
|
||||
required: true
|
||||
arch:
|
||||
type: string
|
||||
required: true
|
||||
os:
|
||||
type: string
|
||||
required: true
|
||||
static:
|
||||
type: boolean
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Publish aqua-native"
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: graalvm/setup-graalvm@v1
|
||||
with:
|
||||
version: '22.3.1'
|
||||
java-version: '17'
|
||||
set-java-home: true
|
||||
components: 'native-image'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: coursier/cache-action@v6
|
||||
|
||||
- uses: coursier/setup-action@v1
|
||||
with:
|
||||
apps: sbt
|
||||
|
||||
- name: build
|
||||
run: sbt "cli/GraalVMNativeImage/packageBin"
|
||||
env:
|
||||
COMPILE_STATIC: ${{ inputs.static }}
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: aqua-${{ inputs.os }}-${{ inputs.arch }}
|
||||
path: cli/cli/.jvm/target/graalvm-native-image/cli
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: aqua-${{ inputs.os }}-${{ inputs.arch }}
|
||||
path: cli/cli/.jvm/target/graalvm-native-image/cli
|
23
.github/workflows/publish.yml
vendored
23
.github/workflows/publish.yml
vendored
@ -41,7 +41,7 @@ jobs:
|
||||
apps: sbt
|
||||
|
||||
- name: scala-js build
|
||||
run: sbt ";cliJS/fullOptJS;language-server-apiJS/fullOptJS;aqua-apiJS/fullOptJS"
|
||||
run: sbt ";language-server-apiJS/fullOptJS;aqua-apiJS/fullOptJS"
|
||||
|
||||
- name: Import secrets
|
||||
uses: hashicorp/vault-action@v2.7.3
|
||||
@ -73,24 +73,3 @@ jobs:
|
||||
|
||||
- name: Publish to NPM registry
|
||||
run: pnpm --filter='!integration-tests' publish --access public --tag unstable
|
||||
|
||||
aqua-native:
|
||||
name: "Publish aqua-native"
|
||||
strategy:
|
||||
matrix:
|
||||
runner:
|
||||
- runs-on: ubuntu-latest
|
||||
arch: amd64
|
||||
os: linux
|
||||
static: true
|
||||
- runs-on: macos-latest
|
||||
arch: amd64
|
||||
os: macos
|
||||
static: false
|
||||
|
||||
uses: ./.github/workflows/binary.yml
|
||||
with:
|
||||
runs-on: ${{ matrix.runner.runs-on }}
|
||||
arch: ${{ matrix.runner.arch }}
|
||||
os: ${{ matrix.runner.os }}
|
||||
static: ${{ matrix.runner.static }}
|
||||
|
9
.github/workflows/release.yml
vendored
9
.github/workflows/release.yml
vendored
@ -74,10 +74,6 @@ jobs:
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Set cli version
|
||||
run: pnpm version ${{ steps.version.outputs.version }}
|
||||
working-directory: cli/cli-npm
|
||||
|
||||
- name: Set api version
|
||||
run: pnpm version ${{ steps.version.outputs.version }}
|
||||
working-directory: api/api-npm
|
||||
@ -86,11 +82,6 @@ jobs:
|
||||
run: pnpm version ${{ steps.version.outputs.version }}
|
||||
working-directory: language-server/language-server-npm
|
||||
|
||||
- name: Set cli version in tests
|
||||
run: |
|
||||
pnpm add @fluencelabs/aqua@${{ steps.version.outputs.version }} --save-workspace-protocol=false
|
||||
working-directory: integration-tests
|
||||
|
||||
- name: Regenerate lock
|
||||
run: pnpm -r i
|
||||
|
||||
|
2
.github/workflows/snapshot.yml
vendored
2
.github/workflows/snapshot.yml
vendored
@ -51,7 +51,7 @@ jobs:
|
||||
- name: scala-js build
|
||||
env:
|
||||
SNAPSHOT: ${{ steps.version.outputs.id }}
|
||||
run: sbt ";cliJS/fastOptJS;language-server-apiJS/fastOptJS;aqua-apiJS/fastOptJS"
|
||||
run: sbt ";language-server-apiJS/fastOptJS;aqua-apiJS/fastOptJS"
|
||||
|
||||
- name: Import secrets
|
||||
uses: hashicorp/vault-action@v2.7.3
|
||||
|
1
ci.cjs
1
ci.cjs
@ -36,7 +36,6 @@ function validateArgs() {
|
||||
}
|
||||
|
||||
const PATHS_TO_PACKAGES = [
|
||||
"./cli/cli-npm",
|
||||
"./api/api-npm",
|
||||
"./language-server/language-server-npm",
|
||||
"./integration-tests"
|
||||
|
3
cli/cli-npm/.gitignore
vendored
3
cli/cli-npm/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
*.jar
|
||||
*.tgz
|
||||
node_modules
|
@ -1,7 +0,0 @@
|
||||
Copyright 2021 Fluence Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,56 +0,0 @@
|
||||
## Aqua
|
||||
|
||||
Aqua is a new-gen language for distributed systems.
|
||||
|
||||
Aqua programs are executed on many peers, sequentially
|
||||
or in parallel, forming a single-use coordination network.
|
||||
|
||||
Aqua's runtime is heterogeneous: it includes browsers, servers, devices, all involved in solving a single task.
|
||||
Therefore, Aqua scripts are compiled into several targets at once, with AIR and Typescript as a default.
|
||||
|
||||
## aqua
|
||||
|
||||
The package contains a convenience `aqua` wrapper for usage in npm-based projects.
|
||||
|
||||
### usage
|
||||
|
||||
Get the latest package
|
||||
|
||||
```bash
|
||||
npm i --save-dev @fluencelabs/aqua
|
||||
```
|
||||
|
||||
Create a directory for the source files: `.aqua` and for compiled files: `.ts`
|
||||
|
||||
```
|
||||
mkdir src/aqua src/compiled
|
||||
```
|
||||
|
||||
To compile files run:
|
||||
|
||||
```bash
|
||||
aqua -i ./src/aqua/ -o ./src/compiled
|
||||
```
|
||||
|
||||
Alternatively the compilation script can be put into scripts section of `package.json`
|
||||
|
||||
```
|
||||
...
|
||||
"scripts": {
|
||||
...
|
||||
"compile": "aqua -i ./src/aqua/ -o ./src/compiled"
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
and can be started with
|
||||
|
||||
```
|
||||
npm run compile
|
||||
```
|
||||
|
||||
### references
|
||||
|
||||
- For the list of compiler options see: https://github.com/fluencelabs/aqua
|
||||
- To get started writing aqua see: https://github.com/fluencelabs/aqua-playground
|
||||
|
@ -1,45 +0,0 @@
|
||||
## Installation
|
||||
|
||||
- run `sbt "cliJS/fullLinkJS"` in a root dir of a project after any code update (better to keep `sbt` running for faster compilation)
|
||||
- `npm` installed required
|
||||
- run `npm i` in `npm` directory
|
||||
- feel free to change `@fluencelabs/fluence` version in `package.json` file, run `npm i` after changes
|
||||
|
||||
## Run script
|
||||
|
||||
Generally, use this script to run compiled aqua compiler from a repo:
|
||||
|
||||
```
|
||||
npm run from:scalajs -- run -f "someFunction(arg1, arg2)" -i path/to/aqua --data-path path/to/args -m path/to/exports --addr /multiaddr/to/node
|
||||
```
|
||||
|
||||
- `-f or --func` is a function to call with arguments
|
||||
- `-i or --input` aqua file where function located
|
||||
- `-m or --import` imports location, could be used multiple times
|
||||
- `-a or --addr` multiaddress to a Fluence node
|
||||
- `-p or --data-path` path to a file with JSON where arguments are located
|
||||
|
||||
As example, use `test/sample.aqua` with args from `test/data.json` running on `/dns4/kras-04.fluence.dev/tcp/19001/wss/p2p/12D3KooWFEwNWcHqi9rtsmDhsYcDbRUCDXH84RC4FW6UfsFWaoHi` node:
|
||||
|
||||
```
|
||||
npm run from:scalajs -- run -f "identityArgsAndReturn(structField, stringField, numberField)" -i test/sample.aqua --data-path test/data.json --addr /dns4/kras-04.fluence.dev/tcp/19001/wss/p2p/12D3KooWFEwNWcHqi9rtsmDhsYcDbRUCDXH84RC4FW6UfsFWaoHi
|
||||
```
|
||||
|
||||
To simplify experience you can use `./aqua-run.sh` command and change all arguments straight in this file.
|
||||
|
||||
## Node addresses
|
||||
|
||||
Different Fluence network addresses could be found here: https://github.com/fluencelabs/fluence-network-environment/blob/main/src/index.ts
|
||||
|
||||
## Useful flags
|
||||
- `--print-air` to print resulted air
|
||||
- `--no-xor` to disable xor wrapping aroung service calls
|
||||
- `--sk secret_key` send request signed with specific secret key. Secret key could be created with `npm run from:scalajs -- create_keypair` or `aqua create_keypair` if you want to use installed aqua
|
||||
- `--data "json""` use instead of `--data-path` to pass arguments through command line
|
||||
- `--timeout 10000` to change timeout
|
||||
- `--log-level debug/info/..` to change log level
|
||||
|
||||
## Builtins for `aqua run`
|
||||
|
||||
You can find all builtins in aqua/run-builtins/run-builtins.aqua
|
||||
|
@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
FUNC='deploy(tsOracle)'
|
||||
INPUT='aqua/dist.aqua'
|
||||
DATAPATH='test/deploy.json'
|
||||
ADDR='/dns4/kras-04.fluence.dev/tcp/19001/wss/p2p/12D3KooWFEwNWcHqi9rtsmDhsYcDbRUCDXH84RC4FW6UfsFWaoHi'
|
||||
# IMPORT=
|
||||
|
||||
if [ -z "$IMPORT" ]
|
||||
then
|
||||
npm run build
|
||||
npm run -- run -f "$FUNC" -i "$INPUT" --data-path "$DATAPATH" --addr "$ADDR"
|
||||
else
|
||||
npm run build
|
||||
npm run run -- run -f "$FUNC" -i "$INPUT" --data-path "$DATAPATH" --addr "$ADDR" -m "$IMPORT"
|
||||
fi
|
@ -1,96 +0,0 @@
|
||||
module ServiceDist declares *
|
||||
|
||||
import "run-builtins/run-builtins.aqua"
|
||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
import "@fluencelabs/aqua-ipfs/ipfs.aqua"
|
||||
|
||||
export deploy, remove, createService, addBlueprint
|
||||
|
||||
data ModuleConf:
|
||||
name: string
|
||||
path: string
|
||||
mounted_binaries: ?[][]string
|
||||
preopened_files: ?[]string
|
||||
mapped_dirs: ?[][]string
|
||||
envs: ?[][]string
|
||||
logger_enabled: ?bool
|
||||
logging_mask: ?i32
|
||||
mem_pages_count: ?u32
|
||||
max_heap_size: ?string
|
||||
|
||||
service OpC("op"):
|
||||
array_length(arr: []ModuleConf) -> u32
|
||||
|
||||
data DeployResult:
|
||||
blueprint_id: string
|
||||
service_id: string
|
||||
|
||||
service DeployHelper("deploy_helper"):
|
||||
create_result(bid: string, sid: string) -> DeployResult
|
||||
|
||||
data ServiceConf:
|
||||
modules: []ModuleConf
|
||||
|
||||
const ON_PEER ?= HOST_PEER_ID
|
||||
|
||||
func flattenSS(input: [][]string) -> ?[][]string:
|
||||
res: *[][]string
|
||||
res <<- input
|
||||
<- res
|
||||
|
||||
func flattenS(input: []string) -> ?[]string:
|
||||
res: *[]string
|
||||
res <<- input
|
||||
<- res
|
||||
|
||||
func deploy(serviceName: string, serviceConf: ServiceConf) -> DeployResult:
|
||||
|
||||
on ON_PEER:
|
||||
multiaddr <- Ipfs.get_external_api_multiaddr()
|
||||
|
||||
mod_hashes: *[]string
|
||||
for m <- serviceConf.modules par:
|
||||
-- TODO check for cache
|
||||
Console.print(Op.concat_strings("Going to upload module: ", m.name))
|
||||
uploadRes <- LocalIpfs.uploadFile(m.path, multiaddr)
|
||||
cid = uploadRes.cid
|
||||
Console.print(Op.concat_strings(Op.concat_strings("Module '", m.name), "' was uploaded"))
|
||||
|
||||
on ON_PEER:
|
||||
hostRes <- Ipfs.get(cid)
|
||||
conf <- Dist.make_module_config(m.name, m.mem_pages_count, m.max_heap_size, m.logger_enabled, m.preopened_files, m.envs, m.mapped_dirs, m.mounted_binaries, m.logging_mask)
|
||||
mod_hash <- Dist.add_module_from_vault(hostRes.path, conf)
|
||||
mod_hashes <<- [mod_hash, m.name]
|
||||
|
||||
join mod_hashes[OpC.array_length(serviceConf.modules) - 1]
|
||||
|
||||
-- sort hashes to the same order as was in serviceConf.modules
|
||||
sorted_hashes: *string
|
||||
for m <- serviceConf.modules:
|
||||
for hash_name <- mod_hashes:
|
||||
if m.name == hash_name[1]:
|
||||
sorted_hashes <<- hash_name[0]
|
||||
|
||||
Console.print("Now time to make the blueprint...")
|
||||
on ON_PEER:
|
||||
blueprint <- Dist.make_blueprint(serviceName, sorted_hashes)
|
||||
blueprint_id <- Dist.add_blueprint(blueprint)
|
||||
service_id <- Srv.create(blueprint_id)
|
||||
|
||||
res <- DeployHelper.create_result(blueprint_id, service_id)
|
||||
<- res
|
||||
|
||||
func remove(service_id: string):
|
||||
on ON_PEER:
|
||||
Srv.remove(service_id)
|
||||
Console.print("Service was deleted")
|
||||
|
||||
func createService(blueprint_id: string) -> string:
|
||||
on ON_PEER:
|
||||
service_id <- Srv.create(blueprint_id)
|
||||
<- service_id
|
||||
|
||||
func addBlueprint(bp: AddBlueprint) -> string:
|
||||
on ON_PEER:
|
||||
blueprint_id <- Dist.add_blueprint(bp)
|
||||
<- blueprint_id
|
@ -1,18 +0,0 @@
|
||||
module IpfsExports
|
||||
|
||||
import "@fluencelabs/aqua-ipfs/ipfs-api.aqua"
|
||||
import "run-builtins/run-builtins.aqua"
|
||||
|
||||
|
||||
export uploadFile
|
||||
|
||||
const ON_PEER ?= HOST_PEER_ID
|
||||
|
||||
func uploadFile(path: string) -> UploadResult:
|
||||
on ON_PEER:
|
||||
multiaddr <- get_external_api_multiaddr(ON_PEER)
|
||||
result <- LocalIpfs.uploadFile(path, multiaddr)
|
||||
<- result
|
||||
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
module NetworkInfo
|
||||
|
||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
|
||||
export list_modules, list_interfaces_by_peer, get_interface, list_blueprints, list_services
|
||||
|
||||
const ON_PEER ?= HOST_PEER_ID
|
||||
|
||||
func list_modules() -> []Module:
|
||||
on ON_PEER:
|
||||
res <- Dist.list_modules()
|
||||
<- res
|
||||
|
||||
func list_services() -> []Service:
|
||||
on ON_PEER:
|
||||
services <- Srv.list()
|
||||
<- services
|
||||
|
||||
func list_interfaces_by_peer(peer: PeerId) -> []Interface:
|
||||
on ON_PEER:
|
||||
services <- Srv.list()
|
||||
interfaces: *Interface
|
||||
for srv <- services:
|
||||
if srv.owner_id == peer:
|
||||
interfaces <- Srv.get_interface(srv.id)
|
||||
<- interfaces
|
||||
|
||||
func get_interface(serviceId: string) -> Interface:
|
||||
on ON_PEER:
|
||||
res <- Srv.get_interface(serviceId)
|
||||
<- res
|
||||
|
||||
func get_module_interface(moduleHash: string) -> Interface:
|
||||
on ON_PEER:
|
||||
res <- Dist.get_module_interface(moduleHash)
|
||||
<- res
|
||||
|
||||
func list_blueprints() -> []Blueprint:
|
||||
on ON_PEER:
|
||||
res <- Dist.list_blueprints()
|
||||
<- res
|
@ -1,13 +0,0 @@
|
||||
import "@fluencelabs/aqua-ipfs/ipfs.aqua"
|
||||
|
||||
-- printing strings in console
|
||||
service Console("run-console"):
|
||||
print(str: string)
|
||||
|
||||
data UploadResult:
|
||||
error: string
|
||||
cid: string
|
||||
size: u64
|
||||
|
||||
service LocalIpfs("ipfs"):
|
||||
uploadFile(path: string, multiaddr: IpfsMultiaddrResult) -> UploadResult
|
@ -1,28 +0,0 @@
|
||||
module ServiceScript declares *
|
||||
|
||||
import "run-builtins/run-builtins.aqua"
|
||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
import "@fluencelabs/aqua-ipfs/ipfs.aqua"
|
||||
|
||||
export schedule, remove, list
|
||||
|
||||
const ON_PEER ?= HOST_PEER_ID
|
||||
|
||||
func schedule(air: string, interval: ?u64) -> string:
|
||||
on ON_PEER:
|
||||
res <- Script.add(air, interval)
|
||||
Console.print("Script was scheduled")
|
||||
<- res
|
||||
|
||||
func remove(script_id: string):
|
||||
on ON_PEER:
|
||||
res <- Script.remove(script_id)
|
||||
if res:
|
||||
Console.print("Script was removed")
|
||||
else:
|
||||
Console.print("No script with such ID")
|
||||
|
||||
func list() -> []ScriptInfo:
|
||||
on ON_PEER:
|
||||
res <- Script.list()
|
||||
<- res
|
@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
"use strict";
|
||||
|
||||
console.error("ERROR: use 'aqua' command!")
|
@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
"use strict";
|
||||
|
||||
handleEPIPE(process.stderr)
|
||||
handleEPIPE(process.stdout)
|
||||
function handleEPIPE(stream) {
|
||||
stream.on('error', onerror)
|
||||
function onerror(err) {
|
||||
if (err.code === 'EPIPE') {
|
||||
stream._write = noopWrite
|
||||
stream._writev = noopWritev
|
||||
stream._read = noopRead
|
||||
return stream.removeListener('error', onerror)
|
||||
}
|
||||
if (EE.listenerCount(stream, 'error') === 1) {
|
||||
stream.removeListener('error', onerror)
|
||||
stream.emit('error', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
function noopWrite(chunk, enc, cb) {
|
||||
cb()
|
||||
}
|
||||
function noopRead() {
|
||||
this.push('')
|
||||
}
|
||||
function noopWritev(chunks, cb) {
|
||||
cb()
|
||||
}
|
||||
|
||||
import "./aqua.js";
|
@ -1,2 +0,0 @@
|
||||
// It should work in scala as js.`import`.meta.url, but it doesn't compile for some reasons
|
||||
export const metaUrl = import.meta.url
|
@ -1,48 +0,0 @@
|
||||
{
|
||||
"name": "@fluencelabs/aqua",
|
||||
"version": "0.12.1",
|
||||
"description": "Aqua compiler",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"aqua.js",
|
||||
"index.js",
|
||||
"error.js",
|
||||
"meta-utils.js",
|
||||
"dist/*",
|
||||
"aqua/*"
|
||||
],
|
||||
"bin": {
|
||||
"aqua": "index.js",
|
||||
"aqua-cli": "error.js"
|
||||
},
|
||||
"scripts": {
|
||||
"run": "node index.js",
|
||||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluencelabs/aqua-ipfs": "0.5.9",
|
||||
"@fluencelabs/aqua-lib": "0.7.3",
|
||||
"@fluencelabs/fluence": "0.28.0",
|
||||
"@fluencelabs/fluence-network-environment": "1.1.2",
|
||||
"ipfs-http-client": "50.1.2",
|
||||
"multiaddr": "10.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "5.1.3"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fluencelabs/aqua.git"
|
||||
},
|
||||
"keywords": [
|
||||
"aqua",
|
||||
"fluence"
|
||||
],
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fluencelabs/aqua/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fluencelabs/aqua#readme"
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
import {create, globSource} from "ipfs-http-client";
|
||||
import { Multiaddr, protocols } from "multiaddr";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
type UploadResult = {
|
||||
cid: string,
|
||||
size: number
|
||||
}
|
||||
|
||||
export async function uploadFile(
|
||||
path: string,
|
||||
multiaddrResult: any,
|
||||
infoLogger: (s: string) => void,
|
||||
errorLogger: (s: string) => void
|
||||
): Promise<UploadResult> {
|
||||
|
||||
let rpcAddr;
|
||||
|
||||
if (multiaddrResult.success) {
|
||||
rpcAddr = multiaddrResult.multiaddr;
|
||||
} else {
|
||||
errorLogger(
|
||||
"Failed to retrieve external api multiaddr"
|
||||
);
|
||||
throw multiaddrResult.error;
|
||||
}
|
||||
|
||||
let rpcMaddr = new Multiaddr(rpcAddr).decapsulateCode(
|
||||
protocols.names.p2p.code
|
||||
);
|
||||
// HACK: `as any` is needed because ipfs-http-client forgot to add `| Multiaddr` to the `create` types
|
||||
const ipfs = create(rpcMaddr as any);
|
||||
infoLogger("created ipfs client to " + rpcMaddr);
|
||||
|
||||
await ipfs.id();
|
||||
infoLogger("connected to ipfs");
|
||||
|
||||
if (!existsSync(path)) {
|
||||
let errMsg = "File does not exist: " + path
|
||||
errorLogger(
|
||||
errMsg
|
||||
);
|
||||
throw errMsg;
|
||||
}
|
||||
|
||||
const source: any = await globSource(path)
|
||||
const file = await ipfs.add(source);
|
||||
|
||||
infoLogger("file uploaded");
|
||||
|
||||
return {
|
||||
cid: file.cid.toString(),
|
||||
size: file.size
|
||||
};
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
{
|
||||
"target": "12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz",
|
||||
"validators": [
|
||||
"12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er",
|
||||
"12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb",
|
||||
"12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB",
|
||||
"12D3KooWCKCeqLPSgMnDjyFsJuWqREDtKNHx1JEBiwaMXhCLNTRb",
|
||||
"12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH",
|
||||
"12D3KooWBSdm6TkqnEFrgBuSkpVE3dR1kr6952DsWQRNwJZjFZBv",
|
||||
"12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H",
|
||||
"12D3KooWF7gjXhQ4LaKj6j7ntxsPpGk34psdQicN2KNfBi9bFKXg",
|
||||
"12D3KooWB9P1xmV3c7ZPpBemovbwCiRRTKd3Kq2jsVPQN4ZukDfy"
|
||||
],
|
||||
"timeout": 5000,
|
||||
"stringField": "some string",
|
||||
"numberField": 123,
|
||||
"structField": {
|
||||
"numField": 42,
|
||||
"arrField": ["str1", "str2", "r43r34", "ferer"],
|
||||
"arr2": [{
|
||||
"a": "fef",
|
||||
"b": [1,2,3,4],
|
||||
"c": "erfer",
|
||||
"d": "frefe"
|
||||
},{
|
||||
"b": [1,2,3,4],
|
||||
"c": "erfer",
|
||||
"d": "frefe"
|
||||
}, {
|
||||
"a": "as",
|
||||
"c": "erfer",
|
||||
"d": "gerrt"
|
||||
}]
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"serviceConf": {
|
||||
"name": "ts-oracle",
|
||||
"modules": [
|
||||
{
|
||||
"name": "ts-oracle",
|
||||
"path": "./deploy/ts_oracle.wasm",
|
||||
"mounted_binaries": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
-- import "run-builtins.aqua"
|
||||
|
||||
data StructType:
|
||||
numField: u32
|
||||
arrField: []string
|
||||
|
||||
service OpString("op"):
|
||||
identity(s: string) -> string
|
||||
|
||||
service OpNumber("op"):
|
||||
identity(n: u32) -> u32
|
||||
|
||||
service OpStruct("op"):
|
||||
identity(st: StructType) -> StructType
|
||||
noop()
|
||||
|
||||
func parseBug():
|
||||
stream: *string
|
||||
if stream[0] != "FOO":
|
||||
Op.noop()
|
||||
|
||||
func identityArgsAndReturn (structArg: StructType, stringArg: string, numberArg: u32) -> string, u32, StructType:
|
||||
on HOST_PEER_ID:
|
||||
sArg <- OpString.identity(stringArg)
|
||||
nArg = OpNumber.identity (numberArg) + OpNumber.identity (numberArg)
|
||||
stArg <- OpStruct.identity(structArg)
|
||||
-- it could be used only on init_peer_id
|
||||
<- sArg, nArg, stArg
|
||||
|
||||
service Ssss("ss"):
|
||||
foo4: u64 -> u16
|
||||
|
||||
func aaa(a: u64) -> u16:
|
||||
res <- Ssss.foo4(a)
|
||||
<- res
|
||||
|
||||
func bar(callback: u32 -> u32):
|
||||
callback(1)
|
||||
|
||||
func baz():
|
||||
bar(aaa)
|
||||
|
||||
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
|
||||
"target": "esnext",
|
||||
"module": "ESNext",
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
"declaration": true,
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": false,
|
||||
"strictNullChecks": false,
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"bundle",
|
||||
"src/__test__",
|
||||
"src/compiled"
|
||||
]
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.builder.ArgumentGetter
|
||||
import aqua.js.VarJson
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.{CallArrowToken, CollectionToken, LiteralToken, VarToken}
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.raw.value.{CollectionRaw, LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
import aqua.run.CliFunc
|
||||
import cats.data.*
|
||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, valid, validNec, validNel}
|
||||
import cats.effect.Concurrent
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.semigroup.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{~>, Id, Semigroup}
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
import scala.collection.immutable.SortedMap
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSON
|
||||
|
||||
case class FuncWithData(func: CliFunc, getters: Map[String, VarJson])
|
||||
|
||||
object ArgOpts {
|
||||
|
||||
// Parses a function name and arguments from a string
|
||||
def funcOpt: Opts[CliFunc] =
|
||||
Opts
|
||||
.option[String]("func", "Function to call with args", "f", "funcName(args)")
|
||||
.mapValidated { str =>
|
||||
CliFunc.fromString(str)
|
||||
}
|
||||
|
||||
// Gets data from a file or from a json string
|
||||
def dataFileOrStringOpt[F[_]: Files: Concurrent]
|
||||
: Opts[F[ValidatedNec[String, Option[js.Dynamic]]]] =
|
||||
(AppOpts.wrapWithOption(dataOpt), AppOpts.wrapWithOption(dataFromFileOpt[F])).mapN {
|
||||
case (dataFromString, dataFromFile) =>
|
||||
dataFromFile match {
|
||||
case Some(dataFromFileF) =>
|
||||
dataFromFileF.map(_.andThen(args => getData(Some(args), dataFromString)))
|
||||
case None => validNec(dataFromString).pure[F]
|
||||
}
|
||||
}
|
||||
|
||||
// Creates getters based on function arguments and data, return all info
|
||||
def funcWithArgsOpt[F[_]: Files: Concurrent]: Opts[F[ValidatedNec[String, FuncWithData]]] = {
|
||||
(dataFileOrStringOpt[F], funcOpt).mapN { case (dataF, func) =>
|
||||
dataF.map { dataV =>
|
||||
dataV.andThen { data =>
|
||||
VarJson.checkDataGetServices(func.args, data).map { case (argsWithTypes, getters) =>
|
||||
FuncWithData(func.copy(args = argsWithTypes), getters)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def dataOpt: Opts[js.Dynamic] =
|
||||
Opts
|
||||
.option[String](
|
||||
"data",
|
||||
"JSON in { [argumentName]: argumentValue } format. You can call a function using these argument names",
|
||||
"d",
|
||||
"json"
|
||||
)
|
||||
.mapValidated { str =>
|
||||
Validated.catchNonFatal {
|
||||
JSON.parse(str)
|
||||
}.leftMap(t => NonEmptyList.one("Data argument isn't a valid JSON: " + t.getMessage))
|
||||
}
|
||||
|
||||
def dataFromFileOpt[F[_]: Files: Concurrent]: Opts[F[ValidatedNec[String, js.Dynamic]]] = {
|
||||
jsonFromFileOpt(
|
||||
"data-path",
|
||||
"Path to a JSON file in { [argumentName]: argumentValue } format. You can call a function using these argument names",
|
||||
"p"
|
||||
)
|
||||
}
|
||||
|
||||
def jsonFromFileOpt[F[_]: Files: Concurrent](
|
||||
name: String,
|
||||
help: String,
|
||||
short: String
|
||||
): Opts[F[ValidatedNec[String, js.Dynamic]]] = {
|
||||
FileOpts.fileOpt(
|
||||
name,
|
||||
help,
|
||||
short,
|
||||
(path, str) => {
|
||||
Validated.catchNonFatal {
|
||||
JSON.parse(str)
|
||||
}.leftMap(t =>
|
||||
NonEmptyChain
|
||||
.one(s"Data in ${path.toString} isn't a valid JSON: " + t.getMessage)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def jsonFromFileOpts[F[_]: Files: Concurrent](
|
||||
name: String,
|
||||
help: String,
|
||||
short: String
|
||||
): Opts[F[ValidatedNec[String, NonEmptyList[(Path, js.Dynamic)]]]] = {
|
||||
FileOpts.fileOpts(
|
||||
name,
|
||||
help,
|
||||
short,
|
||||
(path, str) => {
|
||||
Validated.catchNonFatal {
|
||||
JSON.parse(str)
|
||||
}.leftMap(t =>
|
||||
NonEmptyChain
|
||||
.one(s"Data in ${path.toString} isn't a valid JSON: " + t.getMessage)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// get data from sources, error if both sources exist
|
||||
def getData(
|
||||
dataFromArgument: Option[js.Dynamic],
|
||||
dataFromFile: Option[js.Dynamic]
|
||||
): ValidatedNec[String, Option[js.Dynamic]] = {
|
||||
(dataFromArgument, dataFromFile) match {
|
||||
case (Some(_), Some(_)) =>
|
||||
// TODO: maybe allow to use both and simple merge with data argument having higher priority
|
||||
invalidNec("Please use either --data or --data-path. Don't use both")
|
||||
case _ => validNec(dataFromArgument.orElse(dataFromFile))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.builder.{ArgumentGetter, Service}
|
||||
import aqua.io.{AquaPath, PackagePath}
|
||||
import aqua.js.VarJson
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.run.{CliFunc, GeneralOptions, GeneralOpts, JsonService, RunCommand, RunOpts}
|
||||
import aqua.logging.LogFormatter
|
||||
import cats.data.Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNec}
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.monad.*
|
||||
import cats.{Applicative, Monad}
|
||||
import com.monovore.decline.{Command, Opts}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scalajs.js
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
// All info to run any aqua function
|
||||
case class RunInfo(
|
||||
common: GeneralOptions,
|
||||
func: CliFunc,
|
||||
input: Option[AquaPath],
|
||||
imports: List[Path] = Nil,
|
||||
argumentGetters: Map[String, VarJson] = Map.empty,
|
||||
services: List[Service] = Nil,
|
||||
jsonServices: List[JsonService] = Nil,
|
||||
pluginsPaths: List[String] = Nil
|
||||
)
|
||||
|
||||
// Builds subcommand
|
||||
class SubCommandBuilder[F[_]: Async](
|
||||
name: String,
|
||||
header: String,
|
||||
opts: Opts[F[ValidatedNec[String, RunInfo]]]
|
||||
) extends Logging {
|
||||
|
||||
def command: Command[F[ValidatedNec[String, Unit]]] = Command(name, header) {
|
||||
opts.map { riF =>
|
||||
riF.flatMap {
|
||||
case Validated.Valid(ri) =>
|
||||
LogFormatter.initLogger(Some(ri.common.logLevel.compiler))
|
||||
RunCommand.execRun(
|
||||
ri
|
||||
)
|
||||
case i @ Validated.Invalid(_) =>
|
||||
i.pure[F]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object SubCommandBuilder {
|
||||
|
||||
def apply[F[_]: Async](
|
||||
name: String,
|
||||
header: String,
|
||||
opts: Opts[ValidatedNec[String, RunInfo]]
|
||||
): SubCommandBuilder[F] = {
|
||||
new SubCommandBuilder(name, header, opts.map(_.pure[F]))
|
||||
}
|
||||
|
||||
def applyF[F[_]: Async](
|
||||
name: String,
|
||||
header: String,
|
||||
opts: Opts[F[ValidatedNec[String, RunInfo]]]
|
||||
): SubCommandBuilder[F] = {
|
||||
new SubCommandBuilder(name, header, opts)
|
||||
}
|
||||
|
||||
def valid[F[_]: Async](
|
||||
name: String,
|
||||
header: String,
|
||||
opts: Opts[RunInfo]
|
||||
): SubCommandBuilder[F] = {
|
||||
SubCommandBuilder(name, header, opts.map(ri => validNec[String, RunInfo](ri)))
|
||||
}
|
||||
|
||||
def simple[F[_]: Async](
|
||||
name: String,
|
||||
header: String,
|
||||
path: AquaPath,
|
||||
funcName: String
|
||||
): SubCommandBuilder[F] =
|
||||
SubCommandBuilder
|
||||
.valid(
|
||||
name,
|
||||
header,
|
||||
GeneralOpts.opt.map { c =>
|
||||
RunInfo(c, CliFunc(funcName), Some(path))
|
||||
}
|
||||
)
|
||||
|
||||
def subcommands[F[_]: Async](
|
||||
subs: NonEmptyList[SubCommandBuilder[F]]
|
||||
): Opts[F[ValidatedNec[String, Unit]]] =
|
||||
Opts.subcommands(subs.head.command, subs.tail.map(_.command): _*)
|
||||
}
|
||||
|
||||
// Builds top command with subcommands
|
||||
case class CommandBuilder[F[_]: Async](
|
||||
name: String,
|
||||
header: String,
|
||||
subcommands: NonEmptyList[SubCommandBuilder[F]],
|
||||
rawCommands: List[Command[F[ValidatedNec[String, Unit]]]] = Nil
|
||||
) {
|
||||
|
||||
def command: Command[F[ValidatedNec[String, Unit]]] = {
|
||||
Command(name = name, header = header) {
|
||||
Opts.subcommands(
|
||||
subcommands.head.command,
|
||||
(subcommands.tail.map(_.command) ++ rawCommands): _*
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.js.{LogLevel, FluenceJSLogLevel}
|
||||
import fs2.io.file.Path
|
||||
import scribe.Level
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
object LogLevelTransformer {
|
||||
|
||||
def logLevelToAvm(logLevel: Level): LogLevel = {
|
||||
logLevel match {
|
||||
case Level.Trace => "trace"
|
||||
case Level.Debug => "debug"
|
||||
case Level.Info => "info"
|
||||
case Level.Warn => "warn"
|
||||
case Level.Error => "error"
|
||||
case Level.Fatal => "off"
|
||||
case _ => "info"
|
||||
}
|
||||
}
|
||||
|
||||
def logLevelToFluenceJS(logLevel: Level): FluenceJSLogLevel = {
|
||||
logLevel match {
|
||||
case Level.Trace => "trace"
|
||||
case Level.Debug => "debug"
|
||||
case Level.Info => "info"
|
||||
case Level.Warn => "warn"
|
||||
case Level.Error => "error"
|
||||
case Level.Fatal => "silent"
|
||||
case _ => "info"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.config.ConfigOpts
|
||||
import aqua.ipfs.IpfsOpts
|
||||
import aqua.keypair.KeyPairOpts
|
||||
import aqua.remote.{DistOpts, RemoteOpts}
|
||||
import aqua.run.RunOpts
|
||||
import aqua.script.ScriptOpts
|
||||
import cats.data.ValidatedNec
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.monad.*
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.util.Try
|
||||
import cats.effect.std.Console
|
||||
|
||||
// JS-specific options and subcommands
|
||||
object PlatformOpts extends Logging {
|
||||
|
||||
def opts[F[_]: Files: AquaIO: Async: Console]: Opts[F[ValidatedNec[String, Unit]]] =
|
||||
Opts.subcommand(RunOpts.runCommand[F]) orElse
|
||||
Opts.subcommand(KeyPairOpts.command[F]) orElse
|
||||
Opts.subcommand(IpfsOpts.ipfsOpt[F]) orElse
|
||||
Opts.subcommand(ScriptOpts.scriptOpt[F]) orElse
|
||||
Opts.subcommand(RemoteOpts.commands[F]) orElse
|
||||
Opts.subcommand(ConfigOpts.command[F])
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package aqua.air
|
||||
|
||||
import aqua.backend.AirFunction
|
||||
import aqua.js.Fluence
|
||||
import cats.data.Validated.{invalid, validNec}
|
||||
import cats.data.{Chain, NonEmptyChain, ValidatedNec}
|
||||
import cats.effect.Async
|
||||
import cats.syntax.traverse.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.scalajs.js
|
||||
|
||||
object AirValidation {
|
||||
|
||||
// HACK: memoize doesn't work in scalajs, so, call this function once before `validate`
|
||||
def init[F[_]: Async](): F[Unit] = {
|
||||
Async[F].fromFuture(Fluence.start(js.undefined).toFuture.pure[F]).as(())
|
||||
}
|
||||
|
||||
def validate[F[_]: Async](
|
||||
airs: List[AirFunction]
|
||||
): F[ValidatedNec[String, Unit]] =
|
||||
Async[F].fromFuture {
|
||||
|
||||
Async[F].executionContext.map { implicit ec =>
|
||||
for {
|
||||
statuses <- airs
|
||||
.map(a => Fluence.getPeer().internals.parseAst(a.air).toFuture.map(s => (a.name, s)))
|
||||
.sequence
|
||||
} yield {
|
||||
val errors = NonEmptyChain.fromSeq(statuses.filterNot(_._2.success))
|
||||
errors.map { errs =>
|
||||
val errorsStrs = errs.map { case (fName, status) =>
|
||||
s"Cannot compile AIR for '$fName' function: ${js.JSON.stringify(status.data)}\n\n" +
|
||||
"This is unexpected error. Please, dump your Aqua code and make an issue here https://github.com/fluencelabs/aqua/issues."
|
||||
}
|
||||
invalid(errorsStrs)
|
||||
}.getOrElse(validNec(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag}
|
||||
import aqua.raw.value.{LiteralRaw, VarRaw}
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
|
||||
import scala.concurrent.Promise
|
||||
import scala.scalajs.js
|
||||
|
||||
// Service that can return argument to use it from a code
|
||||
// TODO: create one service with multiple argument getters instead of service per argument
|
||||
abstract class ArgumentGetter(
|
||||
serviceId: String,
|
||||
val function: GetFunction
|
||||
) extends Service(serviceId, NonEmptyList.one(function)) {
|
||||
|
||||
def callTag(): CallArrowRawTag
|
||||
|
||||
}
|
||||
|
||||
case class GetFunction(value: VarRaw, arg: scalajs.js.Dynamic) extends AquaFunction {
|
||||
override def fnName: String = value.name
|
||||
|
||||
def handler: ServiceHandler = _ => js.Promise.resolve(arg)
|
||||
def arrow: ArrowTypeDef = ArrowTypeDef(NilTypeDef, UnlabeledProductTypeDef(TopTypeDef :: Nil))
|
||||
}
|
||||
|
||||
object ArgumentGetter {
|
||||
|
||||
val ServiceId = "getDataSrv"
|
||||
|
||||
private def getFunction(value: VarRaw, arg: scalajs.js.Dynamic) = GetFunction(value, arg)
|
||||
|
||||
def apply(value: VarRaw, arg: scalajs.js.Dynamic): ArgumentGetter =
|
||||
new ArgumentGetter(ServiceId, getFunction(value, arg)) {
|
||||
|
||||
override def callTag(): CallArrowRawTag =
|
||||
CallArrowRawTag.service(
|
||||
LiteralRaw.quote(ServiceId),
|
||||
value.name,
|
||||
Call(List.empty, List(Call.Export(value.name, value.baseType)))
|
||||
)
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.types.ScalarType
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
import scribe.Logging
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSON
|
||||
|
||||
private case class Console(serviceId: String, functions: NonEmptyList[AquaFunction])
|
||||
extends Service(serviceId, functions)
|
||||
|
||||
object Console extends Logging {
|
||||
|
||||
private def printFunction(funcName: String) = new AquaFunction {
|
||||
override def fnName: String = funcName
|
||||
|
||||
def handler: ServiceHandler = { varArgs =>
|
||||
js.typeOf(varArgs(0)) match {
|
||||
case "string" | "number" | "boolean" => println(varArgs(0).toString)
|
||||
case _ => println(JSON.stringify(varArgs(0), space = 2))
|
||||
}
|
||||
js.Promise.resolve(Service.emptyObject)
|
||||
}
|
||||
|
||||
def arrow: ArrowTypeDef = ArrowTypeDef(
|
||||
LabeledProductTypeDef(("str", ScalarTypeDef.fromScalar(ScalarType.string)) :: Nil),
|
||||
NilTypeDef
|
||||
)
|
||||
}
|
||||
|
||||
val PrintName = "print"
|
||||
|
||||
def apply(serviceId: String = "run-console"): Console = {
|
||||
|
||||
Console(serviceId, NonEmptyList.one(printFunction(PrintName)))
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.types.ScalarType
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
import scribe.Logging
|
||||
|
||||
import scala.scalajs.js
|
||||
|
||||
object DeployHelper extends Logging {
|
||||
|
||||
private val CreateResult = "create_result"
|
||||
|
||||
private def createResult(funcName: String): AquaFunction = new AquaFunction {
|
||||
override def fnName: String = funcName
|
||||
|
||||
override def handler: ServiceHandler = args => {
|
||||
val bid = args(0)
|
||||
val sid = args(1)
|
||||
js.Promise.resolve(js.Dynamic.literal(blueprint_id = bid, service_id = sid))
|
||||
}
|
||||
|
||||
def arrow: ArrowTypeDef = ArrowTypeDef(
|
||||
LabeledProductTypeDef(
|
||||
("bid", ScalarTypeDef.fromScalar(ScalarType.string)) :: (
|
||||
"sid",
|
||||
ScalarTypeDef.fromScalar(ScalarType.string)
|
||||
) :: Nil
|
||||
),
|
||||
UnlabeledProductTypeDef(
|
||||
StructTypeDef(
|
||||
"DeployResult",
|
||||
Map(
|
||||
"blueprint_id" -> ScalarTypeDef.fromScalar(ScalarType.string),
|
||||
"service_id" -> ScalarTypeDef.fromScalar(ScalarType.string)
|
||||
)
|
||||
) :: Nil
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def apply(serviceId: String = "deploy_helper"): Service = {
|
||||
val funcs = NonEmptyList.one(createResult(CreateResult))
|
||||
Service(serviceId, funcs)
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag}
|
||||
import aqua.definitions.*
|
||||
import aqua.raw.value.LiteralRaw
|
||||
import cats.data.NonEmptyList
|
||||
|
||||
import scala.concurrent.Promise
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.{Dynamic, JSON}
|
||||
|
||||
// Will finish promise on service call
|
||||
abstract class Finisher private (
|
||||
serviceId: String,
|
||||
functions: NonEmptyList[AquaFunction],
|
||||
val promise: Promise[Unit]
|
||||
) extends Service(serviceId, functions) {
|
||||
|
||||
def callTag(): CallArrowRawTag
|
||||
}
|
||||
|
||||
object Finisher {
|
||||
|
||||
private def finishFunction(funcName: String, promise: Promise[Unit]) = new AquaFunction {
|
||||
def fnName: String = funcName
|
||||
|
||||
def handler: ServiceHandler = _ => {
|
||||
promise.success(())
|
||||
js.Promise.resolve(Service.emptyObject)
|
||||
}
|
||||
def arrow: ArrowTypeDef = ArrowTypeDef(NilTypeDef, NilTypeDef)
|
||||
}
|
||||
|
||||
def apply(servId: String, fnName: String): Finisher = {
|
||||
val promise = Promise[Unit]()
|
||||
val funcs = NonEmptyList.one(finishFunction(fnName, promise))
|
||||
new Finisher(servId, funcs, promise) {
|
||||
def callTag(): CallArrowRawTag =
|
||||
CallArrowRawTag.service(
|
||||
LiteralRaw.quote(servId),
|
||||
fnName,
|
||||
Call(Nil, Nil)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.types.ScalarType
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
import scribe.Logging
|
||||
|
||||
import scala.scalajs.js
|
||||
|
||||
object IPFSUploader extends Logging {
|
||||
|
||||
private val UploadFile = "uploadFile"
|
||||
|
||||
private def uploadFunc(funcName: String): AquaFunction = new AquaFunction {
|
||||
override def fnName: String = funcName
|
||||
|
||||
private def logError(s: String) = logger.error(s)
|
||||
private def logInfo(s: String) = logger.info(s)
|
||||
|
||||
override def handler: ServiceHandler = args => {
|
||||
IpfsApi
|
||||
.uploadFile(args(0), args(1), logInfo, logError)
|
||||
.`catch` { err =>
|
||||
js.Dynamic.literal(error = "File upload error: " + err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def arrow: ArrowTypeDef = ArrowTypeDef(
|
||||
LabeledProductTypeDef(
|
||||
("path", ScalarTypeDef.fromScalar(ScalarType.string)) :: (
|
||||
"multiaddr",
|
||||
ScalarTypeDef.fromScalar(ScalarType.string)
|
||||
) :: Nil
|
||||
),
|
||||
UnlabeledProductTypeDef(
|
||||
StructTypeDef(
|
||||
"UploadResult",
|
||||
Map(
|
||||
"error" -> ScalarTypeDef.fromScalar(ScalarType.string),
|
||||
"cid" -> ScalarTypeDef.fromScalar(ScalarType.string),
|
||||
"size" -> ScalarTypeDef.fromScalar(ScalarType.u64)
|
||||
)
|
||||
) :: Nil
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def apply(serviceId: String): Service = {
|
||||
val funcs = NonEmptyList.one(uploadFunc(UploadFile))
|
||||
Service(serviceId, funcs)
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag}
|
||||
import aqua.raw.value.{LiteralRaw, VarRaw}
|
||||
import aqua.definitions.*
|
||||
import aqua.types.ScalarType
|
||||
import cats.data.NonEmptyList
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.{Dynamic, JSON}
|
||||
|
||||
// Function to print any variables that passed as arguments
|
||||
abstract class ResultPrinter(serviceId: String, functions: NonEmptyList[AquaFunction])
|
||||
extends Service(serviceId, functions) {
|
||||
|
||||
def callTag(variables: List[VarRaw]): CallArrowRawTag
|
||||
}
|
||||
|
||||
object ResultPrinter {
|
||||
|
||||
private def resultPrinterFunc(funcName: String, resultNames: List[String]) = new AquaFunction {
|
||||
override def fnName: String = funcName
|
||||
|
||||
override def handler: ServiceHandler = varArgs => {
|
||||
// drop last argument (tetraplets)
|
||||
val args: Seq[js.Any] = varArgs.init
|
||||
val toPrint = args.toList match {
|
||||
case arg :: Nil => JSON.stringify(arg, space = 2)
|
||||
case _ => args.map(a => JSON.stringify(a, space = 2)).mkString("[\n", ",\n", "\n]")
|
||||
}
|
||||
|
||||
// if an input function returns a result, our success will be after it is printed
|
||||
// otherwise finish after JS SDK will finish sending a request
|
||||
OutputPrinter.print(toPrint)
|
||||
// empty JS object
|
||||
js.Promise.resolve(Service.emptyObject)
|
||||
}
|
||||
|
||||
def arrow: ArrowTypeDef = ArrowTypeDef(
|
||||
LabeledProductTypeDef(resultNames.map(n => (n, TopTypeDef))),
|
||||
NilTypeDef
|
||||
)
|
||||
}
|
||||
|
||||
def apply(serviceId: String, fnName: String, resultNames: List[String]): ResultPrinter = {
|
||||
val funcs = NonEmptyList.one(resultPrinterFunc(fnName, resultNames))
|
||||
new ResultPrinter(serviceId, funcs) {
|
||||
def callTag(variables: List[VarRaw]): CallArrowRawTag =
|
||||
CallArrowRawTag.service(
|
||||
LiteralRaw.quote(serviceId),
|
||||
fnName,
|
||||
Call(variables, Nil)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
import scribe.Logging
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.{Dynamic, JSON}
|
||||
|
||||
class Service(serviceId: String, functions: NonEmptyList[AquaFunction]) extends Logging {
|
||||
|
||||
def register(peer: FluencePeer): Unit = {
|
||||
val handlers = functions.map(f => (f.fnName, f.handler))
|
||||
val defs = LabeledProductTypeDef(
|
||||
functions.map(f => (f.fnName, f.arrow)).toList
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
s"Registering service $serviceId with functions ${functions.map(_.fnName).toList.mkString(",")}"
|
||||
)
|
||||
|
||||
CallJsFunction.registerService(
|
||||
peer,
|
||||
serviceId,
|
||||
handlers.toList,
|
||||
ServiceDef(
|
||||
None,
|
||||
defs,
|
||||
""
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
trait AquaFunction {
|
||||
def fnName: String
|
||||
def handler: ServiceHandler
|
||||
def arrow: ArrowTypeDef
|
||||
}
|
||||
|
||||
object Service {
|
||||
val emptyObject: Dynamic = Dynamic.literal()
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package aqua.config
|
||||
|
||||
import aqua.js.{FluenceEnvironment, FluenceNode}
|
||||
import cats.Applicative
|
||||
import cats.data.{Validated, ValidatedNec}
|
||||
import cats.data.Validated.{invalidNel, validNel}
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
import com.monovore.decline.{Command, Opts}
|
||||
import cats.data.Validated.{invalidNec, validNec}
|
||||
|
||||
import scala.scalajs.js
|
||||
|
||||
object ConfigOpts {
|
||||
|
||||
def command[F[_]: Async]: Command[F[ValidatedNec[String, Unit]]] =
|
||||
Command(name = "config", header = "Aqua CLI configuration") {
|
||||
Opts.subcommands(
|
||||
listPeers
|
||||
)
|
||||
}
|
||||
|
||||
val Krasnodar = "krasnodar"
|
||||
val Stage = "stage"
|
||||
val TestNet = "testnet"
|
||||
|
||||
def envArg: Opts[js.Array[FluenceNode]] =
|
||||
Opts
|
||||
.argument[String](s"$Krasnodar | $Stage | $TestNet")
|
||||
.withDefault(Krasnodar)
|
||||
.mapValidated {
|
||||
case Krasnodar =>
|
||||
validNel(FluenceEnvironment.krasnodar)
|
||||
case TestNet =>
|
||||
validNel(FluenceEnvironment.testnet)
|
||||
case Stage =>
|
||||
validNel(FluenceEnvironment.stage)
|
||||
case e =>
|
||||
invalidNel(
|
||||
s"There is no environment '$e' in our list. Use one of these: '$Krasnodar', '$TestNet', '$Stage'"
|
||||
)
|
||||
}
|
||||
|
||||
def listPeers[F[_]: Applicative]: Command[F[ValidatedNec[String, Unit]]] =
|
||||
Command(
|
||||
name = "default_peers",
|
||||
header = "List addresses of default peers in Fluence network"
|
||||
) {
|
||||
envArg.map { env =>
|
||||
validNec(println(env.toList.map(n => n.multiaddr).mkString("\n"))).pure[F]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package aqua.ipfs
|
||||
|
||||
import aqua.{
|
||||
AppOpts,
|
||||
AquaIO,
|
||||
CommandBuilder,
|
||||
FluenceOpts,
|
||||
LogLevelTransformer,
|
||||
PlatformOpts,
|
||||
RunInfo,
|
||||
SubCommandBuilder
|
||||
}
|
||||
import aqua.keypair.KeyPairShow.show
|
||||
import cats.data.{NonEmptyChain, NonEmptyList, Validated, ValidatedNec, ValidatedNel}
|
||||
import Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import aqua.builder.IPFSUploader
|
||||
import aqua.io.PackagePath
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.model.LiteralModel
|
||||
import aqua.raw.value.LiteralRaw
|
||||
import aqua.run.{GeneralOptions, RunCommand, RunConfig, RunOpts, GeneralOpts, CliFunc}
|
||||
import cats.effect.{Concurrent, ExitCode, Resource, Sync}
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.show.*
|
||||
import cats.{Applicative, Monad}
|
||||
import com.monovore.decline.{Command, Opts}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.scalajs.js
|
||||
|
||||
// Options and commands to work with IPFS
|
||||
object IpfsOpts extends Logging {
|
||||
|
||||
val IpfsAqua = "aqua/ipfs.aqua"
|
||||
|
||||
val UploadFuncName = "uploadFile"
|
||||
|
||||
def pathOpt: Opts[String] =
|
||||
Opts
|
||||
.option[String]("path", "Path to a file", "p", "path")
|
||||
|
||||
def ipfsOpt[F[_]: Async]: Command[F[ValidatedNec[String, Unit]]] =
|
||||
CommandBuilder("ipfs", "Work with IPFS on a peer", NonEmptyList.one(upload[F])).command
|
||||
|
||||
// Uploads a file to IPFS
|
||||
def upload[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.valid(
|
||||
"upload",
|
||||
"Upload a file to IPFS",
|
||||
(GeneralOpts.opt, pathOpt).mapN { (common, path) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(UploadFuncName, LiteralRaw.quote(path) :: Nil),
|
||||
Option(PackagePath(IpfsAqua))
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package aqua.ipfs.js
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.annotation.{JSExportAll, JSImport}
|
||||
|
||||
object IpfsApi {
|
||||
|
||||
@js.native
|
||||
@JSImport("./dist/ipfs.js", "uploadFile")
|
||||
def uploadFile(
|
||||
path: js.Any,
|
||||
multiaddrResult: js.Any,
|
||||
infoLogger: js.Any,
|
||||
errorLogger: js.Any
|
||||
): js.Promise[js.Dynamic] = js.native
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package aqua.keypair
|
||||
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.KeyPair
|
||||
import aqua.keypair.KeyPairShow.show
|
||||
import cats.data.ValidatedNec
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.show.*
|
||||
import cats.{Applicative, Monad}
|
||||
import com.monovore.decline.{Command, Opts}
|
||||
import cats.data.Validated.{invalidNec, validNec}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
// Options and commands to work with KeyPairs
|
||||
object KeyPairOpts extends Logging {
|
||||
|
||||
def command[F[_]: Async]: Command[F[ValidatedNec[String, Unit]]] =
|
||||
Command(name = "key", header = "Manage local keys and identity") {
|
||||
Opts.subcommands(
|
||||
createKeypair
|
||||
)
|
||||
}
|
||||
|
||||
// KeyPair generation
|
||||
def createKeypair[F[_]: Async]: Command[F[ValidatedNec[String, Unit]]] =
|
||||
Command(
|
||||
name = "create",
|
||||
header = "Generate new key pair"
|
||||
) {
|
||||
Opts.unit.map(_ =>
|
||||
Async[F]
|
||||
.fromFuture(
|
||||
KeyPair.randomEd25519().toFuture.pure[F]
|
||||
)
|
||||
.map(keypair => validNec(OutputPrinter.print(s"${keypair.show}")))
|
||||
)
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package aqua.keypair
|
||||
|
||||
import aqua.js.{KeyPair, KeyPairOp}
|
||||
import cats.Show
|
||||
|
||||
import java.util.Base64
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSON
|
||||
|
||||
object KeyPairShow {
|
||||
|
||||
def stringify(keypair: KeyPair): String = {
|
||||
JSON.stringify(KeyPairOp.toDynamicJSON(keypair), space = 4)
|
||||
}
|
||||
|
||||
implicit val show: Show[KeyPair] = Show.show(KeyPairShow.stringify)
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
package aqua.remote
|
||||
|
||||
import aqua.ArgOpts.jsonFromFileOpt
|
||||
import aqua.builder.ArgumentGetter
|
||||
import aqua.raw.value.{LiteralRaw, VarRaw}
|
||||
import aqua.run.{GeneralOptions, GeneralOpts, CliFunc}
|
||||
import aqua.types.{ArrayType, ScalarType, StructType}
|
||||
import aqua.*
|
||||
import aqua.io.PackagePath
|
||||
import aqua.js.{JsonEncoder, VarJson}
|
||||
import cats.data.{NonEmptyList, NonEmptyMap, ValidatedNec}
|
||||
import cats.data.Validated.{invalidNec, validNec}
|
||||
import cats.effect.{Async, Concurrent, ExitCode, Resource, Sync}
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.syntax.show.*
|
||||
import cats.{Applicative, Monad}
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.Files
|
||||
import scribe.Logging
|
||||
|
||||
import scala.collection.immutable.SortedMap
|
||||
import scala.scalajs.js.JSConverters.*
|
||||
import scala.scalajs.js
|
||||
|
||||
// Options and commands to work blueprints, modules and services
|
||||
object DistOpts extends Logging {
|
||||
|
||||
val DistAqua = "aqua/dist.aqua"
|
||||
|
||||
val DeployFuncName = "deploy"
|
||||
val RemoveFuncName = "remove"
|
||||
val CreateServiceFuncName = "createService"
|
||||
val AddBlueprintFuncName = "addBlueprint"
|
||||
|
||||
def srvNameOpt: Opts[String] =
|
||||
Opts
|
||||
.option[String]("service", "Service to deploy from the config file")
|
||||
|
||||
def srvIdOpt: Opts[String] =
|
||||
Opts
|
||||
.option[String]("id", "Service id to remove", "i")
|
||||
|
||||
def blueprintIdOpt: Opts[String] =
|
||||
Opts
|
||||
.option[String]("id", "Blueprint id", "i")
|
||||
|
||||
def blueprintNameOpt: Opts[String] =
|
||||
Opts
|
||||
.option[String]("name", "Blueprint name", "n")
|
||||
|
||||
def dependencyOpt: Opts[NonEmptyList[String]] =
|
||||
Opts
|
||||
.options[String]("dependency", "Blueprint dependency. May be used several times", "d")
|
||||
|
||||
// Removes service from a node
|
||||
def remove[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.valid(
|
||||
"remove_service",
|
||||
"Remove service",
|
||||
(GeneralOpts.opt, srvIdOpt).mapN { (common, srvId) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(RemoveFuncName, LiteralRaw.quote(srvId) :: Nil),
|
||||
Option(PackagePath(DistAqua))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def createService[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.valid(
|
||||
"create_service",
|
||||
"Deploy service from existing blueprint",
|
||||
(GeneralOpts.opt, blueprintIdOpt).mapN { (common, blueprintId) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(CreateServiceFuncName, LiteralRaw.quote(blueprintId) :: Nil),
|
||||
Option(PackagePath(DistAqua))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def addBlueprint[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.valid(
|
||||
"add_blueprint",
|
||||
"Add blueprint to a peer",
|
||||
(GeneralOpts.opt, blueprintNameOpt, dependencyOpt).mapN {
|
||||
(common, blueprintName, dependencies) =>
|
||||
val depsWithHash = dependencies.map { d =>
|
||||
if (d.startsWith("hash:"))
|
||||
d
|
||||
else
|
||||
"hash:" + d
|
||||
}
|
||||
val addBlueprintType = StructType(
|
||||
"AddBlueprint",
|
||||
NonEmptyMap.of(
|
||||
("name", ScalarType.string),
|
||||
("dependencies", ArrayType(ScalarType.string))
|
||||
)
|
||||
)
|
||||
val addBlueprintRequestVar =
|
||||
VarRaw("addBlueprint", addBlueprintType)
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(AddBlueprintFuncName, addBlueprintRequestVar :: Nil),
|
||||
Option(PackagePath(DistAqua)),
|
||||
Nil,
|
||||
Map(
|
||||
addBlueprintRequestVar.name -> VarJson(
|
||||
addBlueprintRequestVar,
|
||||
js.Dynamic
|
||||
.literal("name" -> blueprintName, "dependencies" -> depsWithHash.toList.toJSArray)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def configFromFileOpt[F[_]: Files: Concurrent]: Opts[F[ValidatedNec[String, js.Dynamic]]] = {
|
||||
jsonFromFileOpt("config-path", "Path to a deploy config", "p")
|
||||
}
|
||||
|
||||
// Uploads a file to IPFS, creates blueprints and deploys a service
|
||||
def deploy[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.applyF(
|
||||
"deploy_service",
|
||||
"Deploy service from WASM modules",
|
||||
(
|
||||
GeneralOpts.optWithSecretKeyCustomTimeout(60000),
|
||||
configFromFileOpt[F],
|
||||
srvNameOpt
|
||||
).mapN { (common, configFromFileF, srvName) =>
|
||||
configFromFileF.map { dff =>
|
||||
dff
|
||||
.andThen(config =>
|
||||
val srvConfig = {
|
||||
val c = config.selectDynamic(srvName)
|
||||
if (js.isUndefined(c)) None
|
||||
else Some(c)
|
||||
}
|
||||
srvConfig match {
|
||||
case Some(c) =>
|
||||
JsonEncoder.aquaTypeFromJson(srvName, c).andThen { configType =>
|
||||
val srvArg = VarRaw(srvName, configType)
|
||||
val args = LiteralRaw.quote(srvName) :: srvArg :: Nil
|
||||
// if we have default timeout, increase it
|
||||
validNec(
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(DeployFuncName, args),
|
||||
Option(PackagePath(DistAqua)),
|
||||
Nil,
|
||||
// hack: air cannot use undefined fields, fill undefined arrays with nils
|
||||
Map(srvName -> VarJson(srvArg, c))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
case None =>
|
||||
invalidNec(s"No service '$srvName' in the config.")
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
package aqua.remote
|
||||
|
||||
import aqua.builder.IPFSUploader
|
||||
import DistOpts.*
|
||||
import aqua.ipfs.IpfsOpts.{pathOpt, UploadFuncName}
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw}
|
||||
import aqua.run.{GeneralOptions, GeneralOpts, RunCommand, RunConfig, RunOpts, CliFunc}
|
||||
import aqua.*
|
||||
import cats.Applicative
|
||||
import cats.data.{NonEmptyList, Validated}
|
||||
import Validated.{invalidNel, validNel}
|
||||
import aqua.io.PackagePath
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import com.monovore.decline.{Command, Opts}
|
||||
import fs2.io.file.Path
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.scalajs.js
|
||||
|
||||
object RemoteInfoOpts {
|
||||
|
||||
val NetworkAqua = "aqua/network-info.aqua"
|
||||
|
||||
val ListModulesFuncName = "list_modules"
|
||||
val ListBlueprintsFuncName = "list_blueprints"
|
||||
val ListInterfacesByPeerFuncName = "list_interfaces_by_peer"
|
||||
val ListInterfacesFuncName = "list_services"
|
||||
val GetInterfaceFuncName = "get_interface"
|
||||
val GetModuleInterfaceFuncName = "get_module_interface"
|
||||
|
||||
def ownerOpt: Opts[String] =
|
||||
Opts
|
||||
.option[String]("owner", "PeerId", "o")
|
||||
|
||||
def allFlag: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("all", "Get all services on a node")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
def idOpt: Opts[String] =
|
||||
Opts
|
||||
.option[String]("id", "Service ID", "s")
|
||||
|
||||
def listModules[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.simple(
|
||||
ListModulesFuncName,
|
||||
"List all modules on a peer",
|
||||
PackagePath(NetworkAqua),
|
||||
ListModulesFuncName
|
||||
)
|
||||
|
||||
def listBlueprints[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.simple(
|
||||
ListBlueprintsFuncName,
|
||||
"List all blueprints on a peer",
|
||||
PackagePath(NetworkAqua),
|
||||
ListBlueprintsFuncName
|
||||
)
|
||||
|
||||
def listInterfaces[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.valid(
|
||||
"list_interfaces",
|
||||
"List all service interfaces on a peer by a given owner",
|
||||
(GeneralOpts.opt, AppOpts.wrapWithOption(ownerOpt), allFlag).mapN {
|
||||
(common, peer, printAll) =>
|
||||
if (printAll)
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(
|
||||
ListInterfacesFuncName,
|
||||
Nil
|
||||
),
|
||||
Option(PackagePath(NetworkAqua))
|
||||
)
|
||||
else
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(
|
||||
ListInterfacesByPeerFuncName,
|
||||
peer.map(LiteralRaw.quote).getOrElse(ValueRaw.InitPeerId) :: Nil
|
||||
),
|
||||
Option(PackagePath(NetworkAqua))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def getInterface[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.valid(
|
||||
GetInterfaceFuncName,
|
||||
"Show interface of a service",
|
||||
(GeneralOpts.opt, idOpt).mapN { (common, serviceId) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(GetInterfaceFuncName, LiteralRaw.quote(serviceId) :: Nil),
|
||||
Option(PackagePath(NetworkAqua))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def getModuleInterface[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.valid(
|
||||
GetModuleInterfaceFuncName,
|
||||
"Print a module interface",
|
||||
(GeneralOpts.opt, idOpt).mapN { (common, serviceId) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(GetModuleInterfaceFuncName, LiteralRaw.quote(serviceId) :: Nil),
|
||||
Option(PackagePath(NetworkAqua))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package aqua.remote
|
||||
|
||||
import aqua.{AquaIO, CommandBuilder}
|
||||
import cats.data.{NonEmptyList, ValidatedNec}
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import com.monovore.decline.Command
|
||||
import RemoteInfoOpts.*
|
||||
import DistOpts.*
|
||||
|
||||
object RemoteOpts {
|
||||
|
||||
// All remote commands
|
||||
def commands[F[_]: AquaIO: Async]: Command[F[ValidatedNec[String, Unit]]] =
|
||||
CommandBuilder(
|
||||
"remote",
|
||||
"Manage and query services on a remote peer",
|
||||
NonEmptyList(
|
||||
deploy,
|
||||
remove :: createService :: addBlueprint :: listModules :: listBlueprints :: listInterfaces :: getInterface :: Nil
|
||||
)
|
||||
).command
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.AppOpts
|
||||
import aqua.FluenceOpts.*
|
||||
import aqua.builder.{ArgumentGetter, Service}
|
||||
import aqua.config.ConfigOpts.{Krasnodar, Stage, TestNet}
|
||||
import aqua.js.FluenceEnvironment
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.raw.value.VarRaw
|
||||
import aqua.logging.LogLevels
|
||||
import cats.data.{NonEmptyList, Validated}
|
||||
import cats.data.Validated.{invalidNel, validNel}
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import com.monovore.decline.Opts
|
||||
import scribe.Level
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.scalajs.js
|
||||
import scala.util.Try
|
||||
|
||||
object GeneralOpts {
|
||||
|
||||
val multiaddrOpt: Opts[String] =
|
||||
Opts
|
||||
.option[String]("addr", "Relay multiaddress", "a")
|
||||
.mapValidated { s =>
|
||||
if ((s.startsWith("/dns4/") || s.startsWith("/ip4/")) && s.contains("/p2p/12D3")) {
|
||||
validNel(s)
|
||||
} else {
|
||||
Validated.catchNonFatal {
|
||||
val splitted = s.split("-")
|
||||
val index = splitted(1).toInt
|
||||
splitted.head.toLowerCase match {
|
||||
case Krasnodar =>
|
||||
validNel(FluenceEnvironment.krasnodar(index).multiaddr)
|
||||
case TestNet =>
|
||||
validNel(FluenceEnvironment.testnet(index).multiaddr)
|
||||
case Stage =>
|
||||
validNel(FluenceEnvironment.stage(index).multiaddr)
|
||||
case _ =>
|
||||
invalidNel(
|
||||
// TODO: maybe show an example of valid format in this error message and in the one below
|
||||
"Invalid multiaddr format. Run 'aqua config default_peers' for valid multiaddress."
|
||||
)
|
||||
}
|
||||
}.andThen(identity)
|
||||
.leftMap(_ =>
|
||||
NonEmptyList.one(
|
||||
"Invalid multiaddr format. Run 'aqua config default_peers' for valid multiaddress."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def flagsOpt(isRun: Boolean): Opts[Flags] =
|
||||
((
|
||||
printAir,
|
||||
showConfigOpt,
|
||||
verboseOpt
|
||||
) ++ {
|
||||
if (isRun)
|
||||
(AppOpts.noXorWrapper, AppOpts.noRelay)
|
||||
else
|
||||
(false.pure[Opts], false.pure[Opts])
|
||||
}).mapN(Flags.apply)
|
||||
|
||||
def commonOpt(
|
||||
isRun: Boolean,
|
||||
withSecret: Boolean,
|
||||
withConstants: Boolean,
|
||||
defaultTimeout: Duration = Duration(7000, TimeUnit.MILLISECONDS)
|
||||
): Opts[GeneralOptions] =
|
||||
(
|
||||
timeoutOpt.withDefault(defaultTimeout),
|
||||
logLevelOpt,
|
||||
multiaddrOpt,
|
||||
onOpt,
|
||||
flagsOpt(isRun),
|
||||
if (withSecret) { secretKeyOpt.map(Some.apply) }
|
||||
else { AppOpts.wrapWithOption(secretKeyOpt) },
|
||||
if (withConstants) AppOpts.constantOpts else Nil.pure[Opts]
|
||||
).mapN(GeneralOptions.apply)
|
||||
|
||||
val opt: Opts[GeneralOptions] = commonOpt(false, false, false)
|
||||
val runOpt: Opts[GeneralOptions] = commonOpt(true, false, true)
|
||||
val optWithSecretKey: Opts[GeneralOptions] = commonOpt(false, true, false)
|
||||
|
||||
def optWithSecretKeyCustomTimeout(timeoutMs: Int): Opts[GeneralOptions] =
|
||||
commonOpt(false, true, false, Duration(timeoutMs, TimeUnit.MILLISECONDS))
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.LogLevelTransformer
|
||||
import aqua.builder.{ArgumentGetter, Finisher, ResultPrinter, Service}
|
||||
import aqua.definitions.FunctionDef
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.*
|
||||
import aqua.keypair.KeyPairShow.show
|
||||
import aqua.run.RunCommand.createKeyPair
|
||||
import aqua.run.plugin.Plugin
|
||||
import cats.data.Validated.{invalidNec, validNec}
|
||||
import cats.data.ValidatedNec
|
||||
import cats.effect.kernel.Async
|
||||
import cats.effect.{Resource, Sync}
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.show.*
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.{ExecutionContext, Future, Promise, TimeoutException}
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSConverters.*
|
||||
import scala.scalajs.js.{JSON, JavaScriptException, timers}
|
||||
|
||||
object FuncCaller {
|
||||
|
||||
/**
|
||||
* Register services and call an air code with FluenceJS SDK.
|
||||
* @param air code to call
|
||||
* @return
|
||||
*/
|
||||
def funcCall[F[_]: Async](
|
||||
name: String,
|
||||
air: String,
|
||||
functionDef: FunctionDef,
|
||||
config: RunConfig,
|
||||
resultPrinterService: ResultPrinter,
|
||||
finisherService: Finisher,
|
||||
services: List[Service],
|
||||
getters: List[ArgumentGetter],
|
||||
plugins: List[String]
|
||||
): F[ValidatedNec[String, Unit]] = {
|
||||
|
||||
FluenceUtils.setLogLevel(
|
||||
LogLevelTransformer.logLevelToFluenceJS(config.common.logLevel.fluencejs)
|
||||
)
|
||||
|
||||
// stops peer in any way at the end of execution
|
||||
val resource = Resource.make(Fluence.getPeer().pure[F]) { peer =>
|
||||
Async[F].fromFuture(Sync[F].delay(peer.stop().toFuture))
|
||||
}
|
||||
|
||||
resource.use { peer =>
|
||||
Async[F].executionContext.flatMap { implicit ec =>
|
||||
Async[F].fromFuture {
|
||||
(for {
|
||||
keyPair <- createKeyPair(config.common.secretKey)
|
||||
logLevel: js.UndefOr[aqua.js.LogLevel] = LogLevelTransformer.logLevelToAvm(
|
||||
config.common.logLevel.aquavm
|
||||
)
|
||||
pc = PeerConfig(
|
||||
config.common.multiaddr,
|
||||
config.common.timeout.toMillis.toInt : js.UndefOr[Int],
|
||||
keyPair,
|
||||
Debug(printParticleId = config.common.flags.verbose, marineLogLevel = logLevel)
|
||||
)
|
||||
peerConfig = Some(
|
||||
pc.createObj()
|
||||
).orUndefined
|
||||
_ <- Fluence.start(peerConfig).toFuture
|
||||
_ =
|
||||
if (config.common.flags.showConfig) {
|
||||
val configJson = KeyPairOp.toDynamicJSON(keyPair)
|
||||
configJson.updateDynamic("relay")(config.common.multiaddr)
|
||||
configJson.updateDynamic("timeout")(config.common.timeout.toMillis)
|
||||
configJson.updateDynamic("log-level")(config.common.logLevel.compiler.name)
|
||||
OutputPrinter.print(JSON.stringify(configJson, null, 4))
|
||||
}
|
||||
|
||||
// register all services
|
||||
_ = (services ++ getters :+ finisherService :+ resultPrinterService).map(_.register(peer))
|
||||
// register all plugins
|
||||
plugins <- Plugin.getPlugins(plugins)
|
||||
_ = plugins.map(_.register(peer))
|
||||
callFuture = CallJsFunction.funcCallJs(
|
||||
air,
|
||||
functionDef,
|
||||
List.empty
|
||||
)
|
||||
// error will be thrown on failed call
|
||||
_ <- callFuture
|
||||
finisherFuture = finisherService.promise.future
|
||||
// use a timeout in finisher if we have an async function and it hangs on node's side
|
||||
finisher = setTimeout(name, finisherFuture, config.common.timeout)
|
||||
_ <- finisher
|
||||
_ <- Fluence.stop().toFuture
|
||||
} yield validNec(()))
|
||||
.recover(handleFuncCallErrors(name, config.common.timeout))
|
||||
.pure[F]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private def setTimeout[T](funcName: String, f: Future[T], timeout: Duration)(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[T] = {
|
||||
val p = Promise[T]()
|
||||
val timeoutHandle =
|
||||
timers.setTimeout(timeout.toMillis)(
|
||||
p.tryFailure(new TimeoutException(timeoutErrorMessage(funcName, timeout, None)))
|
||||
)
|
||||
f.onComplete { result =>
|
||||
timers.clearTimeout(timeoutHandle)
|
||||
p.tryComplete(result)
|
||||
}
|
||||
p.future
|
||||
}
|
||||
|
||||
private def timeoutErrorMessage(funcName: String, timeout: Duration, pid: Option[String]) = {
|
||||
val pidStr = pid.map(s => " " + s).getOrElse("")
|
||||
s"Function '$funcName' timed out after ${timeout.toMillis} milliseconds. Increase the timeout with '--timeout' option or check if your code can hang while executing$pidStr."
|
||||
}
|
||||
|
||||
private def handleFuncCallErrors(
|
||||
funcName: String,
|
||||
timeout: Duration
|
||||
): PartialFunction[Throwable, ValidatedNec[String, Unit]] = { t =>
|
||||
val message =
|
||||
t match {
|
||||
case te: TimeoutException => te.getMessage
|
||||
case t if t.getMessage.contains("Request timed out after") =>
|
||||
val msg = t.getMessage
|
||||
timeoutErrorMessage(
|
||||
funcName,
|
||||
timeout,
|
||||
Some(msg.substring(msg.indexOf("particle id") - 1, msg.length))
|
||||
)
|
||||
case tjs: JavaScriptException =>
|
||||
val msg = tjs.exception.asInstanceOf[js.Dynamic].selectDynamic("message")
|
||||
if (scalajs.js.isUndefined(msg)) JSON.stringify(tjs.exception.asInstanceOf[js.Any])
|
||||
else msg.toString
|
||||
case _ => t.toString
|
||||
}
|
||||
|
||||
invalidNec(message)
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.ArgOpts.jsonFromFileOpts
|
||||
import aqua.builder.{AquaFunction, ArgumentGetter, Service}
|
||||
import aqua.definitions.{ArrowTypeDef, ProductTypeDef, TypeDefinition}
|
||||
import aqua.js.{Conversions, ServiceHandler, TypeDefinitionJs}
|
||||
import aqua.model.{AquaContext, ServiceModel}
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.{CallArrowToken, CollectionToken, LiteralToken, VarToken}
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.raw.value.{CollectionRaw, LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
import cats.data.*
|
||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, valid, validNec, validNel}
|
||||
import cats.effect.Concurrent
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.semigroup.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Id, Semigroup, ~>}
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
import scala.scalajs.js
|
||||
|
||||
// Description of a service with functions that return structures
|
||||
case class JsonService(name: String, serviceId: String, functions: NonEmptyList[JsonFunction])
|
||||
case class JsonFunction(name: String, result: js.Dynamic, resultType: Type)
|
||||
|
||||
object JsonService {
|
||||
|
||||
def findServices(
|
||||
contexts: Chain[AquaContext],
|
||||
services: List[JsonService]
|
||||
): ValidatedNec[String, List[Service]] = {
|
||||
services
|
||||
.map(js =>
|
||||
contexts
|
||||
.collectFirstSome(_.services.get(js.name))
|
||||
.map(sm => (js, sm))
|
||||
.map(validNec)
|
||||
.getOrElse(
|
||||
Validated.invalidNec[String, ServiceModel](
|
||||
s"There is no service '${js.name}' (described in json-service file) in aqua source or it is not exported. Check the spelling or see https://fluence.dev/docs/aqua-book/language/header/#export"
|
||||
)
|
||||
)
|
||||
)
|
||||
.sequence
|
||||
.andThen { l =>
|
||||
l.map { case (jsonService: JsonService, sm: ServiceModel) =>
|
||||
val aquaFunctions: ValidatedNec[String, NonEmptyList[AquaFunction]] =
|
||||
jsonService.functions.map { jf =>
|
||||
sm.arrows(jf.name)
|
||||
.map { case arr: ArrowType =>
|
||||
if (arr.domain.isEmpty)
|
||||
TypeValidator
|
||||
.validateTypes(jf.name, arr.codomain, Some(ProductType(jf.resultType :: Nil)))
|
||||
.map { _ =>
|
||||
new AquaFunction {
|
||||
override def fnName: String = jf.name
|
||||
|
||||
override def handler: ServiceHandler = _ => {
|
||||
val converted = arr.codomain.toList match {
|
||||
case h :: _ =>
|
||||
Conversions.ts2aqua(jf.result, TypeDefinitionJs(TypeDefinition(h)))
|
||||
case Nil =>
|
||||
Conversions.ts2aqua(
|
||||
jf.result,
|
||||
TypeDefinitionJs(TypeDefinition(NilType))
|
||||
)
|
||||
}
|
||||
|
||||
js.Promise.resolve(converted)
|
||||
}
|
||||
override def arrow: ArrowTypeDef =
|
||||
ArrowTypeDef(ProductTypeDef(NilType), ProductTypeDef(arr.codomain))
|
||||
}
|
||||
}
|
||||
else
|
||||
invalidNec(s"Json service '${jf.name}' cannot have any arguments")
|
||||
}
|
||||
.getOrElse(
|
||||
Validated.invalidNec[String, AquaFunction](
|
||||
s"There is no function '${jf.name}' in service '${jsonService.name}' in aqua source. Check your 'json-service' options"
|
||||
)
|
||||
)
|
||||
}.sequence
|
||||
|
||||
aquaFunctions.map(funcs => Service(jsonService.serviceId, funcs))
|
||||
}.sequence
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.ArgOpts.jsonFromFileOpts
|
||||
import aqua.builder.ArgumentGetter
|
||||
import aqua.js.JsonEncoder
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.{CallArrowToken, CollectionToken, LiteralToken, VarToken}
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.raw.value.{CollectionRaw, LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
import cats.data.*
|
||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, valid, validNec, validNel}
|
||||
import cats.effect.Concurrent
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.semigroup.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Id, Semigroup, ~>}
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
import scala.scalajs.js
|
||||
|
||||
object JsonServiceOpts {
|
||||
|
||||
def jsonServiceOpt[F[_]: Files: Concurrent]
|
||||
: Opts[F[ValidatedNec[String, NonEmptyList[JsonService]]]] = {
|
||||
jsonFromFileOpts("json-service", "Path to file that describes service with JSON result", "j")
|
||||
.map(b =>
|
||||
b.map { case a: ValidatedNec[String, NonEmptyList[(Path, js.Dynamic)]] =>
|
||||
a.andThen { results =>
|
||||
results.map { case (path, res) =>
|
||||
val name = res.name
|
||||
val serviceId = res.serviceId
|
||||
val functionsRaw = res.functions
|
||||
if (js.isUndefined(name) || js.typeOf(name) != "string")
|
||||
invalidNec(s"No name in JSON service '$path' or it is not a string")
|
||||
else if (js.isUndefined(serviceId) || js.typeOf(serviceId) != "string")
|
||||
invalidNec(s"No serviceId in JSON service '$path' or it is not a string")
|
||||
else if (js.isUndefined(functionsRaw) || !js.Array.isArray(functionsRaw))
|
||||
invalidNec(
|
||||
s"'functions' field should exist and be an array in JSON service '$path'"
|
||||
)
|
||||
else {
|
||||
val functionsV: ValidatedNec[String, List[JsonFunction]] = functionsRaw
|
||||
.asInstanceOf[js.Array[js.Dynamic]]
|
||||
.toList
|
||||
.map { f =>
|
||||
val fName = f.name
|
||||
val fResult = f.result
|
||||
if (js.isUndefined(fName) || js.typeOf(fName) != "string")
|
||||
invalidNec(
|
||||
s"One of the functions doesn't have a name or it is not a string in JSON service '$path'"
|
||||
)
|
||||
else if (js.isUndefined(fResult))
|
||||
invalidNec(s"Function '$fName' don't have a result in '$path'")
|
||||
else {
|
||||
val funcName = fName.asInstanceOf[String]
|
||||
JsonEncoder
|
||||
.aquaTypeFromJson(funcName, fResult)
|
||||
.map(t => JsonFunction(funcName, fResult, t))
|
||||
}
|
||||
}
|
||||
.sequence
|
||||
|
||||
functionsV.andThen { fs =>
|
||||
NonEmptyList
|
||||
.fromList(fs)
|
||||
.map(fNEL =>
|
||||
validNec(
|
||||
JsonService(name.asInstanceOf[String], serviceId.asInstanceOf[String], fNEL)
|
||||
)
|
||||
)
|
||||
.getOrElse(
|
||||
invalidNec(s"List of functions in '$name' service is empty in $path")
|
||||
)
|
||||
}
|
||||
}
|
||||
}.sequence
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.*
|
||||
import aqua.ErrorRendering.showError
|
||||
import aqua.backend.air.{AirBackend, FuncAirGen}
|
||||
import aqua.backend.js.JavaScriptBackend
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.backend.Generated
|
||||
import aqua.logging.LogFormatter
|
||||
import aqua.definitions.{FunctionDef, TypeDefinition}
|
||||
import aqua.builder.{ArgumentGetter, Finisher, ResultPrinter, Service}
|
||||
import aqua.compiler.{AquaCompiled, AquaCompiler}
|
||||
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
|
||||
import aqua.io.{AquaFileError, AquaPath, OutputPrinter, Prelude}
|
||||
import aqua.js.*
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{AquaContext, FuncArrow}
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.LiteralToken
|
||||
import aqua.parser.lift.FileSpan
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.run.RunConfig
|
||||
import aqua.run.RunOpts.transformConfig
|
||||
import aqua.types.*
|
||||
import cats.data.*
|
||||
import cats.effect.*
|
||||
import cats.effect.kernel.{Async, Clock}
|
||||
import cats.effect.syntax.async.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.list.*
|
||||
import cats.syntax.monad.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Id, Monad, ~>}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future, Promise}
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSConverters.*
|
||||
import scala.scalajs.js.JSON
|
||||
import scala.scalajs.js.annotation.*
|
||||
|
||||
object RunCommand extends Logging {
|
||||
|
||||
def createKeyPair(
|
||||
sk: Option[Array[Byte]]
|
||||
): Future[KeyPair] = {
|
||||
sk.map { arr =>
|
||||
val typedArr = js.typedarray.Uint8Array.from(arr.map(_.toShort).toJSArray)
|
||||
KeyPair.fromEd25519SK(typedArr).toFuture
|
||||
}.getOrElse(KeyPair.randomEd25519().toFuture)
|
||||
}
|
||||
|
||||
private def createGetter(value: VarRaw, arg: js.Dynamic, argType: Type): ArgumentGetter = {
|
||||
val converted = Conversions.ts2aqua(arg, TypeDefinitionJs(TypeDefinition(argType)))
|
||||
ArgumentGetter(value.copy(baseType = argType), converted)
|
||||
}
|
||||
|
||||
// Creates getter services for variables. Return an error if there is no variable in services
|
||||
// and type of this variable couldn't be optional
|
||||
private def getGettersForVars(
|
||||
vars: List[(String, Type)],
|
||||
argGetters: Map[String, VarJson]
|
||||
): ValidatedNec[String, List[ArgumentGetter]] = {
|
||||
vars.map { (n, argType) =>
|
||||
val argGetterOp = argGetters.get(n)
|
||||
(argGetterOp, argType) match {
|
||||
case (None, _) => Validated.invalidNec(s"Unexcepted. There is no service for '$n' argument")
|
||||
// BoxType could be undefined, so, pass service that will return 'undefined' for this argument
|
||||
case (Some(s), _: BoxType) if s._2 == js.undefined =>
|
||||
Validated.validNec(createGetter(s._1, s._2, argType) :: Nil)
|
||||
case (Some(s), _) if s._2 == js.undefined =>
|
||||
Validated.invalidNec(
|
||||
s"Argument '$n' is missing. Expected argument '$n' of type '$argType'"
|
||||
)
|
||||
case (Some(s), _) =>
|
||||
Validated.validNec(createGetter(s._1, s._2, argType) :: Nil)
|
||||
}
|
||||
}.reduceOption(_ combine _).getOrElse(Validated.validNec(Nil))
|
||||
}
|
||||
|
||||
def resultVariableNames(funcCallable: FuncArrow, name: String): List[String] =
|
||||
funcCallable.arrowType.codomain.toList.zipWithIndex.map { case (t, idx) =>
|
||||
name + idx
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a function that is located in `input` file with FluenceJS SDK. Returns no output
|
||||
* @param func
|
||||
* function name
|
||||
* @param input
|
||||
* path to an aqua code with a function
|
||||
* @param imports
|
||||
* the sources the input needs
|
||||
*/
|
||||
def run[F[_]: Files: AquaIO: Async](
|
||||
func: CliFunc,
|
||||
input: Option[AquaPath],
|
||||
imports: List[Path],
|
||||
runConfig: RunConfig,
|
||||
// services that will pass arguments to air
|
||||
argumentGetters: Map[String, VarJson],
|
||||
// builtin services for aqua run, for example: Console, FileSystem, etc
|
||||
services: List[Service],
|
||||
jsonServices: List[JsonService],
|
||||
plugins: List[String],
|
||||
transformConfig: TransformConfig
|
||||
): F[ValidatedNec[String, Unit]] = {
|
||||
val funcCompiler = new FuncCompiler[F](input, imports, transformConfig)
|
||||
|
||||
for {
|
||||
prelude <- Prelude.init[F](true)
|
||||
contextV <- funcCompiler.compile(prelude.importPaths, true)
|
||||
callResult <- Clock[F].timed {
|
||||
contextV.andThen { context =>
|
||||
FuncCompiler
|
||||
.findFunction(context, func)
|
||||
.andThen(callable =>
|
||||
JsonService
|
||||
.findServices(context, jsonServices)
|
||||
.map(jsonServices => (callable, jsonServices))
|
||||
)
|
||||
}.andThen { case (funcCallable, jsonServices) =>
|
||||
val resultNames = resultVariableNames(funcCallable, runConfig.resultName)
|
||||
val resultPrinterService =
|
||||
ResultPrinter(
|
||||
runConfig.resultPrinterServiceId,
|
||||
runConfig.resultPrinterName,
|
||||
resultNames
|
||||
)
|
||||
val promiseFinisherService =
|
||||
Finisher(runConfig.finisherServiceId, runConfig.finisherFnName)
|
||||
|
||||
val vars = func.args
|
||||
.zip(funcCallable.arrowType.domain.toList)
|
||||
.collect { case (VarRaw(n, _), argType) =>
|
||||
(n, argType)
|
||||
}
|
||||
.distinctBy(_._1)
|
||||
getGettersForVars(vars, argumentGetters).andThen { getters =>
|
||||
val gettersTags = getters.map(s => s.callTag())
|
||||
val preparer =
|
||||
new CallPreparer(
|
||||
func,
|
||||
funcCallable,
|
||||
gettersTags,
|
||||
resultPrinterService.callTag,
|
||||
promiseFinisherService.callTag(),
|
||||
runConfig,
|
||||
transformConfig
|
||||
)
|
||||
preparer.prepare().map { info =>
|
||||
FuncCaller.funcCall[F](
|
||||
info.name,
|
||||
info.air,
|
||||
info.definitions,
|
||||
info.config,
|
||||
resultPrinterService,
|
||||
promiseFinisherService,
|
||||
services ++ jsonServices,
|
||||
getters,
|
||||
plugins
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
} match {
|
||||
case Validated.Valid(f) =>
|
||||
f
|
||||
case i @ Validated.Invalid(_) => i.pure[F]
|
||||
}
|
||||
}
|
||||
(callTime, result) = callResult
|
||||
} yield {
|
||||
logger.debug(s"Call time: ${callTime.toMillis}ms")
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private val builtinServices =
|
||||
aqua.builder
|
||||
.Console() :: aqua.builder.IPFSUploader("ipfs") :: aqua.builder.DeployHelper() :: Nil
|
||||
|
||||
/**
|
||||
* Executes a function with the specified settings
|
||||
* @param common
|
||||
* common settings
|
||||
* @param funcName
|
||||
* function name
|
||||
* @param inputPath
|
||||
* path to a file with a function
|
||||
* @param imports
|
||||
* imports that must be specified for correct compilation
|
||||
* @param args
|
||||
* arguments to pass into a function
|
||||
* @param argumentGetters
|
||||
* services to get argument if it is a variable
|
||||
* @param services
|
||||
* will be registered before calling for correct execution
|
||||
* @return
|
||||
*/
|
||||
def execRun[F[_]: Async](
|
||||
runInfo: RunInfo
|
||||
): F[ValidatedNec[String, Unit]] = {
|
||||
val common = runInfo.common
|
||||
LogFormatter.initLogger(Some(common.logLevel.compiler))
|
||||
implicit val aio: AquaIO[F] = new AquaFilesIO[F]
|
||||
|
||||
RunCommand
|
||||
.run[F](
|
||||
runInfo.func,
|
||||
runInfo.input,
|
||||
runInfo.imports,
|
||||
RunConfig(
|
||||
common
|
||||
),
|
||||
runInfo.argumentGetters,
|
||||
runInfo.services ++ builtinServices,
|
||||
runInfo.jsonServices,
|
||||
runInfo.pluginsPaths,
|
||||
transformConfig(common.on, common.constants, common.flags.noXor, common.flags.noRelay)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.*
|
||||
import aqua.builder.{ArgumentGetter, Service}
|
||||
import aqua.io.{AquaPath, RelativePath}
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.{LiteralToken, VarToken}
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.logging.LogFormatter
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.run.plugin.Plugin
|
||||
import aqua.types.BottomType
|
||||
import cats.data.*
|
||||
import cats.data.Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import cats.effect.kernel.Async
|
||||
import cats.effect.{Concurrent, ExitCode, IO}
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.{~>, Id, Monad}
|
||||
import com.monovore.decline.{Command, Opts}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import java.util.Base64
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSON
|
||||
|
||||
object RunOpts extends Logging {
|
||||
|
||||
val OnPeerConst = "ON_PEER"
|
||||
|
||||
// Default transform config with `onPeer` constant
|
||||
def transformConfig(
|
||||
onPeer: Option[String],
|
||||
constants: List[ConstantRaw],
|
||||
noXor: Boolean,
|
||||
noRelay: Boolean
|
||||
): TransformConfig = {
|
||||
val tc = TransformConfig(
|
||||
constants =
|
||||
onPeer.map(s => ConstantRaw(OnPeerConst, LiteralRaw.quote(s), false)).toList ++ constants
|
||||
)
|
||||
tc.copy(relayVarName = tc.relayVarName.filterNot(_ => noRelay))
|
||||
}
|
||||
|
||||
def runOptsCompose[F[_]: Files: Concurrent]: Opts[F[ValidatedNec[
|
||||
String,
|
||||
(Option[AquaPath], List[Path], FuncWithData, Option[NonEmptyList[JsonService]], List[String])
|
||||
]]] = {
|
||||
(
|
||||
AppOpts.wrapWithOption(AppOpts.inputOpts[F]),
|
||||
AppOpts.importOpts[F],
|
||||
ArgOpts.funcWithArgsOpt[F],
|
||||
AppOpts.wrapWithOption(JsonServiceOpts.jsonServiceOpt),
|
||||
AppOpts.wrapWithOption(Plugin.opt)
|
||||
).mapN { case (inputF, importF, funcWithArgsF, jsonServiceOp, pluginsOp) =>
|
||||
for {
|
||||
inputV: ValidatedNec[String, Option[AquaPath]] <-
|
||||
inputF.map(_.map(_.map(p => Option(RelativePath(p))))).getOrElse {
|
||||
validNec[String, Option[AquaPath]](None).pure[F]
|
||||
}
|
||||
importV <- importF
|
||||
funcWithArgsV <- funcWithArgsF
|
||||
jsonServiceV <- jsonServiceOp
|
||||
.map(_.map(_.map(js => Some(js))))
|
||||
.getOrElse(validNec[String, Option[NonEmptyList[JsonService]]](None).pure[F])
|
||||
pluginsPathsV <- pluginsOp.getOrElse(validNec[String, List[String]](Nil).pure[F])
|
||||
} yield {
|
||||
(inputV, importV, funcWithArgsV, jsonServiceV, pluginsPathsV).mapN {
|
||||
case (i, im, f, j, p) =>
|
||||
(i, im, f, j, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def runOptions[F[_]: AquaIO: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.applyF(
|
||||
name = "run",
|
||||
header = "Run Aqua code",
|
||||
(
|
||||
GeneralOpts.runOpt,
|
||||
runOptsCompose[F]
|
||||
).mapN {
|
||||
case (
|
||||
common,
|
||||
optionsF
|
||||
) =>
|
||||
LogFormatter.initLogger(Some(common.logLevel.compiler))
|
||||
optionsF.map(
|
||||
_.map { case (input, imps, funcWithArgs, services, pluginsPaths) =>
|
||||
RunInfo(
|
||||
common,
|
||||
funcWithArgs.func,
|
||||
input,
|
||||
imps,
|
||||
funcWithArgs.getters,
|
||||
Nil,
|
||||
services.map(_.toList).getOrElse(Nil),
|
||||
pluginsPaths
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def runCommand[F[_]: Files: AquaIO: Async]: Command[F[ValidatedNec[String, Unit]]] =
|
||||
runOptions.command
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
package aqua.run.plugin
|
||||
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.run.JsonService
|
||||
import aqua.run.plugin.Plugin.toPromise
|
||||
import aqua.types.TopType
|
||||
import aqua.definitions.*
|
||||
import cats.data.{NonEmptyList, ValidatedNec}
|
||||
import cats.effect.Concurrent
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.data.Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
import scalajs.js
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.scalajs.js.Promise
|
||||
|
||||
case class Function(name: String, closure: js.Function)
|
||||
|
||||
case class Plugin(name: String, functions: List[Function]) {
|
||||
|
||||
def register(peer: FluencePeer): Unit = {
|
||||
val (handlers, funcTypes) = functions.map { f =>
|
||||
// get arguments types as TopType
|
||||
val argCount = f.closure.length
|
||||
val fields = Range(0, argCount).toList.map { i => ("arg" + i, TopTypeDef) }
|
||||
val arrowType =
|
||||
ArrowTypeDef(LabeledProductTypeDef(fields), UnlabeledProductTypeDef(TopTypeDef :: Nil))
|
||||
val fType = (f.name, arrowType)
|
||||
|
||||
// handlers for registering
|
||||
val h: ServiceHandler = args => {
|
||||
val argsList = Range(0, argCount).toList.map { i =>
|
||||
args(i)
|
||||
}
|
||||
val res = f.closure.call(this.asInstanceOf[js.Any], argsList: _*)
|
||||
toPromise(res)
|
||||
|
||||
}
|
||||
|
||||
((f.name, h), fType)
|
||||
}.unzip
|
||||
|
||||
CallJsFunction.registerService(
|
||||
peer,
|
||||
name,
|
||||
handlers,
|
||||
ServiceDef(Some(name), LabeledProductTypeDef(funcTypes), "")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object Plugin {
|
||||
|
||||
private def fileExt(p: Path): String =
|
||||
p.fileName.toString.split('.').toList.lastOption.getOrElse("")
|
||||
|
||||
def pathToMjsFilesList[F[_]: Files: Concurrent](str: String): F[ValidatedNec[String, List[String]]] = {
|
||||
val path = Path(str).absolute
|
||||
Files[F]
|
||||
.exists(path)
|
||||
.flatMap { exists =>
|
||||
if (exists)
|
||||
Files[F].isRegularFile(path).flatMap { isFile =>
|
||||
if (isFile) {
|
||||
if (fileExt(path) == "mjs") {
|
||||
validNec(path.toString :: Nil).pure[F]
|
||||
} else {
|
||||
invalidNec(s"If path '$str' is a file, it must be with '.mjs' extension")
|
||||
.pure[F]
|
||||
}
|
||||
} else {
|
||||
Files[F]
|
||||
.list(path)
|
||||
.evalMap { ps =>
|
||||
val psAbs = ps.absolute
|
||||
for {
|
||||
isFile <- Files[F].isRegularFile(ps)
|
||||
files <-
|
||||
if (isFile) {
|
||||
if (fileExt(ps) == "mjs") (psAbs :: Nil).pure[F]
|
||||
else Nil.pure[F]
|
||||
} else if (ps.fileName.toString != "node_modules") {
|
||||
Files[F].list(psAbs).filter(pp => fileExt(pp) == "mjs").compile.toList
|
||||
} else {
|
||||
Nil.pure[F]
|
||||
}
|
||||
} yield {
|
||||
files
|
||||
}
|
||||
}
|
||||
.compile
|
||||
.toList
|
||||
.map(_.flatten.map(_.absolute.toString))
|
||||
.map(validNec)
|
||||
}
|
||||
}
|
||||
else {
|
||||
invalidNec(s"There is no path '$str'").pure[F]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def opt[F[_]: Files: Concurrent]: Opts[F[ValidatedNec[String, List[String]]]] = {
|
||||
Opts
|
||||
.options[String]("plugin", "[experimental] Path to a directory with JS plugins", "", "path")
|
||||
.map { strs =>
|
||||
strs.toList.map(s => pathToMjsFilesList(s)).sequence.map(_.sequence.map(_.flatten))
|
||||
}
|
||||
}
|
||||
|
||||
def getPlugins(paths: List[String])(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[List[Plugin]] =
|
||||
paths.map(p => getPlugin(p)).sequence.map(_.flatten)
|
||||
|
||||
private def toPromise(arg: js.Dynamic): js.Promise[js.Dynamic] = {
|
||||
if (js.typeOf(arg) == "object" && js.typeOf(arg.`then`) == "function")
|
||||
arg.asInstanceOf[js.Promise[js.Dynamic]]
|
||||
else js.Promise.resolve(arg)
|
||||
}
|
||||
|
||||
def getPlugin(path: String)(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[List[Plugin]] = {
|
||||
for {
|
||||
file <- js.`import`[js.Dynamic](path).toFuture
|
||||
plugin <- {
|
||||
if (js.typeOf(file.plugins) == "function") {
|
||||
val res = file.applyDynamic("plugins")()
|
||||
toPromise(res).toFuture.map(_.asInstanceOf[js.Dictionary[js.Dictionary[js.Any]]])
|
||||
} else {
|
||||
Future(js.Dictionary[js.Dictionary[js.Any]]())
|
||||
}
|
||||
}
|
||||
} yield {
|
||||
plugin.map { case (k, v) =>
|
||||
val functions = v.map { case (kf, vf) =>
|
||||
Function(kf, vf.asInstanceOf[js.Function])
|
||||
}.toList
|
||||
Plugin(k, functions)
|
||||
}.toList
|
||||
}
|
||||
}
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
package aqua.script
|
||||
|
||||
import aqua.*
|
||||
import aqua.ArgOpts.{dataFileOrStringOpt, funcOpt, funcWithArgsOpt}
|
||||
import aqua.backend.Generated
|
||||
import aqua.backend.air.{AirBackend, AirGen, FuncAirGen}
|
||||
import aqua.builder.ArgumentGetter
|
||||
import aqua.compiler.AquaCompiler
|
||||
import aqua.js.VarJson
|
||||
import aqua.io.{PackagePath, Prelude, RelativePath}
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.keypair.KeyPairShow.show
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{AquaContext, FuncArrow, LiteralModel}
|
||||
import aqua.parser.lift.FileSpan
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.res.{AquaRes, FuncRes}
|
||||
import aqua.run.RunOpts.logger
|
||||
import aqua.run.{CliFunc, FuncCompiler, GeneralOptions, GeneralOpts, RunCommand, RunConfig, RunOpts}
|
||||
import aqua.types.{ArrowType, LiteralType, NilType, ScalarType}
|
||||
import cats.data.*
|
||||
import cats.data.Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import cats.effect.kernel.{Async, Clock}
|
||||
import cats.effect.{Concurrent, ExitCode, Resource, Sync}
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Applicative, Monad}
|
||||
import com.monovore.decline.{Command, Opts}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.scalajs.js
|
||||
|
||||
object ScriptOpts extends Logging {
|
||||
|
||||
val ScriptAqua = "aqua/script.aqua"
|
||||
|
||||
val AddFuncName = "schedule"
|
||||
val RemoveFuncName = "remove"
|
||||
val ListFuncName = "list"
|
||||
|
||||
case class FuncWithLiteralArgs(func: CliFunc, args: List[LiteralRaw])
|
||||
|
||||
// Func with only literal arguments (strings, booleans or numbers)
|
||||
def funcWithLiteralsOpt[F[_]: Files: Concurrent]
|
||||
: Opts[F[ValidatedNec[String, FuncWithLiteralArgs]]] = {
|
||||
(dataFileOrStringOpt[F], funcOpt).mapN { case (dataF, func) =>
|
||||
dataF.map { dataV =>
|
||||
dataV.andThen { data =>
|
||||
resolveOnlyLiteralsFromData(func.args, data).map { literals =>
|
||||
FuncWithLiteralArgs(func, literals)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def resolveOnlyLiteralsFromData(
|
||||
args: List[ValueRaw],
|
||||
data: Option[js.Dynamic]
|
||||
): ValidatedNec[String, List[LiteralRaw]] = {
|
||||
val literals = args.map {
|
||||
case l: LiteralRaw => validNec(l) // TODO handle CollectionRaw?
|
||||
case v @ VarRaw(name, _) =>
|
||||
data.map { d =>
|
||||
val arg = d.selectDynamic(name)
|
||||
js.typeOf(arg) match {
|
||||
case "number" => validNec(LiteralRaw(arg.toString, LiteralType.number))
|
||||
case "string" => validNec(LiteralRaw(arg.toString, LiteralType.string))
|
||||
case "boolean" => validNec(LiteralRaw(arg.toString, LiteralType.bool))
|
||||
case t =>
|
||||
invalidNec(
|
||||
s"Scheduled script functions support 'string', 'boolean' and 'number' argument types only"
|
||||
)
|
||||
}
|
||||
}.getOrElse(invalidNec(s"There is no '$name' argument in data"))
|
||||
case _ =>
|
||||
invalidNec(
|
||||
s"Scheduled script functions support 'string', 'boolean' and 'number' argument types only"
|
||||
)
|
||||
}
|
||||
|
||||
literals.traverse(identity)
|
||||
}
|
||||
|
||||
def scriptOpt[F[_]: Async: AquaIO]: Command[F[ValidatedNec[String, Unit]]] =
|
||||
CommandBuilder(
|
||||
name = "script",
|
||||
header = "Manage scheduled scripts",
|
||||
NonEmptyList(add, list :: remove :: Nil)
|
||||
).command
|
||||
|
||||
def intervalOpt: Opts[Option[Int]] =
|
||||
AppOpts.wrapWithOption(
|
||||
Opts
|
||||
.option[Int]("interval", "Indicating how often the script will run in seconds", "n")
|
||||
)
|
||||
|
||||
def scriptIdOpt: Opts[String] =
|
||||
Opts
|
||||
.option[String]("script-id", "Script id to remove", "c")
|
||||
|
||||
def generateAir(callable: FuncArrow, transformConfig: TransformConfig): String = {
|
||||
val funcRes = Transform.funcRes(callable, transformConfig).value
|
||||
AirGen(funcRes.body).generate.show
|
||||
}
|
||||
|
||||
private def commonScriptOpts = GeneralOpts.commonOpt(false, true, true)
|
||||
|
||||
private def compileAir[F[_]: Async: AquaIO](
|
||||
input: Path,
|
||||
imports: List[Path],
|
||||
funcWithArgs: FuncWithLiteralArgs
|
||||
): F[ValidatedNec[String, String]] = {
|
||||
val tConfig = TransformConfig(relayVarName = None)
|
||||
val funcCompiler =
|
||||
new FuncCompiler[F](
|
||||
Option(RelativePath(input)),
|
||||
imports,
|
||||
tConfig
|
||||
)
|
||||
|
||||
val funcName = funcWithArgs.func.name
|
||||
|
||||
for {
|
||||
prelude <- Prelude.init[F](true)
|
||||
contextV <- funcCompiler.compile(prelude.importPaths)
|
||||
wrappedBody = CallArrowRawTag.func(funcName, Call(funcWithArgs.func.args, Nil)).leaf
|
||||
result = contextV
|
||||
.andThen(context => FuncCompiler.findFunction(context, funcWithArgs.func))
|
||||
.map { callable =>
|
||||
generateAir(
|
||||
FuncArrow(
|
||||
funcName + "_scheduled",
|
||||
wrappedBody,
|
||||
ArrowType(NilType, NilType),
|
||||
Nil,
|
||||
Map(funcName -> callable),
|
||||
Map.empty,
|
||||
None
|
||||
),
|
||||
tConfig
|
||||
)
|
||||
}
|
||||
} yield result
|
||||
}
|
||||
|
||||
def add[F[_]: Async: AquaIO]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.applyF(
|
||||
name = "add",
|
||||
header = "Upload aqua function as a scheduled script.",
|
||||
(
|
||||
commonScriptOpts,
|
||||
scheduleOptsCompose[F],
|
||||
intervalOpt
|
||||
).mapN { (common, optionsF, intervalOp) =>
|
||||
val res: F[ValidatedNec[String, RunInfo]] = optionsF
|
||||
.flatMap(
|
||||
_.map { case (input, imports, funcWithArgs) =>
|
||||
val intervalArg =
|
||||
intervalOp
|
||||
.map(i => LiteralRaw(i.toString, LiteralType.number))
|
||||
.getOrElse(ValueRaw.Nil)
|
||||
|
||||
val someRes: F[ValidatedNec[String, RunInfo]] = for {
|
||||
scriptV <- compileAir(input, imports, funcWithArgs)
|
||||
result: ValidatedNec[String, RunInfo] = scriptV.map { script =>
|
||||
val scriptVar = VarRaw("script", ScalarType.string)
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(AddFuncName, scriptVar :: intervalArg :: Nil),
|
||||
Option(PackagePath(ScriptAqua)),
|
||||
Nil,
|
||||
Map(
|
||||
"script" -> VarJson(
|
||||
scriptVar,
|
||||
// hack, cannot create unnamed Dynamic
|
||||
// TODO: fix it
|
||||
scalajs.js.Dynamic.literal("script" -> script).selectDynamic("script")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
} yield {
|
||||
result
|
||||
}
|
||||
|
||||
someRes
|
||||
}.fold(
|
||||
errs => Validated.Invalid[NonEmptyChain[String]](errs).pure[F],
|
||||
identity
|
||||
)
|
||||
)
|
||||
res
|
||||
}
|
||||
)
|
||||
|
||||
def scheduleOptsCompose[F[_]: Files: Async]
|
||||
: Opts[F[ValidatedNec[String, (Path, List[Path], FuncWithLiteralArgs)]]] = {
|
||||
(AppOpts.inputOpts[F], AppOpts.importOpts[F], funcWithLiteralsOpt[F]).mapN {
|
||||
case (inputF, importF, funcWithLiteralsF) =>
|
||||
for {
|
||||
inputV <- inputF
|
||||
importV <- importF
|
||||
funcWithLiteralsV <- funcWithLiteralsF
|
||||
} yield {
|
||||
(inputV, importV, funcWithLiteralsV).mapN { case (i, im, f) =>
|
||||
(i, im, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removes scheduled script from a node
|
||||
def remove[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder.valid[F](
|
||||
"remove",
|
||||
"Remove a script from a remote peer",
|
||||
(
|
||||
commonScriptOpts,
|
||||
scriptIdOpt
|
||||
).mapN { (common, scriptId) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(RemoveFuncName, LiteralRaw.quote(scriptId) :: Nil),
|
||||
Option(PackagePath(ScriptAqua))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// Print all scheduled scripts
|
||||
def list[F[_]: Async]: SubCommandBuilder[F] =
|
||||
SubCommandBuilder
|
||||
.simple[F]("list", "Print all scheduled scripts", PackagePath(ScriptAqua), ListFuncName)
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.js.JsonEncoder
|
||||
import aqua.types.{ArrayType, LiteralType, OptionType, StructType}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import cats.data.{NonEmptyList, NonEmptyMap}
|
||||
|
||||
class JsonEncoderSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"json encoder" should "get type from json" in {
|
||||
val json = scalajs.js.JSON.parse("""{
|
||||
|"arr2": [{
|
||||
| "a": "fef",
|
||||
| "b": [1,2,3,4],
|
||||
| "c": "erfer"
|
||||
| },{
|
||||
| "a": "ferfer",
|
||||
| "b": [1,2,3,4],
|
||||
| "c": "erfer"
|
||||
| }, {
|
||||
| "a": "as",
|
||||
| "d": "gerrt"
|
||||
| }]
|
||||
|} """.stripMargin)
|
||||
val res = JsonEncoder.aquaTypeFromJson("n", json)
|
||||
res.isValid shouldBe true
|
||||
|
||||
val elType = StructType(
|
||||
"",
|
||||
NonEmptyMap.of(
|
||||
("a", LiteralType.string),
|
||||
("b", ArrayType(LiteralType.number)),
|
||||
("c", OptionType(LiteralType.string)),
|
||||
("d", OptionType(LiteralType.string))
|
||||
)
|
||||
)
|
||||
res.toOption.get shouldBe StructType("", NonEmptyMap.of(("arr2", ArrayType(elType))))
|
||||
}
|
||||
|
||||
"json encoder" should "get type from json 1" in {
|
||||
val json = scalajs.js.JSON.parse("""{
|
||||
|"arr2": [{
|
||||
| "b": [1,2,3,4]
|
||||
| },{
|
||||
| "b": [1,2,3,4]
|
||||
| }, {
|
||||
| "b": "gerrt"
|
||||
| }]
|
||||
|} """.stripMargin)
|
||||
val res = JsonEncoder.aquaTypeFromJson("n", json)
|
||||
res.isValid shouldBe false
|
||||
}
|
||||
|
||||
"json encoder" should "get type from json 2" in {
|
||||
val json =
|
||||
scalajs.js.JSON.parse(
|
||||
"""{
|
||||
|"arr1": [{"a": [{"c": "", "d": 123}, {"c": ""}], "b": ""}, {"b": ""}],
|
||||
|"arr2": [1,2,3,4],
|
||||
|"arr3": ["fre", "grt", "rtgrt"],
|
||||
|"str": "egrerg",
|
||||
|"num": 123
|
||||
|} """.stripMargin
|
||||
)
|
||||
val res = JsonEncoder.aquaTypeFromJson("n", json)
|
||||
res.isValid shouldBe true
|
||||
|
||||
val innerElType = StructType(
|
||||
"",
|
||||
NonEmptyMap.of(
|
||||
("c", LiteralType.string),
|
||||
("d", OptionType(LiteralType.number))
|
||||
)
|
||||
)
|
||||
val elType = StructType(
|
||||
"",
|
||||
NonEmptyMap.of(
|
||||
("a", ArrayType(innerElType)),
|
||||
("b", LiteralType.string)
|
||||
)
|
||||
)
|
||||
|
||||
val t = StructType(
|
||||
"",
|
||||
NonEmptyMap.of(
|
||||
("arr1", ArrayType(elType)),
|
||||
("arr2", ArrayType(LiteralType.number)),
|
||||
("arr3", ArrayType(LiteralType.string)),
|
||||
("str", LiteralType.string),
|
||||
("num", LiteralType.number)
|
||||
)
|
||||
)
|
||||
|
||||
res.toOption.get shouldBe t
|
||||
}
|
||||
|
||||
"json encoder" should "get type from json 3" in {
|
||||
val json = scalajs.js.JSON.parse("""{
|
||||
|"arr2": [{
|
||||
| "b": [1,2,3,4]
|
||||
| },{
|
||||
| "b": [1,2,3,4]
|
||||
| }, {
|
||||
| "b": "gerrt"
|
||||
| }]
|
||||
|} """.stripMargin)
|
||||
val res = JsonEncoder.aquaTypeFromJson("n", json)
|
||||
res.isValid shouldBe false
|
||||
}
|
||||
|
||||
"json encoder" should "get type from json 4" in {
|
||||
val json =
|
||||
scalajs.js.JSON.parse(
|
||||
"""{
|
||||
|"arr4": [{"a": "", "b": {"c": "", "d": [1,2,3,4]}}, {"a": ""}]
|
||||
|} """.stripMargin
|
||||
)
|
||||
val res = JsonEncoder.aquaTypeFromJson("n", json)
|
||||
res.isValid shouldBe true
|
||||
|
||||
val arr4InnerType = OptionType(
|
||||
StructType(
|
||||
"",
|
||||
NonEmptyMap.of(
|
||||
("c", LiteralType.string),
|
||||
("d", ArrayType(LiteralType.number))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val arr4ElType = StructType(
|
||||
"",
|
||||
NonEmptyMap.of(
|
||||
("a", LiteralType.string),
|
||||
("b", arr4InnerType)
|
||||
)
|
||||
)
|
||||
|
||||
val t = StructType(
|
||||
"",
|
||||
NonEmptyMap.of(
|
||||
("arr4", ArrayType(arr4ElType))
|
||||
)
|
||||
)
|
||||
|
||||
res.toOption.get shouldBe t
|
||||
}
|
||||
}
|
@ -1,207 +0,0 @@
|
||||
package aqua
|
||||
import aqua.run.TypeValidator
|
||||
import aqua.types.{ArrayType, LiteralType, OptionType, ScalarType, StructType, Type}
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import cats.data.{NonEmptyList, NonEmptyMap, ValidatedNec}
|
||||
|
||||
class TypeValidatorSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val aquaType = StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("field1", OptionType(ArrayType(ScalarType.u8))),
|
||||
("field2", OptionType(ArrayType(OptionType(ScalarType.i32)))),
|
||||
("field3", ArrayType(ArrayType(ArrayType(ScalarType.i64)))),
|
||||
(
|
||||
"field4",
|
||||
OptionType(
|
||||
StructType(
|
||||
"some2",
|
||||
NonEmptyMap.of(
|
||||
("innerfield1", OptionType(ScalarType.u32)),
|
||||
("innerfield2", ArrayType(ScalarType.i16))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private def validate(aquaType: Type, jsonType: Type) = {
|
||||
TypeValidator.validateTypes("some", aquaType, Some(jsonType))
|
||||
}
|
||||
|
||||
"type validator" should "return invalid result if check same type" in {
|
||||
val res = validate(aquaType, aquaType)
|
||||
res.isValid shouldBe false
|
||||
}
|
||||
|
||||
"type validator" should "return invalid result if there is no field" in {
|
||||
val res = validate(
|
||||
StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("field1", ScalarType.u8),
|
||||
("field2", ArrayType(ScalarType.string))
|
||||
)
|
||||
),
|
||||
StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("field1", ScalarType.u8)
|
||||
)
|
||||
))
|
||||
res.isValid shouldBe false
|
||||
}
|
||||
|
||||
"type validator" should "validate optional types properly" in {
|
||||
val aquaOptionalArrArrType = OptionType(ArrayType(ArrayType(ScalarType.u8)))
|
||||
val aquaOptionalArrType = OptionType(ArrayType(ScalarType.u8))
|
||||
val aquaOptionalType = OptionType(ScalarType.u8)
|
||||
|
||||
val res1 = validate(aquaOptionalType, LiteralType.number)
|
||||
res1.isValid shouldBe true
|
||||
val res2 = validate(aquaOptionalArrType, ArrayType(LiteralType.number))
|
||||
res2.isValid shouldBe true
|
||||
val res3 = validate(aquaOptionalArrArrType, ArrayType(ArrayType(LiteralType.number)))
|
||||
res3.isValid shouldBe true
|
||||
|
||||
val res1Invalid = validate(aquaOptionalType, ArrayType(LiteralType.number))
|
||||
res1Invalid.isValid shouldBe false
|
||||
}
|
||||
|
||||
"type validator" should "validate array types properly" in {
|
||||
val aquaArrArrArrType = ArrayType(ArrayType(ArrayType(ScalarType.u8)))
|
||||
val aquaArrArrType = ArrayType(ArrayType(ScalarType.u8))
|
||||
val aquaArrType = ArrayType(ScalarType.u8)
|
||||
|
||||
val res1 = validate(aquaArrType, ArrayType(LiteralType.number))
|
||||
res1.isValid shouldBe true
|
||||
val res2 = validate(aquaArrArrType, ArrayType(ArrayType(LiteralType.number)))
|
||||
res2.isValid shouldBe true
|
||||
val res3 = validate(aquaArrArrArrType, ArrayType(ArrayType(ArrayType(LiteralType.number))))
|
||||
res3.isValid shouldBe true
|
||||
|
||||
val res1invalid = validate(aquaArrType, LiteralType.number)
|
||||
res1invalid.isInvalid shouldBe true
|
||||
val res2invalid = validate(aquaArrArrType, ArrayType(LiteralType.number))
|
||||
res2invalid.isInvalid shouldBe true
|
||||
}
|
||||
|
||||
"type validator" should "validate options with arrays types properly" in {
|
||||
val aquaOptArrOptArrType = OptionType(ArrayType(OptionType(ArrayType(ScalarType.u8))))
|
||||
|
||||
val res1 = validate(aquaOptArrOptArrType, ArrayType(ArrayType(LiteralType.number)))
|
||||
res1.isValid shouldBe true
|
||||
|
||||
val res1invalid =
|
||||
validate(aquaOptArrOptArrType, ArrayType(ArrayType(ArrayType(LiteralType.number))))
|
||||
res1invalid.isValid shouldBe false
|
||||
val res2invalid =
|
||||
validate(aquaOptArrOptArrType, ArrayType(ArrayType(ArrayType(ArrayType(LiteralType.number)))))
|
||||
res2invalid.isValid shouldBe false
|
||||
}
|
||||
|
||||
"type validator" should "validate complex types properly" in {
|
||||
|
||||
val res1 = validate(
|
||||
aquaType,
|
||||
StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("field1", ArrayType(LiteralType.number)),
|
||||
("field2", ArrayType(LiteralType.number)),
|
||||
("field3", ArrayType(ArrayType(ArrayType(LiteralType.number)))),
|
||||
(
|
||||
"field4",
|
||||
StructType(
|
||||
"some2",
|
||||
NonEmptyMap.of(
|
||||
("innerfield1", LiteralType.number),
|
||||
("innerfield2", ArrayType(LiteralType.number))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
res1.isValid shouldBe true
|
||||
}
|
||||
|
||||
"type validator" should "return invalid if there is no field" in {
|
||||
val structType = StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("field1", ScalarType.u8),
|
||||
("field2", ScalarType.string),
|
||||
("field3", OptionType(ScalarType.string))
|
||||
)
|
||||
)
|
||||
|
||||
val res1invalid = validate(
|
||||
structType,
|
||||
StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("field2", LiteralType.string)
|
||||
)
|
||||
)
|
||||
)
|
||||
res1invalid.isValid shouldBe false
|
||||
|
||||
val res2invalid = validate(
|
||||
structType,
|
||||
StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("field1", ScalarType.u8)
|
||||
)
|
||||
)
|
||||
)
|
||||
res2invalid.isValid shouldBe false
|
||||
|
||||
val res1 = validate(
|
||||
structType,
|
||||
StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("field1", LiteralType.number),
|
||||
("field2", LiteralType.string)
|
||||
)
|
||||
)
|
||||
)
|
||||
res1.isValid shouldBe true
|
||||
|
||||
validate(
|
||||
structType,
|
||||
StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("field1", ScalarType.u8),
|
||||
("field2", ScalarType.string),
|
||||
("field3", ScalarType.string)
|
||||
)
|
||||
)
|
||||
).isValid shouldBe true
|
||||
}
|
||||
|
||||
"type validator" should "return invalid if there is one array when it must be two" in {
|
||||
val leftType = StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("arrr", OptionType(ArrayType(ArrayType(ScalarType.u8))))
|
||||
)
|
||||
)
|
||||
|
||||
val rightType = StructType(
|
||||
"some",
|
||||
NonEmptyMap.of(
|
||||
("arrr", ArrayType(LiteralType.number))
|
||||
)
|
||||
)
|
||||
|
||||
validate(leftType, rightType).isInvalid shouldBe true
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import cats.data.ValidatedNec
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import cats.effect.std.Console
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
// Scala-specific options and subcommands
|
||||
object PlatformOpts {
|
||||
def opts[F[_]: Files: AquaIO: Async: Console]: Opts[F[ValidatedNec[String, Unit]]] = Opts.never
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.logging.LogFormatter
|
||||
import aqua.model.transform.TransformConfig
|
||||
import cats.data.Validated
|
||||
import cats.effect.{IO, IOApp, Sync}
|
||||
import fs2.io.file.Path
|
||||
import scribe.Level
|
||||
|
||||
object Test extends IOApp.Simple {
|
||||
|
||||
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
|
||||
|
||||
override def run: IO[Unit] = {
|
||||
scribe.Logger.root
|
||||
.clearHandlers()
|
||||
.clearModifiers()
|
||||
.withHandler(formatter = LogFormatter.formatterWithFilename, minimumLevel = Some(Level.Info))
|
||||
.replace()
|
||||
for {
|
||||
start <- IO(System.currentTimeMillis())
|
||||
_ <- AquaPathCompiler
|
||||
.compileFilesTo[IO](
|
||||
Path("./aqua-src/antithesis.aqua"),
|
||||
List(Path("./aqua")),
|
||||
Option(Path("./target")),
|
||||
TypeScriptBackend(false, "IFluenceClient$$"),
|
||||
TransformConfig(),
|
||||
false
|
||||
)
|
||||
.map {
|
||||
case Validated.Invalid(errs) =>
|
||||
errs.map(System.err.println): Unit
|
||||
case Validated.Valid(res) =>
|
||||
res.map(println): Unit
|
||||
}
|
||||
_ <- IO.println("Compilation ends in: " + (System.currentTimeMillis() - start) + " ms")
|
||||
} yield ()
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package aqua.air
|
||||
import aqua.backend.AirFunction
|
||||
import cats.data.ValidatedNec
|
||||
import cats.effect.Async
|
||||
import cats.data.Validated.validNec
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
object AirValidation {
|
||||
|
||||
def init[F[_]: Async](): F[Unit] = Async[F].pure(())
|
||||
|
||||
def validate[F[_]: Async](airs: List[AirFunction]): F[ValidatedNec[String, Unit]] = Async[F].pure(validNec(()))
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
service CustomId("cid"):
|
||||
id() -> string
|
||||
|
||||
func first(node_id: string, viaAr: []string) -> string:
|
||||
on node_id via viaAr:
|
||||
p <- CustomId.id()
|
||||
<- p
|
||||
|
||||
|
||||
func second(node_id: string, viaStr: *string) -> string:
|
||||
on node_id via viaStr:
|
||||
p <- CustomId.id()
|
||||
<- p
|
||||
|
||||
func third(relay: string, node_id: string, viaOpt: ?string) -> string:
|
||||
on node_id via viaOpt:
|
||||
p <- CustomId.id()
|
||||
<- p
|
@ -1,180 +0,0 @@
|
||||
import aqua.AquaIO
|
||||
import aqua.backend.Generated
|
||||
import aqua.compiler.AquaCompiled
|
||||
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
|
||||
import cats.data.Chain
|
||||
import cats.effect.IO
|
||||
import cats.effect.unsafe.implicits.global
|
||||
import fs2.text
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import fs2.io.file.{Files, Path}
|
||||
import org.scalatest.flatspec.AsyncFlatSpec
|
||||
|
||||
class SourcesSpec extends AsyncFlatSpec with Matchers {
|
||||
implicit val aquaIO: AquaIO[IO] = AquaFilesIO.summon[IO]
|
||||
|
||||
"AquaFileSources" should "generate correct fileId with imports" in {
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir/path-test")
|
||||
val importPath = path.resolve("imports")
|
||||
|
||||
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
|
||||
sourceGen.sources.map { result =>
|
||||
result.isValid shouldBe true
|
||||
|
||||
val listResult = result
|
||||
.getOrElse(Chain.empty)
|
||||
.toList
|
||||
.map { case (fid, s) =>
|
||||
(fid.file.toString.split("/").last, s)
|
||||
}
|
||||
.sortBy(_._1) // sort cause different systems have different order of file reading
|
||||
|
||||
val (id, importFile) = listResult(1)
|
||||
id shouldBe "index.aqua"
|
||||
importFile.nonEmpty shouldBe true
|
||||
|
||||
val (importNearId, importFileNear) = listResult.head
|
||||
importNearId shouldBe "importNear.aqua"
|
||||
importFileNear.nonEmpty shouldBe true
|
||||
}.unsafeToFuture()
|
||||
}
|
||||
|
||||
"AquaFileSources" should "throw an error if a source file doesn't exist" in {
|
||||
val path = Path("some/random/path")
|
||||
|
||||
val sourceGen = new AquaFileSources[IO](path, Nil)
|
||||
|
||||
sourceGen.sources.map(result => result.isInvalid shouldBe true).unsafeToFuture()
|
||||
}
|
||||
|
||||
"AquaFileSources" should "throw an error if there is no import that is indicated in a source" in {
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir")
|
||||
val importPath = path.resolve("random/import/path")
|
||||
|
||||
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
|
||||
sourceGen
|
||||
.resolveImport(FileModuleId(path.resolve("no-file.aqua")), "no/file")
|
||||
.map(result => result.isInvalid shouldBe true)
|
||||
.unsafeToFuture()
|
||||
}
|
||||
|
||||
"AquaFileSources" should "find correct imports" in {
|
||||
val srcPath = Path("cli/cli/.jvm/src/test/test-dir/index.aqua")
|
||||
val importPath = srcPath.resolve("imports")
|
||||
|
||||
val sourceGen = new AquaFileSources[IO](srcPath, importPath :: Nil)
|
||||
|
||||
(for {
|
||||
// should be found in importPath
|
||||
result <- sourceGen.resolveImport(FileModuleId(srcPath), "imports/import.aqua")
|
||||
exists <- {
|
||||
result.isValid shouldBe true
|
||||
val file = result.getOrElse(FileModuleId(Path("/some/random"))).file
|
||||
Files[IO].exists(file)
|
||||
}
|
||||
_ = exists shouldBe true
|
||||
|
||||
// should be found near src file
|
||||
result2 <- sourceGen.resolveImport(FileModuleId(srcPath), "importNear.aqua")
|
||||
exists2 <- {
|
||||
result2.isValid shouldBe true
|
||||
val file2 = result2.getOrElse(FileModuleId(Path("/some/random"))).file
|
||||
Files[IO].exists(file2)
|
||||
}
|
||||
_ = exists2 shouldBe true
|
||||
// near src file but in another directory
|
||||
sourceGen2 = new AquaFileSources[IO](srcPath, Nil)
|
||||
result3 <- sourceGen2.resolveImport(FileModuleId(srcPath), "imports/import.aqua")
|
||||
exists3 <- {
|
||||
result3.isValid shouldBe true
|
||||
val file3 = result3.getOrElse(FileModuleId(Path("/some/random"))).file
|
||||
Files[IO].exists(file3)
|
||||
}
|
||||
} yield { exists3 shouldBe true }).unsafeToFuture()
|
||||
}
|
||||
|
||||
"AquaFileSources" should "resolve correct path for target" in {
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir")
|
||||
val filePath = path.resolve("some-dir/file.aqua")
|
||||
|
||||
val targetPath = Path("/target/dir/")
|
||||
|
||||
val sourceGen = new AquaFileSources[IO](path, Nil)
|
||||
|
||||
val suffix = "_custom.super"
|
||||
|
||||
sourceGen
|
||||
.resolveTargetPath(filePath, targetPath, suffix)
|
||||
.map { resolved =>
|
||||
resolved.isValid shouldBe true
|
||||
|
||||
val targetFilePath = resolved.toOption.get
|
||||
targetFilePath.toString shouldBe "/target/dir/some-dir/file_custom.super"
|
||||
}
|
||||
.unsafeToFuture()
|
||||
}
|
||||
|
||||
"AquaFileSources" should "resolve correct path for target when file is in current directory" in {
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir")
|
||||
val filePath = path.resolve("file.aqua")
|
||||
|
||||
val targetPath = Path("/target/dir/")
|
||||
|
||||
val sourceGen = new AquaFileSources[IO](path, Nil)
|
||||
|
||||
val suffix = "_custom.super"
|
||||
|
||||
sourceGen
|
||||
.resolveTargetPath(filePath, targetPath, suffix)
|
||||
.map { resolved =>
|
||||
resolved.isValid shouldBe true
|
||||
|
||||
val targetFilePath = resolved.toOption.get
|
||||
targetFilePath.toString shouldBe "/target/dir/file_custom.super"
|
||||
}
|
||||
.unsafeToFuture()
|
||||
}
|
||||
|
||||
"AquaFileSources" should "write correct file with correct path" in {
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir")
|
||||
val filePath = path.resolve("imports/import.aqua")
|
||||
|
||||
val targetPath = path.resolve("target/")
|
||||
|
||||
// clean up
|
||||
val resultPath = Path("cli/cli/.jvm/src/test/test-dir/target/imports/import_hey.custom")
|
||||
(for {
|
||||
_ <- Files[IO].deleteIfExists(resultPath)
|
||||
sourceGen = new AquaFileSources[IO](path, Nil)
|
||||
content = "some random content"
|
||||
compiled = AquaCompiled[FileModuleId](
|
||||
FileModuleId(filePath),
|
||||
Seq(Generated("_hey.custom", content, Nil)),
|
||||
1,
|
||||
1
|
||||
)
|
||||
resolved <- sourceGen.write(targetPath)(compiled)
|
||||
_ = {
|
||||
resolved.size shouldBe 1
|
||||
resolved.head.isValid shouldBe true
|
||||
}
|
||||
exists <- Files[IO].exists(resultPath)
|
||||
_ = exists shouldBe true
|
||||
result <- Files[IO]
|
||||
.readAll(resultPath)
|
||||
.fold(
|
||||
Vector
|
||||
.empty[Byte]
|
||||
)((acc, b) => acc :+ b)
|
||||
.flatMap(fs2.Stream.emits)
|
||||
.through(text.utf8.decode)
|
||||
.attempt
|
||||
.compile
|
||||
.last
|
||||
resultText = result.get.right.get
|
||||
_ <- Files[IO].deleteIfExists(resultPath)
|
||||
} yield {
|
||||
resultText shouldBe content
|
||||
}).unsafeToFuture()
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
import aqua.AquaPathCompiler
|
||||
import aqua.backend.air.AirBackend
|
||||
import aqua.backend.js.JavaScriptBackend
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.model.transform.TransformConfig
|
||||
import cats.effect.IO
|
||||
import cats.effect.unsafe.implicits.global
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
class WriteFileSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"cli" should "compile aqua code in js" in {
|
||||
val src = Path("./cli/cli/.jvm/src/test/aqua")
|
||||
val targetTs = Files[IO].createTempDirectory.unsafeRunSync()
|
||||
val targetJs = Files[IO].createTempDirectory.unsafeRunSync()
|
||||
val targetAir = Files[IO].createTempDirectory.unsafeRunSync()
|
||||
|
||||
import aqua.files.AquaFilesIO.summon
|
||||
|
||||
val bc = TransformConfig()
|
||||
AquaPathCompiler
|
||||
.compileFilesTo[IO](src, List.empty, Option(targetTs), TypeScriptBackend(false), bc, false)
|
||||
.unsafeRunSync()
|
||||
.leftMap { err =>
|
||||
println(err)
|
||||
err
|
||||
}
|
||||
.isValid should be(true)
|
||||
val targetTsFile = targetTs.resolve("test.ts")
|
||||
Files[IO].exists(targetTsFile).unsafeRunSync() should be(true)
|
||||
Files[IO].deleteIfExists(targetTsFile).unsafeRunSync()
|
||||
|
||||
AquaPathCompiler
|
||||
.compileFilesTo[IO](src, List.empty, Option(targetJs), JavaScriptBackend(false), bc, false)
|
||||
.unsafeRunSync()
|
||||
.leftMap { err =>
|
||||
println(err)
|
||||
err
|
||||
}
|
||||
.isValid should be(true)
|
||||
val targetJsFile = targetJs.resolve("test.js")
|
||||
Files[IO].exists(targetJsFile).unsafeRunSync() should be(true)
|
||||
Files[IO].deleteIfExists(targetJsFile).unsafeRunSync()
|
||||
|
||||
AquaPathCompiler
|
||||
.compileFilesTo[IO](src, List.empty, Option(targetAir), AirBackend, bc, false)
|
||||
.unsafeRunSync()
|
||||
.leftMap { err =>
|
||||
println(err)
|
||||
err
|
||||
}
|
||||
.isValid should be(true)
|
||||
val targetAirFileFirst = targetAir.resolve("test.first.air")
|
||||
val targetAirFileSecond = targetAir.resolve("test.second.air")
|
||||
val targetAirFileThird = targetAir.resolve("test.third.air")
|
||||
Files[IO].exists(targetAirFileFirst).unsafeRunSync() should be(true)
|
||||
Files[IO].exists(targetAirFileSecond).unsafeRunSync() should be(true)
|
||||
Files[IO].exists(targetAirFileThird).unsafeRunSync() should be(true)
|
||||
|
||||
Seq(targetAirFileFirst, targetAirFileSecond, targetAirFileThird).map(f =>
|
||||
Files[IO].deleteIfExists(f).unsafeRunSync()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import "random/import/import.aqua"
|
||||
|
||||
func indexCall():
|
||||
Println.print("it is true")
|
@ -1,5 +0,0 @@
|
||||
service Println("println-service-id"):
|
||||
print: string -> ()
|
||||
|
||||
func print(str: string):
|
||||
Println.print(str)
|
@ -1,5 +0,0 @@
|
||||
service Println("println-service-id"):
|
||||
print: string -> ()
|
||||
|
||||
func print(str: string):
|
||||
Println.print(str)
|
@ -1,4 +0,0 @@
|
||||
import "imports/import.aqua"
|
||||
|
||||
func indexCall():
|
||||
Println.print("it is true")
|
@ -1,5 +0,0 @@
|
||||
service Println("println-service-id"):
|
||||
print: string -> ()
|
||||
|
||||
func print(str: string):
|
||||
Println.print(str)
|
@ -1,4 +0,0 @@
|
||||
import "imports/import.aqua"
|
||||
|
||||
func indexCall():
|
||||
Println.print("it is true")
|
@ -1,32 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name":"aqua.AquaCli",
|
||||
"methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Boolean",
|
||||
"methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.InternalError",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.String",
|
||||
"methods":[
|
||||
{"name":"lastIndexOf","parameterTypes":["int"] },
|
||||
{"name":"substring","parameterTypes":["int"] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.System",
|
||||
"methods":[
|
||||
{"name":"getProperty","parameterTypes":["java.lang.String"] },
|
||||
{"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"java.util.Arrays",
|
||||
"methods":[{"name":"asList","parameterTypes":["java.lang.Object[]"] }]
|
||||
}
|
||||
]
|
@ -1,5 +0,0 @@
|
||||
[
|
||||
{
|
||||
"interfaces":["sun.misc.SignalHandler"]
|
||||
}
|
||||
]
|
@ -1,380 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name":"aqua.AppOpts$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.AquaCli$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.backend.Version$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.io.Prelude$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.logging.LogLevels$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.AquaContext",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.AquaContext$Cache",
|
||||
"fields":[{"name":"0bitmap$2"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.ArgsCall",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.CallServiceModel",
|
||||
"fields":[{"name":"0bitmap$14"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.DetachModel$",
|
||||
"fields":[{"name":"0bitmap$4"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.EmptyModel$",
|
||||
"fields":[{"name":"0bitmap$19"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.FuncArrow",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.OnModel",
|
||||
"fields":[{"name":"0bitmap$6"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.OpModel$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.ParModel$",
|
||||
"fields":[{"name":"0bitmap$3"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.SeqModel$",
|
||||
"fields":[{"name":"0bitmap$2"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.VarModel",
|
||||
"fields":[{"name":"0bitmap$2"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.XorModel$",
|
||||
"fields":[{"name":"0bitmap$5"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.inline.state.InliningState$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.transform.cursor.ChainCursor",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.transform.topology.OpModelTreeCursor",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.model.transform.topology.Topology",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.parser.Expr",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.parser.Expr$$anon$1",
|
||||
"fields":[{"name":"0bitmap$2"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.parser.Expr$AndIndented",
|
||||
"fields":[{"name":"0bitmap$3"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.parser.Parser$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.parser.expr.RootExpr$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.parser.lift.Span$Focus",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.RawContext",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.AssignmentTag",
|
||||
"fields":[{"name":"0bitmap$16"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.CallArrowRawTag",
|
||||
"fields":[{"name":"0bitmap$14"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.EmptyTag$",
|
||||
"fields":[{"name":"0bitmap$19"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.OnTag",
|
||||
"fields":[{"name":"0bitmap$9"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.ParTag$",
|
||||
"fields":[{"name":"0bitmap$5"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.ParTag$Detach$",
|
||||
"fields":[{"name":"0bitmap$4"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.RawTag$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.ReturnTag",
|
||||
"fields":[{"name":"0bitmap$18"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.SeqTag$",
|
||||
"fields":[{"name":"0bitmap$3"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.XorTag$",
|
||||
"fields":[{"name":"0bitmap$7"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.raw.ops.XorTag$LeftBiased$",
|
||||
"fields":[{"name":"0bitmap$6"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.res.CallServiceRes",
|
||||
"fields":[{"name":"0bitmap$9"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.res.ParRes$",
|
||||
"fields":[{"name":"0bitmap$3"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.res.SeqRes$",
|
||||
"fields":[{"name":"0bitmap$2"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.res.XorRes$",
|
||||
"fields":[{"name":"0bitmap$4"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.semantics.CompilerState",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.types.ArrowType",
|
||||
"fields":[{"name":"0bitmap$4"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.types.LabeledConsType",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.types.NilType$",
|
||||
"fields":[{"name":"0bitmap$3"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.types.Type$",
|
||||
"fields":[{"name":"0bitmap$5"}]
|
||||
},
|
||||
{
|
||||
"name":"aqua.types.UnlabeledConsType",
|
||||
"fields":[{"name":"0bitmap$2"}]
|
||||
},
|
||||
{
|
||||
"name":"cats.Later",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"cats.effect.unsafe.IORuntimeCompanionPlatform",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"cats.effect.unsafe.metrics.ComputePoolSampler",
|
||||
"queryAllPublicConstructors":true
|
||||
},
|
||||
{
|
||||
"name":"cats.effect.unsafe.metrics.LiveFiberSnapshotTrigger",
|
||||
"queryAllPublicConstructors":true
|
||||
},
|
||||
{
|
||||
"name":"cats.effect.unsafe.metrics.LocalQueueSampler",
|
||||
"queryAllPublicConstructors":true
|
||||
},
|
||||
{
|
||||
"name":"cats.parse.Parser$State",
|
||||
"fields":[{"name":"0bitmap$2"}]
|
||||
},
|
||||
{
|
||||
"name":"cats.parse.Parser0",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"fs2.Chunk$",
|
||||
"fields":[{"name":"0bitmap$2"}]
|
||||
},
|
||||
{
|
||||
"name":"fs2.Chunk$Queue",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"fs2.internal.ScopedResource$$anon$1",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"fs2.io.file.Files$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"fs2.io.file.Path$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"io.circe.Encoder$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Boolean",
|
||||
"fields":[{"name":"TYPE"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Byte",
|
||||
"fields":[{"name":"TYPE"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Character",
|
||||
"fields":[{"name":"TYPE"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.ClassValue"
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Deprecated",
|
||||
"queryAllPublicMethods":true
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Double",
|
||||
"fields":[{"name":"TYPE"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Float",
|
||||
"fields":[{"name":"TYPE"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Integer",
|
||||
"fields":[{"name":"TYPE"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Long",
|
||||
"fields":[{"name":"TYPE"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Short",
|
||||
"fields":[{"name":"TYPE"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.StackTraceElement",
|
||||
"queryAllPublicMethods":true
|
||||
},
|
||||
{
|
||||
"name":"java.lang.String"
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Void",
|
||||
"fields":[{"name":"TYPE"}]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.invoke.VarHandle",
|
||||
"methods":[{"name":"releaseFence","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"java.math.BigDecimal"
|
||||
},
|
||||
{
|
||||
"name":"java.math.BigInteger"
|
||||
},
|
||||
{
|
||||
"name":"java.util.Date"
|
||||
},
|
||||
{
|
||||
"name":"java.util.PropertyPermission",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"scodec.bits.ByteVector",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.Logger",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.Logger$",
|
||||
"fields":[{"name":"0bitmap$2"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.Platform$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.Priority$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.format.FormatBlock$Date$Standard$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.format.Formatter$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.format.package$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.message.LazyMessage",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.modify.LevelFilter$",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.output.Color$Cyan$",
|
||||
"fields":[{"name":"0bitmap$7"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.output.Color$Green$",
|
||||
"fields":[{"name":"0bitmap$8"}]
|
||||
},
|
||||
{
|
||||
"name":"scribe.record.SimpleLogRecord",
|
||||
"fields":[{"name":"0bitmap$1"}]
|
||||
},
|
||||
{
|
||||
"name":"sun.misc.SignalHandler"
|
||||
},
|
||||
{
|
||||
"name":"sun.misc.Unsafe",
|
||||
"allDeclaredFields":true
|
||||
}
|
||||
]
|
@ -1,199 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.backend.Version
|
||||
import aqua.model.LiteralModel
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.parser.expr.ConstantExpr
|
||||
import aqua.parser.lift.LiftParser
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.raw.value.LiteralRaw
|
||||
import aqua.constants.Constants
|
||||
import cats.data.Validated.{Invalid, Valid}
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNec, ValidatedNel}
|
||||
import cats.data.Validated.{invalidNel, validNel}
|
||||
import cats.effect.kernel.Async
|
||||
import cats.effect.std.Console
|
||||
import cats.effect.{ExitCode, IO}
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{~>, Comonad, Functor, Monad}
|
||||
import com.monovore.decline.Opts.help
|
||||
import com.monovore.decline.{Argument, Opts, Visibility}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Level
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
object AppOpts {
|
||||
|
||||
val helpOpt: Opts[Unit] =
|
||||
Opts.flag("help", help = "Display this help text", "h", Visibility.Partial).asHelp.as(())
|
||||
|
||||
val versionOpt: Opts[Unit] =
|
||||
Opts.flag("version", help = "Show version", "v", Visibility.Partial)
|
||||
|
||||
def checkOutput[F[_]: Monad: Files](pathStr: String): F[ValidatedNec[String, Option[Path]]] = {
|
||||
val p = Path(pathStr)
|
||||
Files[F]
|
||||
.exists(p)
|
||||
.flatMap { exists =>
|
||||
if (exists)
|
||||
Files[F].isRegularFile(p).map { isFile =>
|
||||
if (isFile) {
|
||||
Validated.invalidNec(s"Output path should be a directory. Current: '$p'")
|
||||
} else
|
||||
Validated.validNec(Option(p))
|
||||
}
|
||||
else
|
||||
Files[F].createDirectories(p).map(_ => Validated.validNec(Option(p)))
|
||||
}
|
||||
}
|
||||
|
||||
def inputOpts[F[_]: Monad: Files]: Opts[F[ValidatedNec[String, Path]]] = {
|
||||
FileOpts.pathOpt(
|
||||
"input",
|
||||
"Path to an aqua file or an input directory that contains your .aqua files",
|
||||
"i",
|
||||
s =>
|
||||
FileOpts
|
||||
.checkDirOrFile(s)
|
||||
)
|
||||
}
|
||||
|
||||
def outputOpts[F[_]: Monad: Files]: Opts[F[ValidatedNec[String, Option[Path]]]] =
|
||||
Opts
|
||||
.option[String](
|
||||
"output",
|
||||
"Path to the output directory. Will be created if it doesn't exists",
|
||||
"o",
|
||||
"path"
|
||||
)
|
||||
.map(s => Option(s))
|
||||
.withDefault(None)
|
||||
.map(_.map(checkOutput[F]).getOrElse(Validated.validNec[String, Option[Path]](None).pure[F]))
|
||||
|
||||
def importOpts[F[_]: Monad: Files]: Opts[F[ValidatedNec[String, List[Path]]]] =
|
||||
Opts
|
||||
.options[String](
|
||||
"import",
|
||||
"Path to the directory to import from. May be used several times",
|
||||
"m",
|
||||
"path"
|
||||
)
|
||||
.orEmpty
|
||||
.map { ps =>
|
||||
val checked: List[F[ValidatedNec[String, Path]]] = ps.map { pStr =>
|
||||
val p = Path(pStr)
|
||||
for {
|
||||
exists <- Files[F].exists(p)
|
||||
isDir <- Files[F].isDirectory(p)
|
||||
} yield {
|
||||
if (exists && isDir) Validated.validNec[String, Path](p)
|
||||
else
|
||||
Validated.invalidNec[String, Path](
|
||||
s"${p.toString}: No such directory"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
checked.sequence.map(_.sequence)
|
||||
}
|
||||
|
||||
def constantOpts: Opts[List[ConstantRaw]] =
|
||||
Opts
|
||||
.options[String](
|
||||
"const",
|
||||
"Constant that will be used in the aqua code that you run. Constant name must be upper cased.",
|
||||
"c",
|
||||
"NAME=value"
|
||||
)
|
||||
.mapValidated { strs =>
|
||||
Constants.parse(strs.toList)
|
||||
}
|
||||
.withDefault(List.empty)
|
||||
|
||||
val noAirValidation: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("no-air-validation", "Don't parse and validate AIR after compilation")
|
||||
.map(_ => false)
|
||||
.withDefault(true)
|
||||
|
||||
val compileToAir: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("air", "Generate .air file instead of .ts", "a")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val compileToJs: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("js", "Generate .js file instead of .ts")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val noRelay: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("no-relay", "Do not generate a pass through the relay node")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val noXorWrapper: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("no-xor", "Do not generate a wrapper that catches and displays errors")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val tracing: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("trace", "Generate tace events calls")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val isOldFluenceJs: Opts[Boolean] =
|
||||
Opts
|
||||
.flagOption[String](
|
||||
"old-fluence-js",
|
||||
"Generate TypeScript or JavaScript files for new JS Client"
|
||||
)
|
||||
.mapValidated {
|
||||
case Some(str) =>
|
||||
str.toLowerCase match {
|
||||
case "true" => validNel(true)
|
||||
case "false" => validNel(false)
|
||||
case s => invalidNel(s"'$s' must be 'true' or 'false'")
|
||||
}
|
||||
case None => validNel(true)
|
||||
|
||||
}
|
||||
.withDefault(false)
|
||||
|
||||
val dryOpt: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("dry", "Checks if compilation is succeeded, without output")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val scriptOpt: Opts[Boolean] =
|
||||
Opts
|
||||
.flag(
|
||||
"scheduled",
|
||||
"Generate air code for script storage. Without error handling wrappers, hops on relay and tracing. Will ignore other options"
|
||||
)
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
lazy val versionStr: String =
|
||||
Version.version
|
||||
|
||||
def versionAndExit[F[_]: Console: Functor]: F[ExitCode] = Console[F]
|
||||
.println(versionStr)
|
||||
.as(ExitCode.Success)
|
||||
|
||||
def helpAndExit[F[_]: Console: Functor]: F[ExitCode] = Console[F]
|
||||
.println(help)
|
||||
.as(ExitCode.Success)
|
||||
|
||||
def wrapWithOption[A](opt: Opts[A]): Opts[Option[A]] =
|
||||
opt.map(v => Some(v)).withDefault(None)
|
||||
}
|
@ -1,238 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.backend.Backend
|
||||
import aqua.backend.air.AirBackend
|
||||
import aqua.backend.js.JavaScriptBackend
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.logging.LogFormatter
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import cats.data.*
|
||||
import cats.effect.*
|
||||
import cats.effect.std.Console as ConsoleEff
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{~>, Functor, Id, Monad}
|
||||
import com.monovore.decline
|
||||
import com.monovore.decline.effect.CommandIOApp
|
||||
import com.monovore.decline.effect.CommandIOApp.printHelp
|
||||
import com.monovore.decline.{Command, Help, Opts, PlatformApp}
|
||||
import fs2.io.file.Files
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
object AquaCli extends IOApp with Logging {
|
||||
import AppOpts.*
|
||||
|
||||
sealed trait CompileTarget
|
||||
case object TypescriptTarget extends CompileTarget
|
||||
case object JavaScriptTarget extends CompileTarget
|
||||
case object AirTarget extends CompileTarget
|
||||
|
||||
def targetToBackend(target: CompileTarget, isOldFluenceJs: Boolean): Backend = {
|
||||
val client = if (isOldFluenceJs) "FluencePeer" else Backend.client
|
||||
target match {
|
||||
case TypescriptTarget =>
|
||||
TypeScriptBackend(isOldFluenceJs, client)
|
||||
case JavaScriptTarget =>
|
||||
JavaScriptBackend(isOldFluenceJs, client)
|
||||
case AirTarget =>
|
||||
AirBackend
|
||||
}
|
||||
}
|
||||
|
||||
def printErrorsOr[F[_]: Async: ConsoleEff, T](
|
||||
result: ValidatedNec[String, T],
|
||||
or: T => F[ExitCode]
|
||||
): F[ExitCode] = {
|
||||
result match {
|
||||
case Validated.Invalid(errs) =>
|
||||
printCommandErrors[F](errs).as {
|
||||
ExitCode.Error
|
||||
}
|
||||
case Validated.Valid(results) =>
|
||||
or(results)
|
||||
}
|
||||
}
|
||||
|
||||
def printCommandErrors[F[_]: ConsoleEff: Async](errs: NonEmptyChain[String]): F[Unit] = {
|
||||
errs.map(OutputPrinter.errorF _).sequence.flatMap { _ =>
|
||||
OutputPrinter.errorF("\nTry 'aqua --help' for usage instructions")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def main[F[_]: Files: ConsoleEff: Async](runtime: unsafe.IORuntime): Opts[F[ExitCode]] = {
|
||||
implicit val r = runtime
|
||||
implicit val aio: AquaIO[F] = new AquaFilesIO[F]
|
||||
implicit val ec = r.compute
|
||||
|
||||
PlatformOpts.opts.map { res =>
|
||||
res.flatMap {
|
||||
case Validated.Invalid(errs) =>
|
||||
printCommandErrors[F](errs).as {
|
||||
ExitCode.Error
|
||||
}
|
||||
case Validated.Valid(_) =>
|
||||
ExitCode.Success.pure[F]
|
||||
}
|
||||
} orElse versionOpt
|
||||
.as(
|
||||
versionAndExit
|
||||
) orElse helpOpt.as(
|
||||
helpAndExit
|
||||
) orElse (
|
||||
inputOpts[F],
|
||||
importOpts[F],
|
||||
outputOpts[F],
|
||||
compileToAir,
|
||||
compileToJs,
|
||||
noRelay,
|
||||
noXorWrapper, // TODO: Remove
|
||||
tracing,
|
||||
isOldFluenceJs,
|
||||
wrapWithOption(helpOpt),
|
||||
wrapWithOption(versionOpt),
|
||||
FluenceOpts.logLevelOpt,
|
||||
constantOpts,
|
||||
dryOpt,
|
||||
scriptOpt,
|
||||
noAirValidation
|
||||
).mapN {
|
||||
case (
|
||||
inputF,
|
||||
importsF,
|
||||
outputF,
|
||||
toAirOp,
|
||||
toJs,
|
||||
noRelayOp,
|
||||
noXorOp,
|
||||
tracingOp,
|
||||
isOldFluenceJsOp,
|
||||
h,
|
||||
v,
|
||||
logLevel,
|
||||
constants,
|
||||
isDryRun,
|
||||
isScheduled,
|
||||
disableAirValidation
|
||||
) =>
|
||||
val toAir = toAirOp || isScheduled
|
||||
val noXor = noXorOp || isScheduled
|
||||
val noRelay = noRelayOp || isScheduled
|
||||
val tracingEnabled = tracingOp && !isScheduled
|
||||
|
||||
// if there is `--help` or `--version` flag - show help and version
|
||||
// otherwise continue program execution
|
||||
h.map(_ => helpAndExit) orElse v.map(_ => versionAndExit) getOrElse {
|
||||
val target =
|
||||
if (toAir) AirTarget
|
||||
else if (toJs) JavaScriptTarget
|
||||
else TypescriptTarget
|
||||
val bc = {
|
||||
val bc = TransformConfig(
|
||||
constants = constants,
|
||||
tracing = Option.when(tracingEnabled)(TransformConfig.TracingConfig.default)
|
||||
)
|
||||
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
|
||||
}
|
||||
LogFormatter.initLogger(Some(logLevel.compiler))
|
||||
logger.info(s"Aqua Compiler $versionStr")
|
||||
|
||||
(inputF, outputF, importsF).mapN { (i, o, imp) =>
|
||||
i.andThen { input =>
|
||||
o.andThen { output =>
|
||||
imp.map { imports =>
|
||||
if (output.isEmpty && !isDryRun)
|
||||
Validated
|
||||
.invalidNec(
|
||||
"Output path should be specified ('--output' or '-o'). " +
|
||||
"Or use --dry if you want to check that the code compiles"
|
||||
)
|
||||
.pure[F]
|
||||
else {
|
||||
val resultOutput = if (isDryRun) {
|
||||
None
|
||||
} else {
|
||||
output
|
||||
}
|
||||
AquaPathCompiler
|
||||
.compileFilesTo[F](
|
||||
input,
|
||||
imports,
|
||||
resultOutput,
|
||||
targetToBackend(target, isOldFluenceJsOp),
|
||||
bc,
|
||||
disableAirValidation
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.flatMap { results =>
|
||||
printErrorsOr[F, F[ValidatedNec[String, Chain[String]]]](
|
||||
results,
|
||||
{ res =>
|
||||
res.flatMap {
|
||||
case Validated.Invalid(errs) =>
|
||||
printCommandErrors[F](errs).as {
|
||||
ExitCode.Error
|
||||
}
|
||||
case Validated.Valid(results) =>
|
||||
results.map(OutputPrinter.print _)
|
||||
ExitCode.Success.pure[F]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handleCommand(args: List[String]): IO[ExitCode] = {
|
||||
val command = Command("aqua", "Aqua Compiler", false)(main[IO](runtime))
|
||||
for {
|
||||
parseResult: Either[Help, IO[ExitCode]] <- Sync[IO].delay(
|
||||
// ambientArgs returns arguments for scala.js under node.js
|
||||
command.parse(PlatformApp.ambientArgs getOrElse args, sys.env)
|
||||
)
|
||||
exitCode <- parseResult.fold(
|
||||
{ h =>
|
||||
NonEmptyChain
|
||||
.fromSeq(h.errors)
|
||||
.map { errs =>
|
||||
printCommandErrors[IO](errs).as {
|
||||
ExitCode.Error
|
||||
}
|
||||
}
|
||||
.getOrElse {
|
||||
ConsoleEff[IO].print(h).map { _ =>
|
||||
// hack to show last string in `help`
|
||||
println()
|
||||
ExitCode.Success
|
||||
}
|
||||
}
|
||||
},
|
||||
identity
|
||||
)
|
||||
} yield exitCode
|
||||
}
|
||||
|
||||
override def run(args: List[String]): IO[ExitCode] = {
|
||||
handleCommand(
|
||||
args match {
|
||||
// Weird ugly hack: in case version flag or help flag is present, ignore other options,
|
||||
// be it correct or not
|
||||
case _ if args.contains("-v") || args.contains("--version") => "-v" :: Nil
|
||||
case _ if args.contains("-h") || args.contains("--help") => "-h" :: Nil
|
||||
case _ => args
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.backend.{Backend, Generated}
|
||||
import aqua.compiler.{AirValidator, AquaCompiled, AquaCompiler, AquaCompilerConf, AquaError, CompilerAPI}
|
||||
import aqua.files.{AquaFileSources, FileModuleId}
|
||||
import aqua.io.Prelude
|
||||
import aqua.io.*
|
||||
import aqua.air.AirValidation
|
||||
import aqua.backend.AirFunction
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.model.transform.Transform
|
||||
import aqua.parser.lift.LiftParser.LiftErrorOps
|
||||
import aqua.parser.lift.Span.spanLiftParser
|
||||
import aqua.parser.lift.{FileSpan, LiftParser, Span}
|
||||
import aqua.parser.{Ast, LexerError, Parser}
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.res.AquaRes
|
||||
import cats.data.*
|
||||
import cats.effect.Async
|
||||
import cats.parse.LocationMap
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.show.*
|
||||
import cats.{Applicative, Eval, Monad, Show, ~>}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
import cats.data.Validated.validNec
|
||||
|
||||
object AquaPathCompiler extends Logging {
|
||||
|
||||
/**
|
||||
* @param srcPath path to aqua sources
|
||||
* @param imports additional paths to possible aqua imports
|
||||
* @param targetPath path where compiled files will be created. Creates no output if empty
|
||||
* @param backend creates output (TS, JS, ...) from a model
|
||||
* @param transformConfig transformation configuration for a model
|
||||
* @return errors or result messages
|
||||
*/
|
||||
def compileFilesTo[F[_]: AquaIO: Monad: Files: Async](
|
||||
srcPath: Path,
|
||||
imports: List[Path],
|
||||
targetPath: Option[Path],
|
||||
backend: Backend,
|
||||
transformConfig: TransformConfig,
|
||||
disableAirValidation: Boolean
|
||||
): F[ValidatedNec[String, Chain[String]]] = {
|
||||
import ErrorRendering.showError
|
||||
(for {
|
||||
prelude <- Prelude.init()
|
||||
sources = new AquaFileSources[F](srcPath, imports ++ prelude.importPaths)
|
||||
validator =
|
||||
if (disableAirValidation) {
|
||||
new AirValidator[F] {
|
||||
override def init(): F[Unit] = Applicative[F].pure(())
|
||||
override def validate(airs: List[AirFunction]): F[ValidatedNec[String, Unit]] =
|
||||
Applicative[F].pure(validNec(()))
|
||||
}
|
||||
} else {
|
||||
new AirValidator[F] {
|
||||
override def init(): F[Unit] = AirValidation.init[F]()
|
||||
override def validate(
|
||||
airs: List[AirFunction]
|
||||
): F[ValidatedNec[String, Unit]] = AirValidation.validate[F](airs)
|
||||
}
|
||||
}
|
||||
compiler <- CompilerAPI
|
||||
.compileTo[F, AquaFileError, FileModuleId, FileSpan.F, String](
|
||||
sources,
|
||||
SpanParser.parser,
|
||||
validator,
|
||||
new Backend.Transform:
|
||||
override def transform(ex: AquaContext): AquaRes =
|
||||
Transform.contextRes(ex, transformConfig)
|
||||
|
||||
override def generate(aqua: AquaRes): Seq[Generated] = backend.generate(aqua)
|
||||
,
|
||||
AquaCompilerConf(transformConfig.constantsList),
|
||||
targetPath.map(sources.write).getOrElse(dry[F])
|
||||
)
|
||||
} yield {
|
||||
compiler
|
||||
// 'distinct' to delete all duplicated errors
|
||||
}).map(_.leftMap(_.map(_.show).distinct))
|
||||
|
||||
}
|
||||
|
||||
def dry[F[_]: Applicative](
|
||||
ac: AquaCompiled[FileModuleId]
|
||||
): F[Seq[Validated[AquaFileError, String]]] =
|
||||
Seq(
|
||||
Validated.valid[AquaFileError, String](
|
||||
s"Source ${ac.sourceId.file}: compilation OK"
|
||||
)
|
||||
).pure[F]
|
||||
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import cats.data.Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import cats.data.*
|
||||
import cats.effect.Concurrent
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{~>, Comonad, Functor, Monad}
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
object FileOpts {
|
||||
|
||||
// Validate, read and transform a file
|
||||
def fileOptionalOpt[A, F[_]: Files: Concurrent](
|
||||
long: String,
|
||||
help: String,
|
||||
short: String = "",
|
||||
transform: (Path, String) => ValidatedNec[String, A]
|
||||
): Opts[Option[F[ValidatedNec[String, A]]]] = {
|
||||
AppOpts.wrapWithOption(fileOpt(long, help, short, transform))
|
||||
}
|
||||
|
||||
// Get and validate path
|
||||
def pathOpt[A, F[_]: Files: Monad](
|
||||
long: String,
|
||||
help: String,
|
||||
short: String = "",
|
||||
check: String => F[ValidatedNec[String, Path]]
|
||||
): Opts[F[ValidatedNec[String, Path]]] =
|
||||
Opts
|
||||
.option[String](long, help, short, "path")
|
||||
.map(check)
|
||||
|
||||
def fileOptTransform[A, F[_]: Files: Concurrent](
|
||||
str: String,
|
||||
transform: (Path, String) => ValidatedNec[String, A]
|
||||
): F[ValidatedNec[String, (Path, A)]] = {
|
||||
checkAndTransformPath(
|
||||
str,
|
||||
checkFile,
|
||||
{ p =>
|
||||
Files[F]
|
||||
.readAll(p)
|
||||
.through(fs2.text.utf8.decode)
|
||||
.fold(List.empty[String]) { case (acc, str) => acc :+ str }
|
||||
.map(_.mkString(""))
|
||||
.map(str => transform(p, str).map(r => (p, r)))
|
||||
.compile
|
||||
.last
|
||||
.map(_.getOrElse(invalidNec(s"Path ${p.toString} is empty")))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Validate, read and transform a file
|
||||
def fileOpt[A, F[_]: Files: Concurrent](
|
||||
long: String,
|
||||
help: String,
|
||||
short: String = "",
|
||||
transform: (Path, String) => ValidatedNec[String, A]
|
||||
): Opts[F[ValidatedNec[String, A]]] = {
|
||||
Opts
|
||||
.option[String](long, help, short, "path")
|
||||
.map(str => fileOptTransform(str, transform).map(_.map(_._2)))
|
||||
}
|
||||
|
||||
// Validate, read and transform multiple files
|
||||
def fileOpts[A, F[_]: Files: Concurrent](
|
||||
long: String,
|
||||
help: String,
|
||||
short: String = "",
|
||||
transform: (Path, String) => ValidatedNec[String, A]
|
||||
): Opts[F[ValidatedNec[String, NonEmptyList[(Path, A)]]]] = {
|
||||
Opts
|
||||
.options[String](long, help, short, "path")
|
||||
.map(strs => strs.map(str => fileOptTransform(str, transform)).sequence.map(_.sequence))
|
||||
}
|
||||
|
||||
// Checks if the path is a file and it exists
|
||||
def checkFile[F[_]: Files: Monad](path: String): F[ValidatedNec[String, Path]] = {
|
||||
val p = Path(path)
|
||||
Files[F]
|
||||
.exists(p)
|
||||
.flatMap { exists =>
|
||||
if (exists)
|
||||
Files[F].isRegularFile(p).map { isFile =>
|
||||
if (isFile) {
|
||||
validNec(p)
|
||||
} else {
|
||||
invalidNec(s"Path '${p.toString}' is not a file")
|
||||
}
|
||||
}
|
||||
else {
|
||||
invalidNec(s"There is no path '${p.toString}'").pure[F]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the path is a file or a directory and it exists
|
||||
def checkDirOrFile[F[_]: Files: Monad](
|
||||
path: String,
|
||||
isAqua: Boolean = true
|
||||
): F[ValidatedNec[String, Path]] = {
|
||||
val p = Path(path)
|
||||
Files[F]
|
||||
.exists(p)
|
||||
.flatMap { exists =>
|
||||
if (exists)
|
||||
Files[F].isRegularFile(p).map { isFile =>
|
||||
if (isFile && p.extName != ".aqua")
|
||||
Validated.invalidNec("File must have '.aqua' extension")
|
||||
else Validated.validNec(p)
|
||||
}
|
||||
else {
|
||||
invalidNec(s"'${p.toString}': No such file or directory").pure[F]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks a path and transforms it
|
||||
def checkAndTransformPath[F[_]: Files: Concurrent, T](
|
||||
path: String,
|
||||
check: String => F[ValidatedNec[String, Path]],
|
||||
transform: Path => F[ValidatedNec[String, T]]
|
||||
): F[ValidatedNec[String, T]] = {
|
||||
check(path).flatMap {
|
||||
case Validated.Valid(p) => transform(p)
|
||||
case i @ Validated.Invalid(_) => i.pure[F]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNel}
|
||||
import aqua.logging.LogLevels
|
||||
import com.monovore.decline.Opts
|
||||
import scribe.Level
|
||||
import cats.syntax.traverse.*
|
||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, valid, validNec, validNel}
|
||||
|
||||
import java.util.Base64
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
object FluenceOpts {
|
||||
|
||||
val timeoutOpt: Opts[Duration] =
|
||||
Opts
|
||||
.option[Int]("timeout", "Request timeout in milliseconds", "t")
|
||||
.map(i => Duration(i, TimeUnit.MILLISECONDS))
|
||||
|
||||
val onOpt: Opts[Option[String]] =
|
||||
AppOpts.wrapWithOption(
|
||||
Opts
|
||||
.option[String](
|
||||
"on",
|
||||
"peerId of the peer that will execute the function. Default: host_peer_id",
|
||||
"o",
|
||||
"peerId"
|
||||
)
|
||||
)
|
||||
|
||||
val showConfigOpt: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("show-config", "Print current configuration on start")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val verboseOpt: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("verbose", "Show additional information about the call")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val secretKeyOpt: Opts[Array[Byte]] =
|
||||
Opts
|
||||
.option[String]("sk", "Ed25519 32-byte secret key in base64", "s", "base64")
|
||||
.mapValidated { s =>
|
||||
val decoder = Base64.getDecoder
|
||||
Validated.catchNonFatal {
|
||||
decoder.decode(s)
|
||||
}.leftMap(t => NonEmptyList.one("secret key isn't a valid base64 string: " + t.getMessage))
|
||||
}
|
||||
|
||||
val printAir: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("print-air", "Prints generated AIR code before function execution")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val logLevelOpt: Opts[LogLevels] =
|
||||
Opts.option[String]("log-level", help = s"Set log level. ${LogLevels.logHelpMessage}").mapValidated {
|
||||
str =>
|
||||
LogLevels.fromString(str)
|
||||
}.withDefault(LogLevels())
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
packages:
|
||||
- 'cli/cli-npm'
|
||||
- 'api/api-npm'
|
||||
- 'api/api-example'
|
||||
- 'language-server/language-server-npm'
|
||||
|
Loading…
x
Reference in New Issue
Block a user