gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 02/02: simplify refunds a bit, show in transaction h


From: gnunet
Subject: [taler-wallet-core] 02/02: simplify refunds a bit, show in transaction history, add integration tests
Date: Mon, 10 Aug 2020 13:18:46 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

commit 66d76a35912d7687d76b349f1cac462306306d3f
Author: Florian Dold <florian.dold@gmail.com>
AuthorDate: Mon Aug 10 16:48:38 2020 +0530

    simplify refunds a bit, show in transaction history, add integration tests
---
 packages/taler-integrationtests/package.json       |  29 +----
 packages/taler-integrationtests/scenario           |   2 +-
 packages/taler-integrationtests/src/harness.ts     |  39 +++++--
 .../src/test-refund-incremental.ts                 | 126 +++++++++++++++++++++
 packages/taler-integrationtests/src/test-refund.ts | 102 +++++++++++++++++
 packages/taler-integrationtests/testrunner         |   2 +-
 packages/taler-integrationtests/tsconfig.json      |   3 -
 .../taler-wallet-core/src/operations/refund.ts     |  60 +++++-----
 .../src/operations/transactions.ts                 |  86 ++++++++++++--
 .../taler-wallet-core/src/types/transactions.ts    |   5 +-
 pnpm-lock.yaml                                     |  70 +-----------
 11 files changed, 371 insertions(+), 153 deletions(-)

diff --git a/packages/taler-integrationtests/package.json 
b/packages/taler-integrationtests/package.json
index 71385237..ba2f112e 100644
--- a/packages/taler-integrationtests/package.json
+++ b/packages/taler-integrationtests/package.json
@@ -4,40 +4,19 @@
   "description": "Integration tests and fault injection for GNU Taler 
components",
   "main": "index.js",
   "scripts": {
-    "compile": "tsc",
-    "test": "tsc && ava"
+    "compile": "tsc -b"
   },
   "author": "Florian Dold <dold@taler.net>",
   "license": "AGPL-3.0-or-later",
   "devDependencies": {
-    "@ava/typescript": "^1.1.1",
-    "ava": "^3.11.1",
     "esm": "^3.2.25",
     "source-map-support": "^0.5.19",
-    "ts-node": "^8.10.2"
+    "ts-node": "^8.10.2",
+    "typescript": "^3.9.7"
   },
   "dependencies": {
     "axios": "^0.19.2",
     "taler-wallet-core": "workspace:*",
-    "tslib": "^2.0.0",
-    "typescript": "^3.9.7"
-  },
-  "ava": {
-    "require": [
-      "esm"
-    ],
-    "files": [
-      "src/**/test-*"
-    ],
-    "typescript": {
-      "extensions": [
-        "js",
-        "ts",
-        "tsx"
-      ],
-      "rewritePaths": {
-        "src/": "lib/"
-      }
-    }
+    "tslib": "^2.0.0"
   }
 }
diff --git a/packages/taler-integrationtests/scenario 
b/packages/taler-integrationtests/scenario
index a0050258..9bef68ff 100755
--- a/packages/taler-integrationtests/scenario
+++ b/packages/taler-integrationtests/scenario
@@ -17,7 +17,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 
&& pwd )"
 
 cd $DIR
 
-./node_modules/.bin/tsc
+./node_modules/.bin/tsc -b
 
 export ESM_OPTIONS='{"sourceMap": true}'
 
diff --git a/packages/taler-integrationtests/src/harness.ts 
b/packages/taler-integrationtests/src/harness.ts
index 027869d1..2507d12f 100644
--- a/packages/taler-integrationtests/src/harness.ts
+++ b/packages/taler-integrationtests/src/harness.ts
@@ -50,7 +50,7 @@ import { EddsaKeyPair } from 
"taler-wallet-core/lib/crypto/talerCrypto";
 
 const exec = util.promisify(require("child_process").exec);
 
