Skip to content

feat: add devtool autodiscovery#4

Open
quolpr wants to merge 6 commits into
mainfrom
quolpr/devtool-autodiscovery
Open

feat: add devtool autodiscovery#4
quolpr wants to merge 6 commits into
mainfrom
quolpr/devtool-autodiscovery

Conversation

@quolpr

@quolpr quolpr commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • New Features
    • Demo storage now offers IndexedDB and browser SQLite persistence (plus optional in-memory hybrid tiers) and remembers your choice after reload.
    • Demo shows live persistence queue/status metrics.
    • Devtools can discover additional databases when none is explicitly selected.
  • Improvements
    • Benchmark measurements are more reliable and failures are logged.
    • Devtools selector updates dynamically as databases register.
  • Documentation
    • Updated devtool and driver/persistence docs for the new discovery and SQLite behavior.
  • Tests
    • Enhanced devtool and tracing/registry test coverage for stability.

@codesandbox

codesandbox Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@netlify

netlify Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deploy Preview for hyperdb ready!

Name Link
🔨 Latest commit 89d975c
🔍 Latest deploy log https://app.netlify.com/projects/hyperdb/deploys/6a3991e79ed5820008e67145
😎 Deploy Preview https://deploy-preview-4--hyperdb.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: be8862bb-0ce9-4a71-9e7e-8b2697bae232

📥 Commits

Reviewing files that changed from the base of the PR and between cc2515f and 89d975c.

📒 Files selected for processing (5)
  • AGENTS.md
  • packages/hyperdb-demo/src/stores.ts
  • packages/hyperdb-doc/src/content/docs/runtime/db.md
  • packages/hyperdb/src/hyperdb/tracing/registry.test.ts
  • packages/hyperdb/src/hyperdb/tracing/registry.ts
✅ Files skipped from review due to trivial changes (2)
  • AGENTS.md
  • packages/hyperdb-doc/src/content/docs/runtime/db.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/hyperdb/src/hyperdb/tracing/registry.test.ts
  • packages/hyperdb/src/hyperdb/tracing/registry.ts
  • packages/hyperdb-demo/src/stores.ts

📝 Walkthrough

Walkthrough

The PR adds a global globalThis.__hyperdb registry so that devtools can discover registered DB instances without requiring an explicit prop. It extracts trace DB identity helpers into a new db-info.ts module, adds register/withTraits fixes to the DB constructor, and introduces useOptionalDB. For the demo, it adds an IndexedDB-backed persistence mode with StoreMode, PersistenceMonitor, two-tier DB initialization, and a BenchmarkApp storage selector.

Changes

DB Registry & Devtools Discovery

Layer / File(s) Summary
Trace DB identification extracted to db-info.ts
packages/hyperdb/src/hyperdb/tracing/db-info.ts, packages/hyperdb/src/hyperdb/tracing/store.ts, packages/hyperdb/src/react/context.ts
Creates db-info.ts with nextTraceId and getTraceDBInfo, removes the equivalent local implementation from store.ts, and updates all frame/event ID-generation call sites and re-exports. Adds useOptionalDB to the React context.
DB registration system and DB constructor opt-in
packages/hyperdb/src/hyperdb/tracing/registry.ts, packages/hyperdb/src/hyperdb/tracing/index.ts, packages/hyperdb/src/hyperdb/runtime/db.ts, packages/hyperdb/src/hyperdb/tracing/registry.test.ts
Adds registry.ts with RegisteredHyperDBInfo, registerHyperDB, getRegisteredHyperDBs, and subscribeToRegisteredHyperDBs stored under globalThis.__hyperdb. Extends DBOptions with register?: boolean, wires auto-registration in the DB constructor, rewrites withTraits to use Object.create to avoid phantom entries, and opts HyperDBTraceStore out via register: false. Includes registry tests.
Devtools DB discovery and UI integration
packages/hyperdb-devtool/src/components.tsx
Extends DevtoolsPanelInner with a discoverRegisteredDBs flag, adds getRegisteredDBOptions and useRegisteredDBOptions, merges registered and observed DB options into knownDBOptions. Refactors HyperDBDevtoolsPanel to use useOptionalDB and pass discoverRegisteredDBs when no explicit db prop is set.
Devtools discovery tests, spec, and config
packages/hyperdb/devtool.md, packages/hyperdb-devtool/src/components.test.tsx, packages/hyperdb-devtool/vite.config.ts, AGENTS.md, CLAUDE.md, .prettierignore, TODO.md, packages/hyperdb-doc/src/content/docs/runtime/db.md
Extends devtool.md spec with discovery and registry rules. Adds act() environment setup, registry cleanup hooks, and new component tests for DB selector discovery and explicit db prop precedence. Adds react-dom/client to Vite optimizeDeps. Includes minor doc formatting and agent config changes.

