VariableContext
Variable substitution is complex.
There are two types: path variables and file name variables.
Path variables use the ${NAME} syntax, while file name variables use the {name} syntax. (I have no idea why this is.)
A variable is defined by a Variable.pattern and a Variable.provider
Variable.provider is a function that takes a VariableContext and returns a string. It's used for substitution to get the actual path.
Variable.pattern is a regex pattern that can match any results of Variable.provider.
If Variable.pattern is null, Variable.provider is used to get the value. (Ie, we won't match against a pattern, but always against the realized value. In practice this is for stream name and namespace, because matching always performed at the stream level.)
Matching should be considered deprecated. It is only required for configurations that do not enable staging, which populate destination state by collecting metadata from object headers. It is extremely brittle and can break against malformed paths or paths that do not include enough variables to avoid clashes. If you run into a client issue which requires a path change anyway (a breaking change for some workflows), consider advising them to enable staging.