Skip to content

fix(core,server): implement two-phase seed population correctly#92

Merged
miguelramos merged 6 commits into
nextfrom
fix/vite-20k-seed-population
Mar 3, 2026
Merged

fix(core,server): implement two-phase seed population correctly#92
miguelramos merged 6 commits into
nextfrom
fix/vite-20k-seed-population

Conversation

@miguelramos
Copy link
Copy Markdown
Member

@miguelramos miguelramos commented Mar 3, 2026

Three bugs caused the seed response priority path to be dead code:

  1. updateSeeds() in server.ts reassigned currentSeeds instead of using
    .clear()+.set() in-place mutation. Route closures captured the
    original Map by reference, so reassignment broke the reference chain.
    Now matches the updateHandlers() pattern.

  2. The orchestrator's processSpec() called executeSeeds() to populate
    the store but never called server.updateSeeds() to sync the route
    builder's seed map. Added buildSeedMapFromStore() helper that reads
    materialized data from the store and passes it to updateSeeds().

  3. Hot reload's reloadSpecSeeds() had the same gap — executeSeeds()
    populated the store but the route builder seed map was never synced.
    Now calls updateSeeds() after executeSeeds(), which also handles
    registry hasSeed flags and WebSocket broadcast.

Closes vite-20k

Summary by CodeRabbit

  • New Features

    • Added a utility to derive seed data maps from the populated store for more reliable seed updates.
  • Bug Fixes

    • Fixed hot-reload seed synchronization so large seed sets update consistently and avoid stale data during spec reloads.
  • Tests

    • Updated test coverage to validate per-spec seed reload behavior and clearing/error paths.
  • Documentation

    • Clarified inline guidance around seed-update semantics and in-place update expectations.

Miguel Ramos added 3 commits March 3, 2026 14:44
Three bugs caused the seed response priority path to be dead code:

1. updateSeeds() in server.ts reassigned currentSeeds instead of using
   .clear()+.set() in-place mutation. Route closures captured the
   original Map by reference, so reassignment broke the reference chain.
   Now matches the updateHandlers() pattern.

2. The orchestrator's processSpec() called executeSeeds() to populate
   the store but never called server.updateSeeds() to sync the route
   builder's seed map. Added buildSeedMapFromStore() helper that reads
   materialized data from the store and passes it to updateSeeds().

3. Hot reload's reloadSpecSeeds() had the same gap — executeSeeds()
   populated the store but the route builder seed map was never synced.
   Now calls updateSeeds() after executeSeeds(), which also handles
   registry hasSeed flags and WebSocket broadcast.

Closes vite-20k
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between b877969 and 4e22794.

📒 Files selected for processing (4)
  • packages/server/src/__tests__/per-spec-reload.test.ts
  • packages/server/src/hot-reload.ts
  • packages/server/src/orchestrator.ts
  • packages/server/src/seeds.ts

📝 Walkthrough

Walkthrough

Refactors hot-reload seed handling to use explicit in-place Map mutation and per-spec updateSeeds calls; adds buildSeedMapFromStore(store) to derive seed maps from the in-memory store and replaces broadcast-based seed updates with direct updateSeeds synchronization.

Changes

Cohort / File(s) Summary
Changeset
.changesets/fix-vite-20k-seed-population.json
Adds a patch changeset documenting the fix branch and packages affected; records a large sequence of commit SHAs and metadata.
Core server hot-reload logic
packages/core/src/server.ts
Treats seeds as a separate mutable Map captured by closures; updateSeeds now mutates the existing Map in-place (clear + set) and documentation updated to require supplying the same Map instances for handlers and seeds.
Server seed sync & orchestrator
packages/server/src/hot-reload.ts, packages/server/src/orchestrator.ts
Imports and uses buildSeedMapFromStore to derive seed maps from the store; replaces direct broadcast/count logic with calls to server.updateSeeds(seedMap) after seed execution or load success/failure.
Seed utilities
packages/server/src/seeds.ts
Adds exported helper buildSeedMapFromStore(store: Store): Map<string, unknown[]> and imports Store type from core to build a schema→items map excluding empty schemas.
Tests
packages/server/src/__tests__/per-spec-reload.test.ts
Reworks mocks and assertions to expect per-spec updateSeeds calls (with proper Map contents) instead of wsHub.broadcast; adds scenarios asserting clearing/error paths call updateSeeds(new Map()) and verifies per-spec independence.

Sequence Diagram

