mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
- Created `scripts/agent/validate-events.mjs` to verify event builders against schemas. - Enhanced validation script with test cases for Relay List (object format), Hashtag Preferences (encrypted), and Video Post (additional tags). - Updated `docs/nostr-event-schemas.md` to document the validation workflow. - Verified runtime event construction by instrumenting tests with dev mode (reverted clean). Co-authored-by: PR0M3TH3AN <[email protected]>
421 lines
12 KiB
JavaScript
421 lines
12 KiB
JavaScript
// scripts/agent/validate-events.mjs
|
|
import WebSocket from 'ws';
|
|
import { webcrypto } from 'node:crypto';
|
|
|
|
// Polyfill WebSocket
|
|
if (typeof globalThis.WebSocket === 'undefined') {
|
|
globalThis.WebSocket = WebSocket;
|
|
}
|
|
|
|
// Polyfill window and self
|
|
if (typeof globalThis.window === 'undefined') {
|
|
globalThis.window = globalThis;
|
|
}
|
|
if (typeof globalThis.self === 'undefined') {
|
|
globalThis.self = globalThis;
|
|
}
|
|
|
|
// Polyfill crypto
|
|
if (typeof globalThis.crypto === 'undefined') {
|
|
globalThis.crypto = webcrypto;
|
|
}
|
|
|
|
// Polyfill localStorage
|
|
if (typeof globalThis.localStorage === 'undefined') {
|
|
const storage = new Map();
|
|
globalThis.localStorage = {
|
|
getItem: (k) => storage.get(k) || null,
|
|
setItem: (k, v) => storage.set(String(k), String(v)),
|
|
removeItem: (k) => storage.delete(k),
|
|
clear: () => storage.clear(),
|
|
key: (i) => Array.from(storage.keys())[i] || null,
|
|
get length() { return storage.size; }
|
|
};
|
|
}
|
|
|
|
if (typeof globalThis.window.localStorage === 'undefined') {
|
|
globalThis.window.localStorage = globalThis.localStorage;
|
|
}
|
|
|
|
// Mock navigator
|
|
if (typeof globalThis.navigator === 'undefined') {
|
|
globalThis.navigator = { userAgent: 'node' };
|
|
}
|
|
|
|
import {
|
|
buildVideoPostEvent,
|
|
buildHttpAuthEvent,
|
|
buildReportEvent,
|
|
buildVideoMirrorEvent,
|
|
buildRepostEvent,
|
|
buildShareEvent,
|
|
buildRelayListEvent,
|
|
buildDmRelayListEvent,
|
|
buildProfileMetadataEvent,
|
|
buildMuteListEvent,
|
|
buildDeletionEvent,
|
|
buildLegacyDirectMessageEvent,
|
|
buildDmAttachmentEvent,
|
|
buildDmReadReceiptEvent,
|
|
buildDmTypingIndicatorEvent,
|
|
buildViewEvent,
|
|
buildZapRequestEvent,
|
|
buildReactionEvent,
|
|
buildCommentEvent,
|
|
buildWatchHistoryEvent,
|
|
buildSubscriptionListEvent,
|
|
buildBlockListEvent,
|
|
buildHashtagPreferenceEvent,
|
|
buildAdminListEvent,
|
|
validateEventStructure,
|
|
NOTE_TYPES,
|
|
ADMIN_LIST_IDENTIFIERS,
|
|
getNostrEventSchema,
|
|
sanitizeAdditionalTags
|
|
} from '../../js/nostrEventSchemas.js';
|
|
import { buildNip71VideoEvent } from '../../js/nostr/nip71.js';
|
|
|
|
// Note: Most builders are defined in js/nostrEventSchemas.js and are already instrumented
|
|
// with validation checks (validateEventAgainstSchema) when running in Dev Mode.
|
|
// buildNip71VideoEvent was instrumented in js/nostr/nip71.js as part of this work.
|
|
// This script verifies that all builders produce valid events according to the schema.
|
|
|
|
const testCases = [
|
|
{
|
|
name: 'Video Post',
|
|
type: NOTE_TYPES.VIDEO_POST,
|
|
builder: buildVideoPostEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
dTagValue: 'test-video',
|
|
content: {
|
|
version: 3,
|
|
title: 'Test Video',
|
|
videoRootId: 'test-root',
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'Video Post (Usage Pattern)',
|
|
type: NOTE_TYPES.VIDEO_POST,
|
|
builder: buildVideoPostEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
dTagValue: 'test-video-usage',
|
|
content: {
|
|
version: 3,
|
|
title: 'Test Video Usage',
|
|
videoRootId: 'test-root-usage',
|
|
},
|
|
additionalTags: [['t', 'extra'], ['invalid']]
|
|
}
|
|
},
|
|
{
|
|
name: 'HTTP Auth',
|
|
type: NOTE_TYPES.HTTP_AUTH,
|
|
builder: buildHttpAuthEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
url: 'https://example.com',
|
|
method: 'GET'
|
|
}
|
|
},
|
|
{
|
|
name: 'Report',
|
|
type: NOTE_TYPES.REPORT,
|
|
builder: buildReportEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
eventId: '0000000000000000000000000000000000000000000000000000000000000002',
|
|
reportType: 'nudity'
|
|
}
|
|
},
|
|
{
|
|
name: 'Video Mirror',
|
|
type: NOTE_TYPES.VIDEO_MIRROR,
|
|
builder: buildVideoMirrorEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
content: 'Alt text'
|
|
}
|
|
},
|
|
{
|
|
name: 'Repost',
|
|
type: NOTE_TYPES.REPOST,
|
|
builder: buildRepostEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
eventId: '0000000000000000000000000000000000000000000000000000000000000002',
|
|
targetKind: 1
|
|
}
|
|
},
|
|
{
|
|
name: 'Share',
|
|
type: NOTE_TYPES.SHARE,
|
|
builder: buildShareEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
content: 'Check this out',
|
|
video: { id: '0000000000000000000000000000000000000000000000000000000000000002' }
|
|
}
|
|
},
|
|
{
|
|
name: 'Relay List',
|
|
type: NOTE_TYPES.RELAY_LIST,
|
|
builder: buildRelayListEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
relays: ['wss://relay.example.com']
|
|
}
|
|
},
|
|
{
|
|
name: 'Relay List (Usage Pattern)',
|
|
type: NOTE_TYPES.RELAY_LIST,
|
|
builder: buildRelayListEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
relays: [
|
|
{ url: 'wss://relay.read.com', mode: 'read' },
|
|
{ url: 'wss://relay.write.com', mode: 'write' },
|
|
{ url: 'wss://relay.both.com', mode: 'both' }
|
|
]
|
|
}
|
|
},
|
|
{
|
|
name: 'DM Relay List',
|
|
type: NOTE_TYPES.DM_RELAY_LIST,
|
|
builder: buildDmRelayListEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
relays: ['wss://relay.example.com']
|
|
}
|
|
},
|
|
{
|
|
name: 'Profile Metadata',
|
|
type: NOTE_TYPES.PROFILE_METADATA,
|
|
builder: buildProfileMetadataEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
metadata: { name: 'Alice' }
|
|
}
|
|
},
|
|
{
|
|
name: 'Mute List',
|
|
type: NOTE_TYPES.MUTE_LIST,
|
|
builder: buildMuteListEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
pTags: ['0000000000000000000000000000000000000000000000000000000000000002']
|
|
}
|
|
},
|
|
{
|
|
name: 'Deletion',
|
|
type: NOTE_TYPES.DELETION,
|
|
builder: buildDeletionEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
eventIds: ['0000000000000000000000000000000000000000000000000000000000000002']
|
|
}
|
|
},
|
|
{
|
|
name: 'Legacy DM',
|
|
type: NOTE_TYPES.LEGACY_DM,
|
|
builder: buildLegacyDirectMessageEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
recipientPubkey: '0000000000000000000000000000000000000000000000000000000000000002',
|
|
ciphertext: 'base64ciphertext'
|
|
}
|
|
},
|
|
{
|
|
name: 'DM Attachment',
|
|
type: NOTE_TYPES.DM_ATTACHMENT,
|
|
builder: buildDmAttachmentEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
recipientPubkey: '0000000000000000000000000000000000000000000000000000000000000002',
|
|
attachment: { url: 'https://example.com/file.jpg', x: 'hash' }
|
|
}
|
|
},
|
|
{
|
|
name: 'DM Read Receipt',
|
|
type: NOTE_TYPES.DM_READ_RECEIPT,
|
|
builder: buildDmReadReceiptEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
recipientPubkey: '0000000000000000000000000000000000000000000000000000000000000002',
|
|
eventId: '0000000000000000000000000000000000000000000000000000000000000002'
|
|
}
|
|
},
|
|
{
|
|
name: 'DM Typing Indicator',
|
|
type: NOTE_TYPES.DM_TYPING,
|
|
builder: buildDmTypingIndicatorEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
recipientPubkey: '0000000000000000000000000000000000000000000000000000000000000002',
|
|
expiresAt: 1234567900
|
|
}
|
|
},
|
|
{
|
|
name: 'View Event',
|
|
type: NOTE_TYPES.VIEW_EVENT,
|
|
builder: buildViewEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
pointerValue: '0000000000000000000000000000000000000000000000000000000000000002'
|
|
}
|
|
},
|
|
{
|
|
name: 'Zap Request',
|
|
type: NOTE_TYPES.ZAP_REQUEST,
|
|
builder: buildZapRequestEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
recipientPubkey: '0000000000000000000000000000000000000000000000000000000000000002',
|
|
amountSats: 100
|
|
}
|
|
},
|
|
{
|
|
name: 'Reaction',
|
|
type: NOTE_TYPES.VIDEO_REACTION,
|
|
builder: buildReactionEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
pointerValue: '0000000000000000000000000000000000000000000000000000000000000002',
|
|
content: '+'
|
|
}
|
|
},
|
|
{
|
|
name: 'Comment',
|
|
type: NOTE_TYPES.VIDEO_COMMENT,
|
|
builder: buildCommentEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
videoEventId: '0000000000000000000000000000000000000000000000000000000000000002',
|
|
content: 'Nice video'
|
|
}
|
|
},
|
|
{
|
|
name: 'Watch History',
|
|
type: NOTE_TYPES.WATCH_HISTORY,
|
|
builder: buildWatchHistoryEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
monthIdentifier: 'watch-history:2025-01',
|
|
content: { version: 2, month: '2025-01', items: [] }
|
|
}
|
|
},
|
|
{
|
|
name: 'Subscription List',
|
|
type: NOTE_TYPES.SUBSCRIPTION_LIST,
|
|
builder: buildSubscriptionListEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
content: 'encrypted-payload'
|
|
}
|
|
},
|
|
{
|
|
name: 'Block List',
|
|
type: NOTE_TYPES.USER_BLOCK_LIST,
|
|
builder: buildBlockListEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
content: 'encrypted-payload'
|
|
}
|
|
},
|
|
{
|
|
name: 'Hashtag Preferences',
|
|
type: NOTE_TYPES.HASHTAG_PREFERENCES,
|
|
builder: buildHashtagPreferenceEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
content: JSON.stringify({ version: 1, interests: [], disinterests: [] })
|
|
}
|
|
},
|
|
{
|
|
name: 'Hashtag Preferences (Encrypted)',
|
|
type: NOTE_TYPES.HASHTAG_PREFERENCES,
|
|
builder: buildHashtagPreferenceEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
content: 'encrypted-content-string-nip44'
|
|
}
|
|
},
|
|
{
|
|
name: 'Admin List (Moderation)',
|
|
type: NOTE_TYPES.ADMIN_MODERATION_LIST,
|
|
builder: (params) => buildAdminListEvent(ADMIN_LIST_IDENTIFIERS.moderation, params),
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
created_at: 1234567890,
|
|
hexPubkeys: []
|
|
}
|
|
},
|
|
{
|
|
name: 'NIP-71 Video',
|
|
type: NOTE_TYPES.NIP71_VIDEO,
|
|
builder: buildNip71VideoEvent,
|
|
params: {
|
|
pubkey: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
title: 'Test NIP-71',
|
|
metadata: { kind: 21, title: 'Test NIP-71' },
|
|
pointerIdentifiers: { videoRootId: 'test-root' }
|
|
}
|
|
}
|
|
];
|
|
|
|
let failed = false;
|
|
|
|
for (const testCase of testCases) {
|
|
try {
|
|
const event = testCase.builder(testCase.params);
|
|
if (!event) {
|
|
console.error(`[FAIL] ${testCase.name}: Builder returned null`);
|
|
failed = true;
|
|
continue;
|
|
}
|
|
const { valid, errors } = validateEventStructure(testCase.type, event);
|
|
if (!valid) {
|
|
console.error(`[FAIL] ${testCase.name}:`);
|
|
errors.forEach(err => console.error(` - ${err}`));
|
|
failed = true;
|
|
} else {
|
|
console.log(`[PASS] ${testCase.name}`);
|
|
}
|
|
} catch (err) {
|
|
console.error(`[FAIL] ${testCase.name} threw error:`, err);
|
|
failed = true;
|
|
}
|
|
}
|
|
|
|
if (failed) {
|
|
process.exit(1);
|
|
}
|