diff --git a/artifacts/depcheck.json b/artifacts/depcheck.json index 4990648a..eb45ee0c 100644 --- a/artifacts/depcheck.json +++ b/artifacts/depcheck.json @@ -1 +1 @@ -{"dependencies":[],"devDependencies":["autoprefixer","cross-env","postcss","prettier","serve","stylelint","tailwindcss"],"missing":{"@scure/base":["/app/vendor/crypto-helpers.entry.js","/app/tests/zap-split.test.mjs"],"https:":["/app/js/nostrToolsBootstrap.js"]},"using":{"playwright":["/app/debug_sidebar.mjs","/app/tests/embed_playback_check.spec.mjs"],"@playwright/test":["/app/playwright.config.ts","/app/tests/visual/embed-layout.spec.ts","/app/tests/visual/kitchen-sink.spec.ts","/app/tests/visual/moderation.spec.ts","/app/tests/visual/overlay-layers.spec.ts","/app/tests/visual/reduced-motion.spec.ts","/app/tests/e2e/popover.spec.ts","/app/tests/e2e/repro_issue.spec.ts","/app/tests/e2e/toast-service.spec.ts","/app/tests/e2e/ui-modals.spec.ts","/app/tests/e2e/video-modal-share-bug.spec.ts","/app/tests/e2e/webseed-playback.spec.ts","/app/tests/e2e/helpers/uiTestUtils.ts","/app/examples/reproducers/playwright.config.ts","/app/examples/reproducers/issue-sidebar-layout/repro.spec.ts","/app/examples/reproducers/issue-popover-viewport/repro.spec.ts","/app/examples/reproducers/issue-moderation-visibility/repro.spec.ts","/app/examples/reproducers/issue-moderation-ui/playwright.config.ts","/app/examples/reproducers/issue-moderation-ui/repro.spec.ts","/app/examples/reproducers/issue-1-moderation-ui/playwright.config.ts","/app/examples/reproducers/issue-1-moderation-ui/repro.spec.ts","/app/examples/reproducers/issue-003-popover-ref-error/repro.spec.ts","/app/examples/reproducers/issue-002-login-modal-nsec/repro.spec.ts","/app/examples/reproducers/003-video-modal-reg/repro.spec.ts","/app/examples/reproducers/002-sidebar-layout/repro.spec.ts","/app/examples/reproducers/001-popover-404/repro.spec.ts"],"prettier-plugin-tailwindcss":["/app/prettier.config.cjs"],"@tailwindcss/typography":["/app/tailwind.config.cjs"],"@scure/base":["/app/vendor/crypto-helpers.entry.js","/app/tests/zap-split.test.mjs"],"@noble/hashes":["/app/vendor/crypto-helpers.entry.js"],"jsdom":["/app/tests/design-system-metrics.test.mjs","/app/tests/discussion-count-service.test.mjs","/app/tests/edit-modal-submit-state.test.mjs","/app/tests/login-modal-controller.test.mjs","/app/tests/media-loader.test.mjs","/app/tests/modal-accessibility.test.mjs","/app/tests/nip71-form-manager.test.mjs","/app/tests/position-floating-panel.test.mjs","/app/tests/profile-modal-controller.test.mjs","/app/tests/subscriptions-manager.test.mjs","/app/tests/test-setup.mjs","/app/tests/video-card-source-visibility.test.mjs","/app/tests/video-modal-accessibility.test.mjs","/app/tests/ui/app-chrome-controller.test.mjs","/app/tests/ui/hashtag-strip-helper-bounce.test.mjs","/app/tests/ui/popoverEngine.test.mjs","/app/tests/ui/profile-modal-moderation-settings.test.mjs","/app/tests/ui/tag-pill-list.test.mjs","/app/tests/ui/tag-preference-menu.test.mjs","/app/tests/ui/url-health-controller.test.mjs","/app/tests/ui/video-list-view-popular-tags.test.mjs","/app/tests/ui/video-list-view-sorting.test.mjs","/app/tests/ui/helpers/jsdom-test-helpers.mjs","/app/tests/torrent/style-helpers.test.mjs","/app/tests/torrent/toast-service.test.mjs","/app/tests/services/link-preview-service.test.mjs","/app/tests/moderation/video-card.test.mjs","/app/tests/app/channel-profile-moderation.test.mjs","/app/tests/app/hash-change-handler.test.mjs","/app/tests/app/hydrate-sidebar-navigation.test.mjs","/app/examples/reproducers/issue-channel-moderation-test-crash/repro.mjs","/app/examples/reproducers/issue-001-video-card-logic/repro.mjs"],"fake-indexeddb":["/app/tests/dm-db.test.mjs","/app/tests/dm-normalization.test.mjs","/app/tests/nostr-session-actor.test.mjs","/app/tests/storage-service.test.mjs","/app/tests/ui/storageService.test.mjs","/app/tests/services/link-preview-service.test.mjs","/app/tests/nostr/dm-direct-message-flow.test.mjs","/app/tests/nostr/session-actor.test.mjs","/app/scripts/agent/smoke-test.mjs"],"nostr-tools":["/app/tests/dm-decryptor.test.mjs","/app/tests/nostr-private-key-signer.test.mjs","/app/tests/nostr-publish-rejection.test.mjs","/app/tests/nostr-send-direct-message.test.mjs","/app/tests/nostr/adapters.test.mjs","/app/tests/nostr/dm-direct-message-flow.test.mjs","/app/tests/nostr/dmSignalEvents.test.mjs","/app/tests/nostr/integration-remote-flow.test.mjs","/app/tests/nostr/nip46Client.test.js","/app/tests/e2e/repro_issue.spec.ts","/app/scripts/agent/interop-test.mjs","/app/scripts/agent/load-test-relay.mjs","/app/scripts/agent/load-test.mjs","/app/scripts/agent/simple-relay.mjs","/app/scripts/agent/smoke-test.mjs","/app/js/nostrToolsBootstrap.js"],"pixelmatch":["/app/tests/visual/kitchen-sink.spec.ts"],"pngjs":["/app/tests/visual/kitchen-sink.spec.ts"],"ws":["/app/tests/e2e/repro_issue.spec.ts","/app/scripts/agent/interop-test.mjs","/app/scripts/agent/load-test-relay.mjs","/app/scripts/agent/load-test.mjs","/app/scripts/agent/setup-test-env.js","/app/scripts/agent/simple-relay.mjs","/app/scripts/agent/smoke-test.mjs"],"esbuild":["/app/scripts/build-beacon.mjs"],"@floating-ui/dom":["/app/scripts/build-beacon.mjs","/app/scripts/vendor/floating-ui-dom-entry.mjs"],"https:":["/app/js/nostrToolsBootstrap.js"]},"invalidFiles":{},"invalidDirs":{}} +{"dependencies":["autoprefixer","postcss","tailwindcss"],"devDependencies":["autoprefixer","cross-env","prettier","serve","stylelint"],"missing":{"@scure/base":["/app/vendor/crypto-helpers.entry.js","/app/tests/zap-split.test.mjs"],"https:":["/app/js/nostrToolsBootstrap.js"]},"using":{"playwright":["/app/debug_sidebar.mjs","/app/tests/embed_playback_check.spec.mjs","/app/scripts/agent/smoke-test.mjs"],"@playwright/test":["/app/playwright.config.ts","/app/tests/visual/embed-layout.spec.ts","/app/tests/visual/kitchen-sink.spec.ts","/app/tests/visual/moderation.spec.ts","/app/tests/visual/overlay-layers.spec.ts","/app/tests/visual/reduced-motion.spec.ts","/app/tests/e2e/popover.spec.ts","/app/tests/e2e/repro_issue.spec.ts","/app/tests/e2e/toast-service.spec.ts","/app/tests/e2e/ui-modals.spec.ts","/app/tests/e2e/video-modal-share-bug.spec.ts","/app/tests/e2e/webseed-playback.spec.ts","/app/tests/e2e/helpers/uiTestUtils.ts","/app/examples/reproducers/playwright.config.ts","/app/examples/reproducers/popover-flip-bug/repro.spec.ts","/app/examples/reproducers/issue-sidebar-layout/repro.spec.ts","/app/examples/reproducers/issue-popover-viewport/repro.spec.ts","/app/examples/reproducers/issue-moderation-visibility/repro.spec.ts","/app/examples/reproducers/issue-moderation-ui/playwright.config.ts","/app/examples/reproducers/issue-moderation-ui/repro.spec.ts","/app/examples/reproducers/issue-1-moderation-ui/playwright.config.ts","/app/examples/reproducers/issue-1-moderation-ui/repro.spec.ts","/app/examples/reproducers/issue-003-popover-ref-error/repro.spec.ts","/app/examples/reproducers/issue-002-login-modal-nsec/repro.spec.ts","/app/examples/reproducers/003-video-modal-reg/repro.spec.ts","/app/examples/reproducers/002-sidebar-layout/repro.spec.ts","/app/examples/reproducers/001-popover-404/repro.spec.ts"],"prettier-plugin-tailwindcss":["/app/prettier.config.cjs"],"@tailwindcss/typography":["/app/tailwind.config.cjs"],"@scure/base":["/app/vendor/crypto-helpers.entry.js","/app/tests/zap-split.test.mjs"],"@noble/hashes":["/app/vendor/crypto-helpers.entry.js"],"jsdom":["/app/tests/design-system-metrics.test.mjs","/app/tests/discussion-count-service.test.mjs","/app/tests/edit-modal-submit-state.test.mjs","/app/tests/login-modal-controller.test.mjs","/app/tests/media-loader.test.mjs","/app/tests/modal-accessibility.test.mjs","/app/tests/nip71-form-manager.test.mjs","/app/tests/position-floating-panel.test.mjs","/app/tests/profile-modal-controller.test.mjs","/app/tests/subscriptions-manager.test.mjs","/app/tests/test-setup.mjs","/app/tests/video-card-source-visibility.test.mjs","/app/tests/video-modal-accessibility.test.mjs","/app/tests/ui/app-chrome-controller.test.mjs","/app/tests/ui/hashtag-strip-helper-bounce.test.mjs","/app/tests/ui/popoverEngine.test.mjs","/app/tests/ui/profile-modal-moderation-settings.test.mjs","/app/tests/ui/tag-pill-list.test.mjs","/app/tests/ui/tag-preference-menu.test.mjs","/app/tests/ui/url-health-controller.test.mjs","/app/tests/ui/video-list-view-popular-tags.test.mjs","/app/tests/ui/video-list-view-sorting.test.mjs","/app/tests/ui/helpers/jsdom-test-helpers.mjs","/app/tests/torrent/style-helpers.test.mjs","/app/tests/torrent/toast-service.test.mjs","/app/tests/services/link-preview-service.test.mjs","/app/tests/moderation/video-card.test.mjs","/app/tests/app/channel-profile-moderation.test.mjs","/app/tests/app/hash-change-handler.test.mjs","/app/tests/app/hydrate-sidebar-navigation.test.mjs","/app/examples/reproducers/video-card-bug/repro.mjs","/app/examples/reproducers/issue-channel-moderation-test-crash/repro.mjs","/app/examples/reproducers/issue-001-video-card-logic/repro.mjs"],"fake-indexeddb":["/app/tests/dm-db.test.mjs","/app/tests/dm-normalization.test.mjs","/app/tests/nostr-session-actor.test.mjs","/app/tests/storage-service.test.mjs","/app/tests/ui/storageService.test.mjs","/app/tests/services/link-preview-service.test.mjs","/app/tests/nostr/dm-direct-message-flow.test.mjs","/app/tests/nostr/session-actor.test.mjs"],"nostr-tools":["/app/tests/dm-decryptor.test.mjs","/app/tests/nostr-private-key-signer.test.mjs","/app/tests/nostr-publish-rejection.test.mjs","/app/tests/nostr-send-direct-message.test.mjs","/app/tests/nostr/adapters.test.mjs","/app/tests/nostr/dm-direct-message-flow.test.mjs","/app/tests/nostr/dmSignalEvents.test.mjs","/app/tests/nostr/integration-remote-flow.test.mjs","/app/tests/nostr/nip46Client.test.js","/app/tests/e2e/repro_issue.spec.ts","/app/scripts/agent/interop-test.mjs","/app/scripts/agent/load-test-relay.mjs","/app/scripts/agent/load-test.mjs","/app/scripts/agent/simple-relay.mjs","/app/scripts/agent/smoke-test.mjs","/app/js/nostrToolsBootstrap.js"],"pixelmatch":["/app/tests/visual/kitchen-sink.spec.ts"],"pngjs":["/app/tests/visual/kitchen-sink.spec.ts"],"ws":["/app/tests/e2e/repro_issue.spec.ts","/app/scripts/agent/load-test-relay.mjs","/app/scripts/agent/load-test.mjs","/app/scripts/agent/setup-test-env.js","/app/scripts/agent/simple-relay.mjs","/app/scripts/agent/validate-events.mjs"],"esbuild":["/app/scripts/build-beacon.mjs"],"@floating-ui/dom":["/app/scripts/build-beacon.mjs","/app/scripts/vendor/floating-ui-dom-entry.mjs"],"https:":["/app/js/nostrToolsBootstrap.js"]},"invalidFiles":{},"invalidDirs":{}} diff --git a/js/adminListStore.js b/js/adminListStore.js index afed1d2f..c3ca3698 100644 --- a/js/adminListStore.js +++ b/js/adminListStore.js @@ -270,7 +270,7 @@ export const readCachedAdminState = () => { } }; -export const writeCachedAdminState = (state) => { +const writeCachedAdminState = (state) => { const policy = CACHE_POLICIES[NOTE_TYPES.ADMIN_MODERATION_LIST]; if (policy?.storage !== STORAGE_TIERS.LOCAL_STORAGE) { return; diff --git a/js/config.js b/js/config.js index d8c31343..6e32bee7 100644 --- a/js/config.js +++ b/js/config.js @@ -80,10 +80,8 @@ export const isVerboseDevMode = verboseDevModeOverride === null ? Boolean(IS_VERBOSE_DEV_MODE) : verboseDevModeOverride; -export { IS_VERBOSE_DEV_MODE }; export const isLockdownMode = Boolean(IS_LOCKDOWN_MODE); -export { IS_LOCKDOWN_MODE }; export { LOCKDOWN_TITLE, LOCKDOWN_MESSAGE }; if (typeof window !== "undefined") { @@ -110,7 +108,6 @@ export { TIP_JAR_URL }; export { MAX_WALLET_DEFAULT_ZAP }; export { PLATFORM_FEE_PERCENT }; export { PLATFORM_LUD16_OVERRIDE }; -export { DEFAULT_RELAY_URLS_OVERRIDE }; export const ADMIN_EDITORS_NPUBS = []; // Default moderators (optional) export const ADMIN_LIST_NAMESPACE = "bitvid:admin"; // Reserved for Nostr lists export const ADMIN_COMMUNITY_BLACKLIST_SOURCES = "community:blacklist:sources"; diff --git a/js/magnets.js b/js/magnets.js index c280014d..05a32885 100644 --- a/js/magnets.js +++ b/js/magnets.js @@ -1,23 +1,4 @@ -import { extractBtihFromMagnet, normalizeInfoHash } from "./magnetShared.js"; - -function extractInfoHashFromXt(value) { - if (typeof value !== "string") { - return ""; - } - - const trimmed = value.trim(); - if (!trimmed) { - return ""; - } - - const lower = trimmed.toLowerCase(); - if (!lower.startsWith("urn:btih:")) { - return ""; - } - - const payload = trimmed.slice(9); - return normalizeInfoHash(payload); -} +import { extractBtihFromMagnet } from "./magnetShared.js"; export function infoHashFromMagnet(magnet) { if (typeof magnet !== "string") { @@ -26,71 +7,3 @@ export function infoHashFromMagnet(magnet) { const extracted = extractBtihFromMagnet(magnet); return extracted || null; } - -export function trackersFromMagnet(magnet) { - if (typeof magnet !== "string") { - return []; - } - const parsed = parseMagnetLite(magnet); - if (!parsed || !Array.isArray(parsed.announce)) { - return []; - } - const deduped = new Set(); - parsed.announce.forEach((url) => { - if (typeof url !== "string") { - return; - } - const trimmed = url.trim(); - if (!trimmed) { - return; - } - deduped.add(trimmed); - }); - return Array.from(deduped); -} - -function parseMagnetLite(magnet) { - if (typeof magnet !== "string") { - return { infoHash: "", announce: [] }; - } - - const trimmed = magnet.trim(); - if (!trimmed) { - return { infoHash: "", announce: [] }; - } - - const lower = trimmed.toLowerCase(); - const queryIndex = lower.indexOf("?"); - const paramsPart = queryIndex >= 0 ? trimmed.slice(queryIndex + 1) : ""; - const params = paramsPart.split("&"); - const announce = []; - let infoHash = ""; - - for (const param of params) { - if (!param) { - continue; - } - - const [rawKey, ...rawValueParts] = param.split("="); - const rawValue = rawValueParts.join("="); - const key = decodeURIComponent((rawKey || "").replace(/\+/g, " ")).toLowerCase(); - const value = decodeURIComponent((rawValue || "").replace(/\+/g, " ")); - - if (!key) { - continue; - } - - if (key === "xt") { - const derived = extractInfoHashFromXt(value); - if (derived && !infoHash) { - infoHash = derived; - } - } else if (key === "tr") { - if (value) { - announce.push(value); - } - } - } - - return { infoHash, announce }; -} diff --git a/js/nostr/nip71.js b/js/nostr/nip71.js index 25e5cf6a..1517cb60 100644 --- a/js/nostr/nip71.js +++ b/js/nostr/nip71.js @@ -843,7 +843,7 @@ export function buildVideoPointerValue(pubkey, videoRootId) { return `30078:${normalizedPubkey}:${normalizedRoot}`; } -export function buildNip71PointerTags({ +function buildNip71PointerTags({ pubkey = "", videoRootId = "", videoEventId = "", diff --git a/js/nostr/toolkit.js b/js/nostr/toolkit.js index 4ee1a134..d230c5dc 100644 --- a/js/nostr/toolkit.js +++ b/js/nostr/toolkit.js @@ -335,8 +335,6 @@ export function resolveSimplePoolConstructor(tools, scope = globalScope) { return null; } -export { isSimplePoolConstructor, unwrapSimplePool }; - export function shimLegacySimplePoolMethods(pool) { if (!pool || typeof pool !== "object" || pool[SIMPLE_POOL_SHIM_KEY]) { return pool || null; diff --git a/js/themeController.js b/js/themeController.js index c5858696..2637ac17 100644 --- a/js/themeController.js +++ b/js/themeController.js @@ -307,19 +307,7 @@ const applyTheme = (theme, { persist = true } = {}) => { return normalized; }; -export const setThemeAccentOverrides = (overrides) => { - setAccentOverrides(overrides); - - const root = getRootElement(); - if (!root) { - return; - } - - const theme = currentTheme || FALLBACK_THEME; - applyAccentOverrides(root, theme); -}; - -export const setTheme = (theme) => { +const setTheme = (theme) => { if (!isBrowser()) { currentTheme = normalizeTheme(theme) || FALLBACK_THEME; return currentTheme; diff --git a/js/utils/qrcode.js b/js/utils/qrcode.js index 20bb2932..a86e4239 100644 --- a/js/utils/qrcode.js +++ b/js/utils/qrcode.js @@ -3,10 +3,7 @@ const hasDocument = typeof document.createElement === "function" && typeof document.createElementNS === "function"; -let QRCodeExport = null; -let createQrCodeExport = null; let renderQrCodeExport = null; -let updateQrCodeExport = null; if (hasDocument) { /** @@ -750,56 +747,15 @@ if (hasDocument) { QRCode.CorrectLevel = QRErrorCorrectLevel; })(); - QRCodeExport = QRCode; - - createQrCodeExport = function createQrCode(element, options = {}) { - return new QRCode(element, options); - }; - renderQrCodeExport = function renderQrCode(element, text, options = {}) { const normalizedOptions = Object.assign({}, options, { text }); const instance = new QRCode(element, normalizedOptions); return instance; }; - - updateQrCodeExport = function updateQrCode(instance, text) { - if (!instance || typeof instance.makeCode !== "function") { - throw new Error("Invalid QRCode instance"); - } - instance.makeCode(text); - return instance; - }; } else { - class QRCodeStub { - constructor() { - throw new Error("QR code rendering requires a DOM environment."); - } - - makeCode() { - throw new Error("QR code rendering requires a DOM environment."); - } - - clear() {} - } - - QRCodeExport = QRCodeStub; - - createQrCodeExport = function createQrCodeStub() { - throw new Error("QR code rendering requires a DOM environment."); - }; - renderQrCodeExport = function renderQrCodeStub() { throw new Error("QR code rendering requires a DOM environment."); }; - - updateQrCodeExport = function updateQrCodeStub() { - throw new Error("QR code rendering requires a DOM environment."); - }; } -export { QRCodeExport as QRCode }; -export { - createQrCodeExport as createQrCode, - renderQrCodeExport as renderQrCode, - updateQrCodeExport as updateQrCode, -}; +export { renderQrCodeExport as renderQrCode }; diff --git a/scripts/agent/find-unused-exports.mjs b/scripts/agent/find-unused-exports.mjs new file mode 100644 index 00000000..751385a7 --- /dev/null +++ b/scripts/agent/find-unused-exports.mjs @@ -0,0 +1,73 @@ +import fs from 'fs'; +import path from 'path'; +import { execSync } from 'child_process'; + +function getExportedSymbols(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + const symbols = []; + + // Regex to capture exported names + const exportRegex = /export\s+(?:const|let|var|function|class)\s+([a-zA-Z0-9_$]+)/g; + let match; + while ((match = exportRegex.exec(content)) !== null) { + symbols.push(match[1]); + } + + // Handle export default + if (/export\s+default/.test(content)) { + // For default export, we can't easily get a name to grep for usage unless we look at imports. + // We'll skip default exports for this simple heuristic script. + } + + // Handle export { name } + const exportListRegex = /export\s+\{([^}]+)\}/g; + while ((match = exportListRegex.exec(content)) !== null) { + const names = match[1].split(',').map(s => s.trim().split(' as ')[0].trim()); + symbols.push(...names); + } + + return symbols; +} + +function findUsages(symbol, filePath) { + // Grep for the symbol in the whole js directory, excluding the defining file + // We use word boundary \b to avoid partial matches + try { + const cmd = `grep -r "\\b${symbol}\\b" js --exclude="${path.basename(filePath)}" | wc -l`; + const count = parseInt(execSync(cmd, { encoding: 'utf-8' }).trim()); + return count; + } catch (e) { + return 0; + } +} + +function scanDir(dir) { + const files = fs.readdirSync(dir); + for (const file of files) { + const fullPath = path.join(dir, file); + const stat = fs.statSync(fullPath); + if (stat.isDirectory()) { + scanDir(fullPath); + } else if (file.endsWith('.js') || file.endsWith('.mjs')) { + const symbols = getExportedSymbols(fullPath); + for (const symbol of symbols) { + const usages = findUsages(symbol, fullPath); + if (usages === 0) { + // Check if it is used in tests + try { + const testCmd = `grep -r "\\b${symbol}\\b" tests | wc -l`; + const testCount = parseInt(execSync(testCmd, { encoding: 'utf-8' }).trim()); + if (testCount === 0) { + console.log(`${fullPath}: ${symbol}`); + } + } catch(e) { + console.log(`${fullPath}: ${symbol}`); + } + } + } + } + } +} + +console.log("Scanning for unused exports..."); +scanDir('js'); diff --git a/tests/admin-list-store.test.mjs b/tests/admin-list-store.test.mjs index 5cb39353..e8309d15 100644 --- a/tests/admin-list-store.test.mjs +++ b/tests/admin-list-store.test.mjs @@ -11,6 +11,8 @@ if (!globalThis.window.NostrTools) { globalThis.window.NostrTools = {}; } +globalThis.__BITVID_DEV_MODE_OVERRIDE__ = true; + const mockNip19 = { decode(value) { if (typeof value !== "string") {