Merge pull request #225 from cuckium/main

Use live Workers that actively fetch APIs in favour of static instances.json
This commit is contained in:
Eduard Prigoana 2026-02-21 16:59:52 +02:00 committed by GitHub
commit b05166efa5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 314 additions and 178 deletions

View file

@ -105,7 +105,7 @@ monochrome/
├── 📁 public/ # Static assets ├── 📁 public/ # Static assets
│ ├── assets/ # Images, icons, fonts │ ├── assets/ # Images, icons, fonts
│ ├── manifest.json # PWA manifest │ ├── manifest.json # PWA manifest
│ └── instances.json # API instances configuration │ └── instances.json # API instances configuration (deprecated)
├── 📄 index.html # Application entry point ├── 📄 index.html # Application entry point
├── 📄 vite.config.js # Build and PWA configuration ├── 📄 vite.config.js # Build and PWA configuration
├── 📄 package.json # Dependencies and scripts ├── 📄 package.json # Dependencies and scripts

View file

@ -31,7 +31,12 @@ These instances provide the tidal-ui web interface, not monochrome:
## API Instances ## API Instances
Monochrome uses the Hi-Fi API under the hood. These are available API endpoints that can be used with Monochrome or other Hi-Fi based applications: Monochrome uses the Hi-Fi API under the hood. Live, up-to-date status trackers (which return JSON) can be found below:
- https://tidal-uptime.jiffy-puffs-1j.workers.dev/
- https://tidal-uptime.props-76styles.workers.dev/
These are available API endpoints that can be used with Monochrome or other Hi-Fi based applications:
### Official & Community APIs ### Official & Community APIs

View file

