Browse Source

fix build

master
Silberengel 1 week ago
parent
commit
e81570b9ba
  1. 1
      .npmrc
  2. 6
      esbuild.config.mjs
  3. 1379
      package-lock.json
  4. 10
      package.json
  5. 3
      src/eventManager.ts
  6. 4
      src/eventStorage.ts
  7. 2
      src/main.ts
  8. 105
      src/nostr/authHandler.ts
  9. 21
      src/nostr/eventBuilder.ts
  10. 108
      src/nostr/relayClient.ts
  11. 5
      src/relayManager.ts
  12. 6
      src/types/js-yaml.d.ts

1
.npmrc

@ -0,0 +1 @@
legacy-peer-deps=true

6
esbuild.config.mjs

@ -4,12 +4,10 @@ import builtins from "builtin-modules";
const isProduction = process.argv[2] === "production"; const isProduction = process.argv[2] === "production";
const banner = const banner = `/*
"/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin if you want to view the source, please visit the github repository of this plugin
*/ */`;
";
const prodConfig = { const prodConfig = {
banner: { banner: {

1379
package-lock.json generated

File diff suppressed because it is too large Load Diff

10
package.json

@ -9,20 +9,22 @@
"version": "node version-bump.mjs && git add manifest.json versions.json" "version": "node version-bump.mjs && git add manifest.json versions.json"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "Silberengel",
"homepage": "https://gitcitadel.com",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.6", "@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "5.29.0", "@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0", "@typescript-eslint/parser": "5.29.0",
"@types/js-yaml": "^4.0.9",
"builtin-modules": "3.3.0", "builtin-modules": "3.3.0",
"esbuild": "0.17.3", "esbuild": "0.17.3",
"obsidian": "latest", "obsidian": "latest",
"tslib": "2.4.0", "tslib": "2.4.0",
"typescript": "4.7.4" "typescript": "5.3.3"
}, },
"dependencies": { "dependencies": {
"nostr-tools": "^2.4.0", "nostr-tools": "^2.20.0",
"js-yaml": "^4.1.0" "js-yaml": "^4.1.0"
} }
} }

3
src/eventManager.ts

@ -102,10 +102,11 @@ export async function buildAsciiDocEvents(
} }
// Now build this index event with references to children // Now build this index event with references to children
const baseMetadata = node.metadata as Kind30040Metadata;
const indexMetadata: Kind30040Metadata = { const indexMetadata: Kind30040Metadata = {
...baseMetadata,
kind: 30040, kind: 30040,
title: node.title, title: node.title,
...(node.metadata as Kind30040Metadata),
}; };
const tags = buildTagsFromMetadata(indexMetadata, pubkey, childEvents); const tags = buildTagsFromMetadata(indexMetadata, pubkey, childEvents);

4
src/eventStorage.ts

