@@ -39,6 +39,7 @@ import {
3939 type RpcResponse ,
4040 type RpcResult ,
4141} from './rpc.js' ;
42+ import { createPrioritizedRpcCall } from './rpc-scheduler.js' ;
4243import { openTreecrdtDb , type OpenTreecrdtDbOptions , type OpenTreecrdtDbResult } from './open.js' ;
4344import type {
4445 ClientMaterializationDispatcher ,
@@ -52,6 +53,7 @@ import type {
5253 NormalizedRuntimeOptions ,
5354 NormalizedStorageOptions ,
5455 RpcCall ,
56+ RpcCallOptions ,
5557 RuntimeMode ,
5658 SharedWorkerFactory ,
5759 StorageMode ,
@@ -324,15 +326,8 @@ async function createWorkerClient(opts: {
324326 > ( ) ;
325327 let terminalError : Error | null = null ;
326328 let closed = false ;
327- let callQueue : Promise < void > = Promise . resolve ( ) ;
328329
329330 const closedError = new Error ( CLIENT_CLOSED_ERROR ) ;
330- const settleQueue = < T > ( promise : Promise < T > ) : Promise < void > =>
331- promise . then (
332- ( ) => undefined ,
333- ( ) => undefined ,
334- ) ;
335-
336331 const callRaw = < M extends RpcMethod > ( method : M , params : RpcParams < M > ) : Promise < RpcResult < M > > => {
337332 if ( closed ) return Promise . reject ( closedError ) ;
338333 const id = nextId ++ ;
@@ -342,11 +337,7 @@ async function createWorkerClient(opts: {
342337 worker . postMessage ( { id, method, params } satisfies RpcRequest < M > ) ;
343338 } ) ;
344339 } ;
345- const call = < M extends RpcMethod > ( method : M , params : RpcParams < M > ) : Promise < RpcResult < M > > => {
346- const run = callQueue . then ( ( ) => callRaw ( method , params ) ) ;
347- callQueue = settleQueue ( run ) ;
348- return run ;
349- } ;
340+ const call = createPrioritizedRpcCall ( callRaw ) ;
350341
351342 const onMessage = ( ev : MessageEvent < RpcResponse | RpcPushMessage > ) => {
352343 const data = ev . data ;
@@ -744,7 +735,7 @@ export async function buildDirectClient(
744735 throw wrapError ( method , err ) ;
745736 }
746737 } ;
747- const call : RpcCall = ( method , params ) => {
738+ const call : RpcCall = ( method , params , _options ) => {
748739 const run = callQueue . then ( ( ) => runDirectCall ( method , params ) ) ;
749740 callQueue = settleQueue ( run ) ;
750741 return run ;
@@ -775,7 +766,8 @@ export async function buildDirectClient(
775766
776767// --- helpers
777768
778- function makeTreecrdtClientFromCall ( opts : {
769+ /** @internal Exported for deterministic client scheduling tests. */
770+ export function makeTreecrdtClientFromCall ( opts : {
779771 mode : ClientMode ;
780772 runtime : RuntimeMode ;
781773 storage : StorageMode ;
@@ -806,18 +798,20 @@ function makeTreecrdtClientFromCall(opts: {
806798 localWriters . set ( key , next ) ;
807799 return next ;
808800 } ;
801+ const foregroundCall : RpcCall = ( method , params ) =>
802+ call ( method , params , { priority : 'foreground' } ) ;
809803
810804 const opsSinceImpl = async ( lamport : number , root ?: string ) => {
811- const rows = await call ( 'opsSince' , [ lamport , root ] ) ;
805+ const rows = await foregroundCall ( 'opsSince' , [ lamport , root ] ) ;
812806 return decodeSqliteOps ( rows ) ;
813807 } ;
814- const opRefsAllImpl = async ( ) => decodeSqliteOpRefs ( await call ( 'opRefsAll' , [ ] ) ) ;
808+ const opRefsAllImpl = async ( ) => decodeSqliteOpRefs ( await foregroundCall ( 'opRefsAll' , [ ] ) ) ;
815809 const opRefsChildrenImpl = async ( parent : string ) =>
816- decodeSqliteOpRefs ( await call ( 'opRefsChildren' , [ parent ] ) ) ;
810+ decodeSqliteOpRefs ( await foregroundCall ( 'opRefsChildren' , [ parent ] ) ) ;
817811 const opsByOpRefsImpl = async ( opRefs : Uint8Array [ ] ) =>
818- decodeSqliteOps ( await call ( 'opsByOpRefs' , [ opRefs . map ( ( r ) => Array . from ( r ) ) ] ) ) ;
812+ decodeSqliteOps ( await foregroundCall ( 'opsByOpRefs' , [ opRefs . map ( ( r ) => Array . from ( r ) ) ] ) ) ;
819813 const treeChildrenImpl = async ( parent : string ) =>
820- decodeSqliteNodeIds ( await call ( 'treeChildren' , [ parent ] ) ) ;
814+ decodeSqliteNodeIds ( await foregroundCall ( 'treeChildren' , [ parent ] ) ) ;
821815 const treeChildrenPageImpl = async (
822816 parent : string ,
823817 cursor : { orderKey : Uint8Array ; node : Uint8Array } | null ,
@@ -826,34 +820,42 @@ function makeTreecrdtClientFromCall(opts: {
826820 const rpcCursor = cursor
827821 ? { orderKey : Array . from ( cursor . orderKey ) , node : Array . from ( cursor . node ) }
828822 : null ;
829- return decodeSqliteTreeChildRows ( await call ( 'treeChildrenPage' , [ parent , rpcCursor , limit ] ) ) ;
823+ return decodeSqliteTreeChildRows (
824+ await foregroundCall ( 'treeChildrenPage' , [ parent , rpcCursor , limit ] ) ,
825+ ) ;
830826 } ;
831- const treeDumpImpl = async ( ) => decodeSqliteTreeRows ( await call ( 'treeDump' , [ ] ) ) ;
832- const treeNodeCountImpl = async ( ) => Number ( await call ( 'treeNodeCount' , [ ] ) ) ;
827+ const treeDumpImpl = async ( ) => decodeSqliteTreeRows ( await foregroundCall ( 'treeDump' , [ ] ) ) ;
828+ const treeNodeCountImpl = async ( ) => Number ( await foregroundCall ( 'treeNodeCount' , [ ] ) ) ;
833829 const treeParentImpl = async ( node : string ) => {
834- const result = await call ( 'treeParent' , [ node ] ) ;
830+ const result = await foregroundCall ( 'treeParent' , [ node ] ) ;
835831 if ( result === null ) return null ;
836832 return nodeIdFromBytes16 ( toRpcBytes ( result ) ) ;
837833 } ;
838- const treeExistsImpl = async ( node : string ) => Boolean ( await call ( 'treeExists' , [ node ] ) ) ;
834+ const treeExistsImpl = async ( node : string ) =>
835+ Boolean ( await foregroundCall ( 'treeExists' , [ node ] ) ) ;
839836 const treeGetPayloadImpl = async ( node : string ) => {
840- const result = await call ( 'treePayload' , [ node ] ) ;
837+ const result = await foregroundCall ( 'treePayload' , [ node ] ) ;
841838 return result === null ? null : toRpcBytes ( result ) ;
842839 } ;
843- const headLamportImpl = async ( ) => Number ( await call ( 'headLamport' , [ ] ) ) ;
840+ const headLamportImpl = async ( ) => Number ( await foregroundCall ( 'headLamport' , [ ] ) ) ;
844841 const replicaMaxCounterImpl = async ( replica : Operation [ 'meta' ] [ 'id' ] [ 'replica' ] ) =>
845- Number ( await call ( 'replicaMaxCounter' , [ Array . from ( encodeReplica ( replica ) ) ] ) ) ;
842+ Number ( await foregroundCall ( 'replicaMaxCounter' , [ Array . from ( encodeReplica ( replica ) ) ] ) ) ;
846843 const appendManyImpl = async ( operations : Operation [ ] , writeOpts ?: WriteOptions ) => {
844+ const callOptions = writeOpts ?. priority ? { priority : writeOpts . priority } : undefined ;
847845 if ( operations . length <= APPEND_MANY_RPC_CHUNK_SIZE ) {
848- const outcome = await call ( 'appendMany' , [ operations ] ) ;
846+ const outcome = await call ( 'appendMany' , [ operations ] , callOptions ) ;
849847 materialized . emitOutcome ( outcome , writeOpts ?. writeId ) ;
850848 return ;
851849 }
852850
853851 const outcomes : MaterializationOutcome [ ] = [ ] ;
854852 for ( let start = 0 ; start < operations . length ; start += APPEND_MANY_RPC_CHUNK_SIZE ) {
855853 outcomes . push (
856- await call ( 'appendMany' , [ operations . slice ( start , start + APPEND_MANY_RPC_CHUNK_SIZE ) ] ) ,
854+ await call (
855+ 'appendMany' ,
856+ [ operations . slice ( start , start + APPEND_MANY_RPC_CHUNK_SIZE ) ] ,
857+ callOptions ,
858+ ) ,
857859 ) ;
858860 }
859861 materialized . emitOutcome ( mergeMaterializationOutcomes ( outcomes ) , writeOpts ?. writeId ) ;
@@ -928,7 +930,8 @@ function makeTreecrdtClientFromCall(opts: {
928930 runner,
929931 ops : {
930932 append : async ( op , writeOpts ?: WriteOptions ) => {
931- const outcome = await call ( 'append' , [ op ] ) ;
933+ const callOptions = writeOpts ?. priority ? { priority : writeOpts . priority } : undefined ;
934+ const outcome = await call ( 'append' , [ op ] , callOptions ) ;
932935 materialized . emitOutcome ( outcome , writeOpts ?. writeId ) ;
933936 } ,
934937 appendMany : appendManyImpl ,
0 commit comments