Reuse preallocated import identities and stable recovery document views

This commit is contained in:
2026-11-10 00:59:12 +08:00
parent 5590d55bb1
commit 40f3c97bbd
8 changed files with 116 additions and 10 deletions
+8 -2
View File
@@ -1,11 +1,17 @@
import type { CanvasRecord, Change, Document, ElementRecord, Geometry, Id, Registry, Target } from './types.ts';
import { clone, equal, fault, freeze, readMap, validJson } from './values.ts';
// This owns only the envelope and fixed table membership, not validation or
// immutability of values inherited by applyChanges from an external document.
const factoryDocuments=new WeakSet<object>();
function documentEnvelope(document:Document):Document{const value=Object.freeze(document);factoryDocuments.add(value);return value;}
export const isFactoryDocument=(document:Document)=>factoryDocuments.has(document);
export function createDocument(projectId: Id, canvases: readonly CanvasRecord[], elements: readonly ElementRecord[] = [], tombstones: ReadonlyMap<Id, number> = new Map()): Document {
for (const element of elements) { const id = element.id; if (!validJson(element)) throw fault('invalid_element', [id]); }
for (const canvas of canvases) if (!validJson(canvas)) throw fault('invalid_canvas');
if (!projectId || new Set(canvases.map(item => item.id)).size !== canvases.length || new Set(elements.map(item => item.id)).size !== elements.length) throw fault('duplicate_identity');
return Object.freeze({ projectId, canvases: readMap(new Map(canvases.map(item => [item.id, freeze(clone(item))]))), elements: readMap(new Map(elements.map(item => [item.id, freeze(clone(item))]))), tombstones: readMap(tombstones) });
return documentEnvelope({ projectId, canvases: readMap(new Map(canvases.map(item => [item.id, freeze(clone(item))]))), elements: readMap(new Map(elements.map(item => [item.id, freeze(clone(item))]))), tombstones: readMap(tombstones) });
}
export function geometryValid(value: Geometry): boolean {
return !!value && typeof value === 'object' && [value.x, value.y, value.width, value.height, value.rotation].every(Number.isFinite) && value.width > 0 && value.height > 0 && value.rotation >= 0 && value.rotation < 360;
@@ -115,7 +121,7 @@ export function applyChanges(document: Document, changes: readonly Change[], con
} else canvases.delete(change.id);
} else throw fault('invalid_change');
}
return Object.freeze({ projectId: document.projectId, elements: readMap(elements), canvases: readMap(canvases), tombstones: readMap(tombstones) });
return documentEnvelope({ projectId: document.projectId, elements: readMap(elements), canvases: readMap(canvases), tombstones: readMap(tombstones) });
}
export function invert(changes: readonly Change[]): readonly Change[] {
return changes.slice().reverse().map(change => {