Skip to content

Commit d600811

Browse files
author
Akim
authored
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
1 parent ac407c2 commit d600811

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1652
-153
lines changed

.eslintrc.json

+1-7
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,7 @@
2020
],
2121
"ignorePatterns": ["**/node_modules/", "**/dist/", "**/build/", "**/public/"],
2222
"rules": {
23-
"eqeqeq": [
24-
"error",
25-
"always",
26-
{
27-
"null": "ignore"
28-
}
29-
],
23+
"eqeqeq": ["error", "always"],
3024
"no-console": ["error"],
3125
"arrow-body-style": ["error", "always"],
3226
"no-empty": [

.github/release-please/config.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"packages/core/js-client-isomorphic": {},
1313
"packages/core/marine-worker": {},
1414
"packages/core/aqua-to-js": {},
15-
"packages/core/interfaces": {}
15+
"packages/core/interfaces": {},
16+
"packages/core/npm-aqua-compiler": {}
1617
}
1718
}

.github/release-please/manifest.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
"packages/core/marine-worker": "0.5.0",
44
"packages/core/aqua-to-js": "0.3.4",
55
"packages/core/js-client-isomorphic": "0.3.0",
6-
"packages/core/interfaces": "0.9.0"
6+
"packages/core/interfaces": "0.9.0",
7+
"packages/core/npm-aqua-compiler": "0.0.0"
78
}

packages/@tests/smoke/web/src/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import assert from "node:assert";
1718
import { dirname, join } from "path";
1819
import { fileURLToPath } from "url";
1920

@@ -45,6 +46,8 @@ const test = async () => {
4546
const browser = await puppeteer.launch();
4647
const page = (await browser.pages())[0];
4748

49+
assert(page);
50+
4851
page.on("console", (message) => {
4952
console.log(`${message.type().toUpperCase()}: ${message.text()}`);
5053
});
@@ -77,7 +80,7 @@ const test = async () => {
7780
await browser.close();
7881
await stopServer(localServer);
7982

80-
if (content == null) {
83+
if (content === null || content === undefined) {
8184
throw new Error("smoke test failed!");
8285
}
8386
};

packages/core/aqua-to-js/src/common.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function genTypeName(
3535
const args =
3636
item.tag === "labeledProduct" ? Object.values(item.fields) : item.items;
3737

38-
if (args.length === 1) {
38+
if (args.length === 1 && "0" in args) {
3939
return genTypeName(args[0], name);
4040
}
4141

@@ -112,7 +112,7 @@ export function typeToTs(t: NonArrowType | ArrowType): string {
112112
const retType =
113113
codomain.tag === "nil"
114114
? "void"
115-
: codomain.items.length === 1
115+
: codomain.items.length === 1 && "0" in codomain.items
116116
? typeToTs(codomain.items[0])
117117
: typeToTs(codomain);
118118

packages/core/aqua-to-js/src/generate/interfaces.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export class TSTypeGenerator implements TypeGenerator {
132132
];
133133

134134
const registerServiceArgs =
135-
srvDef.defaultServiceId == null
135+
srvDef.defaultServiceId === undefined
136136
? functionOverloadsWithoutDefaultServiceId
137137
: functionOverloadsWithDefaultServiceId;
138138

packages/core/aqua-to-js/src/utils.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
import assert from "assert";
1817
import { readFile } from "fs/promises";
1918
import { join } from "path";
2019

@@ -86,7 +85,7 @@ export function recursiveRenameLaquaProps(obj: JSONValue): unknown {
8685
// Last part of the property separated by "_" is a correct name
8786
const refinedProperty = prop.split("_").pop();
8887

89-
if (refinedProperty == null) {
88+
if (refinedProperty === undefined) {
9089
throw new Error(`Bad property name: ${prop}.`);
9190
}
9291

@@ -95,11 +94,15 @@ export function recursiveRenameLaquaProps(obj: JSONValue): unknown {
9594
}
9695
}
9796

98-
assert(accessProp in obj);
97+
const laquaProp = obj[accessProp];
98+
99+
if (laquaProp === undefined) {
100+
return acc;
101+
}
99102

100103
return {
101104
...acc,
102-
[accessProp]: recursiveRenameLaquaProps(obj[accessProp]),
105+
[accessProp]: recursiveRenameLaquaProps(laquaProp),
103106
};
104107
}, {});
105108
}

packages/core/aqua-to-js/tsconfig.json

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"extends": "../../../tsconfig.json",
33
"compilerOptions": {
4-
"esModuleInterop": true,
54
"resolveJsonModule": true,
65
"outDir": "./dist"
76
},

packages/core/js-client-isomorphic/src/fetchers/node.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const fetchResource: FetchResourceFn = async (pkg, assetPath) => {
3636

3737
const packagePath = matches?.[0];
3838

39-
if (packagePath == null) {
39+
if (packagePath === undefined) {
4040
throw new Error(`Cannot find dependency ${name} in path ${posixPath}`);
4141
}
4242

packages/core/js-client/src/api.spec.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { fileURLToPath } from "url";
1818

1919
import { compileFromPath } from "@fluencelabs/aqua-api";
2020
import { ServiceDef } from "@fluencelabs/interfaces";
21-
import { describe, expect, it } from "vitest";
21+
import { assert, describe, expect, it } from "vitest";
2222

2323
import { v5_registerService } from "./api.js";
2424
import { callAquaFunction } from "./compilerSupport/callFunction.js";
@@ -65,8 +65,12 @@ describe("User API methods", () => {
6565

6666
const typedServices: Record<string, ServiceDef> = services;
6767

68+
assert("demoCalc" in functions);
69+
6870
const { script } = functions["demoCalc"];
6971

72+
assert("Calc" in typedServices);
73+
7074
v5_registerService([peer, "calc", calcService], {
7175
defaultServiceId: "calc",
7276
functions: typedServices["Calc"].functions,

packages/core/js-client/src/api.ts

+41-37
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ import { z } from "zod";
2626
import { CallAquaFunctionConfig } from "./compilerSupport/callFunction.js";
2727
import {
2828
aqua2js,
29-
SchemaValidationError,
3029
js2aqua,
30+
SchemaValidationError,
3131
wrapJsFunction,
3232
} from "./compilerSupport/conversions.js";
3333
import { ServiceImpl, UserServiceImpl } from "./compilerSupport/types.js";
3434
import { FluencePeer } from "./jsPeer/FluencePeer.js";
35+
import { zip } from "./util/utils.js";
3536

3637
import { callAquaFunction, Fluence, registerService } from "./index.js";
3738

@@ -74,59 +75,57 @@ export const v5_callFunction = async (
7475
);
7576
}
7677

77-
const argNames = Object.keys(
78-
def.arrow.domain.tag === "nil" ? [] : def.arrow.domain.fields,
79-
);
78+
const schemaFunctionArgs: Record<string, FunctionArg> =
79+
def.arrow.domain.tag === "nil" ? {} : def.arrow.domain.fields;
8080

81-
const schemaArgCount = argNames.length;
81+
const schemaFunctionArgEntries = Object.entries(schemaFunctionArgs);
8282

83-
type FunctionArg = SimpleTypes | ArrowWithoutCallbacks;
83+
const schemaArgCount = schemaFunctionArgEntries.length;
8484

85-
const schemaFunctionArgs: Record<string, FunctionArg> =
86-
def.arrow.domain.tag === "nil" ? {} : def.arrow.domain.fields;
85+
type FunctionArg = SimpleTypes | ArrowWithoutCallbacks;
8786

8887
// if there are more args than expected in schema (schemaArgCount) then last arg is config
8988
const config = schemaArgCount < rest.length ? rest.pop() : undefined;
9089

9190
validateAquaConfig(config);
9291

9392
const callArgs = Object.fromEntries<JSONValue | ServiceImpl[string]>(
94-
rest.slice(0, schemaArgCount).map((arg, i) => {
95-
const argName = argNames[i];
96-
const argSchema = schemaFunctionArgs[argName];
93+
zip(rest.slice(0, schemaArgCount), schemaFunctionArgEntries).map(
94+
([arg, [argName, argType]]) => {
95+
if (argType.tag === "arrow") {
96+
if (typeof arg !== "function") {
97+
throw new SchemaValidationError(
98+
[argName],
99+
argType,
100+
"function",
101+
arg,
102+
);
103+
}
104+
105+
return [argName, wrapJsFunction(arg, argType)];
106+
}
97107

98-
if (argSchema.tag === "arrow") {
99-
if (typeof arg !== "function") {
108+
if (typeof arg === "function") {
100109
throw new SchemaValidationError(
101110
[argName],
102-
argSchema,
103-
"function",
111+
argType,
112+
"non-function value",
104113
arg,
105114
);
106115
}
107116

108-
return [argName, wrapJsFunction(arg, argSchema)];
109-
}
110-
111-
if (typeof arg === "function") {
112-
throw new SchemaValidationError(
113-
[argName],
114-
argSchema,
115-
"non-function value",
116-
arg,
117-
);
118-
}
119-
120-
return [argName, js2aqua(arg, argSchema, { path: [def.functionName] })];
121-
}),
117+
return [argName, js2aqua(arg, argType, { path: [def.functionName] })];
118+
},
119+
),
122120
);
123121

124122
const returnTypeVoid =
125123
def.arrow.codomain.tag === "nil" || def.arrow.codomain.items.length === 0;
126124

127125
const returnSchema =
128126
def.arrow.codomain.tag === "unlabeledProduct" &&
129-
def.arrow.codomain.items.length === 1
127+
def.arrow.codomain.items.length === 1 &&
128+
"0" in def.arrow.codomain.items
130129
? def.arrow.codomain.items[0]
131130
: def.arrow.codomain;
132131

@@ -145,7 +144,7 @@ export const v5_callFunction = async (
145144
};
146145

147146
const getDefaultPeer = (): FluencePeer => {
148-
if (Fluence.defaultClient == null) {
147+
if (Fluence.defaultClient === undefined) {
149148
throw new Error(
150149
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
151150
);
@@ -155,7 +154,7 @@ const getDefaultPeer = (): FluencePeer => {
155154
};
156155

157156
const getDefaultServiceId = (def: ServiceDef) => {
158-
if (def.defaultServiceId == null) {
157+
if (def.defaultServiceId === undefined) {
159158
throw new Error("Service ID is not provided");
160159
}
161160

@@ -204,13 +203,18 @@ export const v5_registerService = (
204203

205204
// Wrapping service functions, selecting only those listed in schema, to convert their args js -> aqua and backwards
206205
const wrappedServiceImpl = Object.fromEntries(
207-
Object.keys(serviceSchema).map((schemaKey) => {
206+
Object.entries(serviceSchema).map(([schemaKey, schemaValue]) => {
207+
const serviceImplValue = serviceImpl[schemaKey];
208+
209+
if (serviceImplValue === undefined) {
210+
throw new Error(
211+
`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`,
212+
);
213+
}
214+
208215
return [
209216
schemaKey,
210-
wrapJsFunction(
211-
serviceImpl[schemaKey].bind(serviceImpl),
212-
serviceSchema[schemaKey],
213-
),
217+
wrapJsFunction(serviceImplValue.bind(serviceImpl), schemaValue),
214218
] as const;
215219
}),
216220
);

packages/core/js-client/src/clientPeer/ClientPeer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export const makeClientPeerConfig = async (
6060
relayConfig: {
6161
peerId: keyPair.getLibp2pPeerId(),
6262
relayAddress: relayAddress,
63-
...(config.connectionOptions?.dialTimeoutMs != null
63+
...(config.connectionOptions?.dialTimeoutMs !== undefined
6464
? {
6565
dialTimeout: config.connectionOptions.dialTimeoutMs,
6666
}

packages/core/js-client/src/clientPeer/__test__/client.spec.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import { JSONValue } from "@fluencelabs/interfaces";
18-
import { it, describe, expect } from "vitest";
18+
import { it, describe, expect, assert } from "vitest";
1919

2020
import { ExpirationError } from "../../jsPeer/errors.js";
2121
import { CallServiceData } from "../../jsServiceHost/interfaces.js";
@@ -103,6 +103,7 @@ describe("FluenceClient usage test suite", () => {
103103
callback: {
104104
callback: (args): undefined => {
105105
const [val] = args;
106+
assert(val);
106107
resolve(val);
107108
},
108109
error: (args): undefined => {

packages/core/js-client/src/clientPeer/__test__/connection.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ export const nodes = [
2020
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
2121
peerId: "12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR",
2222
},
23-
];
23+
] as const;
2424

2525
export const RELAY = nodes[0].multiaddr;

packages/core/js-client/src/clientPeer/checkConnection.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export const checkConnection = async (
8686
const [val] = args;
8787

8888
setTimeout(() => {
89-
resolve(val);
89+
resolve(val ?? null);
9090
}, 0);
9191

9292
return {};

packages/core/js-client/src/compilerSupport/__test__/conversion.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const structs = [
7676
c: [null, 2],
7777
},
7878
},
79-
];
79+
] as const;
8080

8181
const labeledProduct2 = {
8282
tag: "labeledProduct",
@@ -167,7 +167,7 @@ const nestedStructs = [
167167
c: [],
168168
},
169169
},
170-
];
170+
] as const;
171171

172172
interface ConversionTestArgs {
173173
aqua: JSONValue;

0 commit comments

Comments
 (0)