Demo Multi-Driver Persistence

Layer / File(s) Summary
StoreMode, PersistenceSnapshot, PersistenceMonitor, and context
packages/hyperdb-demo/src/store-mode.ts, packages/hyperdb-demo/src/persistence-monitor.ts, packages/hyperdb-demo/src/persistence-context.tsx, packages/hyperdb-demo/src/db.ts
Defines StoreMode with four concrete tiers and localStorage read/write helpers; a PersistenceSnapshot type with an INITIAL constant; PersistenceMonitor as a useSyncExternalStore-compatible observable; and a PersistenceContext / usePersistence hook with no-op fallbacks for memory mode. Updates db.ts with traced action/selector factories and byIds indexes.
WA-SQLite worker backend and dependency
packages/hyperdb-demo/package.json, packages/hyperdb-demo/src/wa-sqlite-worker.ts, packages/hyperdb-demo/src/wa-sqlite.d.ts
Adds wa-sqlite dependency. Introduces a web worker module that manages OPFS-backed SQLite connection, handles exec and values RPC requests, and provides connection lifecycle management.
Store orchestration: init, hydration, and persistence mirroring
packages/hyperdb-demo/src/stores.ts
Implements createMemDb, hydrate (IndexedDB → memory), groupConsecutiveOps, traced persistence operations (hydrate:scan, hydrate:load, persist:batch), and startPersisting (memory → IndexedDB commit mirroring with pagehide/beforeunload drain). Exports initStore returning { db, persistence }.
Demo app wiring and BenchmarkApp storage UI
packages/hyperdb-demo/src/main.tsx, packages/hyperdb-demo/src/BenchmarkApp.tsx
main.tsx replaces inline DB construction with initStore and wraps App in PersistenceProvider. BenchmarkApp adds a storage mode <select> with handleStoreModeChange (persists mode, triggers reload), conditional persistence status display showing queue depth or last-save metrics, and converts interactions to async hooks.
SQLite sort-key schema handling and persistence docs
packages/hyperdb/src/hyperdb/drivers/sqlite/async-sql-driver.ts, packages/hyperdb-doc/src/content/docs/guides/in-memory-persistence.md, packages/hyperdb-doc/src/content/docs/runtime/drivers.md
Changes AsyncSqlDriver.loadTables sort-key reconciliation to structural add/drop without row backfill; removes backfillSortKeyColumns method. Updates driver docs to describe AsyncSqlDriver and OPFS worker setup. Updates persistence guide with traced hydration/persist actions and asyncDispatch mirroring patterns.

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant initStore as initStore(mode)
  participant memDB as In-memory DB
  participant persistentDB as Persistent Driver
  participant monitor as PersistenceMonitor

  App->>initStore: await initStore(getStoredMode())
  initStore->>persistentDB: create & load persisted tables
  initStore->>memDB: create in-memory DB
  initStore->>memDB: hydrate:load projects/tasks
  persistentDB-->>memDB: scan byIds indices
  memDB->>persistentDB: subscribe to commit ops
  memDB->>persistentDB: persist:batch queued ops
  persistentDB-->>monitor: report pending/draining state
  initStore-->>App: { db: memDB, persistence }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Poem

