Implement full data modification history and storage backup - #1870
Implement full data modification history and storage backup#1870paustint wants to merge 8 commits into
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
2581546 to
7150630
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a device-local “Data History” system that captures inputs/requests/results for data-modifying workflows (loads, mass updates, inline edits, record modal actions), stores searchable metadata in Dexie, and persists large payloads via pluggable storage backends (OPFS / File System Access directory / Electron-native filesystem). It also adds a dedicated Data History page + navigation entry points, plus settings for retention/storage location.
Changes:
- Add core Data History types, Dexie tables, retention sweep logic, and a pluggable file-store layer (OPFS worker, directory-handle backend, native desktop backend, plus test fakes).
- Wire capture into existing flows (query inline edits, load records, multi-object loads, mass updates, record modal/create record) and add opt-out toggles per operation.
- Add UI entry points (route/nav/home/header link), docs page, and supporting shared export/builders to keep downloaded/exported shapes consistent with captured history.
Reviewed changes
Copilot reviewed 118 out of 119 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.base.json | Adds TS path aliases for the new feature and shared UI data-history libs. |
| pnpm-lock.yaml | Locks dependency updates including fake-indexeddb. |
| package.json | Adds fake-indexeddb dev dependency for IndexedDB-backed unit tests. |
| libs/ui/src/lib/data-table/SalesforceRecordDataTable.tsx | Adds save-capture callback to snapshot edited/prior values + results for history before in-place mutation. |
| libs/ui/src/lib/data-table/PreviewChangesModal.tsx | Refactors export builder logic into a shared module and reuses it. |
| libs/ui/src/lib/data-table/data-table-history-export.ts | New shared export/result builders for query-grid edits (edited/prior/results). |
| libs/ui/src/lib/data-table/tests/PreviewChangesModal.spec.ts | Updates tests to import shared export builders from the new module. |
| libs/ui/src/lib/data-table/tests/data-table-history-export.spec.ts | Adds unit tests for the new query-grid history export builders. |
| libs/ui/src/index.ts | Exposes the new data-table export builder utilities. |
| libs/types/src/lib/data-history-types.ts | Adds zod schemas/types for Data History items, settings, backends, and file refs. |
| libs/types/src/index.ts | Exports Data History types from the shared types package. |
| libs/shared/ui-router/src/lib/ui-router.ts | Adds the DATA_HISTORY route entry (path/docs/title/description/new-until). |
| libs/shared/ui-db/src/lib/ui-db.ts | Adds Dexie tables + schema version bump for data_history and data_history_config. |
| libs/shared/ui-db/src/lib/data-history.db.ts | New DB module for Data History catalog rows + settings/backend config persistence. |
| libs/shared/ui-db/src/lib/client-data-sync.db.ts | Filters out local-only table changes so they don’t trigger server sync POSTs. |
| libs/shared/ui-db/src/index.ts | Re-exports the new Data History DB module. |
| libs/shared/ui-data-history/vite.config.ts | Adds Vitest config for the new shared ui-data-history library. |
| libs/shared/ui-data-history/tsconfig.spec.json | Adds TS config for ui-data-history tests. |
| libs/shared/ui-data-history/tsconfig.lib.json | Adds TS config for ui-data-history library build settings. |
| libs/shared/ui-data-history/tsconfig.json | Adds TS project references for ui-data-history. |
| libs/shared/ui-data-history/src/test-setup.ts | Sets up fake-indexeddb + Blob realm compatibility for compression streams in tests. |
| libs/shared/ui-data-history/src/lib/platform.ts | Adds platform detection helpers used to select tier/backends. |
| libs/shared/ui-data-history/src/lib/file-store/worker-messages.ts | Defines typed RPC protocol for OPFS storage worker requests/responses. |
| libs/shared/ui-data-history/src/lib/file-store/path-utils.ts | Adds validated relative-path helpers and stable org folder naming. |
| libs/shared/ui-data-history/src/lib/file-store/opfs-file-store.ts | Implements OPFS backend with worker-based I/O and RPC request handling. |
| libs/shared/ui-data-history/src/lib/file-store/native-fs-file-store.ts | Implements desktop-native filesystem backend via Electron IPC. |
| libs/shared/ui-data-history/src/lib/file-store/fsa-types.ts | Adds minimal File System Access API typings + permission error type. |
| libs/shared/ui-data-history/src/lib/file-store/file-store.types.ts | Defines the pluggable file store interface + capability flags. |
| libs/shared/ui-data-history/src/lib/file-store/file-store-factory.ts | Resolves active backend from config with fallback behavior and test seams. |
| libs/shared/ui-data-history/src/lib/file-store/fake-file-store.ts | In-memory backend used for unit/contract tests (with real gzip). |
| libs/shared/ui-data-history/src/lib/data-history-state.ts | Central module state for tier limits/settings and deletion helper. |
| libs/shared/ui-data-history/src/lib/data-history-retention.ts | Adds retention + orphan reconciliation sweep with tier enforcement + locks. |
| libs/shared/ui-data-history/src/lib/data-history-manifest.ts | Adds manifest writer so on-disk folders can be re-indexed. |
| libs/shared/ui-data-history/src/lib/data-history-limits.ts | Adds tier-based limits and settings clamping defaults. |
| libs/shared/ui-data-history/src/lib/csv-utils.ts | Adds fixed-format CSV chunk serializer for streaming history writes. |
| libs/shared/ui-data-history/src/lib/tests/path-utils.spec.ts | Tests path validation/builders and org folder hashing behavior. |
| libs/shared/ui-data-history/src/lib/tests/fake-fsa-handles.ts | Adds in-memory FSA handles for DirectoryHandle backend tests. |
| libs/shared/ui-data-history/src/lib/tests/data-history-retention.spec.ts | Adds end-to-end tests for retention sweep behavior and orphan cleanup. |
| libs/shared/ui-data-history/src/lib/tests/csv-utils.spec.ts | Tests streaming CSV chunk output behavior and invariants. |
| libs/shared/ui-data-history/src/index.ts | Exports public API surface of the new shared data history library. |
| libs/shared/ui-data-history/project.json | Adds Nx project config and Vitest target for ui-data-history. |
| libs/shared/ui-data-history/eslint.config.cjs | Adds eslint config passthrough for the new library. |
| libs/shared/ui-core/src/record/ViewEditCloneRecord.tsx | Records record modal create/edit/clone actions to Data History (inline payload). |
| libs/shared/ui-core/src/mass-update-records/useDeployRecords.ts | Wires mass-update flow to create/finalize Data History entries and stream results. |
| libs/shared/ui-core/src/mass-update-records/MassUpdateRecordsDeploymentRow.tsx | Refactors result-building to shared utils to match captured history/export shape. |
| libs/shared/ui-core/src/mass-update-records/mass-update-records.utils.tsx | Adds shared builders for mass-update combined result rows + headers. |
| libs/shared/ui-core/src/mass-update-records/data-history-capture.ts | Adds thin adapters from mass-update deploy flow to history capture APIs. |
| libs/shared/ui-core/src/mass-update-records/tests/mass-update-history-builders.spec.ts | Adds unit tests for the new mass-update result/header builders. |
| libs/shared/ui-core/src/mass-update-records/tests/data-history-capture.spec.ts | Adds integration tests for mass-update → Data History wiring using fake backends. |
| libs/shared/ui-core/src/index.ts | Exports new Data History related UI components/settings from ui-core. |
| libs/shared/ui-core/src/app/HeaderNavbarItems.tsx | Adds Data History entry to header navigation menu config. |
| libs/shared/ui-core/src/app/HeaderNavbar.tsx | Adds a header icon button linking to Data History. |
| libs/shared/ui-core/src/app/DataHistoryLinks.tsx | Adds reusable header button + inline “View Data History” link components. |
| libs/shared/ui-core/src/app/AppHome/AppHome.tsx | Adds Data History card to the “Load” section on the home page. |
| libs/shared/ui-app-state/src/lib/ui-app-state.ts | Adds atom to synchronously track whether capture is enabled (seeded on init). |
| libs/shared/constants/src/lib/shared-constants.ts | Adds analytics keys and page title constant for Data History. |
| libs/icon-factory/src/lib/icon-factory.tsx | Adds new icons (standard/utility pin/pinned/asset_audit) for UI usage. |
| libs/features/update-records/src/deployment/MassUpdateRecordsDeployment.tsx | Adds per-run “skip history” option and links to Data History when enabled. |
| libs/features/query/src/QueryResults/QueryResults.tsx | Wires query inline edit save capture into recordDataHistoryAction. |
| libs/features/query/src/QueryResults/BulkUpdateFromQuery/BulkUpdateFromQueryModal.tsx | Adds “skip history” toggle in bulk-update-from-query modal when enabled. |
| libs/features/load-records/src/utils/data-history-capture.ts | Adds helper adapters for Load Records to write/append/finish/fail history entries. |
| libs/features/load-records/src/utils/tests/data-history-capture.spec.ts | Adds unit tests for load-records history capture wrappers/mappers. |
| libs/features/load-records/src/components/load-results/LoadRecordsResults.tsx | Threads an optional Data History handle through results components. |
| libs/features/load-records/src/components/load-results/LoadRecordsBatchApiResults.tsx | Streams batch results to Data History and finalizes entries with counts. |
| libs/features/load-records/src/components/load-results/load-results-utils.ts | Adds shared result-row/header builders for Load Records exports + history capture. |
| libs/features/load-records/src/components/load-results/tests/load-results-utils.spec.ts | Adds unit tests for Load Records shared result-row/header builders. |
| libs/features/load-records/src/components/load-results/tests/data-history-results-capture.spec.ts | Adds integration tests for Load Records → Data History end-to-end capture. |
| libs/features/load-records-multi-object/src/useDownloadResults.ts | Refactors exports to shared builders used by both downloads and history capture. |
| libs/features/load-records-multi-object/src/LoadRecordsMultiObject.tsx | Adds multi-object load Data History capture, per-run opt-out, and finalize step. |
| libs/features/load-records-multi-object/src/load-records-multi-object-results.ts | Adds shared request/result export builders + derived counts helper. |
| libs/features/load-records-multi-object/src/data-history-capture.ts | Adds adapters for multi-object load to write request/finalize/derive metadata. |
| libs/features/load-records-multi-object/src/tests/data-history-capture.spec.ts | Adds tests for multi-object capture helpers and history wiring. |
| libs/features/data-history/vite.config.ts | Adds Vitest config for the new feature/data-history UI library. |
| libs/features/data-history/tsconfig.spec.json | Adds test TS config for the data-history feature library. |
| libs/features/data-history/tsconfig.lib.json | Adds library TS config for the data-history feature library. |
| libs/features/data-history/tsconfig.json | Adds TS project references for the data-history feature library. |
| libs/features/data-history/src/lib/DataHistoryDetailModal.tsx | Adds detail modal with preview/download of saved payloads for an entry. |
| libs/features/data-history/src/lib/data-history-page.utils.ts | Adds labels, formatting, sorting, preview truncation, and download naming helpers. |
| libs/features/data-history/src/lib/data-history-download.ts | Adds shared download helper that reads/decompresses and triggers file save. |
| libs/features/data-history/src/lib/tests/data-history-page.utils.spec.ts | Adds unit tests for Data History page utility behaviors. |
| libs/features/data-history/src/index.ts | Exports Data History feature components/utils. |
| libs/features/data-history/project.json | Adds Nx project config and Vitest target for the new feature library. |
| libs/features/data-history/eslint.config.cjs | Adds eslint config passthrough for the new feature library. |
| libs/features/create-records/src/lib/CreateRecords.tsx | Records “create record without file” actions to Data History (inline payload). |
| libs/desktop-types/src/lib/desktop-app.types.ts | Adds Electron IPC request types + preference for native data history folder. |
| apps/jetstream/vite.config.ts | Adjusts Vite fs allow list for dev server. |
| apps/jetstream/src/app/components/settings/Settings.tsx | Adds Data History settings section to app settings page. |
| apps/jetstream/src/app/components/core/AppInitializer.tsx | Initializes Data History and seeds capture-enabled state atom at startup. |
| apps/jetstream/src/app/AppRoutes.tsx | Adds route for the new Data History page. |
| apps/jetstream-web-extension/vite.plugins.ts | Adds placeholder page entry for data-history in extension build. |
| apps/jetstream-web-extension/vite.config.ts | Adds dev server fs allow list and worker ts-paths plugin for extension. |
| apps/jetstream-web-extension/src/pages/app/App.tsx | Adds Data History route for the extension app. |
| apps/jetstream-web-extension/src/pages/additional-settings/AdditionalSettings.tsx | Adds Data History settings section to extension additional settings. |
| apps/jetstream-web-extension/src/core/AppInitializer.tsx | Initializes Data History and seeds capture-enabled state for extension. |
| apps/jetstream-desktop/src/services/ipc.service.ts | Adds IPC handlers for native data history file ops and folder configuration. |
| apps/jetstream-desktop/src/preload.ts | Exposes Data History IPC APIs to the renderer via contextBridge. |
| apps/jetstream-desktop-client/vite.config.ts | Enables worker ts-paths plugin and adjusts dev server fs allow list. |
| apps/jetstream-desktop-client/src/app/components/settings/Settings.tsx | Adds Data History settings section to desktop client settings. |
| apps/jetstream-desktop-client/src/app/components/core/AppInitializer.tsx | Initializes Data History and seeds capture-enabled state for desktop client. |
| apps/jetstream-desktop-client/src/app/AppRoutes.tsx | Adds Data History route for the desktop client. |
| apps/jetstream-canvas/vite.config.mts | Enables worker ts-paths plugin and adjusts dev server fs allow list. |
| apps/jetstream-canvas/src/app/core/AppInitializer.tsx | Initializes Data History and seeds capture-enabled state for canvas app. |
| apps/jetstream-canvas/src/app/AppRoutes.tsx | Adds Data History route for the canvas app. |
| apps/docs/sidebars.ts | Adds Data History doc page to docs sidebar. |
| apps/docs/docs/load/data-history.mdx | Adds end-user documentation for the Data History feature. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
eb43783 to
4a4ac1c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 120 out of 121 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (1)
libs/shared/ui-data-history/src/lib/file-store/path-utils.ts:48
getParentDirPath()returns an empty string for single-segment paths (e.g.'manifest.json'). Downstream callers treat this as an entry directory and pass it todeleteEntryDir, which then throws on validation (and can derail migrations / deletes for corrupted rows).
Add an explicit guard so getParentDirPath only accepts paths that actually have a parent directory.
4a4ac1c to
3630cc8
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 120 out of 121 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (3)
libs/shared/ui-data-history/src/lib/data-history.service.ts:444
- Potential race with the retention sweep's orphan-dir cleanup: this branch writes payload files + manifest to the file store before
dataHistoryDb.saveEntry(item)runs (line 475). IfrunDataHistoryRetentionSweep()is running concurrently, step (5) will see adh_*directory with no Dexie row yet and may delete it as an orphan, leaving the eventual saved row pointing at missing files. Consider either (a) inserting a placeholder Dexie row before any file I/O (then updating it with file refs), or (b) marking the entry key as "active" and having the sweep skip active keys for orphan deletion.
libs/features/data-history/src/lib/DataHistoryPayloadTab.tsx:161 buildJsonPreviewparses and pretty-prints the entire JSON string before applyingbuildDataHistoryPreviewText. For large request/result payloads this can freeze the UI (JSON.parse + JSON.stringify on multi‑MB content) even though the preview ultimately truncates to 100k chars / 200 lines. Consider truncating first and skipping JSON.parse when truncation occurred (show raw preview text instead).
function buildJsonPreview(text: string): LoadedDataHistoryPayload {
let formatted = text;
try {
formatted = JSON.stringify(JSON.parse(text), null, 2);
} catch {
apps/docs/docs/load/data-history.mdx:50
- Docs say Jetstream "automatically asks" for persistent storage, but the implementation intentionally skips auto-requesting on Firefox (
browserPromptsForPersist()in@jetstream/ui/data-history), requiring an explicit user gesture instead. Please qualify this sentence (e.g. "In most browsers…" / "Firefox requires clicking the keep-history button") so it matches actual behavior.
Jetstream automatically asks the browser for "persistent storage" to protect your history from
automatic cleanup — no action needed on your part.
3630cc8 to
807b0a6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 121 out of 122 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (2)
libs/shared/ui-db/src/lib/data-history.db.ts:96
getEntries()always scans thecreatedAt(or[org+createdAt]) index and then filterssourcein memory, even though the Dexie schema defines a[source+createdAt]compound index. For large histories this makes common queries likegetEntries({ source: ... })and retention sweeps needlessly expensive.
apps/docs/docs/load/data-history.mdx:50- The docs say Jetstream automatically requests persistent storage with no user action, but the implementation explicitly skips auto-request on Firefox to avoid a doorhanger prompt (it requires an explicit user gesture via the "keep my history" button). This section should mention the Firefox behavior so users aren’t misled.
Jetstream automatically asks the browser for "persistent storage" to protect your history from
automatic cleanup — no action needed on your part.
807b0a6 to
7f91b24
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 121 out of 122 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (1)
libs/features/load-records/src/components/load-results/LoadRecordsBatchApiResults.tsx:1
- Avoid disabling
react-hooks/refsfor the entire module. This rule is intended to catch ref mutations during render that can lead to subtle tearing/stale reads; disabling it file-wide makes it easier for future ref misuse to slip in unnoticed. Prefer ref updates inside auseEffect(or, if the pattern is truly needed, use a narrowly scoped// eslint-disable-next-line react-hooks/refson the exact line that triggers the rule and immediately re-enable afterward).
7f91b24 to
173bb06
Compare
173bb06 to
c3d8c7c
Compare
c3d8c7c to
c967b67
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 121 out of 122 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (2)
libs/features/load-records/src/components/load-results/LoadRecordsBatchApiResults.tsx:1
- The file-level
eslint-disable react-hooks/refsdisables hook/ref lint checks across the entire component, which can hide real dependency/stale-closure issues elsewhere in the file. If only a specific ref usage needs an exception, prefer a narrowly scopedeslint-disable-next-line react-hooks/refson that line (or refactor to satisfy the rule) rather than disabling the rule for the whole file.
apps/docs/docs/load/data-history.mdx:50 - The docs say Jetstream automatically requests persistent storage with no user action, but the implementation intentionally skips auto-request on browsers that prompt (e.g. Firefox) and instead relies on an explicit user gesture. Consider adjusting this line to avoid over-promising persistence behavior across browsers.
Jetstream automatically asks the browser for "persistent storage" to protect your history from
automatic cleanup — no action needed on your part.
c967b67 to
9e3f768
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 121 out of 122 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (1)
libs/features/data-history/src/lib/DataHistoryPayloadTab.tsx:161
buildJsonPreviewattemptsJSON.parse/JSON.stringifyon the full payload before truncation. For large request/result JSON this can be very slow and memory-heavy, even though the UI ultimately only shows a capped preview. Truncate first and only pretty-print when the content is small enough to fully preview.
function buildJsonPreview(text: string): LoadedDataHistoryPayload {
let formatted = text;
try {
formatted = JSON.stringify(JSON.parse(text), null, 2);
} catch {
// not valid JSON — show the raw text
}
const { text: jsonText, truncated } = buildDataHistoryPreviewText(formatted);
return { jsonText, jsonTruncated: truncated };
- New Data History page listing all captured data modifications across orgs and features, with filters, pin/delete, payload preview/download - Data History settings section (enable/disable, retention, storage usage, clear-all) shared across web, desktop, and browser extension - Access points: header clock icon, Load Records menu, home page card, contextual links on capture surfaces, settings cross-links - Documentation page with links from the app feat: add user-selected storage locations for data history Data history can now be stored outside default browser storage: - Chrome/Edge: pick a real folder (File System Access API) — files are user-visible, backed up, survive clearing site data, and the history list can be rebuilt from the folder's manifest files - Desktop app: store history on the file system with a configurable, relocatable folder (default userData/data-history) - Existing history migrates automatically when switching locations, and entries remain readable mid-migration via per-entry backend routing - Storage location controls added to the Data History settings section feat: simplify data history limits and storage controls - Persistent storage is requested automatically; removed the manual settings control - Free plans now keep the 15 most recent entries (count-based, enforced as history is captured) instead of a storage-size cap; upgrade prompts shown on the history page and in settings - Removed the max-storage size setting; storage size remains only as an internal per-plan backstop - History can be moved to a folder directly from the Data History page feat: improve data history table usability - Org column shows the Salesforce org id beneath the org label - Download request/input/results directly from each table row - Sortable column headers (date, org, feature, objects, status, records, size) over the visible entries fix: align data history table row actions and remove size column feat: show the connected data history folder on the history page fix: skip empty data sync push requests for local-only table changes dexie-observable reports changes for every table, including local-only ones like data history — batches with no syncable-table changes are now acknowledged locally instead of POSTing an empty data array to /api/data-sync/push
- Files in a user-chosen folder (Chrome/Edge) or on disk (desktop) are now plain .csv/.json instead of gzip so they open directly in Excel and other tools on any platform; browser-managed storage stays compressed. Existing compressed entries remain readable and are re-encoded when moved between storage locations - Show 'Files are saved to' with the folder name on the history page and in settings; on desktop the full path is shown and opens in Explorer/Finder on click - Change the connected history folder from settings - Use the correct paid-plan signal for history limits (some paid plans were incorrectly limited to 15 entries)
- Detail modal now has a Summary tab plus a tab per saved payload - Input and output data render in the sortable/filterable data table (CSV parsed into columns/rows); JSON payloads show a formatted preview - Download available on the summary tab and each payload tab; the Preview button switches to that payload's tab instead of an inline dump Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9e3f768 to
d0ef096
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 121 out of 122 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (1)
apps/jetstream-canvas/src/app/core/AppInitializer.tsx:65
- This effect now uses
setDataHistoryCaptureEnabled/setDataHistoryInitializedbut still has an empty dependency array. Witheslint-plugin-react-hooks(exhaustive-deps) enabled elsewhere in the repo, this is likely to trigger a lint warning/error and is inconsistent with the other AppInitializers that include these setters in deps.
.then(() => setDataHistoryInitialized(true))
.catch((ex) => {
logger.error('[DB] Error initializing db', ex);
});
}, []);
Data-loss and integrity fixes: - Orphan-dir sweep only collects OPFS dirs — rowless dirs in user-visible folders (reconnected/shared/restored) are recoverable history, not garbage - Keep the folder handle when switching back to browser storage so non-migrated entries stay readable; sweep migrates stragglers later - Change-folder is two-phase: copy old->new before repointing the config, abort (no repoint) on copy failure; skipped entries reported accurately - Migration and retention pruning skip entries likely still being written, including captures in other tabs/documents (recent in-progress rows) - Desktop: folder relocation only persists after a fully successful copy, rejects while streams are open, and cleans up partial copies - Desktop: write-stream error listeners prevent main-process crashes on disk-full/EACCES; write-file is atomic (tmp + rename) to protect manifests - 14-day paid-tier grace window so a billing lapse (PAST_DUE) cannot trigger destructive free-tier pruning of history - One-shot captures write the row before files (orphan-sweep invariant), rolling back on failure; tombstones stop folder re-index from resurrecting user-deleted entries Worker/OPFS protocol: - Stream ids are client-allocated so a respawned worker can never route a stale handle into another entry's file - Store disposal on backend switch is deferred until in-flight captures finish (previously terminated the worker immediately; also fixes the worker leak and the pendingRequests leak on synchronous postMessage failure from the earlier round) - A failed gzip pump errors the stream instead of hanging the capture queue with the file locked (OPFS worker + FSA store) - Backend switches broadcast to other tabs so long-lived tabs stop writing history to the previous backend; permission revocation mid-session surfaces the re-connect affordance immediately - writeFile no longer detaches the caller's buffer Capture correctness: - Bulk API results pair per batch via original submission positions, so failed batches can no longer misalign history rows and Download All - Bulk prepare-failures record attempted counts (parity with Batch API); successful creates are captured even when the display re-fetch fails; query-table capture is isolated from the save path (no duplicate-save risk); retry input rows are flattened so external-Id columns serialize - Manifest-write failure no longer flips a successful entry to failed UI: - Detail modal subscribes to its entry directly so it live-updates even when filters drop it from the list; disabled banner waits for init From the earlier review round: skip + report unreadable-source entries in migration, keep storageBackend stamps aligned with where handles write, pull on initial/reconnect sync for local-only-change batches, cap CSV preview parsing for huge files
d0ef096 to
d6e1596
Compare
Introduce a comprehensive data history tracking system, allowing users to view and manage all modifications across various records. Enhance storage flexibility by enabling user-selected locations for data history, simplifying retention controls, and improving usability of the data history table. Add a dedicated Data History page with filtering options and export capabilities.