Refactor Nostr client imports

This commit is contained in:
thePR0M3TH3AN committed 2025-10-22 20:55:54 -04:00
1 parent 070f326759
commit 75ea73b178
14 files changed
+113 -41

No files matched your search

+2 -2
View File
@@ -12,11 +12,11 @@ Most operators can ship with the defaults—24-hour deduplication, a 90-day back
### Anonymous session keys
Playback telemetry runs even when a viewer has not connected a Nostr account. The client first calls [`ensureSessionActor`](../js/nostr.js#L2011-L2105) to mint or restore an ephemeral keypair stored in `localStorage`. That session actor signs view events until the user authenticates, ensuring relays still see consistent pubkeys without blocking anonymous playback.
Playback telemetry runs even when a viewer has not connected a Nostr account. The client first calls [`ensureSessionActor`](../js/nostr/client.js#L2445-L2504) to mint or restore an ephemeral keypair stored in `localStorage`. That session actor signs view events until the user authenticates, ensuring relays still see consistent pubkeys without blocking anonymous playback.
### Publish latency expectations
When a player crosses the 12-second watch threshold, bitvid invokes [`publishViewEvent`](../js/nostr.js#L2159-L2323) through the view-only [`recordVideoView`](../js/nostr.js#L4516-L4555) contract. The helper considers the publish successful as soon as any configured relay acknowledges the event, which in practice keeps latency under a second on public relays. The UI optimistically increments totals by feeding the signed event into [`ingestLocalViewEvent`](../js/viewCounter.js#L608-L640), so cards and modals reflect the new view immediately even while other relays finish syncing.
When a player crosses the 12-second watch threshold, bitvid invokes [`publishViewEvent`](../js/nostr/viewEvents.js#L662-L838) through the view-only [`recordVideoView`](../js/nostr/viewEvents.js#L854-L879) contract. The helper considers the publish successful as soon as any configured relay acknowledges the event, which in practice keeps latency under a second on public relays. The UI optimistically increments totals by feeding the signed event into [`ingestLocalViewEvent`](../js/viewCounter.js#L619-L633), so cards and modals reflect the new view immediately even while other relays finish syncing.
The watch history pipeline has been retired; `recordVideoView` now focuses solely on analytics publishing.
+2 -2
View File
@@ -48,7 +48,7 @@ Authentication providers should register their capabilities with the
`nostrClient` after login so every publish helper can reuse them:
```js
import { setActiveSigner } from "./nostr.js";
import { setActiveSigner } from "./nostr/index.js";
setActiveSigner({
type: "extension", // optional label, used to request NIP-07 permissions
@@ -143,7 +143,7 @@ plus an optional leading `['head','1']` marker so relays can prioritize the firs
ciphertext. All chunk content is encrypted with NIP-04 and stores only pointer
entries; richer metadata remains on-device via the
[`WatchHistoryService`](../js/watchHistoryService.js) APIs, which default to
pointer-only writes and local-only metadata caches.【F:config/instance-config.js†L60-L78】【F:js/nostrEventSchemas.js†L157-L189】【F:js/nostr.js†L2329-L2369】【F:js/watchHistoryService.js†L331-L376】
pointer-only writes and local-only metadata caches.【F:config/instance-config.js†L60-L78】【F:js/nostrEventSchemas.js†L157-L189】【F:js/nostr/watchHistory.js†L1343-L1400】【F:js/watchHistoryService.js†L331-L376】
Refer to the [`WatchHistoryService`](../js/watchHistoryService.js) for queue
management hooks, manual snapshot helpers, and metadata toggle controls that
+6 -6
View File
@@ -16,16 +16,16 @@ Snapshots publish two event types to the shared `WATCH_HISTORY_KIND` stream:
`['encrypted','nip04']`. The first chunk is additionally marked as
`['head','1']` and may append `"a"` tags that reference previous chunk
addresses, allowing readers to reconstruct the full set even when relays
deduplicate aggressively.【F:js/nostrEventSchemas.js†L175-L189】【F:js/nostr.js†L2329-L2369】
deduplicate aggressively.【F:js/nostrEventSchemas.js†L175-L189】【F:js/nostr/watchHistory.js†L1343-L1400】
2. **Pointer event** — After chunks are accepted, bitvid emits a compact index
event containing the canonical list identifier (`['d', WATCH_HISTORY_LIST_IDENTIFIER]`),
the `snapshot` label, the total chunk count, and an `a` tag for each chunk
address.【F:js/nostrEventSchemas.js†L157-L170】【F:js/nostr.js†L2425-L2441】
address.【F:js/nostrEventSchemas.js†L157-L170】【F:js/nostr/watchHistory.js†L1434-L1462】
Chunk content is a NIP-04 encrypted JSON envelope of `{ version: 2, snapshot,
chunkIndex, totalChunks, items[] }`. Items are pointer descriptors (`e` or `a`
references plus optional relay hints) so that relays never learn which titles
were played—only which events or addresses the client can dereference later.【F:js/nostr.js†L2337-L2364】
were played—only which events or addresses the client can dereference later.【F:js/nostr/watchHistory.js†L1362-L1375】
## Batching configuration
@@ -34,7 +34,7 @@ optionally cap each response by defining `WATCH_HISTORY_BATCH_PAGE_SIZE` in
`config/instance-config.js`. Leaving the page size unset returns the full
`WATCH_HISTORY_MAX_ITEMS` window while still letting the UI paginate locally,
but setting a positive integer keeps API payloads in lockstep with batched
renderers like `historyView`.【F:config/instance-config.js†L101-L123】【F:js/nostr.js†L2987-L2998】【F:js/historyView.js†L1-L38】
renderers like `historyView`.【F:config/instance-config.js†L101-L123】【F:js/nostr/watchHistory.js†L2218-L2256】【F:js/historyView.js†L1-L38】
## Snapshot cadence
@@ -52,7 +52,7 @@ When a snapshot publish fails but the response is retryable, the service stores
the returned `snapshotId`, records the originating reason, and delegates to the
Nostr client to retry with exponential backoff. Retries start after two seconds,
double per attempt with 25% jitter, cap at five minutes, and stop after eight
attempts unless a publish succeeds sooner.【F:js/watchHistoryService.js†L948-L985】【F:js/watchHistoryService.js†L723-L777】【F:js/nostr.js†L1988-L2087】
attempts unless a publish succeeds sooner.【F:js/watchHistoryService.js†L948-L985】【F:js/watchHistoryService.js†L723-L777】【F:js/nostr/watchHistory.js†L929-L999】
## Legacy payload handling
@@ -60,7 +60,7 @@ Readers hydrate history by combining the pointer event, any encrypted chunks,
and fallback metadata embedded in legacy watch-history lists. The resolver still
accepts the `watch-history:v2:index` identifier, merges pointer tags from the
index event, and falls back to plaintext content when decryption fails so older
relays remain compatible.【F:config/instance-config.js†L60-L78】【F:js/nostr.js†L2704-L2779】【F:js/nostr.js†L5216-L5264】
relays remain compatible.【F:config/instance-config.js†L60-L78】【F:js/nostr/watchHistory.js†L2120-L2170】【F:js/nostr/watchHistory.js†L660-L689】
## Local metadata toggles
+4 -1
View File
@@ -1,6 +1,7 @@
// js/app.js
import { nostrClient, recordVideoView as recordVideoViewApi } from "./nostr.js";
import { nostrClient } from "./nostr.js";
import { recordVideoView } from "./nostr/index.js";
import { torrentClient } from "./webtorrent.js";
import {
isDevMode,
@@ -137,6 +138,8 @@ import {
urlHealthConstants,
} from "./state/cache.js";
const recordVideoViewApi = (...args) => recordVideoView(nostrClient, ...args);
const UNSUPPORTED_BTITH_MESSAGE =
"This magnet link is missing a compatible BitTorrent v1 info hash.";
+2 -4
View File
@@ -1,9 +1,7 @@
// js/channelProfile.js
import {
nostrClient,
convertEventToVideo as sharedConvertEventToVideo,
} from "./nostr.js";
import { nostrClient } from "./nostr.js";
import { convertEventToVideo as sharedConvertEventToVideo } from "./nostr/index.js";
import { DEFAULT_RELAY_URLS } from "./nostr/toolkit.js";
import { subscriptions } from "./subscriptions.js";
import { attachHealthBadges } from "./gridHealth.js";
+11 -1
View File
@@ -1,7 +1,10 @@
// js/historyView.js
import watchHistoryService from "./watchHistoryService.js";
import { nostrClient, updateWatchHistoryList } from "./nostr.js";
import { nostrClient } from "./nostr.js";
import {
updateWatchHistoryList as updateWatchHistoryListWithManager,
} from "./nostr/index.js";
import { pointerKey, normalizePointerInput } from "./nostr/watchHistory.js";
import {
WATCH_HISTORY_BATCH_RESOLVE,
@@ -33,6 +36,13 @@ const FALLBACK_AVATAR = "assets/svg/default-profile.svg";
const isDevEnv =
typeof process !== "undefined" && process?.env?.NODE_ENV !== "production";
const updateWatchHistoryList = (items, options) =>
updateWatchHistoryListWithManager(
nostrClient.watchHistory,
items,
options,
);
function escapeSelector(value) {
if (typeof value !== "string") {
return "";
+53
View File
@@ -0,0 +1,53 @@
export {
NostrClient,
getActiveSigner,
setActiveSigner,
clearActiveSigner,
resolveActiveSigner,
shouldRequestExtensionPermissions,
} from "./client.js";
export {
DEFAULT_NIP07_ENCRYPTION_METHODS,
DEFAULT_NIP07_PERMISSION_METHODS,
} from "./nip07Permissions.js";
export {
buildNip71MetadataTags,
buildNip71VideoEvent,
collectNip71PointerRequests,
convertEventToVideo,
extractNip71MetadataFromTags,
getDTagValueFromTags,
mergeNip71MetadataIntoVideo,
populateNip71MetadataForVideos,
processNip71Events,
buildVideoPointerValue,
stringFromInput,
} from "./nip71.js";
export {
normalizePointerInput,
pointerKey,
chunkWatchHistoryPayloadItems,
normalizeActorKey,
parseWatchHistoryContentWithFallback,
isNip04EncryptedWatchHistoryEvent,
watchHistoryHelpers,
updateWatchHistoryList,
removeWatchHistoryItem,
} from "./watchHistory.js";
export {
deriveViewEventBucketIndex,
deriveViewEventPointerScope,
hasRecentViewPublish,
rememberViewPublish,
createVideoViewEventFilters,
getViewEventGuardWindowMs,
listVideoViewEvents,
subscribeVideoViewEvents,
countVideoViewEvents,
publishViewEvent,
recordVideoView,
} from "./viewEvents.js";
+3 -5
View File
@@ -1,10 +1,8 @@
import { isDevMode } from "./config.js";
import { DEFAULT_RELAY_URLS } from "./nostr/toolkit.js";
import {
getActiveSigner,
nostrClient,
requestDefaultExtensionPermissions,
} from "./nostr.js";
import { nostrClient } from "./nostr.js";
import { getActiveSigner } from "./nostr/index.js";
import { requestDefaultExtensionPermissions } from "./nostrClientRegistry.js";
import { buildRelayListEvent } from "./nostrEventSchemas.js";
import { devLogger, userLogger } from "./utils/logger.js";
import {
+1 -1
View File
@@ -7,7 +7,7 @@ import {
getCurrentUserNpub,
} from "../state/appState.js";
import { userLogger } from "../utils/logger.js";
import { requestDefaultExtensionPermissions } from "../nostr.js";
import { requestDefaultExtensionPermissions } from "../nostrClientRegistry.js";
import {
getSavedProfiles,
getActiveProfilePubkey,
+2 -4
View File
@@ -1,7 +1,5 @@
import {
nostrClient,
convertEventToVideo,
} from "../nostr.js";
import { nostrClient } from "../nostr.js";
import { convertEventToVideo } from "../nostr/index.js";
import { accessControl } from "../accessControl.js";
import { ALLOW_NSFW_CONTENT } from "../config.js";
import { userLogger } from "../utils/logger.js";
+10 -5
View File
@@ -1,12 +1,12 @@
// js/subscriptions.js
import { nostrClient } from "./nostr.js";
import {
getActiveSigner,
nostrClient,
convertEventToVideo as sharedConvertEventToVideo,
requestDefaultExtensionPermissions,
listVideoViewEvents as listVideoViewEventsApi,
subscribeVideoViewEvents as subscribeVideoViewEventsApi,
} from "./nostr.js";
listVideoViewEvents,
subscribeVideoViewEvents,
} from "./nostr/index.js";
import { requestDefaultExtensionPermissions } from "./nostrClientRegistry.js";
import { DEFAULT_RELAY_URLS } from "./nostr/toolkit.js";
import {
buildSubscriptionListEvent,
@@ -112,6 +112,11 @@ function determineDecryptionOrder(event, availableSchemes) {
const getApp = () => getApplication();
const listVideoViewEventsApi = (pointer, options) =>
listVideoViewEvents(nostrClient, pointer, options);
const subscribeVideoViewEventsApi = (pointer, options) =>
subscribeVideoViewEvents(nostrClient, pointer, options);
/**
* Manages the user's subscription list (kind=30002) *privately*,
* using NIP-04 encryption for the content field.
+3 -5
View File
@@ -1,9 +1,7 @@
// js/userBlocks.js
import {
getActiveSigner,
nostrClient,
requestDefaultExtensionPermissions,
} from "./nostr.js";
import { nostrClient } from "./nostr.js";
import { getActiveSigner } from "./nostr/index.js";
import { requestDefaultExtensionPermissions } from "./nostrClientRegistry.js";
import { buildBlockListEvent, BLOCK_LIST_IDENTIFIER } from "./nostrEventSchemas.js";
import { userLogger } from "./utils/logger.js";
import {
+12 -4
View File
@@ -17,16 +17,24 @@ import {
VIEW_COUNT_DEDUPE_WINDOW_SECONDS,
} from "./config.js";
import { userLogger } from "./utils/logger.js";
import { nostrClient } from "./nostr.js";
import {
countVideoViewEvents as countVideoViewEventsApi,
listVideoViewEvents as listVideoViewEventsApi,
subscribeVideoViewEvents as subscribeVideoViewEventsApi,
} from "./nostr.js";
countVideoViewEvents,
listVideoViewEvents,
subscribeVideoViewEvents,
} from "./nostr/index.js";
const VIEW_COUNTER_STORAGE_KEY = "bitvid:view-counter:v1";
const STORAGE_DEBOUNCE_MS = 500;
const SECONDS_PER_DAY = 86_400;
const listVideoViewEventsApi = (pointer, options) =>
listVideoViewEvents(nostrClient, pointer, options);
const subscribeVideoViewEventsApi = (pointer, options) =>
subscribeVideoViewEvents(nostrClient, pointer, options);
const countVideoViewEventsApi = (pointer, options) =>
countVideoViewEvents(nostrClient, pointer, options);
/** @type {Map<string, ViewCounterState>} */
const pointerStates = new Map();
/** @type {Map<string, PointerListeners>} */
+2 -1
View File
@@ -1,6 +1,7 @@
// js/watchHistoryService.js
import { nostrClient, requestDefaultExtensionPermissions } from "./nostr.js";
import { nostrClient } from "./nostr.js";
import { requestDefaultExtensionPermissions } from "./nostrClientRegistry.js";
import {
normalizePointerInput,
pointerKey,