mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
update
This commit is contained in:
1 parent
b7d63f235c
commit
8ad7d82a70
2 files changed
+185
-97
No files matched your search
+108
-94
@@ -118,6 +118,50 @@ function normalizePointerCandidate(candidate, expectedType) {
|
||||
};
|
||||
}
|
||||
if (
|
||||
|
||||
function normalizeTagName(name) {
|
||||
return typeof name === "string" ? name.trim().toLowerCase() : "";
|
||||
}
|
||||
|
||||
function normalizeTagValue(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return "";
|
||||
}
|
||||
if (Number.isFinite(value)) {
|
||||
return String(Math.floor(value));
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim();
|
||||
return trimmed ? trimmed.toLowerCase() : "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function normalizeDescriptorString(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return "";
|
||||
}
|
||||
if (Number.isFinite(value)) {
|
||||
return String(Math.floor(value));
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim();
|
||||
return trimmed ? trimmed.toLowerCase() : "";
|
||||
}
|
||||
if (typeof value === "object" && typeof value.value !== "undefined") {
|
||||
return normalizeDescriptorString(value.value);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function normalizeDescriptorRelay(value) {
|
||||
if (typeof value === "string") {
|
||||
return value.trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
expectedType === "a" &&
|
||||
typeof candidate.address === "string" &&
|
||||
candidate.address.trim()
|
||||
@@ -195,7 +239,7 @@ function collectTagsFromEvent(event) {
|
||||
function findTagByName(tags, ...names) {
|
||||
const normalizedNames = names
|
||||
.flat()
|
||||
.map((name) => (typeof name === "string" ? name.trim() : ""))
|
||||
.map((name) => normalizeTagName(name))
|
||||
.filter(Boolean);
|
||||
if (!normalizedNames.length) {
|
||||
return null;
|
||||
@@ -208,7 +252,7 @@ function findTagByName(tags, ...names) {
|
||||
if (typeof name !== "string") {
|
||||
continue;
|
||||
}
|
||||
if (normalizedNames.includes(name.trim())) {
|
||||
if (normalizedNames.includes(normalizeTagName(name))) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
@@ -682,32 +726,34 @@ function normalizeCommentTarget(targetInput = {}, overrides = {}) {
|
||||
normalizedParentAuthorRelay = normalizedRootAuthorRelay;
|
||||
}
|
||||
|
||||
if (!normalizedVideoEventId) {
|
||||
const descriptor = {
|
||||
videoEventId: normalizeDescriptorString(normalizedVideoEventId),
|
||||
videoEventRelay: normalizeDescriptorRelay(normalizedVideoEventRelay),
|
||||
videoDefinitionAddress: normalizeDescriptorString(normalizedVideoDefinitionAddress),
|
||||
videoDefinitionRelay: normalizeDescriptorRelay(normalizedVideoDefinitionRelay),
|
||||
videoKind: normalizeDescriptorString(normalizedVideoKind),
|
||||
videoAuthorPubkey: normalizeDescriptorString(normalizedVideoAuthorPubkey),
|
||||
parentCommentId: normalizeDescriptorString(normalizedParentCommentId),
|
||||
parentCommentRelay: normalizeDescriptorRelay(normalizedParentCommentRelay),
|
||||
threadParticipantPubkey: normalizeDescriptorString(normalizedThreadParticipantPubkey),
|
||||
threadParticipantRelay: normalizeDescriptorRelay(normalizedThreadParticipantRelay),
|
||||
rootIdentifier: normalizeDescriptorString(normalizedRootIdentifier),
|
||||
rootIdentifierRelay: normalizeDescriptorRelay(normalizedRootIdentifierRelay),
|
||||
parentIdentifier: normalizeDescriptorString(normalizedParentIdentifier),
|
||||
parentIdentifierRelay: normalizeDescriptorRelay(normalizedParentIdentifierRelay),
|
||||
rootKind: normalizeDescriptorString(normalizedRootKind),
|
||||
rootAuthorPubkey: normalizeDescriptorString(normalizedRootAuthorPubkey),
|
||||
rootAuthorRelay: normalizeDescriptorRelay(normalizedRootAuthorRelay),
|
||||
parentKind: normalizeDescriptorString(normalizedParentKind),
|
||||
parentAuthorPubkey: normalizeDescriptorString(normalizedParentAuthorPubkey),
|
||||
parentAuthorRelay: normalizeDescriptorRelay(normalizedParentAuthorRelay),
|
||||
};
|
||||
|
||||
if (!descriptor.videoEventId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
videoEventId: normalizedVideoEventId,
|
||||
videoEventRelay: normalizedVideoEventRelay,
|
||||
videoDefinitionAddress: normalizedVideoDefinitionAddress,
|
||||
videoDefinitionRelay: normalizedVideoDefinitionRelay,
|
||||
videoKind: normalizedVideoKind,
|
||||
videoAuthorPubkey: normalizedVideoAuthorPubkey,
|
||||
parentCommentId: normalizedParentCommentId,
|
||||
parentCommentRelay: normalizedParentCommentRelay,
|
||||
threadParticipantPubkey: normalizedThreadParticipantPubkey,
|
||||
threadParticipantRelay: normalizedThreadParticipantRelay,
|
||||
rootIdentifier: normalizedRootIdentifier,
|
||||
rootIdentifierRelay: normalizedRootIdentifierRelay,
|
||||
parentIdentifier: normalizedParentIdentifier,
|
||||
parentIdentifierRelay: normalizedParentIdentifierRelay,
|
||||
rootKind: normalizedRootKind,
|
||||
rootAuthorPubkey: normalizedRootAuthorPubkey,
|
||||
rootAuthorRelay: normalizedRootAuthorRelay,
|
||||
parentKind: normalizedParentKind,
|
||||
parentAuthorPubkey: normalizedParentAuthorPubkey,
|
||||
parentAuthorRelay: normalizedParentAuthorRelay,
|
||||
};
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
function applyFilterOptions(filter, options = {}) {
|
||||
@@ -770,20 +816,16 @@ function createVideoCommentFilters(targetInput, options = {}) {
|
||||
hasUppercasePointer = true;
|
||||
}
|
||||
|
||||
const normalizedRootKind =
|
||||
typeof descriptor.rootKind === "string"
|
||||
? descriptor.rootKind
|
||||
: Number.isFinite(descriptor.rootKind)
|
||||
? String(Math.floor(descriptor.rootKind))
|
||||
: "";
|
||||
const normalizedRootKind = normalizeDescriptorString(
|
||||
descriptor.rootKind || descriptor.videoKind,
|
||||
);
|
||||
if (normalizedRootKind) {
|
||||
uppercaseFilter["#K"] = [normalizedRootKind];
|
||||
}
|
||||
|
||||
const normalizedRootAuthor =
|
||||
typeof descriptor.rootAuthorPubkey === "string"
|
||||
? descriptor.rootAuthorPubkey.trim()
|
||||
: "";
|
||||
const normalizedRootAuthor = normalizeDescriptorString(
|
||||
descriptor.rootAuthorPubkey || descriptor.videoAuthorPubkey,
|
||||
);
|
||||
if (normalizedRootAuthor) {
|
||||
uppercaseFilter["#P"] = [normalizedRootAuthor];
|
||||
}
|
||||
@@ -865,43 +907,19 @@ function isVideoCommentEvent(event, descriptor) {
|
||||
let hasEventTag = !requiresEventMatch;
|
||||
let hasParentTag = false;
|
||||
|
||||
const expectedRootKind = (() => {
|
||||
if (typeof targetDescriptor.rootKind === "string") {
|
||||
return targetDescriptor.rootKind;
|
||||
}
|
||||
if (Number.isFinite(targetDescriptor.rootKind)) {
|
||||
return String(Math.floor(targetDescriptor.rootKind));
|
||||
}
|
||||
if (typeof targetDescriptor.videoKind === "string") {
|
||||
return targetDescriptor.videoKind;
|
||||
}
|
||||
if (Number.isFinite(targetDescriptor.videoKind)) {
|
||||
return String(Math.floor(targetDescriptor.videoKind));
|
||||
}
|
||||
return "";
|
||||
})();
|
||||
const expectedRootKind = normalizeDescriptorString(
|
||||
targetDescriptor.rootKind || targetDescriptor.videoKind,
|
||||
);
|
||||
|
||||
const expectedRootAuthor =
|
||||
typeof targetDescriptor.rootAuthorPubkey === "string"
|
||||
? targetDescriptor.rootAuthorPubkey
|
||||
: typeof targetDescriptor.videoAuthorPubkey === "string"
|
||||
? targetDescriptor.videoAuthorPubkey
|
||||
: "";
|
||||
const expectedRootAuthor = normalizeDescriptorString(
|
||||
targetDescriptor.rootAuthorPubkey || targetDescriptor.videoAuthorPubkey,
|
||||
);
|
||||
|
||||
const expectedParentKind = (() => {
|
||||
if (typeof targetDescriptor.parentKind === "string") {
|
||||
return targetDescriptor.parentKind;
|
||||
}
|
||||
if (Number.isFinite(targetDescriptor.parentKind)) {
|
||||
return String(Math.floor(targetDescriptor.parentKind));
|
||||
}
|
||||
return "";
|
||||
})();
|
||||
const expectedParentKind = normalizeDescriptorString(targetDescriptor.parentKind);
|
||||
|
||||
const expectedParentAuthor =
|
||||
typeof targetDescriptor.parentAuthorPubkey === "string"
|
||||
? targetDescriptor.parentAuthorPubkey
|
||||
: "";
|
||||
const expectedParentAuthor = normalizeDescriptorString(
|
||||
targetDescriptor.parentAuthorPubkey,
|
||||
);
|
||||
|
||||
let sawRootKindTag = false;
|
||||
let rootKindMatches = !expectedRootKind;
|
||||
@@ -923,61 +941,57 @@ function isVideoCommentEvent(event, descriptor) {
|
||||
if (!Array.isArray(tag) || tag.length < 2) {
|
||||
continue;
|
||||
}
|
||||
const [name, value] = tag;
|
||||
if (typeof value !== "string") {
|
||||
const [rawName, rawValue] = tag;
|
||||
const trimmedName = typeof rawName === "string" ? rawName.trim() : "";
|
||||
const lowerName = trimmedName.toLowerCase();
|
||||
const normalizedValue = normalizeTagValue(rawValue);
|
||||
if (!lowerName || !normalizedValue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name === "I") {
|
||||
if (value === targetDescriptor.rootIdentifier) {
|
||||
if (lowerName === "i") {
|
||||
if (normalizedValue === targetDescriptor.rootIdentifier) {
|
||||
hasIdentifierTag = true;
|
||||
}
|
||||
} else if (name === "A") {
|
||||
if (value === targetDescriptor.videoDefinitionAddress) {
|
||||
} else if (lowerName === "a") {
|
||||
if (normalizedValue === targetDescriptor.videoDefinitionAddress) {
|
||||
hasDefinitionTag = true;
|
||||
}
|
||||
} else if (name === "a") {
|
||||
if (value === targetDescriptor.videoDefinitionAddress) {
|
||||
hasDefinitionTag = true;
|
||||
}
|
||||
} else if (name === "E") {
|
||||
if (value === targetDescriptor.videoEventId) {
|
||||
hasEventTag = true;
|
||||
}
|
||||
} else if (name === "e") {
|
||||
if (value === targetDescriptor.videoEventId) {
|
||||
} else if (lowerName === "e") {
|
||||
if (normalizedValue === targetDescriptor.videoEventId) {
|
||||
hasEventTag = true;
|
||||
}
|
||||
if (
|
||||
trimmedName === lowerName &&
|
||||
targetDescriptor.parentCommentId &&
|
||||
value === targetDescriptor.parentCommentId
|
||||
normalizedValue === targetDescriptor.parentCommentId
|
||||
) {
|
||||
hasParentTag = true;
|
||||
}
|
||||
} else if (name === "K") {
|
||||
} else if (trimmedName === "K") {
|
||||
sawRootKindTag = true;
|
||||
if (!expectedRootKind || value === expectedRootKind) {
|
||||
if (!expectedRootKind || normalizedValue === expectedRootKind) {
|
||||
rootKindMatches = true;
|
||||
} else {
|
||||
rootKindMismatch = true;
|
||||
}
|
||||
} else if (name === "P") {
|
||||
} else if (trimmedName === "P") {
|
||||
sawRootAuthorTag = true;
|
||||
if (!expectedRootAuthor || value === expectedRootAuthor) {
|
||||
if (!expectedRootAuthor || normalizedValue === expectedRootAuthor) {
|
||||
rootAuthorMatches = true;
|
||||
} else {
|
||||
rootAuthorMismatch = true;
|
||||
}
|
||||
} else if (name === "k") {
|
||||
} else if (trimmedName === "k") {
|
||||
sawParentKindTag = true;
|
||||
if (!expectedParentKind || value === expectedParentKind) {
|
||||
if (!expectedParentKind || normalizedValue === expectedParentKind) {
|
||||
parentKindMatches = true;
|
||||
} else {
|
||||
parentKindMismatch = true;
|
||||
}
|
||||
} else if (name === "p") {
|
||||
} else if (trimmedName === "p") {
|
||||
sawParentAuthorTag = true;
|
||||
if (!expectedParentAuthor || value === expectedParentAuthor) {
|
||||
if (!expectedParentAuthor || normalizedValue === expectedParentAuthor) {
|
||||
parentAuthorMatches = true;
|
||||
} else {
|
||||
parentAuthorMismatch = true;
|
||||
|
||||
@@ -232,9 +232,6 @@ test("publishComment derives root and parent metadata from parent comment tags",
|
||||
shouldRequestExtensionPermissions: () => false,
|
||||
DEFAULT_NIP07_PERMISSION_METHODS: [],
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true, "publish should succeed when deriving metadata");
|
||||
const tags = buildCommentEventTags(result.event);
|
||||
assert.deepEqual(tags, [
|
||||
["A", "30078:videopk:root", "wss://definition"],
|
||||
@@ -248,6 +245,83 @@ test("publishComment derives root and parent metadata from parent comment tags",
|
||||
]);
|
||||
});
|
||||
|
||||
test("listVideoComments matches comments even when tag casing and whitespace differ", async () => {
|
||||
const mismatchedEvent = {
|
||||
id: "case-comment",
|
||||
kind: COMMENT_EVENT_KIND,
|
||||
created_at: 1700000300,
|
||||
tags: [
|
||||
[" A ", " 30078:AUTHOR:CLIP ", " WSS://Definition "],
|
||||
["e", " VIDEO-1 "],
|
||||
[" E ", " VIDEO-1 "],
|
||||
["e", " PARENT-1 "],
|
||||
["k", String(COMMENT_EVENT_KIND)],
|
||||
["p", " THREAD-PARTICIPANT "],
|
||||
],
|
||||
};
|
||||
|
||||
const { client, pool } = createMockClient();
|
||||
pool.list = async () => [[mismatchedEvent]];
|
||||
|
||||
const descriptorInput = {
|
||||
videoEventId: " video-1 ",
|
||||
videoDefinitionAddress: " 30078:author:clip ",
|
||||
parentCommentId: " parent-1 ",
|
||||
};
|
||||
|
||||
const events = await listVideoComments(client, descriptorInput);
|
||||
assert.equal(events.length, 1, "case-insensitive descriptor should match comment");
|
||||
assert.equal(events[0].id, "case-comment", "matched event should be returned");
|
||||
|
||||
const normalizedDescriptor = normalizeCommentTarget(descriptorInput);
|
||||
assert.ok(normalizedDescriptor, "normalizeCommentTarget should produce a descriptor");
|
||||
assert.equal(
|
||||
isVideoCommentEvent(mismatchedEvent, normalizedDescriptor),
|
||||
true,
|
||||
"isVideoCommentEvent should treat tag casing and whitespace as insignificant",
|
||||
);
|
||||
});
|
||||
test("listVideoComments matches comments even when tag casing and whitespace differ", async () => {
|
||||
const mismatchedEvent = {
|
||||
id: "case-comment",
|
||||
kind: COMMENT_EVENT_KIND,
|
||||
created_at: 1700000300,
|
||||
tags: [
|
||||
[" A ", " 30078:AUTHOR:CLIP ", " WSS://Definition "],
|
||||
["e", " VIDEO-1 "],
|
||||
[" E ", " VIDEO-1 "],
|
||||
["e", " PARENT-1 "],
|
||||
["k", String(COMMENT_EVENT_KIND)],
|
||||
["p", " THREAD-PARTICIPANT "],
|
||||
],
|
||||
};
|
||||
|
||||
const { client, pool } = createMockClient();
|
||||
pool.list = async () => [[mismatchedEvent]];
|
||||
|
||||
const descriptorInput = {
|
||||
videoEventId: " video-1 ",
|
||||
videoDefinitionAddress: " 30078:author:clip ",
|
||||
parentCommentId: " parent-1 ",
|
||||
};
|
||||
|
||||
const events = await listVideoComments(client, descriptorInput);
|
||||
assert.equal(events.length, 1, "case-insensitive descriptor should match comment");
|
||||
assert.equal(events[0].id, "case-comment", "matched event should be returned");
|
||||
|
||||
const normalizedDescriptor = normalizeCommentTarget(descriptorInput);
|
||||
assert.ok(normalizedDescriptor, "normalizeCommentTarget should produce a descriptor");
|
||||
assert.equal(
|
||||
isVideoCommentEvent(mismatchedEvent, normalizedDescriptor),
|
||||
true,
|
||||
"isVideoCommentEvent should treat tag casing and whitespace as insignificant",
|
||||
);
|
||||
});
|
||||
|
||||
["p", "parentpk", "wss://parent"],
|
||||
]);
|
||||
});
|
||||
|
||||
test("publishComment emits only video event #e tag when address and parent are absent", async () => {
|
||||
const { client, publishCalls } = createMockClient();
|
||||
|
||||
|
||||
Reference in new issue
Block a user