mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-07-31 15:01:58 +00:00
feat(npm-aqua-compiler): create package (#401)
* Add npm-aqua-compiler package * Release new package * Remove noUncheckedIndexedAccess from tsconfig.json * Fix a test script * Fix length checks * Fix * Update error description * Try to choose a nicer err message * New import format and API * Fix error message * Improve test * Don't add empty string key when globalImports prop is empty * Fix exports
This commit is contained in:
@@ -18,7 +18,7 @@ import { fileURLToPath } from "url";
|
||||
|
||||
import { compileFromPath } from "@fluencelabs/aqua-api";
|
||||
import { ServiceDef } from "@fluencelabs/interfaces";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { assert, describe, expect, it } from "vitest";
|
||||
|
||||
import { v5_registerService } from "./api.js";
|
||||
import { callAquaFunction } from "./compilerSupport/callFunction.js";
|
||||
@@ -65,8 +65,12 @@ describe("User API methods", () => {
|
||||
|
||||
const typedServices: Record<string, ServiceDef> = services;
|
||||
|
||||
assert("demoCalc" in functions);
|
||||
|
||||
const { script } = functions["demoCalc"];
|
||||
|
||||
assert("Calc" in typedServices);
|
||||
|
||||
v5_registerService([peer, "calc", calcService], {
|
||||
defaultServiceId: "calc",
|
||||
functions: typedServices["Calc"].functions,
|
||||
|
@@ -26,12 +26,13 @@ import { z } from "zod";
|
||||
import { CallAquaFunctionConfig } from "./compilerSupport/callFunction.js";
|
||||
import {
|
||||
aqua2js,
|
||||
SchemaValidationError,
|
||||
js2aqua,
|
||||
SchemaValidationError,
|
||||
wrapJsFunction,
|
||||
} from "./compilerSupport/conversions.js";
|
||||
import { ServiceImpl, UserServiceImpl } from "./compilerSupport/types.js";
|
||||
import { FluencePeer } from "./jsPeer/FluencePeer.js";
|
||||
import { zip } from "./util/utils.js";
|
||||
|
||||
import { callAquaFunction, Fluence, registerService } from "./index.js";
|
||||
|
||||
@@ -74,51 +75,48 @@ export const v5_callFunction = async (
|
||||
);
|
||||
}
|
||||
|
||||
const argNames = Object.keys(
|
||||
def.arrow.domain.tag === "nil" ? [] : def.arrow.domain.fields,
|
||||
);
|
||||
|
||||
const schemaArgCount = argNames.length;
|
||||
|
||||
type FunctionArg = SimpleTypes | ArrowWithoutCallbacks;
|
||||
|
||||
const schemaFunctionArgs: Record<string, FunctionArg> =
|
||||
def.arrow.domain.tag === "nil" ? {} : def.arrow.domain.fields;
|
||||
|
||||
const schemaFunctionArgEntries = Object.entries(schemaFunctionArgs);
|
||||
|
||||
const schemaArgCount = schemaFunctionArgEntries.length;
|
||||
|
||||
type FunctionArg = SimpleTypes | ArrowWithoutCallbacks;
|
||||
|
||||
// if there are more args than expected in schema (schemaArgCount) then last arg is config
|
||||
const config = schemaArgCount < rest.length ? rest.pop() : undefined;
|
||||
|
||||
validateAquaConfig(config);
|
||||
|
||||
const callArgs = Object.fromEntries<JSONValue | ServiceImpl[string]>(
|
||||
rest.slice(0, schemaArgCount).map((arg, i) => {
|
||||
const argName = argNames[i];
|
||||
const argSchema = schemaFunctionArgs[argName];
|
||||
zip(rest.slice(0, schemaArgCount), schemaFunctionArgEntries).map(
|
||||
([arg, [argName, argType]]) => {
|
||||
if (argType.tag === "arrow") {
|
||||
if (typeof arg !== "function") {
|
||||
throw new SchemaValidationError(
|
||||
[argName],
|
||||
argType,
|
||||
"function",
|
||||
arg,
|
||||
);
|
||||
}
|
||||
|
||||
if (argSchema.tag === "arrow") {
|
||||
if (typeof arg !== "function") {
|
||||
return [argName, wrapJsFunction(arg, argType)];
|
||||
}
|
||||
|
||||
if (typeof arg === "function") {
|
||||
throw new SchemaValidationError(
|
||||
[argName],
|
||||
argSchema,
|
||||
"function",
|
||||
argType,
|
||||
"non-function value",
|
||||
arg,
|
||||
);
|
||||
}
|
||||
|
||||
return [argName, wrapJsFunction(arg, argSchema)];
|
||||
}
|
||||
|
||||
if (typeof arg === "function") {
|
||||
throw new SchemaValidationError(
|
||||
[argName],
|
||||
argSchema,
|
||||
"non-function value",
|
||||
arg,
|
||||
);
|
||||
}
|
||||
|
||||
return [argName, js2aqua(arg, argSchema, { path: [def.functionName] })];
|
||||
}),
|
||||
return [argName, js2aqua(arg, argType, { path: [def.functionName] })];
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const returnTypeVoid =
|
||||
@@ -126,7 +124,8 @@ export const v5_callFunction = async (
|
||||
|
||||
const returnSchema =
|
||||
def.arrow.codomain.tag === "unlabeledProduct" &&
|
||||
def.arrow.codomain.items.length === 1
|
||||
def.arrow.codomain.items.length === 1 &&
|
||||
"0" in def.arrow.codomain.items
|
||||
? def.arrow.codomain.items[0]
|
||||
: def.arrow.codomain;
|
||||
|
||||
@@ -145,7 +144,7 @@ export const v5_callFunction = async (
|
||||
};
|
||||
|
||||
const getDefaultPeer = (): FluencePeer => {
|
||||
if (Fluence.defaultClient == null) {
|
||||
if (Fluence.defaultClient === undefined) {
|
||||
throw new Error(
|
||||
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
|
||||
);
|
||||
@@ -155,7 +154,7 @@ const getDefaultPeer = (): FluencePeer => {
|
||||
};
|
||||
|
||||
const getDefaultServiceId = (def: ServiceDef) => {
|
||||
if (def.defaultServiceId == null) {
|
||||
if (def.defaultServiceId === undefined) {
|
||||
throw new Error("Service ID is not provided");
|
||||
}
|
||||
|
||||
@@ -204,13 +203,18 @@ export const v5_registerService = (
|
||||
|
||||
// Wrapping service functions, selecting only those listed in schema, to convert their args js -> aqua and backwards
|
||||
const wrappedServiceImpl = Object.fromEntries(
|
||||
Object.keys(serviceSchema).map((schemaKey) => {
|
||||
Object.entries(serviceSchema).map(([schemaKey, schemaValue]) => {
|
||||
const serviceImplValue = serviceImpl[schemaKey];
|
||||
|
||||
if (serviceImplValue === undefined) {
|
||||
throw new Error(
|
||||
`Service function ${schemaKey} listed in Aqua schema but wasn't provided in schema implementation object or class instance. Check that your Aqua service definition matches passed service implementation`,
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
schemaKey,
|
||||
wrapJsFunction(
|
||||
serviceImpl[schemaKey].bind(serviceImpl),
|
||||
serviceSchema[schemaKey],
|
||||
),
|
||||
wrapJsFunction(serviceImplValue.bind(serviceImpl), schemaValue),
|
||||
] as const;
|
||||
}),
|
||||
);
|
||||
|
@@ -60,7 +60,7 @@ export const makeClientPeerConfig = async (
|
||||
relayConfig: {
|
||||
peerId: keyPair.getLibp2pPeerId(),
|
||||
relayAddress: relayAddress,
|
||||
...(config.connectionOptions?.dialTimeoutMs != null
|
||||
...(config.connectionOptions?.dialTimeoutMs !== undefined
|
||||
? {
|
||||
dialTimeout: config.connectionOptions.dialTimeoutMs,
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { it, describe, expect } from "vitest";
|
||||
import { it, describe, expect, assert } from "vitest";
|
||||
|
||||
import { ExpirationError } from "../../jsPeer/errors.js";
|
||||
import { CallServiceData } from "../../jsServiceHost/interfaces.js";
|
||||
@@ -103,6 +103,7 @@ describe("FluenceClient usage test suite", () => {
|
||||
callback: {
|
||||
callback: (args): undefined => {
|
||||
const [val] = args;
|
||||
assert(val);
|
||||
resolve(val);
|
||||
},
|
||||
error: (args): undefined => {
|
||||
|
@@ -20,6 +20,6 @@ export const nodes = [
|
||||
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
export const RELAY = nodes[0].multiaddr;
|
||||
|
@@ -86,7 +86,7 @@ export const checkConnection = async (
|
||||
const [val] = args;
|
||||
|
||||
setTimeout(() => {
|
||||
resolve(val);
|
||||
resolve(val ?? null);
|
||||
}, 0);
|
||||
|
||||
return {};
|
||||
|
@@ -76,7 +76,7 @@ const structs = [
|
||||
c: [null, 2],
|
||||
},
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
const labeledProduct2 = {
|
||||
tag: "labeledProduct",
|
||||
@@ -167,7 +167,7 @@ const nestedStructs = [
|
||||
c: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
interface ConversionTestArgs {
|
||||
aqua: JSONValue;
|
||||
|
@@ -25,6 +25,8 @@ import {
|
||||
UnlabeledProductType,
|
||||
} from "@fluencelabs/interfaces";
|
||||
|
||||
import { zip } from "../util/utils.js";
|
||||
|
||||
import { ServiceImpl, UserServiceImpl } from "./types.js";
|
||||
|
||||
export class SchemaValidationError extends Error {
|
||||
@@ -103,10 +105,10 @@ export function aqua2js(
|
||||
throw new SchemaValidationError([], schema, "array", value);
|
||||
}
|
||||
|
||||
if (value.length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
if ("0" in value) {
|
||||
return aqua2js(value[0], schema.type);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (
|
||||
schema.tag === "scalar" ||
|
||||
@@ -127,17 +129,23 @@ export function aqua2js(
|
||||
throw new SchemaValidationError([], schema, "array", value);
|
||||
}
|
||||
|
||||
return value.map((y, i) => {
|
||||
return aqua2js(y, schema.items[i]);
|
||||
return zip(value, schema.items).map(([v, s]) => {
|
||||
return aqua2js(v, s);
|
||||
});
|
||||
} else if (["labeledProduct", "struct"].includes(schema.tag)) {
|
||||
if (typeof value !== "object" || value == null || Array.isArray(value)) {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
||||
throw new SchemaValidationError([], schema, "object", value);
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(schema.fields).map(([key, type]) => {
|
||||
const val = aqua2js(value[key], type);
|
||||
let v = value[key];
|
||||
|
||||
if (v === undefined) {
|
||||
v = null;
|
||||
}
|
||||
|
||||
const val = aqua2js(v, type);
|
||||
return [key, val];
|
||||
}),
|
||||
);
|
||||
@@ -159,7 +167,7 @@ export function js2aqua(
|
||||
return value;
|
||||
} else if (schema.tag === "option") {
|
||||
// option means 'type | null'
|
||||
return value == null ? [] : [js2aqua(value, schema.type, { path })];
|
||||
return value === null ? [] : [js2aqua(value, schema.type, { path })];
|
||||
} else if (schema.tag === "topType") {
|
||||
// topType equals to 'any'
|
||||
return value;
|
||||
@@ -181,8 +189,8 @@ export function js2aqua(
|
||||
throw new SchemaValidationError(path, schema, "array", value);
|
||||
}
|
||||
|
||||
return value.map((y, i) => {
|
||||
return js2aqua(y, schema.items[i], { path: [...path, `[${i}]`] });
|
||||
return zip(value, schema.items).map(([v, s], i) => {
|
||||
return js2aqua(v, s, { path: [...path, `[${i}]`] });
|
||||
});
|
||||
} else if (["labeledProduct", "struct"].includes(schema.tag)) {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
||||
@@ -191,7 +199,13 @@ export function js2aqua(
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(schema.fields).map(([key, type]) => {
|
||||
const val = js2aqua(value[key], type, { path: [...path, key] });
|
||||
let v = value[key];
|
||||
|
||||
if (v === undefined) {
|
||||
v = null;
|
||||
}
|
||||
|
||||
const val = js2aqua(v, type, { path: [...path, key] });
|
||||
return [key, val];
|
||||
}),
|
||||
);
|
||||
@@ -222,8 +236,8 @@ export const wrapJsFunction = (
|
||||
);
|
||||
}
|
||||
|
||||
const jsArgs = args.map((arg, i) => {
|
||||
return aqua2js(arg, schemaArgs[i]);
|
||||
const jsArgs = zip(args, schemaArgs).map(([arg, schemaArg]) => {
|
||||
return aqua2js(arg, schemaArg);
|
||||
});
|
||||
|
||||
const returnTypeVoid =
|
||||
@@ -231,7 +245,8 @@ export const wrapJsFunction = (
|
||||
|
||||
const resultSchema =
|
||||
schema.codomain.tag === "unlabeledProduct" &&
|
||||
schema.codomain.items.length === 1
|
||||
schema.codomain.items.length === 1 &&
|
||||
"0" in schema.codomain.items
|
||||
? schema.codomain.items[0]
|
||||
: schema.codomain;
|
||||
|
||||
|
@@ -28,10 +28,12 @@ interface RegisterServiceArgs {
|
||||
service: ServiceImpl;
|
||||
}
|
||||
|
||||
type ServiceFunctionPair = [key: string, value: ServiceImpl[string]];
|
||||
|
||||
// This function iterates on plain object or class instance functions ignoring inherited functions and prototype chain.
|
||||
const findAllPossibleRegisteredServiceFunctions = (
|
||||
service: ServiceImpl,
|
||||
): Array<string> => {
|
||||
): Array<ServiceFunctionPair> => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const prototype = Object.getPrototypeOf(service) as ServiceImpl;
|
||||
|
||||
@@ -41,9 +43,14 @@ const findAllPossibleRegisteredServiceFunctions = (
|
||||
service = prototype;
|
||||
}
|
||||
|
||||
return Object.getOwnPropertyNames(service).filter((prop) => {
|
||||
return typeof service[prop] === "function" && prop !== "constructor";
|
||||
});
|
||||
return Object.getOwnPropertyNames(service)
|
||||
.map((prop) => {
|
||||
return [prop, service[prop]];
|
||||
})
|
||||
.filter((entry): entry is ServiceFunctionPair => {
|
||||
const [prop, value] = entry;
|
||||
return typeof value === "function" && prop !== "constructor";
|
||||
});
|
||||
};
|
||||
|
||||
export const registerService = ({
|
||||
@@ -55,8 +62,7 @@ export const registerService = ({
|
||||
|
||||
const serviceFunctions = findAllPossibleRegisteredServiceFunctions(service);
|
||||
|
||||
for (const serviceFunction of serviceFunctions) {
|
||||
const handler = service[serviceFunction];
|
||||
for (const [serviceFunction, handler] of serviceFunctions) {
|
||||
const userDefinedHandler = handler.bind(service);
|
||||
|
||||
const serviceDescription = userHandlerService(
|
||||
|
@@ -79,7 +79,7 @@ export const responseService = (resolveCallback: (val: JSONValue) => void) => {
|
||||
const userFunctionReturn =
|
||||
req.args.length === 0
|
||||
? null
|
||||
: req.args.length === 1
|
||||
: req.args.length === 1 && "0" in req.args
|
||||
? req.args[0]
|
||||
: req.args;
|
||||
|
||||
@@ -108,7 +108,10 @@ export const errorHandlingService = (
|
||||
const [err] = req.args;
|
||||
|
||||
setTimeout(() => {
|
||||
rejectCallback(err);
|
||||
rejectCallback(
|
||||
err ??
|
||||
"Unknown error happened when executing aqua code. No error text was passed to 'errorHandlingSrv.error' function, probably because AIR code was modified or aqua compiler didn't produce the correct call",
|
||||
);
|
||||
}, 0);
|
||||
|
||||
return {
|
||||
|
@@ -92,7 +92,7 @@ export class RelayConnection implements IConnection {
|
||||
this.relayAddress = multiaddr(this.config.relayAddress);
|
||||
const peerId = this.relayAddress.getPeerId();
|
||||
|
||||
if (peerId == null) {
|
||||
if (peerId === null) {
|
||||
throwHasNoPeerId(this.relayAddress);
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ export class RelayConnection implements IConnection {
|
||||
streamMuxers: [yamux()],
|
||||
connectionEncryption: [noise()],
|
||||
connectionManager: {
|
||||
...(this.config.dialTimeoutMs != null
|
||||
...(this.config.dialTimeoutMs !== undefined
|
||||
? {
|
||||
dialTimeout: this.config.dialTimeoutMs,
|
||||
}
|
||||
|
@@ -30,10 +30,12 @@ import { logger } from "../util/logger.js";
|
||||
const log = logger("ephemeral");
|
||||
|
||||
interface EphemeralConfig {
|
||||
peers: Array<{
|
||||
peerId: PeerIdB58;
|
||||
sk: string;
|
||||
}>;
|
||||
peers: Readonly<
|
||||
Array<{
|
||||
peerId: PeerIdB58;
|
||||
sk: string;
|
||||
}>
|
||||
>;
|
||||
}
|
||||
|
||||
export const defaultConfig = {
|
||||
@@ -119,7 +121,7 @@ export const defaultConfig = {
|
||||
sk: "RbgKmG6soWW9uOi7yRedm+0Qck3f3rw6MSnDP7AcBQs=",
|
||||
},
|
||||
],
|
||||
};
|
||||
} as const;
|
||||
|
||||
export interface IEphemeralConnection extends IConnection {
|
||||
readonly selfPeerId: PeerIdB58;
|
||||
@@ -248,17 +250,17 @@ export class EphemeralNetwork {
|
||||
|
||||
const peers = await Promise.all(promises);
|
||||
|
||||
for (let i = 0; i < peers.length; i++) {
|
||||
for (let j = 0; j < i; j++) {
|
||||
peers.forEach((peer, i) => {
|
||||
const otherPeers = peers.slice(0, i);
|
||||
|
||||
otherPeers.forEach((otherPeer, j) => {
|
||||
if (i === j) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
peers[i].ephemeralConnection.connectToOther(
|
||||
peers[j].ephemeralConnection,
|
||||
);
|
||||
}
|
||||
}
|
||||
peer.ephemeralConnection.connectToOther(otherPeer.ephemeralConnection);
|
||||
});
|
||||
});
|
||||
|
||||
const startPromises = peers.map((x) => {
|
||||
return x.start();
|
||||
|
@@ -111,7 +111,7 @@ export const Fluence: FluencePublicApi = {
|
||||
* @returns IFluenceClient instance
|
||||
*/
|
||||
getClient: function () {
|
||||
if (this.defaultClient == null) {
|
||||
if (this.defaultClient === undefined) {
|
||||
throw new Error(
|
||||
"Fluence client is not initialized. Call Fluence.connect() first",
|
||||
);
|
||||
|
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { it, describe, expect } from "vitest";
|
||||
import { it, describe, expect, assert } from "vitest";
|
||||
|
||||
import { handleTimeout } from "../../particle/Particle.js";
|
||||
import { registerHandlersHelper, withPeer } from "../../util/testUtils.js";
|
||||
@@ -39,6 +39,7 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
print: {
|
||||
print: (args): undefined => {
|
||||
const [res] = args;
|
||||
assert(res);
|
||||
resolve(res);
|
||||
},
|
||||
},
|
||||
@@ -80,6 +81,7 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
registerHandlersHelper(peer, particle, {
|
||||
print: {
|
||||
print: (args): undefined => {
|
||||
assert(args[0]);
|
||||
res.push(args[0]);
|
||||
|
||||
if (res.length === 2) {
|
||||
|
@@ -14,10 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, assert } from "vitest";
|
||||
|
||||
import {
|
||||
CallServiceData,
|
||||
@@ -52,6 +50,7 @@ describe("FluencePeer flow tests", () => {
|
||||
(req: CallServiceData) => {
|
||||
const [timeout, message] = req.args;
|
||||
assert(typeof timeout === "number");
|
||||
assert(message);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
@@ -77,6 +76,7 @@ describe("FluencePeer flow tests", () => {
|
||||
callback: {
|
||||
callback1: (args): undefined => {
|
||||
const [val] = args;
|
||||
assert(val);
|
||||
values.push(val);
|
||||
|
||||
if (values.length === 2) {
|
||||
@@ -85,6 +85,7 @@ describe("FluencePeer flow tests", () => {
|
||||
},
|
||||
callback2: (args): undefined => {
|
||||
const [val] = args;
|
||||
assert(val);
|
||||
values.push(val);
|
||||
|
||||
if (values.length === 2) {
|
||||
|
@@ -18,7 +18,7 @@ import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import * as url from "url";
|
||||
|
||||
import { it, describe, expect, beforeAll } from "vitest";
|
||||
import { it, describe, expect, beforeAll, assert } from "vitest";
|
||||
|
||||
import { compileAqua, CompiledFnCall, withPeer } from "../../util/testUtils.js";
|
||||
|
||||
@@ -46,6 +46,7 @@ describe("Marine js tests", () => {
|
||||
await peer.registerMarineService(wasm, "greeting");
|
||||
|
||||
// act
|
||||
assert(aqua["call"]);
|
||||
const res = await aqua["call"](peer, { arg: "test" });
|
||||
|
||||
// assert
|
||||
|
@@ -43,7 +43,7 @@ export class MarineBackgroundRunner implements IMarineHost {
|
||||
) {}
|
||||
|
||||
async hasService(serviceId: string) {
|
||||
if (this.workerThread == null) {
|
||||
if (this.workerThread === undefined) {
|
||||
throw new Error("Worker is not initialized");
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export class MarineBackgroundRunner implements IMarineHost {
|
||||
}
|
||||
|
||||
async removeService(serviceId: string) {
|
||||
if (this.workerThread == null) {
|
||||
if (this.workerThread === undefined) {
|
||||
throw new Error("Worker is not initialized");
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export class MarineBackgroundRunner implements IMarineHost {
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
if (this.workerThread != null) {
|
||||
if (this.workerThread !== undefined) {
|
||||
throw new Error("Worker thread already initialized");
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export class MarineBackgroundRunner implements IMarineHost {
|
||||
const logfn: LogFunction = (message) => {
|
||||
const serviceLogger = this.loggers.get(message.service);
|
||||
|
||||
if (serviceLogger == null) {
|
||||
if (serviceLogger === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export class MarineBackgroundRunner implements IMarineHost {
|
||||
serviceModule: ArrayBuffer | SharedArrayBuffer,
|
||||
serviceId: string,
|
||||
): Promise<void> {
|
||||
if (this.workerThread == null) {
|
||||
if (this.workerThread === undefined) {
|
||||
throw new Error("Worker is not initialized");
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export class MarineBackgroundRunner implements IMarineHost {
|
||||
args: Array<JSONValueNonNullable> | Record<string, JSONValueNonNullable>,
|
||||
callParams?: CallParameters,
|
||||
): Promise<JSONValue> {
|
||||
if (this.workerThread == null) {
|
||||
if (this.workerThread === undefined) {
|
||||
throw new Error("Worker is not initialized");
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ export class MarineBackgroundRunner implements IMarineHost {
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (this.workerThread == null) {
|
||||
if (this.workerThread === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -14,11 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
|
||||
import { JSONArray } from "@fluencelabs/interfaces";
|
||||
import { toUint8Array } from "js-base64";
|
||||
import { describe, expect, it, test } from "vitest";
|
||||
import { describe, expect, it, test, assert } from "vitest";
|
||||
|
||||
import { CallServiceData } from "../../jsServiceHost/interfaces.js";
|
||||
import { KeyPair } from "../../keypair/index.js";
|
||||
@@ -155,7 +153,8 @@ describe("Tests for default handler", () => {
|
||||
};
|
||||
|
||||
// act
|
||||
const fn = builtInServices[req.serviceId][req.fnName];
|
||||
const fn = builtInServices[req.serviceId]?.[req.fnName];
|
||||
assert(fn);
|
||||
const res = await fn(req);
|
||||
|
||||
// Our test cases above depend on node error message. In node 20 error message for JSON.parse has changed.
|
||||
@@ -192,7 +191,8 @@ describe("Tests for default handler", () => {
|
||||
};
|
||||
|
||||
// act
|
||||
const fn = builtInServices[req.serviceId][req.fnName];
|
||||
const fn = builtInServices[req.serviceId]?.[req.fnName];
|
||||
assert(fn);
|
||||
const res = await fn(req);
|
||||
|
||||
// assert
|
||||
|
@@ -17,7 +17,7 @@
|
||||
import * as path from "path";
|
||||
import * as url from "url";
|
||||
|
||||
import { it, describe, expect, beforeAll } from "vitest";
|
||||
import { it, describe, expect, beforeAll, assert } from "vitest";
|
||||
|
||||
import { registerService } from "../../compilerSupport/registerService.js";
|
||||
import { KeyPair } from "../../keypair/index.js";
|
||||
@@ -72,6 +72,7 @@ describe("Sig service test suite", () => {
|
||||
|
||||
customSig.securityGuard = allowServiceFn("data", "provide_data");
|
||||
|
||||
assert(aqua["callSig"]);
|
||||
const result = await aqua["callSig"](peer, { sigId: "CustomSig" });
|
||||
|
||||
expect(result).toHaveProperty("success", true);
|
||||
@@ -116,6 +117,7 @@ describe("Sig service test suite", () => {
|
||||
|
||||
customSig.securityGuard = allowServiceFn("wrong", "wrong");
|
||||
|
||||
assert(aqua["callSig"]);
|
||||
const result = await aqua["callSig"](peer, { sigId: "CustomSig" });
|
||||
expect(result).toHaveProperty("success", false);
|
||||
});
|
||||
@@ -137,6 +139,7 @@ describe("Sig service test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
assert(aqua["callSig"]);
|
||||
const callAsSigRes = await aqua["callSig"](peer, { sigId: "sig" });
|
||||
|
||||
const callAsPeerIdRes = await aqua["callSig"](peer, {
|
||||
|
@@ -17,7 +17,7 @@
|
||||
import * as path from "path";
|
||||
import * as url from "url";
|
||||
|
||||
import { it, describe, expect, beforeAll } from "vitest";
|
||||
import { it, describe, expect, beforeAll, assert } from "vitest";
|
||||
|
||||
import { compileAqua, CompiledFnCall, withPeer } from "../../util/testUtils.js";
|
||||
import { registerNodeUtils } from "../_aqua/node-utils.js";
|
||||
@@ -42,6 +42,7 @@ describe("Srv service test suite", () => {
|
||||
const wasm = path.join(__dirname, "../../../data_for_test/greeting.wasm");
|
||||
|
||||
// act
|
||||
assert(aqua["happy_path"]);
|
||||
const res = await aqua["happy_path"](peer, { file_path: wasm });
|
||||
|
||||
// assert
|
||||
@@ -57,6 +58,7 @@ describe("Srv service test suite", () => {
|
||||
const wasm = path.join(__dirname, "../../../data_for_test/greeting.wasm");
|
||||
|
||||
// act
|
||||
assert(aqua["list_services"]);
|
||||
const res = await aqua["list_services"](peer, { file_path: wasm });
|
||||
|
||||
// assert
|
||||
@@ -72,6 +74,8 @@ describe("Srv service test suite", () => {
|
||||
const wasm = path.join(__dirname, "../../../data_for_test/greeting.wasm");
|
||||
|
||||
// act
|
||||
assert(aqua["service_removed"]);
|
||||
|
||||
const res = await aqua["service_removed"](peer, {
|
||||
file_path: wasm,
|
||||
});
|
||||
@@ -87,6 +91,7 @@ describe("Srv service test suite", () => {
|
||||
registerNodeUtils(peer, "node_utils", new NodeUtils(peer));
|
||||
|
||||
// act
|
||||
assert(aqua["file_not_found"]);
|
||||
const res = await aqua["file_not_found"](peer, {});
|
||||
|
||||
// assert
|
||||
@@ -102,6 +107,7 @@ describe("Srv service test suite", () => {
|
||||
registerNodeUtils(peer, "node_utils", new NodeUtils(peer));
|
||||
|
||||
// act
|
||||
assert(aqua["removing_non_exiting"]);
|
||||
const res = await aqua["removing_non_exiting"](peer, {});
|
||||
|
||||
// assert
|
||||
|
@@ -109,7 +109,7 @@ const parseWithSchema = <T extends z.ZodTypeAny>(
|
||||
if (result.success) {
|
||||
return [result.data, null];
|
||||
} else {
|
||||
return [null, result.error.errors[0].message];
|
||||
return [null, result.error.errors[0]?.message ?? "Unknown error"];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -141,7 +141,7 @@ const withSchema: withSchema = <T extends z.ZodTypeAny>(schema: T) => {
|
||||
return (req) => {
|
||||
const [value, message] = parseWithSchema(schema, req);
|
||||
|
||||
if (message != null) {
|
||||
if (message !== null) {
|
||||
return error(message);
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ export const builtInServices: Record<
|
||||
}),
|
||||
|
||||
identity: withSchema(z.array(jsonSchema).max(1))((args) => {
|
||||
return success(args.length === 0 ? {} : args[0]);
|
||||
return success("0" in args ? args[0] : {});
|
||||
}),
|
||||
|
||||
concat: withSchema(z.array(z.array(z.any())))((args) => {
|
||||
|
@@ -33,8 +33,8 @@ export const allowTetraplet = (
|
||||
pred: (tetraplet: SecurityTetraplet) => boolean,
|
||||
): SecurityGuard => {
|
||||
return (params) => {
|
||||
const t = params.tetraplets[0][0];
|
||||
return pred(t);
|
||||
const t = params.tetraplets[0]?.[0];
|
||||
return t !== undefined && pred(t);
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -26,7 +26,7 @@ export function relayOptionToMultiaddr(relay: RelayOptions): Multiaddr {
|
||||
|
||||
const peerId = ma.getPeerId();
|
||||
|
||||
if (peerId == null) {
|
||||
if (peerId === null) {
|
||||
throwHasNoPeerId(ma);
|
||||
}
|
||||
|
||||
|
@@ -33,3 +33,19 @@ export const getErrorMessage = (error: unknown) => {
|
||||
|
||||
return String(error);
|
||||
};
|
||||
|
||||
export function zip<A, B>(arr1: Array<A>, arr2: Array<B>): Array<[A, B]> {
|
||||
if (arr1.length !== arr2.length) {
|
||||
throw new Error(`Array length doesn't match`);
|
||||
}
|
||||
|
||||
const arr = new Array<[A, B]>(arr1.length);
|
||||
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
// Length has been checked above
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
arr[i] = [arr1[i]!, arr2[i]!];
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
Reference in New Issue
Block a user