molecule-core/canvas/src/store/deleteTombstones.ts
rabbitblood 8c69a98da2 chore(simplify): share FALLBACK_POLL_MS as the tombstone TTL + trim verbose comments
Simplify pass on top of #2069 fix:

- Export FALLBACK_POLL_MS from canvas/src/store/socket.ts and import
  it as TOMBSTONE_TTL_MS in deleteTombstones.ts. Single source of
  truth — tuning one without the other would silently re-open the
  hydrate-races-delete window. Required-fix per simplify reviewer.
- Compress deleteTombstones.ts docstring from 30 lines to 10 — keep
  the "what + why module-level"; drop the long-form problem
  description (issue #2069 carries it).
- Compress canvas.ts call-site comments at removeSubtree (4 lines →
  2) and hydrate (2 lines → 2 but tighter).
- Don't reassign the workspaces parameter inside hydrate — use a
  const `live` and thread it through the two downstream calls
  (computeAutoLayout, buildNodesAndEdges). Same effect, no lint
  smell.
- Trim the canvas.test.ts integration-test preamble.

No behaviour change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 13:52:49 -07:00

56 lines
1.6 KiB
TypeScript

/**
* Transient "recently deleted" map keyed by workspace id.
*
* `removeSubtree` calls `markDeleted(ids)` on every removal; `hydrate`
* calls `wasRecentlyDeleted(id)` to filter out incoming workspaces
* whose ids match a fresh tombstone — prevents an in-flight
* GET /workspaces from resurrecting just-deleted nodes via hydrate.
*
* TTL is shared with the WS-fallback poll cadence so a single
* round-trip is covered. Module-level (not store state) so it doesn't
* trigger React Flow re-renders. (#2069)
*/
import { FALLBACK_POLL_MS } from "./socket";
const TOMBSTONE_TTL_MS = FALLBACK_POLL_MS;
const tombstones = new Map<string, number>();
function gcExpired(now: number): void {
for (const [id, deletedAt] of tombstones) {
if (now - deletedAt >= TOMBSTONE_TTL_MS) {
tombstones.delete(id);
}
}
}
export function markDeleted(ids: Iterable<string>): void {
const now = Date.now();
gcExpired(now);
for (const id of ids) {
tombstones.set(id, now);
}
}
export function wasRecentlyDeleted(id: string): boolean {
const deletedAt = tombstones.get(id);
if (deletedAt === undefined) return false;
if (Date.now() - deletedAt >= TOMBSTONE_TTL_MS) {
tombstones.delete(id);
return false;
}
return true;
}
/** Test-only: clear the module-level map between tests. Production code
* must not call this — the map is intentionally process-lifetime. */
export function __resetTombstonesForTest(): void {
tombstones.clear();
}
/** Test-only: inspect the current tombstone count. */
export function __tombstoneCountForTest(): number {
return tombstones.size;
}