mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
feat(wallet): Bitcoin Connect NWC "Connect wallet" flow [LOCAL — flag ON, do not push as-is]
Phase 0+1 of docs/bitcoin-connect-plan.md (#61). Adds Alby's Bitcoin Connect as a one-tap NWC connect front-end that feeds bitvid's EXISTING wallet pipeline; manual URI entry stays as the backup. - Vendored: pinned @getalby/[email protected].3 bundled by scripts/build-bitcoin-connect.mjs → vendor/bitcoin-connect.bundle.min.js (esbuild, browser ESM). crypto-shim aliases the L402-only `import('crypto')` bitvid never calls. `npm run build:bitcoin-connect` rebuilds it. - Flag: FEATURE_BITCOIN_CONNECT wired like FEATURE_AUDIO_INGEST (instance-config → config → constants). - UI: a "Connect wallet" button in the Wallet Connect pane (hidden unless the flag is on); the manual URI field stays below as the backup. - Controller: ProfileWalletController.handleBitcoinConnect() lazy-imports the bundle (only on click — the ~700KB never touches the main bundle), inits NWC-only with least-privilege requestMethods and persistConnection:false, then on onConnected extracts provider.client.nostrWalletConnectUrl → the SAME validate→save path as the manual field → disconnect() so bitvid solely holds the spending secret. The URI is never logged. NOTE: config/instance-config.js FEATURE_BITCOIN_CONNECT is TRUE for local testing only. Reset to false (shipping default) before pushing/deploying. Local commit; not pushed, not deployed. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
6dd40ce330
commit
69e87c6e15
12 files changed
+1663
-3
No files matched your search
@@ -860,6 +860,30 @@
|
||||
<p class="text-sm text-muted">
|
||||
Connect a Nostr Wallet Connect (NWC) compatible wallet to zap creators directly from bitvid.
|
||||
</p>
|
||||
<div id="profileWalletBitcoinConnectRow" class="hidden space-y-2">
|
||||
<button
|
||||
id="profileWalletBitcoinConnect"
|
||||
type="button"
|
||||
class="btn focus-ring inline-flex items-center gap-2"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M13 2 4 14h6l-1 8 9-12h-6l1-8Z"></path>
|
||||
</svg>
|
||||
Connect wallet
|
||||
</button>
|
||||
<p class="text-xs text-muted">
|
||||
One-tap connect via Bitcoin Connect (NWC) — or paste a connection manually below.
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<label class="block text-sm text-muted-strong" for="profileWalletUri">
|
||||
<span class="mb-2 block font-medium">Wallet Connect URI</span>
|
||||
|
||||
@@ -264,6 +264,18 @@ export const FEATURE_NIP71_INGEST = true;
|
||||
*/
|
||||
export const FEATURE_AUDIO_INGEST = false;
|
||||
|
||||
/**
|
||||
* Whether this deployment shows the "Connect wallet" button that uses Alby's
|
||||
* Bitcoin Connect for a smooth NWC connect flow (docs/bitcoin-connect-plan.md,
|
||||
* TODO #61). When on, the Wallet Connect pane offers Bitcoin Connect alongside
|
||||
* the manual URI field (which stays as the backup). It only lazy-loads the
|
||||
* vendored Bitcoin Connect bundle when the user actually clicks Connect.
|
||||
*
|
||||
* SHIPPING DEFAULT should be `false` ("off = no trace"). Currently `true` for
|
||||
* local testing only — reset to `false` before pushing/shipping until verified.
|
||||
*/
|
||||
export const FEATURE_BITCOIN_CONNECT = true;
|
||||
|
||||
/**
|
||||
* Card liveness visibility policy — what a non-owner video card does while its
|
||||
* CDN/WebTorrent liveness probes are still running:
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
ALLOW_NSFW_CONTENT,
|
||||
FEATURE_NIP71_INGEST,
|
||||
FEATURE_AUDIO_INGEST,
|
||||
FEATURE_BITCOIN_CONNECT,
|
||||
CARD_LIVENESS_POLICY,
|
||||
LIVENESS_PROBE_PREFETCH_MARGIN,
|
||||
DEFAULT_TRUST_SEED_NPUBS,
|
||||
@@ -123,6 +124,7 @@ export const ADMIN_COMMUNITY_BLACKLIST_PREFIX = "community:blacklist";
|
||||
export { ALLOW_NSFW_CONTENT };
|
||||
export { FEATURE_NIP71_INGEST };
|
||||
export { FEATURE_AUDIO_INGEST };
|
||||
export { FEATURE_BITCOIN_CONNECT };
|
||||
export { CARD_LIVENESS_POLICY };
|
||||
export { LIVENESS_PROBE_PREFETCH_MARGIN };
|
||||
export { DEFAULT_TRUST_SEED_NPUBS };
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
DEFAULT_PLAYBACK_START_TIMEOUT,
|
||||
FEATURE_NIP71_INGEST as CONFIG_FEATURE_NIP71_INGEST,
|
||||
FEATURE_AUDIO_INGEST as CONFIG_FEATURE_AUDIO_INGEST,
|
||||
FEATURE_BITCOIN_CONNECT as CONFIG_FEATURE_BITCOIN_CONNECT,
|
||||
CARD_LIVENESS_POLICY as CONFIG_CARD_LIVENESS_POLICY,
|
||||
LIVENESS_PROBE_PREFETCH_MARGIN as CONFIG_LIVENESS_PROBE_PREFETCH_MARGIN,
|
||||
IS_DEV_MODE,
|
||||
@@ -199,6 +200,9 @@ const DEFAULT_FLAGS = Object.freeze({
|
||||
// Default OFF ("off = no trace") — only an explicit `true` in the instance
|
||||
// config lights it up.
|
||||
FEATURE_AUDIO_INGEST: CONFIG_FEATURE_AUDIO_INGEST === true,
|
||||
// Bitcoin Connect NWC "Connect wallet" flow (docs/bitcoin-connect-plan.md, #61).
|
||||
// Default OFF; only an explicit `true` in the instance config enables it.
|
||||
FEATURE_BITCOIN_CONNECT: CONFIG_FEATURE_BITCOIN_CONNECT === true,
|
||||
FEATURE_HASHTAG_PREFERENCES: false,
|
||||
FEATURE_TRUST_SEEDS: true, // Rollback: disable to drop baseline trust seeds without code changes.
|
||||
FEATURE_TRUSTED_HIDE_CONTROLS: true,
|
||||
@@ -226,6 +230,7 @@ const runtimeFlags = (() => {
|
||||
FEATURE_NIP71_MIRROR: DEFAULT_FLAGS.FEATURE_NIP71_MIRROR,
|
||||
FEATURE_NIP71_INGEST: DEFAULT_FLAGS.FEATURE_NIP71_INGEST,
|
||||
FEATURE_AUDIO_INGEST: DEFAULT_FLAGS.FEATURE_AUDIO_INGEST,
|
||||
FEATURE_BITCOIN_CONNECT: DEFAULT_FLAGS.FEATURE_BITCOIN_CONNECT,
|
||||
FEATURE_HASHTAG_PREFERENCES: DEFAULT_FLAGS.FEATURE_HASHTAG_PREFERENCES,
|
||||
FEATURE_TRUST_SEEDS: DEFAULT_FLAGS.FEATURE_TRUST_SEEDS,
|
||||
FEATURE_TRUSTED_HIDE_CONTROLS: DEFAULT_FLAGS.FEATURE_TRUSTED_HIDE_CONTROLS,
|
||||
@@ -276,6 +281,11 @@ export let FEATURE_AUDIO_INGEST = coerceBoolean(
|
||||
DEFAULT_FLAGS.FEATURE_AUDIO_INGEST
|
||||
);
|
||||
|
||||
export let FEATURE_BITCOIN_CONNECT = coerceBoolean(
|
||||
runtimeFlags.FEATURE_BITCOIN_CONNECT,
|
||||
DEFAULT_FLAGS.FEATURE_BITCOIN_CONNECT
|
||||
);
|
||||
|
||||
export let FEATURE_HASHTAG_PREFERENCES = coerceBoolean(
|
||||
runtimeFlags.FEATURE_HASHTAG_PREFERENCES,
|
||||
DEFAULT_FLAGS.FEATURE_HASHTAG_PREFERENCES
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { devLogger } from "../../utils/logger.js";
|
||||
import { FEATURE_BITCOIN_CONNECT } from "../../constants.js";
|
||||
import { showConfirm } from "../confirmDialog.js";
|
||||
import { NWC_URI_SCHEME } from "../profileModalContract.js";
|
||||
|
||||
// Vendored, pinned Bitcoin Connect bundle (scripts/build-bitcoin-connect.mjs).
|
||||
// Lazy-imported only when the user clicks "Connect wallet", so its ~700KB never
|
||||
// weighs on the main bundle. See docs/bitcoin-connect-plan.md.
|
||||
const BITCOIN_CONNECT_BUNDLE_URL =
|
||||
"../../../vendor/bitcoin-connect.bundle.min.js";
|
||||
import { createWalletSyncService } from "../../services/walletSyncService.js";
|
||||
import { runWalletZappabilityCheck } from "./walletZappabilityCheck.js";
|
||||
import {
|
||||
@@ -32,6 +39,11 @@ export class ProfileWalletController {
|
||||
this.walletSyncRestoreBtn = null;
|
||||
this.walletSyncStatus = null;
|
||||
this._walletSyncService = null;
|
||||
|
||||
this.walletBitcoinConnectRow = null;
|
||||
this.walletBitcoinConnectButton = null;
|
||||
this._bitcoinConnectModulePromise = null;
|
||||
this._bitcoinConnectInitialized = false;
|
||||
}
|
||||
|
||||
cacheDomReferences() {
|
||||
@@ -46,9 +58,27 @@ export class ProfileWalletController {
|
||||
this.walletSyncToggle = document.getElementById("walletSyncToggle") || null;
|
||||
this.walletSyncRestoreBtn = document.getElementById("walletSyncRestoreBtn") || null;
|
||||
this.walletSyncStatus = document.getElementById("walletSyncStatus") || null;
|
||||
this.walletBitcoinConnectRow =
|
||||
document.getElementById("profileWalletBitcoinConnectRow") || null;
|
||||
this.walletBitcoinConnectButton =
|
||||
document.getElementById("profileWalletBitcoinConnect") || null;
|
||||
|
||||
// Backwards compatibility alias if needed by tests/external code
|
||||
this.mainController.profileWalletStatusText = this.walletStatusText;
|
||||
|
||||
this.applyBitcoinConnectVisibility();
|
||||
}
|
||||
|
||||
// Reveal the "Connect wallet" button only when the deployment enables the flag.
|
||||
// Off ⇒ the row stays hidden and the vendored bundle is never imported.
|
||||
applyBitcoinConnectVisibility() {
|
||||
if (!(this.walletBitcoinConnectRow instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
this.walletBitcoinConnectRow.classList.toggle(
|
||||
"hidden",
|
||||
FEATURE_BITCOIN_CONNECT !== true,
|
||||
);
|
||||
}
|
||||
|
||||
registerEventListeners() {
|
||||
@@ -90,6 +120,15 @@ export class ProfileWalletController {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
FEATURE_BITCOIN_CONNECT === true &&
|
||||
this.walletBitcoinConnectButton instanceof HTMLElement
|
||||
) {
|
||||
this.walletBitcoinConnectButton.addEventListener("click", () => {
|
||||
void this.handleBitcoinConnect();
|
||||
});
|
||||
}
|
||||
|
||||
if (this.walletSyncToggle instanceof HTMLElement) {
|
||||
this.walletSyncToggle.addEventListener("change", () => {
|
||||
void this.handleToggleSync();
|
||||
@@ -521,6 +560,104 @@ export class ProfileWalletController {
|
||||
return { valid: true, sanitized: value };
|
||||
}
|
||||
|
||||
// Lazy-load + one-time init of the vendored Bitcoin Connect bundle. Restricts
|
||||
// the modal to NWC and requests only the methods bitvid actually calls. Does
|
||||
// NOT let Bitcoin Connect persist the connection — bitvid owns the secret.
|
||||
async ensureBitcoinConnectModule() {
|
||||
if (this._bitcoinConnectModulePromise) {
|
||||
return this._bitcoinConnectModulePromise;
|
||||
}
|
||||
this._bitcoinConnectModulePromise = (async () => {
|
||||
const bc = await import(BITCOIN_CONNECT_BUNDLE_URL);
|
||||
if (!this._bitcoinConnectInitialized) {
|
||||
bc.init({
|
||||
appName: "bitvid",
|
||||
filters: ["nwc"],
|
||||
persistConnection: false,
|
||||
providerConfig: {
|
||||
nwc: {
|
||||
authorizationUrlOptions: {
|
||||
requestMethods: [
|
||||
"pay_invoice",
|
||||
"get_balance",
|
||||
"make_invoice",
|
||||
"lookup_invoice",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
// One persistent handler: extract the NWC URI, hand it to the SAME
|
||||
// validate→save path as the manual field, then disconnect so bitvid is
|
||||
// the only holder of the spending secret. NEVER log the URI.
|
||||
bc.onConnected(async (provider) => {
|
||||
const uri =
|
||||
typeof provider?.client?.nostrWalletConnectUrl === "string"
|
||||
? provider.client.nostrWalletConnectUrl
|
||||
: "";
|
||||
try {
|
||||
if (uri && this.walletUriInput instanceof HTMLElement) {
|
||||
this.setSecretInputValue(this.walletUriInput, uri);
|
||||
await this.handleWalletSave();
|
||||
} else if (!uri) {
|
||||
this.updateWalletStatus(
|
||||
"That wallet didn't expose an NWC connection. Paste one manually below.",
|
||||
"error",
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
devLogger.error("[wallet] Bitcoin Connect save failed", error);
|
||||
} finally {
|
||||
try {
|
||||
bc.disconnect();
|
||||
} catch (disconnectError) {
|
||||
devLogger.warn(
|
||||
"[wallet] Bitcoin Connect disconnect failed",
|
||||
disconnectError,
|
||||
);
|
||||
}
|
||||
try {
|
||||
bc.closeModal?.();
|
||||
} catch (closeError) {
|
||||
// non-fatal
|
||||
}
|
||||
}
|
||||
});
|
||||
this._bitcoinConnectInitialized = true;
|
||||
}
|
||||
return bc;
|
||||
})().catch((error) => {
|
||||
// Reset so a later click can retry a failed load.
|
||||
this._bitcoinConnectModulePromise = null;
|
||||
throw error;
|
||||
});
|
||||
return this._bitcoinConnectModulePromise;
|
||||
}
|
||||
|
||||
async handleBitcoinConnect() {
|
||||
if (FEATURE_BITCOIN_CONNECT !== true || this.isWalletBusy()) {
|
||||
return;
|
||||
}
|
||||
let bc;
|
||||
try {
|
||||
bc = await this.ensureBitcoinConnectModule();
|
||||
} catch (error) {
|
||||
devLogger.error("[wallet] Failed to load Bitcoin Connect", error);
|
||||
this.updateWalletStatus(
|
||||
"Couldn't load the wallet connector. Try again, or paste a connection manually.",
|
||||
"error",
|
||||
);
|
||||
this.mainController.showError?.("Couldn't load the wallet connector.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
bc.launchModal();
|
||||
} catch (error) {
|
||||
devLogger.error("[wallet] Bitcoin Connect launch failed", error);
|
||||
this.updateWalletStatus("Couldn't open the wallet connector.", "error");
|
||||
}
|
||||
}
|
||||
|
||||
async handleWalletSave() {
|
||||
const { uri, defaultZap, error } = this.getWalletFormValues();
|
||||
const context = {
|
||||
|
||||
Generated
+112
-3
@@ -20,6 +20,7 @@
|
||||
"ws": "^8.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@getalby/bitcoin-connect": "3.12.3",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"cssnano": "^7.1.2",
|
||||
"fake-indexeddb": "^6.2.5",
|
||||
@@ -832,6 +833,52 @@
|
||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@getalby/bitcoin-connect": {
|
||||
"version": "3.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@getalby/bitcoin-connect/-/bitcoin-connect-3.12.3.tgz",
|
||||
"integrity": "sha512-2d+tNHBCZBoImKJgZwTUOyK9ypXaTg/hwvtQ8EHbgSKKkuycOrks7SzlUc0iJHthD3JGFVMWZEgOgw4AQVVE4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@getalby/lightning-tools": "^8.1.1",
|
||||
"@getalby/sdk": "^8.0.3",
|
||||
"@lightninglabs/lnc-web": "^0.3.6-alpha",
|
||||
"qrcode-generator": "1.4.4",
|
||||
"zustand": "^5.0.13"
|
||||
}
|
||||
},
|
||||
"node_modules/@getalby/lightning-tools": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@getalby/lightning-tools/-/lightning-tools-8.2.0.tgz",
|
||||
"integrity": "sha512-eL+cnHyzeUARKVzNRuFBRBppAU5RBJOLjhFVzSWt8hDibRzL5+e4hq8I79+RGHfrWWMUDv382Jx8ewOazrBj7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"type": "lightning",
|
||||
"url": "lightning:[email protected]"
|
||||
}
|
||||
},
|
||||
"node_modules/@getalby/sdk": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@getalby/sdk/-/sdk-8.0.3.tgz",
|
||||
"integrity": "sha512-vPEogAWwLHbL55COeXrRN7yRBXMDGDxX1M2A+o3Lqw60FFng6fa9FK9sw8ptdILMD1dQZq8FzPXuabQ7seoDBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@getalby/lightning-tools": "^8.1.1",
|
||||
"nostr-tools": "^2.23.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
},
|
||||
"funding": {
|
||||
"type": "lightning",
|
||||
"url": "lightning:[email protected]"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
@@ -891,6 +938,24 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@lightninglabs/lnc-core": {
|
||||
"version": "0.3.6-alpha",
|
||||
"resolved": "https://registry.npmjs.org/@lightninglabs/lnc-core/-/lnc-core-0.3.6-alpha.tgz",
|
||||
"integrity": "sha512-ObzY5wi+PS0GQduFHYDjGJpYZxKD27vtMf3fUVne2gGNMej1mvvJAOwATgef8YV/IZodAzy73Z/F2zfaQ+iE4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@lightninglabs/lnc-web": {
|
||||
"version": "0.3.6-alpha",
|
||||
"resolved": "https://registry.npmjs.org/@lightninglabs/lnc-web/-/lnc-web-0.3.6-alpha.tgz",
|
||||
"integrity": "sha512-oiL1mKlbEjb1lcmNAHA0Gn+mlx5oQOJ8YVONJR/vgo4QLmL/48EyA12QbZ71mP9li45hGOoGqpYMRwBYtIeFog==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lightninglabs/lnc-core": "^0.3.6-alpha",
|
||||
"crypto-js": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/ciphers": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-2.1.1.tgz",
|
||||
@@ -2321,6 +2386,13 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/css-declaration-sorter": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.3.1.tgz",
|
||||
@@ -3789,9 +3861,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nostr-tools": {
|
||||
"version": "2.23.1",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.23.1.tgz",
|
||||
"integrity": "sha512-Q5SJ1omrseBFXtLwqDhufpFLA6vX3rS/IuBCc974qaYX6YKGwEPxa/ZsyxruUOr+b+5EpWL2hFmCB5AueYrfBw==",
|
||||
"version": "2.23.9",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.23.9.tgz",
|
||||
"integrity": "sha512-PBN3TpGh+EszlTZWpQdvcqEMqhcKcY1vTw0Xkkccg+TyP7y/icu9/iXJw72aFZ/ETm35ehU0hneGTXLaTSkImw==",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@noble/ciphers": "2.1.1",
|
||||
@@ -5046,6 +5118,13 @@
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode-generator": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz",
|
||||
"integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -6738,6 +6817,36 @@
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "5.0.14",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz",
|
||||
"integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=18.0.0",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=18.0.0",
|
||||
"use-sync-external-store": ">=1.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
"build:css": "postcss css/tailwind.source.css -o css/tailwind.generated.css",
|
||||
"build:beacon:bundle": "node scripts/build-beacon.mjs",
|
||||
"build:beacon": "npm run build:beacon:bundle && npm run lint:inline-styles",
|
||||
"build:bitcoin-connect": "node scripts/build-bitcoin-connect.mjs",
|
||||
"ci:beacon": "npm run build:beacon",
|
||||
"format": "prettier --cache --write css/tailwind.source.css css/tokens.css css/docs.css \"docs/**/*.html\" tailwind.config.cjs README.md CONTRIBUTING.md",
|
||||
"audit": "node scripts/daily-design-system-audit.mjs",
|
||||
@@ -57,6 +58,7 @@
|
||||
"lint:pool-access": "node scripts/check-direct-pool-access.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@getalby/bitcoin-connect": "3.12.3",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"cssnano": "^7.1.2",
|
||||
"fake-indexeddb": "^6.2.5",
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Bundles Alby's Bitcoin Connect into a single self-contained ESM file that
|
||||
// bitvid lazy-imports behind FEATURE_BITCOIN_CONNECT (docs/bitcoin-connect-plan.md).
|
||||
// Mirrors the floating-ui vendor step in build-beacon.mjs. Pinned via the exact
|
||||
// devDependency version in package.json.
|
||||
import { build } from "esbuild";
|
||||
import { mkdir, copyFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const entryPoint = path.join(repoRoot, "scripts/vendor/bitcoin-connect-entry.mjs");
|
||||
const vendorDir = path.join(repoRoot, "vendor");
|
||||
const outFile = path.join(vendorDir, "bitcoin-connect.bundle.min.js");
|
||||
const licenseSrc = path.join(
|
||||
repoRoot,
|
||||
"node_modules/@getalby/bitcoin-connect/LICENSE",
|
||||
);
|
||||
const licenseOut = path.join(vendorDir, "bitcoin-connect.LICENSE");
|
||||
|
||||
await mkdir(vendorDir, { recursive: true });
|
||||
|
||||
await build({
|
||||
bundle: true,
|
||||
entryPoints: [entryPoint],
|
||||
outfile: outFile,
|
||||
format: "esm",
|
||||
splitting: false,
|
||||
platform: "browser",
|
||||
target: ["es2020"],
|
||||
minify: true,
|
||||
sourcemap: false,
|
||||
define: {
|
||||
"process.env.NODE_ENV": '"production"',
|
||||
global: "globalThis",
|
||||
},
|
||||
// @getalby/lightning-tools lazily `import('crypto')` for L402 macaroons — a
|
||||
// path bitvid never calls. Alias it to a browser shim so the bundle resolves.
|
||||
alias: {
|
||||
crypto: path.join(repoRoot, "scripts/vendor/crypto-shim.mjs"),
|
||||
"node:crypto": path.join(repoRoot, "scripts/vendor/crypto-shim.mjs"),
|
||||
},
|
||||
logLevel: "info",
|
||||
});
|
||||
|
||||
try {
|
||||
await copyFile(licenseSrc, licenseOut);
|
||||
} catch (error) {
|
||||
console.warn("[build-bitcoin-connect] could not copy LICENSE:", error.message);
|
||||
}
|
||||
|
||||
console.log("[build-bitcoin-connect] wrote", path.relative(repoRoot, outFile));
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Vendor entry for Alby's Bitcoin Connect (getalby/bitcoin-connect), bundled by
|
||||
// scripts/build-bitcoin-connect.mjs into vendor/bitcoin-connect.bundle.min.js so
|
||||
// bitvid can lazy-import it behind FEATURE_BITCOIN_CONNECT without a runtime CDN.
|
||||
//
|
||||
// Importing the package self-registers its web components (bc-modal, etc.), so a
|
||||
// single import both loads the API and boots the UI. We re-export only the small
|
||||
// surface ProfileWalletController needs. See docs/bitcoin-connect-plan.md.
|
||||
export {
|
||||
init,
|
||||
launchModal,
|
||||
closeModal,
|
||||
onConnected,
|
||||
onDisconnected,
|
||||
disconnect,
|
||||
getConnectorConfig,
|
||||
isConnected,
|
||||
} from "@getalby/bitcoin-connect";
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
// Minimal browser shim for node's `crypto`, aliased in when bundling Bitcoin
|
||||
// Connect (scripts/build-bitcoin-connect.mjs). The only place its deps reach for
|
||||
// `crypto` is @getalby/lightning-tools' L402 macaroon helpers via a lazy
|
||||
// `await import('crypto')` — a path bitvid's NWC connect/pay flow never calls.
|
||||
// timingSafeEqual is implemented properly; the L402-only createHmac is stubbed so
|
||||
// it fails loudly rather than silently misbehaving if ever reached.
|
||||
export function timingSafeEqual(a, b) {
|
||||
const ua = a instanceof Uint8Array ? a : new Uint8Array(a);
|
||||
const ub = b instanceof Uint8Array ? b : new Uint8Array(b);
|
||||
if (ua.length !== ub.length) {
|
||||
return false;
|
||||
}
|
||||
let diff = 0;
|
||||
for (let i = 0; i < ua.length; i += 1) {
|
||||
diff |= ua[i] ^ ub[i];
|
||||
}
|
||||
return diff === 0;
|
||||
}
|
||||
|
||||
export function createHmac() {
|
||||
throw new Error(
|
||||
"createHmac (L402 macaroons) is not available in the bitvid browser bundle",
|
||||
);
|
||||
}
|
||||
|
||||
export default { timingSafeEqual, createHmac };
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Alby contributors <[email protected]> (https://getalby.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Vendored
+1249
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user