import type {CanvasRecord,Change,Document,ElementRecord,Id} from './types.ts'; import {applyChanges} from './document.ts'; import {clone,fault,freeze,readMap,validJson} from './values.ts'; /** Transaction-local append views. Each retained view has fixed membership; * at most 64 additions are copied before folding into a new read-only base. */ const overlays=new WeakMap;added:ReadonlyMap}>(),drafts=new WeakSet(); function append(input:ReadonlyMap,id:Id,value:T):ReadonlyMap{ if(input.has(id))throw fault('duplicate_identity',[id]); let previous=overlays.get(input) as {base:ReadonlyMap;added:ReadonlyMap}|undefined; if(previous&&previous.added.size>=64)previous=undefined; const base=previous?.base??(overlays.has(input)?readMap(input):input),added=new Map(previous?.added); added.set(id,value); const entries=function*():Generator<[Id,T],undefined,unknown>{yield* base;yield* added;return undefined;}; const view:ReadonlyMap=Object.freeze({size:base.size+added.size,get:(key:Id)=>added.has(key)?added.get(key):base.get(key),has:(key:Id)=>added.has(key)||base.has(key),entries, *keys():Generator{for(const [key]of entries())yield key;return undefined;},*values():Generator{for(const [,item]of entries())yield item;return undefined;},[Symbol.iterator]:entries, forEach(callback:(value:T,key:Id,map:ReadonlyMap)=>void,thisArg?:unknown){for(const[key,item]of entries())callback.call(thisArg,item,key,view);}, }); overlays.set(view,{base,added});return view; } /** Internal editor path: input tables already have fixed membership. */ export function stageCreation(document:Document,change:Extract):Document{ if(change.before!==null||!change.after)throw fault('invalid_creation_draft'); if(!validJson(change.after))throw fault(change.kind==='canvas'?'invalid_canvas':'invalid_element',[change.id]); if(change.after.id!==change.id)throw fault('identity_mismatch',[change.id]); const value=freeze(clone(change.after)); const next=Object.freeze({...document,...(change.kind==='canvas'?{canvases:append(document.canvases,change.id,value as CanvasRecord)}:{elements:append(document.elements,change.id,value as ElementRecord)})});drafts.add(next);return next; } /** Only published documents use the core's owned table envelopes. */ export const materializeCreations=(document:Document)=>drafts.has(document)?applyChanges(document,[]):document;