Merge pull request #1017 from PR0M3TH3AN/codex/update-revertvideo-for-stable-d-tag

Ensure video reverts always emit stable d tags
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-30 14:45:05 -04:00
commit 925be7b654
2 files changed
+135 -3

No files matched your search

+8 -3
View File
@@ -4642,6 +4642,8 @@ export class NostrClient {
}
const existingD = this.resolveEventDTag(baseEvent, originalEvent) || null;
const stableDTag =
existingD || baseEvent?.id || originalEvent?.id || null;
let oldContent = {};
try {
@@ -4656,7 +4658,7 @@ export class NostrClient {
oldContent.videoRootId ||
(existingD
? `LEGACY:${baseEvent.pubkey}:${existingD}`
: baseEvent.id);
: stableDTag || baseEvent?.id || originalEvent?.id || "");
const oldIsNsfw = oldContent.isNsfw === true;
const oldIsForKids = oldContent.isForKids === true && !oldIsNsfw;
@@ -4677,8 +4679,11 @@ export class NostrClient {
};
const tags = [["t", "video"]];
if (existingD) {
tags.push(["d", existingD]);
if (stableDTag) {
// NIP-78 requires replaceable events to publish a stable "d" tag.
// Legacy videos may not carry one, so reuse the prior event id to
// guarantee the revert stays addressable.
tags.push(["d", stableDTag]);
}
const event = {
+127
View File
@@ -0,0 +1,127 @@
import "../test-helpers/setup-localstorage.mjs";
import test from "node:test";
import assert from "node:assert/strict";
import {
NostrClient,
setActiveSigner,
clearActiveSigner,
} from "../../js/nostr.js";
function createStubPool() {
return {
publish(urls, event) {
void urls;
void event;
return {
on(type, handler) {
if (type === "ok") {
handler();
return true;
}
if (type === "failed") {
return true;
}
return false;
},
};
},
};
}
test("revertVideo preserves existing d tag", async (t) => {
const client = new NostrClient();
const pubkey = "ABC123";
const existingD = "stable-d-tag";
const baseEvent = {
id: "event-original",
pubkey,
content: JSON.stringify({
version: 3,
videoRootId: "root-abc",
}),
tags: [
["t", "video"],
["d", existingD],
],
};
const signedEvents = [];
const signer = {
type: "app",
pubkey,
signEvent: async (event) => {
signedEvents.push(event);
return { ...event, id: `signed-${signedEvents.length}` };
},
};
client.relays = ["wss://relay.one"];
client.pool = createStubPool();
client.ensureExtensionPermissions = async () => ({ ok: true });
client.ensureActiveSignerForPubkey = async () => signer;
setActiveSigner(signer);
t.after(() => {
clearActiveSigner();
});
const result = await client.revertVideo(baseEvent, pubkey);
assert.ok(result?.event, "revertVideo should return the signed event");
const signedEvent = signedEvents[0];
assert.ok(signedEvent, "revertVideo should sign the revert event");
const dTags = signedEvent.tags.filter((tag) => Array.isArray(tag) && tag[0] === "d");
assert.deepEqual(
dTags,
[["d", existingD]],
"revertVideo should preserve the existing d tag",
);
});
test("revertVideo falls back to original event id when d tag missing", async (t) => {
const client = new NostrClient();
const pubkey = "ABC123";
const baseEvent = {
id: "event-legacy",
pubkey,
content: JSON.stringify({
version: 2,
videoRootId: "event-legacy",
}),
tags: [["t", "video"]],
};
const signedEvents = [];
const signer = {
type: "app",
pubkey,
signEvent: async (event) => {
signedEvents.push(event);
return { ...event, id: `signed-${signedEvents.length}` };
},
};
client.relays = ["wss://relay.one"];
client.pool = createStubPool();
client.ensureExtensionPermissions = async () => ({ ok: true });
client.ensureActiveSignerForPubkey = async () => signer;
setActiveSigner(signer);
t.after(() => {
clearActiveSigner();
});
const result = await client.revertVideo(baseEvent, pubkey);
assert.ok(result?.event, "revertVideo should return the signed event");
const signedEvent = signedEvents[0];
assert.ok(signedEvent, "revertVideo should sign the revert event");
const dTags = signedEvent.tags.filter((tag) => Array.isArray(tag) && tag[0] === "d");
assert.deepEqual(
dTags,
[["d", baseEvent.id]],
"revertVideo should reuse the original event id for the d tag when missing",
);
});