🐇 Hop hop, I've dug a new den,
Where DBs register and speak again!
IndexedDB holds the carrots I've stored,
Devtools discover each table adored.
No op goes untracked in my warren so deep—
The registry hums while the UI's asleep. 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add devtool autodiscovery' accurately reflects the main objective: adding automatic database discovery to the HyperDB devtools panel, which is the primary feature implemented across registry, DB registration, and component discovery mechanisms.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch quolpr/devtool-autodiscovery

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/hyperdb-demo/src/stores.ts`:
- Around line 190-197: The batch is being removed from the pending queue via
pending.shift() before persistBatch succeeds, causing lost batches on failure.
Move the pending.shift() call to execute only after the persistBatch await
completes successfully, either by placing it after the await statement within
the try block or by only removing the batch once persistence is confirmed. This
ensures failed batches remain in the queue and can be retried.

In `@packages/hyperdb-doc/src/content/docs/runtime/db.md`:
- Around line 32-39: The DB options table in
packages/hyperdb-doc/src/content/docs/runtime/db.md is missing documentation for
the `register` option that was introduced in this feature set. Add a new row to
the options table following the same format as the existing entries like
`runtimeRowsValidation`, `freezeArgs`, and `freezeRows`, including the option
name `register`, its default value, and a clear description of what this option
does (controlling the auto-registration behavior of the database).

In `@packages/hyperdb/src/hyperdb/tracing/registry.ts`:
- Around line 79-81: The listener invocation in the registry notification loop
does not isolate failures, causing a throwing listener to abort the entire
registerHyperDB operation and cascade into DB construction failures. Wrap the
listener invocation call in a try-catch block within the for loop that iterates
over the registry.listeners array, and in the catch block, log or handle the
error gracefully without re-throwing so that subsequent listeners are still
notified and the registration completes successfully regardless of individual
listener failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ffa8f06-c0e2-4bf2-9d49-91fedb1ce1be

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70f59 and f6b8008.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • AGENTS.md
  • CLAUDE.md
  • packages/hyperdb-demo/src/BenchmarkApp.tsx
  • packages/hyperdb-demo/src/db.ts
  • packages/hyperdb-demo/src/main.tsx
  • packages/hyperdb-demo/src/persistence-context.tsx
  • packages/hyperdb-demo/src/persistence-monitor.ts
  • packages/hyperdb-demo/src/store-mode.ts
  • packages/hyperdb-demo/src/stores.ts
  • packages/hyperdb-devtool/src/components.test.tsx
  • packages/hyperdb-devtool/src/components.tsx
  • packages/hyperdb-devtool/vite.config.ts
  • packages/hyperdb-doc/src/content/docs/database/writing-data.md
  • packages/hyperdb-doc/src/content/docs/index.mdx
  • packages/hyperdb-doc/src/content/docs/runtime/db.md
  • packages/hyperdb/devtool.md
  • packages/hyperdb/src/hyperdb/runtime/db.ts
  • packages/hyperdb/src/hyperdb/tracing/db-info.ts
  • packages/hyperdb/src/hyperdb/tracing/index.ts
  • packages/hyperdb/src/hyperdb/tracing/registry.test.ts
  • packages/hyperdb/src/hyperdb/tracing/registry.ts
  • packages/hyperdb/src/hyperdb/tracing/store.ts
  • packages/hyperdb/src/react/context.ts

Comment thread packages/hyperdb-demo/src/stores.ts
Comment thread packages/hyperdb-doc/src/content/docs/runtime/db.md
Comment thread packages/hyperdb/src/hyperdb/tracing/registry.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/hyperdb-demo/src/stores.ts (1)

363-376: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider returning cleanup for resource management.

startPersisting() returns a cleanup function that removes event listeners and the DB subscription. Currently it's discarded, which prevents proper teardown if the store is ever reinitialized without a page reload.

For a demo that reloads on mode change this is acceptable, but if reinitialization without reload is ever needed, this would leak resources.

Optional: return cleanup in InitResult
 export type InitResult = {
   db: SubscribableDB;
   persistence: PersistenceMonitor | null;
+  cleanup?: () => void;
 };
-    startPersisting(persistentDB, memDB, persistence);
+    const cleanup = startPersisting(persistentDB, memDB, persistence);

-    return { db: memDB, persistence };
+    return { db: memDB, persistence, cleanup };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hyperdb-demo/src/stores.ts` around lines 363 - 376, The
startPersisting() function returns a cleanup function that is currently being
discarded, which can cause resource leaks if initStore() is called again without
a page reload. Capture the return value from the startPersisting() call, add a
cleanup property to the InitResult object being returned, and include this
cleanup function in the return statement alongside db and persistence. This
allows consumers of initStore() to properly invoke the cleanup function when the
store needs to be reinitialized.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/hyperdb-demo/src/BenchmarkApp.tsx`:
- Around line 181-199: The select element with value={storeMode} and
onChange={handleStoreModeChange} lacks an associated accessible label for screen
readers and accessibility tools. Wrap both the span element containing "Driver"
text and the select element in a <label> element to properly associate the label
with the form control, following the same pattern used for other form controls
in the component.

In `@packages/hyperdb-demo/src/wa-sqlite-worker.ts`:
- Around line 109-131: The message event listener in the worker currently
processes requests concurrently without serialization, which causes SQL
operations to interleave on the same database connection. To fix this, implement
a request queue mechanism that serializes all incoming requests. Create a queue
to hold pending WorkerRequest objects and a processing loop that dequeues and
processes requests sequentially. The event listener should add requests to the
queue instead of immediately executing the async operation, ensuring that each
call to getConnection, execSQL, and readValues completes before the next request
begins execution.
- Around line 53-71: The createConnection promise caching in the getConnection
function causes permanent failures if the initial connection attempt rejects,
since the rejected promise is cached and never retried. To fix this, add error
handling to the connectionPromise assignment in the getConnection function that
resets connectionPromise back to null when createConnection rejects, allowing
subsequent calls to attempt a fresh connection instead of returning the cached
rejected promise.

---

Nitpick comments:
In `@packages/hyperdb-demo/src/stores.ts`:
- Around line 363-376: The startPersisting() function returns a cleanup function
that is currently being discarded, which can cause resource leaks if initStore()
is called again without a page reload. Capture the return value from the
startPersisting() call, add a cleanup property to the InitResult object being
returned, and include this cleanup function in the return statement alongside db
and persistence. This allows consumers of initStore() to properly invoke the
cleanup function when the store needs to be reinitialized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d3ba46bb-9f0d-4498-a59a-ecae94ab5994

📥 Commits

Reviewing files that changed from the base of the PR and between f6b8008 and cc2515f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • .prettierignore
  • TODO.md
  • packages/hyperdb-demo/package.json
  • packages/hyperdb-demo/src/BenchmarkApp.tsx
  • packages/hyperdb-demo/src/db.ts
  • packages/hyperdb-demo/src/store-mode.ts
  • packages/hyperdb-demo/src/stores.ts
  • packages/hyperdb-demo/src/wa-sqlite-worker.ts
  • packages/hyperdb-demo/src/wa-sqlite.d.ts
  • packages/hyperdb-devtool/src/components.tsx
  • packages/hyperdb-doc/src/content/docs/guides/in-memory-persistence.md
  • packages/hyperdb-doc/src/content/docs/runtime/drivers.md
  • packages/hyperdb/src/hyperdb/drivers/sqlite/async-sql-driver.ts
💤 Files with no reviewable changes (1)
  • packages/hyperdb/src/hyperdb/drivers/sqlite/async-sql-driver.ts
✅ Files skipped from review due to trivial changes (1)
  • .prettierignore
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/hyperdb-demo/src/db.ts
  • packages/hyperdb-devtool/src/components.tsx

Comment on lines +181 to +199
<div className="flex min-w-[200px] flex-col justify-center gap-2 border-t border-line bg-base/60 p-6 text-left sm:border-l sm:border-t-0">
<span className={LABEL}>Driver</span>
<select
value={storeMode}
onChange={handleStoreModeChange}
className="h-10 w-full cursor-pointer rounded-md border border-line bg-base px-3 font-mono text-sm text-ink outline-none transition focus-visible:border-signal/60 focus-visible:ring-2 focus-visible:ring-signal/20"
>
<option value="idb">IndexedDB</option>
<option value="idb-inmem">IndexedDB + in-memory</option>
<option value="wa-sqlite">WA-SQLite OPFS</option>
<option value="wa-sqlite-inmem">WA-SQLite OPFS + in-memory</option>
</select>
<div className="flex items-center gap-2">
<span
className={`size-2 shrink-0 rounded-full ${storageStatus.dot}`}
/>
<p className="text-xs text-faint">{storageStatus.text}</p>
</div>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing accessible label for the driver select.

The <select> element lacks an associated label. Other form controls in this component use a <label> wrapper pattern for accessibility.

Proposed fix
-        <div className="flex min-w-[200px] flex-col justify-center gap-2 border-t border-line bg-base/60 p-6 text-left sm:border-l sm:border-t-0">
-          <span className={LABEL}>Driver</span>
+        <label className="flex min-w-[200px] flex-col justify-center gap-2 border-t border-line bg-base/60 p-6 text-left sm:border-l sm:border-t-0">
+          <span className={LABEL} aria-hidden="true">Driver</span>
           <select
+            aria-label="Driver"
             value={storeMode}

Or simply wrap the span-and-select portion in a <label> element.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hyperdb-demo/src/BenchmarkApp.tsx` around lines 181 - 199, The
select element with value={storeMode} and onChange={handleStoreModeChange} lacks
an associated accessible label for screen readers and accessibility tools. Wrap
both the span element containing "Driver" text and the select element in a
<label> element to properly associate the label with the form control, following
the same pattern used for other form controls in the component.

