mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-30 09:22:24 +00:00
Merge pull request #1547 from PR0M3TH3AN/codex/update-share-note-format-documentation
Document and test share note events
This commit is contained in:
3 files changed
+111
-4
No files matched your search
@@ -174,6 +174,22 @@ import { updateWatchHistoryListWithDefaultClient } from "./nostrWatchHistoryFaca
|
||||
| Zap request (`NOTE_TYPES.ZAP_REQUEST`) | `9734` | `['p', <recipient pubkey>]` plus optional `['e', <event id>]`, `['a', <coordinate>]`, `['amount', <msats>]`, `['lnurl', <bech32>]`, and `['relays', ...]` for receipt publishing | Optional plaintext zap note |
|
||||
| Zap receipt (`NOTE_TYPES.ZAP_RECEIPT`) | `9735` | `['bolt11', <invoice>]`, `['description', <zap request JSON>]`, `['p', <recipient pubkey>]` plus optional `['e', <event id>]` and `['a', <coordinate>]` | Empty content; receipts are published by the recipient's LNURL server |
|
||||
|
||||
### Share notes (kind `1`)
|
||||
|
||||
Share notes are plain text `kind:1` events generated by the Share modal. The
|
||||
`content` field is preserved verbatim (after UTF-8 sanitization) and can contain
|
||||
any short message or commentary accompanying the share.
|
||||
|
||||
The builder attaches the following tags:
|
||||
|
||||
* `['e', <event id>, '', 'mention']` — the shared video’s event id, normalized to
|
||||
lowercase hex.
|
||||
* `['p', <pubkey>, '', 'mention']` — the shared video’s author pubkey,
|
||||
normalized to lowercase hex.
|
||||
* `['r', <relay url>, <read/write?>]` — optional relay hints. Relay URLs are
|
||||
trimmed, duplicates are removed, and the optional marker is normalized to
|
||||
`read` or `write` when provided.
|
||||
|
||||
Subscription lists therefore match the
|
||||
[NIP-51 follow-set specification](./nips/51.md#sets) by emitting kind `30000`
|
||||
events with the shared `['d','subscriptions']` identifier. Builders continue to
|
||||
|
||||
@@ -1010,14 +1010,12 @@ export function buildShareEvent(params) {
|
||||
const readMarker = schema?.readMarker || "read";
|
||||
const writeMarker = schema?.writeMarker || "write";
|
||||
|
||||
const normalizedVideoId =
|
||||
typeof video?.id === "string" ? video.id.trim() : "";
|
||||
const normalizedVideoId = normalizePointerIdentifier(video?.id);
|
||||
if (normalizedVideoId) {
|
||||
tags.push(["e", normalizedVideoId, "", "mention"]);
|
||||
}
|
||||
|
||||
const normalizedVideoPubkey =
|
||||
typeof video?.pubkey === "string" ? video.pubkey.trim() : "";
|
||||
const normalizedVideoPubkey = normalizePointerIdentifier(video?.pubkey);
|
||||
if (normalizedVideoPubkey) {
|
||||
tags.push(["p", normalizedVideoPubkey, "", "mention"]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const SAMPLE_HEX = "deadbeef".repeat(8);
|
||||
const SAMPLE_NPUB = "npub1shareeventfixture000000000000000000000";
|
||||
|
||||
if (typeof globalThis.window === "undefined") {
|
||||
globalThis.window = {};
|
||||
}
|
||||
|
||||
if (!globalThis.window.NostrTools) {
|
||||
globalThis.window.NostrTools = {};
|
||||
}
|
||||
|
||||
const existingNip19 =
|
||||
typeof globalThis.window.NostrTools.nip19 === "object"
|
||||
? globalThis.window.NostrTools.nip19
|
||||
: {};
|
||||
|
||||
if (typeof existingNip19.decode !== "function") {
|
||||
globalThis.window.NostrTools.nip19 = {
|
||||
...existingNip19,
|
||||
decode: () => ({ type: "npub", data: SAMPLE_HEX }),
|
||||
};
|
||||
}
|
||||
|
||||
const { buildShareEvent } = await import("../js/nostrEventSchemas.js");
|
||||
|
||||
test("buildShareEvent preserves share content", () => {
|
||||
const event = buildShareEvent({
|
||||
pubkey: "share-author",
|
||||
created_at: 1700000800,
|
||||
content: "Check this out!",
|
||||
video: { id: "a".repeat(64), pubkey: "b".repeat(64) },
|
||||
});
|
||||
|
||||
assert.equal(event.content, "Check this out!");
|
||||
});
|
||||
|
||||
test("buildShareEvent normalizes hex identifiers for e and p tags", () => {
|
||||
const event = buildShareEvent({
|
||||
pubkey: "share-author",
|
||||
created_at: 1700000801,
|
||||
content: "Normalize IDs",
|
||||
video: {
|
||||
id: "ABCDEF".repeat(10) + "ABCD",
|
||||
pubkey: SAMPLE_NPUB,
|
||||
},
|
||||
});
|
||||
|
||||
const eventTags = event.tags.filter((tag) => Array.isArray(tag) && tag[0] === "e");
|
||||
const authorTags = event.tags.filter((tag) => Array.isArray(tag) && tag[0] === "p");
|
||||
|
||||
assert.deepEqual(eventTags, [["e", "abcdef".repeat(10) + "abcd", "", "mention"]]);
|
||||
assert.deepEqual(authorTags, [["p", SAMPLE_HEX, "", "mention"]]);
|
||||
});
|
||||
|
||||
test("buildShareEvent includes sanitized relay hints", () => {
|
||||
const event = buildShareEvent({
|
||||
pubkey: "share-author",
|
||||
created_at: 1700000802,
|
||||
content: "Relay hints",
|
||||
video: { id: "c".repeat(64), pubkey: "d".repeat(64) },
|
||||
relays: [
|
||||
" wss://relay.one ",
|
||||
{ url: " wss://relay.two ", mode: "READ" },
|
||||
["r", "wss://relay.one", "write"],
|
||||
{ relay: "wss://relay.three", write: true, read: false },
|
||||
["wss://relay.four", "write"],
|
||||
],
|
||||
});
|
||||
|
||||
const relayTags = event.tags.filter((tag) => Array.isArray(tag) && tag[0] === "r");
|
||||
|
||||
assert.deepEqual(relayTags, [
|
||||
["r", "wss://relay.one"],
|
||||
["r", "wss://relay.two", "read"],
|
||||
["r", "wss://relay.one", "write"],
|
||||
["r", "wss://relay.three", "write"],
|
||||
["r", "wss://relay.four", "write"],
|
||||
]);
|
||||
});
|
||||
|
||||
test("buildShareEvent tolerates missing optional fields", () => {
|
||||
const event = buildShareEvent({
|
||||
pubkey: "share-author",
|
||||
created_at: 1700000803,
|
||||
});
|
||||
|
||||
assert.equal(event.kind, 1);
|
||||
assert.equal(event.content, "");
|
||||
assert.deepEqual(event.tags, []);
|
||||
});
|
||||
Reference in new issue
Block a user