Support Edit History #3

Closed
opened 2026-03-17 20:24:54 +00:00 by maxtkc · 0 comments
Owner

There are a few routes we can go with this, but what I suggest is this:

  • Store snapshots of the whole feed to indexeddb
  • Store changes from the snapshot in indexeddb
  • Maintain in memory the current state

In the ui, we can show all the changes. We can at least support linear undo. I think we can cherry pick specific historical changes and undo them as well. We don't need to support tons of changes, just add, update, and remove.

Here is the chatgpt summary:

Client-Side Patch-Based GTFS Editor Architecture

Core Principles

  • Memory-first: The current fully materialized GTFS state lives in memory for fast editing and undo/redo.
  • IndexedDB for persistence only: Stores patches (incremental changes) and periodic snapshots (checkpoints).
  • Patch-based history: Every edit generates a small, semantic patch describing the change.
  • Snapshots for performance: Store full compressed state every ~50–100 edits to avoid replaying too many patches.
  • Time-travel / rollback: Reconstruct state by loading the nearest snapshot and applying subsequent patches.

Data Flow

Memory (runtime)

  • currentState — full GTFS data
  • Updated instantly as edits are made
  • Source of truth for UI and operations

IndexedDB (persistent storage)

  1. Snapshots
{
  "version": 100,
  "state": "<compressed full GTFS object>"
}
  1. Patches
{
  "version": 101,
  "patch": {
    "op": "update",
    "table": "stops",
    "id": "123",
    "changes": { "stop_name": ["Old", "New"] }
  },
  "timestamp": 1680000000000,
  "description": "Renamed stop"
}

Workflow

Initial Load / Cold Start

  1. Load the latest snapshot from IndexedDB.
  2. Load patches after that snapshot.
  3. Apply patches sequentially to reconstruct currentState.

Editing

  1. User edits → generate a patch.
  2. Apply patch to currentState (in-memory).
  3. Persist patch to IndexedDB.

Snapshotting

  • Every ~50–100 patches, compress and save a snapshot to IndexedDB.
  • Reduces reconstruction time for future loads.

Rollback / Time Travel

  1. Find the nearest snapshot ≤ target version.
  2. Load snapshot and apply patches forward to the desired version.

Undo / Redo (optional)

  • Store inverse patches when generating forward patches.
  • Apply inverse for undo, forward for redo without full reconstruction.

Performance Considerations

  • Keep one active state in memory; avoid duplicating full objects.
  • Patch storage is tiny compared to full GTFS objects.
  • Compression for snapshots reduces IndexedDB storage footprint.
  • Large tables (e.g., stop_times) may benefit from partial/lazy loading if needed.

Optional Enhancements

  • Deduplicate strings to reduce memory usage (e.g., repeated stop names or route IDs).
  • Use Immer or similar libraries to generate patches automatically.
  • Cache recently reconstructed versions for history preview.

Summary

  • Memory: source of truth, fast edits.
  • Patches: append-only, incremental changes stored in IndexedDB.
  • Snapshots: periodic compressed checkpoints to speed reconstruction.
  • Reconstruction: snapshot + patches → current state.
  • Undo/Redo & Rollback: inverse patches or replaying patches.
  • No backend required: fully client-side, durable, and performant for 10–30MB GTFS feeds.
There are a few routes we can go with this, but what I suggest is this: - Store snapshots of the whole feed to indexeddb - Store changes from the snapshot in indexeddb - Maintain in memory the current state In the ui, we can show all the changes. We can at least support linear undo. I think we can cherry pick specific historical changes and undo them as well. We don't need to support tons of changes, just add, update, and remove. Here is the chatgpt summary: # Client-Side Patch-Based GTFS Editor Architecture ## Core Principles - **Memory-first:** The current fully materialized GTFS state lives in memory for fast editing and undo/redo. - **IndexedDB for persistence only:** Stores patches (incremental changes) and periodic snapshots (checkpoints). - **Patch-based history:** Every edit generates a small, semantic patch describing the change. - **Snapshots for performance:** Store full compressed state every ~50–100 edits to avoid replaying too many patches. - **Time-travel / rollback:** Reconstruct state by loading the nearest snapshot and applying subsequent patches. --- ## Data Flow ### Memory (runtime) - `currentState` — full GTFS data - Updated instantly as edits are made - Source of truth for UI and operations ### IndexedDB (persistent storage) 1. **Snapshots** ```json { "version": 100, "state": "<compressed full GTFS object>" } ``` 2. **Patches** ```json { "version": 101, "patch": { "op": "update", "table": "stops", "id": "123", "changes": { "stop_name": ["Old", "New"] } }, "timestamp": 1680000000000, "description": "Renamed stop" } ``` --- ## Workflow ### Initial Load / Cold Start 1. Load the latest snapshot from IndexedDB. 2. Load patches after that snapshot. 3. Apply patches sequentially to reconstruct `currentState`. ### Editing 1. User edits → generate a patch. 2. Apply patch to `currentState` (in-memory). 3. Persist patch to IndexedDB. ### Snapshotting - Every ~50–100 patches, compress and save a snapshot to IndexedDB. - Reduces reconstruction time for future loads. ### Rollback / Time Travel 1. Find the nearest snapshot ≤ target version. 2. Load snapshot and apply patches forward to the desired version. ### Undo / Redo (optional) - Store inverse patches when generating forward patches. - Apply inverse for undo, forward for redo without full reconstruction. --- ## Performance Considerations - Keep **one active state** in memory; avoid duplicating full objects. - Patch storage is tiny compared to full GTFS objects. - Compression for snapshots reduces IndexedDB storage footprint. - Large tables (e.g., `stop_times`) may benefit from partial/lazy loading if needed. --- ## Optional Enhancements - Deduplicate strings to reduce memory usage (e.g., repeated stop names or route IDs). - Use `Immer` or similar libraries to generate patches automatically. - Cache recently reconstructed versions for history preview. --- ## Summary - **Memory:** source of truth, fast edits. - **Patches:** append-only, incremental changes stored in IndexedDB. - **Snapshots:** periodic compressed checkpoints to speed reconstruction. - **Reconstruction:** snapshot + patches → current state. - **Undo/Redo & Rollback:** inverse patches or replaying patches. - **No backend required:** fully client-side, durable, and performant for 10–30MB GTFS feeds.
maxtkc self-assigned this 2026-03-17 20:31:49 +00:00
maxtkc referenced this issue from a commit 2026-03-18 23:13:44 +00:00
maxtkc referenced this issue from a commit 2026-03-20 17:26:35 +00:00
maxtkc referenced this issue from a commit 2026-03-20 17:26:35 +00:00
maxtkc referenced this issue from a commit 2026-03-20 17:26:35 +00:00
maxtkc referenced this issue from a commit 2026-03-20 17:26:35 +00:00
maxtkc referenced this issue from a commit 2026-03-23 13:20:25 +00:00
maxtkc referenced this issue from a commit 2026-03-25 16:21:52 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
gtfs.zone/coloring-book#3
No description provided.