Comment on lines +53 to +71
let connectionPromise: Promise<Connection> | null = null;

async function createConnection(databaseName: string): Promise<Connection> {
const module = await SQLiteAsyncESMFactory({
locateFile: () => asyncSqlWasmUrl,
});
const sqlite3 = SQLite.Factory(module) as WaSQLiteAPI;
const vfs = new OriginPrivateFileSystemVFS();
sqlite3.vfs_register(vfs, true);

return {
sqlite3,
dbHandle: await sqlite3.open_v2(databaseName),
};
}

function getConnection(databaseName: string): Promise<Connection> {
connectionPromise ??= createConnection(databaseName);
return connectionPromise;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reset cached connection state when initialization fails.

If createConnection(...) rejects once, connectionPromise stays rejected and every later request fails until reload.

Suggested fix
 let connectionPromise: Promise<Connection> | null = null;

 function getConnection(databaseName: string): Promise<Connection> {
-  connectionPromise ??= createConnection(databaseName);
+  connectionPromise ??=
+    createConnection(databaseName).catch((error) => {
+      connectionPromise = null;
+      throw error;
+    });
   return connectionPromise;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hyperdb-demo/src/wa-sqlite-worker.ts` around lines 53 - 71, The
createConnection promise caching in the getConnection function causes permanent
failures if the initial connection attempt rejects, since the rejected promise
is cached and never retried. To fix this, add error handling to the
connectionPromise assignment in the getConnection function that resets
connectionPromise back to null when createConnection rejects, allowing
subsequent calls to attempt a fresh connection instead of returning the cached
rejected promise.

Comment on lines +109 to +131
self.addEventListener("message", (event: MessageEvent<WorkerRequest>) => {
const request = event.data;

void (async () => {
try {
const connection = await getConnection(request.databaseName);

if (request.type === "exec") {
await execSQL(connection, request.sql, request.params);
self.postMessage({ id: request.id, ok: true });
} else {
const rows = await readValues(connection, request.sql, request.values);
self.postMessage({ id: request.id, ok: true, rows });
}
} catch (error) {
self.postMessage({
id: request.id,
ok: false,
error: error instanceof Error ? error.message : String(error),
});
}
})();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Serialize worker requests on the shared SQLite connection.

Current message handling runs requests concurrently; SQL calls can interleave on one DB handle and break expected statement/transaction ordering.

Suggested fix
+self.addEventListener("message", (event: MessageEvent<WorkerRequest>) => {
+  const request = event.data;
+  requestQueue = requestQueue
+    .then(() => handleRequest(request))
+    .catch(() => {
+      // keep queue usable after failures
+    });
+});
+
+let requestQueue: Promise<void> = Promise.resolve();
+
+async function handleRequest(request: WorkerRequest): Promise<void> {
+  try {
+    const connection = await getConnection(request.databaseName);
+
+    if (request.type === "exec") {
+      await execSQL(connection, request.sql, request.params);
+      self.postMessage({ id: request.id, ok: true });
+    } else {
+      const rows = await readValues(connection, request.sql, request.values);
+      self.postMessage({ id: request.id, ok: true, rows });
+    }
+  } catch (error) {
+    self.postMessage({
+      id: request.id,
+      ok: false,
+      error: error instanceof Error ? error.message : String(error),
+    });
+  }
+}
-
-self.addEventListener("message", (event: MessageEvent<WorkerRequest>) => {
-  const request = event.data;
-
-  void (async () => {
-    try {
-      const connection = await getConnection(request.databaseName);
-
-      if (request.type === "exec") {
-        await execSQL(connection, request.sql, request.params);
-        self.postMessage({ id: request.id, ok: true });
-      } else {
-        const rows = await readValues(connection, request.sql, request.values);
-        self.postMessage({ id: request.id, ok: true, rows });
-      }
-    } catch (error) {
-      self.postMessage({
-        id: request.id,
-        ok: false,
-        error: error instanceof Error ? error.message : String(error),
-      });
-    }
-  })();
-});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hyperdb-demo/src/wa-sqlite-worker.ts` around lines 109 - 131, The
message event listener in the worker currently processes requests concurrently
without serialization, which causes SQL operations to interleave on the same
database connection. To fix this, implement a request queue mechanism that
serializes all incoming requests. Create a queue to hold pending WorkerRequest
objects and a processing loop that dequeues and processes requests sequentially.
The event listener should add requests to the queue instead of immediately
executing the async operation, ensuring that each call to getConnection,
execSQL, and readValues completes before the next request begins execution.

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.

1 participant