chore(js-client)!: Simplify/optimize js-client and update README [fixes DXJ-490] (#366)

* Update README

* Improve build experience

* Fix eslint

* Fix eslint

* Fix eslint

* Fix tooling

* Fix formatting

* Fix formatting

* Fix test resource name

* Remove async

* Fix comments

* Add ts-check

* Add new line

* Fix arg

* add todo's

* Fix lint

* Fix typo

* Type module

* Add deps to isomorphic package

* Extract function type

* Fix newline

* Remove private

* Use prepare hook instead of postinstall
This commit is contained in:
Akim
2023-10-25 19:02:42 +07:00
committed by GitHub
parent 1266a90737
commit f9abc6419c
24 changed files with 576 additions and 278 deletions

View File

@ -1,69 +0,0 @@
/**
* 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.
*/
interface PackageJsonContent {
dependencies: Record<string, string | undefined>;
devDependencies: Record<string, string | undefined>;
}
// This will be substituted in build phase
const packageJsonContentString = `__PACKAGE_JSON_CONTENT__`;
let parsedPackageJsonContent: PackageJsonContent | undefined;
/**
* @param pkg name of package
* @param assetPath path of required asset in given package
* @param root CDN domain in browser or file system root in node
*/
export async function fetchResource(
pkg: string,
assetPath: string,
root: string,
) {
const packageJsonContent =
parsedPackageJsonContent ??
// TODO: Should be validated
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
(parsedPackageJsonContent = JSON.parse(
packageJsonContentString,
) as PackageJsonContent);
const version =
packageJsonContent.dependencies[pkg] ??
packageJsonContent.devDependencies[pkg];
if (version === undefined) {
const availableDeps = [
...Object.keys(packageJsonContent.dependencies),
...Object.keys(packageJsonContent.devDependencies),
];
throw new Error(
`Cannot find version of ${pkg} in package.json. Available versions: ${availableDeps.join(
",",
)}`,
);
}
const refinedAssetPath = assetPath.startsWith("/")
? assetPath.slice(1)
: assetPath;
const url = new globalThis.URL(`${pkg}@${version}/` + refinedAssetPath, root);
return fetch(url).catch(() => {
throw new Error(`Cannot fetch from ${url.toString()}`);
});
}

View File

@ -1,70 +0,0 @@
/**
* 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 fs from "fs";
import module from "module";
import path from "path";
/**
* @param pkg name of package
* @param assetPath path of required asset in given package
* @param root CDN domain in browser or file system root in node
*/
export async function fetchResource(
pkg: string,
assetPath: string,
root: string,
) {
// TODO: `root` will be handled somehow in the future. For now, we use filesystem root where js-client is running;
root = "/";
const require = module.createRequire(import.meta.url);
const packagePathIndex = require.resolve(pkg);
// Ensure that windows path is converted to posix path. So we can find a package
const posixPath = packagePathIndex.split(path.sep).join(path.posix.sep);
const matches = new RegExp(`(.+${pkg})`).exec(posixPath);
const packagePath = matches?.[0];
if (packagePath == null) {
throw new Error(`Cannot find dependency ${pkg} in path ${posixPath}`);
}
const pathToResource = path.join(root, packagePath, assetPath);
const file = await new Promise<ArrayBuffer>((resolve, reject) => {
// Cannot use 'fs/promises' with current vite config. This module is not polyfilled by default.
fs.readFile(pathToResource, (err, data) => {
if (err != null) {
reject(err);
return;
}
resolve(data);
});
});
return new Response(file, {
headers: {
"Content-type": assetPath.endsWith(".wasm")
? "application/wasm"
: assetPath.endsWith(".js")
? "application/javascript"
: "application/text",
},
});
}

View File

@ -14,12 +14,9 @@
* limitations under the License.
*/
import module from "module";
import path from "path";
import process from "process";
import url from "url";
import { BlobWorker, Worker } from "threads/master";
import type { VersionedPackage } from "@fluencelabs/js-client-isomorphic";
import { fetchResource } from "@fluencelabs/js-client-isomorphic/fetcher";
import { getWorker } from "@fluencelabs/js-client-isomorphic/worker-resolver";
import { ClientPeer, makeClientPeerConfig } from "./clientPeer/ClientPeer.js";
import {
@ -30,36 +27,26 @@ import {
import { callAquaFunction } from "./compilerSupport/callFunction.js";
import { registerService } from "./compilerSupport/registerService.js";
import { MarineBackgroundRunner } from "./marine/worker/index.js";
import { doRegisterNodeUtils } from "./services/NodeUtils.js";
import { fetchResource } from "#fetcher";
import versions from "./versions.js";
const DEFAULT_CDN_URL = "https://unpkg.com";
const isNode =
// process.release is undefined in browser env
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
typeof process !== "undefined" && process.release?.name === "node";
const getVersionedPackage = (pkg: keyof typeof versions): VersionedPackage => {
return {
name: pkg,
version: versions[pkg],
};
};
const createClient = async (
relay: RelayOptions,
config: ClientConfig,
config: ClientConfig = {},
): Promise<ClientPeer> => {
const CDNUrl = config.CDNUrl ?? DEFAULT_CDN_URL;
const fetchWorkerCode = async () => {
const resource = await fetchResource(
"@fluencelabs/marine-worker",
"/dist/browser/marine-worker.umd.cjs",
CDNUrl,
);
return resource.text();
};
const fetchMarineJsWasm = async () => {
const resource = await fetchResource(
"@fluencelabs/marine-js",
getVersionedPackage("@fluencelabs/marine-js"),
"/dist/marine-js.wasm",
CDNUrl,
);
@ -69,7 +56,7 @@ const createClient = async (
const fetchAvmWasm = async () => {
const resource = await fetchResource(
"@fluencelabs/avm",
getVersionedPackage("@fluencelabs/avm"),
"/dist/avm.wasm",
CDNUrl,
);
@ -83,25 +70,10 @@ const createClient = async (
const marine = new MarineBackgroundRunner(
{
async getValue() {
if (isNode) {
const require = module.createRequire(import.meta.url);
const pathToThisFile = path.dirname(
url.fileURLToPath(import.meta.url),
);
const pathToWorker = require.resolve("@fluencelabs/marine-worker");
const relativePathToWorker = path.relative(
pathToThisFile,
pathToWorker,
);
return new Worker(relativePathToWorker);
} else {
const workerCode = await fetchWorkerCode();
return BlobWorker.fromText(workerCode);
}
return getWorker(
getVersionedPackage("@fluencelabs/marine-worker"),
CDNUrl,
);
},
start() {
return Promise.resolve(undefined);
@ -141,9 +113,10 @@ const createClient = async (
const client = new ClientPeer(peerConfig, relayConfig, keyPair, marine);
if (isNode) {
doRegisterNodeUtils(client);
}
// TODO: Support node specific utils
// if (isNode) {
// doRegisterNodeUtils(client);
// }
await client.connect();
return client;
@ -154,7 +127,7 @@ const createClient = async (
*/
interface FluencePublicApi {
defaultClient: ClientPeer | undefined;
connect: (relay: RelayOptions, config: ClientConfig) => Promise<void>;
connect: (relay: RelayOptions, config?: ClientConfig) => Promise<void>;
disconnect: () => Promise<void>;
onConnectionStateChange: (
handler: (state: ConnectionState) => void,
@ -205,37 +178,8 @@ export const Fluence: FluencePublicApi = {
},
};
export type { CallParams } from "@fluencelabs/interfaces";
export type { ClientConfig, IFluenceClient } from "./clientPeer/types.js";
export type {
ArrayType,
ArrowType,
ArrowWithCallbacks,
ArrowWithoutCallbacks,
BottomType,
FunctionCallConstants,
FunctionCallDef,
LabeledProductType,
NilType,
NonArrowType,
OptionType,
ProductType,
ScalarNames,
ScalarType,
ServiceDef,
StructType,
TopType,
UnlabeledProductType,
CallAquaFunctionType,
CallAquaFunctionArgs,
PassedArgs,
FnConfig,
RegisterServiceType,
RegisterServiceArgs,
} from "@fluencelabs/interfaces";
export { v5_callFunction, v5_registerService } from "./api.js";
// @ts-expect-error Writing to global object like this prohibited by ts
@ -249,9 +193,13 @@ globalThis.fluence = {
};
export { createClient, callAquaFunction, registerService };
// Deprecated exports. Later they will be exposed only under js-client/keypair path
export {
KeyPair,
fromBase64Sk,
fromBase58Sk,
fromOpts,
} from "./keypair/index.js";
export * from "./network.js";

View File

@ -0,0 +1,187 @@
/**
* 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.
*/
export type Relay = { peerId: string; multiaddr: string };
export const stage: Relay[] = [
{
multiaddr:
"/dns4/0-stage.fluence.dev/tcp/9000/wss/p2p/12D3KooWDcpWuyrMTDinqNgmXAuRdfd2mTdY9VoXZSAet2pDzh6r",
peerId: "12D3KooWDcpWuyrMTDinqNgmXAuRdfd2mTdY9VoXZSAet2pDzh6r",
},
{
multiaddr:
"/dns4/1-stage.fluence.dev/tcp/9000/wss/p2p/12D3KooWHCJbJKGDfCgHSoCuK9q4STyRnVveqLoXAPBbXHTZx9Cv",
peerId: "12D3KooWHCJbJKGDfCgHSoCuK9q4STyRnVveqLoXAPBbXHTZx9Cv",
},
{
multiaddr:
"/dns4/2-stage.fluence.dev/tcp/9000/wss/p2p/12D3KooWMigkP4jkVyufq5JnDJL6nXvyjeaDNpRfEZqQhsG3sYCU",
peerId: "12D3KooWMigkP4jkVyufq5JnDJL6nXvyjeaDNpRfEZqQhsG3sYCU",
},
{
multiaddr:
"/dns4/3-stage.fluence.dev/tcp/9000/wss/p2p/12D3KooWMMGdfVEJ1rWe1nH1nehYDzNEHhg5ogdfiGk88AupCMnf",
peerId: "12D3KooWMMGdfVEJ1rWe1nH1nehYDzNEHhg5ogdfiGk88AupCMnf",
},
{
multiaddr:
"/dns4/4-stage.fluence.dev/tcp/9000/wss/p2p/12D3KooWJ4bTHirdTFNZpCS72TAzwtdmavTBkkEXtzo6wHL25CtE",
peerId: "12D3KooWJ4bTHirdTFNZpCS72TAzwtdmavTBkkEXtzo6wHL25CtE",
},
{
multiaddr:
"/dns4/5-stage.fluence.dev/tcp/9000/wss/p2p/12D3KooWAKNos2KogexTXhrkMZzFYpLHuWJ4PgoAhurSAv7o5CWA",
peerId: "12D3KooWAKNos2KogexTXhrkMZzFYpLHuWJ4PgoAhurSAv7o5CWA",
},
];
export const testNet: Relay[] = [
{
multiaddr:
"/dns4/0-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz",
peerId: "12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz",
},
{
multiaddr:
"/dns4/1-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9",
peerId: "12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9",
},
{
multiaddr:
"/dns4/2-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er",
peerId: "12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er",
},
{
multiaddr:
"/dns4/3-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb",
peerId: "12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb",
},
{
multiaddr:
"/dns4/4-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB",
peerId: "12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB",
},
{
multiaddr:
"/dns4/5-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWCKCeqLPSgMnDjyFsJuWqREDtKNHx1JEBiwaMXhCLNTRb",
peerId: "12D3KooWCKCeqLPSgMnDjyFsJuWqREDtKNHx1JEBiwaMXhCLNTRb",
},
{
multiaddr:
"/dns4/6-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH",
peerId: "12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH",
},
{
multiaddr:
"/dns4/7-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWBSdm6TkqnEFrgBuSkpVE3dR1kr6952DsWQRNwJZjFZBv",
peerId: "12D3KooWBSdm6TkqnEFrgBuSkpVE3dR1kr6952DsWQRNwJZjFZBv",
},
{
multiaddr:
"/dns4/8-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H",
peerId: "12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H",
},
{
multiaddr:
"/dns4/9-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWF7gjXhQ4LaKj6j7ntxsPpGk34psdQicN2KNfBi9bFKXg",
peerId: "12D3KooWF7gjXhQ4LaKj6j7ntxsPpGk34psdQicN2KNfBi9bFKXg",
},
{
multiaddr:
"/dns4/10-testnet.fluence.dev/tcp/9000/wss/p2p/12D3KooWB9P1xmV3c7ZPpBemovbwCiRRTKd3Kq2jsVPQN4ZukDfy",
peerId: "12D3KooWB9P1xmV3c7ZPpBemovbwCiRRTKd3Kq2jsVPQN4ZukDfy",
},
];
export const kras: Relay[] = [
{
multiaddr:
"/dns4/0-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWSD5PToNiLQwKDXsu8JSysCwUt8BVUJEqCHcDe7P5h45e",
peerId: "12D3KooWSD5PToNiLQwKDXsu8JSysCwUt8BVUJEqCHcDe7P5h45e",
},
{
multiaddr:
"/dns4/1-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWR4cv1a8tv7pps4HH6wePNaK6gf1Hww5wcCMzeWxyNw51",
peerId: "12D3KooWR4cv1a8tv7pps4HH6wePNaK6gf1Hww5wcCMzeWxyNw51",
},
{
multiaddr:
"/dns4/2-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWKnEqMfYo9zvfHmqTLpLdiHXPe4SVqUWcWHDJdFGrSmcA",
peerId: "12D3KooWKnEqMfYo9zvfHmqTLpLdiHXPe4SVqUWcWHDJdFGrSmcA",
},
{
multiaddr:
"/dns4/3-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf",
peerId: "12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf",
},
{
multiaddr:
"/dns4/4-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWJd3HaMJ1rpLY1kQvcjRPEvnDwcXrH8mJvk7ypcZXqXGE",
peerId: "12D3KooWJd3HaMJ1rpLY1kQvcjRPEvnDwcXrH8mJvk7ypcZXqXGE",
},
{
multiaddr:
"/dns4/5-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWFEwNWcHqi9rtsmDhsYcDbRUCDXH84RC4FW6UfsFWaoHi",
peerId: "12D3KooWFEwNWcHqi9rtsmDhsYcDbRUCDXH84RC4FW6UfsFWaoHi",
},
{
multiaddr:
"/dns4/6-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWCMr9mU894i8JXAFqpgoFtx6qnV1LFPSfVc3Y34N4h4LS",
peerId: "12D3KooWCMr9mU894i8JXAFqpgoFtx6qnV1LFPSfVc3Y34N4h4LS",
},
{
multiaddr:
"/dns4/7-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWDUszU2NeWyUVjCXhGEt1MoZrhvdmaQQwtZUriuGN1jTr",
peerId: "12D3KooWDUszU2NeWyUVjCXhGEt1MoZrhvdmaQQwtZUriuGN1jTr",
},
{
multiaddr:
"/dns4/8-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWEFFCZnar1cUJQ3rMWjvPQg6yMV2aXWs2DkJNSRbduBWn",
peerId: "12D3KooWEFFCZnar1cUJQ3rMWjvPQg6yMV2aXWs2DkJNSRbduBWn",
},
{
multiaddr:
"/dns4/9-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWFtf3rfCDAfWwt6oLZYZbDfn9Vn7bv7g6QjjQxUUEFVBt",
peerId: "12D3KooWFtf3rfCDAfWwt6oLZYZbDfn9Vn7bv7g6QjjQxUUEFVBt",
},
{
multiaddr:
"/dns4/10-kras.fluence.dev/tcp/9000/wss/p2p/12D3KooWD7CvsYcpF9HE9CCV9aY3SJ317tkXVykjtZnht2EbzDPm",
peerId: "12D3KooWD7CvsYcpF9HE9CCV9aY3SJ317tkXVykjtZnht2EbzDPm",
},
];
export const randomKras = () => {
return randomItem(kras);
};
export const randomTestNet = () => {
return randomItem(testNet);
};
export const randomStage = () => {
return randomItem(stage);
};
function randomItem(arr: Relay[]) {
const index = randomInt(0, arr.length);
return arr[index];
}
function randomInt(min: number, max: number) {
return Math.floor(Math.random() * (max - min)) + min;
}