Fix Netlify build by restoring dist build script and config

- Restore `scripts/build-dist.mjs` to clean `dist/`, build CSS, and copy assets.
- Update `package.json` to use the new build script and serve `dist`.
- Update `netlify.toml` to publish the `dist` directory.
- Update `dist/` artifacts to match current source.

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-01 13:30:49 +00:00
1 parent a432d3d4c0
commit c862cbc358
72 files changed
+1555 -5883

No files matched your search

+32 -177
View File
@@ -5,7 +5,7 @@ import {
requestDefaultExtensionPermissions,
} from "./nostrClientFacade.js";
import { convertEventToVideo as sharedConvertEventToVideo } from "./nostr/index.js";
import { normalizeNostrPubkey, sanitizeRelayList } from "./nostr/nip46Client.js";
import { normalizeNostrPubkey } from "./nostr/nip46Client.js";
import {
listVideoViewEvents,
subscribeVideoViewEvents,
@@ -31,16 +31,11 @@ import { devLogger, userLogger } from "./utils/logger.js";
import moderationService from "./services/moderationService.js";
import nostrService from "./services/nostrService.js";
import { profileCache } from "./state/profileCache.js";
import {
DEFAULT_NIP07_ENCRYPTION_METHODS,
runNip07WithRetry,
NIP07_PRIORITY,
} from "./nostr/nip07Permissions.js";
import { relaySubscriptionService } from "./services/relaySubscriptionService.js";
import { runNip07WithRetry, NIP07_PRIORITY } from "./nostr/nip07Permissions.js";
const SUBSCRIPTION_SET_KIND =
getNostrEventSchema(NOTE_TYPES.SUBSCRIPTION_LIST)?.kind ?? 30000;
const DECRYPT_TIMEOUT_MS = 15000;
const DECRYPT_TIMEOUT_MS = 90000;
const DECRYPT_RETRY_DELAY_MS = 10000;
function normalizeHexPubkey(value) {
@@ -334,12 +329,10 @@ class SubscriptionsManager {
this.currentUserPubkey = null;
this.loaded = false;
this.loadingPromise = null;
this.backgroundLoading = false;
this.subscriptionListView = null;
this.lastRunOptions = null;
this.lastResult = null;
this.lastContainerId = null;
this.lastLoadError = null;
this.unsubscribeFromNostrUpdates = null;
this.pendingRefreshPromise = null;
this.scheduledRefreshDetail = null;
@@ -348,7 +341,6 @@ class SubscriptionsManager {
this.emitter = new TinyEventEmitter();
this.decryptRetryTimeoutId = null;
this.ensureNostrServiceListener();
this.subscriptionKey = null;
profileCache.subscribe((event, detail) => {
if (event === "profileChanged") {
@@ -358,9 +350,7 @@ class SubscriptionsManager {
} else if (event === "runtimeCleared" && detail.pubkey === this.currentUserPubkey) {
this.reset();
if (this.currentUserPubkey) {
this.loadSubscriptions(this.currentUserPubkey, {
allowPermissionPrompt: false,
});
this.loadSubscriptions(this.currentUserPubkey);
}
}
});
@@ -369,13 +359,12 @@ class SubscriptionsManager {
/**
* Decrypt the subscription list from kind=30000 (d="subscriptions").
*/
async loadSubscriptions(userPubkey, options = {}) {
async loadSubscriptions(userPubkey) {
if (!userPubkey) {
userLogger.warn("[SubscriptionsManager] No pubkey => cannot load subs.");
return;
}
const allowPermissionPrompt = options?.allowPermissionPrompt !== false;
const normalizedUserPubkey = normalizeHexPubkey(userPubkey) || userPubkey;
// 1. Attempt to load from cache first
@@ -390,25 +379,14 @@ class SubscriptionsManager {
this.loaded = true;
// Trigger background update
if (!allowPermissionPrompt && !this.backgroundLoading) {
this.backgroundLoading = true;
this.emitter.emit("change", {
action: "background-loading",
subscribedPubkeys: Array.from(this.subscribedPubkeys),
background: true,
});
} else if (allowPermissionPrompt) {
this.backgroundLoading = false;
}
this.updateFromRelays(userPubkey, { allowPermissionPrompt }).catch((err) => {
this.updateFromRelays(userPubkey).catch((err) => {
devLogger.warn("[SubscriptionsManager] Background update failed:", err);
});
return;
}
// 2. If no cache, must wait for relays
await this.updateFromRelays(userPubkey, { allowPermissionPrompt });
await this.updateFromRelays(userPubkey);
}
saveToCache(userPubkey) {
@@ -424,7 +402,7 @@ class SubscriptionsManager {
return this.emitter.on(eventName, handler);
}
scheduleDecryptRetry(userPubkey, error, options = {}) {
scheduleDecryptRetry(userPubkey, error) {
const normalized = normalizeHexPubkey(userPubkey) || userPubkey;
if (!normalized) {
return;
@@ -437,7 +415,7 @@ class SubscriptionsManager {
if (this.currentUserPubkey && this.currentUserPubkey !== normalized) {
return;
}
this.updateFromRelays(normalized, options).catch((retryError) => {
this.updateFromRelays(normalized).catch((retryError) => {
userLogger.warn("[SubscriptionsManager] Decryption retry failed:", retryError);
});
}, DECRYPT_RETRY_DELAY_MS);
@@ -447,14 +425,11 @@ class SubscriptionsManager {
);
}
async updateFromRelays(userPubkey, options = {}) {
async updateFromRelays(userPubkey) {
if (!userPubkey) return;
try {
const allowPermissionPrompt = options?.allowPermissionPrompt !== false;
const wasBackgroundLoading = this.backgroundLoading;
const normalizedUserPubkey = normalizeHexPubkey(userPubkey) || userPubkey;
this.lastLoadError = null;
const readRelays = relayManager.getReadRelayUrls();
const relayUrls = readRelays.length > 0 ? readRelays : Array.from(DEFAULT_RELAY_URLS);
@@ -462,26 +437,8 @@ class SubscriptionsManager {
devLogger.warn(
"[SubscriptionsManager] No relay URLs available while loading subscriptions.",
);
if (wasBackgroundLoading) {
this.backgroundLoading = false;
this.emitter.emit("change", {
action: "background-loaded",
subscribedPubkeys: Array.from(this.subscribedPubkeys),
});
}
return;
}
if (!nostrClient?.pool && typeof nostrClient?.ensurePool === "function") {
try {
await nostrClient.ensurePool();
} catch (error) {
devLogger.warn(
"[SubscriptionsManager] Failed to initialize relay pool for subscription",
error,
);
}
}
this.ensureSubscriptionListSubscription(normalizedUserPubkey, relayUrls);
// Use incremental fetch helper
const cachedSnapshot = parseCachedSubscriptionSnapshot(
@@ -493,7 +450,7 @@ class SubscriptionsManager {
pubkey: normalizedUserPubkey,
dTag: SUBSCRIPTION_LIST_IDENTIFIER,
relayUrls,
since: shouldForceFullFetch ? 0 : (cachedSnapshot.createdAt || 0),
since: shouldForceFullFetch ? 0 : undefined,
timeoutMs: 12000,
});
@@ -516,7 +473,7 @@ class SubscriptionsManager {
pubkey: normalizedSessionActorPubkey,
dTag: SUBSCRIPTION_LIST_IDENTIFIER,
relayUrls,
since: shouldForceSessionFetch ? 0 : (sessionCachedSnapshot.createdAt || 0),
since: shouldForceSessionFetch ? 0 : undefined,
timeoutMs: 12000,
});
if (sessionEvents.length) {
@@ -559,13 +516,6 @@ class SubscriptionsManager {
// This means no updates. We keep what we have.
devLogger.log("[SubscriptionsManager] No updates from relays.");
}
if (wasBackgroundLoading) {
this.backgroundLoading = false;
this.emitter.emit("change", {
action: "background-loaded",
subscribedPubkeys: Array.from(this.subscribedPubkeys),
});
}
return;
}
@@ -584,9 +534,7 @@ class SubscriptionsManager {
let decryptResult;
try {
const decryptPromise = this.decryptSubscriptionEvent(newest, userPubkey, {
allowPermissionPrompt,
});
const decryptPromise = this.decryptSubscriptionEvent(newest, userPubkey);
const timeoutPromise = new Promise((_, reject) =>
setTimeout(
() => {
@@ -605,33 +553,17 @@ class SubscriptionsManager {
}
if (!decryptResult.ok) {
this.lastLoadError = decryptResult.error || null;
if (decryptResult.error?.code === "subscriptions-decrypt-timeout") {
if (!this.loaded && !cachedSnapshot.hasSnapshot) {
this.loaded = true;
}
this.scheduleDecryptRetry(normalizedUserPubkey, decryptResult.error, {
allowPermissionPrompt,
});
return;
}
if (
!allowPermissionPrompt &&
decryptResult.error?.code === "subscriptions-permission-required"
) {
this.scheduleDecryptRetry(normalizedUserPubkey, decryptResult.error);
return;
}
userLogger.error(
"[SubscriptionsManager] Failed to decrypt subscription list:",
decryptResult.error,
);
if (wasBackgroundLoading) {
this.backgroundLoading = false;
this.emitter.emit("change", {
action: "background-loaded",
subscribedPubkeys: Array.from(this.subscribedPubkeys),
});
}
if (!this.loaded) {
this.subscribedPubkeys.clear();
this.subsEventId = null;
@@ -651,9 +583,6 @@ class SubscriptionsManager {
this.subscribedPubkeys = newSet;
this.currentUserPubkey = normalizedUserPubkey;
this.loaded = true;
if (wasBackgroundLoading) {
this.backgroundLoading = false;
}
// Update persistent cache
this.saveToCache(normalizedUserPubkey);
@@ -681,31 +610,22 @@ class SubscriptionsManager {
error
);
});
} else if (wasBackgroundLoading) {
this.emitter.emit("change", {
action: "background-loaded",
subscribedPubkeys: Array.from(this.subscribedPubkeys),
});
}
} catch (err) {
this.lastLoadError = err || null;
userLogger.error("[SubscriptionsManager] Failed to update subs from relays:", err);
}
}
reset() {
this.stopSubscriptionListSubscription();
this.subscribedPubkeys.clear();
this.subsEventId = null;
this.subsEventCreatedAt = null;
this.currentUserPubkey = null;
this.loaded = false;
this.backgroundLoading = false;
this.lastRunOptions = null;
this.lastResult = null;
this.hasRenderedOnce = false;
this.lastLoadError = null;
if (this.decryptRetryTimeoutId) {
clearTimeout(this.decryptRetryTimeoutId);
this.decryptRetryTimeoutId = null;
@@ -713,56 +633,7 @@ class SubscriptionsManager {
this.emitter.emit("change", { action: "reset", subscribedPubkeys: [] });
}
stopSubscriptionListSubscription() {
if (this.subscriptionKey) {
relaySubscriptionService.stopSubscription(this.subscriptionKey, "reset");
this.subscriptionKey = null;
}
}
ensureSubscriptionListSubscription(userPubkey, relays) {
const normalized = normalizeHexPubkey(userPubkey);
if (!normalized || !nostrClient?.pool) {
return null;
}
const key = `subscriptions:${normalized}`;
this.subscriptionKey = key;
const filters = [
{
kinds: [SUBSCRIPTION_SET_KIND],
authors: [normalized],
"#d": [SUBSCRIPTION_LIST_IDENTIFIER],
},
];
return relaySubscriptionService.ensureSubscription({
key,
pool: nostrClient.pool,
relays,
filters,
label: "subscription-list",
onEvent: (event) => this.handleSubscriptionListEvent(event),
});
}
handleSubscriptionListEvent(event) {
const normalized = normalizeHexPubkey(event?.pubkey);
if (!normalized || normalized !== this.currentUserPubkey) {
return;
}
this.updateFromRelays(normalized, { allowPermissionPrompt: false })
.catch((error) => {
devLogger.warn(
"[SubscriptionsManager] Failed to refresh after subscription list event",
error,
);
});
}
async ensureLoaded(actorHex, options = {}) {
async ensureLoaded(actorHex) {
devLogger.log("[SubscriptionsManager] ensureLoaded start", actorHex);
const normalizedActor = normalizeHexPubkey(actorHex) || actorHex;
if (!normalizedActor) {
@@ -784,7 +655,7 @@ class SubscriptionsManager {
return;
}
const loader = this.loadSubscriptions(normalizedActor, options);
const loader = this.loadSubscriptions(normalizedActor);
this.loadingPromise = loader;
try {
@@ -838,7 +709,7 @@ class SubscriptionsManager {
return Array.from(this.subscribedPubkeys);
}
async decryptSubscriptionEvent(event, userPubkey, options = {}) {
async decryptSubscriptionEvent(event, userPubkey) {
const ciphertext = typeof event?.content === "string" ? event.content : "";
if (!ciphertext) {
const error = new Error("Subscription event is missing ciphertext content.");
@@ -846,7 +717,6 @@ class SubscriptionsManager {
return { ok: false, error };
}
const allowPermissionPrompt = options?.allowPermissionPrompt !== false;
const hints = extractEncryptionHints(event);
const requiresNip44 = hints.includes("nip44") || hints.includes("nip44_v2");
const requiresNip04 = !hints.length || hints.includes("nip04") || !requiresNip44;
@@ -861,8 +731,7 @@ class SubscriptionsManager {
if (
!signer ||
(!signerHasRequiredDecryptors(signer) &&
typeof nostrClient?.ensureActiveSignerForPubkey === "function" &&
allowPermissionPrompt)
typeof nostrClient?.ensureActiveSignerForPubkey === "function")
) {
signer = await nostrClient.ensureActiveSignerForPubkey(userPubkey);
}
@@ -870,11 +739,7 @@ class SubscriptionsManager {
const signerHasNip04 = typeof signer?.nip04Decrypt === "function";
const signerHasNip44 = typeof signer?.nip44Decrypt === "function";
const nostrApi = allowPermissionPrompt
? typeof window !== "undefined"
? window?.nostr
: null
: null;
const nostrApi = typeof window !== "undefined" ? window?.nostr : null;
const windowHasNip04 = typeof nostrApi?.nip04?.decrypt === "function";
const windowHasNip44 =
(nostrApi?.nip44 && typeof nostrApi.nip44.decrypt === "function") ||
@@ -884,17 +749,8 @@ class SubscriptionsManager {
(!signerHasNip44 && !windowHasNip44 && requiresNip44) ||
(!signerHasNip04 && !windowHasNip04 && requiresNip04)
) {
if (!allowPermissionPrompt) {
const error = new Error(
"Decrypt permissions are required to read subscriptions."
);
error.code = "subscriptions-permission-required";
return { ok: false, error };
}
try {
const permissionResult = await requestDefaultExtensionPermissions(
DEFAULT_NIP07_ENCRYPTION_METHODS,
);
const permissionResult = await requestDefaultExtensionPermissions();
if (!permissionResult?.ok) {
const error =
permissionResult?.error instanceof Error
@@ -942,19 +798,13 @@ class SubscriptionsManager {
}
if (nostrApi) {
const decrypterOptions = {
priority: NIP07_PRIORITY.HIGH,
timeoutMs: 12000,
retryMultiplier: 1,
};
if (typeof nostrApi.nip04?.decrypt === "function") {
registerDecryptor("nip04", (payload) =>
runNip07WithRetry(
() => nostrApi.nip04.decrypt(userPubkey, payload),
{
label: "nip04.decrypt",
...decrypterOptions,
priority: NIP07_PRIORITY.HIGH,
},
),
);
@@ -971,7 +821,7 @@ class SubscriptionsManager {
() => nip44.decrypt(userPubkey, payload),
{
label: "nip44.decrypt",
...decrypterOptions,
priority: NIP07_PRIORITY.HIGH,
},
),
);
@@ -985,7 +835,7 @@ class SubscriptionsManager {
() => nip44v2.decrypt(userPubkey, payload),
{
label: "nip44.v2.decrypt",
...decrypterOptions,
priority: NIP07_PRIORITY.HIGH,
},
),
);
@@ -995,7 +845,7 @@ class SubscriptionsManager {
() => nip44v2.decrypt(userPubkey, payload),
{
label: "nip44.v2.decrypt",
...decrypterOptions,
priority: NIP07_PRIORITY.HIGH,
},
),
);
@@ -1129,9 +979,7 @@ class SubscriptionsManager {
}
if (signer.type === "extension") {
const permissionResult = await requestDefaultExtensionPermissions(
DEFAULT_NIP07_ENCRYPTION_METHODS,
);
const permissionResult = await requestDefaultExtensionPermissions();
if (!permissionResult.ok) {
userLogger.warn(
"[SubscriptionsManager] Signer permissions denied while updating subscriptions.",
@@ -1240,6 +1088,13 @@ class SubscriptionsManager {
throw signErr;
}
const sanitizeRelayList = (candidate) =>
Array.isArray(candidate)
? candidate
.map((url) => (typeof url === "string" ? url.trim() : ""))
.filter(Boolean)
: [];
const writeRelays = sanitizeRelayList(nostrClient.writeRelays);
const fallbackRelays = writeRelays.length
? writeRelays