mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 12:52:26 +00:00
Add local nostr-tools fallback for restricted domains
This commit is contained in:
1 parent
c4cc89af29
commit
4f75f2dff4
4 files changed
+242
-28
No files matched your search
+96
-2
@@ -217,6 +217,76 @@ async function ensureNostrTools() {
|
||||
return cachedNostrTools || null;
|
||||
}
|
||||
|
||||
function isSimplePoolConstructor(candidate) {
|
||||
if (typeof candidate !== "function") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const prototype = candidate.prototype;
|
||||
if (!prototype || typeof prototype !== "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return typeof prototype.sub === "function" && typeof prototype.close === "function";
|
||||
}
|
||||
|
||||
function unwrapSimplePool(candidate) {
|
||||
if (!candidate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isSimplePoolConstructor(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
if (typeof candidate === "object") {
|
||||
if (isSimplePoolConstructor(candidate.SimplePool)) {
|
||||
return candidate.SimplePool;
|
||||
}
|
||||
if (isSimplePoolConstructor(candidate.default)) {
|
||||
return candidate.default;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function resolveSimplePoolConstructor(tools, scope = globalScope) {
|
||||
const candidates = [
|
||||
tools?.SimplePool,
|
||||
tools?.pool?.SimplePool,
|
||||
tools?.pool,
|
||||
tools?.SimplePool?.SimplePool,
|
||||
tools?.SimplePool?.default,
|
||||
tools?.pool?.default,
|
||||
tools?.default?.SimplePool,
|
||||
tools?.default?.pool?.SimplePool,
|
||||
tools?.default?.pool,
|
||||
];
|
||||
|
||||
if (scope && typeof scope === "object") {
|
||||
candidates.push(scope?.SimplePool);
|
||||
candidates.push(scope?.pool?.SimplePool);
|
||||
candidates.push(scope?.pool);
|
||||
const scopedTools =
|
||||
scope?.NostrTools && scope.NostrTools !== tools ? scope.NostrTools : null;
|
||||
if (scopedTools) {
|
||||
candidates.push(scopedTools.SimplePool);
|
||||
candidates.push(scopedTools.pool?.SimplePool);
|
||||
candidates.push(scopedTools.pool);
|
||||
}
|
||||
}
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const resolved = unwrapSimplePool(candidate);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
let ingestLocalViewEventRef = null;
|
||||
|
||||
async function loadIngestLocalViewEvent() {
|
||||
@@ -4292,10 +4362,34 @@ class NostrClient {
|
||||
}
|
||||
|
||||
const tools = await ensureNostrTools();
|
||||
const SimplePool = tools?.SimplePool;
|
||||
const SimplePool = resolveSimplePoolConstructor(tools);
|
||||
if (typeof SimplePool !== "function") {
|
||||
const error = new Error("NostrTools SimplePool is unavailable.");
|
||||
if (isDevMode) {
|
||||
if (tools && typeof tools === "object") {
|
||||
const availableKeys = Object.keys(tools).join(", ");
|
||||
console.warn(
|
||||
"[nostr] NostrTools helpers did not expose SimplePool. Available keys:",
|
||||
availableKeys
|
||||
);
|
||||
} else {
|
||||
console.warn(
|
||||
"[nostr] NostrTools helpers were unavailable. Check that nostr-tools bundles can load on this domain."
|
||||
);
|
||||
}
|
||||
if (nostrToolsBootstrapFailure) {
|
||||
console.warn(
|
||||
"[nostr] nostr-tools bootstrap failure details:",
|
||||
nostrToolsBootstrapFailure
|
||||
);
|
||||
}
|
||||
}
|
||||
const error = new Error(
|
||||
"NostrTools SimplePool is unavailable. Verify that nostr-tools resources can load on this domain."
|
||||
);
|
||||
error.code = "nostr-simplepool-unavailable";
|
||||
if (nostrToolsBootstrapFailure) {
|
||||
error.bootstrapFailure = nostrToolsBootstrapFailure;
|
||||
}
|
||||
this.poolPromise = null;
|
||||
throw error;
|
||||
}
|
||||
|
||||
+114
-26
@@ -1,7 +1,38 @@
|
||||
const REMOTE_IMPORT_TIMEOUT = 4500;
|
||||
const LOCAL_IMPORT_TIMEOUT = 2500;
|
||||
const SCRIPT_FALLBACK_TIMEOUT = 6000;
|
||||
const CDN_BUNDLE_URL = "https://cdn.jsdelivr.net/npm/[email protected]/lib/nostr.bundle.min.js";
|
||||
const CDN_BUNDLE_URL =
|
||||
"https://cdn.jsdelivr.net/npm/[email protected]/lib/nostr.bundle.min.js";
|
||||
const LOCAL_BUNDLE_RELATIVE_URL = "../vendor/nostr-tools.bundle.min.js";
|
||||
|
||||
const moduleUrl =
|
||||
typeof import.meta !== "undefined" && import.meta?.url ? import.meta.url : null;
|
||||
|
||||
const resolveRelativeUrl = (relativePath) => {
|
||||
if (!relativePath) {
|
||||
return relativePath;
|
||||
}
|
||||
if (/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(relativePath)) {
|
||||
return relativePath;
|
||||
}
|
||||
if (moduleUrl) {
|
||||
try {
|
||||
return new URL(relativePath, moduleUrl).href;
|
||||
} catch (error) {
|
||||
// Fall through to other resolution strategies
|
||||
}
|
||||
}
|
||||
if (typeof window !== "undefined" && window?.location?.href) {
|
||||
try {
|
||||
return new URL(relativePath, window.location.href).href;
|
||||
} catch (error) {
|
||||
return relativePath;
|
||||
}
|
||||
}
|
||||
return relativePath;
|
||||
};
|
||||
|
||||
const LOCAL_BUNDLE_URL = resolveRelativeUrl(LOCAL_BUNDLE_RELATIVE_URL);
|
||||
|
||||
let bootstrapPromise = null;
|
||||
|
||||
@@ -137,6 +168,60 @@ const ensureGlobalReadyPromise = (scope, promise) => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadScriptBundle = (scope, url, label) => {
|
||||
if (!scope?.document) {
|
||||
return Promise.resolve({
|
||||
label,
|
||||
ok: false,
|
||||
timedOut: false,
|
||||
duration: 0,
|
||||
error: new Error("Document is unavailable for script loading."),
|
||||
});
|
||||
}
|
||||
|
||||
return withTimeout(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
const { document } = scope;
|
||||
const target = document.head || document.body || document.documentElement;
|
||||
if (!target) {
|
||||
reject(new Error("Unable to locate a DOM target for script injection."));
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.async = true;
|
||||
script.src = url;
|
||||
script.crossOrigin = "anonymous";
|
||||
|
||||
const cleanup = () => {
|
||||
script.onload = null;
|
||||
script.onerror = null;
|
||||
if (script.parentNode) {
|
||||
script.parentNode.removeChild(script);
|
||||
}
|
||||
};
|
||||
|
||||
script.onload = () => {
|
||||
cleanup();
|
||||
resolve(scope?.NostrTools || null);
|
||||
};
|
||||
script.onerror = (event) => {
|
||||
cleanup();
|
||||
if (event?.error instanceof Error) {
|
||||
reject(event.error);
|
||||
} else {
|
||||
reject(new Error(`nostr-tools bundle failed to load: ${url}`));
|
||||
}
|
||||
};
|
||||
|
||||
target.appendChild(script);
|
||||
}),
|
||||
SCRIPT_FALLBACK_TIMEOUT,
|
||||
label
|
||||
);
|
||||
};
|
||||
|
||||
export function bootstrapNostrTools() {
|
||||
if (bootstrapPromise) {
|
||||
return bootstrapPromise;
|
||||
@@ -256,38 +341,41 @@ export function bootstrapNostrTools() {
|
||||
!!scope.document && (resolvedModules.length === 0 || allDynamicTimedOut);
|
||||
|
||||
if (shouldAttemptScriptFallback) {
|
||||
const scriptLoadResult = await withTimeout(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
const script = scope.document.createElement("script");
|
||||
script.src = CDN_BUNDLE_URL;
|
||||
script.async = true;
|
||||
script.onload = () => resolve(scope?.NostrTools || null);
|
||||
script.onerror = () =>
|
||||
reject(new Error("nostr-tools CDN bundle failed to load"));
|
||||
scope.document.head.appendChild(script);
|
||||
}),
|
||||
SCRIPT_FALLBACK_TIMEOUT,
|
||||
"cdn-bundle"
|
||||
);
|
||||
const scriptSources = [
|
||||
LOCAL_BUNDLE_URL
|
||||
? { url: LOCAL_BUNDLE_URL, label: "local-bundle" }
|
||||
: null,
|
||||
{ url: CDN_BUNDLE_URL, label: "cdn-bundle" },
|
||||
].filter(Boolean);
|
||||
|
||||
attempts.push({
|
||||
target: scriptLoadResult.label,
|
||||
ok: scriptLoadResult.ok,
|
||||
timedOut: !!scriptLoadResult.timedOut,
|
||||
duration: scriptLoadResult.duration,
|
||||
error: scriptLoadResult.ok
|
||||
? null
|
||||
: toSerializableError(scriptLoadResult.error),
|
||||
});
|
||||
for (const source of scriptSources) {
|
||||
const scriptLoadResult = await loadScriptBundle(
|
||||
scope,
|
||||
source.url,
|
||||
source.label
|
||||
);
|
||||
|
||||
attempts.push({
|
||||
target: scriptLoadResult.label,
|
||||
ok: scriptLoadResult.ok,
|
||||
timedOut: !!scriptLoadResult.timedOut,
|
||||
duration: scriptLoadResult.duration,
|
||||
error: scriptLoadResult.ok
|
||||
? null
|
||||
: toSerializableError(scriptLoadResult.error),
|
||||
});
|
||||
|
||||
if (!scriptLoadResult.ok) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scriptLoadResult.ok) {
|
||||
const extracted = extractToolsFromModule(scriptLoadResult.value);
|
||||
if (extracted && Object.keys(extracted).length > 0) {
|
||||
resolvedModules.push({ source: "cdn-bundle", tools: extracted });
|
||||
resolvedModules.push({ source: source.label, tools: extracted });
|
||||
if (!resolvedNip04 && hasWorkingNip04(extracted?.nip04)) {
|
||||
resolvedNip04 = extracted.nip04;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user