This commit is contained in:
thePR0M3TH3AN committed 2026-03-04 11:22:49 -05:00
1 parent 589925f1fe
commit ef29bb6eb3
97 files changed
+46855

No files matched your search

+130
View File
@@ -0,0 +1,130 @@
import { devLogger } from "./logger.js";
/**
* Maps over an iterable with a concurrency limit.
*
* @param {Iterable} iterable - The items to map over.
* @param {Function} mapper - The async mapping function.
* @param {Object} options - Options object.
* @param {number} options.concurrency - The concurrency limit (default: Infinity).
* @returns {Promise<Array>} - The results of the mapping function in original order.
*/
export async function pMap(iterable, mapper, { concurrency = Infinity } = {}) {
const results = [];
const iterator = iterable[Symbol.iterator]();
let index = 0;
let active = 0;
let done = false;
let rejected = false;
return new Promise((resolve, reject) => {
function next() {
if (rejected) return;
if (done && active === 0) {
resolve(results);
return;
}
while (active < concurrency && !done) {
const nextItem = iterator.next();
if (nextItem.done) {
done = true;
if (active === 0) resolve(results);
return;
}
const currentIndex = index++;
active++;
Promise.resolve(nextItem.value)
.then((value) => mapper(value, currentIndex))
.then((result) => {
results[currentIndex] = result;
active--;
next();
})
.catch((err) => {
rejected = true;
reject(err);
});
}
}
next();
});
}
/**
* Similar to pMap, but returns { status: 'fulfilled', value } or { status: 'rejected', reason }
* for each item, ensuring all items are processed (like Promise.allSettled).
*
* @param {Iterable} iterable - The items to map over.
* @param {Function} mapper - The async mapping function.
* @param {Object} options - Options object.
* @param {number} options.concurrency - The concurrency limit (default: Infinity).
* @returns {Promise<Array>} - The settled results.
*/
export async function pMapSettled(iterable, mapper, options) {
return pMap(
iterable,
async (item, index) => {
try {
const value = await mapper(item, index);
return { status: "fulfilled", value };
} catch (reason) {
return { status: "rejected", reason };
}
},
options
);
}
export function withRequestTimeout(promise, timeoutMs, onTimeout, message = "Request timed out") {
const resolvedTimeout = Number(timeoutMs);
const effectiveTimeout =
Number.isFinite(resolvedTimeout) && resolvedTimeout > 0
? Math.floor(resolvedTimeout)
: 4000;
let timeoutId = null;
let settled = false;
return new Promise((resolve, reject) => {
timeoutId = setTimeout(() => {
if (settled) {
return;
}
settled = true;
if (typeof onTimeout === "function") {
try {
onTimeout();
} catch (cleanupError) {
devLogger.warn("[asyncUtils] Timeout cleanup failed:", cleanupError);
}
}
reject(new Error(message));
}, effectiveTimeout);
Promise.resolve(promise)
.then((value) => {
if (settled) {
return;
}
settled = true;
if (timeoutId) {
clearTimeout(timeoutId);
}
resolve(value);
})
.catch((error) => {
if (settled) {
return;
}
settled = true;
if (timeoutId) {
clearTimeout(timeoutId);
}
reject(error);
});
});
}
+69
View File
@@ -0,0 +1,69 @@
import { bytesToHex, sha256 } from "../../vendor/crypto-helpers.bundle.min.js";
import { devLogger } from "./logger.js";
let sharedTextEncoder = null;
export function getSharedTextEncoder() {
if (!sharedTextEncoder && typeof TextEncoder !== "undefined") {
sharedTextEncoder = new TextEncoder();
}
return sharedTextEncoder;
}
const BlobConstructor = typeof Blob !== "undefined" ? Blob : null;
export async function valueToUint8Array(value) {
if (!value) {
return null;
}
try {
if (
BlobConstructor &&
value instanceof BlobConstructor &&
typeof value.arrayBuffer === "function"
) {
const buffer = await value.arrayBuffer();
return new Uint8Array(buffer);
}
} catch (error) {
devLogger.warn("[nostr] Failed to read Blob while computing hash:", error);
return null;
}
if (typeof ArrayBuffer !== "undefined") {
if (value instanceof ArrayBuffer) {
return new Uint8Array(value);
}
if (ArrayBuffer.isView?.(value)) {
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
}
}
if (typeof Buffer !== "undefined" && Buffer.isBuffer?.(value)) {
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
}
if (typeof value === "string") {
const encoder = getSharedTextEncoder();
return encoder ? encoder.encode(value) : null;
}
return null;
}
export async function computeSha256HexFromValue(value) {
const data = await valueToUint8Array(value);
if (!data) {
return "";
}
try {
const digest = sha256(data);
const hex = typeof digest === "string" ? digest : bytesToHex(digest);
return hex ? hex.toLowerCase() : "";
} catch (error) {
devLogger.warn("[nostr] Failed to compute SHA-256 for mirror payload:", error);
return "";
}
}
+49
View File
@@ -0,0 +1,49 @@
export const EXTENSION_MIME_MAP = Object.freeze(
Object.fromEntries(
Object.entries({
mp4: "video/mp4",
m4v: "video/x-m4v",
webm: "video/webm",
mkv: "video/x-matroska",
mov: "video/quicktime",
avi: "video/x-msvideo",
ogv: "video/ogg",
ogg: "video/ogg",
m3u8: "application/x-mpegurl",
mpd: "application/dash+xml",
ts: "video/mp2t",
mpg: "video/mpeg",
mpeg: "video/mpeg",
flv: "video/x-flv",
"3gp": "video/3gpp",
}).map(([extension, mimeType]) => [
extension,
typeof mimeType === "string" ? mimeType.toLowerCase() : "",
]),
),
);
export function inferMimeTypeFromUrl(url) {
if (!url || typeof url !== "string") {
return "";
}
let pathname = "";
try {
const parsed = new URL(url);
pathname = parsed.pathname || "";
} catch (err) {
const sanitized = url.split("?")[0].split("#")[0];
pathname = sanitized || "";
}
const lastSegment = pathname.split("/").pop() || "";
const match = lastSegment.match(/\.([a-z0-9]+)$/i);
if (!match) {
return "";
}
const extension = match[1].toLowerCase();
const mimeType = EXTENSION_MIME_MAP[extension];
return typeof mimeType === "string" ? mimeType : "";
}
+102
View File
@@ -0,0 +1,102 @@
import { HEX64_REGEX } from "./hex.js";
/**
* Safely encode a hex pubkey to npub.
*
* @param {string} pubkey - The hex public key.
* @returns {string|null} The npub string or null if encoding failed.
*/
export function safeEncodeNpub(pubkey) {
if (typeof pubkey !== "string") {
return null;
}
const trimmed = pubkey.trim();
if (!trimmed) {
return null;
}
if (trimmed.startsWith("npub1")) {
return trimmed;
}
try {
if (
typeof window !== "undefined" &&
window.NostrTools &&
window.NostrTools.nip19 &&
typeof window.NostrTools.nip19.npubEncode === "function"
) {
return window.NostrTools.nip19.npubEncode(trimmed);
}
} catch (err) {
// ignore
}
return null;
}
/**
* Safely decode an npub to hex pubkey.
*
* @param {string} npub - The npub string.
* @returns {string|null} The hex public key or null if decoding failed.
*/
export function safeDecodeNpub(npub) {
if (typeof npub !== "string") {
return null;
}
const trimmed = npub.trim();
if (!trimmed) {
return null;
}
try {
if (
typeof window !== "undefined" &&
window.NostrTools &&
window.NostrTools.nip19 &&
typeof window.NostrTools.nip19.decode === "function"
) {
const decoded = window.NostrTools.nip19.decode(trimmed);
if (decoded.type === "npub" && typeof decoded.data === "string") {
return decoded.data;
}
}
} catch (err) {
// ignore
}
return null;
}
/**
* Normalize a public key to hex format, handling npub inputs.
*
* @param {string} pubkey - The public key (hex or npub).
* @returns {string|null} The normalized hex public key or null.
*/
export function normalizeHexPubkey(pubkey) {
if (typeof pubkey !== "string") {
return null;
}
const trimmed = pubkey.trim();
if (!trimmed) {
return null;
}
if (HEX64_REGEX.test(trimmed)) {
return trimmed.toLowerCase();
}
if (trimmed.startsWith("npub1")) {
const decoded = safeDecodeNpub(trimmed);
if (decoded && HEX64_REGEX.test(decoded)) {
return decoded.toLowerCase();
}
}
return null;
}
+164
View File
@@ -0,0 +1,164 @@
import { devLogger } from "./logger.js";
import { getCachedNostrTools } from "../nostr/toolkit.js";
export function pointerKey(pointer) {
if (!pointer) {
return "";
}
const type = pointer.type === "a" ? "a" : "e";
const value = typeof pointer.value === "string" ? pointer.value.trim().toLowerCase() : "";
if (!type || !value) {
return "";
}
return `${type}:${value}`;
}
export function normalizePointerTag(tag) {
if (!Array.isArray(tag) || tag.length < 2) {
return null;
}
const type = tag[0] === "a" ? "a" : tag[0] === "e" ? "e" : "";
if (!type) {
return null;
}
const value = typeof tag[1] === "string" ? tag[1].trim() : "";
if (!value) {
return null;
}
const relay =
tag.length > 2 && typeof tag[2] === "string" && tag[2].trim()
? tag[2].trim()
: null;
return { type, value, relay };
}
function clonePointerItem(pointer) {
if (!pointer || typeof pointer !== "object") {
return null;
}
const cloned = {
type: pointer.type === "a" ? "a" : "e",
value: typeof pointer.value === "string" ? pointer.value.trim() : "",
};
if (!cloned.value) {
return null;
}
if (typeof pointer.relay === "string" && pointer.relay.trim()) {
cloned.relay = pointer.relay.trim();
}
if (Number.isFinite(pointer.watchedAt)) {
cloned.watchedAt = Math.max(0, Math.floor(pointer.watchedAt));
}
if (Number.isFinite(pointer.resumeAt)) {
cloned.resumeAt = Math.max(0, Math.floor(pointer.resumeAt));
}
if (pointer.completed === true) {
cloned.completed = true;
}
if (pointer.session === true) {
cloned.session = true;
}
return cloned;
}
export { clonePointerItem };
export function mergePointerDetails(target, source) {
if (!target || typeof target !== "object" || !source || typeof source !== "object") {
return target;
}
if (source.session === true) {
target.session = true;
}
if (Number.isFinite(source.resumeAt)) {
target.resumeAt = Math.max(0, Math.floor(source.resumeAt));
}
if (source.completed === true) {
target.completed = true;
}
if (Number.isFinite(source.watchedAt)) {
target.watchedAt = Math.max(0, Math.floor(source.watchedAt));
}
if (typeof source.relay === "string" && source.relay.trim()) {
if (!target.relay || !target.relay.trim()) {
target.relay = source.relay.trim();
}
}
return target;
}
export function normalizePointerInput(pointer) {
if (!pointer) {
return null;
}
if (Array.isArray(pointer)) {
return normalizePointerTag(pointer);
}
if (typeof pointer === "object") {
if (typeof pointer.type === "string" && typeof pointer.value === "string") {
return clonePointerItem(pointer);
}
if (Array.isArray(pointer.tag)) {
return normalizePointerTag(pointer.tag);
}
}
if (typeof pointer !== "string") {
return null;
}
const trimmed = pointer.trim();
if (!trimmed) {
return null;
}
if (trimmed.startsWith("naddr") || trimmed.startsWith("nevent")) {
try {
const decoder = getCachedNostrTools()?.nip19?.decode;
if (typeof decoder === "function") {
const decoded = decoder(trimmed);
if (decoded?.type === "naddr" && decoded.data) {
const { kind, pubkey, identifier, relays } = decoded.data;
if (
typeof kind === "number" &&
typeof pubkey === "string" &&
typeof identifier === "string"
) {
const relay =
Array.isArray(relays) && relays.length && typeof relays[0] === "string"
? relays[0]
: null;
return {
type: "a",
value: `${kind}:${pubkey}:${identifier}`,
relay,
};
}
}
if (decoded?.type === "nevent" && decoded.data) {
const { id, relays } = decoded.data;
if (typeof id === "string" && id.trim()) {
const relay =
Array.isArray(relays) && relays.length && typeof relays[0] === "string"
? relays[0]
: null;
return {
type: "e",
value: id.trim(),
relay,
};
}
}
}
} catch (error) {
devLogger.warn(`[nostr] Failed to decode pointer ${trimmed}:`, error);
}
}
const type = trimmed.includes(":") ? "a" : "e";
return { type, value: trimmed, relay: null };
}
+40
View File
@@ -0,0 +1,40 @@
export class SimpleEventEmitter {
constructor(logger = null) {
this.logger = typeof logger === "function" ? logger : null;
this.listeners = new Map();
}
on(eventName, handler) {
if (typeof handler !== "function") {
return () => {};
}
if (!this.listeners.has(eventName)) {
this.listeners.set(eventName, new Set());
}
const handlers = this.listeners.get(eventName);
handlers.add(handler);
return () => {
handlers.delete(handler);
if (!handlers.size) {
this.listeners.delete(eventName);
}
};
}
emit(eventName, detail) {
const handlers = this.listeners.get(eventName);
if (!handlers || !handlers.size) {
return;
}
for (const handler of Array.from(handlers)) {
try {
handler(detail);
} catch (error) {
if (this.logger) {
this.logger(`Listener for "${eventName}" threw`, error);
}
}
}
}
}