mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
chore(ai): event schema validation tool + fixes
- Added `scripts/agent/validate-events.mjs` to validate event builders against schemas. - Fixed `NOTE_TYPES.REPOST` schema content format (json instead of empty). - Added `NOTE_TYPES.MUTE_LIST` and `buildMuteListEvent`. - Added `buildProfileMetadataEvent` to standardise Kind 0 creation. - Updated `js/ui/profileModalController.js` to use `buildProfileMetadataEvent`. - Updated `js/userBlocks.js` to use `buildMuteListEvent` for publishing mute list snapshots. - Updated `docs/nostr-event-schemas.md` with new schemas and builders.
This commit is contained in:
1 parent
474071e84a
commit
4d2bb8bf3d
5 files changed
+406
-26
No files matched your search
@@ -16,6 +16,7 @@ import {
|
||||
setNostrEventSchemaOverrides,
|
||||
buildVideoPostEvent,
|
||||
buildRepostEvent,
|
||||
buildProfileMetadataEvent,
|
||||
} from "./nostrEventSchemas.js";
|
||||
|
||||
// Inspect the current schema
|
||||
@@ -142,7 +143,7 @@ import { updateWatchHistoryListWithDefaultClient } from "./nostrWatchHistoryFaca
|
||||
| --- | --- | --- | --- |
|
||||
| Video post (`NOTE_TYPES.VIDEO_POST`) | `30078` | `['t','video']`, `['d', <stable video identifier>]` plus optional schema append tags | JSON payload using Content Schema v3 (`version`, `title`, optional `url`, `magnet`, `thumbnail`, `description`, `mode`, `videoRootId`, `deleted`, `isPrivate`, `isNsfw`, `isForKids`, `enableComments`, `ws`, `xs`) |
|
||||
| NIP-94 mirror (`NOTE_TYPES.VIDEO_MIRROR`) | `1063` | Tags forwarded from `publishVideo` (URL, mime type, thumbnail, alt text, magnet) | Plain text alt description |
|
||||
| Repost (`NOTE_TYPES.REPOST`) | `6` | `['e', <event id>, <relay?>]` with optional address pointer `['a', <kind:pubkey:identifier>, <relay?>]`, and `['p', <pubkey>]` when the origin author is known; inherits schema append tags | Empty content |
|
||||
| Repost (`NOTE_TYPES.REPOST`) | `6` | `['e', <event id>, <relay?>]` with optional address pointer `['a', <kind:pubkey:identifier>, <relay?>]`, and `['p', <pubkey>]` when the origin author is known; inherits schema append tags | JSON-serialized event being reposted |
|
||||
| Video comment (`NOTE_TYPES.VIDEO_COMMENT`) | `1111` | NIP-22 root scope tags `['A'\|`E`\|`I`, <pointer>, <relay?>?], `['K', <root kind>]`, and `['P', <root author pubkey>, <relay?>?]` plus parent metadata `['E'\|`I`, <parent pointer>, <relay?>?, <author?>], `['K', <parent kind>]`, `['P', <parent author>, <relay?>?]`; inherits schema append tags | Plain text body sanitized to valid UTF-8 |
|
||||
|
||||
> **Publishing note:** Session actors only emit passive telemetry (for example, view counters) and must **not** sign video comments. Require a logged-in Nostr signer for comment publishing via [`commentEvents`](../js/nostr/commentEvents.js).
|
||||
@@ -159,6 +160,8 @@ import { updateWatchHistoryListWithDefaultClient } from "./nostrWatchHistoryFaca
|
||||
| Admin blacklist (`NOTE_TYPES.ADMIN_BLACKLIST`) | `30000` | `['d', 'bitvid:admin:blacklist']`, repeated `['p', <pubkey>]` entries | Empty content |
|
||||
| Admin whitelist (`NOTE_TYPES.ADMIN_WHITELIST`) | `30000` | `['d', 'bitvid:admin:whitelist']`, repeated `['p', <pubkey>]` entries | Empty content |
|
||||
| Storage challenge (`NOTE_TYPES.STORAGE_CHALLENGE`) | `22242` | `['challenge', <hex>]`, `['purpose', 'bitvid-storage-key']` | Empty content. This event is **ephemeral** and never published to relays. It serves as a challenge for the signer; the deterministic signature produced is used to derive the local encryption Master Key for the Storage Service. |
|
||||
| Profile metadata (`NOTE_TYPES.PROFILE_METADATA`) | `0` | `['d', ...]` is not used | JSON payload with NIP-01 fields (`name`, `about`, `picture`, `nip05`, etc.) |
|
||||
| Mute list (`NOTE_TYPES.MUTE_LIST`) | `10000` | Repeated `['p', <pubkey>]` tags for blocked/muted users | Optional content (often encrypted) |
|
||||
|
||||
Subscription lists therefore match the
|
||||
[NIP-51 follow-set specification](./nips/51.md#sets) by emitting kind `30000`
|
||||
|
||||
+91
-2
@@ -28,6 +28,7 @@ export const NOTE_TYPES = Object.freeze({
|
||||
ADMIN_BLACKLIST: "adminBlacklist",
|
||||
ADMIN_WHITELIST: "adminWhitelist",
|
||||
PROFILE_METADATA: "profileMetadata",
|
||||
MUTE_LIST: "muteList",
|
||||
});
|
||||
|
||||
export const SUBSCRIPTION_LIST_IDENTIFIER = "subscriptions";
|
||||
@@ -270,8 +271,8 @@ const BASE_SCHEMAS = {
|
||||
kind: 6,
|
||||
appendTags: DEFAULT_APPEND_TAGS,
|
||||
content: {
|
||||
format: "empty",
|
||||
description: "Content field intentionally empty for pure repost events.",
|
||||
format: "json",
|
||||
description: "Content field contains the JSON-serialized event being reposted.",
|
||||
},
|
||||
},
|
||||
[NOTE_TYPES.NIP71_VIDEO]: {
|
||||
@@ -458,6 +459,17 @@ const BASE_SCHEMAS = {
|
||||
description: "Standard NIP-01 profile metadata (name, about, picture, etc.).",
|
||||
},
|
||||
},
|
||||
[NOTE_TYPES.MUTE_LIST]: {
|
||||
type: NOTE_TYPES.MUTE_LIST,
|
||||
label: "Mute list",
|
||||
kind: 10000,
|
||||
participantTagName: "p",
|
||||
appendTags: DEFAULT_APPEND_TAGS,
|
||||
content: {
|
||||
format: "text",
|
||||
description: "Optional content (often encrypted) with public p tags.",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let schemaOverrides = {};
|
||||
@@ -929,6 +941,83 @@ export function buildRelayListEvent(params) {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildProfileMetadataEvent(params) {
|
||||
const {
|
||||
pubkey,
|
||||
created_at,
|
||||
metadata = {},
|
||||
content,
|
||||
additionalTags = [],
|
||||
} = params || {};
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.PROFILE_METADATA);
|
||||
const tags = [];
|
||||
appendSchemaTags(tags, schema);
|
||||
const sanitizedAdditionalTags = sanitizeAdditionalTags(additionalTags);
|
||||
if (sanitizedAdditionalTags.length) {
|
||||
tags.push(...sanitizedAdditionalTags.map((tag) => tag.slice()));
|
||||
}
|
||||
|
||||
let serializedContent = "";
|
||||
if (typeof content === "string") {
|
||||
serializedContent = content;
|
||||
} else {
|
||||
try {
|
||||
serializedContent = JSON.stringify(metadata || {});
|
||||
} catch (error) {
|
||||
serializedContent = "{}";
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
kind: schema?.kind ?? 0,
|
||||
pubkey,
|
||||
created_at,
|
||||
tags,
|
||||
content: serializedContent,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildMuteListEvent(params) {
|
||||
const {
|
||||
pubkey,
|
||||
created_at,
|
||||
pTags = [],
|
||||
content = "",
|
||||
encrypted = false,
|
||||
encryptionTag = "",
|
||||
additionalTags = [],
|
||||
} = params || {};
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.MUTE_LIST);
|
||||
const tags = [];
|
||||
|
||||
const participantTagName = schema?.participantTagName || "p";
|
||||
if (Array.isArray(pTags)) {
|
||||
pTags.forEach((hex) => {
|
||||
if (typeof hex === "string" && hex.trim()) {
|
||||
tags.push([participantTagName, hex.trim()]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (encrypted && encryptionTag) {
|
||||
tags.push(["encrypted", encryptionTag]);
|
||||
}
|
||||
|
||||
appendSchemaTags(tags, schema);
|
||||
const sanitizedAdditionalTags = sanitizeAdditionalTags(additionalTags);
|
||||
if (sanitizedAdditionalTags.length) {
|
||||
tags.push(...sanitizedAdditionalTags.map((tag) => tag.slice()));
|
||||
}
|
||||
|
||||
return {
|
||||
kind: schema?.kind ?? 10000,
|
||||
pubkey,
|
||||
created_at,
|
||||
tags,
|
||||
content: typeof content === "string" ? content : "",
|
||||
};
|
||||
}
|
||||
|
||||
export function buildViewEvent(params) {
|
||||
const {
|
||||
pubkey,
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from "../utils/hashtagNormalization.js";
|
||||
import { getActiveSigner } from "../nostr/client.js";
|
||||
import { buildPublicUrl, buildR2Key } from "../r2.js";
|
||||
import { buildProfileMetadataEvent } from "../nostrEventSchemas.js";
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
@@ -6376,14 +6377,11 @@ export class ProfileModalController {
|
||||
}
|
||||
|
||||
try {
|
||||
const content = JSON.stringify(profile);
|
||||
const event = {
|
||||
kind: 0,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
const event = buildProfileMetadataEvent({
|
||||
pubkey,
|
||||
tags: [],
|
||||
content,
|
||||
};
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
metadata: profile,
|
||||
});
|
||||
|
||||
const result =
|
||||
await this.services.nostrClient.signAndPublishEvent(event);
|
||||
|
||||
+11
-16
@@ -6,7 +6,7 @@ import {
|
||||
import { getActiveSigner } from "./nostr/index.js";
|
||||
import { isSessionActor } from "./nostr/sessionActor.js";
|
||||
import { normalizeNostrPubkey } from "./nostr/nip46Client.js";
|
||||
import { buildBlockListEvent, BLOCK_LIST_IDENTIFIER, NOTE_TYPES } from "./nostrEventSchemas.js";
|
||||
import { buildBlockListEvent, buildMuteListEvent, BLOCK_LIST_IDENTIFIER, NOTE_TYPES } from "./nostrEventSchemas.js";
|
||||
import { CACHE_POLICIES, STORAGE_TIERS } from "./nostr/cachePolicies.js";
|
||||
import { devLogger, userLogger } from "./utils/logger.js";
|
||||
import {
|
||||
@@ -1536,20 +1536,13 @@ class UserBlockListManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tags = sanitizeMuteTags(blockedPubkeys, owner);
|
||||
const sanitizedTags = sanitizeMuteTags(blockedPubkeys, owner);
|
||||
const pTags = sanitizedTags.map((tag) => tag[1]);
|
||||
|
||||
const timestamp = Number.isFinite(createdAt)
|
||||
? Math.max(0, Math.floor(createdAt))
|
||||
: Math.floor(Date.now() / 1000);
|
||||
|
||||
const event = {
|
||||
kind: 10000,
|
||||
pubkey: owner,
|
||||
created_at: timestamp,
|
||||
tags,
|
||||
content: "",
|
||||
};
|
||||
|
||||
const payloadText = typeof plaintext === "string" ? plaintext : "";
|
||||
let encryptedContent = "";
|
||||
let encryptionTagValue = "";
|
||||
@@ -1579,12 +1572,14 @@ class UserBlockListManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (encryptedContent) {
|
||||
event.content = encryptedContent;
|
||||
if (encryptionTagValue) {
|
||||
event.tags.push(["encrypted", encryptionTagValue]);
|
||||
}
|
||||
}
|
||||
const event = buildMuteListEvent({
|
||||
pubkey: owner,
|
||||
created_at: timestamp,
|
||||
pTags,
|
||||
content: encryptedContent,
|
||||
encrypted: !!encryptionTagValue,
|
||||
encryptionTag: encryptionTagValue,
|
||||
});
|
||||
|
||||
onStatus?.({ status: "mute-publishing" });
|
||||
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
import {
|
||||
getNostrEventSchema,
|
||||
buildVideoPostEvent,
|
||||
buildVideoMirrorEvent,
|
||||
buildRepostEvent,
|
||||
buildRelayListEvent,
|
||||
buildViewEvent,
|
||||
buildReactionEvent,
|
||||
buildCommentEvent,
|
||||
buildWatchHistoryEvent,
|
||||
buildSubscriptionListEvent,
|
||||
buildBlockListEvent,
|
||||
buildHashtagPreferenceEvent,
|
||||
buildAdminListEvent,
|
||||
buildProfileMetadataEvent,
|
||||
buildMuteListEvent,
|
||||
NOTE_TYPES,
|
||||
ADMIN_LIST_IDENTIFIERS
|
||||
} from "../../js/nostrEventSchemas.js";
|
||||
|
||||
const TEST_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000001";
|
||||
const TEST_TIMESTAMP = 1700000000;
|
||||
|
||||
function validateEvent(event, schema, label) {
|
||||
const errors = [];
|
||||
|
||||
// Check Kind
|
||||
if (schema.kind !== undefined && event.kind !== schema.kind) {
|
||||
errors.push(`Kind mismatch: expected ${schema.kind}, got ${event.kind}`);
|
||||
}
|
||||
|
||||
// Check Content Format
|
||||
if (schema.content) {
|
||||
if (schema.content.format === "json") {
|
||||
try {
|
||||
const parsed = JSON.parse(event.content);
|
||||
if (schema.content.fields) {
|
||||
for (const field of schema.content.fields) {
|
||||
if (field.required && parsed[field.key] === undefined) {
|
||||
errors.push(`Missing required content field: ${field.key}`);
|
||||
}
|
||||
if (parsed[field.key] !== undefined) {
|
||||
// Basic type checking
|
||||
if (field.type === 'number' && typeof parsed[field.key] !== 'number') {
|
||||
errors.push(`Field ${field.key} expected number, got ${typeof parsed[field.key]}`);
|
||||
}
|
||||
if (field.type === 'string' && typeof parsed[field.key] !== 'string') {
|
||||
errors.push(`Field ${field.key} expected string, got ${typeof parsed[field.key]}`);
|
||||
}
|
||||
if (field.type === 'boolean' && typeof parsed[field.key] !== 'boolean') {
|
||||
errors.push(`Field ${field.key} expected boolean, got ${typeof parsed[field.key]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
errors.push(`Content is not valid JSON: ${e.message}`);
|
||||
}
|
||||
} else if (schema.content.format === "empty") {
|
||||
if (event.content !== "") {
|
||||
errors.push(`Content expected to be empty, got length ${event.content.length}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check Tags
|
||||
if (schema.topicTag) {
|
||||
const hasTag = event.tags.some(t => t[0] === schema.topicTag.name && t[1] === schema.topicTag.value);
|
||||
if (!hasTag) {
|
||||
errors.push(`Missing topic tag: ${schema.topicTag.name}=${schema.topicTag.value}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (schema.identifierTag) {
|
||||
const hasTag = event.tags.some(t => t[0] === schema.identifierTag.name);
|
||||
if (!hasTag) {
|
||||
errors.push(`Missing identifier tag: ${schema.identifierTag.name}`);
|
||||
}
|
||||
if (schema.identifierTag.value) {
|
||||
const hasValue = event.tags.some(t => t[0] === schema.identifierTag.name && t[1] === schema.identifierTag.value);
|
||||
if (!hasValue) {
|
||||
errors.push(`Missing identifier tag value: ${schema.identifierTag.name}=${schema.identifierTag.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append Tags Check (Simple existence check for fixed tags)
|
||||
if (schema.appendTags) {
|
||||
schema.appendTags.forEach(expectedTag => {
|
||||
if (Array.isArray(expectedTag)) {
|
||||
const found = event.tags.some(t =>
|
||||
t.length >= expectedTag.length &&
|
||||
expectedTag.every((val, i) => t[i] === val)
|
||||
);
|
||||
if (!found) {
|
||||
errors.push(`Missing appended tag: ${JSON.stringify(expectedTag)}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error(`❌ ${label} validation failed:`);
|
||||
errors.forEach(e => console.error(` - ${e}`));
|
||||
return false;
|
||||
} else {
|
||||
console.log(`✅ ${label} validated successfully.`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async function runValidation() {
|
||||
console.log("Starting Event Schema Validation...\n");
|
||||
let allPassed = true;
|
||||
|
||||
// 1. Video Post
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.VIDEO_POST);
|
||||
const event = buildVideoPostEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
dTagValue: "test-video",
|
||||
content: {
|
||||
version: 1,
|
||||
title: "Test Video",
|
||||
videoRootId: "root-id"
|
||||
}
|
||||
});
|
||||
if (!validateEvent(event, schema, "Video Post")) allPassed = false;
|
||||
}
|
||||
|
||||
// 2. Video Mirror
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.VIDEO_MIRROR);
|
||||
const event = buildVideoMirrorEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
content: "mirror description"
|
||||
});
|
||||
if (!validateEvent(event, schema, "Video Mirror")) allPassed = false;
|
||||
}
|
||||
|
||||
// 3. Repost
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.REPOST);
|
||||
const event = buildRepostEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
eventId: "event-id",
|
||||
eventRelay: "wss://relay.example.com",
|
||||
serializedEvent: JSON.stringify({ id: "event-id", kind: 1, tags: [], content: "test" })
|
||||
});
|
||||
// Schema updated to expect JSON content, so this should pass now.
|
||||
if (!validateEvent(event, schema, "Repost")) allPassed = false;
|
||||
}
|
||||
|
||||
// 4. Relay List
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.RELAY_LIST);
|
||||
const event = buildRelayListEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
relays: ["wss://relay.one", { url: "wss://relay.two", mode: "read" }]
|
||||
});
|
||||
if (!validateEvent(event, schema, "Relay List")) allPassed = false;
|
||||
}
|
||||
|
||||
// 5. View Event
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.VIEW_EVENT);
|
||||
const event = buildViewEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
dedupeTag: "dedupe-val",
|
||||
includeSessionTag: true,
|
||||
content: "view log"
|
||||
});
|
||||
if (!validateEvent(event, schema, "View Event")) allPassed = false;
|
||||
}
|
||||
|
||||
// 6. Reaction Event
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.VIDEO_REACTION);
|
||||
const event = buildReactionEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
targetPointer: { type: "e", value: "target-id", relay: "wss://r.com" },
|
||||
content: "+"
|
||||
});
|
||||
if (!validateEvent(event, schema, "Reaction Event")) allPassed = false;
|
||||
}
|
||||
|
||||
// 7. Comment Event
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.VIDEO_COMMENT);
|
||||
const event = buildCommentEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
videoEventId: "vid-id",
|
||||
content: "Nice video!"
|
||||
});
|
||||
if (!validateEvent(event, schema, "Comment Event")) allPassed = false;
|
||||
}
|
||||
|
||||
// 8. Watch History
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.WATCH_HISTORY);
|
||||
const event = buildWatchHistoryEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
monthIdentifier: "2023-10",
|
||||
content: JSON.stringify(["watched-id-1"])
|
||||
});
|
||||
if (!validateEvent(event, schema, "Watch History")) allPassed = false;
|
||||
}
|
||||
|
||||
// 9. Subscription List
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.SUBSCRIPTION_LIST);
|
||||
const event = buildSubscriptionListEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
content: "encrypted-stuff"
|
||||
});
|
||||
if (!validateEvent(event, schema, "Subscription List")) allPassed = false;
|
||||
}
|
||||
|
||||
// 10. Block List
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.USER_BLOCK_LIST);
|
||||
const event = buildBlockListEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
content: "encrypted-stuff"
|
||||
});
|
||||
if (!validateEvent(event, schema, "Block List")) allPassed = false;
|
||||
}
|
||||
|
||||
// 11. Hashtag Preference
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.HASHTAG_PREFERENCES);
|
||||
const event = buildHashtagPreferenceEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
content: JSON.stringify({ version: 1, interests: [], disinterests: [] })
|
||||
});
|
||||
if (!validateEvent(event, schema, "Hashtag Preferences")) allPassed = false;
|
||||
}
|
||||
|
||||
// 12. Admin List (Moderation)
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.ADMIN_MODERATION_LIST);
|
||||
const event = buildAdminListEvent("moderation", {
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
hexPubkeys: ["0000000000000000000000000000000000000000000000000000000000000002"]
|
||||
});
|
||||
if (!validateEvent(event, schema, "Admin Moderation List")) allPassed = false;
|
||||
}
|
||||
|
||||
// 13. Profile Metadata
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.PROFILE_METADATA);
|
||||
const event = buildProfileMetadataEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
metadata: { name: "Test User", about: "Testing" }
|
||||
});
|
||||
if (!validateEvent(event, schema, "Profile Metadata")) allPassed = false;
|
||||
}
|
||||
|
||||
// 14. Mute List
|
||||
{
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.MUTE_LIST);
|
||||
const event = buildMuteListEvent({
|
||||
pubkey: TEST_PUBKEY,
|
||||
created_at: TEST_TIMESTAMP,
|
||||
pTags: ["0000000000000000000000000000000000000000000000000000000000000002"]
|
||||
});
|
||||
if (!validateEvent(event, schema, "Mute List")) allPassed = false;
|
||||
}
|
||||
|
||||
if (!allPassed) {
|
||||
console.error("\n❌ Some validations failed.");
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("\n✅ All validations passed.");
|
||||
}
|
||||
}
|
||||
|
||||
runValidation().catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in new issue
Block a user