@ -39,8 +39,8 @@ export async function loadEvents(
return []; return [];
} }
const content = await app.vault.read(eventsFile); const content = await app.vault.read(eventsFile);
const lines = content.split("\n").filter((line) => line.trim().length > 0); const lines = content.split("\n").filter((line: string) => line.trim().length > 0);
return lines.map((line) => JSON.parse(line) as SignedEvent); return lines.map((line: string) => JSON.parse(line) as SignedEvent);
} catch (error) { } catch (error) {
console.error("Error loading events:", error); console.error("Error loading events:", error);
return []; return [];

2
src/main.ts

@ -12,7 +12,7 @@ import { parseAsciiDocStructure, isAsciiDocDocument } from "./asciidocParser";
import { normalizeSecretKey, getPubkeyFromPrivkey } from "./nostr/eventBuilder"; import { normalizeSecretKey, getPubkeyFromPrivkey } from "./nostr/eventBuilder";
export default class ScriptoriumPlugin extends Plugin { export default class ScriptoriumPlugin extends Plugin {
settings: ScriptoriumSettings; settings!: ScriptoriumSettings;
async onload() { async onload() {
await this.loadSettings(); await this.loadSettings();

105
src/nostr/authHandler.ts

@ -12,49 +12,14 @@ export async function handleAuthChallenge(
): Promise<boolean> { ): Promise<boolean> {
try { try {
const normalizedKey = normalizeSecretKey(privkey); const normalizedKey = normalizeSecretKey(privkey);
const pubkey = getPublicKey(normalizedKey);
// Create kind 22242 AUTH event // Use relay.auth() method which handles the AUTH flow
const authEvent = finalizeEvent( // auth() returns a promise that resolves with a string (challenge response)
{ // We consider it successful if it doesn't throw
kind: 22242, await relay.auth(async (eventTemplate) => {
pubkey, return finalizeEvent(eventTemplate, normalizedKey);
created_at: Math.floor(Date.now() / 1000),
tags: [
["relay", relayUrl],
["challenge", challenge],
],
content: "",
},
normalizedKey
);
// Send AUTH event
return new Promise((resolve) => {
const timeout = setTimeout(() => {
resolve(false);
}, 10000);
relay.on("ok", (ok) => {
if (ok.id === authEvent.id && ok.ok) {
clearTimeout(timeout);
resolve(true);
}
});
relay.on("error", () => {
clearTimeout(timeout);
resolve(false);
});
relay.send(["AUTH", authEvent]);
// Also listen for OK message directly
setTimeout(() => {
clearTimeout(timeout);
resolve(false);
}, 5000);
}); });
return true;
} catch (error) { } catch (error) {
console.error("Error handling AUTH challenge:", error); console.error("Error handling AUTH challenge:", error);
return false; return false;
@ -69,34 +34,21 @@ export async function ensureAuthenticated(
privkey: string, privkey: string,
relayUrl: string relayUrl: string
): Promise<boolean> { ): Promise<boolean> {
return new Promise((resolve) => { try {
let challengeReceived = false; const normalizedKey = normalizeSecretKey(privkey);
let authHandled = false;
const timeout = setTimeout(() => {
if (!authHandled) {
resolve(true); // Assume no AUTH required if no challenge received
}
}, 2000);
// Listen for AUTH challenge // Set up auth handler if relay sends challenge
relay.on("auth", async (challenge: string) => { relay.onauth = async (eventTemplate) => {
challengeReceived = true; return finalizeEvent(eventTemplate, normalizedKey);
clearTimeout(timeout); };
const success = await handleAuthChallenge(relay, challenge, privkey, relayUrl);
authHandled = true;
resolve(success);
});
// If no challenge received within timeout, assume no AUTH required // Try to authenticate - this will only run if relay requires it
setTimeout(() => { // The relay will call onauth if it needs authentication
if (!challengeReceived) { return true;
clearTimeout(timeout); } catch (error) {
authHandled = true; console.error("Error ensuring authentication:", error);
resolve(true); return false;
} }
}, 2000);
});
} }
/** /**
@ -108,12 +60,17 @@ export async function handleAuthRequiredError(
relayUrl: string, relayUrl: string,
originalOperation: () => Promise<any> originalOperation: () => Promise<any>
): Promise<any> { ): Promise<any> {
// Try to authenticate try {
const authenticated = await ensureAuthenticated(relay, privkey, relayUrl); const normalizedKey = normalizeSecretKey(privkey);
if (!authenticated) {
throw new Error("Failed to authenticate with relay"); // Authenticate using relay.auth()
} await relay.auth(async (eventTemplate) => {
return finalizeEvent(eventTemplate, normalizedKey);
});
// Retry original operation // Retry original operation
return originalOperation(); return originalOperation();
} catch (error: any) {
throw new Error(`Failed to authenticate with relay: ${error.message}`);
}
} }

21
src/nostr/eventBuilder.ts

@ -4,7 +4,7 @@ import { EventKind, EventMetadata, SignedEvent } from "../types";
/** /**
* Normalize secret key from bech32 nsec or hex format to hex * Normalize secret key from bech32 nsec or hex format to hex
*/ */
export function normalizeSecretKey(key: string): string { export function normalizeSecretKey(key: string): Uint8Array {
if (key.startsWith("nsec")) { if (key.startsWith("nsec")) {
try { try {
const decoded = nip19.decode(key); const decoded = nip19.decode(key);
@ -17,7 +17,12 @@ export function normalizeSecretKey(key: string): string {
} }
// Assume hex format (64 chars) // Assume hex format (64 chars)
if (key.length === 64) { if (key.length === 64) {
return key.toLowerCase(); const hex = key.toLowerCase();
const bytes = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return bytes;
} }
throw new Error("Invalid key format. Expected nsec bech32 or 64-char hex string."); throw new Error("Invalid key format. Expected nsec bech32 or 64-char hex string.");
} }
@ -30,6 +35,13 @@ export function getPubkeyFromPrivkey(privkey: string): string {
return getPublicKey(normalized); return getPublicKey(normalized);
} }
/**
* Get public key from private key (Uint8Array version)
*/
export function getPubkeyFromPrivkeyBytes(privkey: Uint8Array): string {
return getPublicKey(privkey);
}
/** /**
* Build tags array from metadata * Build tags array from metadata
*/ */
@ -185,15 +197,14 @@ export function createSignedEvent(
const pubkey = getPublicKey(normalizedKey); const pubkey = getPublicKey(normalizedKey);
const created_at = createdAt || Math.floor(Date.now() / 1000); const created_at = createdAt || Math.floor(Date.now() / 1000);
const unsignedEvent = { const eventTemplate = {
kind, kind,
pubkey,
created_at, created_at,
tags, tags,
content, content,
}; };
const signedEvent = finalizeEvent(unsignedEvent, normalizedKey); const signedEvent = finalizeEvent(eventTemplate, normalizedKey);
return { return {
...signedEvent, ...signedEvent,

108
src/nostr/relayClient.ts

@ -1,4 +1,4 @@
import { Relay, relayInit } from "nostr-tools"; import { Relay } from "nostr-tools";
import { SignedEvent, PublishingResult } from "../types"; import { SignedEvent, PublishingResult } from "../types";
import { ensureAuthenticated, handleAuthRequiredError } from "./authHandler"; import { ensureAuthenticated, handleAuthRequiredError } from "./authHandler";
@ -14,73 +14,63 @@ export async function publishEventToRelay(
let relay: Relay | null = null; let relay: Relay | null = null;
try { try {
relay = relayInit(relayUrl); relay = new Relay(relayUrl);
await relay.connect(); await relay.connect();
// Ensure authenticated if needed // Ensure authenticated if needed
await ensureAuthenticated(relay, privkey, relayUrl); await ensureAuthenticated(relay, privkey, relayUrl);
return new Promise((resolve) => { // Set up notice handler for auth-required messages
const timer = setTimeout(() => { const originalOnNotice = relay.onnotice;
if (relay) { relay.onnotice = (notice: string) => {
relay.close(); if (notice.includes("auth-required")) {
} handleAuthRequiredError(relay!, privkey, relayUrl, async () => {
resolve({ return await relay!.publish(event);
eventId: event.id, }).catch((error) => {
relay: relayUrl, console.error("Auth failed:", error);
success: false,
message: "Timeout waiting for relay response",
});
}, timeout);
const publishPromise = new Promise<PublishingResult>((innerResolve) => {
relay!.on("ok", (ok) => {
if (ok.id === event.id) {
clearTimeout(timer);
relay?.close();
innerResolve({
eventId: event.id,
relay: relayUrl,
success: ok.ok,
message: ok.message || undefined,
});
}
});
relay!.on("error", (error) => {
clearTimeout(timer);
relay?.close();
innerResolve({
eventId: event.id,
relay: relayUrl,
success: false,
message: error.message || "Relay error",
});
}); });
}
if (originalOnNotice) {
originalOnNotice(notice);
}
};
// Handle auth-required errors // Publish event - returns a promise that resolves with the reason string
relay!.on("notice", (notice) => { // The reason indicates success (e.g., "seen", "duplicate") or failure
if (notice.includes("auth-required")) { try {
handleAuthRequiredError(relay!, privkey, relayUrl, async () => { const reason = await Promise.race([
relay!.publish(event); relay.publish(event),
}).catch((error) => { new Promise<string>((resolve) => {
clearTimeout(timer); setTimeout(() => resolve("timeout"), timeout);
relay?.close(); }),
innerResolve({ ]);
eventId: event.id,
relay: relayUrl,
success: false,
message: `Auth failed: ${error.message}`,
});
});
}
});
relay!.publish(event); relay.close();
});
publishPromise.then(resolve); // Check if publish was successful
}); // Reasons like "seen", "duplicate", "broadcast" indicate success
// "blocked", "invalid", etc. indicate failure
const success = reason !== "timeout" &&
!reason.toLowerCase().includes("error") &&
!reason.toLowerCase().includes("blocked") &&
!reason.toLowerCase().includes("invalid") &&
!reason.toLowerCase().includes("restricted");
return {
eventId: event.id,
relay: relayUrl,
success,
message: success ? undefined : reason,
};
} catch (error: any) {
relay.close();
return {
eventId: event.id,
relay: relayUrl,
success: false,
message: error.message || "Publish failed",
};
}
} catch (error: any) { } catch (error: any) {
if (relay) { if (relay) {
relay.close(); relay.close();

5
src/relayManager.ts

@ -1,5 +1,6 @@
import { Relay, relayInit, getPublicKey } from "nostr-tools"; import { Relay, getPublicKey } from "nostr-tools";
import { RelayInfo } from "./types"; import { RelayInfo } from "./types";
import { normalizeSecretKey } from "./nostr/eventBuilder";
/** /**
* Default relay URLs to query for kind 10002 * Default relay URLs to query for kind 10002
@ -60,7 +61,7 @@ export async function fetchRelayListFromRelay(
}, timeout); }, timeout);
try { try {
relay = relayInit(relayUrl); relay = new Relay(relayUrl);
await relay.connect(); await relay.connect();
const sub = relay.subscribe( const sub = relay.subscribe(

6
src/types/js-yaml.d.ts vendored

@ -0,0 +1,6 @@
declare module "js-yaml" {
export function load(str: string, options?: any): any;
export function dump(obj: any, options?: any): string;
export function safeLoad(str: string, options?: any): any;
export function safeDump(obj: any, options?: any): string;
}
Loading…
Cancel
Save