bug: lndhub payment state
refactor: use webln package
This commit is contained in:
parent
7ab8eff33a
commit
c52eb38833
@ -31,6 +31,7 @@
|
|||||||
"react-textarea-autosize": "^8.4.0",
|
"react-textarea-autosize": "^8.4.0",
|
||||||
"react-twitter-embed": "^4.0.4",
|
"react-twitter-embed": "^4.0.4",
|
||||||
"use-long-press": "^2.0.3",
|
"use-long-press": "^2.0.3",
|
||||||
|
"webln": "^0.3.2",
|
||||||
"workbox-background-sync": "^6.4.2",
|
"workbox-background-sync": "^6.4.2",
|
||||||
"workbox-broadcast-update": "^6.4.2",
|
"workbox-broadcast-update": "^6.4.2",
|
||||||
"workbox-cacheable-response": "^6.4.2",
|
"workbox-cacheable-response": "^6.4.2",
|
||||||
|
@ -5,7 +5,7 @@ import { RouteObject, useNavigate } from "react-router-dom";
|
|||||||
import { FormattedMessage, FormattedNumber, useIntl } from "react-intl";
|
import { FormattedMessage, FormattedNumber, useIntl } from "react-intl";
|
||||||
|
|
||||||
import NoteTime from "Element/NoteTime";
|
import NoteTime from "Element/NoteTime";
|
||||||
import { WalletInvoice, Sats, WalletInfo, WalletInvoiceState, useWallet, LNWallet, Wallets, WalletKind } from "Wallet";
|
import { WalletInvoice, Sats, WalletInfo, WalletInvoiceState, useWallet, LNWallet, Wallets } from "Wallet";
|
||||||
import AsyncButton from "Element/AsyncButton";
|
import AsyncButton from "Element/AsyncButton";
|
||||||
import { unwrap } from "Util";
|
import { unwrap } from "Util";
|
||||||
import { WebLNWallet } from "Wallet/WebLN";
|
import { WebLNWallet } from "Wallet/WebLN";
|
||||||
@ -50,7 +50,7 @@ export default function WalletPage() {
|
|||||||
if (wallet) {
|
if (wallet) {
|
||||||
if (wallet.isReady()) {
|
if (wallet.isReady()) {
|
||||||
loadWallet(wallet).catch(console.warn);
|
loadWallet(wallet).catch(console.warn);
|
||||||
} else if (walletState.config?.kind !== WalletKind.LNC) {
|
} else if (wallet.canAutoLogin()) {
|
||||||
wallet
|
wallet
|
||||||
.login()
|
.login()
|
||||||
.then(async () => await loadWallet(wallet))
|
.then(async () => await loadWallet(wallet))
|
||||||
|
@ -17,41 +17,10 @@ import { System } from "System";
|
|||||||
import { unwrap } from "Util";
|
import { unwrap } from "Util";
|
||||||
import { EventBuilder } from "./EventBuilder";
|
import { EventBuilder } from "./EventBuilder";
|
||||||
import { EventExt } from "./EventExt";
|
import { EventExt } from "./EventExt";
|
||||||
|
import { barrierQueue, processWorkQueue, WorkQueueItem } from "WorkQueue";
|
||||||
|
|
||||||
interface Nip7QueueItem {
|
const Nip7Queue: Array<WorkQueueItem> = [];
|
||||||
next: () => Promise<unknown>;
|
processWorkQueue(Nip7Queue);
|
||||||
resolve(v: unknown): void;
|
|
||||||
reject(e: unknown): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Nip7QueueDelay = 200;
|
|
||||||
const Nip7Queue: Array<Nip7QueueItem> = [];
|
|
||||||
async function processQueue() {
|
|
||||||
while (Nip7Queue.length > 0) {
|
|
||||||
const v = Nip7Queue.shift();
|
|
||||||
if (v) {
|
|
||||||
try {
|
|
||||||
const ret = await v.next();
|
|
||||||
v.resolve(ret);
|
|
||||||
} catch (e) {
|
|
||||||
v.reject(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setTimeout(processQueue, Nip7QueueDelay);
|
|
||||||
}
|
|
||||||
processQueue();
|
|
||||||
|
|
||||||
export const barrierNip07 = async <T>(then: () => Promise<T>): Promise<T> => {
|
|
||||||
return new Promise<T>((resolve, reject) => {
|
|
||||||
Nip7Queue.push({
|
|
||||||
next: then,
|
|
||||||
resolve,
|
|
||||||
reject,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export type EventBuilderHook = (ev: EventBuilder) => EventBuilder;
|
export type EventBuilderHook = (ev: EventBuilder) => EventBuilder;
|
||||||
|
|
||||||
export class EventPublisher {
|
export class EventPublisher {
|
||||||
@ -78,12 +47,12 @@ export class EventPublisher {
|
|||||||
|
|
||||||
async #sign(eb: EventBuilder) {
|
async #sign(eb: EventBuilder) {
|
||||||
if (this.#hasNip07 && !this.#privateKey) {
|
if (this.#hasNip07 && !this.#privateKey) {
|
||||||
const nip7PubKey = await barrierNip07(() => unwrap(window.nostr).getPublicKey());
|
const nip7PubKey = await barrierQueue(Nip7Queue, () => unwrap(window.nostr).getPublicKey());
|
||||||
if (nip7PubKey !== this.#pubKey) {
|
if (nip7PubKey !== this.#pubKey) {
|
||||||
throw new Error("Can't sign event, NIP-07 pubkey does not match");
|
throw new Error("Can't sign event, NIP-07 pubkey does not match");
|
||||||
}
|
}
|
||||||
const ev = eb.build();
|
const ev = eb.build();
|
||||||
return await barrierNip07(() => unwrap(window.nostr).signEvent(ev));
|
return await barrierQueue(Nip7Queue, () => unwrap(window.nostr).signEvent(ev));
|
||||||
} else if (this.#privateKey) {
|
} else if (this.#privateKey) {
|
||||||
return await eb.buildAndSign(this.#privateKey);
|
return await eb.buildAndSign(this.#privateKey);
|
||||||
} else {
|
} else {
|
||||||
@ -93,11 +62,13 @@ export class EventPublisher {
|
|||||||
|
|
||||||
async nip4Encrypt(content: string, key: HexKey) {
|
async nip4Encrypt(content: string, key: HexKey) {
|
||||||
if (this.#hasNip07 && !this.#privateKey) {
|
if (this.#hasNip07 && !this.#privateKey) {
|
||||||
const nip7PubKey = await barrierNip07(() => unwrap(window.nostr).getPublicKey());
|
const nip7PubKey = await barrierQueue(Nip7Queue, () => unwrap(window.nostr).getPublicKey());
|
||||||
if (nip7PubKey !== this.#pubKey) {
|
if (nip7PubKey !== this.#pubKey) {
|
||||||
throw new Error("Can't encrypt content, NIP-07 pubkey does not match");
|
throw new Error("Can't encrypt content, NIP-07 pubkey does not match");
|
||||||
}
|
}
|
||||||
return await barrierNip07(() => unwrap(window.nostr?.nip04?.encrypt).call(window.nostr?.nip04, key, content));
|
return await barrierQueue(Nip7Queue, () =>
|
||||||
|
unwrap(window.nostr?.nip04?.encrypt).call(window.nostr?.nip04, key, content)
|
||||||
|
);
|
||||||
} else if (this.#privateKey) {
|
} else if (this.#privateKey) {
|
||||||
return await EventExt.encryptData(content, key, this.#privateKey);
|
return await EventExt.encryptData(content, key, this.#privateKey);
|
||||||
} else {
|
} else {
|
||||||
@ -107,7 +78,7 @@ export class EventPublisher {
|
|||||||
|
|
||||||
async nip4Decrypt(content: string, otherKey: HexKey) {
|
async nip4Decrypt(content: string, otherKey: HexKey) {
|
||||||
if (this.#hasNip07 && !this.#privateKey && window.nostr?.nip04?.decrypt) {
|
if (this.#hasNip07 && !this.#privateKey && window.nostr?.nip04?.decrypt) {
|
||||||
return await barrierNip07(() =>
|
return await barrierQueue(Nip7Queue, () =>
|
||||||
unwrap(window.nostr?.nip04?.decrypt).call(window.nostr?.nip04, otherKey, content)
|
unwrap(window.nostr?.nip04?.decrypt).call(window.nostr?.nip04, otherKey, content)
|
||||||
);
|
);
|
||||||
} else if (this.#privateKey) {
|
} else if (this.#privateKey) {
|
||||||
|
@ -10,6 +10,10 @@ export class CashuWallet implements LNWallet {
|
|||||||
this.#mint = mint;
|
this.#mint = mint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canAutoLogin(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
isReady(): boolean {
|
isReady(): boolean {
|
||||||
return this.#wallet !== undefined;
|
return this.#wallet !== undefined;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,10 @@ export class LNCWallet implements LNWallet {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canAutoLogin(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
isReady(): boolean {
|
isReady(): boolean {
|
||||||
return this.#lnc.isReady;
|
return this.#lnc.isReady;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,10 @@ export default class LNDHubWallet implements LNWallet {
|
|||||||
return this.auth !== undefined;
|
return this.auth !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canAutoLogin(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
close(): Promise<boolean> {
|
close(): Promise<boolean> {
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
@ -91,7 +95,12 @@ export default class LNDHubWallet implements LNWallet {
|
|||||||
return {
|
return {
|
||||||
pr: pr,
|
pr: pr,
|
||||||
paymentHash: pRsp.payment_hash,
|
paymentHash: pRsp.payment_hash,
|
||||||
state: pRsp.payment_error === undefined ? WalletInvoiceState.Paid : WalletInvoiceState.Pending,
|
preimage: pRsp.payment_preimage,
|
||||||
|
state: pRsp.payment_error
|
||||||
|
? WalletInvoiceState.Failed
|
||||||
|
: pRsp.payment_preimage
|
||||||
|
? WalletInvoiceState.Paid
|
||||||
|
: WalletInvoiceState.Pending,
|
||||||
} as WalletInvoice;
|
} as WalletInvoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +50,14 @@ export class NostrConnectWallet implements LNWallet {
|
|||||||
} as WalletConnectConfig;
|
} as WalletConnectConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
isReady(): boolean {
|
canAutoLogin(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isReady(): boolean {
|
||||||
|
return this.#conn !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async getInfo() {
|
async getInfo() {
|
||||||
await this.login();
|
await this.login();
|
||||||
return await new Promise<WalletInfo>((resolve, reject) => {
|
return await new Promise<WalletInfo>((resolve, reject) => {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { requestProvider, WebLNProvider } from "webln";
|
||||||
import {
|
import {
|
||||||
InvoiceRequest,
|
InvoiceRequest,
|
||||||
LNWallet,
|
LNWallet,
|
||||||
@ -12,77 +13,19 @@ import {
|
|||||||
WalletKind,
|
WalletKind,
|
||||||
WalletStore,
|
WalletStore,
|
||||||
} from "Wallet";
|
} from "Wallet";
|
||||||
import { delay } from "Util";
|
import { unwrap } from "Util";
|
||||||
|
import { barrierQueue, processWorkQueue, WorkQueueItem } from "WorkQueue";
|
||||||
|
|
||||||
let isWebLnBusy = false;
|
const WebLNQueue: Array<WorkQueueItem> = [];
|
||||||
export const barrierWebLn = async <T>(then: () => Promise<T>): Promise<T> => {
|
processWorkQueue(WebLNQueue);
|
||||||
while (isWebLnBusy) {
|
|
||||||
await delay(10);
|
|
||||||
}
|
|
||||||
isWebLnBusy = true;
|
|
||||||
try {
|
|
||||||
return await then();
|
|
||||||
} finally {
|
|
||||||
isWebLnBusy = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
interface SendPaymentResponse {
|
|
||||||
paymentHash?: string;
|
|
||||||
preimage: string;
|
|
||||||
route?: {
|
|
||||||
total_amt: number;
|
|
||||||
total_fees: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RequestInvoiceArgs {
|
|
||||||
amount?: string | number;
|
|
||||||
defaultAmount?: string | number;
|
|
||||||
minimumAmount?: string | number;
|
|
||||||
maximumAmount?: string | number;
|
|
||||||
defaultMemo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RequestInvoiceResponse {
|
|
||||||
paymentRequest: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetInfoResponse {
|
|
||||||
node: {
|
|
||||||
alias: string;
|
|
||||||
pubkey: string;
|
|
||||||
color?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SignMessageResponse {
|
|
||||||
message: string;
|
|
||||||
signature: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WebLN {
|
|
||||||
enabled: boolean;
|
|
||||||
getInfo(): Promise<GetInfoResponse>;
|
|
||||||
enable(): Promise<void>;
|
|
||||||
makeInvoice(args: RequestInvoiceArgs): Promise<RequestInvoiceResponse>;
|
|
||||||
signMessage(message: string): Promise<SignMessageResponse>;
|
|
||||||
verifyMessage(signature: string, message: string): Promise<void>;
|
|
||||||
sendPayment: (pr: string) => Promise<SendPaymentResponse>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
webln?: WebLN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a wallet config for WebLN if detected
|
* Adds a wallet config for WebLN if detected
|
||||||
*/
|
*/
|
||||||
export function setupWebLNWalletConfig(store: WalletStore) {
|
export async function setupWebLNWalletConfig(store: WalletStore) {
|
||||||
const wallets = store.list();
|
const wallets = store.list();
|
||||||
if (window.webln && !wallets.some(a => a.kind === WalletKind.WebLN)) {
|
const provider = await requestProvider();
|
||||||
|
if (provider && !wallets.some(a => a.kind === WalletKind.WebLN)) {
|
||||||
const newConfig = {
|
const newConfig = {
|
||||||
id: "webln",
|
id: "webln",
|
||||||
kind: WalletKind.WebLN,
|
kind: WalletKind.WebLN,
|
||||||
@ -96,17 +39,20 @@ export function setupWebLNWalletConfig(store: WalletStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class WebLNWallet implements LNWallet {
|
export class WebLNWallet implements LNWallet {
|
||||||
|
#provider?: WebLNProvider;
|
||||||
|
|
||||||
isReady(): boolean {
|
isReady(): boolean {
|
||||||
if (window.webln) {
|
return this.#provider !== undefined;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
return false;
|
canAutoLogin(): boolean {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getInfo(): Promise<WalletInfo> {
|
async getInfo(): Promise<WalletInfo> {
|
||||||
await this.login();
|
await this.login();
|
||||||
if (this.isReady()) {
|
if (this.isReady() && this.#provider) {
|
||||||
const rsp = await barrierWebLn(async () => await window.webln?.getInfo());
|
const rsp = await barrierQueue(WebLNQueue, async () => await unwrap(this.#provider).getInfo());
|
||||||
if (rsp) {
|
if (rsp) {
|
||||||
return {
|
return {
|
||||||
nodePubKey: rsp.node.pubkey,
|
nodePubKey: rsp.node.pubkey,
|
||||||
@ -120,8 +66,8 @@ export class WebLNWallet implements LNWallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async login(): Promise<boolean> {
|
async login(): Promise<boolean> {
|
||||||
if (window.webln && !window.webln.enabled) {
|
if (this.#provider === undefined) {
|
||||||
await window.webln.enable();
|
this.#provider = await requestProvider();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -137,9 +83,10 @@ export class WebLNWallet implements LNWallet {
|
|||||||
async createInvoice(req: InvoiceRequest): Promise<WalletInvoice> {
|
async createInvoice(req: InvoiceRequest): Promise<WalletInvoice> {
|
||||||
await this.login();
|
await this.login();
|
||||||
if (this.isReady()) {
|
if (this.isReady()) {
|
||||||
const rsp = await barrierWebLn(
|
const rsp = await barrierQueue(
|
||||||
|
WebLNQueue,
|
||||||
async () =>
|
async () =>
|
||||||
await window.webln?.makeInvoice({
|
await unwrap(this.#provider).makeInvoice({
|
||||||
amount: req.amount,
|
amount: req.amount,
|
||||||
defaultMemo: req.memo,
|
defaultMemo: req.memo,
|
||||||
})
|
})
|
||||||
@ -162,7 +109,7 @@ export class WebLNWallet implements LNWallet {
|
|||||||
if (!invoice) {
|
if (!invoice) {
|
||||||
throw new WalletError(WalletErrorCode.InvalidInvoice, "Could not parse invoice");
|
throw new WalletError(WalletErrorCode.InvalidInvoice, "Could not parse invoice");
|
||||||
}
|
}
|
||||||
const rsp = await barrierWebLn(async () => await window.webln?.sendPayment(pr));
|
const rsp = await barrierQueue(WebLNQueue, async () => await unwrap(this.#provider).sendPayment(pr));
|
||||||
if (rsp) {
|
if (rsp) {
|
||||||
invoice.state = WalletInvoiceState.Paid;
|
invoice.state = WalletInvoiceState.Paid;
|
||||||
invoice.preimage = rsp.preimage;
|
invoice.preimage = rsp.preimage;
|
||||||
|
@ -100,6 +100,7 @@ export type MilliSats = number;
|
|||||||
|
|
||||||
export interface LNWallet {
|
export interface LNWallet {
|
||||||
isReady(): boolean;
|
isReady(): boolean;
|
||||||
|
canAutoLogin(): boolean;
|
||||||
getInfo: () => Promise<WalletInfo>;
|
getInfo: () => Promise<WalletInfo>;
|
||||||
login: (password?: string) => Promise<boolean>;
|
login: (password?: string) => Promise<boolean>;
|
||||||
close: () => Promise<boolean>;
|
close: () => Promise<boolean>;
|
||||||
@ -140,8 +141,8 @@ export class WalletStore {
|
|||||||
configs: [],
|
configs: [],
|
||||||
});
|
});
|
||||||
this.load(false);
|
this.load(false);
|
||||||
setupWebLNWalletConfig(this);
|
|
||||||
this.snapshotState();
|
this.snapshotState();
|
||||||
|
setupWebLNWalletConfig(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
hook(fn: WalletStateHook) {
|
hook(fn: WalletStateHook) {
|
||||||
|
30
packages/app/src/WorkQueue.ts
Normal file
30
packages/app/src/WorkQueue.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export interface WorkQueueItem {
|
||||||
|
next: () => Promise<unknown>;
|
||||||
|
resolve(v: unknown): void;
|
||||||
|
reject(e: unknown): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function processWorkQueue(queue?: Array<WorkQueueItem>, queueDelay = 200) {
|
||||||
|
while (queue && queue.length > 0) {
|
||||||
|
const v = queue.shift();
|
||||||
|
if (v) {
|
||||||
|
try {
|
||||||
|
const ret = await v.next();
|
||||||
|
v.resolve(ret);
|
||||||
|
} catch (e) {
|
||||||
|
v.reject(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(() => processWorkQueue(queue, queueDelay), queueDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const barrierQueue = async <T>(queue: Array<WorkQueueItem>, then: () => Promise<T>): Promise<T> => {
|
||||||
|
return new Promise<T>((resolve, reject) => {
|
||||||
|
queue.push({
|
||||||
|
next: then,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
26
yarn.lock
26
yarn.lock
@ -2119,6 +2119,13 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz"
|
resolved "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz"
|
||||||
integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==
|
integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==
|
||||||
|
|
||||||
|
"@types/chrome@^0.0.74":
|
||||||
|
version "0.0.74"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.74.tgz#f69827c48fcf7fecc90c96089807661749a5a5e3"
|
||||||
|
integrity sha512-hzosS5CkQcIKCgxcsV2AzbJ36KNxG/Db2YEN/erEu7Boprg+KpMDLBQqKFmSo+JkQMGqRcicUyqCowJpuT+C6A==
|
||||||
|
dependencies:
|
||||||
|
"@types/filesystem" "*"
|
||||||
|
|
||||||
"@types/connect-history-api-fallback@^1.3.5":
|
"@types/connect-history-api-fallback@^1.3.5":
|
||||||
version "1.3.5"
|
version "1.3.5"
|
||||||
resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz"
|
resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz"
|
||||||
@ -2206,6 +2213,18 @@
|
|||||||
"@types/qs" "*"
|
"@types/qs" "*"
|
||||||
"@types/serve-static" "*"
|
"@types/serve-static" "*"
|
||||||
|
|
||||||
|
"@types/filesystem@*":
|
||||||
|
version "0.0.32"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.32.tgz#307df7cc084a2293c3c1a31151b178063e0a8edf"
|
||||||
|
integrity sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/filewriter" "*"
|
||||||
|
|
||||||
|
"@types/filewriter@*":
|
||||||
|
version "0.0.29"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.29.tgz#a48795ecadf957f6c0d10e0c34af86c098fa5bee"
|
||||||
|
integrity sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==
|
||||||
|
|
||||||
"@types/hoist-non-react-statics@^3.3.1":
|
"@types/hoist-non-react-statics@^3.3.1":
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz"
|
resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz"
|
||||||
@ -9153,6 +9172,13 @@ webidl-conversions@^4.0.2:
|
|||||||
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz"
|
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz"
|
||||||
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
|
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
|
||||||
|
|
||||||
|
webln@^0.3.2:
|
||||||
|
version "0.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/webln/-/webln-0.3.2.tgz#bbadf52916666b6059e3661ef5ab73a76b7cd0f4"
|
||||||
|
integrity sha512-YYT83aOCLup2AmqvJdKtdeBTaZpjC6/JDMe8o6x1kbTYWwiwrtWHyO//PAsPixF3jwFsAkj5DmiceB6w/QSe7Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/chrome" "^0.0.74"
|
||||||
|
|
||||||
webpack-bundle-analyzer@^4.8.0:
|
webpack-bundle-analyzer@^4.8.0:
|
||||||
version "4.8.0"
|
version "4.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz#951b8aaf491f665d2ae325d8b84da229157b1d04"
|
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz#951b8aaf491f665d2ae325d8b84da229157b1d04"
|
||||||
|
Loading…
Reference in New Issue
Block a user