File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -353,6 +353,13 @@ export class ConfigClient {
353353 this . close ( ) ;
354354 }
355355
356+ /**
357+ * Async dispose pattern support — use with `await using`.
358+ */
359+ async [ Symbol . asyncDispose ] ( ) : Promise < void > {
360+ this . close ( ) ;
361+ }
362+
356363 // --- Private helpers ---
357364
358365 private async fetchServerInfo ( ) : Promise < ServerInfo > {
Original file line number Diff line number Diff line change @@ -357,12 +357,19 @@ export class ConfigWatcher {
357357 * Dispose pattern support (TypeScript 5.2+).
358358 *
359359 * Calls stop() synchronously (best-effort). For full cleanup, prefer
360- * calling `await watcher.stop()` explicitly.
360+ * `await using` or calling `await watcher.stop()` explicitly.
361361 */
362362 [ Symbol . dispose ] ( ) : void {
363363 void this . stop ( ) ;
364364 }
365365
366+ /**
367+ * Async dispose pattern support — use with `await using`.
368+ */
369+ async [ Symbol . asyncDispose ] ( ) : Promise < void > {
370+ await this . stop ( ) ;
371+ }
372+
366373 private async loadSnapshot ( ) : Promise < void > {
367374 const resp = await this . callGetConfig ( {
368375 tenantId : this . tenantId ,
Original file line number Diff line number Diff line change @@ -382,6 +382,23 @@ describe("ConfigClient", () => {
382382 } ) ;
383383 } ) ;
384384
385+ describe ( "Symbol.asyncDispose" , ( ) => {
386+ it ( "closes both stubs and resolves" , async ( ) => {
387+ await client [ Symbol . asyncDispose ] ( ) ;
388+ expect ( configStub . close ) . toHaveBeenCalledTimes ( 1 ) ;
389+ expect ( serverStub . close ) . toHaveBeenCalledTimes ( 1 ) ;
390+ } ) ;
391+
392+ it ( "works with await using" , async ( ) => {
393+ await ( async ( ) => {
394+ await using c = client ;
395+ void c ;
396+ } ) ( ) ;
397+ expect ( configStub . close ) . toHaveBeenCalledTimes ( 1 ) ;
398+ expect ( serverStub . close ) . toHaveBeenCalledTimes ( 1 ) ;
399+ } ) ;
400+ } ) ;
401+
385402 describe ( "per-call timeout" , ( ) => {
386403 it ( "get() uses per-call timeout over client default" , async ( ) => {
387404 let capturedDeadline : number | undefined ;
Original file line number Diff line number Diff line change @@ -484,6 +484,33 @@ describe("ConfigWatcher", () => {
484484 } ) ;
485485 } ) ;
486486
487+ describe ( "Symbol.asyncDispose" , ( ) => {
488+ it ( "awaits stop() before resolving" , async ( ) => {
489+ const watcher = createWatcher ( ) ;
490+ mockGetConfigSuccess ( [ ] ) ;
491+ watcher . field ( "payments.fee" , Number , { default : 0.01 } ) ;
492+
493+ await watcher . start ( ) ;
494+ await watcher [ Symbol . asyncDispose ] ( ) ;
495+
496+ expect ( mockStream . cancel ) . toHaveBeenCalledOnce ( ) ;
497+ } ) ;
498+
499+ it ( "works with await using" , async ( ) => {
500+ const watcher = createWatcher ( ) ;
501+ mockGetConfigSuccess ( [ ] ) ;
502+ watcher . field ( "payments.fee" , Number , { default : 0.01 } ) ;
503+ await watcher . start ( ) ;
504+
505+ await ( async ( ) => {
506+ await using w = watcher ;
507+ void w ;
508+ } ) ( ) ;
509+
510+ expect ( mockStream . cancel ) . toHaveBeenCalledOnce ( ) ;
511+ } ) ;
512+ } ) ;
513+
487514 describe ( "processing changes" , ( ) => {
488515 it ( "updates fields on data events" , async ( ) => {
489516 const watcher = createWatcher ( ) ;
Original file line number Diff line number Diff line change 33 "target" : " ES2022" ,
44 "module" : " Node16" ,
55 "moduleResolution" : " Node16" ,
6- "lib" : [" ES2022" ],
6+ "lib" : [" ES2022" , " esnext.disposable " ],
77 "outDir" : " dist" ,
88 "rootDir" : " src" ,
99 "declaration" : true ,
You can’t perform that action at this time.
0 commit comments