- Add scripts/fetch-kinds.js to fetch kinds.json from central source - Add event-kinds.ts with TypeScript types and 184 event kinds - Update package.json build scripts to fetch kinds before building - Source: https://git.mleku.dev/mleku/nostr/raw/branch/main/encoders/kind/kinds.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
148 lines
4.4 KiB
JavaScript
148 lines
4.4 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Fetches kinds.json from the nostr library and generates TypeScript definitions
|
|
* Run: node scripts/fetch-kinds.js
|
|
*/
|
|
|
|
import { writeFileSync } from 'fs';
|
|
import { join, dirname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const KINDS_URL = 'https://git.mleku.dev/mleku/nostr/raw/branch/main/encoders/kind/kinds.json';
|
|
|
|
async function fetchKinds() {
|
|
console.log(`Fetching kinds from ${KINDS_URL}...`);
|
|
|
|
const response = await fetch(KINDS_URL);
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch kinds.json: ${response.status} ${response.statusText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log(`Fetched ${Object.keys(data.kinds).length} kinds (version: ${data.version})`);
|
|
|
|
return data;
|
|
}
|
|
|
|
function generateTypeScript(data) {
|
|
const kinds = [];
|
|
|
|
for (const [kindNum, info] of Object.entries(data.kinds)) {
|
|
const k = parseInt(kindNum, 10);
|
|
|
|
// Determine classification
|
|
let classification = 'regular';
|
|
if (info.classification) {
|
|
classification = info.classification;
|
|
} else if (k === 0 || k === 3 || (k >= data.ranges.replaceable.start && k < data.ranges.replaceable.end)) {
|
|
classification = 'replaceable';
|
|
} else if (k >= data.ranges.parameterized.start && k <= data.ranges.parameterized.end) {
|
|
classification = 'parameterized';
|
|
} else if (k >= data.ranges.ephemeral.start && k < data.ranges.ephemeral.end) {
|
|
classification = 'ephemeral';
|
|
}
|
|
|
|
kinds.push({
|
|
kind: k,
|
|
name: info.name,
|
|
description: info.description,
|
|
nip: info.nip || null,
|
|
classification,
|
|
deprecated: info.deprecated || false,
|
|
spec: info.spec || null
|
|
});
|
|
}
|
|
|
|
// Sort by kind number
|
|
kinds.sort((a, b) => a.kind - b.kind);
|
|
|
|
return `/**
|
|
* Nostr Event Kinds Database
|
|
* Auto-generated from ${KINDS_URL}
|
|
* Version: ${data.version}
|
|
* Source: ${data.source}
|
|
*
|
|
* DO NOT EDIT - This file is auto-generated by scripts/fetch-kinds.js
|
|
*/
|
|
|
|
export interface KindInfo {
|
|
kind: number;
|
|
name: string;
|
|
description: string;
|
|
nip: string | null;
|
|
classification: 'regular' | 'replaceable' | 'ephemeral' | 'parameterized';
|
|
deprecated: boolean;
|
|
spec: string | null;
|
|
}
|
|
|
|
export interface KindRanges {
|
|
regular: { start: number; end: number; description: string };
|
|
replaceable: { start: number; end: number; description: string };
|
|
ephemeral: { start: number; end: number; description: string };
|
|
parameterized: { start: number; end: number; description: string };
|
|
}
|
|
|
|
export const EVENT_KINDS: KindInfo[] = ${JSON.stringify(kinds, null, 2)};
|
|
|
|
export const KIND_RANGES: KindRanges = ${JSON.stringify(data.ranges, null, 2)};
|
|
|
|
export const PRIVILEGED_KINDS: number[] = ${JSON.stringify(data.privileged)};
|
|
|
|
export const DIRECTORY_KINDS: number[] = ${JSON.stringify(data.directory)};
|
|
|
|
export const KIND_ALIASES: Record<string, number> = ${JSON.stringify(data.aliases, null, 2)};
|
|
|
|
// Lookup map for fast access
|
|
const kindMap = new Map<number, KindInfo>(EVENT_KINDS.map(k => [k.kind, k]));
|
|
|
|
export function getKindInfo(kind: number): KindInfo | undefined {
|
|
return kindMap.get(kind);
|
|
}
|
|
|
|
export function getKindName(kind: number): string {
|
|
const info = kindMap.get(kind);
|
|
return info ? info.name : \`Kind \${kind}\`;
|
|
}
|
|
|
|
export function isReplaceable(kind: number): boolean {
|
|
if (kind === 0 || kind === 3) return true;
|
|
return kind >= KIND_RANGES.replaceable.start && kind < KIND_RANGES.replaceable.end;
|
|
}
|
|
|
|
export function isEphemeral(kind: number): boolean {
|
|
return kind >= KIND_RANGES.ephemeral.start && kind < KIND_RANGES.ephemeral.end;
|
|
}
|
|
|
|
export function isParameterized(kind: number): boolean {
|
|
return kind >= KIND_RANGES.parameterized.start && kind <= KIND_RANGES.parameterized.end;
|
|
}
|
|
|
|
export function isPrivileged(kind: number): boolean {
|
|
return PRIVILEGED_KINDS.includes(kind);
|
|
}
|
|
|
|
export function isDirectoryKind(kind: number): boolean {
|
|
return DIRECTORY_KINDS.includes(kind);
|
|
}
|
|
`;
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
const data = await fetchKinds();
|
|
const ts = generateTypeScript(data);
|
|
|
|
// Write to common library
|
|
const outPath = join(__dirname, '..', 'projects', 'common', 'src', 'lib', 'constants', 'event-kinds.ts');
|
|
|
|
writeFileSync(outPath, ts);
|
|
console.log(`Generated ${outPath} with ${Object.keys(data.kinds).length} kinds`);
|
|
} catch (error) {
|
|
console.error('Error:', error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main();
|