Files
logseq/docs/agent-guide/task--db-worker-nodejs-compatible.md
2026-03-12 15:10:55 +08:00

7.1 KiB

task--db-worker-nodejs-compatible

Goal

Make frontend.worker.db-worker and its dependencies run in both browser and Node.js. Add a Node.js daemon that can be started from the command line and exposes HTTP APIs to the same worker capabilities.

Scope

  • Primary target: src/main/frontend/worker/db_worker.cljs.
  • All dependencies used by db-worker: src/main/frontend/worker/**, src/main/frontend/worker_common/**, and any browser-only utilities used by those namespaces.
  • Callers: src/main/frontend/persist_db/browser.cljs, src/main/frontend/handler/worker.cljs, and any callers that assume a WebWorker or Comlink transport.

Refactor Items (Concrete Work List)

  1. Split worker core logic from runtime-specific host APIs.
    • Create a core module (e.g. frontend.worker.db-core) that contains thread-api functions and business logic.
    • Move all direct uses of js/self, js/location, js/navigator, importScripts, BroadcastChannel, and navigator.locks out of core.
  2. Add a platform adapter layer with a consistent interface for browser and Node.js.
    • Define frontend.worker.platform interface: storage, kv-store, broadcast, websocket, crypto, timers, and env flags.
    • Implement frontend.worker.platform.browser using OPFS, IDB, BroadcastChannel, navigator.locks, WebSocket, and globalThis.
    • Implement frontend.worker.platform.node using fs/promises, path, crypto, and ws.
  3. Abstract sqlite storage and VFS specifics.
    • Browser: keep OPFS SAH pool implementation.
    • Node: use file-backed sqlite storage (via sqlite-wasm Node VFS or a Node sqlite binding).
    • Route db path resolution through the platform adapter (data dir, per-repo paths).
  4. Replace importScripts bootstrap with an explicit init entrypoint.
    • Browser build still uses :web-worker, but entrypoint should call init! with a browser platform adapter.
    • Node build should call the same init! with a Node adapter.
  5. Normalize RPC and transport.
    • Define a transport-agnostic RPC layer that accepts a method name and args (transit string or direct args).
    • Keep Comlink for browser worker transport.
    • Add HTTP transport for Node (see daemon section).
  6. Update shared-service for non-browser environments.
    • Provide a "single-client" fallback for Node; no multi-client coordination is needed.
  7. Replace browser-only storage in RTC and crypto modules.
    • frontend.worker.rtc.crypt uses IDB/OPFS and should switch to the platform kv-store and file API.
    • Any other worker modules using js/navigator or OPFS should be routed through the platform adapter.
  8. Replace direct js/WebSocket usage with a platform websocket factory.
    • Browser: js/WebSocket.
    • Node: ws client with the same interface shape.
  9. Update caller-side initialization.
    • Add a Node-specific db worker client (e.g. frontend.persist-db.node or frontend.persist-db.remote) that talks to the HTTP daemon.
    • Keep browser frontend.persist-db.browser using WebWorker + Comlink.
  10. Build config changes.
    • Add a Node build target in shadow-cljs.edn for db-worker (e.g. :db-worker-node).
    • Ensure shared code compiles for :node-script or :node-library with the correct externs.
  11. Tests and fixtures.
    • Add unit tests for platform adapters and storage abstraction.
    • Add a minimal integration test that starts the Node daemon and exercises a small RPC call.

Refactor Steps (Milestones + Status)

Milestone 1: Architecture & Abstractions

  • TODO 1. Inventory db-worker dependencies and classify browser-only APIs.
  • TODO 2. Define a platform adapter interface (storage, kv, broadcast, websocket, crypto, timers, env flags).
  • TODO 3. Extract db-worker core logic into a platform-agnostic module (e.g. frontend.worker.db-core).

Milestone 2: Browser Path Parity

  • TODO 4. Implement frontend.worker.platform.browser.
  • TODO 5. Update db-worker entry to inject the platform adapter and call core init.
  • TODO 6. Route OPFS/IDB usage through the platform adapter in worker submodules.
  • TODO 7. Replace direct js/WebSocket usage with platform websocket factory.

Milestone 3: Node Path & Daemon

  • TODO 8. Implement frontend.worker.platform.node in single-client mode (no locks or BroadcastChannel).
  • TODO 9. Update shared-service to no-op/single-client behavior in Node.
  • TODO 10. Add Node build target in shadow-cljs.edn for db-worker.
  • TODO 11. Implement Node daemon entrypoint and HTTP server.
  • TODO 12. Add a Node client in frontend to call the daemon (HTTP + SSE/WS events).

Milestone 4: Validation

  • TODO 13. Add tests: adapter unit tests + daemon integration smoke test.
  • TODO 14. Verify browser worker path still works with Comlink.

Node.js Daemon Requirements

The db-worker should be runnable as a standalone process for Node.js environments.

Entry Point

  • Provide a CLI entry (example: bin/logseq-db-worker or node dist/db-worker-node.js).
  • CLI flags (suggested):
    • --host (default 127.0.0.1)
    • --port (default 8080)
    • --data-dir (path for sqlite files, required or default to ~/.logseq/db-worker)
    • --repo (optional: auto-open a repo on boot)
    • --rtc-ws-url (optional)
    • --log-level (default info)
    • --auth-token (optional; bearer token for HTTP)

Lifecycle

  1. Initialize platform adapter (Node).
  2. Initialize sqlite module and storage roots.
  3. Start HTTP server.
  4. Emit readiness when init completes.
  5. Graceful shutdown on SIGINT/SIGTERM (close dbs, flush logs).

HTTP API (Minimum)

Use HTTP for RPC and event delivery. Prefer a single generic RPC entrypoint to avoid one endpoint per method.

Required endpoints:

  • GET /healthz -> 200 OK when process is alive.
  • GET /readyz -> 200 OK only after sqlite init completes.
  • POST /v1/invoke
    • Request JSON:
      • method: string, e.g. "thread-api/create-or-open-db"
      • directPass: boolean
      • argsTransit: string (transit-encoded args) OR args: array for direct pass
    • Response JSON:
      • ok: boolean
      • resultTransit: string (transit-encoded result) when directPass=false
      • result: any (when directPass=true)
      • error: error object if failed

Event delivery options:

  • GET /v1/events using SSE for worker -> client events
    • Event payload should mirror current postMessage payloads in frontend.handler.worker.
    • Each event should be tagged with type and payload.
  • Alternatively, provide WS /v1/events with the same payload format.

Security

  • If --auth-token is provided, require Authorization: Bearer <token> for all endpoints except healthz and readyz.
  • Bind to localhost by default.

Notes on Compatibility Gaps

  • OPFS and IndexedDB do not exist in Node; file-backed storage and a Node KV store are required.
  • BroadcastChannel and navigator.locks are browser-only; Node should use a simpler single-client mode.
  • Comlink is browser-optimized; the Node daemon should use HTTP, not Comlink.

Success Criteria

  • Browser build continues to work with WebWorker + Comlink.
  • Node daemon can start from CLI, open a repo, and respond to at least:
    • list-db
    • create-or-open-db
    • q
    • transact
  • A minimal client can call the daemon and receive event notifications.