mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2024-12-11 05:00:18 +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:
parent
ac407c204d
commit
d6008110cf
@ -20,13 +20,7 @@
|
||||
],
|
||||
"ignorePatterns": ["**/node_modules/", "**/dist/", "**/build/", "**/public/"],
|
||||
"rules": {
|
||||
"eqeqeq": [
|
||||
"error",
|
||||
"always",
|
||||
{
|
||||
"null": "ignore"
|
||||
}
|
||||
],
|
||||
"eqeqeq": ["error", "always"],
|
||||
"no-console": ["error"],
|
||||
"arrow-body-style": ["error", "always"],
|
||||
"no-empty": [
|
||||
|
3
.github/release-please/config.json
vendored
3
.github/release-please/config.json
vendored
@ -12,6 +12,7 @@
|
||||
"packages/core/js-client-isomorphic": {},
|
||||
"packages/core/marine-worker": {},
|
||||
"packages/core/aqua-to-js": {},
|
||||
"packages/core/interfaces": {}
|
||||
"packages/core/interfaces": {},
|
||||
"packages/core/npm-aqua-compiler": {}
|
||||
}
|
||||
}
|
||||
|
3
.github/release-please/manifest.json
vendored
3
.github/release-please/manifest.json
vendored
@ -3,5 +3,6 @@
|
||||
"packages/core/marine-worker": "0.5.0",
|
||||
"packages/core/aqua-to-js": "0.3.4",
|
||||
"packages/core/js-client-isomorphic": "0.3.0",
|
||||
"packages/core/interfaces": "0.9.0"
|
||||
"packages/core/interfaces": "0.9.0",
|
||||
"packages/core/npm-aqua-compiler": "0.0.0"
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import assert from "node:assert";
|
||||
import { dirname, join } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
@ -45,6 +46,8 @@ const test = async () => {
|
||||
const browser = await puppeteer.launch();
|
||||
const page = (await browser.pages())[0];
|
||||
|
||||
assert(page);
|
||||
|
||||
page.on("console", (message) => {
|
||||
console.log(`${message.type().toUpperCase()}: ${message.text()}`);
|
||||
});
|
||||
@ -77,7 +80,7 @@ const test = async () => {
|
||||
await browser.close();
|
||||
await stopServer(localServer);
|
||||
|
||||
if (content == null) {
|
||||
if (content === null || content === undefined) {
|
||||
throw new Error("smoke test failed!");
|
||||
}
|
||||
};
|
||||
|
@ -35,7 +35,7 @@ export function genTypeName(
|
||||
const args =
|
||||
item.tag === "labeledProduct" ? Object.values(item.fields) : item.items;
|
||||
|
||||
if (args.length === 1) {
|
||||
if (args.length === 1 && "0" in args) {
|
||||
return genTypeName(args[0], name);
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ export function typeToTs(t: NonArrowType | ArrowType): string {
|
||||
const retType =
|
||||
codomain.tag === "nil"
|
||||
? "void"
|
||||
: codomain.items.length === 1
|
||||
: codomain.items.length === 1 && "0" in codomain.items
|
||||
? typeToTs(codomain.items[0])
|
||||
: typeToTs(codomain);
|
||||
|
||||
|
@ -132,7 +132,7 @@ export class TSTypeGenerator implements TypeGenerator {
|
||||
];
|
||||
|
||||
const registerServiceArgs =
|
||||
srvDef.defaultServiceId == null
|
||||
srvDef.defaultServiceId === undefined
|
||||
? functionOverloadsWithoutDefaultServiceId
|
||||
: functionOverloadsWithDefaultServiceId;
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
import { readFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
||||
@ -86,7 +85,7 @@ export function recursiveRenameLaquaProps(obj: JSONValue): unknown {
|
||||
// Last part of the property separated by "_" is a correct name
|
||||
const refinedProperty = prop.split("_").pop();
|
||||
|
||||
if (refinedProperty == null) {
|
||||
if (refinedProperty === undefined) {
|
||||
throw new Error(`Bad property name: ${prop}.`);
|
||||
}
|
||||
|
||||
@ -95,11 +94,15 @@ export function recursiveRenameLaquaProps(obj: JSONValue): unknown {
|
||||
}
|
||||
}
|
||||
|
||||
assert(accessProp in obj);
|
||||
const laquaProp = obj[accessProp];
|
||||
|
||||
if (laquaProp === undefined) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[accessProp]: recursiveRenameLaquaProps(obj[accessProp]),
|
||||
[accessProp]: recursiveRenameLaquaProps(laquaProp),
|
||||
};
|
||||
}, {});
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
|
@ -36,7 +36,7 @@ export const fetchResource: FetchResourceFn = async (pkg, assetPath) => {
|
||||
|
||||
const packagePath = matches?.[0];
|
||||
|
||||
if (packagePath == null) {
|
||||
if (packagePath === undefined) {
|
||||
throw new Error(`Cannot find dependency ${name} in path ${posixPath}`);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ const toExpose = {
|
||||
serviceId: string,
|
||||
envs?: Env,
|
||||
): Promise<void> => {
|
||||
if (controlModule == null) {
|
||||
if (controlModule === undefined) {
|
||||
throw new Error(
|
||||
"MarineJS is not initialized. To initialize call `init` function",
|
||||
);
|
||||
@ -135,7 +135,7 @@ const toExpose = {
|
||||
) => {
|
||||
const srv = marineServices.get(serviceId);
|
||||
|
||||
if (srv == null) {
|
||||
if (srv === undefined) {
|
||||
throw new Error(`service with id=${serviceId} not found`);
|
||||
}
|
||||
|
||||
|
29
packages/core/npm-aqua-compiler/package.json
Normal file
29
packages/core/npm-aqua-compiler/package.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@fluencelabs/npm-aqua-compiler",
|
||||
"version": "0.0.0",
|
||||
"description": "Tool for converting npm imports to aqua compiler input",
|
||||
"types": "./dist/imports.d.ts",
|
||||
"exports": {
|
||||
".": "./dist/imports.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm i --prefix ./test/transitive-deps/project && vitest run",
|
||||
"build": "tsc"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"keywords": [],
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@npmcli/arborist": "^7.2.1",
|
||||
"treeverse": "3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/npmcli__arborist": "5.6.5",
|
||||
"@types/treeverse": "3.0.4",
|
||||
"vitest": "0.34.6"
|
||||
}
|
||||
}
|
115
packages/core/npm-aqua-compiler/src/imports.spec.ts
Normal file
115
packages/core/npm-aqua-compiler/src/imports.spec.ts
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* 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 { join } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { assert, describe, expect, it } from "vitest";
|
||||
|
||||
import { gatherImportsFromNpm } from "./imports.js";
|
||||
|
||||
describe("imports", () => {
|
||||
/**
|
||||
* NOTE: This test expects that `npm i` is run
|
||||
* inside `./__test__/data/transitive-deps/project` folder
|
||||
*/
|
||||
it("should resolve transitive dependencies", async () => {
|
||||
const npmProjectDirPath = "./test/transitive-deps/project";
|
||||
const aquaToCompileDirPath = "./test/transitive-deps/aqua-project";
|
||||
const globalImports = ["./.fluence/aqua"];
|
||||
|
||||
const expectedResolution: Record<
|
||||
string,
|
||||
Record<string, string[] | string>
|
||||
> = {
|
||||
[aquaToCompileDirPath]: {
|
||||
"": globalImports,
|
||||
A: "./A",
|
||||
B: "./B",
|
||||
},
|
||||
"./A": {
|
||||
C: "./C",
|
||||
D: "./D",
|
||||
},
|
||||
"./B": {
|
||||
C: "./B/C",
|
||||
D: "./B/D",
|
||||
},
|
||||
"./C": {
|
||||
D: "./C/D",
|
||||
},
|
||||
"./B/C": {
|
||||
D: "./B/C/D",
|
||||
},
|
||||
};
|
||||
|
||||
const prefix = join(
|
||||
fileURLToPath(new URL("./", import.meta.url)),
|
||||
"..",
|
||||
"test",
|
||||
"transitive-deps",
|
||||
"project",
|
||||
);
|
||||
|
||||
const buildResolutionKey = (str: string) => {
|
||||
return (
|
||||
"./" +
|
||||
str
|
||||
.slice(prefix.length)
|
||||
.split("/node_modules/")
|
||||
.filter(Boolean)
|
||||
.join("/")
|
||||
);
|
||||
};
|
||||
|
||||
const imports = await gatherImportsFromNpm({
|
||||
npmProjectDirPath,
|
||||
aquaToCompileDirPath,
|
||||
globalImports,
|
||||
});
|
||||
|
||||
expect(Object.keys(imports).length).toBe(
|
||||
Object.keys(expectedResolution).length,
|
||||
);
|
||||
|
||||
Object.entries(imports).forEach(([key, value]) => {
|
||||
const resolutionKey =
|
||||
key === aquaToCompileDirPath ? key : buildResolutionKey(key);
|
||||
|
||||
const resolutionValues = expectedResolution[resolutionKey];
|
||||
|
||||
assert(resolutionValues);
|
||||
|
||||
expect(Object.keys(value).length).toBe(
|
||||
Object.keys(resolutionValues).length,
|
||||
);
|
||||
|
||||
for (const [dep, path] of Object.entries(value)) {
|
||||
if (Array.isArray(path)) {
|
||||
expect(dep).toBe("");
|
||||
expect(expectedResolution[resolutionKey]).toHaveProperty(dep, path);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
expect(expectedResolution[resolutionKey]).toHaveProperty(
|
||||
dep,
|
||||
buildResolutionKey(path),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
92
packages/core/npm-aqua-compiler/src/imports.ts
Normal file
92
packages/core/npm-aqua-compiler/src/imports.ts
Normal file
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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 Arborist from "@npmcli/arborist";
|
||||
import { breadth } from "treeverse";
|
||||
|
||||
export interface GatherImportsArg {
|
||||
npmProjectDirPath: string;
|
||||
aquaToCompileDirPath?: string; // Default: npmProjectDirPath
|
||||
globalImports?: string[];
|
||||
}
|
||||
|
||||
export type GatherImportsResult = Record<
|
||||
string,
|
||||
Record<string, string[] | string>
|
||||
>;
|
||||
|
||||
export async function gatherImportsFromNpm({
|
||||
npmProjectDirPath,
|
||||
aquaToCompileDirPath,
|
||||
globalImports = [],
|
||||
}: GatherImportsArg): Promise<GatherImportsResult> {
|
||||
const arborist = new Arborist({ path: npmProjectDirPath });
|
||||
const tree = await arborist.loadActual();
|
||||
|
||||
/**
|
||||
* Traverse dependency tree to construct map
|
||||
* (real path of a package) -> (real paths of its immediate dependencies)
|
||||
*/
|
||||
const result: GatherImportsResult = {};
|
||||
|
||||
breadth({
|
||||
tree,
|
||||
getChildren(node) {
|
||||
const deps: Arborist.Node[] = [];
|
||||
|
||||
for (const edge of node.edgesOut.values()) {
|
||||
// Skip dependencies that are not installed.
|
||||
|
||||
// Looks like Arborist type is incorrect here, so it's possible to have null here
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (edge.to === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: Any errors in edge are ignored.
|
||||
const dep = edge.to;
|
||||
|
||||
// Gather dependencies to traverse them.
|
||||
deps.push(dep);
|
||||
|
||||
// Root node should have top-level property pointed to aqua dependency folder
|
||||
if (node.isRoot) {
|
||||
const aquaDepPath = aquaToCompileDirPath ?? npmProjectDirPath;
|
||||
|
||||
result[aquaDepPath] = {
|
||||
...(result[aquaDepPath] ??
|
||||
(globalImports.length > 0
|
||||
? {
|
||||
"": globalImports,
|
||||
}
|
||||
: {})),
|
||||
[dep.name]: dep.realpath,
|
||||
};
|
||||
} else {
|
||||
// Gather dependencies real paths.
|
||||
result[node.realpath] = {
|
||||
...(result[node.realpath] ?? {}),
|
||||
[dep.name]: dep.realpath,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return deps;
|
||||
},
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/A-0.1.0.tgz
Normal file
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/A-0.1.0.tgz
Normal file
Binary file not shown.
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/B-0.1.0.tgz
Normal file
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/B-0.1.0.tgz
Normal file
Binary file not shown.
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/C-0.1.0.tgz
Normal file
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/C-0.1.0.tgz
Normal file
Binary file not shown.
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/C-0.2.0.tgz
Normal file
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/C-0.2.0.tgz
Normal file
Binary file not shown.
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/D-0.1.0.tgz
Normal file
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/D-0.1.0.tgz
Normal file
Binary file not shown.
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/D-0.2.0.tgz
Normal file
BIN
packages/core/npm-aqua-compiler/test/transitive-deps/D-0.2.0.tgz
Normal file
Binary file not shown.
@ -0,0 +1,8 @@
|
||||
use "B.aqua"
|
||||
|
||||
func versionAC() -> string:
|
||||
<- A.versionC()
|
||||
|
||||
func versionBC() -> string:
|
||||
<- B.versionC()
|
||||
|
70
packages/core/npm-aqua-compiler/test/transitive-deps/project/package-lock.json
generated
Normal file
70
packages/core/npm-aqua-compiler/test/transitive-deps/project/package-lock.json
generated
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "project",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "project",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"A": "file:../A-0.1.0.tgz",
|
||||
"B": "file:../B-0.1.0.tgz"
|
||||
}
|
||||
},
|
||||
"node_modules/A": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "file:../A-0.1.0.tgz",
|
||||
"integrity": "sha512-H0nhbQVQxPm3VwXYiePLJ0oyHa2FxPNNPjOcTdz3YWvIoE0/dZFJ1yrqig7fkrETYEYfLuVJaN0yg1BX/HAScg==",
|
||||
"dependencies": {
|
||||
"C": "file:./C-0.2.0.tgz",
|
||||
"D": "file:./D-0.1.0.tgz"
|
||||
}
|
||||
},
|
||||
"node_modules/B": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "file:../B-0.1.0.tgz",
|
||||
"integrity": "sha512-u6n6V5KlxIN/GwRQt82gZQAPwYi0OzqQ2wr8ufmygreLK3fPIfO49f13qagbGXaYiRxN9effXaPqZlMIyTygng==",
|
||||
"dependencies": {
|
||||
"C": "file:./C-0.1.0.tgz",
|
||||
"D": "file:./D-0.2.0.tgz"
|
||||
}
|
||||
},
|
||||
"node_modules/B/node_modules/C": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "file:../C-0.1.0.tgz",
|
||||
"integrity": "sha512-zvzWgHLm+ptWwysP+dJItnogVSca/jvHegWmwi6NmmHFO/wTqlGrMPnC2dEkpXDJBU4X1bUjevFh0q3Xe9e0MA==",
|
||||
"dependencies": {
|
||||
"D": "file:./D-0.1.0.tgz"
|
||||
}
|
||||
},
|
||||
"node_modules/B/node_modules/C/node_modules/D": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "file:../D-0.1.0.tgz",
|
||||
"integrity": "sha512-1rlKmuyzHSGTt9tBhEBY3j7gZvMBg0LnZMogZSucmX4gww4l0+HPQwBIPjJpqOspE2ND8PcLymQoiw06xWXn0g=="
|
||||
},
|
||||
"node_modules/B/node_modules/D": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "file:../D-0.2.0.tgz",
|
||||
"integrity": "sha512-7h1TUU8j60q6BZ0Wq/xDZOUf6iS0S4SgL/lgXOaiyxN76q7ld8Rx/qIxtGKmrWh65v5cjvAG5asbMEkXb6DuYQ=="
|
||||
},
|
||||
"node_modules/C": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "file:../C-0.2.0.tgz",
|
||||
"integrity": "sha512-uNqb8p69kuombZsb3xI/ygeL94WHpwkGR9/GRWgdg+01iKGsRMaZgL5up0UG7D/9DW7NQBozZG8ITPQ8DLgwSQ==",
|
||||
"dependencies": {
|
||||
"D": "file:./D-0.2.0.tgz"
|
||||
}
|
||||
},
|
||||
"node_modules/C/node_modules/D": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "file:../D-0.2.0.tgz",
|
||||
"integrity": "sha512-7h1TUU8j60q6BZ0Wq/xDZOUf6iS0S4SgL/lgXOaiyxN76q7ld8Rx/qIxtGKmrWh65v5cjvAG5asbMEkXb6DuYQ=="
|
||||
},
|
||||
"node_modules/D": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "file:../D-0.1.0.tgz",
|
||||
"integrity": "sha512-1rlKmuyzHSGTt9tBhEBY3j7gZvMBg0LnZMogZSucmX4gww4l0+HPQwBIPjJpqOspE2ND8PcLymQoiw06xWXn0g=="
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "project",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"A": "file:../A-0.1.0.tgz",
|
||||
"B": "file:../B-0.1.0.tgz"
|
||||
}
|
||||
}
|
9
packages/core/npm-aqua-compiler/tsconfig.json
Normal file
9
packages/core/npm-aqua-compiler/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "src/**/__test__"]
|
||||
}
|
1150
pnpm-lock.yaml
generated
1150
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -7,8 +7,7 @@
|
||||
"module": "ESNext",
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"moduleResolution": "nodenext",
|
||||
"noUncheckedIndexedAccess": false
|
||||
"moduleResolution": "nodenext"
|
||||
},
|
||||
"files": ["reset.d.ts"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user