diff --git a/apps/hermes/client/js/package.json b/apps/hermes/client/js/package.json index 0527074b51..92da10a176 100644 --- a/apps/hermes/client/js/package.json +++ b/apps/hermes/client/js/package.json @@ -6,8 +6,17 @@ "name": "Pyth Data Association" }, "homepage": "https://pyth.network", - "main": "lib/HermesClient.js", - "types": "lib/HermesClient.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/HermesClient.js", + "require": "./lib/cjs/HermesClient.js", + "types": "./lib/HermesClient.d.ts" + } + }, + "main": "./lib/cjs/HermesClient.js", + "module": "./lib/HermesClient.js", + "types": "./lib/HermesClient.d.ts", "files": [ "lib/**/*" ], @@ -20,13 +29,15 @@ "access": "public" }, "scripts": { - "build:typescript": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "build:schemas": "openapi-zod-client ./schema.json --output src/zodSchemas.ts", "pull:schema": "curl -o schema.json -z schema.json https://hermes.pyth.network/docs/openapi.json", "example": "node lib/examples/HermesClient.js", "format": "prettier --write \"src/**/*.ts\"", "test:lint": "eslint src/", - "prepublishOnly": "pnpm run build:typescript && pnpm run test:lint", + "prepublishOnly": "pnpm run build && pnpm run test:lint", "preversion": "pnpm run test:lint", "version": "pnpm run format && git add -A src" }, diff --git a/apps/hermes/client/js/tsconfig.cjs.json b/apps/hermes/client/js/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/apps/hermes/client/js/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/apps/hermes/client/js/tsconfig.json b/apps/hermes/client/js/tsconfig.json index 1b72ccf95f..bf5e5e7818 100644 --- a/apps/hermes/client/js/tsconfig.json +++ b/apps/hermes/client/js/tsconfig.json @@ -1,16 +1,10 @@ { "extends": "../../../../tsconfig.base.json", "compilerOptions": { - "target": "esnext", - "module": "commonjs", - "declaration": true, - "composite": true, - "declarationMap": true, - "incremental": true, - "outDir": "./lib", - "strict": true, "rootDir": "src/", - "esModuleInterop": true + "outDir": "./lib", + "module": "node16", + "moduleResolution": "node16" }, "include": ["src"], "exclude": ["node_modules"] diff --git a/apps/price_pusher/package.json b/apps/price_pusher/package.json index 3a858803f3..051ea64608 100644 --- a/apps/price_pusher/package.json +++ b/apps/price_pusher/package.json @@ -3,8 +3,17 @@ "version": "9.0.0", "description": "Pyth Price Pusher", "homepage": "https://pyth.network", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -20,10 +29,12 @@ "access": "public" }, "scripts": { - "build": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "format": "prettier --write \"src/**/*.ts\"", "test:lint": "eslint src/", - "start": "node lib/index.js", + "start": "node lib/cjs/index.js", "dev": "ts-node src/index.ts", "prepublishOnly": "pnpm run build && pnpm run test:lint", "preversion": "pnpm run test:lint", diff --git a/apps/price_pusher/tsconfig.cjs.json b/apps/price_pusher/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/apps/price_pusher/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/apps/price_pusher/tsconfig.json b/apps/price_pusher/tsconfig.json index 277aacdfce..9572ee0d06 100644 --- a/apps/price_pusher/tsconfig.json +++ b/apps/price_pusher/tsconfig.json @@ -1,14 +1,10 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "target": "esnext", - "module": "commonjs", - "declaration": true, "rootDir": "src/", "outDir": "./lib", - "strict": true, - "esModuleInterop": true, - "resolveJsonModule": true + "module": "node16", + "moduleResolution": "node16" }, "include": ["src"], "exclude": ["node_modules", "**/__tests__/*"] diff --git a/price_service/client/js/package.json b/price_service/client/js/package.json index 04ec0c0598..c03b91dff1 100644 --- a/price_service/client/js/package.json +++ b/price_service/client/js/package.json @@ -47,7 +47,6 @@ "yargs": "^17.4.1" }, "dependencies": { - "@pythnetwork/price-service-sdk": "workspace:*", "@types/ws": "^8.5.3", "axios": "^1.5.1", "axios-retry": "^4.0.0", diff --git a/price_service/sdk/js/package.json b/price_service/sdk/js/package.json index a6e0dad272..6daa71747c 100644 --- a/price_service/sdk/js/package.json +++ b/price_service/sdk/js/package.json @@ -3,8 +3,17 @@ "version": "1.8.0", "description": "Pyth price service SDK", "homepage": "https://pyth.network", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -17,7 +26,9 @@ "access": "public" }, "scripts": { - "build": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "format": "prettier --write \"src/**/*.ts\"", "gen-ts-schema": "quicktype --src-lang schema src/schemas/price_feed.json -o src/schemas/PriceFeed.ts --raw-type any --converters all-objects && prettier --write \"src/schemas/*.ts\"", "test:unit": "jest", @@ -44,6 +55,12 @@ "typescript": "^4.6.3" }, "dependencies": { - "bn.js": "^5.2.1" + "bn.js": "^5.2.1", + "axios": "^1.5.1", + "axios-retry": "^4.0.0", + "isomorphic-ws": "^4.0.1", + "ts-log": "^2.2.4", + "ws": "^8.6.0", + "@types/ws": "^8.5.3" } } diff --git a/price_service/sdk/js/src/client/PriceServiceConnection.ts b/price_service/sdk/js/src/client/PriceServiceConnection.ts new file mode 100644 index 0000000000..f927bcabe3 --- /dev/null +++ b/price_service/sdk/js/src/client/PriceServiceConnection.ts @@ -0,0 +1,430 @@ +import type { HexString, PriceFeedRequestConfig, PriceServiceConnectionConfig, PriceFeedUpdateCallback } from "../types.js"; +import { PriceFeed } from "../schemas/PriceFeed.js"; +import axios, { AxiosInstance } from "axios"; +import axiosRetry from "axios-retry"; +import * as WebSocket from "isomorphic-ws"; +import { Logger } from "ts-log"; +import { ResilientWebSocket } from "./ResillientWebSocket.js"; +import { makeWebsocketUrl, removeLeading0xIfExists } from "./utils.js"; + +export type DurationInMs = number; + +export type PriceFeedRequestConfig = { + /* Optional verbose to request for verbose information from the service */ + verbose?: boolean; + /* Optional binary to include the price feeds binary update data */ + binary?: boolean; + /* Optional config for the websocket subscription to receive out of order updates */ + allowOutOfOrder?: boolean; +}; + +export type PriceServiceConnectionConfig = { + /* Timeout of each request (for all of retries). Default: 5000ms */ + timeout?: DurationInMs; + /** + * Number of times a HTTP request will be retried before the API returns a failure. Default: 3. + * + * The connection uses exponential back-off for the delay between retries. However, + * it will timeout regardless of the retries at the configured `timeout` time. + */ + httpRetries?: number; + /* Optional logger (e.g: console or any logging library) to log internal events */ + logger?: Logger; + /* Deprecated: please use priceFeedRequestConfig.verbose instead */ + verbose?: boolean; + /* Configuration for the price feed requests */ + priceFeedRequestConfig?: PriceFeedRequestConfig; +}; + +type ClientMessage = { + type: "subscribe" | "unsubscribe"; + ids: HexString[]; + verbose?: boolean; + binary?: boolean; + allow_out_of_order?: boolean; +}; + +type ServerResponse = { + type: "response"; + status: "success" | "error"; + error?: string; +}; + +type ServerPriceUpdate = { + type: "price_update"; + price_feed: any; +}; + +type ServerMessage = ServerResponse | ServerPriceUpdate; + +export type PriceFeedUpdateCallback = (priceFeed: PriceFeed) => void; + +export class PriceServiceConnection { + private httpClient: AxiosInstance; + + private priceFeedCallbacks: Map>; + private wsClient: undefined | ResilientWebSocket; + private wsEndpoint: undefined | string; + + private logger: Logger; + + private priceFeedRequestConfig: PriceFeedRequestConfig; + + /** + * Custom handler for web socket errors (connection and message parsing). + * + * Default handler only logs the errors. + */ + onWsError: (error: Error) => void; + + /** + * Constructs a new Connection. + * + * @param endpoint endpoint URL to the price service. Example: https://website/example/ + * @param config Optional PriceServiceConnectionConfig for custom configurations. + */ + constructor(endpoint: string, config?: PriceServiceConnectionConfig) { + this.httpClient = axios.create({ + baseURL: endpoint, + timeout: config?.timeout || 5000, + }); + axiosRetry(this.httpClient, { + retries: config?.httpRetries || 3, + retryDelay: axiosRetry.exponentialDelay, + }); + + this.priceFeedRequestConfig = { + binary: config?.priceFeedRequestConfig?.binary, + verbose: config?.priceFeedRequestConfig?.verbose ?? config?.verbose, + allowOutOfOrder: config?.priceFeedRequestConfig?.allowOutOfOrder, + }; + + this.priceFeedCallbacks = new Map(); + + // Default logger is console for only warnings and errors. + this.logger = config?.logger || { + trace: () => {}, + debug: () => {}, + info: () => {}, + warn: console.warn, + error: console.error, + }; + + this.onWsError = (error: Error) => { + this.logger.error(error); + + // Exit the process if it is running in node. + if ( + typeof process !== "undefined" && + typeof process.exit === "function" + ) { + this.logger.error("Halting the process due to the websocket error"); + process.exit(1); + } else { + this.logger.error( + "Cannot halt process. Please handle the websocket error." + ); + } + }; + + this.wsEndpoint = makeWebsocketUrl(endpoint); + } + + /** + * Fetch Latest PriceFeeds of given price ids. + * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids) + * + * @param priceIds Array of hex-encoded price ids. + * @returns Array of PriceFeeds + */ + async getLatestPriceFeeds( + priceIds: HexString[] + ): Promise { + if (priceIds.length === 0) { + return []; + } + + const response = await this.httpClient.get("/api/latest_price_feeds", { + params: { + ids: priceIds, + verbose: this.priceFeedRequestConfig.verbose, + binary: this.priceFeedRequestConfig.binary, + }, + }); + const priceFeedsJson = response.data as any[]; + return priceFeedsJson.map((priceFeedJson) => + PriceFeed.fromJson(priceFeedJson) + ); + } + + /** + * Fetch latest VAA of given price ids. + * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids) + * + * This function is coupled to wormhole implemntation. + * + * @param priceIds Array of hex-encoded price ids. + * @returns Array of base64 encoded VAAs. + */ + async getLatestVaas(priceIds: HexString[]): Promise { + const response = await this.httpClient.get("/api/latest_vaas", { + params: { + ids: priceIds, + }, + }); + return response.data; + } + + /** + * Fetch the earliest VAA of the given price id that is published since the given publish time. + * This will throw an error if the given publish time is in the future, or if the publish time + * is old and the price service endpoint does not have a db backend for historical requests. + * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price id) + * + * This function is coupled to wormhole implemntation. + * + * @param priceId Hex-encoded price id. + * @param publishTime Epoch timestamp in seconds. + * @returns Tuple of VAA and publishTime. + */ + async getVaa( + priceId: HexString, + publishTime: EpochTimeStamp + ): Promise<[string, EpochTimeStamp]> { + const response = await this.httpClient.get("/api/get_vaa", { + params: { + id: priceId, + publish_time: publishTime, + }, + }); + return [response.data.vaa, response.data.publishTime]; + } + + /** + * Fetch the PriceFeed of the given price id that is published since the given publish time. + * This will throw an error if the given publish time is in the future, or if the publish time + * is old and the price service endpoint does not have a db backend for historical requests. + * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price id) + * + * @param priceId Hex-encoded price id. + * @param publishTime Epoch timestamp in seconds. + * @returns PriceFeed + */ + async getPriceFeed( + priceId: HexString, + publishTime: EpochTimeStamp + ): Promise { + const response = await this.httpClient.get("/api/get_price_feed", { + params: { + id: priceId, + publish_time: publishTime, + verbose: this.priceFeedRequestConfig.verbose, + binary: this.priceFeedRequestConfig.binary, + }, + }); + + return PriceFeed.fromJson(response.data); + } + + /** + * Fetch the list of available price feed ids. + * This will throw an axios error if there is a network problem or the price service returns a non-ok response. + * + * @returns Array of hex-encoded price ids. + */ + async getPriceFeedIds(): Promise { + const response = await this.httpClient.get("/api/price_feed_ids"); + return response.data; + } + + /** + * Subscribe to updates for given price ids. + * + * It will start a websocket connection if it's not started yet. + * Also, it won't throw any exception if given price ids are invalid or connection errors. Instead, + * it calls `connection.onWsError`. If you want to handle the errors you should set the + * `onWsError` function to your custom error handler. + * + * @param priceIds Array of hex-encoded price ids. + * @param cb Callback function that is called with a PriceFeed upon updates to given price ids. + */ + async subscribePriceFeedUpdates( + priceIds: HexString[], + cb: PriceFeedUpdateCallback + ) { + if (this.wsClient === undefined) { + await this.startWebSocket(); + } + + priceIds = priceIds.map((priceId) => removeLeading0xIfExists(priceId)); + + const newPriceIds: HexString[] = []; + + for (const id of priceIds) { + if (!this.priceFeedCallbacks.has(id)) { + this.priceFeedCallbacks.set(id, new Set()); + newPriceIds.push(id); + } + + this.priceFeedCallbacks.get(id)!.add(cb); + } + + const message: ClientMessage = { + ids: newPriceIds, + type: "subscribe", + verbose: this.priceFeedRequestConfig.verbose, + binary: this.priceFeedRequestConfig.binary, + allow_out_of_order: this.priceFeedRequestConfig.allowOutOfOrder, + }; + + await this.wsClient?.send(JSON.stringify(message)); + } + + /** + * Unsubscribe from updates for given price ids. + * + * It will close the websocket connection if it's not subscribed to any price feed updates anymore. + * Also, it won't throw any exception if given price ids are invalid or connection errors. Instead, + * it calls `connection.onWsError`. If you want to handle the errors you should set the + * `onWsError` function to your custom error handler. + * + * @param priceIds Array of hex-encoded price ids. + * @param cb Optional callback, if set it will only unsubscribe this callback from updates for given price ids. + */ + async unsubscribePriceFeedUpdates( + priceIds: HexString[], + cb?: PriceFeedUpdateCallback + ) { + if (this.wsClient === undefined) { + await this.startWebSocket(); + } + + priceIds = priceIds.map((priceId) => removeLeading0xIfExists(priceId)); + + const removedPriceIds: HexString[] = []; + + for (const id of priceIds) { + if (this.priceFeedCallbacks.has(id)) { + let idRemoved = false; + + if (cb === undefined) { + this.priceFeedCallbacks.delete(id); + idRemoved = true; + } else { + this.priceFeedCallbacks.get(id)!.delete(cb); + + if (this.priceFeedCallbacks.get(id)!.size === 0) { + this.priceFeedCallbacks.delete(id); + idRemoved = true; + } + } + + if (idRemoved) { + removedPriceIds.push(id); + } + } + } + + const message: ClientMessage = { + ids: removedPriceIds, + type: "unsubscribe", + }; + + await this.wsClient?.send(JSON.stringify(message)); + + if (this.priceFeedCallbacks.size === 0) { + this.closeWebSocket(); + } + } + + /** + * Starts connection websocket. + * + * This function is called automatically upon subscribing to price feed updates. + */ + async startWebSocket() { + if (this.wsEndpoint === undefined) { + throw new Error("Websocket endpoint is undefined."); + } + + this.wsClient = new ResilientWebSocket(this.wsEndpoint, this.logger); + + this.wsClient.onError = this.onWsError; + + this.wsClient.onReconnect = () => { + if (this.priceFeedCallbacks.size > 0) { + const message: ClientMessage = { + ids: Array.from(this.priceFeedCallbacks.keys()), + type: "subscribe", + verbose: this.priceFeedRequestConfig.verbose, + binary: this.priceFeedRequestConfig.binary, + allow_out_of_order: this.priceFeedRequestConfig.allowOutOfOrder, + }; + + this.logger.info("Resubscribing to existing price feeds."); + this.wsClient?.send(JSON.stringify(message)); + } + }; + + this.wsClient.onMessage = (data: WebSocket.Data) => { + this.logger.info(`Received message ${data.toString()}`); + + let message: ServerMessage; + + try { + message = JSON.parse(data.toString()) as ServerMessage; + } catch (e: any) { + this.logger.error(`Error parsing message ${data.toString()} as JSON.`); + this.logger.error(e); + this.onWsError(e); + return; + } + + if (message.type === "response") { + if (message.status === "error") { + this.logger.error( + `Error response from the websocket server ${message.error}.` + ); + this.onWsError(new Error(message.error)); + } + } else if (message.type === "price_update") { + let priceFeed; + try { + priceFeed = PriceFeed.fromJson(message.price_feed); + } catch (e: any) { + this.logger.error( + `Error parsing price feeds from message ${data.toString()}.` + ); + this.logger.error(e); + this.onWsError(e); + return; + } + + if (this.priceFeedCallbacks.has(priceFeed.id)) { + for (const cb of this.priceFeedCallbacks.get(priceFeed.id)!) { + cb(priceFeed); + } + } + } else { + this.logger.warn( + `Ignoring unsupported server response ${data.toString()}.` + ); + } + }; + + await this.wsClient.startWebSocket(); + } + + /** + * Closes connection websocket. + * + * At termination, the websocket should be closed to finish the + * process elegantly. It will automatically close when the connection + * is subscribed to no price feeds. + */ + closeWebSocket() { + this.wsClient?.closeWebSocket(); + this.wsClient = undefined; + this.priceFeedCallbacks.clear(); + } +} diff --git a/price_service/sdk/js/src/client/ResillientWebSocket.ts b/price_service/sdk/js/src/client/ResillientWebSocket.ts new file mode 100644 index 0000000000..c39c0075a0 --- /dev/null +++ b/price_service/sdk/js/src/client/ResillientWebSocket.ts @@ -0,0 +1,180 @@ +import WebSocket from "isomorphic-ws"; +import { Logger } from "ts-log"; + +const PING_TIMEOUT_DURATION = 30000 + 3000; // It is 30s on the server and 3s is added for delays + +/** + * This class wraps websocket to provide a resilient web socket client. + * + * It will reconnect if connection fails with exponential backoff. Also, in node, it will reconnect + * if it receives no ping request from server within a while as indication of timeout (assuming + * the server sends it regularly). + * + * This class also logs events if logger is given and by replacing onError method you can handle + * connection errors yourself (e.g: do not retry and close the connection). + */ +export class ResilientWebSocket { + private endpoint: string; + private wsClient: undefined | WebSocket; + private wsUserClosed: boolean; + private wsFailedAttempts: number; + private pingTimeout: undefined | NodeJS.Timeout; + private logger: undefined | Logger; + + onError: (error: Error) => void; + onMessage: (data: WebSocket.Data) => void; + onReconnect: () => void; + + constructor(endpoint: string, logger?: Logger) { + this.endpoint = endpoint; + this.logger = logger; + + this.wsFailedAttempts = 0; + this.onError = (error: Error) => { + this.logger?.error(error); + }; + this.wsUserClosed = true; + this.onMessage = () => {}; + this.onReconnect = () => {}; + } + + async send(data: any) { + this.logger?.info(`Sending ${data}`); + + await this.waitForMaybeReadyWebSocket(); + + if (this.wsClient === undefined) { + this.logger?.error( + "Couldn't connect to the websocket server. Error callback is called." + ); + } else { + this.wsClient?.send(data); + } + } + + async startWebSocket() { + if (this.wsClient !== undefined) { + return; + } + + this.logger?.info(`Creating Web Socket client`); + + this.wsClient = new WebSocket(this.endpoint); + this.wsUserClosed = false; + + this.wsClient.onopen = () => { + this.wsFailedAttempts = 0; + // Ping handler is undefined in browser side so heartbeat is disabled. + if (this.wsClient!.on !== undefined) { + this.heartbeat(); + } + }; + + this.wsClient.onerror = (event) => { + this.onError(event.error); + }; + + this.wsClient.onmessage = (event) => { + this.onMessage(event.data); + }; + + this.wsClient.onclose = async () => { + if (this.pingTimeout !== undefined) { + clearInterval(this.pingTimeout); + } + + if (this.wsUserClosed === false) { + this.wsFailedAttempts += 1; + this.wsClient = undefined; + const waitTime = expoBackoff(this.wsFailedAttempts); + + this.logger?.error( + `Connection closed unexpectedly or because of timeout. Reconnecting after ${waitTime}ms.` + ); + + await sleep(waitTime); + this.restartUnexpectedClosedWebsocket(); + } else { + this.logger?.info("The connection has been closed successfully."); + } + }; + + if (this.wsClient.on !== undefined) { + // Ping handler is undefined in browser side + this.wsClient.on("ping", this.heartbeat.bind(this)); + } + } + + /** + * Heartbeat is only enabled in node clients because they support handling + * ping-pong events. + * + * This approach only works when server constantly pings the clients which. + * Otherwise you might consider sending ping and acting on pong responses + * yourself. + */ + private heartbeat() { + this.logger?.info("Heartbeat"); + + if (this.pingTimeout !== undefined) { + clearTimeout(this.pingTimeout); + } + + this.pingTimeout = setTimeout(() => { + this.logger?.warn(`Connection timed out. Reconnecting...`); + this.wsClient?.terminate(); + this.restartUnexpectedClosedWebsocket(); + }, PING_TIMEOUT_DURATION); + } + + private async waitForMaybeReadyWebSocket() { + let waitedTime = 0; + while ( + this.wsClient !== undefined && + this.wsClient.readyState !== this.wsClient.OPEN + ) { + if (waitedTime > 5000) { + this.wsClient.close(); + return; + } else { + waitedTime += 10; + await sleep(10); + } + } + } + + private async restartUnexpectedClosedWebsocket() { + if (this.wsUserClosed === true) { + return; + } + + await this.startWebSocket(); + await this.waitForMaybeReadyWebSocket(); + + if (this.wsClient === undefined) { + this.logger?.error( + "Couldn't reconnect to websocket. Error callback is called." + ); + return; + } + + this.onReconnect(); + } + + closeWebSocket() { + if (this.wsClient !== undefined) { + const client = this.wsClient; + this.wsClient = undefined; + client.close(); + } + this.wsUserClosed = true; + } +} + +async function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +function expoBackoff(attempts: number): number { + return 2 ** attempts * 100; +} diff --git a/price_service/sdk/js/src/client/utils.ts b/price_service/sdk/js/src/client/utils.ts new file mode 100644 index 0000000000..cd814f2dec --- /dev/null +++ b/price_service/sdk/js/src/client/utils.ts @@ -0,0 +1,24 @@ +import { HexString } from "@pythnetwork/price-service-sdk"; + +/** + * Convert http(s) endpoint to ws(s) endpoint. + * + * @param endpoint Http(s) protocol endpoint + * @returns Ws(s) protocol endpoint of the same address + */ +export function makeWebsocketUrl(endpoint: string) { + const url = new URL("ws", endpoint); + const useHttps = url.protocol === "https:"; + + url.protocol = useHttps ? "wss:" : "ws:"; + + return url.toString(); +} + +export function removeLeading0xIfExists(id: HexString): HexString { + if (id.startsWith("0x")) { + return id.substring(2); + } else { + return id; + } +} diff --git a/price_service/sdk/js/src/index.ts b/price_service/sdk/js/src/index.ts index 27640f1114..999ea017ff 100644 --- a/price_service/sdk/js/src/index.ts +++ b/price_service/sdk/js/src/index.ts @@ -3,7 +3,7 @@ import { Price as JsonPrice, PriceFeed as JsonPriceFeed, PriceFeedMetadata as JsonPriceFeedMetadata, -} from "./schemas/PriceFeed"; +} from "./schemas/PriceFeed.js"; export type UnixTimestamp = number; export type DurationInSeconds = number; @@ -16,7 +16,15 @@ export { AccumulatorUpdateData, parsePriceFeedMessage, parseTwapMessage, -} from "./AccumulatorUpdateData"; +} from "./AccumulatorUpdateData.js"; + +export type { + PriceFeedRequestConfig, + PriceServiceConnectionConfig, + PriceFeedUpdateCallback, +} from "./types.js"; +export { PriceServiceConnection } from "./client/PriceServiceConnection.js"; +export { HexString } from "./types.js"; /** * A Pyth Price represented as `${price} ± ${conf} * 10^${expo}` published at `publishTime`. diff --git a/price_service/sdk/js/src/schemas/PriceFeed.ts b/price_service/sdk/js/src/schemas/PriceFeed.ts index f7d59856fa..ff209808ba 100644 --- a/price_service/sdk/js/src/schemas/PriceFeed.ts +++ b/price_service/sdk/js/src/schemas/PriceFeed.ts @@ -10,7 +10,7 @@ /** * Represents an aggregate price from Pyth publisher feeds. */ -export interface PriceFeed { +export class PriceFeed { /** * Exponentially-weighted moving average Price */ @@ -31,6 +31,18 @@ export interface PriceFeed { * VAA of the price */ vaa?: string; + + constructor(data: any) { + this.ema_price = data.ema_price; + this.id = data.id; + this.metadata = data.metadata; + this.price = data.price; + this.vaa = data.vaa; + } + + static fromJson(json: any): PriceFeed { + return new PriceFeed(Convert.toPriceFeed(json)); + } } /** @@ -40,7 +52,7 @@ export interface PriceFeed { * * Price */ -export interface Price { +export class Price { /** * Confidence interval around the price. */ @@ -57,6 +69,17 @@ export interface Price { * Publish Time of the price */ publish_time: number; + + constructor(data: any) { + this.conf = data.conf; + this.expo = data.expo; + this.price = data.price; + this.publish_time = data.publish_time; + } + + static fromJson(json: any): Price { + return new Price(Convert.toPrice(json)); + } } /** diff --git a/price_service/sdk/js/src/types.ts b/price_service/sdk/js/src/types.ts new file mode 100644 index 0000000000..ab4305a39f --- /dev/null +++ b/price_service/sdk/js/src/types.ts @@ -0,0 +1,32 @@ +export const HexString = String as { new(...args: any[]): string }; +export type HexString = string; + +export interface PriceFeedRequestConfig { + binary?: boolean; + verbose?: boolean; + allowOutOfOrder?: boolean; + logger?: { + trace: (...args: any[]) => void; + debug: (...args: any[]) => void; + info: (...args: any[]) => void; + warn: (...args: any[]) => void; + error: (...args: any[]) => void; + }; +} + +export interface PriceServiceConnectionConfig { + url: string; + priceFeedRequestConfig?: PriceFeedRequestConfig; + timeout?: number; + httpRetries?: number; + verbose?: boolean; + logger?: { + trace: (...args: any[]) => void; + debug: (...args: any[]) => void; + info: (...args: any[]) => void; + warn: (...args: any[]) => void; + error: (...args: any[]) => void; + }; +} + +export type PriceFeedUpdateCallback = (priceFeed: any) => void; diff --git a/price_service/sdk/js/tsconfig.cjs.json b/price_service/sdk/js/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/price_service/sdk/js/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/price_service/sdk/js/tsconfig.json b/price_service/sdk/js/tsconfig.json index be27cdded2..fa757eabd5 100644 --- a/price_service/sdk/js/tsconfig.json +++ b/price_service/sdk/js/tsconfig.json @@ -1,9 +1,11 @@ { "extends": "../../../tsconfig.base.json", - "include": ["src"], - "exclude": ["node_modules", "**/__tests__/*"], "compilerOptions": { "rootDir": "src/", - "outDir": "./lib" - } + "outDir": "./lib", + "module": "node16", + "moduleResolution": "node16" + }, + "include": ["src"], + "exclude": ["node_modules", "**/__tests__/*"] } diff --git a/target_chains/aptos/sdk/js/package.json b/target_chains/aptos/sdk/js/package.json index 69f4c105c1..025d036468 100644 --- a/target_chains/aptos/sdk/js/package.json +++ b/target_chains/aptos/sdk/js/package.json @@ -6,8 +6,17 @@ "author": { "name": "Pyth Data Association" }, - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -20,7 +29,9 @@ "access": "public" }, "scripts": { - "build": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "example-relay": "pnpm run build && node lib/examples/AptosRelay.js", "format": "prettier --write \"src/**/*.ts\"", "test:lint": "eslint src/", @@ -51,7 +62,7 @@ "yargs": "^17.4.1" }, "dependencies": { - "@pythnetwork/price-service-client": "workspace:*", + "@pythnetwork/price-service-sdk": "workspace:*", "aptos": "^1.3.14", "buffer": "^6.0.3" } diff --git a/target_chains/aptos/sdk/js/src/AptosPriceServiceConnection.ts b/target_chains/aptos/sdk/js/src/AptosPriceServiceConnection.ts index 6090f0300c..3f3565d049 100644 --- a/target_chains/aptos/sdk/js/src/AptosPriceServiceConnection.ts +++ b/target_chains/aptos/sdk/js/src/AptosPriceServiceConnection.ts @@ -1,7 +1,5 @@ -import { - PriceServiceConnection, - HexString, -} from "@pythnetwork/price-service-client"; +import { PriceServiceConnection } from "@pythnetwork/price-service-sdk/lib/client/PriceServiceConnection.js"; +import { HexString } from "@pythnetwork/price-service-sdk/lib/types.js"; import { BCS } from "aptos"; import { Buffer } from "buffer"; @@ -14,9 +12,10 @@ export class AptosPriceServiceConnection extends PriceServiceConnection { * @returns Array of price update data. */ async getPriceFeedsUpdateData(priceIds: HexString[]): Promise { - // Fetch the latest price feed update VAAs from the price service - const latestVaas = await this.getLatestVaas(priceIds); - return latestVaas.map((vaa) => Array.from(Buffer.from(vaa, "base64"))); + // Fetch the latest price feeds from the price service + // Use getLatestVaas directly since we only need the VAAs + const vaas = await this.getLatestVaas(priceIds); + return vaas.map((vaa: string) => Array.from(Buffer.from(vaa, "base64"))); } /** diff --git a/target_chains/aptos/sdk/js/src/index.ts b/target_chains/aptos/sdk/js/src/index.ts index a2087cb3c4..89d885d4ff 100644 --- a/target_chains/aptos/sdk/js/src/index.ts +++ b/target_chains/aptos/sdk/js/src/index.ts @@ -1,4 +1,4 @@ -export { AptosPriceServiceConnection } from "./AptosPriceServiceConnection"; +export { AptosPriceServiceConnection } from "./AptosPriceServiceConnection.js"; export { DurationInMs, @@ -7,4 +7,4 @@ export { PriceFeed, PriceServiceConnectionConfig, UnixTimestamp, -} from "@pythnetwork/price-service-client"; +} from "@pythnetwork/price-service-sdk"; diff --git a/target_chains/aptos/sdk/js/tsconfig.cjs.json b/target_chains/aptos/sdk/js/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/target_chains/aptos/sdk/js/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/target_chains/aptos/sdk/js/tsconfig.json b/target_chains/aptos/sdk/js/tsconfig.json index 927049ab35..b9ad606f4f 100644 --- a/target_chains/aptos/sdk/js/tsconfig.json +++ b/target_chains/aptos/sdk/js/tsconfig.json @@ -1,13 +1,10 @@ { "extends": "../../../../tsconfig.base.json", "compilerOptions": { - "target": "esnext", - "module": "commonjs", - "declaration": true, - "outDir": "./lib", "rootDir": "src/", - "strict": true, - "esModuleInterop": true + "outDir": "./lib", + "module": "node16", + "moduleResolution": "node16" }, "include": ["src"], "exclude": ["node_modules", "**/__tests__/*"] diff --git a/target_chains/ethereum/sdk/js/package.json b/target_chains/ethereum/sdk/js/package.json index 6d92b7b037..1a64a5acf7 100644 --- a/target_chains/ethereum/sdk/js/package.json +++ b/target_chains/ethereum/sdk/js/package.json @@ -6,8 +6,17 @@ "author": { "name": "Pyth Data Association" }, - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -20,7 +29,9 @@ "access": "public" }, "scripts": { - "build": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "example-client": "pnpm run build && node lib/examples/EvmPriceServiceClient.js", "example-relay": "pnpm run build && node lib/examples/EvmRelay.js", "example-benchmark": "pnpm run build && node lib/examples/EvmBenchmark.js", diff --git a/target_chains/ethereum/sdk/js/tsconfig.cjs.json b/target_chains/ethereum/sdk/js/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/target_chains/ethereum/sdk/js/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/target_chains/ethereum/sdk/js/tsconfig.json b/target_chains/ethereum/sdk/js/tsconfig.json index 462f0d5983..b9ad606f4f 100644 --- a/target_chains/ethereum/sdk/js/tsconfig.json +++ b/target_chains/ethereum/sdk/js/tsconfig.json @@ -1,14 +1,10 @@ { "extends": "../../../../tsconfig.base.json", "compilerOptions": { - "target": "esnext", - "module": "commonjs", - "declaration": true, "rootDir": "src/", "outDir": "./lib", - "strict": true, - "esModuleInterop": true, - "resolveJsonModule": true + "module": "node16", + "moduleResolution": "node16" }, "include": ["src"], "exclude": ["node_modules", "**/__tests__/*"] diff --git a/target_chains/fuel/sdk/js/package.json b/target_chains/fuel/sdk/js/package.json index ec6431308a..9fd62b77ea 100644 --- a/target_chains/fuel/sdk/js/package.json +++ b/target_chains/fuel/sdk/js/package.json @@ -6,8 +6,17 @@ "author": { "name": "Pyth Data Association" }, - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -21,7 +30,9 @@ }, "scripts": { "usage-example": "ts-node src/examples/usage.ts", - "build": "pnpm run generate-fuel-types && tsc && copyfiles -u 1 \"src/**/*.d.ts\" lib", + "build": "pnpm run generate-fuel-types && pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc && copyfiles -u 1 \"src/**/*.d.ts\" lib", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "test:format": "prettier --check \"src/**/*.ts\"", "fix:format": "prettier --write \"src/**/*.ts\"", "test:lint": "eslint src/", diff --git a/target_chains/fuel/sdk/js/tsconfig.cjs.json b/target_chains/fuel/sdk/js/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/target_chains/fuel/sdk/js/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/target_chains/fuel/sdk/js/tsconfig.json b/target_chains/fuel/sdk/js/tsconfig.json index bae5d05cde..6a7798a42a 100644 --- a/target_chains/fuel/sdk/js/tsconfig.json +++ b/target_chains/fuel/sdk/js/tsconfig.json @@ -1,14 +1,10 @@ { "extends": "../../../../tsconfig.base.json", "compilerOptions": { - "target": "esnext", - "module": "commonjs", - "declaration": true, - "outDir": "./lib", "rootDir": "src/", - "strict": true, - "esModuleInterop": true, - "resolveJsonModule": true + "outDir": "./lib", + "module": "node16", + "moduleResolution": "node16" }, "include": ["src", "src/**/*.json"], "exclude": ["node_modules", "**/__tests__/*"] diff --git a/target_chains/solana/sdk/js/pyth_solana_receiver/package.json b/target_chains/solana/sdk/js/pyth_solana_receiver/package.json index ff826b26f4..3f4f6f2b7b 100644 --- a/target_chains/solana/sdk/js/pyth_solana_receiver/package.json +++ b/target_chains/solana/sdk/js/pyth_solana_receiver/package.json @@ -3,8 +3,17 @@ "version": "0.9.1", "description": "Pyth solana receiver SDK", "homepage": "https://pyth.network", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -17,7 +26,9 @@ "access": "public" }, "scripts": { - "build": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "format": "prettier --write \"src/**/*.ts\"", "test:lint": "eslint src/", "prepublishOnly": "pnpm run build && pnpm test:lint", diff --git a/target_chains/solana/sdk/js/pyth_solana_receiver/tsconfig.cjs.json b/target_chains/solana/sdk/js/pyth_solana_receiver/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/target_chains/solana/sdk/js/pyth_solana_receiver/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/target_chains/solana/sdk/js/pyth_solana_receiver/tsconfig.json b/target_chains/solana/sdk/js/pyth_solana_receiver/tsconfig.json index 4a55b57b49..0ff1d3c98c 100644 --- a/target_chains/solana/sdk/js/pyth_solana_receiver/tsconfig.json +++ b/target_chains/solana/sdk/js/pyth_solana_receiver/tsconfig.json @@ -1,9 +1,11 @@ { "extends": "../../../../../tsconfig.base.json", - "include": ["src/**/*.ts", "src/**/*.json"], - "exclude": ["node_modules", "**/__tests__/*"], "compilerOptions": { "rootDir": "src/", - "outDir": "./lib" - } + "outDir": "./lib", + "module": "node16", + "moduleResolution": "node16" + }, + "include": ["src/**/*.ts", "src/**/*.json"], + "exclude": ["node_modules", "**/__tests__/*"] } diff --git a/target_chains/solana/sdk/js/solana_utils/package.json b/target_chains/solana/sdk/js/solana_utils/package.json index e2754ab9d6..6b48ff13f3 100644 --- a/target_chains/solana/sdk/js/solana_utils/package.json +++ b/target_chains/solana/sdk/js/solana_utils/package.json @@ -3,8 +3,17 @@ "version": "0.4.4", "description": "Utility functions for Solana", "homepage": "https://pyth.network", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -17,7 +26,9 @@ "access": "public" }, "scripts": { - "build": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "format": "prettier --write \"src/**/*.ts\"", "test:unit": "jest", "test:lint": "eslint src/", diff --git a/target_chains/solana/sdk/js/solana_utils/tsconfig.cjs.json b/target_chains/solana/sdk/js/solana_utils/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/target_chains/solana/sdk/js/solana_utils/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/target_chains/solana/sdk/js/solana_utils/tsconfig.json b/target_chains/solana/sdk/js/solana_utils/tsconfig.json index 4a55b57b49..0ff1d3c98c 100644 --- a/target_chains/solana/sdk/js/solana_utils/tsconfig.json +++ b/target_chains/solana/sdk/js/solana_utils/tsconfig.json @@ -1,9 +1,11 @@ { "extends": "../../../../../tsconfig.base.json", - "include": ["src/**/*.ts", "src/**/*.json"], - "exclude": ["node_modules", "**/__tests__/*"], "compilerOptions": { "rootDir": "src/", - "outDir": "./lib" - } + "outDir": "./lib", + "module": "node16", + "moduleResolution": "node16" + }, + "include": ["src/**/*.ts", "src/**/*.json"], + "exclude": ["node_modules", "**/__tests__/*"] } diff --git a/target_chains/starknet/sdk/js/package.json b/target_chains/starknet/sdk/js/package.json index 8f485a3e66..afe55443b7 100644 --- a/target_chains/starknet/sdk/js/package.json +++ b/target_chains/starknet/sdk/js/package.json @@ -6,8 +6,17 @@ "author": { "name": "Pyth Data Association" }, - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -20,7 +29,9 @@ "access": "public" }, "scripts": { - "build": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "usage-example": "ts-node src/examples/usage.ts", "format": "prettier --write \"src/**/*.ts\"", "test:lint": "eslint src/", diff --git a/target_chains/starknet/sdk/js/tsconfig.cjs.json b/target_chains/starknet/sdk/js/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/target_chains/starknet/sdk/js/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/target_chains/starknet/sdk/js/tsconfig.json b/target_chains/starknet/sdk/js/tsconfig.json index bae5d05cde..6a7798a42a 100644 --- a/target_chains/starknet/sdk/js/tsconfig.json +++ b/target_chains/starknet/sdk/js/tsconfig.json @@ -1,14 +1,10 @@ { "extends": "../../../../tsconfig.base.json", "compilerOptions": { - "target": "esnext", - "module": "commonjs", - "declaration": true, - "outDir": "./lib", "rootDir": "src/", - "strict": true, - "esModuleInterop": true, - "resolveJsonModule": true + "outDir": "./lib", + "module": "node16", + "moduleResolution": "node16" }, "include": ["src", "src/**/*.json"], "exclude": ["node_modules", "**/__tests__/*"] diff --git a/target_chains/sui/sdk/js/package.json b/target_chains/sui/sdk/js/package.json index 5b0a77fb1f..43ef2748cd 100644 --- a/target_chains/sui/sdk/js/package.json +++ b/target_chains/sui/sdk/js/package.json @@ -6,8 +6,17 @@ "author": { "name": "Pyth Data Association" }, - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -20,7 +29,9 @@ "access": "public" }, "scripts": { - "build": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "example-relay": "pnpm run build && node lib/examples/SuiRelay.js", "format": "prettier --write \"src/**/*.ts\"", "test:lint": "eslint src/", diff --git a/target_chains/sui/sdk/js/tsconfig.cjs.json b/target_chains/sui/sdk/js/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/target_chains/sui/sdk/js/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/target_chains/sui/sdk/js/tsconfig.json b/target_chains/sui/sdk/js/tsconfig.json index 927049ab35..b9ad606f4f 100644 --- a/target_chains/sui/sdk/js/tsconfig.json +++ b/target_chains/sui/sdk/js/tsconfig.json @@ -1,13 +1,10 @@ { "extends": "../../../../tsconfig.base.json", "compilerOptions": { - "target": "esnext", - "module": "commonjs", - "declaration": true, - "outDir": "./lib", "rootDir": "src/", - "strict": true, - "esModuleInterop": true + "outDir": "./lib", + "module": "node16", + "moduleResolution": "node16" }, "include": ["src"], "exclude": ["node_modules", "**/__tests__/*"] diff --git a/target_chains/ton/sdk/js/package.json b/target_chains/ton/sdk/js/package.json index afcf8c1f0c..085603fc7c 100644 --- a/target_chains/ton/sdk/js/package.json +++ b/target_chains/ton/sdk/js/package.json @@ -6,8 +6,17 @@ "author": { "name": "Pyth Data Association" }, - "main": "lib/index.js", - "types": "lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/index.d.ts" + } + }, + "main": "./lib/cjs/index.js", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", "files": [ "lib/**/*" ], @@ -20,7 +29,9 @@ "access": "public" }, "scripts": { - "build": "tsc", + "build": "pnpm run build:esm && pnpm run build:cjs", + "build:esm": "tsc", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", "format": "prettier --write \"src/**/*.ts\"", "test:lint": "eslint src/", "prepublishOnly": "pnpm run build && pnpm run test:lint", diff --git a/target_chains/ton/sdk/js/tsconfig.cjs.json b/target_chains/ton/sdk/js/tsconfig.cjs.json new file mode 100644 index 0000000000..d944d7cfd5 --- /dev/null +++ b/target_chains/ton/sdk/js/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./lib/cjs" + } +} diff --git a/target_chains/ton/sdk/js/tsconfig.json b/target_chains/ton/sdk/js/tsconfig.json index bae5d05cde..6a7798a42a 100644 --- a/target_chains/ton/sdk/js/tsconfig.json +++ b/target_chains/ton/sdk/js/tsconfig.json @@ -1,14 +1,10 @@ { "extends": "../../../../tsconfig.base.json", "compilerOptions": { - "target": "esnext", - "module": "commonjs", - "declaration": true, - "outDir": "./lib", "rootDir": "src/", - "strict": true, - "esModuleInterop": true, - "resolveJsonModule": true + "outDir": "./lib", + "module": "node16", + "moduleResolution": "node16" }, "include": ["src", "src/**/*.json"], "exclude": ["node_modules", "**/__tests__/*"] diff --git a/tsconfig.base.json b/tsconfig.base.json index 1192b913fb..6a83a195e7 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,7 +1,8 @@ { "compilerOptions": { "target": "esnext", - "module": "commonjs", + "module": "node16", + "moduleResolution": "node16", "declaration": true, "composite": true, "declarationMap": true,