@@ -1695,18 +1695,22 @@ function normalizeAndValidateEnvironment(
16951695 validateDefines ( envName ) ,
16961696 { }
16971697 ) ,
1698- durable_objects : notInheritable (
1699- diagnostics ,
1700- topLevelEnv ,
1701- rawConfig ,
1702- rawEnv ,
1703- envName ,
1704- "durable_objects" ,
1705- validateBindingsProperty ( envName , validateDurableObjectBinding ) ,
1706- {
1707- bindings : [ ] ,
1708- }
1709- ) ,
1698+ durable_objects : ( ( ) => {
1699+ const durableObjects = notInheritable (
1700+ diagnostics ,
1701+ topLevelEnv ,
1702+ rawConfig ,
1703+ rawEnv ,
1704+ envName ,
1705+ "durable_objects" ,
1706+ validateDurableObjectsProperty ( envName , true ) ,
1707+ { bindings : [ ] }
1708+ ) ;
1709+ return {
1710+ ...durableObjects ,
1711+ bindings : durableObjects . bindings ?? [ ] ,
1712+ } ;
1713+ } ) ( ) ,
17101714 workflows : notInheritable (
17111715 diagnostics ,
17121716 topLevelEnv ,
@@ -2719,6 +2723,136 @@ const validateBindingsProperty =
27192723 return isValid ;
27202724 } ;
27212725
2726+ const DURABLE_OBJECTS_CODE_UPDATE_MAX_DELAY_SECONDS = 24 * 60 * 60 ;
2727+ // Absorbs float64 error in the millisecond precision check. Near the 24-hour
2728+ // maximum the error reaches ~1e-8 ms, so a tighter bound would reject valid
2729+ // values such as 65536.001.
2730+ const MILLISECOND_PRECISION_TOLERANCE = 1e-6 ;
2731+
2732+ const validateDurableObjectsProperty =
2733+ (
2734+ envName : string ,
2735+ allowMissingBindings = false ,
2736+ allowCodeUpdateStrategy = true
2737+ ) : ValidatorFn =>
2738+ ( diagnostics , field , value , config ) => {
2739+ const fieldPath =
2740+ config === undefined ? `${ field } ` : `env.${ envName } .${ field } ` ;
2741+
2742+ if ( value === undefined ) {
2743+ return true ;
2744+ }
2745+ if ( typeof value !== "object" || value === null || Array . isArray ( value ) ) {
2746+ diagnostics . errors . push (
2747+ `The field "${ fieldPath } " should be an object but got ${ JSON . stringify ( value ) } .`
2748+ ) ;
2749+ return false ;
2750+ }
2751+
2752+ validateAdditionalProperties (
2753+ diagnostics ,
2754+ fieldPath ,
2755+ Object . keys ( value ) ,
2756+ allowCodeUpdateStrategy
2757+ ? [ "bindings" , "code_update_strategy" ]
2758+ : [ "bindings" ]
2759+ ) ;
2760+
2761+ const bindingsContainer =
2762+ allowMissingBindings && ! hasProperty ( value , "bindings" )
2763+ ? { ...value , bindings : [ ] }
2764+ : value ;
2765+ let isValid = validateBindingsProperty (
2766+ envName ,
2767+ validateDurableObjectBinding
2768+ ) ( diagnostics , field , bindingsContainer , config ) ;
2769+ if ( ! allowCodeUpdateStrategy ) {
2770+ return isValid ;
2771+ }
2772+
2773+ if (
2774+ ! hasProperty ( value , "code_update_strategy" ) ||
2775+ value . code_update_strategy === undefined
2776+ ) {
2777+ return isValid ;
2778+ }
2779+
2780+ const strategy = value . code_update_strategy ;
2781+ const strategyPath = `${ fieldPath } .code_update_strategy` ;
2782+ if (
2783+ typeof strategy !== "object" ||
2784+ strategy === null ||
2785+ Array . isArray ( strategy )
2786+ ) {
2787+ diagnostics . errors . push (
2788+ `The field "${ strategyPath } " should be an object but got ${ JSON . stringify ( strategy ) } .`
2789+ ) ;
2790+ return false ;
2791+ }
2792+
2793+ validateAdditionalProperties (
2794+ diagnostics ,
2795+ strategyPath ,
2796+ Object . keys ( strategy ) ,
2797+ [ "mode" , "max_delay" ]
2798+ ) ;
2799+ isValid =
2800+ validateRequiredProperty (
2801+ diagnostics ,
2802+ strategyPath ,
2803+ "mode" ,
2804+ hasProperty ( strategy , "mode" ) ? strategy . mode : undefined ,
2805+ "string" ,
2806+ [ "immediate" , "deferred" ]
2807+ ) && isValid ;
2808+
2809+ const maxDelay = hasProperty ( strategy , "max_delay" )
2810+ ? strategy . max_delay
2811+ : undefined ;
2812+ const maxDelayHasValidType = validateOptionalProperty (
2813+ diagnostics ,
2814+ strategyPath ,
2815+ "max_delay" ,
2816+ maxDelay ,
2817+ "number"
2818+ ) ;
2819+ isValid = maxDelayHasValidType && isValid ;
2820+ if (
2821+ maxDelayHasValidType &&
2822+ typeof maxDelay === "number" &&
2823+ ( ! Number . isFinite ( maxDelay ) ||
2824+ maxDelay < 0 ||
2825+ maxDelay > DURABLE_OBJECTS_CODE_UPDATE_MAX_DELAY_SECONDS )
2826+ ) {
2827+ diagnostics . errors . push (
2828+ `Expected "${ strategyPath } .max_delay" to be between 0 and ${ DURABLE_OBJECTS_CODE_UPDATE_MAX_DELAY_SECONDS } seconds but got ${ JSON . stringify ( maxDelay ) } .`
2829+ ) ;
2830+ isValid = false ;
2831+ }
2832+ if (
2833+ maxDelayHasValidType &&
2834+ typeof maxDelay === "number" &&
2835+ Number . isFinite ( maxDelay ) &&
2836+ maxDelay >= 0 &&
2837+ maxDelay <= DURABLE_OBJECTS_CODE_UPDATE_MAX_DELAY_SECONDS
2838+ ) {
2839+ const milliseconds = maxDelay * 1000 ;
2840+ const roundedMilliseconds = Math . round ( milliseconds ) ;
2841+ if (
2842+ ( maxDelay > 0 && roundedMilliseconds === 0 ) ||
2843+ Math . abs ( milliseconds - roundedMilliseconds ) >
2844+ MILLISECOND_PRECISION_TOLERANCE
2845+ ) {
2846+ diagnostics . errors . push (
2847+ `Expected "${ strategyPath } .max_delay" to use millisecond precision but got ${ JSON . stringify ( maxDelay ) } .`
2848+ ) ;
2849+ isValid = false ;
2850+ }
2851+ }
2852+
2853+ return isValid ;
2854+ } ;
2855+
27222856const validateUnsafeSettings =
27232857 ( envName : string ) : ValidatorFn =>
27242858 ( diagnostics , field , value , config ) => {
@@ -6415,7 +6549,7 @@ const validatePreviewsConfig =
64156549 ) ;
64166550
64176551 isValid =
6418- validateBindingsProperty ( envName , validateDurableObjectBinding ) (
6552+ validateDurableObjectsProperty ( envName , false , false ) (
64196553 diagnostics ,
64206554 `${ field } .durable_objects` ,
64216555 previews . durable_objects ,
0 commit comments