Всичко, което потребител на Lolly натрупва, живее на неговото устройство - без акаунт, без облак. Пакетът за прехвърляне на данни е начинът, по който тази стойност се движи: изнеси я на една инсталация, пренеси файла с каквото и да е средство (USB, AirDrop, имейл до себе си, споделена мрежова папка) и внеси я на друга. Файлът е транспортът. Целта може да е офлайн или онлайн. Няма значение, защото нищо никога не комуникира със сървър.
signed by Lollyvector SVGПровери самGet the signed file12 paths~5.1k nodes12 groups60 KB
signed by Lollyvector SVGПровери самGet the signed file12 paths~5.1k nodes12 groups60 KB
Тази страница е спецификацията на формата. За описание за крайния потребител виж Using Lolly → Moving to another device. Реализацията е в shells/web/src/data-transfer.ts, а tests/data-transfer.test.ts фиксира договора за пренасяне на данните напред-назад.
Обхват. Пакетът носи потребителски данни, не инструменти. Инструментите и активите от каталога се синхронизират отделно и се приема, че вече присъстват на целта (в най-лошия случай в по-нова версия). Внасянето никога не инсталира или обновява инструмент.
Цели
- Един формат, всяка обвивка. Едни и същи байтове се произвеждат и консумират от уеб PWA, приложенията на Tauri за desktop/mobile и всяка бъдеща обвивка. Пакетът е договорът. Мостът на възможностите на всяка обвивка е адаптерът за конкретната платформа зад него.
- Оцелява пътуването. Пакет, повреден или орязан по време на пренос, се проваля шумно при внасяне, никога не възстановява наполовина.
- Надживява тази версия. По-старо приложение все още може да внесе разпознатите части на по-нов пакет. Действително прекъсваща промяна на формата се отказва чисто.
- Безопасно за сливане. Внасянето върху вече използвана инсталация никога не изтрива нещо, което не е било в пакета.
Обвивката
Пакетът е обикновен .zip. Изтегленият файл е именуван на човека, на когото принадлежи - LollyTools-<First>-<Last>-<YYYY-MM-DD>-<n>.zip (например LollyTools-Ada-Lovelace-2026-06-26-1.zip) - така че папката с изтегляния, съдържаща резервни копия, остава четлива. Частите за собствено и фамилно име идват от профила и се пропускат, ако не са зададени. Липсващ профил дава LollyTools-2026-06-26-1.zip, а само собствено име дава LollyTools-Ada-2026-06-26-1.zip. Всяка част се пречиства до безопасен за файлово име токен (запазват се Unicode букви/цифри, премахват се интервали/пунктуация, ограничение от 32 знака). <n> е последователност за ден и устройство, така че повторни експорти в един и същи ден не се сблъскват и остават подредени. backupFilename() в shells/web/src/data-transfer.ts изгражда името. Съдържанието на zip файла е еднакво независимо от името. Вътре:
| Път | Задължителен | Съдържание |
|---|---|---|
manifest.json | да | Id на формата, версии, брой елементи и цялостност по части. Първото нещо, което чете четецът. |
profile.json | ако е зададен | Записът me на потребителя (име, контакт, връзка към снимка, флагове). Чете се чрез host.profile. |
sessions.json | да | Всяка запазена сесия: слот, id/версия на инструмента, етикет, миниатюра (data-URL) и пълни входни данни. Чете се чрез host.state. |
assets.json | да | Метаданни за всеки качен актив (изображения, шрифтове, брандови токени), всяко сочещо към своите байтове под assets/blobs/. |
assets/blobs/<n>.<ext> | по актив | Суровите байтове на актива (файлове с изображения и шрифтове). Съхраняват се некомпресирани (вече компресирани формати). Разширението е козметично. MIME типът в assets.json е меродавен. |
prefs.json | да | Локални предпочитания, притежавани от потребителя: theme, sidebarWidth и брояча на активност ct-metrics. |
lolly.txt | да | Четимо от човек резюме на пакета (брой елементи, профил, име на файл) за всеки, който отваря zip-а без Lolly. Прегенерира се при всеки експорт и се разпознава при внасяне, така че никога не се брои за пропусната част. Записва се след картата на цялостността, така че остава извън нея. |
Пакетът е обикновен zip нарочно: оцелява всеки транспорт непокътнат, а всеки инструмент за разархивиране може да го инспектира.
profile.json е най-малката част и тази, която потребителят вижда пръв в приложението: данните, попълнени веднъж от него, плюс опцията, която позволява на инструментите да ги ползват.
signed by Lollyvector SVGПровери самGet the signed file18 paths~2.0k nodes41 groups30 KB
signed by Lollyvector SVGПровери самGet the signed file18 paths~2.0k nodes41 groups30 KB
manifest.json
{
"format": "lolly-backup",
"formatVersion": 1,
"minReader": 1,
"app": "lolly",
"exportedAt": "2026-06-22T09:30:00.000Z",
"counts": { "profile": true, "sessions": 2, "userAssets": 4, "prefs": 3 },
"integrity": {
"profile.json": "sha256-…",
"sessions.json": "sha256-…",
"assets.json": "sha256-…",
"assets/blobs/0.webp": "sha256-…",
"prefs.json": "sha256-…"
}
}
| Поле | Значение |
|---|---|
format | Винаги lolly-backup. Файл без него се отхвърля като „не е резервно копие на Lolly“. |
formatVersion | Оформлението, с което е записан този пакет. Увеличава се при всяка промяна на набора или формата на частите. Четците не се базират на него. |
minReader | Минималната версия на четеца, необходима за безопасно внасяне на този пакет. Това е полето, на което се базират четците. |
app | Id на произвеждащото приложение, за диагностика. |
exportedAt | ISO времеви печат на създаването на пакета. |
counts | Какво е сложил писателят, за показване и проверка за здравина. |
integrity | По избор. Свързва всяка част освен manifest.json с дайджест във формат SRI - sha256-<base64> - на нейните некомпресирани байтове. |
Политика за версии (обратна съвместимост напред)
Разделянето между formatVersion и minReader е това, което позволява на формата да расте, без да изолира по-стари инсталации:
- Четец внася пакет, когато
manifest.minReader ≤собствената му версия на четеца. Отказва (с „нуждае се от по-нова версия на приложението“) само когато пакетът изрично изисква по-нов четец. - Добавяща промяна - нова по избор част или ново по избор поле в манифеста - увеличава
formatVersion, но оставяminReaderнепроменен. По-старите приложения продължават да внасят всяка част, която разпознават. Части, които не разпознават, се пропускат (виж по-долу), а не се изхвърлят мълчаливо. - Прекъсваща промяна - такава, при която грешно внасяне на част поврежда данни, или при която досега по избор част става задължителна - вдига
minReader. По-старите приложения тогава отказват чисто, вместо да внасят нещо, което не могат да обработят. - Ако бъдещ пакет зададе
formatVersion, но пропуснеminReader, четците консервативно се връщат към базиране наformatVersion(третират промяната като прекъсваща).
Правило на палеца за автори: ако всеки съществуващ четец все пак ще постъпи правилно, като игнорира добавката ти, тя е добавяща - увеличи
formatVersion, оставиminReader. В противен случай вдигниminReader.
Цялостност
Когато manifest.integrity присъства, четец проверява SHA-256 на всяка изброена част преди да запише каквото и да е. Несъответствие („не премина проверката за цялостност“) или липсваща част („непълен“) прекратява цялото внасяне - няма частично възстановяване. Това улавя повредата, която транспортът на файл може да внесе (орязан AirDrop, имейл шлюз, който е прекодирал прикачения файл, лош USB сектор).
Цялостността е по замисъл best-effort: записва се само там, където Web Crypto е налична (всеки сигурен контекст на браузър и съвременен Node), и се проверява само когато и картата, и Web Crypto присъстват. Пакет без картата - например такъв отпреди появата на цялостността - се внася непроменен. „Не може да се провери“ никога не се третира като „повреден“.
Манифестът не изброява нито себе си, нито прегенерирания README lolly.txt. Дайджестите покриват частите, за които манифестът гарантира.
Семантика на внасянето
Внасянето е сливане с презаписване, никога пълна замяна:
- Съществуващите данни на целта остават на място.
- Всеки ключ, който се сблъска - профилът, слот на сесия, id на качено изображение - се заменя с внесеното копие.
- Нищо, което не е било в пакета, не се докосва. Сесия, която целта е имала, но пакетът - не, оцелява при внасянето.
Запазените сесии се повторно свързват към своите изображения автоматично: препратките към активи се пазят по id, а мостът ги преразрешава наново, след като качените изображения бъдат възстановени (задължително, тъй като URL адресите blob: не оцеляват при презареждане).
Резюмето на внасянето отчита { profile, sessions, userAssets, prefs, skipped, failedAssets }. failedAssets брои качени активи, които не са могли да бъдат възстановени (например пълно устройство за съхранение). Различно е от skipped, което брои части от по-нов писател с обратна съвместимост, които тази версия не е разпознала. Интерфейсът показва skipped („… · N по-нови елемента пропуснати“), така че възстановяването е честно относно това, което е оставило зад себе си.
Какво не пътува
- Кешове на каталога (изтеглени метаданни и blobs на активи, индексът на инструментите) - синхронизират се безплатно наново на целта.
- Инструменти и активи на бранда - извън обхвата, и се приема, че вече присъстват на целта.
- URL адреси
blob:/ object URL - прегенерирани от моста при зареждане. - Броячът на последователността за експорт - брояч за именуване на изтеглянията по ден (ключ на
localStoragelolly-export-seq) е локално удобство за именуване. Държи се извънPREF_KEYS, така че никога не пътува в пакет.
Индикаторът за съхранение изброява същото разделение. Запазените сесии и My images пътуват в пакет. Кешът на активите, преглед на инструментите и офлайн закачените под тях всички са възстановими наново, така че остават извън него.
signed by Lollyvector SVGПровери самGet the signed file25 paths~4.8k nodes46 groups59 KB
signed by Lollyvector SVGПровери самGet the signed file25 paths~4.8k nodes46 groups59 KB
Гаранция между обвивки
data-transfer.ts чете и записва изключително през capability bridge (host.profile, host.state, host.assets) и споделените localStorage предпочитания. Тъй като bridge-ът е единствената точка на допир, един и същ модул произвежда байт-идентичен пакет на всяка обвивка, дори когато съхранението отдолу е различно - IndexedDB в уеб, файловата система в Tauri. Обвивките на Tauri преизползват този модул непроменен. Различава се само тяхната реализация на host.state. Headless тестът упражнява пълния round-trip срещу in-memory bridge, затова той представя всички тях.
Две обвивки остават извън тази гаранция, по различни причини:
- Еднократният CLI няма какво да пренася - състоянието му е in-memory и ефимерно за всяко извикване.
- TUI наистина запазва състояние (
~/.lolly: сесии, папки, профил) и неговият Profile изглед може да го архивира, но записва по-прост собствен архив:sessions/<slot>.jsonза всяка сесия плюсprofile.jsonиfolders.json, без манифест, безformatVersion/minReaderи без карта за цялост. Той не е импортируем в този формат - четецът го отхвърля като „not a Lolly backup“ - и объркващо използва подобно име (lolly-backup-<stamp>.zip). Обединяването на двата е известен пропуск.
Запазени точки за разширение
Обвивката е манифест плюс набор от именувани части по замисъл, така че по-нови видове преносими данни да могат да я използват по-късно без пробиваща промяна. Те се вграждат като допълнителни части (нов formatVersion, същия minReader), а днешният четец пропуска това, което не разпознава. Това са неща от пътната карта, още не са реализирани. Имената са запазени тук, за да остане форматът съгласуван, когато бъдат добавени.
tokens.json- дизайн токъни. Документ с дизайн токъни по W3C DTCG (форматът, който Penpot импортира и експортира - токъни с$value/$type/$description, организирани в групи, набори и теми). Набор от токъни в пакета позволява на потребителя да пренася своите брандови примитиви между инсталации заедно със сесиите си. В по-дългосрочен план внесен набор от токъни ще стане пълноправен източник, спрямо който инструментите и палитрите се разрешават.penpot/- внесени Penpot файлове. Запазена директория за Penpot файл (или извлечено от него подмножество, релевантно за Lolly), внесен и показан като инструмент. Пакетът ще носи внесената дефиниция, така че тя пътува заедно с останалите данни на потребителя.
Всичко извън тези запазени имена и частите по-горе е за четеца непозната част: оставя се недокоснато и се брои в skipped.
Справочник
- Модул:
shells/web/src/data-transfer.ts(exportBackup,importBackup,BACKUP_FORMAT,BACKUP_FORMAT_VERSION,BACKUP_READER_VERSION- именованетоbackupFilename()е вътрешно). - Контрактен тест:
tests/data-transfer.test.ts- случаи на round-trip, сливане, цялост, съвместимост напред и заграждане на четеца. - Използвана повърхност на bridge:
host.profile,host.state,host.assets- вж. Host API.