From 6edbcaf0fafee5ddf18521cf95a3a7468db56b25 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Wed, 28 Oct 2026 04:59:58 +0800 Subject: [PATCH] fix(environment): index stored deleted entities separately --- .../environment/environmentStateStore.ts | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/application/environment/environmentStateStore.ts b/src/application/environment/environmentStateStore.ts index 3a8b504a8a..b6e9d99a5e 100644 --- a/src/application/environment/environmentStateStore.ts +++ b/src/application/environment/environmentStateStore.ts @@ -1,6 +1,7 @@ -import type { - StateEntity, - StateType, +import { + markedDeleted, + type StateEntity, + type StateType, } from "../../domain/analysisView/environmentStateRules.js"; import { nextSequence } from "../../domain/analysisView/environmentStateWriteValues.js"; import { @@ -100,6 +101,7 @@ export const initialStateBytes = (state: EnvironmentState): number => */ export class EnvironmentStateStore { #entities: ReadonlyMap = new Map(); + #storedDeleted: ReadonlyMap = new Map(); #collections: ReadonlyMap = new Map(); #typeCollections: ReadonlyMap = new Map(); #documents: ReadonlyMap = new Map(); @@ -247,7 +249,9 @@ export class EnvironmentStateStore { /** Every held entity of a type, in state order. */ ofType(type: string): readonly StateEntity[] { - return this.#content.entities.filter((entity) => entity.type === type); + return this.#content.entities.filter( + (entity) => entity.type === type && !markedDeleted(entity.fields), + ); } /** Add an entity at `index` of its collection's order, unless the state would outgrow what `data/add` loads. */ @@ -255,9 +259,16 @@ export class EnvironmentStateStore { entity: StateEntity, index: number, ): Result { - const entities = this.#content.entities; + const key = compositeKey([entity.type, entity.collection, entity.id]); + const replaced = this.#storedDeleted.get(key); + const entities = + replaced === undefined + ? this.#content.entities + : this.#content.entities.filter((held) => held !== replaced); const bytes = this.#grown( - jsonByteLength(entity) + (entities.length > 0 ? 1 : 0), + jsonByteLength(entity) - + (replaced === undefined ? 0 : jsonByteLength(replaced)) + + (replaced === undefined && entities.length > 0 ? 1 : 0), ); if (!bytes.ok) return bytes; const known = this.#knownCollections.get(entity.type); @@ -390,7 +401,9 @@ export class EnvironmentStateStore { id: string, ): StateEntity | undefined { const key = compositeKey([type, collection, id]); - return this.#entities.has(key) ? undefined : this.#deleted.get(key); + return this.#entities.has(key) + ? undefined + : (this.#storedDeleted.get(key) ?? this.#deleted.get(key)); } #keepDeleted(entity: StateEntity): void { @@ -510,9 +523,20 @@ export class EnvironmentStateStore { /** Build current-state indexes without copying baseline collections into generated history. */ #install(content: StateContent, bytes: number): void { const entities = new Map(); + const storedDeleted = new Map(); const collections = new Map(); const typeCollections = new Map(); for (const entity of content.entities) { + if (markedDeleted(entity.fields)) { + storedDeleted.set( + compositeKey([entity.type, entity.collection, entity.id]), + entity, + ); + const named = typeCollections.get(entity.type) ?? []; + if (!named.includes(entity.collection)) named.push(entity.collection); + typeCollections.set(entity.type, named); + continue; + } entities.set( compositeKey([entity.type, entity.collection, entity.id]), entity, @@ -527,9 +551,11 @@ export class EnvironmentStateStore { const named = typeCollections.get(entity.type); if (named === undefined) typeCollections.set(entity.type, [entity.collection]); - else named.push(entity.collection); + else if (!named.includes(entity.collection)) + named.push(entity.collection); } this.#entities = entities; + this.#storedDeleted = storedDeleted; this.#collections = collections; this.#typeCollections = typeCollections; this.#documents = new Map(