@@ -331,63 +331,114 @@ function getDurableObjectClassNames(
331331	allWorkerOpts : PluginWorkerOptions [ ] 
332332) : DurableObjectClassNames  { 
333333	const  serviceClassNames : DurableObjectClassNames  =  new  Map ( ) ; 
334- 	for  ( const  workerOpts  of  allWorkerOpts )  { 
335- 		const  workerServiceName  =  getUserServiceName ( workerOpts . core . name ) ; 
336- 		for  ( const  designator  of  Object . values ( 
337- 			workerOpts . do . durableObjects  ??  { } 
338- 		) )  { 
339- 			const  { 
340- 				className, 
341- 				// Fallback to current worker service if name not defined 
342- 				serviceName =  workerServiceName , 
343- 				enableSql, 
344- 				unsafeUniqueKey, 
345- 				unsafePreventEviction, 
346- 				container, 
347- 			}  =  normaliseDurableObject ( designator ) ; 
348- 			// Get or create `Map` mapping class name to optional unsafe unique key 
349- 			let  classNames  =  serviceClassNames . get ( serviceName ) ; 
350- 			if  ( classNames  ===  undefined )  { 
351- 				classNames  =  new  Map ( ) ; 
352- 				serviceClassNames . set ( serviceName ,  classNames ) ; 
353- 			} 
354- 			if  ( classNames . has ( className ) )  { 
355- 				// If we've already seen this class in this service, make sure the 
356- 				// unsafe unique keys and unsafe prevent eviction values match 
357- 				const  existingInfo  =  classNames . get ( className ) ; 
358- 				if  ( existingInfo ?. enableSql  !==  enableSql )  { 
359- 					throw  new  MiniflareCoreError ( 
360- 						"ERR_DIFFERENT_STORAGE_BACKEND" , 
361- 						`Different storage backends defined for Durable Object "${ className } ${ serviceName } ${ JSON . stringify (  
362- 							enableSql  
363- 						) }   and ${ JSON . stringify ( existingInfo ?. enableSql ) } 
364- 					) ; 
334+ 
335+ 	const  allDurableObjects  =  allWorkerOpts 
336+ 		. flatMap ( ( workerOpts )  =>  { 
337+ 			const  workerServiceName  =  getUserServiceName ( workerOpts . core . name ) ; 
338+ 
339+ 			return  Object . values ( workerOpts . do . durableObjects  ??  { } ) . map ( 
340+ 				( workerDODesignator )  =>  { 
341+ 					const  doInfo  =  normaliseDurableObject ( workerDODesignator ) ; 
342+ 					if  ( doInfo . serviceName  ===  undefined )  { 
343+ 						// Fallback to current worker service if name not defined 
344+ 						doInfo . serviceName  =  workerServiceName ; 
345+ 					} 
346+ 					return  { 
347+ 						doInfo, 
348+ 						workerRawName : workerOpts . core . name , 
349+ 					} ; 
365350				} 
366- 				if  ( existingInfo ?. unsafeUniqueKey  !==  unsafeUniqueKey )  { 
367- 					throw  new  MiniflareCoreError ( 
368- 						"ERR_DIFFERENT_UNIQUE_KEYS" , 
369- 						`Multiple unsafe unique keys defined for Durable Object "${ className } ${ serviceName } ${ JSON . stringify (  
370- 							unsafeUniqueKey  
371- 						) }   and ${ JSON . stringify ( existingInfo ?. unsafeUniqueKey ) } 
372- 					) ; 
351+ 			) ; 
352+ 		} ) 
353+ 		// We sort the list of durable objects because we want the durable objects without a scriptName or a scriptName 
354+ 		// that matches the raw worker's name (meaning that they are defined within their worker) to be processed first 
355+ 		. sort ( ( {  doInfo,  workerRawName } )  => 
356+ 			doInfo . scriptName  ===  undefined  ||  doInfo . scriptName  ===  workerRawName 
357+ 				? - 1 
358+ 				: 0 
359+ 		) 
360+ 		. map ( ( {  doInfo } )  =>  doInfo ) ; 
361+ 
362+ 	for  ( const  doInfo  of  allDurableObjects )  { 
363+ 		const  {  className,  serviceName,  container,  ...doConfigs  }  =  doInfo ; 
364+ 		// We know that the service name is always defined (since if it is not we do default it to the current worker service) 
365+ 		assert ( serviceName ) ; 
366+ 		// Get or create `Map` mapping class name to optional unsafe unique key 
367+ 		let  classNames  =  serviceClassNames . get ( serviceName ) ; 
368+ 		if  ( classNames  ===  undefined )  { 
369+ 			classNames  =  new  Map ( ) ; 
370+ 			serviceClassNames . set ( serviceName ,  classNames ) ; 
371+ 		} 
372+ 
373+ 		if  ( classNames . has ( className ) )  { 
374+ 			// If we've already seen this class in this service, make sure the 
375+ 			// unsafe unique keys and unsafe prevent eviction values match 
376+ 			const  existingInfo  =  classNames . get ( className ) ; 
377+ 
378+ 			const  isDoUnacceptableDiff  =  ( 
379+ 				field : Extract < 
380+ 					keyof  typeof  doConfigs , 
381+ 					"enableSql"  |  "unsafeUniqueKey"  |  "unsafePreventEviction" 
382+ 				> 
383+ 			)  =>  { 
384+ 				if  ( ! existingInfo )  { 
385+ 					return  false ; 
373386				} 
374- 				if  ( existingInfo ?. unsafePreventEviction  !==  unsafePreventEviction )  { 
375- 					throw  new  MiniflareCoreError ( 
376- 						"ERR_DIFFERENT_PREVENT_EVICTION" , 
377- 						`Multiple unsafe prevent eviction values defined for Durable Object "${ className } ${ serviceName } ${ JSON . stringify (  
378- 							unsafePreventEviction  
379- 						) }   and ${ JSON . stringify ( existingInfo ?. unsafePreventEviction ) } 
380- 					) ; 
387+ 
388+ 				const  same  =  existingInfo [ field ]  ===  doConfigs [ field ] ; 
389+ 				if  ( same )  { 
390+ 					return  false ; 
381391				} 
382- 			}  else  { 
383- 				// Otherwise, just add it 
384- 				classNames . set ( className ,  { 
385- 					enableSql, 
386- 					unsafeUniqueKey, 
387- 					unsafePreventEviction, 
388- 					container, 
389- 				} ) ; 
392+ 
393+ 				const  oneIsUndefined  = 
394+ 					existingInfo [ field ]  ===  undefined  ||  doConfigs [ field ]  ===  undefined ; 
395+ 
396+ 				// If one of the configurations is `undefined` (either the current one or the existing one) then there we 
397+ 				// want to consider this as an acceptable difference since we might be in a potentially valid situation in 
398+ 				// which worker A defines a DO with a config, while worker B simply uses the DO from worker A but without 
399+ 				// providing the configuration (thus leaving it `undefined`) (this for example is exactly what Wrangler does 
400+ 				// with the implicitly defined `enableSql` flag) 
401+ 				if  ( oneIsUndefined )  { 
402+ 					return  false ; 
403+ 				} 
404+ 
405+ 				return  true ; 
406+ 			} ; 
407+ 
408+ 			if  ( isDoUnacceptableDiff ( "enableSql" ) )  { 
409+ 				throw  new  MiniflareCoreError ( 
410+ 					"ERR_DIFFERENT_STORAGE_BACKEND" , 
411+ 					`Different storage backends defined for Durable Object "${ className } ${ serviceName } ${ JSON . stringify (  
412+ 						doConfigs . enableSql  
413+ 					) }   and ${ JSON . stringify ( existingInfo ?. enableSql ) } 
414+ 				) ; 
415+ 			} 
416+ 
417+ 			if  ( isDoUnacceptableDiff ( "unsafeUniqueKey" ) )  { 
418+ 				throw  new  MiniflareCoreError ( 
419+ 					"ERR_DIFFERENT_UNIQUE_KEYS" , 
420+ 					`Multiple unsafe unique keys defined for Durable Object "${ className } ${ serviceName } ${ JSON . stringify (  
421+ 						doConfigs . unsafeUniqueKey  
422+ 					) }   and ${ JSON . stringify ( existingInfo ?. unsafeUniqueKey ) } 
423+ 				) ; 
390424			} 
425+ 
426+ 			if  ( isDoUnacceptableDiff ( "unsafePreventEviction" ) )  { 
427+ 				throw  new  MiniflareCoreError ( 
428+ 					"ERR_DIFFERENT_PREVENT_EVICTION" , 
429+ 					`Multiple unsafe prevent eviction values defined for Durable Object "${ className } ${ serviceName } ${ JSON . stringify (  
430+ 						doConfigs . unsafePreventEviction  
431+ 					) }   and ${ JSON . stringify ( existingInfo ?. unsafePreventEviction ) } 
432+ 				) ; 
433+ 			} 
434+ 		}  else  { 
435+ 			// Otherwise, just add it 
436+ 			classNames . set ( className ,  { 
437+ 				enableSql : doConfigs . enableSql , 
438+ 				unsafeUniqueKey : doConfigs . unsafeUniqueKey , 
439+ 				unsafePreventEviction : doConfigs . unsafePreventEviction , 
440+ 				container, 
441+ 			} ) ; 
391442		} 
392443	} 
393444	return  serviceClassNames ; 
0 commit comments