11import { NextFunction , Request , Response } from 'express'
22
3- import {
4- isSegmentSubproject ,
5- populateSegmentRelations ,
6- } from '@crowd/data-access-layer/src/segments'
3+ import { isSegmentSubproject } from '@crowd/data-access-layer/src/segments'
74import { getServiceChildLogger } from '@crowd/logging'
85
96import { IRepositoryOptions } from '../database/repositories/IRepositoryOptions'
@@ -80,6 +77,13 @@ async function resolveToLeafSegments(
8077 return 'projectGroup'
8178 }
8279
80+ // nullActivityTypes: strips the eagerly deep-cloned activityTypes from each record.
81+ // findInIds() and populateSegmentRelations() call cloneDeep(DEFAULT_ACTIVITY_TYPE_SETTINGS)
82+ // for every leaf segment. With 10K+ segments per request (e.g. merge endpoint) this
83+ // allocates gigabytes and causes OOM. Downstream code (getActivityTypes) was already
84+ // receiving null here before this PR, so null is the safe value to return.
85+ const nullActivityTypes = ( record : any ) => ( { ...record , activityTypes : null } )
86+
8387 if ( nonLeaf . length === 0 ) {
8488 log . debug (
8589 {
@@ -88,7 +92,7 @@ async function resolveToLeafSegments(
8892 } ,
8993 `All segments are already leaf — used as-is in DB queries` ,
9094 )
91- return fetched
95+ return fetched . map ( nullActivityTypes )
9296 }
9397
9498 const leafRecords = await segmentRepository . getSegmentSubprojects ( segmentIds )
@@ -103,5 +107,5 @@ async function resolveToLeafSegments(
103107 'Non-leaf segments resolved to leaf sub-projects' ,
104108 )
105109
106- return leafRecords . map ( populateSegmentRelations )
110+ return leafRecords . map ( nullActivityTypes )
107111}
0 commit comments