@@ -371,6 +371,7 @@ export interface ServiceMap<in Services> extends Equal.Equal, Pipeable, Inspecta
371371 readonly _Services : Types . Contravariant < Services >
372372 }
373373 readonly mapUnsafe : ReadonlyMap < string , any >
374+ mutable : boolean
374375}
375376
376377/**
@@ -392,10 +393,11 @@ export interface ServiceMap<in Services> extends Equal.Equal, Pipeable, Inspecta
392393export const makeUnsafe = < Services = never > ( mapUnsafe : ReadonlyMap < string , any > ) : ServiceMap < Services > => {
393394 const self = Object . create ( Proto )
394395 self . mapUnsafe = mapUnsafe
396+ self . mutable = false
395397 return self
396398}
397399
398- const Proto : Omit < ServiceMap < never > , "mapUnsafe" > = {
400+ const Proto : Omit < ServiceMap < never > , "mapUnsafe" | "mutable" > = {
399401 ...PipeInspectableProto ,
400402 [ TypeId ] : {
401403 _Services : ( _ : never ) => _
@@ -558,11 +560,10 @@ export const add: {
558560 self : ServiceMap < Services > ,
559561 key : Key < I , S > ,
560562 service : Types . NoInfer < S >
561- ) : ServiceMap < Services | I > => {
562- const map = new Map ( self . mapUnsafe )
563- map . set ( key . key , service )
564- return makeUnsafe ( map )
565- } )
563+ ) : ServiceMap < Services | I > =>
564+ withMapUnsafe ( self , ( map ) => {
565+ map . set ( key . key , service )
566+ } ) )
566567
567568/**
568569 * @since 4.0.0
@@ -582,15 +583,14 @@ export const addOrOmit: {
582583 self : ServiceMap < Services > ,
583584 key : Key < I , S > ,
584585 service : Option . Option < Types . NoInfer < S > >
585- ) : ServiceMap < Services | I > => {
586- const map = new Map ( self . mapUnsafe )
587- if ( service . _tag === "None" ) {
588- map . delete ( key . key )
589- } else {
590- map . set ( key . key , service . value )
591- }
592- return makeUnsafe ( map )
593- } )
586+ ) : ServiceMap < Services | I > =>
587+ withMapUnsafe ( self , ( map ) => {
588+ if ( service . _tag === "None" ) {
589+ map . delete ( key . key )
590+ } else {
591+ map . set ( key . key , service . value )
592+ }
593+ } ) )
594594
595595/**
596596 * Get a service from the context that corresponds to the given key, or
@@ -844,9 +844,9 @@ export const merge: {
844844} = dual ( 2 , < Services , R1 > ( self : ServiceMap < Services > , that : ServiceMap < R1 > ) : ServiceMap < Services | R1 > => {
845845 if ( self . mapUnsafe . size === 0 ) return that as any
846846 if ( that . mapUnsafe . size === 0 ) return self as any
847- const map = new Map ( self . mapUnsafe )
848- that . mapUnsafe . forEach ( ( value , key ) => map . set ( key , value ) )
849- return makeUnsafe ( map )
847+ return withMapUnsafe ( self , ( map ) => {
848+ that . mapUnsafe . forEach ( ( value , key ) => map . set ( key , value ) )
849+ } )
850850} )
851851
852852/**
@@ -924,16 +924,14 @@ export const mergeAll = <T extends Array<unknown>>(
924924export const pick = < S extends ReadonlyArray < Key < any , any > > > (
925925 ...services : S
926926) =>
927- < Services > ( self : ServiceMap < Services > ) : ServiceMap < Services & Service . Identifier < S [ number ] > > => {
928- const map = new Map < string , any > ( )
929- const keySet = new Set ( services . map ( ( key ) => key . key ) )
930- self . mapUnsafe . forEach ( ( value , key ) => {
931- if ( keySet . has ( key ) ) {
932- map . set ( key , value )
933- }
927+ < Services > ( self : ServiceMap < Services > ) : ServiceMap < Services & Service . Identifier < S [ number ] > > =>
928+ withMapUnsafe ( self , ( map ) => {
929+ const keySet = new Set ( services . map ( ( key ) => key . key ) )
930+ map . forEach ( ( _ , key ) => {
931+ if ( keySet . has ( key ) ) return
932+ map . delete ( key )
933+ } )
934934 } )
935- return makeUnsafe ( map )
936- }
937935
938936/**
939937 * @example
@@ -964,11 +962,43 @@ export const pick = <S extends ReadonlyArray<Key<any, any>>>(
964962export const omit = < S extends ReadonlyArray < Key < any , any > > > (
965963 ...keys : S
966964) =>
967- < Services > ( self : ServiceMap < Services > ) : ServiceMap < Exclude < Services , Service . Identifier < S [ number ] > > > => {
968- const map = new Map ( self . mapUnsafe )
969- for ( let i = 0 ; i < keys . length ; i ++ ) {
970- map . delete ( keys [ i ] . key )
965+ < Services > ( self : ServiceMap < Services > ) : ServiceMap < Exclude < Services , Service . Identifier < S [ number ] > > > =>
966+ withMapUnsafe ( self , ( map ) => {
967+ for ( let i = 0 ; i < keys . length ; i ++ ) {
968+ map . delete ( keys [ i ] . key )
969+ }
970+ } )
971+
972+ /**
973+ * Perform a series of mutations on a `ServiceMap`. Prevents unnecessary copying
974+ * of the underlying map when multiple mutations are needed.
975+ *
976+ * @since 4.0.0
977+ * @category Utils
978+ */
979+ export const mutate : {
980+ < Services , B > (
981+ f : ( serviceMap : ServiceMap < Services > ) => ServiceMap < B >
982+ ) : < Services > ( self : ServiceMap < Services > ) => ServiceMap < B >
983+ < Services , B > ( self : ServiceMap < Services > , f : ( serviceMap : ServiceMap < Services > ) => ServiceMap < B > ) : ServiceMap < B >
984+ } = dual (
985+ 2 ,
986+ < Services , B > ( self : ServiceMap < Services > , f : ( serviceMap : ServiceMap < Services > ) => ServiceMap < B > ) : ServiceMap < B > => {
987+ const next = makeUnsafe < Services > ( new Map ( self . mapUnsafe ) )
988+ next . mutable = true
989+ const result = f ( next )
990+ result . mutable = false
991+ return result
971992 }
993+ )
994+
995+ const withMapUnsafe = < Services , B > ( self : ServiceMap < Services > , f : ( map : Map < string , any > ) => void ) : ServiceMap < B > => {
996+ if ( self . mutable ) {
997+ f ( self . mapUnsafe as any )
998+ return self as any
999+ }
1000+ const map = new Map ( self . mapUnsafe )
1001+ f ( map )
9721002 return makeUnsafe ( map )
9731003}
9741004
0 commit comments