@@ -12,7 +12,6 @@ import {
1212} from '../bson' ;
1313import { type ProxyOptions } from '../cmap/connection' ;
1414import { CursorTimeoutContext } from '../cursor/abstract_cursor' ;
15- import { type ListCollectionsCursor } from '../cursor/list_collections_cursor' ;
1615import { getSocks , type SocksLib } from '../deps' ;
1716import { MongoOperationTimeoutError } from '../error' ;
1817import { type MongoClient , type MongoClientOptions } from '../mongo_client' ;
@@ -207,11 +206,19 @@ export class StateMachine {
207206 const mongocryptdManager = executor . _mongocryptdManager ;
208207 let result : Uint8Array | null = null ;
209208
210- while ( context . state !== MONGOCRYPT_CTX_DONE && context . state !== MONGOCRYPT_CTX_ERROR ) {
209+ // Typescript treats getters just like properties: Once you've tested it for equality
210+ // it cannot change. Which is exactly the opposite of what we use state and status for.
211+ // Every call to at least `addMongoOperationResponse` and `finalize` can change the state.
212+ // These wrappers let us write code more naturally and not add compiler exceptions
213+ // to conditions checks inside the state machine.
214+ const getStatus = ( ) => context . status ;
215+ const getState = ( ) => context . state ;
216+
217+ while ( getState ( ) !== MONGOCRYPT_CTX_DONE && getState ( ) !== MONGOCRYPT_CTX_ERROR ) {
211218 options . signal ?. throwIfAborted ( ) ;
212- debug ( `[context#${ context . id } ] ${ stateToString . get ( context . state ) || context . state } ` ) ;
219+ debug ( `[context#${ context . id } ] ${ stateToString . get ( getState ( ) ) || getState ( ) } ` ) ;
213220
214- switch ( context . state ) {
221+ switch ( getState ( ) ) {
215222 case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO : {
216223 const filter = deserialize ( context . nextMongoOperation ( ) ) ;
217224 if ( ! metaDataClient ) {
@@ -229,8 +236,11 @@ export class StateMachine {
229236
230237 for await ( const collInfo of collInfoCursor ) {
231238 context . addMongoOperationResponse ( serialize ( collInfo ) ) ;
239+ if ( getState ( ) === MONGOCRYPT_CTX_ERROR ) break ;
232240 }
233241
242+ if ( getState ( ) === MONGOCRYPT_CTX_ERROR ) break ;
243+
234244 context . finishMongoOperation ( ) ;
235245 break ;
236246 }
@@ -286,22 +296,21 @@ export class StateMachine {
286296
287297 case MONGOCRYPT_CTX_READY : {
288298 const finalizedContext = context . finalize ( ) ;
289- // @ts -expect-error finalize can change the state, check for error
290- if ( context . state === MONGOCRYPT_CTX_ERROR ) {
291- const message = context . status . message || 'Finalization error' ;
299+ if ( getState ( ) === MONGOCRYPT_CTX_ERROR ) {
300+ const message = getStatus ( ) . message || 'Finalization error' ;
292301 throw new MongoCryptError ( message ) ;
293302 }
294303 result = finalizedContext ;
295304 break ;
296305 }
297306
298307 default :
299- throw new MongoCryptError ( `Unknown state: ${ context . state } ` ) ;
308+ throw new MongoCryptError ( `Unknown state: ${ getState ( ) } ` ) ;
300309 }
301310 }
302311
303- if ( context . state === MONGOCRYPT_CTX_ERROR || result == null ) {
304- const message = context . status . message ;
312+ if ( getState ( ) === MONGOCRYPT_CTX_ERROR || result == null ) {
313+ const message = getStatus ( ) . message ;
305314 if ( ! message ) {
306315 debug (
307316 `unidentifiable error in MongoCrypt - received an error status from \`libmongocrypt\` but received no error message.`
@@ -535,7 +544,7 @@ export class StateMachine {
535544 ns : string ,
536545 filter : Document ,
537546 options ?: { timeoutContext ?: TimeoutContext } & Abortable
538- ) : ListCollectionsCursor < CollectionInfo > {
547+ ) : AsyncIterable < CollectionInfo > {
539548 const { db } = MongoDBCollectionNamespace . fromString ( ns ) ;
540549
541550 const cursor = client . db ( db ) . listCollections ( filter , {
0 commit comments