-async function delay(ms: number): Promise<void> {
+export async function delayMs(ms: number): Promise<void> {
   return new Promise((resolve, reject) => {
     setTimeout(() => resolve(), ms);
   });
@@ -410,7 +410,7 @@ async function pingProc(
       return;
     } catch (e) {
       console.log(`service ${serviceName} not ready:`, e.toString());
-      await delay(1000);
+      await delayMs(1000);
     }
     if (!proc || proc.proc.exitCode !== null) {
       throw Error(`service process ${serviceName} stopped unexpectedly`);
@@ -951,14 +951,39 @@ export class MerchantService {
   }
 
   async queryPrivateOrderStatus(instanceName: string, orderId: string) {
-    let url;
+    const reqUrl = new URL(
+      `private/orders/${orderId}`,
+      this.makeInstanceBaseUrl(instanceName),
+    );
+    const resp = await axios.get(reqUrl.href);
+    return codecForMerchantOrderPrivateStatusResponse().decode(resp.data);
+  }
+
+  makeInstanceBaseUrl(instanceName: string): string {
     if (instanceName === "default") {
-      url = 
`http://localhost:${this.merchantConfig.httpPort}/private/orders/${orderId}`;
+      return `http://localhost:${this.merchantConfig.httpPort}/`;
     } else {
-      url = 
`http://localhost:${this.merchantConfig.httpPort}/instances/${instanceName}/private/orders/${orderId}`;
+      return 
`http://localhost:${this.merchantConfig.httpPort}/instances/${instanceName}/`;
+    }
+  }
+
+  async giveRefund(r: {
+    instance: string;
+    orderId: string;
+    amount: string;
+    justification: string;
+  }): Promise<{ talerRefundUri: string }> {
+    const reqUrl = new URL(
+      `private/orders/${r.orderId}/refund`,
+      this.makeInstanceBaseUrl(r.instance),
+    );
+    const resp = await axios.post(reqUrl.href, {
+      refund: r.amount,
+      reason: r.justification,
+    });
+    return {
+      talerRefundUri: resp.data.taler_refund_uri,
     }
-    const resp = await axios.get(url);
-    return codecForMerchantOrderPrivateStatusResponse().decode(resp.data);
   }
 
   async createOrder(
diff --git a/packages/taler-integrationtests/src/test-refund-incremental.ts 
b/packages/taler-integrationtests/src/test-refund-incremental.ts
new file mode 100644
index 00000000..29685dd3
--- /dev/null
+++ b/packages/taler-integrationtests/src/test-refund-incremental.ts
@@ -0,0 +1,126 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import { runTest, GlobalTestState, delayMs } from "./harness";
+import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
+
+/**
+ * Run test for basic, bank-integrated withdrawal.
+ */
+runTest(async (t: GlobalTestState) => {
+  // Set up test environment
+
+  const {
+    wallet,
+    bank,
+    exchange,
+    merchant,
+  } = await createSimpleTestkudosEnvironment(t);
+
+  // Withdraw digital cash into the wallet.
+
+  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+
+  // Set up order.
+
+  const orderResp = await merchant.createOrder("default", {
+    order: {
+      summary: "Buy me!",
+      amount: "TESTKUDOS:5",
+      fulfillment_url: "taler://fulfillment-success/thx",
+    },
+  });
+
+  let orderStatus = await merchant.queryPrivateOrderStatus(
+    "default",
+    orderResp.order_id,
+  );
+
+  t.assertTrue(orderStatus.order_status === "unpaid")
+
+  // Make wallet pay for the order
+
+  const r1 = await wallet.apiRequest("preparePay", {
+    talerPayUri: orderStatus.taler_pay_uri,
+  });
+  t.assertTrue(r1.type === "response");
+
+  const r2 = await wallet.apiRequest("confirmPay", {
+    // FIXME: should be validated, don't cast!
+    proposalId: (r1.result as any).proposalId,
+  });
+  t.assertTrue(r2.type === "response");
+
+  // Check if payment was successful.
+
+  orderStatus = await merchant.queryPrivateOrderStatus(
+    "default",
+    orderResp.order_id,
+  );
+
+  t.assertTrue(orderStatus.order_status === "paid");
+
+  let ref = await merchant.giveRefund({
+    amount: "TESTKUDOS:2.5",
+    instance: "default",
+    justification: "foo",
+    orderId: orderResp.order_id,
+  });
+
+  console.log("first refund increase response", ref);
+
+  // Wait at least a second, because otherwise the increased
+  // refund will be grouped with the previous one.
+  await delayMs(1.2);
+
+  ref = await merchant.giveRefund({
+    amount: "TESTKUDOS:5",
+    instance: "default",
+    justification: "bar",
+    orderId: orderResp.order_id,
+  });
+
+  console.log("second refund increase response", ref);
+
+  let r = await wallet.apiRequest("applyRefund", {
+    talerRefundUri: ref.talerRefundUri,
+  });
+  console.log(r);
+
+  orderStatus = await merchant.queryPrivateOrderStatus(
+    "default",
+    orderResp.order_id,
+  );
+
+  t.assertTrue(orderStatus.order_status === "paid");
+
+  t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:5");
+
+  console.log(JSON.stringify(orderStatus, undefined, 2));
+
+  await wallet.runUntilDone();
+
+  r = await wallet.apiRequest("getBalances", {});
+  console.log(JSON.stringify(r, undefined, 2));
+
+  r = await wallet.apiRequest("getTransactions", {});
+  console.log(JSON.stringify(r, undefined, 2));
+
+  await t.shutdown();
+});
diff --git a/packages/taler-integrationtests/src/test-refund.ts 
b/packages/taler-integrationtests/src/test-refund.ts
new file mode 100644
index 00000000..c2f152f5
--- /dev/null
+++ b/packages/taler-integrationtests/src/test-refund.ts
@@ -0,0 +1,102 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import { runTest, GlobalTestState } from "./harness";
+import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
+
+/**
+ * Run test for basic, bank-integrated withdrawal.
+ */
+runTest(async (t: GlobalTestState) => {
+  // Set up test environment
+
+  const {
+    wallet,
+    bank,
+    exchange,
+    merchant,
+  } = await createSimpleTestkudosEnvironment(t);
+
+  // Withdraw digital cash into the wallet.
+
+  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+
+  // Set up order.
+
+  const orderResp = await merchant.createOrder("default", {
+    order: {
+      summary: "Buy me!",
+      amount: "TESTKUDOS:5",
+      fulfillment_url: "taler://fulfillment-success/thx",
+    },
+  });
+
+  let orderStatus = await merchant.queryPrivateOrderStatus(
+    "default",
+    orderResp.order_id,
+  );
+
+  t.assertTrue(orderStatus.order_status === "unpaid")
+
+  // Make wallet pay for the order
+
+  const r1 = await wallet.apiRequest("preparePay", {
+    talerPayUri: orderStatus.taler_pay_uri,
+  });
+  t.assertTrue(r1.type === "response");
+
+  const r2 = await wallet.apiRequest("confirmPay", {
+    // FIXME: should be validated, don't cast!
+    proposalId: (r1.result as any).proposalId,
+  });
+  t.assertTrue(r2.type === "response");
+
+  // Check if payment was successful.
+
+  orderStatus = await merchant.queryPrivateOrderStatus(
+    "default",
+    orderResp.order_id,
+  );
+
+  t.assertTrue(orderStatus.order_status === "paid");
+
+  const ref = await merchant.giveRefund({
+    amount: "TESTKUDOS:5",
+    instance: "default",
+    justification: "foo",
+    orderId: orderResp.order_id,
+  });
+
+  console.log(ref);
+
+  let r = await wallet.apiRequest("applyRefund", {
+    talerRefundUri: ref.talerRefundUri,
+  });
+  console.log(r);
+
+  await wallet.runUntilDone();
+
+  r = await wallet.apiRequest("getBalances", {});
+  console.log(JSON.stringify(r, undefined, 2));
+
+  r = await wallet.apiRequest("getTransactions", {});
+  console.log(JSON.stringify(r, undefined, 2));
+
+  await t.shutdown();
+});
diff --git a/packages/taler-integrationtests/testrunner 
b/packages/taler-integrationtests/testrunner
index 6476d73f..03cb15b3 100755
--- a/packages/taler-integrationtests/testrunner
+++ b/packages/taler-integrationtests/testrunner
@@ -17,7 +17,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 
&& pwd )"
 
 cd $DIR
 
-./node_modules/.bin/tsc
+./node_modules/.bin/tsc -b
 
 export ESM_OPTIONS='{"sourceMap": true}'
 
diff --git a/packages/taler-integrationtests/tsconfig.json 
b/packages/taler-integrationtests/tsconfig.json
index 9fa8001a..2fe0853d 100644
--- a/packages/taler-integrationtests/tsconfig.json
+++ b/packages/taler-integrationtests/tsconfig.json
@@ -24,9 +24,6 @@
     "typeRoots": ["./node_modules/@types"]
   },
   "references": [
-    {
-      "path": "../idb-bridge/",
-    },
     {
       "path": "../taler-wallet-core"
     }
diff --git a/packages/taler-wallet-core/src/operations/refund.ts 
b/packages/taler-wallet-core/src/operations/refund.ts
index 9792d226..2b6ee97a 100644
--- a/packages/taler-wallet-core/src/operations/refund.ts
+++ b/packages/taler-wallet-core/src/operations/refund.ts
@@ -203,7 +203,7 @@ async function acceptRefunds(
   refunds: MerchantCoinRefundStatus[],
   reason: RefundReason,
 ): Promise<void> {
-  console.log("handling refunds", refunds);
+  logger.trace("handling refunds", refunds);
   const now = getTimestampNow();
 
   await ws.db.runWithWriteTransaction(
@@ -302,37 +302,6 @@ async function acceptRefunds(
   });
 }
 
-async function startRefundQuery(
-  ws: InternalWalletState,
-  proposalId: string,
-): Promise<void> {
-  const success = await ws.db.runWithWriteTransaction(
-    [Stores.purchases],
-    async (tx) => {
-      const p = await tx.get(Stores.purchases, proposalId);
-      if (!p) {
-        logger.error("no purchase found for refund URL");
-        return false;
-      }
-      p.refundStatusRequested = true;
-      p.lastRefundStatusError = undefined;
-      p.refundStatusRetryInfo = initRetryInfo();
-      await tx.put(Stores.purchases, p);
-      return true;
-    },
-  );
-
-  if (!success) {
-    return;
-  }
-
-  ws.notify({
-    type: NotificationType.RefundStarted,
-  });
-
-  await processPurchaseQueryRefund(ws, proposalId);
-}
-
 /**
  * Accept a refund, return the contract hash for the contract
  * that was involved in the refund.
@@ -360,8 +329,31 @@ export async function applyRefund(
     );
   }
 
+  const proposalId = purchase.proposalId;
+
   logger.info("processing purchase for refund");
-  await startRefundQuery(ws, purchase.proposalId);
+  const success = await ws.db.runWithWriteTransaction(
+    [Stores.purchases],
+    async (tx) => {
+      const p = await tx.get(Stores.purchases, proposalId);
+      if (!p) {
+        logger.error("no purchase found for refund URL");
+        return false;
+      }
+      p.refundStatusRequested = true;
+      p.lastRefundStatusError = undefined;
+      p.refundStatusRetryInfo = initRetryInfo();
+      await tx.put(Stores.purchases, p);
+      return true;
+    },
+  );
+
+  if (success) {
+    ws.notify({
+      type: NotificationType.RefundStarted,
+    });
+    await processPurchaseQueryRefund(ws, proposalId);
+  }
 
   return {
     contractTermsHash: purchase.contractData.contractTermsHash,
@@ -422,7 +414,7 @@ async function processPurchaseQueryRefundImpl(
 
   const request = await ws.http.get(requestUrl.href);
 
-  console.log("got json", JSON.stringify(await request.json(), undefined, 2));
+  logger.trace("got json", JSON.stringify(await request.json(), undefined, 2));
 
   const refundResponse = await readSuccessResponseJsonOrThrow(
     request,
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index 2d66b5e9..8de204d4 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -18,7 +18,12 @@
  * Imports.
  */
 import { InternalWalletState } from "./state";
-import { Stores, WithdrawalSourceType } from "../types/dbTypes";
+import {
+  Stores,
+  WithdrawalSourceType,
+  WalletRefundItem,
+  RefundState,
+} from "../types/dbTypes";
 import { Amounts, AmountJson } from "../util/amounts";
 import { timestampCmp } from "../util/time";
 import {
@@ -29,8 +34,10 @@ import {
   PaymentStatus,
   WithdrawalType,
   WithdrawalDetails,
+  PaymentShortInfo,
 } from "../types/transactions";
 import { getFundingPaytoUris } from "./reserves";
+import { ResultLevel } from "idb-bridge";
 
 /**
  * Create an event ID from the type and the primary key for the event.
@@ -224,6 +231,18 @@ export async function getTransactions(
         if (!proposal) {
           return;
         }
+        const info: PaymentShortInfo = {
+          fulfillmentUrl: pr.contractData.fulfillmentUrl,
+          merchant: pr.contractData.merchant,
+          orderId: pr.contractData.orderId,
+          products: pr.contractData.products,
+          summary: pr.contractData.summary,
+          summary_i18n: pr.contractData.summaryI18n,
+        };
+        const paymentTransactionId = makeEventId(
+          TransactionType.Payment,
+          pr.proposalId,
+        );
         transactions.push({
           type: TransactionType.Payment,
           amountRaw: Amounts.stringify(pr.contractData.amount),
@@ -233,15 +252,62 @@ export async function getTransactions(
             : PaymentStatus.Accepted,
           pending: !pr.timestampFirstSuccessfulPay,
           timestamp: pr.timestampAccept,
-          transactionId: makeEventId(TransactionType.Payment, pr.proposalId),
-          info: {
-            fulfillmentUrl: pr.contractData.fulfillmentUrl,
-            merchant: pr.contractData.merchant,
-            orderId: pr.contractData.orderId,
-            products: pr.contractData.products,
-            summary: pr.contractData.summary,
-            summary_i18n: pr.contractData.summaryI18n,
-          },
+          transactionId: paymentTransactionId,
+          info: info,
+        });
+
+        const refundGroupKeys = new Set<string>();
+
+        for (const rk of Object.keys(pr.refunds)) {
+          const refund = pr.refunds[rk];
+          const groupKey = `${refund.executionTime.t_ms}`;
+          refundGroupKeys.add(groupKey);
+        }
+
+        refundGroupKeys.forEach((groupKey: string) => {
+          const refundTransactionId = makeEventId(
+            TransactionType.Payment,
+            pr.proposalId,
+            groupKey,
+          );
+          let r0: WalletRefundItem | undefined;
+          let amountEffective = Amounts.getZero(
+            pr.contractData.amount.currency,
+          );
+          let amountRaw = Amounts.getZero(pr.contractData.amount.currency);
+          for (const rk of Object.keys(pr.refunds)) {
+            const refund = pr.refunds[rk];
+            if (!r0) {
+              r0 = refund;
+            }
+            if (refund.type === RefundState.Applied) {
+              amountEffective = Amounts.add(
+                amountEffective,
+                refund.refundAmount,
+              ).amount;
+              amountRaw = Amounts.add(
+                amountRaw,
+                Amounts.sub(
+                  refund.refundAmount,
+                  refund.refundFee,
+                  refund.totalRefreshCostBound,
+                ).amount,
+              ).amount;
+            }
+          }
+          if (!r0) {
+            throw Error("invariant violated");
+          }
+          transactions.push({
+            type: TransactionType.Refund,
+            info,
+            refundedTransactionId: paymentTransactionId,
+            transactionId: refundTransactionId,
+            timestamp: r0.executionTime,
+            amountEffective: Amounts.stringify(amountEffective),
+            amountRaw: Amounts.stringify(amountRaw),
+            pending: false,
+          });
         });
 
         // for (const rg of pr.refundGroups) {
diff --git a/packages/taler-wallet-core/src/types/transactions.ts 
b/packages/taler-wallet-core/src/types/transactions.ts
index de378f51..fe5580f8 100644
--- a/packages/taler-wallet-core/src/types/transactions.ts
+++ b/packages/taler-wallet-core/src/types/transactions.ts
@@ -218,7 +218,7 @@ export interface TransactionPayment extends 
TransactionCommon {
   amountEffective: AmountString;
 }
 
-interface PaymentShortInfo {
+export interface PaymentShortInfo {
   /**
    * Order ID, uniquely identifies the order within a merchant instance
    */
@@ -259,9 +259,6 @@ interface TransactionRefund extends TransactionCommon {
   // Additional information about the refunded payment
   info: PaymentShortInfo;
 
-  // Part of the refund that couldn't be applied because the refund 
permissions were expired
-  amountInvalid: AmountString;
-
   // Amount that has been refunded by the merchant
   amountRaw: AmountString;
 
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d8ff6bb2..16143c47 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -36,16 +36,12 @@ importers:
       axios: 0.19.2
       taler-wallet-core: 'link:../taler-wallet-core'
       tslib: 2.0.0
-      typescript: 3.9.7
     devDependencies:
-      '@ava/typescript': 1.1.1
-      ava: 3.11.1
       esm: 3.2.25
       source-map-support: 0.5.19
       ts-node: 8.10.2_typescript@3.9.7
+      typescript: 3.9.7
     specifiers:
-      '@ava/typescript': ^1.1.1
-      ava: ^3.11.1
       axios: ^0.19.2
       esm: ^3.2.25
       source-map-support: ^0.5.19
@@ -1123,69 +1119,6 @@ packages:
     hasBin: true
     resolution:
       integrity: 
sha512-y5U8BGeSRjs/OypsC4CJxr+L1KtLKU5kUyHr5hcghXn7HNr2f4LE/4gvl0Q5lNkLX1obdRW1oODphNdU/glwmA==
-  /ava/3.11.1:
-    dependencies:
-      '@concordance/react': 2.0.0
-      acorn: 7.3.1
-      acorn-walk: 7.2.0
-      ansi-styles: 4.2.1
-      arrgv: 1.0.2
-      arrify: 2.0.1
-      callsites: 3.1.0
-      chalk: 4.1.0
-      chokidar: 3.4.1
-      chunkd: 2.0.1
-      ci-info: 2.0.0
-      ci-parallel-vars: 1.0.1
-      clean-yaml-object: 0.1.0
-      cli-cursor: 3.1.0
-      cli-truncate: 2.1.0
-      code-excerpt: 3.0.0
-      common-path-prefix: 3.0.0
-      concordance: 5.0.0
-      convert-source-map: 1.7.0
-      currently-unhandled: 0.4.1
-      debug: 4.1.1
-      del: 5.1.0
-      emittery: 0.7.1
-      equal-length: 1.0.1
-      figures: 3.2.0
-      globby: 11.0.1
-      ignore-by-default: 2.0.0
-      import-local: 3.0.2
-      indent-string: 4.0.0
-      is-error: 2.2.2
-      is-plain-object: 4.1.1
-      is-promise: 4.0.0
-      lodash: 4.17.19
-      matcher: 3.0.0
-      md5-hex: 3.0.1
-      mem: 6.1.0
-      ms: 2.1.2
-      ora: 4.0.5
-      p-map: 4.0.0
-      picomatch: 2.2.2
-      pkg-conf: 3.1.0
-      plur: 4.0.0
-      pretty-ms: 7.0.0
-      read-pkg: 5.2.0
-      resolve-cwd: 3.0.0
-      slash: 3.0.0
-      source-map-support: 0.5.19
-      stack-utils: 2.0.2
-      strip-ansi: 6.0.0
-      supertap: 1.0.0
-      temp-dir: 2.0.0
-      trim-off-newlines: 1.0.1
-      update-notifier: 4.1.0
-      write-file-atomic: 3.0.3
-      yargs: 15.4.1
-    dev: true
-    engines:
-      node: '>=10.18.0 <11 || >=12.14.0 <12.17.0 || >=12.17.0 <13 || >=14.0.0'
-    hasBin: true
-    resolution:
-      integrity: 
sha512-yGPD0msa5Qronw7GHDNlLaB7oU5zryYtXeuvny40YV6TMskSghqK7Ky3NisM/sr+aqI3DY7sfmORx8dIWQgMoQ==
   /axe-core/3.5.5:
     dev: true
     engines:
@@ -4757,6 +4690,7 @@ packages:
     resolution:
       integrity: 
sha512-/OyrHCJ8jtzu+QZ+771YaxQ9s4g5Z3XsQE3Ma7q+BL392xxBn4UMvvCdVnqKC2T/dz03/VXSLVKOP3lHmDdc/w==
   /typescript/3.9.7:
+    dev: true
     engines:
       node: '>=4.2.0'
     hasBin: true

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]