Add nostr facades for client, view events, and watch history

This commit is contained in:
thePR0M3TH3AN committed 2025-10-22 22:17:30 -04:00
1 parent a1d053b21a
commit 3c214f96df
7 files changed
+126 -33

No files matched your search

+25 -14
View File
@@ -1,22 +1,33 @@
// js/nostr.js
//
// Compatibility shim that forwards legacy imports to the new domain-specific
// facades. Prefer importing from the facades directly in new modules so the
// wiring between the default client and helper layers stays explicit.
import {
nostrClient,
requestDefaultExtensionPermissions,
} from "./nostr/defaultClient.js";
export { nostrClient, requestDefaultExtensionPermissions };
export { nostrClient, requestDefaultExtensionPermissions } from "./nostrClientFacade.js";
export {
recordVideoView,
listVideoViewEvents,
subscribeVideoViewEvents,
countVideoViewEvents,
} from "./nostr/viewEventBindings.js";
recordVideoViewWithDefaultClient,
listVideoViewEventsWithDefaultClient,
subscribeVideoViewEventsWithDefaultClient,
countVideoViewEventsWithDefaultClient,
} from "./nostrViewEventsFacade.js";
export {
updateWatchHistoryList,
removeWatchHistoryItem,
} from "./nostr/watchHistoryBindings.js";
updateWatchHistoryListWithDefaultClient,
removeWatchHistoryItemWithDefaultClient,
} from "./nostrWatchHistoryFacade.js";
export {
recordVideoViewWithDefaultClient as recordVideoView,
listVideoViewEventsWithDefaultClient as listVideoViewEvents,
subscribeVideoViewEventsWithDefaultClient as subscribeVideoViewEvents,
countVideoViewEventsWithDefaultClient as countVideoViewEvents,
} from "./nostrViewEventsFacade.js";
export {
updateWatchHistoryListWithDefaultClient as updateWatchHistoryList,
removeWatchHistoryItemWithDefaultClient as removeWatchHistoryItem,
} from "./nostrWatchHistoryFacade.js";
export * from "./nostr/index.js";
+11
View File
@@ -0,0 +1,11 @@
// js/nostrClientFacade.js
//
// Compatibility-forwarding module that exposes the default Nostr client
// singleton alongside the convenience permission helper.
// Future modules should import from this facade instead of reaching into the
// nested default client implementation directly.
export {
nostrClient,
requestDefaultExtensionPermissions,
} from "./nostr/defaultClient.js";
+27
View File
@@ -0,0 +1,27 @@
// js/nostrViewEventsFacade.js
//
// Central export for NIP-71 view event helpers. Consumers can choose between
// helpers that accept an explicit Nostr client and thin wrappers that call the
// default client instance configured by the app shell.
export {
recordVideoView as recordVideoViewWithDefaultClient,
listVideoViewEvents as listVideoViewEventsWithDefaultClient,
subscribeVideoViewEvents as subscribeVideoViewEventsWithDefaultClient,
countVideoViewEvents as countVideoViewEventsWithDefaultClient,
} from "./nostr/viewEventBindings.js";
export {
VIEW_EVENT_KIND,
createVideoViewEventFilters,
deriveViewEventBucketIndex,
deriveViewEventPointerScope,
getViewEventGuardWindowMs,
hasRecentViewPublish,
rememberViewPublish,
publishViewEvent,
recordVideoView,
listVideoViewEvents,
subscribeVideoViewEvents,
countVideoViewEvents,
} from "./nostr/viewEvents.js";
+10
View File
@@ -0,0 +1,10 @@
// js/nostrWatchHistoryFacade.js
//
// Wrapper exports for watch history helpers that operate on the default Nostr
// client instance. Future consumers should import from this module rather than
// reaching into the binding layer directly.
export {
updateWatchHistoryList as updateWatchHistoryListWithDefaultClient,
removeWatchHistoryItem as removeWatchHistoryItemWithDefaultClient,
} from "./nostr/watchHistoryBindings.js";
+22 -15
View File
@@ -3,12 +3,12 @@
import "./test-helpers/setup-localstorage.mjs";
import assert from "node:assert/strict";
const { nostrClient } = await import("../js/nostr.js");
const { nostrClient } = await import("../js/nostrClientFacade.js");
const {
listVideoViewEvents,
subscribeVideoViewEvents,
countVideoViewEvents,
} = await import("../js/nostr/viewEventBindings.js");
listVideoViewEventsWithDefaultClient,
subscribeVideoViewEventsWithDefaultClient,
countVideoViewEventsWithDefaultClient,
} = await import("../js/nostrViewEventsFacade.js");
function temporarilyUnset(client, methodName) {
const original = client[methodName];
@@ -21,9 +21,12 @@ function temporarilyUnset(client, methodName) {
async function testListBindingGuardsMissingMethod() {
const restore = temporarilyUnset(nostrClient, "listVideoViewEvents");
try {
assert.throws(() => listVideoViewEvents({ type: "e", value: "guard" }), {
message: "Video view listing is unavailable in this build.",
});
assert.throws(
() => listVideoViewEventsWithDefaultClient({ type: "e", value: "guard" }),
{
message: "Video view listing is unavailable in this build.",
}
);
} finally {
restore();
}
@@ -33,7 +36,8 @@ async function testSubscribeBindingGuardsMissingMethod() {
const restore = temporarilyUnset(nostrClient, "subscribeVideoViewEvents");
try {
assert.throws(
() => subscribeVideoViewEvents({ type: "e", value: "guard" }),
() =>
subscribeVideoViewEventsWithDefaultClient({ type: "e", value: "guard" }),
{
message: "Video view subscriptions are unavailable in this build.",
}
@@ -46,9 +50,12 @@ async function testSubscribeBindingGuardsMissingMethod() {
async function testCountBindingGuardsMissingMethod() {
const restore = temporarilyUnset(nostrClient, "countVideoViewEvents");
try {
assert.throws(() => countVideoViewEvents({ type: "e", value: "guard" }), {
message: "Video view counting is unavailable in this build.",
});
assert.throws(
() => countVideoViewEventsWithDefaultClient({ type: "e", value: "guard" }),
{
message: "Video view counting is unavailable in this build.",
}
);
} finally {
restore();
}
@@ -65,7 +72,7 @@ async function testListBindingPassesThroughWhenAvailable() {
return sentinel;
};
try {
const result = await listVideoViewEvents(pointer, options);
const result = await listVideoViewEventsWithDefaultClient(pointer, options);
assert.equal(result, sentinel, "binding should return the client result");
assert.deepEqual(
receivedArgs,
@@ -89,7 +96,7 @@ async function testSubscribeBindingPassesThroughWhenAvailable() {
return unsubscribe;
};
try {
const result = subscribeVideoViewEvents(pointer, options);
const result = subscribeVideoViewEventsWithDefaultClient(pointer, options);
assert.equal(result, unsubscribe, "binding should return unsubscribe value");
assert.deepEqual(
receivedArgs,
@@ -112,7 +119,7 @@ async function testCountBindingPassesThroughWhenAvailable() {
return sentinel;
};
try {
const result = await countVideoViewEvents(pointer, options);
const result = await countVideoViewEventsWithDefaultClient(pointer, options);
assert.equal(result, sentinel, "binding should return the client result");
assert.deepEqual(
receivedArgs,
+27
View File
@@ -0,0 +1,27 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
nostrClient as facadeClient,
requestDefaultExtensionPermissions as facadeRequest,
} from "../../js/nostrClientFacade.js";
import {
nostrClient as directClient,
requestDefaultExtensionPermissions as directRequest,
} from "../../js/nostr/defaultClient.js";
test("nostrClientFacade forwards the default client instance", () => {
assert.equal(
facadeClient,
directClient,
"facade should expose the default client singleton",
);
});
test("nostrClientFacade forwards the default permission helper", async () => {
assert.equal(
facadeRequest,
directRequest,
"facade should reuse the registered permission helper",
);
});
+4 -4
View File
@@ -1,11 +1,11 @@
import assert from "node:assert/strict";
import test from "node:test";
import { nostrClient } from "../../js/nostr/defaultClient.js";
import { nostrClient } from "../../js/nostrClientFacade.js";
import {
updateWatchHistoryList,
removeWatchHistoryItem,
} from "../../js/nostr/watchHistoryBindings.js";
updateWatchHistoryListWithDefaultClient as updateWatchHistoryList,
removeWatchHistoryItemWithDefaultClient as removeWatchHistoryItem,
} from "../../js/nostrWatchHistoryFacade.js";
const originalWatchHistory = nostrClient.watchHistory;