mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-06-27 23:01:33 +00:00
feat(js-client)!: Segregation of responsibility between js-client packages [fixes DXJ-525] (#378)
Schema validation in js-client
This commit is contained in:
@ -1,3 +1,3 @@
|
||||
{
|
||||
"ignorePatterns": ["src/**/__snapshots__/**/*"]
|
||||
"ignorePatterns": ["src/**/__snapshots__/**/*", "src/**/*.js"]
|
||||
}
|
||||
|
@ -18,12 +18,14 @@
|
||||
"ts-pattern": "5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fluencelabs/aqua-api": "0.12.0",
|
||||
"@fluencelabs/aqua-api": "0.12.4-main-cee4448-2196-1",
|
||||
"@fluencelabs/aqua-lib": "0.7.3",
|
||||
"@fluencelabs/interfaces": "workspace:*",
|
||||
"@fluencelabs/js-client": "workspace:^",
|
||||
"@fluencelabs/registry": "0.8.7",
|
||||
"@fluencelabs/spell": "0.5.20",
|
||||
"@fluencelabs/trust-graph": "0.4.7",
|
||||
"vitest": "0.34.6"
|
||||
"vitest": "0.34.6",
|
||||
"zod": "3.22.4"
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ArrowWithoutCallbacks, NonArrowType } from "@fluencelabs/interfaces";
|
||||
import { ArrowType, NonArrowType } from "@fluencelabs/interfaces";
|
||||
import { match, P } from "ts-pattern";
|
||||
|
||||
import { getFuncArgs } from "./utils.js";
|
||||
|
||||
export function genTypeName(
|
||||
t: NonArrowType | ArrowWithoutCallbacks,
|
||||
t: NonArrowType | ArrowType,
|
||||
name: string,
|
||||
): readonly [string | undefined, string] {
|
||||
const genType = typeToTs(t);
|
||||
@ -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,7 @@ export function typeToTs(t: NonArrowType | ArrowWithoutCallbacks): string {
|
||||
return [name, typeToTs(type)];
|
||||
});
|
||||
|
||||
const generic =
|
||||
args.length === 0
|
||||
? "null"
|
||||
: args
|
||||
.map(([name]) => {
|
||||
return `'${name}'`;
|
||||
})
|
||||
.join(" | ");
|
||||
|
||||
args.push(["callParams", `CallParams$$<${generic}>`]);
|
||||
args.push(["callParams", `ParticleContext$$`]);
|
||||
|
||||
const funcArgs = args
|
||||
.map(([name, type]) => {
|
||||
|
20
packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.d.ts
vendored
Normal file
20
packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.d.ts
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
/**
|
||||
*
|
||||
* This file is generated using:
|
||||
* @fluencelabs/aqua-api version: 0.0.0
|
||||
* @fluencelabs/aqua-to-js version: 0.0.0
|
||||
* If you find any bugs in generated AIR, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
||||
* If you find any bugs in generated JS/TS, please write an issue on GitHub: https://github.com/fluencelabs/js-client/issues
|
||||
*
|
||||
*/
|
||||
import type { IFluenceClient as IFluenceClient$$, ParticleContext as ParticleContext$$ } from '@fluencelabs/js-client';
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$
|
||||
} from '@fluencelabs/js-client';
|
||||
|
||||
|
@ -0,0 +1,20 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
/**
|
||||
*
|
||||
* This file is generated using:
|
||||
* @fluencelabs/aqua-api version: 0.0.0
|
||||
* @fluencelabs/aqua-to-js version: 0.0.0
|
||||
* If you find any bugs in generated AIR, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
||||
* If you find any bugs in generated JS/TS, please write an issue on GitHub: https://github.com/fluencelabs/js-client/issues
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$
|
||||
} from '@fluencelabs/js-client';
|
||||
|
||||
|
@ -0,0 +1,20 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
/**
|
||||
*
|
||||
* This file is generated using:
|
||||
* @fluencelabs/aqua-api version: 0.0.0
|
||||
* @fluencelabs/aqua-to-js version: 0.0.0
|
||||
* If you find any bugs in generated AIR, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
||||
* If you find any bugs in generated JS/TS, please write an issue on GitHub: https://github.com/fluencelabs/js-client/issues
|
||||
*
|
||||
*/
|
||||
import type { IFluenceClient as IFluenceClient$$, ParticleContext as ParticleContext$$ } from '@fluencelabs/js-client';
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$
|
||||
} from '@fluencelabs/js-client';
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,46 +14,55 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import url from "url";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { compileFromPath } from "@fluencelabs/aqua-api";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
|
||||
import { getPackageJsonContent, PackageJson } from "../../utils.js";
|
||||
import { generateTypes, generateSources } from "../index.js";
|
||||
import { CompilationResult } from "../interfaces.js";
|
||||
|
||||
let res: Omit<CompilationResult, "funcCall">;
|
||||
let pkg: PackageJson;
|
||||
|
||||
describe("Aqua to js/ts compiler", () => {
|
||||
it("compiles smoke tests successfully", async () => {
|
||||
const res = await compileFromPath({
|
||||
filePath: url.fileURLToPath(
|
||||
beforeAll(async () => {
|
||||
res = await compileFromPath({
|
||||
filePath: fileURLToPath(
|
||||
new URL("./sources/smoke_test.aqua", import.meta.url),
|
||||
),
|
||||
imports: ["./node_modules"],
|
||||
targetType: "air",
|
||||
});
|
||||
|
||||
const pkg: PackageJson = {
|
||||
pkg = {
|
||||
...(await getPackageJsonContent()),
|
||||
version: "0.0.0",
|
||||
devDependencies: {
|
||||
"@fluencelabs/aqua-api": "0.0.0",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// TODO: see https://github.com/fluencelabs/js-client/pull/366#discussion_r1370567711
|
||||
// @ts-expect-error don't use compileFromPath directly here
|
||||
it("matches js snapshots", async () => {
|
||||
const jsResult = generateSources(res, "js", pkg);
|
||||
// TODO: see https://github.com/fluencelabs/js-client/pull/366#discussion_r1370567711
|
||||
// @ts-expect-error don't use compileFromPath directly here
|
||||
const jsTypes = generateTypes(res, pkg);
|
||||
|
||||
expect(jsResult).toMatchSnapshot();
|
||||
expect(jsTypes).toMatchSnapshot();
|
||||
await expect(jsResult).toMatchFileSnapshot(
|
||||
"./__snapshots__/generate.snap.js",
|
||||
);
|
||||
|
||||
// TODO: see https://github.com/fluencelabs/js-client/pull/366#discussion_r1370567711
|
||||
// @ts-expect-error don't use compileFromPath directly here
|
||||
await expect(jsTypes).toMatchFileSnapshot(
|
||||
"./__snapshots__/generate.snap.d.ts",
|
||||
);
|
||||
});
|
||||
|
||||
it("matches ts snapshots", async () => {
|
||||
const tsResult = generateSources(res, "ts", pkg);
|
||||
|
||||
expect(tsResult).toMatchSnapshot();
|
||||
await expect(tsResult).toMatchFileSnapshot(
|
||||
"./__snapshots__/generate.snap.ts",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { recursiveRenameLaquaProps } from "../utils.js";
|
||||
import { capitalize, recursiveRenameLaquaProps } from "../utils.js";
|
||||
|
||||
import { AquaFunction, TypeGenerator } from "./interfaces.js";
|
||||
|
||||
@ -40,8 +40,11 @@ ${func.script}\`;
|
||||
${typeGenerator.funcType(func)}
|
||||
export function ${func.funcDef.functionName}(${typeGenerator.type(
|
||||
"...args",
|
||||
"any[]",
|
||||
)}) {
|
||||
`${capitalize(func.funcDef.functionName)}Params`,
|
||||
)})${typeGenerator.type(
|
||||
"",
|
||||
`${capitalize(func.funcDef.functionName)}Result`,
|
||||
)} {
|
||||
return callFunction$$(
|
||||
args,
|
||||
${JSON.stringify(recursiveRenameLaquaProps(funcDef), null, 4)},
|
||||
|
@ -35,12 +35,13 @@ export default function generateHeader(
|
||||
*/
|
||||
${
|
||||
outputType === "ts"
|
||||
? "import type { IFluenceClient as IFluenceClient$$, CallParams as CallParams$$ } from '@fluencelabs/js-client';"
|
||||
? "import type { IFluenceClient as IFluenceClient$$, ParticleContext as ParticleContext$$ } from '@fluencelabs/js-client';"
|
||||
: ""
|
||||
}
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$,
|
||||
v5_registerService as registerService$$
|
||||
} from '@fluencelabs/js-client';`;
|
||||
}
|
||||
|
@ -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;
|
||||
@ -54,7 +56,7 @@ export class TSTypeGenerator implements TypeGenerator {
|
||||
args.push([undefined, `config?: {ttl?: number}`]);
|
||||
|
||||
const argsDefs = args.map(([, def]) => {
|
||||
return " " + def;
|
||||
return def;
|
||||
});
|
||||
|
||||
const argsDesc = args
|
||||
@ -66,28 +68,30 @@ export class TSTypeGenerator implements TypeGenerator {
|
||||
});
|
||||
|
||||
const functionOverloads = [
|
||||
argsDefs.join(",\n"),
|
||||
[` peer: ${CLIENT}`, ...argsDefs].join(",\n"),
|
||||
argsDefs.join(", "),
|
||||
[`peer: ${CLIENT}`, ...argsDefs].join(", "),
|
||||
];
|
||||
|
||||
const [resTypeDesc, resType] = genTypeName(
|
||||
funcDef.arrow.codomain,
|
||||
capitalize(funcDef.functionName) + "Result",
|
||||
capitalize(funcDef.functionName) + "ResultType",
|
||||
);
|
||||
|
||||
const functionOverloadArgsType = functionOverloads
|
||||
.map((overload) => {
|
||||
return `[${overload}]`;
|
||||
})
|
||||
.join(" | ");
|
||||
|
||||
return [
|
||||
argsDesc.join("\n"),
|
||||
resTypeDesc ?? "",
|
||||
functionOverloads
|
||||
.flatMap((fo) => {
|
||||
return [
|
||||
`export function ${funcDef.functionName}(`,
|
||||
fo,
|
||||
`): Promise<${resType}>;`,
|
||||
"",
|
||||
];
|
||||
})
|
||||
.join("\n"),
|
||||
`export type ${capitalize(
|
||||
funcDef.functionName,
|
||||
)}Params = ${functionOverloadArgsType};`,
|
||||
`export type ${capitalize(
|
||||
funcDef.functionName,
|
||||
)}Result = Promise<${resType}>;\n`,
|
||||
]
|
||||
.filter((s) => {
|
||||
return s !== "";
|
||||
@ -117,13 +121,25 @@ export class TSTypeGenerator implements TypeGenerator {
|
||||
const serviceDecl = `service: ${srvName}Def`;
|
||||
const serviceIdDecl = `serviceId: string`;
|
||||
|
||||
const registerServiceArgs = [
|
||||
const functionOverloadsWithDefaultServiceId = [
|
||||
[serviceDecl],
|
||||
[serviceIdDecl, serviceDecl],
|
||||
[peerDecl, serviceDecl],
|
||||
[peerDecl, serviceIdDecl, serviceDecl],
|
||||
];
|
||||
|
||||
const functionOverloadsWithoutDefaultServiceId = [
|
||||
[serviceIdDecl, serviceDecl],
|
||||
[peerDecl, serviceIdDecl, serviceDecl],
|
||||
];
|
||||
|
||||
const registerServiceArgs =
|
||||
// This wrong type comes from aqua team. We need to discuss fix with them
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
(srvDef.defaultServiceId as DefaultServiceId).s_Some__f_value != null
|
||||
? functionOverloadsWithDefaultServiceId
|
||||
: functionOverloadsWithoutDefaultServiceId;
|
||||
|
||||
return [
|
||||
interfaces,
|
||||
...registerServiceArgs.map((registerServiceArg) => {
|
||||
|
@ -20,7 +20,8 @@ import { recursiveRenameLaquaProps } from "../utils.js";
|
||||
|
||||
import { TypeGenerator } from "./interfaces.js";
|
||||
|
||||
interface DefaultServiceId {
|
||||
// Actual value of defaultServiceId which comes from aqua-api
|
||||
export interface DefaultServiceId {
|
||||
s_Some__f_value?: string;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { generateSources, generateTypes } from "./generate/index.js";
|
||||
import { CompilationResult, OutputType } from "./generate/interfaces.js";
|
||||
import { CompilationResult } from "./generate/interfaces.js";
|
||||
import { getPackageJsonContent } from "./utils.js";
|
||||
|
||||
interface JsOutput {
|
||||
@ -33,6 +33,7 @@ type LanguageOutput = {
|
||||
};
|
||||
|
||||
type NothingToGenerate = null;
|
||||
type OutputType = "js" | "ts";
|
||||
|
||||
export default async function aquaToJs<T extends OutputType>(
|
||||
res: CompilationResult,
|
||||
@ -52,8 +53,7 @@ export default async function aquaToJs<T extends OutputType>(
|
||||
sources: generateSources(res, "js", packageJson),
|
||||
types: generateTypes(res, packageJson),
|
||||
}
|
||||
: // TODO: probably there is a way to remove this type assert
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
: // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
({
|
||||
sources: generateSources(res, "ts", packageJson),
|
||||
} as LanguageOutput[T]);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import assert from "assert";
|
||||
import { readFile } from "fs/promises";
|
||||
import path from "path";
|
||||
import { join } from "path";
|
||||
|
||||
import {
|
||||
ArrowType,
|
||||
@ -27,24 +27,26 @@ import {
|
||||
SimpleTypes,
|
||||
UnlabeledProductType,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { z } from "zod";
|
||||
|
||||
export interface PackageJson {
|
||||
name: string;
|
||||
version: string;
|
||||
devDependencies: {
|
||||
["@fluencelabs/aqua-api"]: string;
|
||||
};
|
||||
}
|
||||
const packageJsonSchema = z.object({
|
||||
name: z.string(),
|
||||
version: z.string(),
|
||||
devDependencies: z.object({
|
||||
// @fluencelabs/aqua-api version is included as part of the comment at the top of each js and ts file
|
||||
["@fluencelabs/aqua-api"]: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type PackageJson = z.infer<typeof packageJsonSchema>;
|
||||
|
||||
export async function getPackageJsonContent(): Promise<PackageJson> {
|
||||
const content = await readFile(
|
||||
new URL(path.join("..", "package.json"), import.meta.url),
|
||||
new URL(join("..", "package.json"), import.meta.url),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
// TODO: Add validation here
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
return JSON.parse(content) as PackageJson;
|
||||
return packageJsonSchema.parse(JSON.parse(content));
|
||||
}
|
||||
|
||||
export function getFuncArgs(
|
||||
|
Reference in New Issue
Block a user