Skip to content

Conversation

@pavelglac
Copy link
Contributor

No description provided.

@github-actions
Copy link

github-actions bot commented Oct 3, 2025

📊 Benchmark Analysis Report

No significant performance changes detected


Threshold: 5% change


Updated: 2025-11-20T11:55:19.841Z

@pavelglac pavelglac force-pushed the pavelglac/forest-run-history branch 2 times, most recently from 6729b8a to 932bf32 Compare October 6, 2025 12:38
@pavelglac pavelglac marked this pull request as draft October 6, 2025 16:38
@pavelglac pavelglac force-pushed the pavelglac/forest-run-history branch from bed2872 to 4f41b34 Compare October 24, 2025 17:34
@pavelglac pavelglac force-pushed the pavelglac/forest-run-history branch 3 times, most recently from 53bfa9a to 4e1e27e Compare November 6, 2025 09:05
Copy link
Contributor

@vladar vladar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing stuff. Going to be a game changer for debugging state issues! But before releasing it, we need a couple of tweaks in the core ForestRun code.

@@ -0,0 +1,24 @@
import type { HistoryEntry } from "../forest/types";

export class HistoryArray {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a generic utility called CircularBuffer. The idea is that jsutils folder contains lower-level things without ForestRun-specific semantics, i.e. applicable in any JS project.

When changing to CircularBuffer, HistoryEntry should become a generic TItem on a class of course

Also make it iterable for convenient iteration and Array.from() compatibility.

Suggested change
export class HistoryArray {
export class HistoryArray implements Iterable<HistoryEntry> {
[Symbol.iterator]() {
// implementation
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 41 to 42
if (shouldPushOptimisticHistory(targetForest)) {
results.outputTree.history.push(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldPushOptimisticHistory should return false when history is disabled, which is not the case now. Today this will always add history entry, even if it is disabled, which is not what we want, right?

Oh I see now, history.push ignores undefined values and createOptimisticHistoryEntry returns undefined when history is disabled. This is somewhat counter-intuitive when reading this code. Let's please change it:

  • make history.push() accept HistoryEntry (do not accept undefined)
  • make shouldPushOptimisticHistory return false when the flag is off.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the logic the handling the condition in caller.

Comment on lines 43 to 44
enableRichHistory: config?.enableRichHistory ?? false,
defaultHistorySize: config?.defaultHistorySize ?? 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a sanity check I suggest you to run the full test suite with history enabled before merging (manually). It may uncover some hidden quirks.

Ideally, also run with other flags enabled/disabled, especially optimizeFragmentReads.

Comment on lines 124 to 126
if (dirtyFields?.length) {
context.changes.set(base, dirtyFields);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So context.changes are now captured only when history is enabled:

image

We should revert this. Changes should be always captured. I think it may even break existing code when flag optimizeFragmentReads is enabled.

Regardless, changes should be always captured (if there were dirty fields).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the logic and pushing always changes. I have change the structure of those changes and since they are not used anywhere it should be safe.

export type ChangedChunksMap = Map<ObjectChunk, FieldInfo[]> &
Map<CompositeListChunk, null>;
export type ChangedChunksMap = Map<
ObjectChunk | CompositeListChunk,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see in the actual code that the key could be CompositeListChunk. Am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to

export type ChangedChunksMap = Map<
  CompositeListChunk,
  CompositeListDifferenceEntry
> &
  Map<ObjectChunk, (FillerEntry | ReplacementEntry)[]>;

But it is quite tricky to iterate this intersection type. I did not find better way than typecast it when iteration. So there is also a tuple but it is not directly connected to the map.

export type ChangedChunksTuple =
 | [CompositeListChunk, CompositeListDifferenceEntry]
 | [ObjectChunk, (FillerEntry | ReplacementEntry)[]];

Comment on lines 203 to 205
if (dirty) {
context.changes.set(base, null);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, we should always capture actual changes, even if history is disabled (we just don't add changes to history if its disabled). Maybe childChanges is a replacement you meant. But then please double-check that this context.changes is not used anywhere, especially in the context of optimizeFragmentReads flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed, now I am capturing always changes

@pavelglac pavelglac changed the title feat(forest-run): operation history feat(dev-tools): operation history Nov 19, 2025
@pavelglac pavelglac closed this Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants