From f9f563956ac0ef78f7cb9029d634af86ec885ca2 Mon Sep 17 00:00:00 2001 From: Seweryn Kras Date: Tue, 16 Jan 2024 20:09:00 +0100 Subject: [PATCH 1/3] feat(payment): add payment command to list and/or pay invoices --- package-lock.json | 29 +++- package.json | 3 + src/main.ts | 3 +- src/payment/payment.action.ts | 171 ++++++++++++++++++++++++ src/payment/payment.command.ts | 98 ++++++++++++++ src/payment/payment.options.ts | 17 +++ src/run-on-golem/run-on-golem.action.ts | 4 +- 7 files changed, 315 insertions(+), 10 deletions(-) create mode 100644 src/payment/payment.action.ts create mode 100644 src/payment/payment.command.ts create mode 100644 src/payment/payment.options.ts diff --git a/package-lock.json b/package-lock.json index f3eb3b3..809a039 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,10 @@ "@golem-sdk/golem-js": "^1.0.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", + "chalk": "^4.1.2", "commander": "^11.1.0", + "console-table-printer": "^2.12.0", + "decimal.js-light": "^2.5.1", "enquirer": "^2.4.1", "lodash": "^4.17.21", "luxon": "^3.4.4", @@ -3470,7 +3473,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -4010,7 +4012,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4138,7 +4139,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -4149,8 +4149,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/colorette": { "version": "2.0.20", @@ -4201,6 +4200,14 @@ "proto-list": "~1.2.1" } }, + "node_modules/console-table-printer": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.12.0.tgz", + "integrity": "sha512-Q/Ax+UOpZw0oPZGmv8bH8/W5NpC2rAYy6cX20BVLGQ45v944oL+srmLTZAse/5a3vWDl0MXR/0GTEdsz2dDTbg==", + "dependencies": { + "simple-wcswidth": "^1.0.1" + } + }, "node_modules/conventional-changelog-angular": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", @@ -4462,6 +4469,11 @@ "node": ">=0.10.0" } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -6276,7 +6288,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -13184,6 +13195,11 @@ "node": ">=4" } }, + "node_modules/simple-wcswidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz", + "integrity": "sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -13516,7 +13532,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, diff --git a/package.json b/package.json index 4e73dc4..5c9b0b8 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,10 @@ "@golem-sdk/golem-js": "^1.0.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", + "chalk": "^4.1.2", "commander": "^11.1.0", + "console-table-printer": "^2.12.0", + "decimal.js-light": "^2.5.1", "enquirer": "^2.4.1", "lodash": "^4.17.21", "luxon": "^3.4.4", diff --git a/src/main.ts b/src/main.ts index 36eff98..d19727c 100755 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import { version } from "./lib/version"; import { manifestCommand } from "./manifest/manifest.command"; import { newCommand } from "./new/new.command"; import { runOnGolemCommand } from "./run-on-golem/run-on-golem.command"; +import { paymentCommand } from "./payment/payment.command"; const program = new Command("golem-sdk"); program.version(version); @@ -12,6 +13,6 @@ program.version(version); // chalk.level = 0; // }); -program.addCommand(manifestCommand).addCommand(newCommand).addCommand(runOnGolemCommand); +program.addCommand(manifestCommand).addCommand(newCommand).addCommand(runOnGolemCommand).addCommand(paymentCommand); program.parse(); diff --git a/src/payment/payment.action.ts b/src/payment/payment.action.ts new file mode 100644 index 0000000..87a34be --- /dev/null +++ b/src/payment/payment.action.ts @@ -0,0 +1,171 @@ +import { PaymentProcessor } from "@golem-sdk/golem-js"; +import { Invoice } from "ya-ts-client/dist/ya-payment"; +import { Table } from "console-table-printer"; +import { PaymentOptions } from "./payment.options"; +import _ from "lodash"; +import { prompt } from "enquirer"; +import Decimal from "decimal.js-light"; +import chalk from "chalk"; + +async function fetchInvoices(options: PaymentOptions, processor: PaymentProcessor) { + if (options.invoice && options.invoice.length > 0) { + return Promise.all(options.invoice.map(async (invoiceId) => processor.fetchSingleInvoice(invoiceId))); + } + return processor.collectInvoices({ + limit: options.limit, + after: options.after, + statuses: options.status, + providerIds: options.provider, + providerWallets: options.wallet, + minAmount: options.minAmount, + maxAmount: options.maxAmount, + }); +} + +function printRows(invoices: Invoice[], options: PaymentOptions) { + const rows = invoices.map((invoice) => { + const allColumns = { + id: invoice.invoiceId, + paid: invoice.status !== "RECEIVED" ? "paid" : "unpaid", + status: invoice.status, + amount: invoice.amount, + timestamp: invoice.timestamp, + platform: invoice.paymentPlatform, + payer: invoice.payerAddr, + issuer: invoice.payeeAddr, + providerId: invoice.issuerId, + }; + return _.pick(allColumns, options.columns); + }); + + if (options.format === "table") { + if (rows.length === 0) { + console.log("No invoices found"); + return; + } + const table = new Table(); + rows.forEach((row) => + table.addRow(row, { color: row["paid"] === undefined ? "white" : row["paid"] === "paid" ? "green" : "red" }), + ); + table.printTable(); + return; + } + if (options.format === "json") { + console.log(JSON.stringify(rows)); + return; + } + if (options.format === "csv") { + console.log(options.columns); + console.log(rows.map((row) => Object.values(row).join(",")).join("\n")); + return; + } +} + +export async function paymentAction(options: PaymentOptions) { + const paymentProcessor = await PaymentProcessor.create({ + apiKey: options.yagnaAppkey, + }); + let invoices: Invoice[]; + try { + invoices = await fetchInvoices(options, paymentProcessor); + invoices.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); + } catch { + console.log(chalk.red("Failed to fetch invoices, check your parameters and try again")); + return; + } + + if (!options.silent) { + printRows(invoices, options); + } + if (options.pay) { + const unpaidInvoices = invoices.filter((invoice) => invoice.status === "RECEIVED"); + await pay(options, unpaidInvoices, paymentProcessor); + } +} + +async function askForConfirmation(invoices: Invoice[]) { + const invoicesToPay = []; + + let i = 0; + for (const invoice of invoices) { + console.log( + `${++i}/${invoices.length}:\n` + + ` - ${chalk.bold("Invoice id:\t")} ${invoice.invoiceId}\n` + + ` - ${chalk.bold("Amount:\t")} ${invoice.amount}\n` + + ` - ${chalk.bold("Timestamp:\t")} ${invoice.timestamp}\n` + + ` - ${chalk.bold("Provider id:\t")} ${invoice.issuerId}\n` + + ` - ${chalk.bold("Provider address:\t")} ${invoice.payeeAddr}`, + ); + const { decision } = (await prompt({ + type: "confirm", + name: "decision", + message: "Do you want to pay this invoice?", + })) as { decision: boolean }; + if (decision) { + invoicesToPay.push(invoice); + } + } + return invoicesToPay; +} + +export async function pay(options: PaymentOptions, invoices: Invoice[], paymentProcessor: PaymentProcessor) { + if (invoices.length === 0) { + console.log(chalk.blue.bold("No unpaid invoices found")); + return; + } + console.log(chalk.blue.bold(`Found ${invoices.length} unpaid invoices:\n`)); + const invoicesToPay = []; + + if (options.yes) { + invoicesToPay.push(...invoices); + } else { + try { + invoicesToPay.push(...(await askForConfirmation(invoices))); + } catch { + console.log(chalk.red("Script cancelled")); + return; + } + + const total = invoicesToPay.reduce((acc, invoice) => acc.add(invoice.amount), new Decimal(0)); + + console.log(chalk.blue.bold(`${invoicesToPay.length} Invoices selected. Total amount to pay: ${total}`)); + try { + const { decision } = (await prompt({ + type: "confirm", + name: "decision", + message: "Do you want to pay these invoices?", + })) as { decision: boolean }; + if (!decision) { + console.log(chalk.red("Payment cancelled")); + return; + } + } catch { + console.log(chalk.red("Payment cancelled")); + return; + } + } + + await paymentProcessor.acceptManyInvoices({ + invoices: invoicesToPay, + dryRun: options.dryRun, + }); + + if (options.silent) { + return; + } + + const summaryTable = new Table(); + paymentProcessor.stats.forEach((stat) => + summaryTable.addRow( + { + invoiceId: stat.invoiceId, + status: (stat.success ? "success" : "failed") + (stat.dryRun ? " (dry run)" : ""), + amount: stat.amount, + }, + { + color: stat.dryRun ? "yellow" : stat.success ? "green" : "red", + }, + ), + ); + summaryTable.printTable(); +} diff --git a/src/payment/payment.command.ts b/src/payment/payment.command.ts new file mode 100644 index 0000000..afedf97 --- /dev/null +++ b/src/payment/payment.command.ts @@ -0,0 +1,98 @@ +import { Command, InvalidArgumentError, Option } from "commander"; +import { PaymentOptions } from "./payment.options"; + +function parseIntOrThrow(value: string) { + const parsedValue = parseInt(value, 10); + if (isNaN(parsedValue) || parsedValue < 0) { + throw new InvalidArgumentError("Not a valid positive integer."); + } + return parsedValue; +} +function parseDateOrThrow(value: string) { + const parsedValue = new Date(value); + if (isNaN(parsedValue.getTime())) { + throw new InvalidArgumentError("Not a valid date."); + } + return parsedValue; +} + +export const paymentCommand = new Command("payment") + .summary("View and manage invoices") + .addOption(new Option("-k, --yagna-appkey ", "Yagna app key").env("YAGNA_APPKEY").makeOptionMandatory()) + .addOption( + new Option("--after ", "Search for invoices after this date") + .default(new Date(0)) + .argParser(parseDateOrThrow), + ) + .addOption( + new Option("--limit ", "Limit the number of invoices returned by the search") + .default(50) + .argParser(parseIntOrThrow), + ) + .addOption(new Option("--provider [provider...]", "Search by provider ID")) + .option( + "--columns [columns...]", + "Columns to display. Valid options are: id, paid, status, amount, timestamp, platform, payer, issuer, providerId", + ["id", "paid", "status", "amount", "timestamp"], + ) + .option("--wallet [wallet...]", "Search by wallet address") + .option("--min-amount ", "Search by minimum invoice amount") + .option("--max-amount ", "Search by maximum invoice amount") + .option( + "--status [status...]", + "Search by invoice status. Valid options are: ISSUED, RECEIVED, ACCEPTED, REJECTED, FAILED, SETTLED, CANCELLED. To search for unpaid invoices use: RECEIVED", + ["RECEIVED", "ACCEPTED", "SETTLED"], + ) + .addOption( + new Option( + "-i, --invoice [invoices...]", + "Instead of searching for invoices, list or pay for the invoices specified by this option.", + ) + .conflicts("limit") + .conflicts("after") + .conflicts("providerId") + .conflicts("wallet") + .conflicts("minAmount") + .conflicts("maxAmount"), + ) + .option("-f, --format ", "Output format: table, json, csv.", "table") + .addOption( + new Option( + "-p, --pay", + "Pay for unpaid invoices returned by the query or for the invoices specified by --invoice", + ).default(false), + ) + .addOption(new Option("-y --yes", "Skip confirmation when paying").default(false)) + .addOption(new Option("--dry-run", "Dry run").default(false)) + .addOption(new Option("-s, --silent", "Don't print anything to stdout").default(false).conflicts("format")) + .allowUnknownOption(false) + .action(async (options: PaymentOptions) => { + const action = await import("./payment.action.js"); + await action.default.paymentAction(options); + }) + .addHelpText( + "after", + ` +Examples: +Search the first 25 invoices issued after 2021-01-01: +$ golem-sdk payment -k --after 2021-01-01 --limit 25 + +Search the invoices issued by providers 0x1234 or 0x4321 and display only id and amount: +$ golem-sdk payment -k --status RECEIVED --providerId 0x1234 0x4321 --limit 100 --columns id amount + +Search for invoices issued by wallet address 0x1234 that are above 0.5 GLM: +$ golem-sdk payment -k --min-amount 0.5 --wallet 0x1234 + +Search for the first 25 invoices issued after 2021-01-01 and display id amount and providerId in JSON format: +$ golem-sdk payment -k --after 2021-01-01 --limit 25 --columns id amount providerId --format json + +Pay for the first 25 unpaid invoices issued after 2021-01-01 (interactively): +$ golem-sdk payment -k --after 2021-01-01 --limit 25 --status RECEIVED --pay + +Pay for the first 25 unpaid invoices issued after 2021-01-01 (auto-confirm): +$ golem-sdk payment -k --after 2021-01-01 --limit 25 --status RECEIVED --pay --yes + +Pay for a specific invoice: +$ golem-sdk payment -k --invoice 0x1234 --pay +`, + ); diff --git a/src/payment/payment.options.ts b/src/payment/payment.options.ts new file mode 100644 index 0000000..4911ad2 --- /dev/null +++ b/src/payment/payment.options.ts @@ -0,0 +1,17 @@ +export interface PaymentOptions { + yagnaAppkey: string; + after: Date; + limit: number; + provider?: string[]; + wallet?: string[]; + minAmount?: number; + maxAmount?: number; + columns: string[]; + status: string[]; + invoice?: string[]; + format: "table" | "json" | "csv"; + pay: boolean; + yes: boolean; + dryRun: boolean; + silent: boolean; +} diff --git a/src/run-on-golem/run-on-golem.action.ts b/src/run-on-golem/run-on-golem.action.ts index 08ff854..028015b 100644 --- a/src/run-on-golem/run-on-golem.action.ts +++ b/src/run-on-golem/run-on-golem.action.ts @@ -3,7 +3,7 @@ import { createInterface } from "readline/promises"; import { parse, ParseEntry } from "shell-quote"; import { CommanderError } from "commander"; import { ParseError, ShellError } from "./errors"; -import { EventType, ExecutorOptions, TaskExecutor } from "@golem-sdk/golem-js"; +import { EVENT_TYPE, ExecutorOptions, TaskExecutor } from "@golem-sdk/golem-js"; import { assertFileExists, checkFileExists } from "../lib/file"; import { readFile } from "fs/promises"; import { TaskAPIContext, VarsType } from "./shell-context"; @@ -241,7 +241,7 @@ function installSignalHandlers(context: TaskAPIContext, et: EventTarget) { // This is used to detect if the activity was terminated by the provider, error or timeout. // If it is, TaskExecutor is already shutting down. Make sure we terminate the shell. - et.addEventListener(EventType, async (e) => { + et.addEventListener(EVENT_TYPE, async (e) => { // FIXME: Will be fixed after JST-526 if (e instanceof Events.ActivityDestroyed) { // // This will happen on activity timeout From 14ff616535c9b73666ba16fb14cdaf9772f7f9c8 Mon Sep 17 00:00:00 2001 From: Seweryn Kras Date: Tue, 23 Jan 2024 18:38:45 +0100 Subject: [PATCH 2/3] feat(invoice): separate pay and search into two commands --- src/main.ts | 4 +- src/payment/common.ts | 69 +++++++ src/payment/invoice-accept.action.ts | 158 ++++++++++++++++ src/payment/invoice-search.action.ts | 69 +++++++ src/payment/invoice.command.ts | 52 ++++++ ...{payment.options.ts => invoice.options.ts} | 7 +- src/payment/payment.action.ts | 171 ------------------ src/payment/payment.command.ts | 98 ---------- 8 files changed, 355 insertions(+), 273 deletions(-) create mode 100644 src/payment/common.ts create mode 100644 src/payment/invoice-accept.action.ts create mode 100644 src/payment/invoice-search.action.ts create mode 100644 src/payment/invoice.command.ts rename src/payment/{payment.options.ts => invoice.options.ts} (67%) delete mode 100644 src/payment/payment.action.ts delete mode 100644 src/payment/payment.command.ts diff --git a/src/main.ts b/src/main.ts index d19727c..f2d1ee8 100755 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import { version } from "./lib/version"; import { manifestCommand } from "./manifest/manifest.command"; import { newCommand } from "./new/new.command"; import { runOnGolemCommand } from "./run-on-golem/run-on-golem.command"; -import { paymentCommand } from "./payment/payment.command"; +import { invoiceCommand } from "./payment/invoice.command"; const program = new Command("golem-sdk"); program.version(version); @@ -13,6 +13,6 @@ program.version(version); // chalk.level = 0; // }); -program.addCommand(manifestCommand).addCommand(newCommand).addCommand(runOnGolemCommand).addCommand(paymentCommand); +program.addCommand(manifestCommand).addCommand(newCommand).addCommand(runOnGolemCommand).addCommand(invoiceCommand); program.parse(); diff --git a/src/payment/common.ts b/src/payment/common.ts new file mode 100644 index 0000000..6ef3138 --- /dev/null +++ b/src/payment/common.ts @@ -0,0 +1,69 @@ +import { Command, InvalidArgumentError, Option } from "commander"; +import { InvoiceSearchOptions } from "./invoice.options"; +import { InvoiceProcessor } from "@golem-sdk/golem-js"; + +function parseIntOrThrow(value: string) { + const parsedValue = parseInt(value, 10); + if (isNaN(parsedValue) || parsedValue < 0) { + throw new InvalidArgumentError("Not a valid positive integer."); + } + return parsedValue; +} +function parseDateOrThrow(value: string) { + const parsedValue = new Date(value); + if (isNaN(parsedValue.getTime())) { + throw new InvalidArgumentError("Not a valid date."); + } + return parsedValue; +} + +export function createInvoiceCommand(name: string): Command { + return new Command(name) + .addOption(new Option("-k, --yagna-appkey ", "Yagna app key").env("YAGNA_APPKEY").makeOptionMandatory()) + .addOption( + new Option("--after ", "Search for invoices after this date") + .default(new Date(0)) + .argParser(parseDateOrThrow), + ) + .addOption( + new Option("--limit ", "Limit the number of invoices returned by the search") + .default(50) + .argParser(parseIntOrThrow), + ) + .addOption(new Option("--provider [provider...]", "Search by provider ID")) + .option( + "--columns [columns...]", + "Columns to display. Valid options are: id, status, amount, timestamp, platform, payer, issuer, providerId", + ["id", "status", "amount", "timestamp", "providerId", "platform"], + ) + .option("--wallet [wallet...]", "Search by wallet address") + .option("--min-amount ", "Search by minimum invoice amount") + .option("--max-amount ", "Search by maximum invoice amount") + .option( + "--status [status...]", + "Search by invoice status. For example to search for invoices you received but did not accept yet, use `--status RECEIVED`. Valid options are: ISSUED, RECEIVED, ACCEPTED, REJECTED, FAILED, SETTLED, CANCELLED.", + ["RECEIVED", "ACCEPTED", "SETTLED"], + ) + .option("--payment-platform [paymentPlatform...]", "Search by payment platform") + .option( + "-i --invoice [invoice...]", + "Instead of searching, fetch specific invoices by ID. If this option is used, all other search options are ignored.", + ) + .option("-f, --format ", "Output format: table, json, csv.", "table"); +} + +export async function fetchInvoices(options: InvoiceSearchOptions, processor: InvoiceProcessor) { + if (options.invoice && options.invoice.length > 0) { + return Promise.all(options.invoice.map(async (invoiceId) => processor.fetchSingleInvoice(invoiceId))); + } + return processor.collectInvoices({ + limit: options.limit, + after: options.after, + statuses: options.status, + providerIds: options.provider, + providerWallets: options.wallet, + minAmount: options.minAmount, + maxAmount: options.maxAmount, + paymentPlatforms: options.paymentPlatform, + }); +} diff --git a/src/payment/invoice-accept.action.ts b/src/payment/invoice-accept.action.ts new file mode 100644 index 0000000..c6d6afc --- /dev/null +++ b/src/payment/invoice-accept.action.ts @@ -0,0 +1,158 @@ +import { prompt } from "enquirer"; +import Decimal from "decimal.js-light"; +import { Table } from "console-table-printer"; +import { Invoice } from "ya-ts-client/dist/ya-payment"; +import chalk from "chalk"; +import { InvoiceAcceptOptions } from "./invoice.options"; +import { InvoiceAcceptResult, InvoiceProcessor } from "@golem-sdk/golem-js"; +import { fetchInvoices } from "./common"; +import _ from "lodash"; + +async function askForConfirmation(invoices: Invoice[], columns: InvoiceAcceptOptions["columns"]) { + const invoicesToPay = []; + + let i = 0; + for (const invoice of invoices) { + const allColumns = { + id: invoice.invoiceId, + accepted: invoice.status !== "RECEIVED" ? "accepted" : "not accepted", + status: invoice.status, + amount: invoice.amount, + timestamp: invoice.timestamp, + platform: invoice.paymentPlatform, + payer: invoice.payerAddr, + issuer: invoice.payeeAddr, + providerId: invoice.issuerId, + }; + const selectedColumns = _.pick(allColumns, columns); + + console.log( + `${++i}/${invoices.length}:\n` + + Object.entries(selectedColumns) + .map(([key, value]) => ` - ${chalk.bold(key)}:\t${value}`) + .join("\n"), + ); + const { decision } = (await prompt({ + type: "confirm", + name: "decision", + message: "Add this invoice to the list of invoices to accept?", + })) as { decision: boolean }; + if (decision) { + invoicesToPay.push(invoice); + } + } + return invoicesToPay; +} + +export async function acceptAction(options: InvoiceAcceptOptions) { + const paymentProcessor = await InvoiceProcessor.create({ + apiKey: options.yagnaAppkey, + }); + let invoices: Invoice[]; + try { + invoices = await fetchInvoices(options, paymentProcessor); + } catch (e) { + console.error(e); + console.log(chalk.red("Failed to fetch invoices, check your parameters and try again.")); + process.exitCode = 1; + return; + } + + if (invoices.length === 0) { + if (!options.silent) { + console.log(chalk.blue.bold("No unaccepted invoices found")); + } + return; + } + const invoicesToPay = []; + + if (options.yes) { + invoicesToPay.push(...invoices); + } else { + console.log(chalk.blue.bold(`Found ${invoices.length} unaccepted invoices:`)); + try { + invoicesToPay.push(...(await askForConfirmation(invoices, options.columns))); + + if (invoicesToPay.length === 0) { + console.log(chalk.blue.bold("No invoices selected")); + return; + } + + const invoicesOnTestnet = invoicesToPay.filter((invoice) => + invoice.paymentPlatform.toLowerCase().endsWith("-tglm"), + ); + const invoicesOnMainnet = invoicesToPay.filter((invoice) => + invoice.paymentPlatform.toLowerCase().endsWith("-glm"), + ); + if (invoicesOnTestnet.length > 0) { + const totalTestGLM = invoicesOnTestnet.reduce((acc, invoice) => acc.add(invoice.amount), new Decimal(0)); + console.log( + chalk.blue.bold( + `Selected ${invoicesOnTestnet.length} invoices on Testnet for a total of ${totalTestGLM} tGLM`, + ), + ); + } + if (invoicesOnMainnet.length > 0) { + const totalRealGLM = invoicesOnMainnet.reduce((acc, invoice) => acc.add(invoice.amount), new Decimal(0)); + console.log( + chalk.blue.bold( + `Selected ${invoicesOnMainnet.length} invoices on Mainnet for a total of ${totalRealGLM} GLM`, + ), + ); + } + const { decision } = (await prompt({ + type: "confirm", + name: "decision", + message: + invoicesToPay.length === 1 + ? "Do you want to accept this invoice?" + : `Do you want to accept these ${invoicesToPay.length} invoices?`, + })) as { decision: boolean }; + if (!decision) { + return; + } + } catch { + process.exitCode = 1; + console.log(chalk.red("Script cancelled")); + return; + } + } + + const paymentResults = await paymentProcessor.acceptManyInvoices({ + invoices: invoicesToPay, + dryRun: options.dryRun, + }); + + if (options.silent) { + return; + } + + const getRow = (result: InvoiceAcceptResult) => { + return { + invoiceId: result.invoiceId, + status: (result.success ? "success" : "failed") + (result.dryRun ? " (dry run)" : ""), + amount: result.amount, + platform: result.allocation.paymentPlatform, + }; + }; + + if (options.format === "table") { + const summaryTable = new Table(); + paymentResults.forEach((result) => + summaryTable.addRow(getRow(result), { + color: result.dryRun ? "yellow" : result.success ? "green" : "red", + }), + ); + summaryTable.printTable(); + } + + if (options.format === "json") { + console.log(JSON.stringify(paymentResults.map(getRow))); + } + + if (options.format === "csv") { + const rows = paymentResults.map(getRow); + console.log(Object.keys(rows[0]).join(",")); + console.log(rows.map((row) => Object.values(row).join(",")).join("\n")); + } +} diff --git a/src/payment/invoice-search.action.ts b/src/payment/invoice-search.action.ts new file mode 100644 index 0000000..d55af2e --- /dev/null +++ b/src/payment/invoice-search.action.ts @@ -0,0 +1,69 @@ +import { InvoiceProcessor } from "@golem-sdk/golem-js"; +import { Invoice } from "ya-ts-client/dist/ya-payment"; +import { Table } from "console-table-printer"; +import { InvoiceSearchOptions } from "./invoice.options"; +import _ from "lodash"; +import chalk from "chalk"; +import { fetchInvoices } from "./common"; + +function printRows(invoices: Invoice[], options: InvoiceSearchOptions) { + const getRow = (invoice: Invoice) => { + const allColumns = { + id: invoice.invoiceId, + status: invoice.status, + amount: invoice.amount, + timestamp: invoice.timestamp, + platform: invoice.paymentPlatform, + payer: invoice.payerAddr, + issuer: invoice.payeeAddr, + providerId: invoice.issuerId, + }; + return _.pick(allColumns, options.columns); + }; + + if (options.format === "table") { + if (invoices.length === 0) { + console.log(chalk.red("No invoices found")); + return; + } + const table = new Table(); + invoices.forEach((invoice) => { + const isHighlighted = invoice.status === "RECEIVED" || invoice.status === "ISSUED"; + const row = getRow(invoice); + table.addRow(row, { + color: isHighlighted ? "red" : "green", + }); + }); + + table.printTable(); + return; + } + if (options.format === "json") { + console.log(JSON.stringify(invoices.map(getRow))); + return; + } + if (options.format === "csv") { + console.log(options.columns); + console.log( + invoices + .map(getRow) + .map((row) => Object.values(row).join(",")) + .join("\n"), + ); + return; + } +} + +export async function searchAction(options: InvoiceSearchOptions) { + const paymentProcessor = await InvoiceProcessor.create({ + apiKey: options.yagnaAppkey, + }); + let invoices: Invoice[]; + try { + invoices = await fetchInvoices(options, paymentProcessor); + } catch { + console.log(chalk.red("Failed to fetch invoices, check your parameters and try again")); + return; + } + printRows(invoices, options); +} diff --git a/src/payment/invoice.command.ts b/src/payment/invoice.command.ts new file mode 100644 index 0000000..afde83f --- /dev/null +++ b/src/payment/invoice.command.ts @@ -0,0 +1,52 @@ +import { Command, Option } from "commander"; +import { InvoiceAcceptOptions, InvoiceSearchOptions } from "./invoice.options"; +import { createInvoiceCommand } from "./common"; + +export const invoiceCommand = new Command("invoice").summary("Search and accept invoices.").addHelpText( + "after", + ` +Examples: + +Search for the first 10 invoices after 2023-01-01: +$ golem-sdk invoice search -k yagna-appkey --after 2023-01-01 --limit 10 + +Search for invoices issued by provider 0x1234 with status RECEIVED and print them in JSON format: +$ golem-sdk invoice search -k yagna-appkey --provider 0x1234 --status RECEIVED --format json + +Search for invoices above 0.5 GLM on payment platform erc20-polygon-glm: +$ golem-sdk invoice search -k yagna-appkey --min-amount 0.5 --payment-platform erc20-polygon-glm + +Search for invoices by their ID and only list their id, timestamp and payment platform: +$ golem-sdk invoice search -k yagna-appkey --invoice 0x1234 0x5678 --columns id timestamp platform + +Accept all invoices from provider 0x1234 (interactive): +$ golem-sdk invoice accept -k yagna-appkey --provider 0x1234 + +Accept all invoices from provider 0x1234 (auto-accept): +$ golem-sdk invoice accept -k yagna-appkey --provider 0x1234 --yes + +Accept all invoices from provider 0x1234 (dry run): +$ golem-sdk invoice accept -k yagna-appkey --provider 0x1234 --dry-run +`, +); + +const searchCommand = createInvoiceCommand("search") + .summary("Search for invoices.") + .allowUnknownOption(false) + .action(async (options: InvoiceSearchOptions) => { + const action = await import("./invoice-search.action.js"); + await action.default.searchAction(options); + }); + +const payCommand = createInvoiceCommand("accept") + .summary("Accept invoices. This command is interactive by default and takes the same options as search.") + .addOption(new Option("-y --yes", "Skip confirmation").default(false)) + .addOption(new Option("--dry-run", "Dry run").default(false)) + .addOption(new Option("-s, --silent", "Don't print anything to stdout").default(false)) + .allowUnknownOption(false) + .action(async (options: InvoiceAcceptOptions) => { + const action = await import("./invoice-accept.action.js"); + await action.default.acceptAction(options); + }); + +invoiceCommand.addCommand(searchCommand).addCommand(payCommand); diff --git a/src/payment/payment.options.ts b/src/payment/invoice.options.ts similarity index 67% rename from src/payment/payment.options.ts rename to src/payment/invoice.options.ts index 4911ad2..50cb503 100644 --- a/src/payment/payment.options.ts +++ b/src/payment/invoice.options.ts @@ -1,4 +1,4 @@ -export interface PaymentOptions { +export interface InvoiceSearchOptions { yagnaAppkey: string; after: Date; limit: number; @@ -6,11 +6,14 @@ export interface PaymentOptions { wallet?: string[]; minAmount?: number; maxAmount?: number; + paymentPlatform?: string[]; columns: string[]; status: string[]; invoice?: string[]; format: "table" | "json" | "csv"; - pay: boolean; +} + +export interface InvoiceAcceptOptions extends InvoiceSearchOptions { yes: boolean; dryRun: boolean; silent: boolean; diff --git a/src/payment/payment.action.ts b/src/payment/payment.action.ts deleted file mode 100644 index 87a34be..0000000 --- a/src/payment/payment.action.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { PaymentProcessor } from "@golem-sdk/golem-js"; -import { Invoice } from "ya-ts-client/dist/ya-payment"; -import { Table } from "console-table-printer"; -import { PaymentOptions } from "./payment.options"; -import _ from "lodash"; -import { prompt } from "enquirer"; -import Decimal from "decimal.js-light"; -import chalk from "chalk"; - -async function fetchInvoices(options: PaymentOptions, processor: PaymentProcessor) { - if (options.invoice && options.invoice.length > 0) { - return Promise.all(options.invoice.map(async (invoiceId) => processor.fetchSingleInvoice(invoiceId))); - } - return processor.collectInvoices({ - limit: options.limit, - after: options.after, - statuses: options.status, - providerIds: options.provider, - providerWallets: options.wallet, - minAmount: options.minAmount, - maxAmount: options.maxAmount, - }); -} - -function printRows(invoices: Invoice[], options: PaymentOptions) { - const rows = invoices.map((invoice) => { - const allColumns = { - id: invoice.invoiceId, - paid: invoice.status !== "RECEIVED" ? "paid" : "unpaid", - status: invoice.status, - amount: invoice.amount, - timestamp: invoice.timestamp, - platform: invoice.paymentPlatform, - payer: invoice.payerAddr, - issuer: invoice.payeeAddr, - providerId: invoice.issuerId, - }; - return _.pick(allColumns, options.columns); - }); - - if (options.format === "table") { - if (rows.length === 0) { - console.log("No invoices found"); - return; - } - const table = new Table(); - rows.forEach((row) => - table.addRow(row, { color: row["paid"] === undefined ? "white" : row["paid"] === "paid" ? "green" : "red" }), - ); - table.printTable(); - return; - } - if (options.format === "json") { - console.log(JSON.stringify(rows)); - return; - } - if (options.format === "csv") { - console.log(options.columns); - console.log(rows.map((row) => Object.values(row).join(",")).join("\n")); - return; - } -} - -export async function paymentAction(options: PaymentOptions) { - const paymentProcessor = await PaymentProcessor.create({ - apiKey: options.yagnaAppkey, - }); - let invoices: Invoice[]; - try { - invoices = await fetchInvoices(options, paymentProcessor); - invoices.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); - } catch { - console.log(chalk.red("Failed to fetch invoices, check your parameters and try again")); - return; - } - - if (!options.silent) { - printRows(invoices, options); - } - if (options.pay) { - const unpaidInvoices = invoices.filter((invoice) => invoice.status === "RECEIVED"); - await pay(options, unpaidInvoices, paymentProcessor); - } -} - -async function askForConfirmation(invoices: Invoice[]) { - const invoicesToPay = []; - - let i = 0; - for (const invoice of invoices) { - console.log( - `${++i}/${invoices.length}:\n` + - ` - ${chalk.bold("Invoice id:\t")} ${invoice.invoiceId}\n` + - ` - ${chalk.bold("Amount:\t")} ${invoice.amount}\n` + - ` - ${chalk.bold("Timestamp:\t")} ${invoice.timestamp}\n` + - ` - ${chalk.bold("Provider id:\t")} ${invoice.issuerId}\n` + - ` - ${chalk.bold("Provider address:\t")} ${invoice.payeeAddr}`, - ); - const { decision } = (await prompt({ - type: "confirm", - name: "decision", - message: "Do you want to pay this invoice?", - })) as { decision: boolean }; - if (decision) { - invoicesToPay.push(invoice); - } - } - return invoicesToPay; -} - -export async function pay(options: PaymentOptions, invoices: Invoice[], paymentProcessor: PaymentProcessor) { - if (invoices.length === 0) { - console.log(chalk.blue.bold("No unpaid invoices found")); - return; - } - console.log(chalk.blue.bold(`Found ${invoices.length} unpaid invoices:\n`)); - const invoicesToPay = []; - - if (options.yes) { - invoicesToPay.push(...invoices); - } else { - try { - invoicesToPay.push(...(await askForConfirmation(invoices))); - } catch { - console.log(chalk.red("Script cancelled")); - return; - } - - const total = invoicesToPay.reduce((acc, invoice) => acc.add(invoice.amount), new Decimal(0)); - - console.log(chalk.blue.bold(`${invoicesToPay.length} Invoices selected. Total amount to pay: ${total}`)); - try { - const { decision } = (await prompt({ - type: "confirm", - name: "decision", - message: "Do you want to pay these invoices?", - })) as { decision: boolean }; - if (!decision) { - console.log(chalk.red("Payment cancelled")); - return; - } - } catch { - console.log(chalk.red("Payment cancelled")); - return; - } - } - - await paymentProcessor.acceptManyInvoices({ - invoices: invoicesToPay, - dryRun: options.dryRun, - }); - - if (options.silent) { - return; - } - - const summaryTable = new Table(); - paymentProcessor.stats.forEach((stat) => - summaryTable.addRow( - { - invoiceId: stat.invoiceId, - status: (stat.success ? "success" : "failed") + (stat.dryRun ? " (dry run)" : ""), - amount: stat.amount, - }, - { - color: stat.dryRun ? "yellow" : stat.success ? "green" : "red", - }, - ), - ); - summaryTable.printTable(); -} diff --git a/src/payment/payment.command.ts b/src/payment/payment.command.ts deleted file mode 100644 index afedf97..0000000 --- a/src/payment/payment.command.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Command, InvalidArgumentError, Option } from "commander"; -import { PaymentOptions } from "./payment.options"; - -function parseIntOrThrow(value: string) { - const parsedValue = parseInt(value, 10); - if (isNaN(parsedValue) || parsedValue < 0) { - throw new InvalidArgumentError("Not a valid positive integer."); - } - return parsedValue; -} -function parseDateOrThrow(value: string) { - const parsedValue = new Date(value); - if (isNaN(parsedValue.getTime())) { - throw new InvalidArgumentError("Not a valid date."); - } - return parsedValue; -} - -export const paymentCommand = new Command("payment") - .summary("View and manage invoices") - .addOption(new Option("-k, --yagna-appkey ", "Yagna app key").env("YAGNA_APPKEY").makeOptionMandatory()) - .addOption( - new Option("--after ", "Search for invoices after this date") - .default(new Date(0)) - .argParser(parseDateOrThrow), - ) - .addOption( - new Option("--limit ", "Limit the number of invoices returned by the search") - .default(50) - .argParser(parseIntOrThrow), - ) - .addOption(new Option("--provider [provider...]", "Search by provider ID")) - .option( - "--columns [columns...]", - "Columns to display. Valid options are: id, paid, status, amount, timestamp, platform, payer, issuer, providerId", - ["id", "paid", "status", "amount", "timestamp"], - ) - .option("--wallet [wallet...]", "Search by wallet address") - .option("--min-amount ", "Search by minimum invoice amount") - .option("--max-amount ", "Search by maximum invoice amount") - .option( - "--status [status...]", - "Search by invoice status. Valid options are: ISSUED, RECEIVED, ACCEPTED, REJECTED, FAILED, SETTLED, CANCELLED. To search for unpaid invoices use: RECEIVED", - ["RECEIVED", "ACCEPTED", "SETTLED"], - ) - .addOption( - new Option( - "-i, --invoice [invoices...]", - "Instead of searching for invoices, list or pay for the invoices specified by this option.", - ) - .conflicts("limit") - .conflicts("after") - .conflicts("providerId") - .conflicts("wallet") - .conflicts("minAmount") - .conflicts("maxAmount"), - ) - .option("-f, --format ", "Output format: table, json, csv.", "table") - .addOption( - new Option( - "-p, --pay", - "Pay for unpaid invoices returned by the query or for the invoices specified by --invoice", - ).default(false), - ) - .addOption(new Option("-y --yes", "Skip confirmation when paying").default(false)) - .addOption(new Option("--dry-run", "Dry run").default(false)) - .addOption(new Option("-s, --silent", "Don't print anything to stdout").default(false).conflicts("format")) - .allowUnknownOption(false) - .action(async (options: PaymentOptions) => { - const action = await import("./payment.action.js"); - await action.default.paymentAction(options); - }) - .addHelpText( - "after", - ` -Examples: -Search the first 25 invoices issued after 2021-01-01: -$ golem-sdk payment -k --after 2021-01-01 --limit 25 - -Search the invoices issued by providers 0x1234 or 0x4321 and display only id and amount: -$ golem-sdk payment -k --status RECEIVED --providerId 0x1234 0x4321 --limit 100 --columns id amount - -Search for invoices issued by wallet address 0x1234 that are above 0.5 GLM: -$ golem-sdk payment -k --min-amount 0.5 --wallet 0x1234 - -Search for the first 25 invoices issued after 2021-01-01 and display id amount and providerId in JSON format: -$ golem-sdk payment -k --after 2021-01-01 --limit 25 --columns id amount providerId --format json - -Pay for the first 25 unpaid invoices issued after 2021-01-01 (interactively): -$ golem-sdk payment -k --after 2021-01-01 --limit 25 --status RECEIVED --pay - -Pay for the first 25 unpaid invoices issued after 2021-01-01 (auto-confirm): -$ golem-sdk payment -k --after 2021-01-01 --limit 25 --status RECEIVED --pay --yes - -Pay for a specific invoice: -$ golem-sdk payment -k --invoice 0x1234 --pay -`, - ); From 10e2d96c1440f2485038d4b780a7d7e8e4bd9a04 Mon Sep 17 00:00:00 2001 From: Seweryn Kras Date: Fri, 26 Jan 2024 18:27:05 +0100 Subject: [PATCH 3/3] chore: bump golem-js to 2.0.0-beta.16 --- package-lock.json | 284 +++++++++++++++++++++++++++++++++------------- package.json | 2 +- 2 files changed, 204 insertions(+), 82 deletions(-) diff --git a/package-lock.json b/package-lock.json index 809a039..b29d642 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "data/project-templates/*" ], "dependencies": { - "@golem-sdk/golem-js": "^1.0.1", + "@golem-sdk/golem-js": "^2.0.0-beta.16", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "chalk": "^4.1.2", @@ -77,6 +77,54 @@ "@golem-sdk/cli": "^2.0.1" } }, + "data/project-templates/js-node-esm/node_modules/@golem-sdk/golem-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@golem-sdk/golem-js/-/golem-js-1.0.3.tgz", + "integrity": "sha512-d6vk1LOXTiEjB9DkdmQtlhwGEizVtKIBlQ/7wCTORWITZRp2b4EWRrWdXXo2yCnMeeyHLnFOY3IdnvXb781I6Q==", + "dependencies": { + "async-lock": "^1.4.0", + "axios": "^1.6.2", + "bottleneck": "^2.19.5", + "collect.js": "^4.36.1", + "eventemitter3": "^5.0.1", + "eventsource": "^2.0.2", + "flatbuffers": "^23.5.26", + "ip-num": "^1.5.1", + "js-sha3": "^0.9.2", + "pino": "^8.16.2", + "pino-pretty": "^10.2.3", + "tmp": "^0.2.1", + "uuid": "^9.0.1", + "ya-ts-client": "^0.5.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "data/project-templates/js-node/node_modules/@golem-sdk/golem-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@golem-sdk/golem-js/-/golem-js-1.0.3.tgz", + "integrity": "sha512-d6vk1LOXTiEjB9DkdmQtlhwGEizVtKIBlQ/7wCTORWITZRp2b4EWRrWdXXo2yCnMeeyHLnFOY3IdnvXb781I6Q==", + "dependencies": { + "async-lock": "^1.4.0", + "axios": "^1.6.2", + "bottleneck": "^2.19.5", + "collect.js": "^4.36.1", + "eventemitter3": "^5.0.1", + "eventsource": "^2.0.2", + "flatbuffers": "^23.5.26", + "ip-num": "^1.5.1", + "js-sha3": "^0.9.2", + "pino": "^8.16.2", + "pino-pretty": "^10.2.3", + "tmp": "^0.2.1", + "uuid": "^9.0.1", + "ya-ts-client": "^0.5.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "data/project-templates/react-js": { "name": "react-js-golem-app", "version": "0.0.0", @@ -97,6 +145,30 @@ "vite": "^5.0.10" } }, + "data/project-templates/react-js/node_modules/@golem-sdk/golem-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@golem-sdk/golem-js/-/golem-js-1.0.3.tgz", + "integrity": "sha512-d6vk1LOXTiEjB9DkdmQtlhwGEizVtKIBlQ/7wCTORWITZRp2b4EWRrWdXXo2yCnMeeyHLnFOY3IdnvXb781I6Q==", + "dependencies": { + "async-lock": "^1.4.0", + "axios": "^1.6.2", + "bottleneck": "^2.19.5", + "collect.js": "^4.36.1", + "eventemitter3": "^5.0.1", + "eventsource": "^2.0.2", + "flatbuffers": "^23.5.26", + "ip-num": "^1.5.1", + "js-sha3": "^0.9.2", + "pino": "^8.16.2", + "pino-pretty": "^10.2.3", + "tmp": "^0.2.1", + "uuid": "^9.0.1", + "ya-ts-client": "^0.5.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "data/project-templates/react-ts": { "name": "react-ts-golem-app", "version": "0.0.0", @@ -119,6 +191,30 @@ "vite": "^5.0.10" } }, + "data/project-templates/react-ts/node_modules/@golem-sdk/golem-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@golem-sdk/golem-js/-/golem-js-1.0.3.tgz", + "integrity": "sha512-d6vk1LOXTiEjB9DkdmQtlhwGEizVtKIBlQ/7wCTORWITZRp2b4EWRrWdXXo2yCnMeeyHLnFOY3IdnvXb781I6Q==", + "dependencies": { + "async-lock": "^1.4.0", + "axios": "^1.6.2", + "bottleneck": "^2.19.5", + "collect.js": "^4.36.1", + "eventemitter3": "^5.0.1", + "eventsource": "^2.0.2", + "flatbuffers": "^23.5.26", + "ip-num": "^1.5.1", + "js-sha3": "^0.9.2", + "pino": "^8.16.2", + "pino-pretty": "^10.2.3", + "tmp": "^0.2.1", + "uuid": "^9.0.1", + "ya-ts-client": "^0.5.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "data/project-templates/ts-node": { "name": "ts-node-golem-app", "version": "1.0.0", @@ -143,6 +239,54 @@ "@golem-sdk/cli": "^2.0.1" } }, + "data/project-templates/ts-node-esm/node_modules/@golem-sdk/golem-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@golem-sdk/golem-js/-/golem-js-1.0.3.tgz", + "integrity": "sha512-d6vk1LOXTiEjB9DkdmQtlhwGEizVtKIBlQ/7wCTORWITZRp2b4EWRrWdXXo2yCnMeeyHLnFOY3IdnvXb781I6Q==", + "dependencies": { + "async-lock": "^1.4.0", + "axios": "^1.6.2", + "bottleneck": "^2.19.5", + "collect.js": "^4.36.1", + "eventemitter3": "^5.0.1", + "eventsource": "^2.0.2", + "flatbuffers": "^23.5.26", + "ip-num": "^1.5.1", + "js-sha3": "^0.9.2", + "pino": "^8.16.2", + "pino-pretty": "^10.2.3", + "tmp": "^0.2.1", + "uuid": "^9.0.1", + "ya-ts-client": "^0.5.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "data/project-templates/ts-node/node_modules/@golem-sdk/golem-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@golem-sdk/golem-js/-/golem-js-1.0.3.tgz", + "integrity": "sha512-d6vk1LOXTiEjB9DkdmQtlhwGEizVtKIBlQ/7wCTORWITZRp2b4EWRrWdXXo2yCnMeeyHLnFOY3IdnvXb781I6Q==", + "dependencies": { + "async-lock": "^1.4.0", + "axios": "^1.6.2", + "bottleneck": "^2.19.5", + "collect.js": "^4.36.1", + "eventemitter3": "^5.0.1", + "eventsource": "^2.0.2", + "flatbuffers": "^23.5.26", + "ip-num": "^1.5.1", + "js-sha3": "^0.9.2", + "pino": "^8.16.2", + "pino-pretty": "^10.2.3", + "tmp": "^0.2.1", + "uuid": "^9.0.1", + "ya-ts-client": "^0.5.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -1562,11 +1706,13 @@ "node": ">=18.0.0" } }, - "node_modules/@golem-sdk/golem-js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@golem-sdk/golem-js/-/golem-js-1.0.1.tgz", - "integrity": "sha512-vrrWWXlZWOf5DzsCHwBKdzrf0s1ZGthHIJK4fLvFJCj59RbntCgOeCSTKAO/qf/SJtF5LSF3hdlMIi7CIIx7eA==", + "node_modules/@golem-sdk/cli/node_modules/@golem-sdk/golem-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@golem-sdk/golem-js/-/golem-js-1.0.3.tgz", + "integrity": "sha512-d6vk1LOXTiEjB9DkdmQtlhwGEizVtKIBlQ/7wCTORWITZRp2b4EWRrWdXXo2yCnMeeyHLnFOY3IdnvXb781I6Q==", + "dev": true, "dependencies": { + "async-lock": "^1.4.0", "axios": "^1.6.2", "bottleneck": "^2.19.5", "collect.js": "^4.36.1", @@ -1585,6 +1731,33 @@ "node": ">=18.0.0" } }, + "node_modules/@golem-sdk/golem-js": { + "version": "2.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@golem-sdk/golem-js/-/golem-js-2.0.0-beta.16.tgz", + "integrity": "sha512-msGQ/he5ieSePOGHpIxOzg2o4sygcDHyYbjg/MFdafUICQu4ujQeRyoHnrYmDhFHJaLD3cGdJNLwuAvD7AHr9w==", + "dependencies": { + "async-lock": "^1.4.0", + "axios": "^1.6.2", + "bottleneck": "^2.19.5", + "collect.js": "^4.36.1", + "debug": "^4.3.4", + "decimal.js-light": "^2.5.1", + "eventemitter3": "^5.0.1", + "eventsource": "^2.0.2", + "flatbuffers": "^23.5.26", + "ip-num": "^1.5.1", + "js-sha3": "^0.9.3", + "pino": "^8.17.1", + "pino-pretty": "^10.3.0", + "semver": "^7.5.4", + "tmp": "^0.2.1", + "uuid": "^9.0.1", + "ya-ts-client": "^0.5.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@golem-sdk/react": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@golem-sdk/react/-/react-2.0.0.tgz", @@ -3640,6 +3813,11 @@ "node": ">=0.10.0" } }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" + }, "node_modules/asynciterator.prototype": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", @@ -6356,63 +6534,9 @@ } }, "node_modules/help-me": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", - "integrity": "sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==", - "dependencies": { - "glob": "^8.0.0", - "readable-stream": "^3.6.0" - } - }, - "node_modules/help-me/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/help-me/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/help-me/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/help-me/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" }, "node_modules/hook-std": { "version": "3.0.0", @@ -7775,9 +7899,9 @@ "link": true }, "node_modules/js-sha3": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.9.2.tgz", - "integrity": "sha512-8kgvwd03wNGQG1GRvl3yy1Yt40sICAcIMsDU2ZLgoL0Z6z9rkRmf9Vd+bi/gYSzgAqMUGl/jiDKu0J8AWFd+BQ==" + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.9.3.tgz", + "integrity": "sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg==" }, "node_modules/js-tokens": { "version": "4.0.0", @@ -11872,16 +11996,16 @@ } }, "node_modules/pino": { - "version": "8.16.2", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.2.tgz", - "integrity": "sha512-2advCDGVEvkKu9TTVSa/kWW7Z3htI/sBKEZpqiHk6ive0i/7f5b1rsU8jn0aimxqfnSz5bj/nOYkwhBUn5xxvg==", + "version": "8.17.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", + "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "v1.1.0", "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0", + "process-warning": "^3.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", @@ -11902,15 +12026,15 @@ } }, "node_modules/pino-pretty": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.2.3.tgz", - "integrity": "sha512-4jfIUc8TC1GPUfDyMSlW1STeORqkoxec71yhxIpLDQapUu8WOuoz2TTCoidrIssyz78LZC69whBMPIKCMbi3cw==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.3.1.tgz", + "integrity": "sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g==", "dependencies": { "colorette": "^2.0.7", "dateformat": "^4.6.3", "fast-copy": "^3.0.0", "fast-safe-stringify": "^2.1.1", - "help-me": "^4.0.1", + "help-me": "^5.0.0", "joycon": "^3.1.1", "minimist": "^1.2.6", "on-exit-leak-free": "^2.1.0", @@ -12190,9 +12314,9 @@ "dev": true }, "node_modules/process-warning": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", - "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" }, "node_modules/prompts": { "version": "2.4.2", @@ -12964,7 +13088,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -13006,7 +13129,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -13017,8 +13139,7 @@ "node_modules/semver/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/set-function-length": { "version": "1.1.1", @@ -14025,7 +14146,8 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true }, "node_modules/uuid": { "version": "9.0.1", diff --git a/package.json b/package.json index 5c9b0b8..24fcd85 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=18.0.0" }, "dependencies": { - "@golem-sdk/golem-js": "^1.0.1", + "@golem-sdk/golem-js": "^2.0.0-beta.16", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "chalk": "^4.1.2",