sequenceDiagram
    participant Client as Hot-Reload Watcher
    participant HotReload as hot-reload.ts
    participant Orchestrator as orchestrator.ts
    participant Store as In-Memory Store
    participant Server as server.ts
    participant Routes as Route Builder

    Client->>HotReload: detects seed file change
    HotReload->>Orchestrator: processSpec(spec)
    Orchestrator->>Orchestrator: executeSeeds(spec)
    Orchestrator->>Store: seed functions populate store
    Orchestrator->>Orchestrator: buildSeedMapFromStore(store)
    Orchestrator->>Server: server.updateSeeds(seedMap)
    Server->>Server: currentSeeds.clear() and .set(...entries)
    Server->>Routes: route closures read updated currentSeeds
    HotReload->>Client: emit reload notification (seedMap.size)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble through maps, small and grand,
Mutating in-place with careful hand.
Seeds reflow, each route keeps its link,
No stale data, no broken sync.
A hop, a patch — the server's bright and planned.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title directly and clearly summarizes the main fix: implementing two-phase seed population correctly across core and server packages.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/vite-20k-seed-population

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/server.ts (1)

577-613: ⚠️ Potential issue | 🟠 Major

Snapshot entries before mutation and split updateSeeds into helpers.

At Line 581, clearing currentSeeds before iterating newSeeds can wipe data if both references are the same map. Also, CI is failing on Line 577 for cognitive complexity (16 > 15), so this block needs decomposition.

