mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2024-12-04 18:00:18 +00:00
Fix aqua-to-js, start to split responsibility
This commit is contained in:
parent
f5e9923974
commit
2f316cc8fb
48
packages/@tests/aqua/compile-aqua.ts
Normal file
48
packages/@tests/aqua/compile-aqua.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright 2023 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { writeFile } from "fs/promises";
|
||||
import { join, dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { compileFromPath } from "@fluencelabs/aqua-api";
|
||||
import aquaToJs from "@fluencelabs/aqua-to-js";
|
||||
|
||||
const cr = await compileFromPath({
|
||||
filePath: join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
"_aqua",
|
||||
"smoke_test.aqua",
|
||||
),
|
||||
targetType: "air",
|
||||
imports: [join(dirname(fileURLToPath(import.meta.url)), "node_modules")],
|
||||
});
|
||||
|
||||
const res = await aquaToJs(cr, "ts");
|
||||
|
||||
if (res == null) {
|
||||
throw new Error(cr.errors.join("\n"));
|
||||
}
|
||||
|
||||
await writeFile(
|
||||
join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
"src",
|
||||
"_aqua",
|
||||
"smoke_test.ts",
|
||||
),
|
||||
res.sources,
|
||||
);
|
@ -11,7 +11,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"compile-aqua": "fluence aqua -i ./_aqua -o ./src/_aqua"
|
||||
"compile-aqua": "node --loader ts-node/esm compile-aqua.ts"
|
||||
},
|
||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||
"author": "Fluence Labs",
|
||||
@ -20,10 +20,12 @@
|
||||
"base64-js": "1.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fluencelabs/aqua-api": "0.12.2",
|
||||
"@fluencelabs/aqua-lib": "0.6.0",
|
||||
"@fluencelabs/cli": "0.7.2",
|
||||
"@fluencelabs/js-client": "workspace:^",
|
||||
"@fluencelabs/registry": "0.8.2",
|
||||
"@fluencelabs/trust-graph": "3.1.2"
|
||||
"@fluencelabs/aqua-to-js": "workspace:*",
|
||||
"@fluencelabs/js-client": "workspace:*",
|
||||
"@fluencelabs/registry": "0.8.7",
|
||||
"@fluencelabs/trust-graph": "3.1.2",
|
||||
"ts-node": "10.9.1"
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ArrowWithoutCallbacks, NonArrowType } from "@fluencelabs/interfaces";
|
||||
import { ArrowType, NonArrowType } from "@fluencelabs/interfaces";
|
||||
import { match, P } from "ts-pattern";
|
||||
|
||||
import { getFuncArgs } from "./utils.js";
|
||||
|
||||
export function genTypeName(
|
||||
t: NonArrowType | ArrowWithoutCallbacks,
|
||||
t: NonArrowType | ArrowType,
|
||||
name: string,
|
||||
): readonly [string | undefined, string] {
|
||||
const genType = typeToTs(t);
|
||||
@ -46,7 +46,7 @@ export function genTypeName(
|
||||
});
|
||||
}
|
||||
|
||||
export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks): string {
|
||||
export function typeToTs(t: NonArrowType | ArrowType): string {
|
||||
return match(t)
|
||||
.with({ tag: "nil" }, () => {
|
||||
return "null";
|
||||
@ -120,16 +120,19 @@ export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks): string {
|
||||
return [name, typeToTs(type)];
|
||||
});
|
||||
|
||||
const generic =
|
||||
args.length === 0
|
||||
? "null"
|
||||
: args
|
||||
.map(([name]) => {
|
||||
return `'${name}'`;
|
||||
})
|
||||
.join(" | ");
|
||||
// JS-client argument
|
||||
if (domain.tag !== "unlabeledProduct") {
|
||||
const generic =
|
||||
args.length === 0
|
||||
? "null"
|
||||
: args
|
||||
.map(([name]) => {
|
||||
return `'${name}'`;
|
||||
})
|
||||
.join(" | ");
|
||||
|
||||
args.push(["callParams", `CallParams$$<${generic}>`]);
|
||||
args.push(["callParams", `CallParams$$<${generic}>`]);
|
||||
}
|
||||
|
||||
const funcArgs = args
|
||||
.map(([name, type]) => {
|
||||
|
@ -20,6 +20,8 @@ import { genTypeName, typeToTs } from "../common.js";
|
||||
import { CLIENT } from "../constants.js";
|
||||
import { capitalize, getFuncArgs } from "../utils.js";
|
||||
|
||||
import { DefaultServiceId } from "./service.js";
|
||||
|
||||
export interface TypeGenerator {
|
||||
type(field: string, type: string): string;
|
||||
generic(field: string, type: string): string;
|
||||
@ -117,12 +119,19 @@ export class TSTypeGenerator implements TypeGenerator {
|
||||
const serviceDecl = `service: ${srvName}Def`;
|
||||
const serviceIdDecl = `serviceId: string`;
|
||||
|
||||
const registerServiceArgs = [
|
||||
[serviceDecl],
|
||||
[serviceIdDecl, serviceDecl],
|
||||
[peerDecl, serviceDecl],
|
||||
[peerDecl, serviceIdDecl, serviceDecl],
|
||||
];
|
||||
const registerServiceArgs =
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
(srvDef.defaultServiceId as DefaultServiceId).s_Some__f_value != null
|
||||
? [
|
||||
[serviceDecl],
|
||||
[serviceIdDecl, serviceDecl],
|
||||
[peerDecl, serviceDecl],
|
||||
[peerDecl, serviceIdDecl, serviceDecl],
|
||||
]
|
||||
: [
|
||||
[serviceIdDecl, serviceDecl],
|
||||
[peerDecl, serviceIdDecl, serviceDecl],
|
||||
];
|
||||
|
||||
return [
|
||||
interfaces,
|
||||
|
@ -20,7 +20,7 @@ import { recursiveRenameLaquaProps } from "../utils.js";
|
||||
|
||||
import { TypeGenerator } from "./interfaces.js";
|
||||
|
||||
interface DefaultServiceId {
|
||||
export interface DefaultServiceId {
|
||||
s_Some__f_value?: string;
|
||||
}
|
||||
|
||||
|
120
packages/core/aqua-to-js/src/generate/validators.ts
Normal file
120
packages/core/aqua-to-js/src/generate/validators.ts
Normal file
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright 2023 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ArrowWithoutCallbacks,
|
||||
FunctionCallDef,
|
||||
JSONValue,
|
||||
SimpleTypes,
|
||||
UnlabeledProductType,
|
||||
} from "@fluencelabs/interfaces";
|
||||
|
||||
import { typeToTs } from "../common.js";
|
||||
|
||||
export function validateFunctionCall(
|
||||
schema: FunctionCallDef,
|
||||
...args: JSONValue[]
|
||||
) {
|
||||
const schemaArgs =
|
||||
schema.arrow.domain.tag === "nil"
|
||||
? []
|
||||
: Object.values(schema.arrow.domain.fields);
|
||||
|
||||
if (args.length !== schemaArgs.length) {
|
||||
throw new Error(
|
||||
`Expected ${schemaArgs.length} arguments but provided ${args.length}`,
|
||||
);
|
||||
}
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
validateFunctionCallArg(schemaArgs[i], args[i], i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateFunctionCallArg(
|
||||
schema: SimpleTypes | UnlabeledProductType | ArrowWithoutCallbacks,
|
||||
arg: JSONValue,
|
||||
argIndex: number,
|
||||
) {
|
||||
if (!isTypeMatchesSchema(schema, arg)) {
|
||||
const expectedType = typeToTs(schema);
|
||||
throw new Error(
|
||||
`Argument ${argIndex} doesn't match schema. Expected type: ${expectedType}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function isTypeMatchesSchema(
|
||||
schema: SimpleTypes | UnlabeledProductType | ArrowWithoutCallbacks,
|
||||
arg: JSONValue,
|
||||
): boolean {
|
||||
if (schema.tag === "nil") {
|
||||
return arg === null;
|
||||
} else if (schema.tag === "option") {
|
||||
return arg === null || isTypeMatchesSchema(schema.type, arg);
|
||||
} else if (schema.tag === "scalar") {
|
||||
if (
|
||||
[
|
||||
"u8",
|
||||
"u16",
|
||||
"u32",
|
||||
"u64",
|
||||
"i8",
|
||||
"i16",
|
||||
"i32",
|
||||
"i64",
|
||||
"f32",
|
||||
"f64",
|
||||
].includes(schema.name)
|
||||
) {
|
||||
return typeof arg === "number";
|
||||
} else if (schema.name === "bool") {
|
||||
return typeof arg === "boolean";
|
||||
} else if (schema.name === "string") {
|
||||
return typeof arg === "string";
|
||||
} else {
|
||||
// Should not be possible
|
||||
return false;
|
||||
}
|
||||
} else if (schema.tag === "array") {
|
||||
return (
|
||||
Array.isArray(arg) &&
|
||||
arg.every((item) => {
|
||||
return isTypeMatchesSchema(schema.type, item);
|
||||
})
|
||||
);
|
||||
} else if (schema.tag === "struct") {
|
||||
return (
|
||||
!Array.isArray(arg) &&
|
||||
typeof arg === "object" &&
|
||||
arg !== null &&
|
||||
Object.entries(schema.fields).every(([field, type]) => {
|
||||
return isTypeMatchesSchema(type, arg[field]);
|
||||
})
|
||||
);
|
||||
} else if (schema.tag === "unlabeledProduct") {
|
||||
return (
|
||||
Array.isArray(arg) &&
|
||||
arg.every((item, index) => {
|
||||
return isTypeMatchesSchema(schema.items[index], item);
|
||||
})
|
||||
);
|
||||
} else if (schema.tag === "arrow") {
|
||||
return typeof arg === "function";
|
||||
} else {
|
||||
return schema.tag === "topType";
|
||||
}
|
||||
}
|
@ -154,7 +154,13 @@ export type ProductType = UnlabeledProductType | LabeledProductType;
|
||||
* ArrowType is a profunctor pointing its domain to codomain.
|
||||
* Profunctor means variance: Arrow is contravariant on domain, and variant on codomain.
|
||||
*/
|
||||
export type ArrowType<T extends LabeledProductType | UnlabeledProductType> = {
|
||||
export type ArrowType<
|
||||
T extends
|
||||
| LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||
| UnlabeledProductType =
|
||||
| LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||
| UnlabeledProductType,
|
||||
> = {
|
||||
/**
|
||||
* Type descriptor. Used for pattern-matching
|
||||
*/
|
||||
@ -174,14 +180,14 @@ export type ArrowType<T extends LabeledProductType | UnlabeledProductType> = {
|
||||
/**
|
||||
* Arrow which domain contains only non-arrow types
|
||||
*/
|
||||
export type ArrowWithoutCallbacks = ArrowType<
|
||||
UnlabeledProductType | LabeledProductType<SimpleTypes>
|
||||
>;
|
||||
export type ArrowWithoutCallbacks = ArrowType<UnlabeledProductType>;
|
||||
|
||||
/**
|
||||
* Arrow which domain does can contain both non-arrow types and arrows (which themselves cannot contain arrows)
|
||||
*/
|
||||
export type ArrowWithCallbacks = ArrowType<LabeledProductType>;
|
||||
export type ArrowWithCallbacks = ArrowType<
|
||||
LabeledProductType<SimpleTypes | ArrowWithoutCallbacks>
|
||||
>;
|
||||
|
||||
export interface FunctionCallConstants {
|
||||
/**
|
||||
@ -232,9 +238,7 @@ export interface FunctionCallDef {
|
||||
/**
|
||||
* Underlying arrow which represents function in aqua
|
||||
*/
|
||||
arrow: ArrowType<
|
||||
LabeledProductType<SimpleTypes | ArrowType<UnlabeledProductType>>
|
||||
>;
|
||||
arrow: ArrowWithCallbacks;
|
||||
|
||||
/**
|
||||
* Names of the different entities used in generated air script
|
||||
|
@ -25,7 +25,9 @@ import type {
|
||||
ArrowWithoutCallbacks,
|
||||
JSONArray,
|
||||
JSONValue,
|
||||
LabeledProductType,
|
||||
NonArrowType,
|
||||
SimpleTypes,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { match } from "ts-pattern";
|
||||
|
||||
@ -95,7 +97,7 @@ export const aqua2ts = (value: JSONValue, type: NonArrowType): JSONValue => {
|
||||
*/
|
||||
export const aquaArgs2Ts = (
|
||||
req: CallServiceData,
|
||||
arrow: ArrowWithoutCallbacks,
|
||||
arrow: ArrowType<LabeledProductType<SimpleTypes>>,
|
||||
): JSONArray => {
|
||||
const argTypes = match(arrow.domain)
|
||||
.with({ tag: "labeledProduct" }, (x) => {
|
||||
@ -187,6 +189,7 @@ export const returnType2Aqua = (
|
||||
returnValue: any,
|
||||
arrowType: ArrowType<NonArrowType>,
|
||||
) => {
|
||||
// TODO: cover with tests
|
||||
if (arrowType.codomain.tag === "nil") {
|
||||
return {};
|
||||
}
|
||||
|
28
packages/core/js-client/src/compilerSupport/types.ts
Normal file
28
packages/core/js-client/src/compilerSupport/types.ts
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright 2023 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { JSONArray, JSONValue } from "@fluencelabs/interfaces";
|
||||
|
||||
import { ParticleContext } from "../jsServiceHost/interfaces.js";
|
||||
|
||||
export type MaybePromise<T> = T | Promise<T>;
|
||||
|
||||
export type ServiceImpl = Record<
|
||||
string,
|
||||
(
|
||||
...args: [...JSONArray, ParticleContext]
|
||||
) => MaybePromise<JSONValue | undefined>
|
||||
>;
|
6697
pnpm-lock.yaml
generated
6697
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,5 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["**/src/**/*"]
|
||||
"include": ["packages"],
|
||||
"exclude": ["node_modules", "dist", "build"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user