mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-05-29 00:41:21 +00:00
Fix more comments
This commit is contained in:
parent
166475e26c
commit
26bfc36985
@ -45,7 +45,7 @@ describe("Aqua to js/ts compiler", () => {
|
||||
};
|
||||
});
|
||||
|
||||
it("matches js snapshot", async () => {
|
||||
it("matches js snapshots", async () => {
|
||||
const jsResult = generateSources(res, "js", pkg);
|
||||
const jsTypes = generateTypes(res, pkg);
|
||||
|
||||
@ -58,7 +58,7 @@ describe("Aqua to js/ts compiler", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("matches ts snapshot", async () => {
|
||||
it("matches ts snapshots", async () => {
|
||||
const tsResult = generateSources(res, "ts", pkg);
|
||||
|
||||
await expect(tsResult).toMatchFileSnapshot(
|
||||
|
@ -39,6 +39,7 @@ ${
|
||||
: ""
|
||||
}
|
||||
|
||||
// Making aliases to reduce chance of accidental name collision
|
||||
import {
|
||||
v5_callFunction as callFunction$$,
|
||||
v5_registerService as registerService$$,
|
||||
|
@ -119,19 +119,24 @@ export class TSTypeGenerator implements TypeGenerator {
|
||||
const serviceDecl = `service: ${srvName}Def`;
|
||||
const serviceIdDecl = `serviceId: string`;
|
||||
|
||||
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
|
||||
? [
|
||||
[serviceDecl],
|
||||
[serviceIdDecl, serviceDecl],
|
||||
[peerDecl, serviceDecl],
|
||||
[peerDecl, serviceIdDecl, serviceDecl],
|
||||
]
|
||||
: [
|
||||
[serviceIdDecl, serviceDecl],
|
||||
[peerDecl, serviceIdDecl, serviceDecl],
|
||||
];
|
||||
? functionOverloadsWithDefaultServiceId
|
||||
: functionOverloadsWithoutDefaultServiceId;
|
||||
|
||||
return [
|
||||
interfaces,
|
||||
|
@ -20,6 +20,7 @@ import { recursiveRenameLaquaProps } from "../utils.js";
|
||||
|
||||
import { TypeGenerator } from "./interfaces.js";
|
||||
|
||||
// Actual value of defaultServiceId which comes from aqua-api
|
||||
export interface DefaultServiceId {
|
||||
s_Some__f_value?: string;
|
||||
}
|
||||
|
@ -18,12 +18,39 @@ import {
|
||||
ArrowWithoutCallbacks,
|
||||
FunctionCallDef,
|
||||
JSONValue,
|
||||
ScalarType,
|
||||
SimpleTypes,
|
||||
UnlabeledProductType,
|
||||
} from "@fluencelabs/interfaces";
|
||||
|
||||
import { typeToTs } from "../common.js";
|
||||
|
||||
const numberTypes = [
|
||||
"u8",
|
||||
"u16",
|
||||
"u32",
|
||||
"u64",
|
||||
"i8",
|
||||
"i16",
|
||||
"i32",
|
||||
"i64",
|
||||
"f32",
|
||||
"f64",
|
||||
];
|
||||
|
||||
function isScalar(schema: ScalarType, arg: JSONValue) {
|
||||
if (numberTypes.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;
|
||||
}
|
||||
}
|
||||
|
||||
export function validateFunctionCall(
|
||||
schema: FunctionCallDef,
|
||||
...args: JSONValue[]
|
||||
@ -47,12 +74,12 @@ export function validateFunctionCall(
|
||||
export function validateFunctionCallArg(
|
||||
schema: SimpleTypes | UnlabeledProductType | ArrowWithoutCallbacks,
|
||||
arg: JSONValue,
|
||||
argIndex: number,
|
||||
argPosition: number,
|
||||
) {
|
||||
if (!isTypeMatchesSchema(schema, arg)) {
|
||||
const expectedType = typeToTs(schema);
|
||||
throw new Error(
|
||||
`Argument ${argIndex} doesn't match schema. Expected type: ${expectedType}`,
|
||||
`Argument ${argPosition} doesn't match schema. Expected type: ${expectedType}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -66,29 +93,7 @@ export function isTypeMatchesSchema(
|
||||
} 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;
|
||||
}
|
||||
return isScalar(schema, arg);
|
||||
} else if (schema.tag === "array") {
|
||||
return (
|
||||
Array.isArray(arg) &&
|
||||
|
@ -33,6 +33,7 @@ const packageJsonSchema = z.object({
|
||||
name: z.string(),
|
||||
version: z.string(),
|
||||
devDependencies: z.object({
|
||||
// This version used in header file
|
||||
["@fluencelabs/aqua-api"]: z.string(),
|
||||
}),
|
||||
});
|
||||
|
@ -33,6 +33,17 @@ import { FluencePeer } from "./jsPeer/FluencePeer.js";
|
||||
|
||||
import { callAquaFunction, Fluence, registerService } from "./index.js";
|
||||
|
||||
const isAquaConfig = (
|
||||
config: JSONValue | ServiceImpl[string] | undefined,
|
||||
): config is CallAquaFunctionConfig => {
|
||||
return (
|
||||
typeof config === "object" &&
|
||||
config !== null &&
|
||||
!Array.isArray(config) &&
|
||||
["undefined", "number"].includes(typeof config["ttl"])
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience function to support Aqua `func` generation backend
|
||||
* The compiler only need to generate a call the function and provide the corresponding definitions and the air script
|
||||
@ -47,9 +58,11 @@ export const v5_callFunction = async (
|
||||
script: string,
|
||||
): Promise<unknown> => {
|
||||
const argNames = Object.keys(def.arrow);
|
||||
const argCount = argNames.length;
|
||||
const schemaArgCount = argNames.length;
|
||||
|
||||
const functionArgs: Record<string, SimpleTypes | ArrowWithoutCallbacks> =
|
||||
type FunctionArg = SimpleTypes | ArrowWithoutCallbacks;
|
||||
|
||||
const schemaFunctionArgs: Record<string, FunctionArg> =
|
||||
def.arrow.domain.tag === "nil" ? {} : def.arrow.domain.fields;
|
||||
|
||||
let peer: FluencePeer | undefined;
|
||||
@ -61,26 +74,26 @@ export const v5_callFunction = async (
|
||||
peer = Fluence.defaultClient;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const config =
|
||||
argCount < args.length
|
||||
? // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
(args.pop() as CallAquaFunctionConfig | undefined)
|
||||
: undefined;
|
||||
|
||||
if (peer == null) {
|
||||
throw new Error(
|
||||
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
|
||||
);
|
||||
}
|
||||
|
||||
// if args more than expected in schema (schemaArgCount) then last arg is config
|
||||
const config = schemaArgCount < args.length ? args.pop() : undefined;
|
||||
|
||||
if (!isAquaConfig(config)) {
|
||||
throw new Error("Config should be object type");
|
||||
}
|
||||
|
||||
const callArgs = Object.fromEntries<JSONValue | ServiceImpl[string]>(
|
||||
args.slice(0, argCount).map((arg, i) => {
|
||||
const argSchema = functionArgs[argNames[i]];
|
||||
args.slice(0, schemaArgCount).map((arg, i) => {
|
||||
const argSchema = schemaFunctionArgs[argNames[i]];
|
||||
|
||||
if (argSchema.tag === "arrow") {
|
||||
if (typeof arg !== "function") {
|
||||
throw new Error("Argument and schema doesn't match");
|
||||
throw new Error("Argument and schema don't match");
|
||||
}
|
||||
|
||||
const wrappedFunction = wrapFunction(arg, argSchema);
|
||||
@ -89,7 +102,7 @@ export const v5_callFunction = async (
|
||||
}
|
||||
|
||||
if (typeof arg === "function") {
|
||||
throw new Error("Argument and schema doesn't match");
|
||||
throw new Error("Argument and schema don't match");
|
||||
}
|
||||
|
||||
return [argNames[i], ts2aqua(arg, argSchema)];
|
||||
@ -111,13 +124,13 @@ export const v5_callFunction = async (
|
||||
fireAndForget: returnTypeVoid,
|
||||
});
|
||||
|
||||
const valueSchema =
|
||||
const returnSchema =
|
||||
def.arrow.codomain.tag === "unlabeledProduct" &&
|
||||
def.arrow.codomain.items.length === 1
|
||||
? def.arrow.codomain.items[0]
|
||||
: def.arrow.codomain;
|
||||
|
||||
return aqua2ts(result, valueSchema);
|
||||
return aqua2ts(result, returnSchema);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -145,7 +158,13 @@ export const v5_registerService = (args: unknown[], def: ServiceDef): void => {
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof args[0] === "string") {
|
||||
if (args.length === 2) {
|
||||
if (typeof args[0] !== "string") {
|
||||
throw new Error(
|
||||
`Service ID should be of type string. ${typeof args[0]} provided.`,
|
||||
);
|
||||
}
|
||||
|
||||
serviceId = args[0];
|
||||
}
|
||||
|
||||
@ -153,8 +172,10 @@ export const v5_registerService = (args: unknown[], def: ServiceDef): void => {
|
||||
throw new Error("Service ID is not provided");
|
||||
}
|
||||
|
||||
// Schema for every function in service
|
||||
const serviceSchema = def.functions.tag === "nil" ? {} : def.functions.fields;
|
||||
|
||||
// Wrapping service impl to convert their args ts -> aqua and backwards
|
||||
const wrappedServiceImpl = Object.fromEntries(
|
||||
Object.entries(serviceImpl).map(([name, func]) => {
|
||||
return [name, wrapFunction(func, serviceSchema[name])];
|
||||
|
@ -25,8 +25,10 @@ import { checkConnection } from "../checkConnection.js";
|
||||
|
||||
import { nodes, RELAY } from "./connection.js";
|
||||
|
||||
const ONE_SECOND = 1000;
|
||||
|
||||
describe("FluenceClient usage test suite", () => {
|
||||
it("Should resolve at TTL when fire and forget behavior is used", async () => {
|
||||
it("Should stop particle processing after TTL is reached", async () => {
|
||||
await withClient(RELAY, { defaultTtlMs: 600 }, async (peer) => {
|
||||
const script = `
|
||||
(seq
|
||||
@ -36,7 +38,7 @@ describe("FluenceClient usage test suite", () => {
|
||||
|
||||
const particle = await peer.internals.createNewParticle(script);
|
||||
|
||||
const now = Date.now();
|
||||
const start = Date.now();
|
||||
|
||||
const promise = new Promise<JSONValue>((resolve, reject) => {
|
||||
registerHandlersHelper(peer, particle, {
|
||||
@ -51,7 +53,11 @@ describe("FluenceClient usage test suite", () => {
|
||||
});
|
||||
|
||||
await expect(promise).rejects.toThrow(ExpirationError);
|
||||
expect(Date.now() - 500).toBeGreaterThanOrEqual(now);
|
||||
|
||||
expect(
|
||||
Date.now() - 500,
|
||||
"Particle processing didn't stop after TTL is reached",
|
||||
).toBeGreaterThanOrEqual(start);
|
||||
});
|
||||
});
|
||||
|
||||
@ -209,13 +215,17 @@ describe("FluenceClient usage test suite", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("With connection options: defaultTTL", async () => {
|
||||
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
|
||||
const isConnected = await checkConnection(peer);
|
||||
it(
|
||||
"With connection options: defaultTTL",
|
||||
async () => {
|
||||
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
|
||||
const isConnected = await checkConnection(peer);
|
||||
|
||||
expect(isConnected).toBeFalsy();
|
||||
});
|
||||
}, 1000);
|
||||
expect(isConnected).toBeFalsy();
|
||||
});
|
||||
},
|
||||
ONE_SECOND,
|
||||
);
|
||||
});
|
||||
|
||||
it.skip("Should throw correct error when the client tries to send a particle not to the relay", async () => {
|
||||
@ -247,15 +257,11 @@ describe("FluenceClient usage test suite", () => {
|
||||
particle,
|
||||
() => {},
|
||||
(error: Error) => {
|
||||
if (error instanceof SendError) {
|
||||
reject(error.message);
|
||||
}
|
||||
reject(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
await promise;
|
||||
|
||||
await expect(promise).rejects.toMatch(
|
||||
"Particle is expected to be sent to only the single peer (relay which client is connected to)",
|
||||
);
|
||||
|
@ -69,6 +69,7 @@ export const callAquaFunction = async ({
|
||||
const particle = await peer.internals.createNewParticle(script, config.ttl);
|
||||
|
||||
return new Promise<JSONValue>((resolve, reject) => {
|
||||
// Registering function args as a services
|
||||
for (const [name, argVal] of Object.entries(args)) {
|
||||
let service: ServiceDescription;
|
||||
|
||||
|
@ -147,12 +147,12 @@ export const wrapFunction = (
|
||||
|
||||
const result = await value(...tsArgs, context);
|
||||
|
||||
const valueSchema =
|
||||
const resultSchema =
|
||||
schema.codomain.tag === "unlabeledProduct" &&
|
||||
schema.codomain.items.length === 1
|
||||
? schema.codomain.items[0]
|
||||
: schema.codomain;
|
||||
|
||||
return ts2aqua(result, valueSchema);
|
||||
return ts2aqua(result, resultSchema);
|
||||
};
|
||||
};
|
||||
|
@ -37,6 +37,7 @@ export class EphemeralNetworkClient extends FluencePeer {
|
||||
) {
|
||||
const workerLoader = new WorkerLoader();
|
||||
|
||||
// TODO: use js-client-isomorphic
|
||||
const controlModuleLoader = new WasmLoaderFromNpm(
|
||||
"@fluencelabs/marine-js",
|
||||
"marine-js.wasm",
|
||||
|
@ -232,6 +232,7 @@ export class EphemeralNetwork {
|
||||
// shared worker for all the peers
|
||||
this.workerLoader = new WorkerLoaderFromFs("../../marine/worker-script");
|
||||
|
||||
// TODO: use js-client-isomorphic
|
||||
this.controlModuleLoader = new WasmLoaderFromNpm(
|
||||
"@fluencelabs/marine-js",
|
||||
"marine-js.wasm",
|
||||
|
@ -538,6 +538,17 @@ export abstract class FluencePeer {
|
||||
"id %s. send successful",
|
||||
newParticle.id,
|
||||
);
|
||||
|
||||
if (
|
||||
this.jsServiceHost.getHandler(
|
||||
"callbackSrv",
|
||||
"response",
|
||||
item.particle.id,
|
||||
) == null
|
||||
) {
|
||||
// try to finish script if fire-and-forget enabled
|
||||
item.onSuccess({});
|
||||
}
|
||||
})
|
||||
.catch((e: unknown) => {
|
||||
log_particle.error(
|
||||
@ -623,10 +634,9 @@ export abstract class FluencePeer {
|
||||
"callbackSrv",
|
||||
"response",
|
||||
item.particle.id,
|
||||
) == null &&
|
||||
item.result.nextPeerPks.length === 0
|
||||
) == null
|
||||
) {
|
||||
// try to finish script
|
||||
// try to finish script if fire-and-forget enabled
|
||||
item.onSuccess({});
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +148,7 @@ export class TestPeer extends FluencePeer {
|
||||
constructor(keyPair: KeyPair, connection: IConnection) {
|
||||
const workerLoader = new WorkerLoader();
|
||||
|
||||
// TODO: use js-client-isomorphic
|
||||
const controlModuleLoader = new WasmLoaderFromNpm(
|
||||
"@fluencelabs/marine-js",
|
||||
"marine-js.wasm",
|
||||
@ -193,6 +194,7 @@ export const withClient = async (
|
||||
) => {
|
||||
const workerLoader = new WorkerLoader();
|
||||
|
||||
// TODO: use js-client-isomorphic
|
||||
const controlModuleLoader = new WasmLoaderFromNpm(
|
||||
"@fluencelabs/marine-js",
|
||||
"marine-js.wasm",
|
||||
|
Loading…
x
Reference in New Issue
Block a user