Story 2 — schaduw-fase: BacklogHydrationWrapper en useBacklogRealtime voeden
nu ook de nieuwe product-workspace-store, terwijl de oude useBacklogStore /
useProductStore leidend blijft voor componenten. Story 3 verschuift consumers
één voor één; Story 8 ruimt de oude stores op.
- T-844: BacklogHydrationWrapper roept naast useBacklogStore.setInitialData
ook useProductWorkspaceStore.hydrateSnapshot aan. Productname-prop optioneel
toegevoegd voor activeProduct-context.
- T-845: useBacklogRealtime onmessage dispatcht events naar zowel oude store
(applyChange) als nieuwe store (applyRealtimeEvent). Geen wijziging aan
reconnect/visibility — Story 5.
- T-846: dev-only logWorkspaceFingerprint helper vergelijkt counts tussen
oude en nieuwe store na hydrate en na elk realtime-event. console.warn bij
mismatch; opt-in debug log via NEXT_PUBLIC_DEBUG_WORKSPACE_FINGERPRINT=1.
Bestand TODO-marked voor verwijdering in Story 8 (T-878).
- T-847: SetCurrentProduct schrijft naast oude useProductStore ook
useProductWorkspaceStore.setActiveProduct({id, name}); cleanup cleart beide.
setActiveProduct triggert ensureProductLoaded — fetch-stub tot Story 7
(T-870) de LIST-endpoints toevoegt.
Verify: lint+typecheck clean, 636/636 tests groen (geen UI-regressie omdat
oude store leidend blijft).
Refs: PBI-74, ST-1319, T-844..T-847
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
2.5 KiB
TypeScript
71 lines
2.5 KiB
TypeScript
// PBI-74 / T-846: dev-only schaduw-store fingerprint verifyer.
|
|
// Logt counts van oude (useBacklogStore) en nieuwe (useProductWorkspaceStore)
|
|
// na elke hydratie- of realtime-event. Bij mismatch verschijnt er een
|
|
// console.warn zodat we tijdens Story 2 in dev-tools zien dat beide stores
|
|
// dezelfde inhoud houden.
|
|
//
|
|
// TODO(PBI-74 / Story 8 / T-878): verwijder dit bestand en alle aanroepen
|
|
// vóór merge van de cleanup-PR.
|
|
|
|
import { useBacklogStore } from '@/stores/backlog-store'
|
|
import { useProductWorkspaceStore } from '@/stores/product-workspace/store'
|
|
|
|
interface Fingerprint {
|
|
pbis: number
|
|
storiesByPbi: Record<string, number>
|
|
tasksByStory: Record<string, number>
|
|
}
|
|
|
|
function fingerprintOld(): Fingerprint {
|
|
const s = useBacklogStore.getState()
|
|
const storiesByPbi: Record<string, number> = {}
|
|
for (const [pbiId, list] of Object.entries(s.storiesByPbi)) {
|
|
storiesByPbi[pbiId] = list.length
|
|
}
|
|
const tasksByStory: Record<string, number> = {}
|
|
for (const [storyId, list] of Object.entries(s.tasksByStory)) {
|
|
tasksByStory[storyId] = list.length
|
|
}
|
|
return { pbis: s.pbis.length, storiesByPbi, tasksByStory }
|
|
}
|
|
|
|
function fingerprintNew(): Fingerprint {
|
|
const s = useProductWorkspaceStore.getState()
|
|
const storiesByPbi: Record<string, number> = {}
|
|
for (const [pbiId, ids] of Object.entries(s.relations.storyIdsByPbi)) {
|
|
storiesByPbi[pbiId] = ids.length
|
|
}
|
|
const tasksByStory: Record<string, number> = {}
|
|
for (const [storyId, ids] of Object.entries(s.relations.taskIdsByStory)) {
|
|
tasksByStory[storyId] = ids.length
|
|
}
|
|
return { pbis: s.relations.pbiIds.length, storiesByPbi, tasksByStory }
|
|
}
|
|
|
|
function shapeEqual(a: Record<string, number>, b: Record<string, number>): boolean {
|
|
const keys = new Set([...Object.keys(a), ...Object.keys(b)])
|
|
for (const k of keys) {
|
|
if ((a[k] ?? 0) !== (b[k] ?? 0)) return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
export function logWorkspaceFingerprint(label: string): void {
|
|
if (process.env.NODE_ENV === 'production') return
|
|
|
|
const oldFp = fingerprintOld()
|
|
const newFp = fingerprintNew()
|
|
const match =
|
|
oldFp.pbis === newFp.pbis &&
|
|
shapeEqual(oldFp.storiesByPbi, newFp.storiesByPbi) &&
|
|
shapeEqual(oldFp.tasksByStory, newFp.tasksByStory)
|
|
|
|
if (!match) {
|
|
console.warn(
|
|
`[workspace-fingerprint:${label}] MISMATCH oud↔nieuw`,
|
|
{ old: oldFp, new: newFp },
|
|
)
|
|
} else if (process.env.NEXT_PUBLIC_DEBUG_WORKSPACE_FINGERPRINT === '1') {
|
|
console.debug(`[workspace-fingerprint:${label}] match`, oldFp)
|
|
}
|
|
}
|