The chain tap in packages/resolver/src/smoldot-db.ts now has two mechanisms for claiming its own traffic. The owned one is typed and id-scoped (REQUEST_ID_PREFIX, pendingDbRequests, extractDb). The newer one is a caller-supplied TapIntercept predicate, which makes the tap surrender demux ownership and re-export raw JSON-RPC shape as its public contract. State that belongs to the tap then lives outside it, which is why followSubscriptions sits in smoldot.ts and has to be cleared by hand in terminateSmoldot.
The deeper shape is two primitives the tap is already half-implementing. A tap.request(method, params) generalises extractDb, which today hardcodes chainHead_unstable_finalizedDatabase, and would turn the health poll into a plain await. A tap.subscribe(method, params, onEvent) would keep the subscription id inside the tap's closure and route matching followEvent notifications, which is what makeSideChannelIntercept plus followSubscriptions do today.
That deletes TapIntercept, frees smoldot.ts of JSON-RPC framing, and ties the subscription teardown to the tap's own lifetime. Roughly 80 lines move between the two files, plus test churn. Worth doing while there is exactly one caller. Renaming smoldot-db.ts to chain-tap.ts would also stop the drift, since the module is now the chain demux layer rather than a DB helper.
The chain tap in packages/resolver/src/smoldot-db.ts now has two mechanisms for claiming its own traffic. The owned one is typed and id-scoped (REQUEST_ID_PREFIX, pendingDbRequests, extractDb). The newer one is a caller-supplied TapIntercept predicate, which makes the tap surrender demux ownership and re-export raw JSON-RPC shape as its public contract. State that belongs to the tap then lives outside it, which is why followSubscriptions sits in smoldot.ts and has to be cleared by hand in terminateSmoldot.
The deeper shape is two primitives the tap is already half-implementing. A tap.request(method, params) generalises extractDb, which today hardcodes chainHead_unstable_finalizedDatabase, and would turn the health poll into a plain await. A tap.subscribe(method, params, onEvent) would keep the subscription id inside the tap's closure and route matching followEvent notifications, which is what makeSideChannelIntercept plus followSubscriptions do today.
That deletes TapIntercept, frees smoldot.ts of JSON-RPC framing, and ties the subscription teardown to the tap's own lifetime. Roughly 80 lines move between the two files, plus test churn. Worth doing while there is exactly one caller. Renaming smoldot-db.ts to chain-tap.ts would also stop the drift, since the module is now the chain demux layer rather than a DB helper.