@ -2,27 +2,52 @@
class ServerAPI { class ServerAPI {
constructor() { constructor() {
this.INSTANCES_URL = 'https://raw.githubusercontent.com/Monochrome-music/monochrome/main/instances.json'; this.INSTANCES_URLS = [
'https://tidal-uptime.jiffy-puffs-1j.workers.dev/',
'https://tidal-uptime.props-76styles.workers.dev/',
];
this.apiInstances = null; this.apiInstances = null;
} }
async getInstances() { async getInstances() {
if (this.apiInstances) return this.apiInstances; if (this.apiInstances) return this.apiInstances;
try {
const response = await fetch(this.INSTANCES_URL); let data = null;
if (!response.ok) throw new Error('Failed to fetch instances'); const urls = [...this.INSTANCES_URLS].sort(() => Math.random() - 0.5);
const data = await response.json();
this.apiInstances = data.api || []; for (const url of urls) {
return this.apiInstances; try {
} catch (error) { const response = await fetch(url);
console.error('Failed to load instances:', error); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return [ data = await response.json();
'https://triton.squid.wtf', break;
'https://wolf.qqdl.site', } catch (error) {
'https://tidal-api.binimum.org', console.warn(`Failed to fetch from ${url}:`, error);
'https://monochrome-api.samidy.com', }
];
} }
if (data) {
this.apiInstances = (data.api || [])
.map((item) => item.url || item)
.filter((url) => !url.includes('spotisaver.net'));
return this.apiInstances;
}
console.error('Failed to load instances from all uptime APIs');
return [
'https://eu-central.monochrome.tf',
'https://us-west.monochrome.tf',
'https://arran.monochrome.tf',
'https://triton.squid.wtf',
'https://api.monochrome.tf',
'https://monochrome-api.samidy.com',
'https://maus.qqdl.site',
'https://vogel.qqdl.site',
'https://katze.qqdl.site',
'https://hund.qqdl.site',
'https://tidal.kinoplus.online',
'https://wolf.qqdl.site',
];
} }
async fetchWithRetry(relativePath) { async fetchWithRetry(relativePath) {

View file

@ -2,27 +2,52 @@
class ServerAPI { class ServerAPI {
constructor() { constructor() {
this.INSTANCES_URL = 'https://raw.githubusercontent.com/Monochrome-music/monochrome/main/public/instances.json'; this.INSTANCES_URLS = [
'https://tidal-uptime.jiffy-puffs-1j.workers.dev/',
'https://tidal-uptime.props-76styles.workers.dev/',
];
this.apiInstances = null; this.apiInstances = null;
} }
async getInstances() { async getInstances() {
if (this.apiInstances) return this.apiInstances; if (this.apiInstances) return this.apiInstances;
try {
const response = await fetch(this.INSTANCES_URL); let data = null;
if (!response.ok) throw new Error('Failed to fetch instances'); const urls = [...this.INSTANCES_URLS].sort(() => Math.random() - 0.5);
const data = await response.json();
this.apiInstances = data.api || []; for (const url of urls) {
return this.apiInstances; try {
} catch (error) { const response = await fetch(url);
console.error('Failed to load instances:', error); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return [ data = await response.json();
'https://triton.squid.wtf', break;
'https://wolf.qqdl.site', } catch (error) {
'https://tidal-api.binimum.org', console.warn(`Failed to fetch from ${url}:`, error);
'https://monochrome-api.samidy.com', }
];
} }
if (data) {
this.apiInstances = (data.api || [])
.map((item) => item.url || item)
.filter((url) => !url.includes('spotisaver.net'));
return this.apiInstances;
}
console.error('Failed to load instances from all uptime APIs');
return [
'https://eu-central.monochrome.tf',
'https://us-west.monochrome.tf',
'https://arran.monochrome.tf',
'https://triton.squid.wtf',
'https://api.monochrome.tf',
'https://monochrome-api.samidy.com',
'https://maus.qqdl.site',
'https://vogel.qqdl.site',
'https://katze.qqdl.site',
'https://hund.qqdl.site',
'https://tidal.kinoplus.online',
'https://wolf.qqdl.site',
];
} }
async fetchWithRetry(relativePath) { async fetchWithRetry(relativePath) {

View file

@ -2,27 +2,52 @@
class ServerAPI { class ServerAPI {
constructor() { constructor() {
this.INSTANCES_URL = 'https://raw.githubusercontent.com/Monochrome-music/monochrome/main/public/instances.json'; this.INSTANCES_URLS = [
'https://tidal-uptime.jiffy-puffs-1j.workers.dev/',
'https://tidal-uptime.props-76styles.workers.dev/',
];
this.apiInstances = null; this.apiInstances = null;
} }
async getInstances() { async getInstances() {
if (this.apiInstances) return this.apiInstances; if (this.apiInstances) return this.apiInstances;
try {
const response = await fetch(this.INSTANCES_URL); let data = null;
if (!response.ok) throw new Error('Failed to fetch instances'); const urls = [...this.INSTANCES_URLS].sort(() => Math.random() - 0.5);
const data = await response.json();
this.apiInstances = data.api || []; for (const url of urls) {
return this.apiInstances; try {
} catch (error) { const response = await fetch(url);
console.error('Failed to load instances:', error); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return [ data = await response.json();
'https://triton.squid.wtf', break;
'https://wolf.qqdl.site', } catch (error) {
'https://tidal-api.binimum.org', console.warn(`Failed to fetch from ${url}:`, error);
'https://monochrome-api.samidy.com', }
];
} }
if (data) {
this.apiInstances = (data.api || [])
.map((item) => item.url || item)
.filter((url) => !url.includes('spotisaver.net'));
return this.apiInstances;
}
console.error('Failed to load instances from all uptime APIs');
return [
'https://eu-central.monochrome.tf',
'https://us-west.monochrome.tf',
'https://arran.monochrome.tf',
'https://triton.squid.wtf',
'https://api.monochrome.tf',
'https://monochrome-api.samidy.com',
'https://maus.qqdl.site',
'https://vogel.qqdl.site',
'https://katze.qqdl.site',
'https://hund.qqdl.site',
'https://tidal.kinoplus.online',
'https://wolf.qqdl.site',
];
} }
async fetchWithRetry(relativePath) { async fetchWithRetry(relativePath) {

View file

@ -14,27 +14,52 @@ function getTrackArtists(track = {}, { fallback = 'Unknown Artist' } = {}) {
class ServerAPI { class ServerAPI {
constructor() { constructor() {
this.INSTANCES_URL = 'https://raw.githubusercontent.com/Monochrome-music/monochrome/main/public/instances.json'; this.INSTANCES_URLS = [
'https://tidal-uptime.jiffy-puffs-1j.workers.dev/',
'https://tidal-uptime.props-76styles.workers.dev/',
];
this.apiInstances = null; this.apiInstances = null;
} }
async getInstances() { async getInstances() {
if (this.apiInstances) return this.apiInstances; if (this.apiInstances) return this.apiInstances;
try {
const response = await fetch(this.INSTANCES_URL); let data = null;
if (!response.ok) throw new Error('Failed to fetch instances'); const urls = [...this.INSTANCES_URLS].sort(() => Math.random() - 0.5);
const data = await response.json();
this.apiInstances = data.api || []; for (const url of urls) {
return this.apiInstances; try {
} catch (error) { const response = await fetch(url);
console.error('Failed to load instances from GitHub:', error); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return [ data = await response.json();
'https://triton.squid.wtf', break;
'https://wolf.qqdl.site', } catch (error) {
'https://tidal-api.binimum.org', console.warn(`Failed to fetch from ${url}:`, error);
'https://monochrome-api.samidy.com', }
];
} }
if (data) {
this.apiInstances = (data.api || [])
.map((item) => item.url || item)
.filter((url) => !url.includes('spotisaver.net'));
return this.apiInstances;
}
console.error('Failed to load instances from all uptime APIs');
return [
'https://eu-central.monochrome.tf',
'https://us-west.monochrome.tf',
'https://arran.monochrome.tf',
'https://triton.squid.wtf',
'https://api.monochrome.tf',
'https://monochrome-api.samidy.com',
'https://maus.qqdl.site',
'https://vogel.qqdl.site',
'https://katze.qqdl.site',
'https://hund.qqdl.site',
'https://tidal.kinoplus.online',
'https://wolf.qqdl.site',
];
} }
async fetchWithRetry(relativePath) { async fetchWithRetry(relativePath) {

View file

@ -106,19 +106,7 @@ export function trackToggleMute(muted) {
trackEvent('Toggle Mute', { muted }); trackEvent('Toggle Mute', { muted });
} }
export function trackSeek(position, duration) { // Track listening progress milestones (10%, 50%, 90%, 100%)
const progress = duration ? Math.round((position / duration) * 100) : 0;
// Track seek at 25%, 50%, 75% milestones
if (progress >= 25 && progress < 30) {
trackEvent('Seek', { milestone: '25%', position });
} else if (progress >= 50 && progress < 55) {
trackEvent('Seek', { milestone: '50%', position });
} else if (progress >= 75 && progress < 80) {
trackEvent('Seek', { milestone: '75%', position });
}
}
// Track listening progress milestones (10%, 25%, 50%, 75%, 90%, 100%)
export function trackListeningProgress(track, percent) { export function trackListeningProgress(track, percent) {
trackEvent('Listening Progress', { trackEvent('Listening Progress', {
track_id: track?.id || 'unknown', track_id: track?.id || 'unknown',

View file

@ -42,17 +42,28 @@ export class LosslessAPI {
async fetchWithRetry(relativePath, options = {}) { async fetchWithRetry(relativePath, options = {}) {
const type = options.type || 'api'; const type = options.type || 'api';
const instances = await this.settings.getInstances(type); let instances = await this.settings.getInstances(type);
if (instances.length === 0) { if (instances.length === 0) {
throw new Error(`No API instances configured for type: ${type}`); throw new Error(`No API instances configured for type: ${type}`);
} }
if (options.minVersion) {
instances = instances.filter((instance) => {
if (!instance.version) return false;
return parseFloat(instance.version) >= parseFloat(options.minVersion);
});
if (instances.length === 0) {
throw new Error(`No API instances configured for type: ${type} with minVersion: ${options.minVersion}`);
}
}
const maxTotalAttempts = instances.length * 2; // Allow some retries across instances const maxTotalAttempts = instances.length * 2; // Allow some retries across instances
let lastError = null; let lastError = null;
let instanceIndex = Math.floor(Math.random() * instances.length); let instanceIndex = Math.floor(Math.random() * instances.length);
for (let attempt = 1; attempt <= maxTotalAttempts; attempt++) { for (let attempt = 1; attempt <= maxTotalAttempts; attempt++) {
const baseUrl = instances[instanceIndex % instances.length]; const instance = instances[instanceIndex % instances.length];
const baseUrl = typeof instance === 'string' ? instance : instance.url;
const url = baseUrl.endsWith('/') ? `${baseUrl}${relativePath.substring(1)}` : `${baseUrl}${relativePath}`; const url = baseUrl.endsWith('/') ? `${baseUrl}${relativePath.substring(1)}` : `${baseUrl}${relativePath}`;
try { try {
@ -644,7 +655,7 @@ export class LosslessAPI {
const cached = await this.cache.get('mix', id); const cached = await this.cache.get('mix', id);
if (cached) return cached; if (cached) return cached;
const response = await this.fetchWithRetry(`/mix/?id=${id}`, { type: 'api' }); const response = await this.fetchWithRetry(`/mix/?id=${id}`, { type: 'api', minVersion: '2.3' });
const data = await response.json(); const data = await response.json();
const mixData = data.mix; const mixData = data.mix;
@ -778,7 +789,10 @@ export class LosslessAPI {
if (cached) return cached; if (cached) return cached;
try { try {
const response = await this.fetchWithRetry(`/artist/similar/?id=${artistId}`, { type: 'api' }); const response = await this.fetchWithRetry(`/artist/similar/?id=${artistId}`, {
type: 'api',
minVersion: '2.3',
});
const data = await response.json(); const data = await response.json();
// Handle various response structures // Handle various response structures
@ -829,7 +843,10 @@ export class LosslessAPI {
if (cached) return cached; if (cached) return cached;
try { try {
const response = await this.fetchWithRetry(`/album/similar/?id=${albumId}`, { type: 'api' }); const response = await this.fetchWithRetry(`/album/similar/?id=${albumId}`, {
type: 'api',
minVersion: '2.3',
});
const data = await response.json(); const data = await response.json();
const items = data.items || data.albums || data.data || (Array.isArray(data) ? data : []); const items = data.items || data.albums || data.data || (Array.isArray(data) ? data : []);
@ -976,7 +993,10 @@ export class LosslessAPI {
if (cached) return cached; if (cached) return cached;
try { try {
const response = await this.fetchWithRetry(`/recommendations/?id=${id}`, { type: 'api' }); const response = await this.fetchWithRetry(`/recommendations/?id=${id}`, {
type: 'api',
minVersion: '2.4',
});
const json = await response.json(); const json = await response.json();
const data = json.data || json; const data = json.data || json;

View file

@ -27,7 +27,6 @@ import {
trackSkipTrack, trackSkipTrack,
trackToggleShuffle, trackToggleShuffle,
trackToggleRepeat, trackToggleRepeat,
trackSeek,
trackAddToQueue, trackAddToQueue,
trackPlayNext, trackPlayNext,
trackLikeTrack, trackLikeTrack,
@ -137,9 +136,6 @@ export function initializePlayerEvents(player, audioPlayer, scrobbler, ui) {
progressFill.style.width = `${(currentTime / duration) * 100}%`; progressFill.style.width = `${(currentTime / duration) * 100}%`;
currentTimeEl.textContent = formatTime(currentTime); currentTimeEl.textContent = formatTime(currentTime);
// Track seek milestones
trackSeek(currentTime, duration);
// Log to history after 10 seconds of playback // Log to history after 10 seconds of playback
if (currentTime >= 10 && player.currentTrack && player.currentTrack.id !== historyLoggedTrackId) { if (currentTime >= 10 && player.currentTrack && player.currentTrack.id !== historyLoggedTrackId) {
historyLoggedTrackId = player.currentTrack.id; historyLoggedTrackId = player.currentTrack.id;
@ -1668,7 +1664,6 @@ export function initializeTrackInteractions(player, api, mainContent, contextMen
api.getTrackRecommendations(clickedTrack.id).then((recs) => { api.getTrackRecommendations(clickedTrack.id).then((recs) => {
if (recs && recs.length > 0) { if (recs && recs.length > 0) {
player.addToQueue(recs); player.addToQueue(recs);
showNotification(`Added ${recs.length} recommendations to queue`);
} }
}); });
} }

View file

@ -1,120 +1,133 @@
//storage.js //storage.js
export const apiSettings = { export const apiSettings = {
STORAGE_KEY: 'monochrome-api-instances-v8', STORAGE_KEY: 'monochrome-api-instances-v9',
INSTANCES_URL: 'instances.json', INSTANCES_URLS: [
'https://tidal-uptime.jiffy-puffs-1j.workers.dev/',
'https://tidal-uptime.props-76styles.workers.dev/',
],
defaultInstances: { api: [], streaming: [] }, defaultInstances: { api: [], streaming: [] },
instancesLoaded: false, instancesLoaded: false,
_loadPromise: null,
async loadInstancesFromGitHub() { async loadInstancesFromGitHub() {
if (this.instancesLoaded) { if (this.instancesLoaded) {
return this.defaultInstances; return this.defaultInstances;
} }
try { if (this._loadPromise) {
const response = await fetch(this.INSTANCES_URL); return this._loadPromise;
if (!response.ok) throw new Error('Failed to fetch instances'); }
const data = await response.json(); this._loadPromise = (async () => {
const cachedData = localStorage.getItem(this.STORAGE_KEY);
if (cachedData) {
try {
const parsed = JSON.parse(cachedData);
const now = Date.now();
// Check if cached data is less than 15 minutes old
if (parsed.timestamp && now - parsed.timestamp < 15 * 60 * 1000) {
this.defaultInstances = parsed.data;
this.instancesLoaded = true;
this._loadPromise = null;
return this.defaultInstances;
}
} catch (e) {
console.warn('Failed to parse cached instances:', e);
}
}
let data = null;
let fetchError = null;
// Shuffle URLs to pick a random one first
const urls = [...this.INSTANCES_URLS].sort(() => Math.random() - 0.5);
for (const url of urls) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
data = await response.json();
break; // Success, exit loop
} catch (error) {
console.warn(`Failed to fetch from ${url}:`, error);
fetchError = error;
}
}
if (!data) {
console.error('Failed to load instances from all uptime APIs:', fetchError);
this.defaultInstances = {
api: [
{ url: 'https://eu-central.monochrome.tf', version: '2.4' },
{ url: 'https://us-west.monochrome.tf', version: '2.4' },
{ url: 'https://arran.monochrome.tf', version: '2.4' },
{ url: 'https://triton.squid.wtf', version: '2.4' },
{ url: 'https://api.monochrome.tf', version: '2.3' },
{ url: 'https://monochrome-api.samidy.com', version: '2.3' },
{ url: 'https://maus.qqdl.site', version: '2.2' },
{ url: 'https://vogel.qqdl.site', version: '2.2' },
{ url: 'https://katze.qqdl.site', version: '2.2' },
{ url: 'https://hund.qqdl.site', version: '2.2' },
{ url: 'https://tidal.kinoplus.online', version: '2.2' },
{ url: 'https://wolf.qqdl.site', version: '2.2' },
],
streaming: [
{ url: 'https://arran.monochrome.tf', version: '2.4' },
{ url: 'https://triton.squid.wtf', version: '2.4' },
{ url: 'https://api.monochrome.tf', version: '2.3' },
{ url: 'https://monochrome-api.samidy.com', version: '2.3' },
{ url: 'https://maus.qqdl.site', version: '2.2' },
{ url: 'https://vogel.qqdl.site', version: '2.2' },
{ url: 'https://katze.qqdl.site', version: '2.2' },
{ url: 'https://hund.qqdl.site', version: '2.2' },
{ url: 'https://wolf.qqdl.site', version: '2.2' },
],
};
this.instancesLoaded = true;
this._loadPromise = null;
return this.defaultInstances;
}
let groupedInstances = { api: [], streaming: [] }; let groupedInstances = { api: [], streaming: [] };
if (Array.isArray(data)) { if (data.api && Array.isArray(data.api)) {
// Legacy array format groupedInstances.api = data.api.filter((instance) => !instance.url.includes('spotisaver.net'));
groupedInstances.api = [...data]; }
groupedInstances.streaming = [...data];
} else {
// New object format or legacy object format
if (data.api && Array.isArray(data.api)) {
const isSimpleArray = data.api.length > 0 && typeof data.api[0] === 'string';
if (isSimpleArray) {
groupedInstances.api = [...data.api];
} else {
for (const [, config] of Object.entries(data.api)) {
if (config.cors === false && Array.isArray(config.urls)) {
groupedInstances.api.push(...config.urls);
}
}
}
}
if (data.streaming && Array.isArray(data.streaming)) { if (data.streaming && Array.isArray(data.streaming)) {
groupedInstances.streaming = [...data.streaming]; groupedInstances.streaming = data.streaming.filter(
} else if (groupedInstances.api.length > 0) { (instance) => !instance.url.includes('spotisaver.net')
groupedInstances.streaming = [...groupedInstances.api]; );
} } else if (groupedInstances.api.length > 0) {
groupedInstances.streaming = [...groupedInstances.api];
} }
this.defaultInstances = groupedInstances; this.defaultInstances = groupedInstances;
this.instancesLoaded = true; this.instancesLoaded = true;
try {
localStorage.setItem(
this.STORAGE_KEY,
JSON.stringify({
timestamp: Date.now(),
data: groupedInstances,
})
);
} catch (e) {
console.warn('Failed to cache instances:', e);
}
this._loadPromise = null;
return groupedInstances; return groupedInstances;
} catch (error) { })();
console.error('Failed to load instances from GitHub:', error);
this.defaultInstances = { return this._loadPromise;
api: [
'https://eu-central.monochrome.tf',
'https://us-west.monochrome.tf',
'https://arran.monochrome.tf',
'https://api.monochrome.tf',
'https://triton.squid.wtf',
'https://wolf.qqdl.site',
'https://monochrome-api.samidy.com',
'https://maus.qqdl.site',
'https://tidal.kinoplus.online',
'https://hund.qqdl.site',
'https://vogel.qqdl.site',
],
streaming: [
'https://arran.monochrome.tf',
'https://triton.squid.wtf',
'https://wolf.qqdl.site',
'https://maus.qqdl.site',
'https://vogel.qqdl.site',
'https://katze.qqdl.site',
'https://hund.qqdl.site',
],
};
this.instancesLoaded = true;
return this.defaultInstances;
}
}, },
async getInstances(type = 'api', _sortBySpeed = false) { async getInstances(type = 'api', _sortBySpeed = false) {
let instancesObj; let instancesObj;
const stored = localStorage.getItem(this.STORAGE_KEY); instancesObj = await this.loadInstancesFromGitHub();
if (stored) {
instancesObj = JSON.parse(stored);
// love it when local storage doesnt update
if (instancesObj?.api?.length === 2) {
const hasBinimum = instancesObj.api.some((url) => {
try {
const urlObj = new URL(url);
return urlObj.hostname === 'tidal-api.binimum.org';
} catch {
return false;
}
});
const hasSamidy = instancesObj.api.some((url) => {
try {
const urlObj = new URL(url);
return urlObj.hostname === 'monochrome-api.samidy.com';
} catch {
return false;
}
});
if (hasBinimum && hasSamidy) {
localStorage.removeItem(this.STORAGE_KEY);
instancesObj = null;
}
}
}
if (!instancesObj) {
instancesObj = await this.loadInstancesFromGitHub();
}
const targetUrls = instancesObj[type] || instancesObj.api || []; const targetUrls = instancesObj[type] || instancesObj.api || [];
if (targetUrls.length === 0) return []; if (targetUrls.length === 0) return [];

15
package-lock.json generated
View file

@ -81,6 +81,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5", "@babel/generator": "^7.28.5",
@ -1609,6 +1610,7 @@
"integrity": "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==", "integrity": "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@keyv/serialize": "^1.1.1" "@keyv/serialize": "^1.1.1"
} }
@ -1650,6 +1652,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
@ -1693,6 +1696,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@ -3270,6 +3274,7 @@
"resolved": "https://registry.npmjs.org/@svta/cml-xml/-/cml-xml-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@svta/cml-xml/-/cml-xml-1.0.1.tgz",
"integrity": "sha512-11LkJa5kDEcsRMWkVI1ABH3KLCxGoiSVe4kQ293ItVj8ncTTQ7htmCGiJDjS+Cmy35UgF3e/vc0ysJIiWRTx2g==", "integrity": "sha512-11LkJa5kDEcsRMWkVI1ABH3KLCxGoiSVe4kQ293ItVj8ncTTQ7htmCGiJDjS+Cmy35UgF3e/vc0ysJIiWRTx2g==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"engines": { "engines": {
"node": ">=20" "node": ">=20"
}, },
@ -3318,6 +3323,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@ -3341,6 +3347,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",
@ -3638,6 +3645,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.9.0", "baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759", "caniuse-lite": "^1.0.30001759",
@ -4675,6 +4683,7 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@ -7296,6 +7305,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.11",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
@ -7379,6 +7389,7 @@
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"cssesc": "^3.0.0", "cssesc": "^3.0.0",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"
@ -8484,6 +8495,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-parser-algorithms": "^3.0.5",
"@csstools/css-syntax-patches-for-csstree": "^1.0.19", "@csstools/css-syntax-patches-for-csstree": "^1.0.19",
@ -8934,6 +8946,7 @@
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"peer": true,
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0", "acorn": "^8.15.0",
@ -9308,6 +9321,7 @@
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.27.0", "esbuild": "^0.27.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",
@ -9744,6 +9758,7 @@
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
}, },

View file

@ -62,7 +62,7 @@ export default defineConfig(({ mode }) => {
}, },
], ],
}, },
includeAssets: ['instances.json', 'discord.html'], includeAssets: ['discord.html'],
manifest: false, // Use existing public/manifest.json manifest: false, // Use existing public/manifest.json
}), }),
], ],