327bcd3385
Zwei Befunde aus der Integrations-Review: - Race: der Page-Drain und der Worker machen je ein read-modify-write auf 'quicksave_pending' ohne kontextuebergreifende Atomizitaet. Ein Worker-Append im await-Fenster des Drains konnte einen bereits gedrainten Eintrag in der Queue belassen, den ein Folge-Drain erneut in die Inbox schrieb (Duplikat). Jede eingespielte Bookmark traegt jetzt die Pending-id als srcId; ein erneut auftauchender Eintrag wird uebersprungen statt doppelt eingefuegt. boards-Write bleibt vor der Queue-Bereinigung -> kein Verlust. - Validierung: der Drain hat e.url ohne isSafeUrl gepusht, anders als jeder andere Bookmark-Schreibpfad. isSafeUrl (jetzt im DOM-freien quicksave-core, http/https/ftp) filtert unsichere/leere Protokolle vor dem Schreiben ins Board.
70 lines
2.9 KiB
JavaScript
70 lines
2.9 KiB
JavaScript
/* =============================================
|
|
HELLION NEWTAB — quicksave-core.js
|
|
DOM-freie geteilte Helfer fuer Seite UND Background-Worker.
|
|
Laeuft als <script> (newtab.html) und via importScripts (Service-Worker/Event-Page).
|
|
KEIN window/document/Store-Zugriff. Alle Exporte auf globalThis.
|
|
============================================= */
|
|
(function (root) {
|
|
'use strict';
|
|
|
|
// Feste, nicht-zufaellige ID des Inbox-Boards (Quick-Save-Ziel).
|
|
// Bewusst KEIN uid(): die Inbox muss von Seite und Worker deterministisch
|
|
// wiedererkennbar sein, sonst entstehen Duplikate (QS-08).
|
|
const INBOX_ID = 'inbox';
|
|
|
|
// Kollisionsarme Kurz-ID. Identisch zur frueheren state.js-Variante,
|
|
// damit bestehende Aufrufer (boards.js, data.js, app.js, bookmark-import.js) unveraendert weiterlaufen.
|
|
function uid() {
|
|
return Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
|
|
}
|
|
|
|
// Sichert, dass im uebergebenen boards-Array genau EIN Inbox-Board existiert,
|
|
// und gibt dieses Inbox-Board-Objekt zurueck. Idempotent: findet ein Board mit
|
|
// id === INBOX_ID, sonst legt es eins mit fester id vorne an (unshift) und mutiert
|
|
// das uebergebene Array dabei IN-PLACE. Der Worker schreibt anschliessend dasselbe
|
|
// (mutierte) boards-Array via storage zurueck; der Rueckgabewert ist das Board,
|
|
// damit Aufrufer direkt inbox.bookmarks.push(...) machen koennen (QS-08).
|
|
function ensureInbox(boardsArr) {
|
|
const list = Array.isArray(boardsArr) ? boardsArr : [];
|
|
let inbox = list.find(b => b && b.id === INBOX_ID);
|
|
if (!inbox) {
|
|
inbox = { id: INBOX_ID, title: 'Inbox', bookmarks: [], blurred: false };
|
|
list.unshift(inbox);
|
|
}
|
|
return inbox;
|
|
}
|
|
|
|
// Sicheres URL-Protokoll (http/https/ftp). Inhaltlich identisch zur data.js-Variante, aber
|
|
// DOM-frei und auf globalThis, damit der Quick-Save-Drain (app.js) dieselbe Validierung nutzt
|
|
// wie jeder andere Bookmark-Schreibpfad. URL ist in Worker UND Seite verfuegbar.
|
|
function isSafeUrl(url) {
|
|
try {
|
|
return ['http:', 'https:', 'ftp:'].includes(new URL(url).protocol);
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Normalisiert eine Bookmark in die kanonische Form { id, title, url, desc }.
|
|
// title-Fallback auf url, desc auf ''. Begrenzt Laengen wie data.js (200/500),
|
|
// damit Quick-Save-Eintraege das gleiche Schema wie Import/Manuell haben.
|
|
function normalizeBookmark(raw) {
|
|
const url = (raw && typeof raw.url === 'string') ? raw.url : '';
|
|
const title = (raw && typeof raw.title === 'string' && raw.title.trim())
|
|
? raw.title.trim()
|
|
: url;
|
|
return {
|
|
id: (raw && raw.id) ? raw.id : uid(),
|
|
title: String(title).slice(0, 200),
|
|
url: url,
|
|
desc: String((raw && raw.desc) || '').slice(0, 500)
|
|
};
|
|
}
|
|
|
|
root.INBOX_ID = INBOX_ID;
|
|
root.uid = uid;
|
|
root.isSafeUrl = isSafeUrl;
|
|
root.ensureInbox = ensureInbox;
|
|
root.normalizeBookmark = normalizeBookmark;
|
|
})(typeof globalThis !== 'undefined' ? globalThis : self);
|