draft-storage.js (1896B)
1 // One local recovery slot, never executable state or an automatic run request. 2 // Browser limits are intentionally NOT persisted: recovery starts in Standard. 3 export const DRAFT_KEY = 'arboricx.local-draft.v1'; 4 export function savedDraft(raw) { 5 if (!raw || typeof raw !== 'object' || Array.isArray(raw)) 6 throw new Error('Invalid saved draft'); 7 const v = raw; 8 for (const [key, max] of [['source', 32768], ['name', 256], ['arguments', 16000], ['output', 32]]) 9 if (typeof v[key] !== 'string' || v[key].length > max) 10 throw new Error('Invalid saved draft ' + key); 11 if (v['schema'] !== 1 || (v['mode'] !== 'pure' && v['mode'] !== 'io')) 12 throw new Error('Invalid saved draft version/mode'); 13 let demo; 14 if (v['demo'] !== undefined) { 15 const d = v['demo']; 16 if (!d || typeof d !== 'object' || typeof d['hash'] !== 'string' || !/^[0-9a-f]{64}$/u.test(d['hash']) || typeof d['environment'] !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/u.test(d['environment'])) 17 throw new Error('Invalid saved demo context'); 18 demo = { hash: d['hash'], environment: d['environment'] }; 19 } 20 return { schema: 1, source: v['source'], name: v['name'], arguments: v['arguments'], 21 output: v['output'], mode: v['mode'], ...(demo ? { demo } : {}) }; 22 } 23 export function readDraft(storage) { 24 const text = storage.getItem(DRAFT_KEY); 25 if (text === null) 26 return undefined; 27 if (text.length > 100000) 28 throw new Error('Saved draft too large'); 29 return savedDraft(JSON.parse(text)); 30 } 31 export function writeDraft(storage, draft) { 32 const text = JSON.stringify(savedDraft(draft)); 33 if (text.length > 100000) 34 throw new Error('Saved draft too large'); 35 storage.setItem(DRAFT_KEY, text); 36 if (storage.getItem(DRAFT_KEY) !== text) 37 throw new Error('Draft storage verification failed'); 38 }