Proposed refactor (fix aliasing + reduce complexity)
+function replaceMapInPlace<K, V>(target: Map<K, V>, source: Map<K, V>): Array<[K, V]> {
+  const entries = Array.from(source.entries());
+  target.clear();
+  for (const [k, v] of entries) target.set(k, v);
+  return entries;
+}
+
+function repopulateStoreFromSeedEntries(
+  store: Store,
+  entries: ReadonlyArray<[string, unknown[]]>,
+  logger: Logger,
+): void {
+  store.clearAll();
+  for (const [schemaName, items] of entries) {
+    for (const item of items) {
+      try {
+        store.create(schemaName, item);
+      } catch (error) {
+        logger.warn(`[vite-plugin-open-api-core] Failed to seed ${schemaName}:`, error);
+      }
+    }
+  }
+}
+
 updateSeeds(newSeeds: Map<string, unknown[]>): void {
-  currentSeeds.clear();
-  for (const [key, value] of newSeeds) {
-    currentSeeds.set(key, value);
-  }
-
-  store.clearAll();
-  for (const [schemaName, items] of newSeeds) {
-    for (const item of items) {
-      try {
-        store.create(schemaName, item);
-      } catch (error) {
-        logger.warn(`[vite-plugin-open-api-core] Failed to seed ${schemaName}:`, error);
-      }
-    }
-  }
+  const seedEntries = replaceMapInPlace(currentSeeds, newSeeds);
+  repopulateStoreFromSeedEntries(store, seedEntries, logger);

-  const seedSchemaNames = new Set(newSeeds.keys());
+  const seedSchemaNames = new Set(seedEntries.map(([schemaName]) => schemaName));
   for (const entry of registry.endpoints.values()) {
     if (entry.responseSchema) {
       entry.hasSeed = seedSchemaNames.has(entry.responseSchema);
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/server.ts` around lines 577 - 613, The updateSeeds method
both risks aliasing bugs (clearing currentSeeds while newSeeds may be the same
Map) and exceeds cognitive complexity limits; to fix, first avoid in-place
iteration over newSeeds by capturing its entries up-front (e.g., const entries =
Array.from(newSeeds.entries()) or a shallow copy) before clearing currentSeeds,
then repopulate currentSeeds from that captured list; next split updateSeeds
into small helper functions (e.g., applySeedsToCurrentSeeds(entries),
repopulateStore(entries) which uses store.create and preserves the existing
try/catch behavior, and updateRegistrySeedFlags(seedNames) which updates
registry.endpoints' hasSeed flags), then call wsHub.broadcast and logger.info
from the top-level updateSeeds to keep it under the complexity threshold;
reference updateSeeds, currentSeeds, store.create, registry.endpoints,
wsHub.broadcast, and logger.info when making these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/server/src/__tests__/per-spec-reload.test.ts`:
- Around line 385-390: The mocked implementation for mockedExecuteSeeds is
misformatted; reformat the returned object literal and surrounding lines to
match project formatting rules (e.g., spacing, indentation, trailing commas) so
CI formatter passes. Locate the mockedExecuteSeeds.mockImplementation block and
adjust the object returned from the mock (the { schemaCount: 1, totalItems: 1,
itemsPerSchema: { Pet: 1 }, skippedSchemas: [], warnings: [] } literal) and the
adjacent mockStore.getSchemas.mockReturnValue and mockStore.list.mockReturnValue
calls to follow the repository's formatter style.

In `@packages/server/src/hot-reload.ts`:
- Around line 460-478: Extract the duplicated buildSeedMapFromStore
implementation into a shared utility (e.g., export a function from a new or
existing common module) and replace both local implementations with an import;
specifically, move the function body that iterates store.getSchemas(), calls
store.list(schemaName), and sets non-empty arrays into a Map<string, unknown[]>
into the shared helper, export it, then update the callers in hot-reload
(function buildSeedMapFromStore) and the duplicate in orchestrator to import and
call the shared buildSeedMapFromStore; ensure the shared module exports the same
signature (accepts Store and returns Map<string, unknown[]>) and update any
imports/typing references accordingly.

---

Outside diff comments:
In `@packages/core/src/server.ts`:
- Around line 577-613: The updateSeeds method both risks aliasing bugs (clearing
currentSeeds while newSeeds may be the same Map) and exceeds cognitive
complexity limits; to fix, first avoid in-place iteration over newSeeds by
capturing its entries up-front (e.g., const entries =
Array.from(newSeeds.entries()) or a shallow copy) before clearing currentSeeds,
then repopulate currentSeeds from that captured list; next split updateSeeds
into small helper functions (e.g., applySeedsToCurrentSeeds(entries),
repopulateStore(entries) which uses store.create and preserves the existing
try/catch behavior, and updateRegistrySeedFlags(seedNames) which updates
registry.endpoints' hasSeed flags), then call wsHub.broadcast and logger.info
from the top-level updateSeeds to keep it under the complexity threshold;
reference updateSeeds, currentSeeds, store.create, registry.endpoints,
wsHub.broadcast, and logger.info when making these changes.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between f345067 and b877969.

📒 Files selected for processing (5)
  • .changesets/fix-vite-20k-seed-population.json
  • packages/core/src/server.ts
  • packages/server/src/__tests__/per-spec-reload.test.ts
  • packages/server/src/hot-reload.ts
  • packages/server/src/orchestrator.ts

Comment thread packages/server/src/__tests__/per-spec-reload.test.ts
Comment thread packages/server/src/hot-reload.ts Outdated
Comment on lines +460 to +478
/**
* Build a seed data Map from the store's current contents.
*
* After `executeSeeds()` populates the store, this reads back the
* materialized data so it can be passed to `server.updateSeeds()`.
*
* @param store - Store populated by executeSeeds()
* @returns Map of schema name to array of items
*/
function buildSeedMapFromStore(store: Store): Map<string, unknown[]> {
const seedMap = new Map<string, unknown[]>();
for (const schemaName of store.getSchemas()) {
const items = store.list(schemaName);
if (items.length > 0) {
seedMap.set(schemaName, items);
}
}
return seedMap;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Extract buildSeedMapFromStore into a shared utility.

This helper is duplicated in packages/server/src/orchestrator.ts (Line 158 onward). Centralizing it avoids drift in seed-map materialization behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/server/src/hot-reload.ts` around lines 460 - 478, Extract the
duplicated buildSeedMapFromStore implementation into a shared utility (e.g.,
export a function from a new or existing common module) and replace both local
implementations with an import; specifically, move the function body that
iterates store.getSchemas(), calls store.list(schemaName), and sets non-empty
arrays into a Map<string, unknown[]> into the shared helper, export it, then
update the callers in hot-reload (function buildSeedMapFromStore) and the
duplicate in orchestrator to import and call the shared buildSeedMapFromStore;
ensure the shared module exports the same signature (accepts Store and returns
Map<string, unknown[]>) and update any imports/typing references accordingly.

Miguel Ramos added 3 commits March 3, 2026 17:16
Break long single-line return statements into multi-line object
literals at lines 389, 501, and 521 to satisfy biome formatting rules.
Move the duplicated buildSeedMapFromStore() utility from both
orchestrator.ts and hot-reload.ts into seeds.ts, which already
houses seed-related utilities. Both files now import from the
shared location, eliminating verbatim code duplication.
The extraction of buildSeedMapFromStore() to seeds.ts requires the
test mock to pass through the actual implementation so it can read
from the mock store objects during test execution.
@miguelramos miguelramos merged commit 0cb1580 into next Mar 3, 2026
5 checks passed
@miguelramos miguelramos deleted the fix/vite-20k-seed-population branch March 3, 2026 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 TYPE: Bug Something is broken

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant