একজন Lolly ব্যবহারকারী যা কিছু সঞ্চয় করেন তা তাদের ডিভাইসেই থাকে - কোনো অ্যাকাউন্ট নেই, কোনো ক্লাউড নেই। data-transfer বান্ডেল হলো সেই মূল্য স্থানান্তরের উপায়: এটি একটি ইনস্টলে এক্সপোর্ট করুন, যেকোনো মাধ্যমে ফাইলটি বহন করুন (USB, AirDrop, ইমেইল-টু-সেলফ, একটি নেটওয়ার্ক শেয়ার) এবং অন্যটিতে ইমপোর্ট করুন। ফাইলটিই পরিবহন। টার্গেট অফলাইন বা অনলাইন হতে পারে। এতে কোনো পার্থক্য হয় না, কারণ কখনো কোনো সার্ভারের সাথে যোগাযোগ হয় না।
signed by Lollyvector SVGনিজেই যাচাই করুনGet the signed file১২ paths~5.1k nodes১২ groups৬০ KB
signed by Lollyvector SVGনিজেই যাচাই করুনGet the signed file১২ paths~5.1k nodes১২ groups৬০ KB
এই পৃষ্ঠাটি ফরম্যাট স্পেক। এন্ড-ইউজার ওয়াকথ্রুর জন্য দেখুন Using Lolly → Moving to another device। ইমপ্লিমেন্টেশনটি হলো shells/web/src/data-transfer.ts, এবং tests/data-transfer.test.ts round-trip কন্ট্র্যাক্টকে পিন করে।
Scope. একটি বান্ডেল ব্যবহারকারীর ডেটা বহন করে, টুল নয়। টুল এবং ক্যাটালগ অ্যাসেট আলাদাভাবে সিঙ্ক করা হয় এবং ধরে নেওয়া হয় যে সেগুলো টার্গেটে ইতিমধ্যে উপস্থিত (সবচেয়ে খারাপ ক্ষেত্রে উচ্চতর ভার্সনে)। ইমপোর্ট করা কখনো কোনো টুল ইনস্টল বা আপগ্রেড করে না।
লক্ষ্য
- One format, every shell. একই বাইট ওয়েব PWA, Tauri ডেস্কটপ/মোবাইল অ্যাপ এবং যেকোনো ভবিষ্যৎ শেল দ্বারা তৈরি ও ব্যবহৃত হয়। বান্ডেলটিই চুক্তি। প্রতিটি শেলের capability bridge এর পেছনের প্ল্যাটফর্ম-ভিত্তিক অ্যাডাপ্টার।
- Survives the trip. পরিবহনে বিকৃত বা ছেঁটে যাওয়া একটি বান্ডেল ইমপোর্টে স্পষ্টভাবে ব্যর্থ হয়, কখনো অর্ধেক-পুনরুদ্ধার করে না।
- Outlives this version. একটি পুরনো অ্যাপ এখনও একটি নতুন বান্ডেলের স্বীকৃত অংশগুলো ইমপোর্ট করতে পারে। প্রকৃতভাবে ব্রেকিং একটি ফরম্যাট পরিষ্কারভাবে প্রত্যাখ্যাত হয়।
- Safe to merge. ইতিমধ্যে ব্যবহৃত একটি ইনস্টলে ইমপোর্ট করা বান্ডেলে না থাকা কিছুই কখনো মুছে দেয় না।
এনভেলপ
একটি বান্ডেল একটি সাধারণ .zip। ডাউনলোডটির নাম যার জন্য এটি তার নামে - LollyTools-<First>-<Last>-<YYYY-MM-DD>-<n>.zip (উদাহরণস্বরূপ LollyTools-Ada-Lovelace-2026-06-26-1.zip) - যাতে ব্যাকআপের Downloads ফোল্ডার সুপাঠ্য থাকে। First এবং Last অংশ প্রোফাইল থেকে আসে এবং সেট না থাকলে বাদ দেওয়া হয়। কোনো প্রোফাইল না থাকলে LollyTools-2026-06-26-1.zip পাওয়া যায়, এবং শুধু একটি প্রথম নাম থাকলে LollyTools-Ada-2026-06-26-1.zip পাওয়া যায়। প্রতিটি অংশ একটি ফাইলনেম-সেফ টোকেনে স্যানিটাইজ করা হয় (ইউনিকোড অক্ষর/সংখ্যা রাখা হয়, স্পেস/যতিচিহ্ন সরানো হয়, 32 অক্ষরে সীমাবদ্ধ)। <n> একটি প্রতি-দিন, প্রতি-ডিভাইস সিকোয়েন্স, তাই একই দিনে বারবার এক্সপোর্ট করলে সংঘর্ষ হয় না এবং ক্রম বজায় থাকে। shells/web/src/data-transfer.ts-এর backupFilename() নামটি তৈরি করে। নাম যাই হোক না কেন, zip-এর বিষয়বস্তু অভিন্ন থাকে। ভেতরে:
| পাথ | আবশ্যক | বিষয়বস্তু |
|---|---|---|
manifest.json | হ্যাঁ | ফরম্যাট id, ভার্সন, গণনা এবং প্রতি-অংশ integrity। রিডার সর্বপ্রথম যা দেখে। |
profile.json | সেট থাকলে | ব্যবহারকারীর me রেকর্ড (নাম, যোগাযোগ, হেডশট রেফ, ফ্ল্যাগ)। host.profile দিয়ে পড়া হয়। |
sessions.json | হ্যাঁ | প্রতিটি সেভ করা সেশন: স্লট, টুল id/ভার্সন, লেবেল, থাম্বনেইল (data-URL) এবং সম্পূর্ণ ইনপুট ডেটা। host.state দিয়ে পড়া হয়। |
assets.json | হ্যাঁ | প্রতিটি আপলোড করা অ্যাসেটের (ইমেজ, ফন্ট, ব্র্যান্ড টোকেন) মেটাডেটা, যেগুলো assets/blobs/-এর অধীনে তাদের বাইট নির্দেশ করে। |
assets/blobs/<n>.<ext> | প্রতি অ্যাসেটে | কাঁচা অ্যাসেট বাইট (ইমেজ এবং ফন্ট ফাইল)। আনকমপ্রেসড অবস্থায় সংরক্ষিত (ইতিমধ্যে-কমপ্রেসড ফরম্যাট)। এক্সটেনশনটি নান্দনিক। assets.json-এর MIME-ই কর্তৃত্বপূর্ণ। |
prefs.json | হ্যাঁ | ব্যবহারকারীর-মালিকানাধীন লোকাল পছন্দ: theme, sidebarWidth এবং ct-metrics কার্যকলাপ গণনা। |
lolly.txt | হ্যাঁ | Lolly ছাড়া zip খোলা যেকারো জন্য বান্ডেলের একটি মানুষ-পাঠযোগ্য সারসংক্ষেপ (গণনা, প্রোফাইল, ফাইলনাম)। প্রতিটি এক্সপোর্টে পুনরায় তৈরি হয় এবং ইমপোর্টে স্বীকৃত হয়, তাই এটি কখনো একটি বাদ পড়া অংশ হিসেবে গণ্য হয় না। এটি integrity ম্যাপের পরে লেখা হয়, তাই এটি তার বাইরে থাকে। |
বান্ডেলটি ইচ্ছাকৃতভাবে একটি সাধারণ zip: এটি যেকোনো পরিবহনে অক্ষত থাকে, এবং যেকোনো unzip টুল এটি পরিদর্শন করতে পারে।
profile.json সবচেয়ে ছোট অংশ এবং অ্যাপে রিডার প্রথমে যা দেখে: একজন প্রযোজক একবার যা পূরণ করেন তার বিবরণ, এবং টুলগুলোকে সেগুলো ব্যবহার করতে দেওয়ার অপ্ট-ইন।
signed by Lollyvector SVGনিজেই যাচাই করুনGet the signed file১৮ paths~2.0k nodes৪১ groups৩০ KB
signed by Lollyvector SVGনিজেই যাচাই করুনGet the signed file১৮ paths~2.0k nodes৪১ groups৩০ 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। এটি ছাড়া একটি ফাইল "not a Lolly backup" হিসেবে প্রত্যাখ্যাত হয়। |
formatVersion | এই বান্ডেলটি যে লেআউট দিয়ে লেখা হয়েছিল। অংশের সেট বা আকার পরিবর্তনে বৃদ্ধি পায়। রিডাররা এর উপর নির্ভর করে গেট করে না। |
minReader | এই বান্ডেলটি নিরাপদে ইমপোর্ট করার জন্য প্রয়োজনীয় সর্বনিম্ন রিডার ভার্সন। এই ফিল্ডের উপরই রিডাররা গেট করে। |
app | প্রস্তুতকারী অ্যাপ id, ডায়াগনস্টিকসের জন্য। |
exportedAt | বান্ডেলটি তৈরির ISO টাইমস্ট্যাম্প। |
counts | রাইটার যা যোগ করেছে, প্রদর্শন এবং sanity-checking-এর জন্য। |
integrity | ঐচ্ছিক। manifest.json ছাড়া প্রতিটি অংশকে তার আনকমপ্রেসড বাইটের একটি SRI-স্টাইল sha256-<base64> ডাইজেস্টের সাথে ম্যাপ করে। |
ভার্সন নীতি (forward compatibility)
formatVersion এবং minReader-এর মধ্যে এই বিভাজনটিই পুরনো ইনস্টলগুলোকে অনাথ না করে ফরম্যাটটিকে বাড়তে দেয়:
- একটি রিডার একটি বান্ডেল ইমপোর্ট করে যখন
manifest.minReader ≤তার নিজস্ব রিডার ভার্সন। এটি শুধুমাত্র প্রত্যাখ্যান করে ("needs a newer version of the app" সহ) যখন বান্ডেলটি স্পষ্টভাবে একটি নতুন রিডার দাবি করে। - একটি additive পরিবর্তন - একটি নতুন ঐচ্ছিক অংশ, বা একটি নতুন ঐচ্ছিক ম্যানিফেস্ট ফিল্ড -
formatVersionবাড়ায় কিন্তুminReaderঅপরিবর্তিত রাখে। পুরনো অ্যাপগুলো তখনও তাদের স্বীকৃত প্রতিটি অংশ ইমপোর্ট করে। তারা যেসব অংশ চেনে না, সেগুলো বাদ দেওয়া হয় (নিচে দেখুন), নীরবে ফেলে দেওয়া হয় না। - একটি breaking পরিবর্তন - যেখানে একটি অংশের ভুল ইমপোর্ট ডেটা নষ্ট করে, বা যেখানে পূর্বে ঐচ্ছিক একটি অংশ বাধ্যতামূলক হয়ে যায় -
minReaderবাড়ায়। পুরনো অ্যাপগুলো তখন এমন কিছু ইমপোর্ট করার পরিবর্তে পরিষ্কারভাবে প্রত্যাখ্যান করে যা তারা সামলাতে পারে না। - যদি একটি ভবিষ্যৎ বান্ডেল
formatVersionসেট করে কিন্তুminReaderবাদ দেয়, তাহলে রিডাররা সতর্কতার সাথেformatVersion-এর উপর গেট করার দিকে ফিরে যায় (পরিবর্তনটিকে breaking হিসেবে গণ্য করে)।
লেখকদের জন্য সাধারণ নিয়ম: যদি প্রতিটি বিদ্যমান রিডার আপনার সংযোজন উপেক্ষা করেও সঠিক কাজ করে, তাহলে এটি additive -
formatVersionবাড়ান,minReaderঅপরিবর্তিত রাখুন। অন্যথায়minReaderবাড়ান।
অখণ্ডতা
যখন manifest.integrity উপস্থিত থাকে, একটি রিডার কিছু লেখার আগে তালিকাভুক্ত প্রতিটি অংশের SHA-256 যাচাই করে। একটি অমিল ("failed its integrity check") বা একটি অনুপস্থিত অংশ ("incomplete") সম্পূর্ণ ইমপোর্ট বাতিল করে দেয় - কোনো আংশিক পুনরুদ্ধার নেই। এটি ফাইল পরিবহনে সৃষ্ট বিকৃতি ধরে ফেলে (একটি ছেঁটে যাওয়া AirDrop, একটি ইমেইল গেটওয়ে যা অ্যাটাচমেন্ট পুনরায়-এনকোড করেছে, একটি খারাপ USB সেক্টর)।
Integrity ইচ্ছাকৃতভাবে best-effort: এটি শুধুমাত্র সেখানে লেখা হয় যেখানে Web Crypto উপলব্ধ (প্রতিটি সিকিউর ব্রাউজার কনটেক্সট এবং আধুনিক Node), এবং শুধুমাত্র তখনই যাচাই করা হয় যখন ম্যাপ এবং Web Crypto উভয়ই উপস্থিত। ম্যাপ ছাড়া একটি বান্ডেল - উদাহরণস্বরূপ integrity চালু হওয়ার আগের একটি - অপরিবর্তিতভাবে ইমপোর্ট হয়। "Cannot verify"-কে কখনো "corrupt" হিসেবে গণ্য করা হয় না।
ম্যানিফেস্টটি নিজেকে বা পুনরায় তৈরি করা lolly.txt README-কে তালিকাভুক্ত করে না। ডাইজেস্টগুলো শুধু সেই অংশগুলো কভার করে যেগুলোর জন্য ম্যানিফেস্ট নিশ্চয়তা দেয়।
ইমপোর্ট সিমান্টিক্স
ইমপোর্ট হলো merge-overwrite, কখনো replace-all নয়:
- টার্গেটের বিদ্যমান ডেটা যেমন আছে তেমনই থাকে।
- যে কোনো key যা সংঘর্ষ করে - প্রোফাইল, একটি সেশন স্লট, একটি আপলোড করা ইমেজ id - ইমপোর্ট করা কপি দিয়ে প্রতিস্থাপিত হয়।
- বান্ডেলে না থাকা কিছুই স্পর্শ করা হয় না। টার্গেটে থাকা কিন্তু বান্ডেলে না থাকা একটি সেশন ইমপোর্টের পরেও টিকে থাকে।
সেভ করা সেশনগুলো স্বয়ংক্রিয়ভাবে তাদের ইমেজের সাথে পুনরায়-লিংক হয়: অ্যাসেট রেফারেন্সগুলো id দিয়ে রাখা হয়, এবং আপলোড করা ইমেজগুলো পুনরুদ্ধার হওয়ার পর bridge সেগুলো পুনরায়-রিজলভ করে (যেভাবেই হোক এটি করতেই হবে, কারণ blob: URL রিলোডে টিকে থাকে না)।
ইমপোর্ট সারসংক্ষেপ রিপোর্ট করে { profile, sessions, userAssets, prefs, skipped, failedAssets }। failedAssets সেইসব আপলোড করা অ্যাসেট গণনা করে যেগুলো পুনরুদ্ধার করা যায়নি (যেমন ডিভাইসের স্টোরেজ পূর্ণ)। এটি skipped-এর থেকে আলাদা, যা একটি forward-compatible নতুন রাইটারের সেই অংশগুলো গণনা করে যা এই বিল্ড চিনতে পারেনি। UI skipped-কে প্রকাশ করে ("… · N newer items skipped"), যাতে পুনরুদ্ধারটি কী রেখে গেছে সে বিষয়ে সৎ থাকে।
কী ভ্রমণ করে না
- Catalog caches (ডাউনলোড করা অ্যাসেট মেটাডেটা ও ব্লব, টুল ইনডেক্স) - টার্গেটে বিনামূল্যে পুনরায়-সিঙ্ক হয়।
- Tools and brand assets - স্কোপের বাইরে, এবং ধরে নেওয়া হয় যে সেগুলো টার্গেটে ইতিমধ্যে উপস্থিত।
blob:/ object URLs - লোডের সময় bridge দ্বারা পুনরায় তৈরি হয়।- The export sequence counter - প্রতি-দিন ডাউনলোড-নামকরণ কাউন্টার (
localStoragekeylolly-export-seq) একটি লোকাল নামকরণ সুবিধা মাত্র। এটিPREF_KEYS-এর বাইরে রাখা হয়, তাই এটি কখনো একটি বান্ডেলে থাকে না।
স্টোরেজ মিটার একই বিভাজনটি আইটেমাইজ করে। Saved sessions এবং My images একটি বান্ডেলে থাকে। তাদের নিচের asset cache, tool previews এবং offline pins সবই পুনরায়-উদ্ভূতযোগ্য, তাই সেগুলো পেছনে থেকে যায়।
signed by Lollyvector SVGনিজেই যাচাই করুনGet the signed file২৫ paths~4.8k nodes৪৬ groups৫৯ KB
signed by Lollyvector SVGনিজেই যাচাই করুনGet the signed file২৫ paths~4.8k nodes৪৬ groups৫৯ KB
ক্রস-শেল গ্যারান্টি
data-transfer.ts শুধুমাত্র ক্যাপাবিলিটি ব্রিজের (host.profile, host.state, host.assets) মাধ্যমে এবং শেয়ার্ড localStorage prefs-এর মাধ্যমে পড়ে ও লেখে। যেহেতু ব্রিজই একমাত্র সংযোগস্থল, তাই একই মডিউল প্রতিটি শেলে বাইট-হুবহু বান্ডেল তৈরি করে, যদিও নিচের স্টোরেজ ভিন্ন - ওয়েবে IndexedDB, Tauri-তে ফাইল সিস্টেম। Tauri শেলগুলো এই মডিউল অপরিবর্তিতভাবে পুনরায় ব্যবহার করে। শুধু তাদের host.state বাস্তবায়ন ভিন্ন। হেডলেস টেস্টটি একটি ইন-মেমরি ব্রিজের বিপরীতে সম্পূর্ণ রাউন্ড-ট্রিপ যাচাই করে, তাই এটি এদের সবার প্রতিনিধিত্ব করে।
দুটি শেল ভিন্ন কারণে এই গ্যারান্টির বাইরে থাকে:
- one-shot CLI-এর বহন করার মতো কিছু নেই - এর স্টেট প্রতিটি কল অনুযায়ী মেমরিতে এবং সাময়িক।
- TUI স্টেট ধরে রাখে (
~/.lolly: sessions, folders, profile) এবং এর Profile ভিউ এটি ব্যাকআপ করতে পারে, কিন্তু এটি নিজের একটি সরলতর আর্কাইভ লেখে: প্রতি সেশনেsessions/<slot>.jsonএবংprofile.jsonওfolders.json, কোনো ম্যানিফেস্ট,formatVersion/minReaderবা ইন্টিগ্রিটি ম্যাপ ছাড়াই। এটি এই ফরম্যাটে আমদানিযোগ্য নয় - একটি রিডার এটিকে "not a Lolly backup" বলে প্রত্যাখ্যান করে - এবং বিভ্রান্তিকরভাবে এটি একই ধরনের নাম ব্যবহার করে (lolly-backup-<stamp>.zip)। এই দুটিকে একীভূত করা একটি পরিচিত ফাঁক।
সংরক্ষিত এক্সটেনশন পয়েন্ট
এনভেলপটি ডিজাইন অনুযায়ী একটি ম্যানিফেস্ট এবং নামযুক্ত পার্টের একটি সেট, যাতে পরবর্তীতে নতুন ধরনের পোর্টেবল ডেটা এতে কোনো ব্রেকিং চেঞ্জ ছাড়াই যুক্ত হতে পারে। এগুলো অতিরিক্ত পার্ট হিসেবে যুক্ত হয় (নতুন formatVersion, একই minReader), এবং আজকের রিডার যা চেনে না তা এড়িয়ে যায়। এগুলো রোডম্যাপে আছে, এখনও বাস্তবায়িত হয়নি। নামগুলো এখানে সংরক্ষিত রাখা হয়েছে যাতে এগুলো চালু হলে ফরম্যাটটি সংগতিপূর্ণ থাকে।
tokens.json- design tokens. একটি W3C DTCG ডিজাইন-টোকেন ডকুমেন্ট (যে ফরম্যাট Penpot ইমপোর্ট ও এক্সপোর্ট করে -$value/$type/$descriptionসহ টোকেন, গ্রুপ, সেট ও থিমে সংগঠিত)। বান্ডেলে একটি টোকেন সেট থাকলে ব্যবহারকারী তার সেশনের পাশাপাশি ব্র্যান্ড প্রিমিটিভও ইনস্টলের মধ্যে সরাতে পারেন। দীর্ঘমেয়াদে, ইনজেস্ট করা টোকেন সেট একটি প্রথম-শ্রেণীর উৎস হয়ে উঠবে যার বিপরীতে টুল ও প্যালেট অ্যাসেট রিজলভ করবে।penpot/- ingested Penpot files. একটি 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- রাউন্ড-ট্রিপ, মার্জ, ইন্টিগ্রিটি, ফরওয়ার্ড-কম্প্যাট এবং রিডার-গেট কেস। - ব্যবহৃত ব্রিজ সারফেস:
host.profile,host.state,host.assets- দেখুন Host API।