Add Dexie storage migration foundation#248
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@copilot please continue this PR. Context:
Please do the following:
Expected lockfile resolutions previously seen from Vercel:
|
f363ad4 to
ace5ee5
Compare
|
@copilot run appropriate bmad skills to validate whether this is a worthy effort going forward or not |
Ran BMAD party-mode validation against commit |
|
@copilot please have bmad create a plan for this effort and document it in the proper place |
Done in 2e57b15. Engaged John ( |
Agent-Logs-Url: https://github.com/maniator/blipit-legends/sessions/30b6285c-6882-49cb-a935-01804e862a16 Co-authored-by: maniator <539579+maniator@users.noreply.github.com>
2e57b15 to
24feef1
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 24feef1c14
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await db.transaction("rw", db.saves, db.events, async () => { | ||
| await db.saves.put(cleanHeader); | ||
| if (cleanEvents.length > 0) { | ||
| await db.events.bulkPut(cleanEvents); | ||
| } |
There was a problem hiding this comment.
Remove stale event rows during save import
When importing a save that reuses an existing header.id, this path upserts the header and bulkPuts incoming events but never deletes prior rows for that save. If the existing save has more events than the imported bundle, higher-index stale events remain in events, so later load/replay can read extra history not present in the imported file. The RxDB SaveStore.importRxdbSave path explicitly clears existing events first, so this is a parity regression in the new Dexie store.
Useful? React with 👍 / 👎.
|
|
||
| const { matchupMode: _drop, ...headerRest } = header as unknown as Record<string, unknown>; | ||
| const cleanHeader = { ...headerRest } as unknown as SaveRecord; | ||
| const cleanEvents = Array.isArray(events) ? events : []; |
There was a problem hiding this comment.
Validate imported event records before persisting
The import flow currently accepts any array as events and writes it directly, without checking saveId/id consistency, index contiguity, or record shape. Because the signature key is static and public, a manually crafted bundle can still pass signature verification and then persist malformed or foreign-save rows, violating assumptions that the existing RxDB import path enforces via event validation. This can leave orphaned/misaligned event logs for the imported save.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR lays groundwork for migrating persistence from RxDB-on-Dexie to direct Dexie/IndexedDB by introducing a typed Dexie schema, a Dexie-backed save/event store slice, and a bmad planning artifact set—while keeping RxDB as the production runtime backend for now.
Changes:
- Added
dexieanddexie-react-hooksdependencies (with lockfile updates). - Introduced
BallgameDexieDb(typed tables + v1 schema/indexes) and a Dexie-backedDexieSaveStorewith parity-oriented unit tests. - Added/updated Dexie migration documentation (pointer doc + full bmad artifact set).
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Adds direct Dexie dependencies. |
| yarn.lock | Locks Dexie dependency resolutions. |
| src/storage/types.ts | Re-exports PortableSaveExport via @storage/types. |
| src/features/saves/storage/types.ts | Renames the portable save envelope type to PortableSaveExport and deprecates RxdbExportedSave. |
| src/storage/dexieDb.ts | Adds typed Dexie DB singleton + v1 schema/index definitions. |
| src/storage/dexieDb.test.ts | Adds schema/index tests for the Dexie DB. |
| src/features/saves/storage/dexieSaveStore.ts | Adds Dexie-backed save/event store implementation (create/list/update/delete, eviction, append queue, export/import). |
| src/features/saves/storage/dexieSaveStore.test.ts | Adds parity-oriented tests for the Dexie save store slice. |
| docs/dexie-migration-plan.md | Converts old plan doc into a pointer to the bmad artifact set. |
| _bmad-output/planning-artifacts/dexie-migration/README.md | Adds planning artifact hub (scope, contracts, validation gates). |
| _bmad-output/planning-artifacts/dexie-migration/roadmap.md | Adds phased v0–v5 migration roadmap. |
| _bmad-output/planning-artifacts/dexie-migration/decisions.md | Captures locked decisions and pending decisions for the migration. |
| _bmad-output/planning-artifacts/dexie-migration/risks.md | Adds risk register with mitigations and owning phases. |
| _bmad-output/planning-artifacts/dexie-migration/epics-and-stories.md | Breaks migration into epics/stories with acceptance criteria. |
| _bmad-output/planning-artifacts/dexie-migration/cutover-checklist.md | Adds pre-merge gates for v4 cutover and v5 RxDB removal. |
| _bmad-output/planning-artifacts/dexie-migration/phase-plans/v1-foundation.md | Adds phase plan for v1 foundation (this PR’s scope). |
| _bmad-output/planning-artifacts/dexie-migration/phase-plans/v2-repository-abstraction.md | Adds phase plan for repository interface seam work. |
| _bmad-output/planning-artifacts/dexie-migration/phase-plans/v3-dexie-repositories.md | Adds phase plan for Dexie repository implementations + parity. |
| _bmad-output/planning-artifacts/dexie-migration/phase-plans/v4-cutover.md | Adds phase plan for runtime flip + bridge migration + kill switch. |
| _bmad-output/planning-artifacts/dexie-migration/phase-plans/v5-rxdb-removal.md | Adds phase plan for RxDB removal once stable. |
|
@copilot resolve the merge conflicts in this pull request and create a merge commit |
Agent-Logs-Url: https://github.com/maniator/blipit-legends/sessions/51c937cb-9b92-482f-89bc-c0dea97dccf7 Co-authored-by: maniator <539579+maniator@users.noreply.github.com>
Done in f57b510 — true two-parent merge commit (parents: |
| if (!nextIdxMap.has(saveId)) { | ||
| const existing = await db.events.where("saveId").equals(saveId).toArray(); | ||
| const highestIdx = existing.reduce((max, event) => Math.max(max, event.idx), -1); | ||
| nextIdxMap.set(saveId, highestIdx + 1); | ||
| } |
| try { | ||
| await db.events.bulkAdd(docs); | ||
| } catch (err) { | ||
| nextIdxMap.set(saveId, startIdx); | ||
| throw err; | ||
| } |
| const events = await db.events.where("saveId").equals(saveId).sortBy("idx"); | ||
| const sig = fnv1a(PORTABLE_SAVE_EXPORT_KEY + JSON.stringify({ header, events })); |
| await db.transaction("rw", db.saves, db.events, async () => { | ||
| await db.saves.put(cleanHeader); | ||
| if (cleanEvents.length > 0) { | ||
| await db.events.bulkPut(cleanEvents); | ||
| } |
| await db.open(); | ||
|
|
||
| expect(db.tables.map((table) => table.name).sort()).toEqual([ | ||
| "batterGameStats", | ||
| "completedGames", | ||
| "events", | ||
| "pitcherGameStats", | ||
| "players", | ||
| "saves", | ||
| "teams", | ||
| ]); | ||
| }); | ||
|
|
||
| it("creates the indexes needed by current storage queries", async () => { | ||
| const db = createDexieDb(TEST_DB_NAME); | ||
|
|
||
| await db.open(); | ||
|
|
||
| expect(db.saves.schema.primKey.keyPath).toBe("id"); | ||
| expect(db.saves.schema.idxByName.updatedAt).toBeDefined(); | ||
|
|
||
| expect(db.events.schema.primKey.keyPath).toBe("id"); | ||
| expect(db.events.schema.idxByName.saveId).toBeDefined(); | ||
| expect(db.events.schema.idxByName["[saveId+idx]"]).toBeDefined(); | ||
|
|
||
| expect(db.teams.schema.primKey.keyPath).toBe("id"); | ||
| expect(db.teams.schema.idxByName.updatedAt).toBeDefined(); | ||
| expect(db.teams.schema.idxByName.nameLowercase).toBeDefined(); | ||
|
|
||
| expect(db.players.schema.primKey.keyPath).toBe("id"); | ||
| expect(db.players.schema.idxByName.teamId).toBeDefined(); | ||
|
|
||
| expect(db.completedGames.schema.idxByName.playedAt).toBeDefined(); | ||
| expect(db.completedGames.schema.idxByName["[homeTeamId+playedAt]"]).toBeDefined(); | ||
| expect(db.completedGames.schema.idxByName["[awayTeamId+playedAt]"]).toBeDefined(); | ||
|
|
||
| expect(db.batterGameStats.schema.idxByName.gameId).toBeDefined(); | ||
| expect(db.batterGameStats.schema.idxByName["[playerId+createdAt]"]).toBeDefined(); | ||
| expect(db.batterGameStats.schema.idxByName["[teamId+createdAt]"]).toBeDefined(); | ||
|
|
||
| expect(db.pitcherGameStats.schema.idxByName.gameId).toBeDefined(); | ||
| expect(db.pitcherGameStats.schema.idxByName["[playerId+createdAt]"]).toBeDefined(); | ||
| expect(db.pitcherGameStats.schema.idxByName["[teamId+createdAt]"]).toBeDefined(); |
|
@copilot please fix workflow failures, apply changes based on the comments in this thread, run a bmad code review loop and fix all issues found |
Agent-Logs-Url: https://github.com/maniator/blipit-legends/sessions/2f047553-4025-46ff-9e6e-0cebb7fc7feb Co-authored-by: maniator <539579+maniator@users.noreply.github.com>
Done in a131eff. Workflow fix: the failing Reviewer-thread fixes (against
bmad code-review loop: ran Local validation: |
Summary
Starts the migration from RxDB-on-Dexie toward direct Dexie/IndexedDB storage without changing the production runtime storage path yet.
This PR establishes the direct Dexie foundation, documents the staged migration plan, and adds a Dexie-backed save/event store slice with parity-oriented tests. RxDB remains the live app storage path for now.
Changes
dexieanddexie-react-hooksdependencies topackage.json.yarn.lockfor the new direct Dexie dependencies.src/storage/dexieDb.tswith typed Dexie tables for the current storage collections:saveseventsteamsplayerscompletedGamesbatterGameStatspitcherGameStatsdocs/dexie-migration-plan.mdwith the staged plan for moving from RxDB to direct Dexie.src/features/saves/storage/dexieSaveStore.ts, a Dexie-backed save/event store that mirrors current SaveStore behavior for:Testing
Not run in this session.
Expected follow-up validation:
The lockfile now includes the expected dependency resolutions:
dexie@npm:^4.2.0→4.4.2dexie-react-hooks@npm:^4.2.0→4.4.0Risks
SaveStoreuntil the storage abstraction/cutover work is complete.