import type { ParsingContext , ParsingExtension , ParsingExtensionKey , ParsingExtensionTransform , } from '@app-config/core' ; import { parseValue , Root , AppConfigError } from '@app-config/core' ; import { SchemaBuilder } from '@serafin/schema-builder' ; export function composeExtensions ( extensions ) { const composed = ( value , [ k ] , _ , context ) => { if ( k !== Root ) return false ; return ( _ , __ , source , baseExtensions ) => parseValue ( value , source , baseExtensions . concat ( extensions ) . filter ( ( v ) => v !== composed ) , { shouldFlatten : true } , context ) ; } ; return composed ; } export function named ( name , parsingExtension ) { Object . defineProperty ( parsingExtension , 'extensionName' , { value : name } ) ; return parsingExtension ; } export function forKey ( key , parsingExtension ) { const shouldApply = ( [ _ , k ] ) => { if ( typeof k !== 'string' ) return false ; if ( Array . isArray ( key ) ) { return key . includes ( k ) ; } return key === k ; } ; return ( value , currentKey , parentKeys , context ) => { if ( shouldApply ( currentKey ) ) { return parsingExtension ( value , currentKey , parentKeys , context ) ; } return false ; } ; } export function keysToPath ( keys ) { if ( keys . length === 0 ) return 'root' ; return ( keys . map ( ( [ , k ] ) => k ) . filter ( ( v ) => v ) . join ( '.' ) || 'root' ) ; } export class ParsingExtensionInvalidOptions extends AppConfigError { } export function validateOptions < T > ( builder , extension , { lazy = false } = { } ) { const validate = validationFunction ( builder ) ; return ( value , key , parentKeys , context ) => { return async ( parse , ... args ) => { let valid ; if ( lazy ) { valid = value ; } else { valid = ( await parse ( value ) ) . toJSON ( ) ; } validate ( valid , [ ... parentKeys , key ] ) ; const call = extension ( valid , key , parentKeys , context ) ; if ( call ) { return call ( parse , ... args ) ; } throw new AppConfigError ( `A parsing extension returned as non-applicable, when using validateOptions. This isn't supported.` ) ; } ; } ; } export type ValidationFunction < T > = ( value : any , parentKeys : ParsingExtensionKey [ ] ) => asserts value is T ; export function validationFunction < T > ( builder ) { const schema = builder ( SchemaBuilder ) ; schema . cacheValidationFunction ( ) ; return ( value , parentKeys ) : asserts value is T => { try { schema . validate ( value ) ; } catch ( error ) { const message = error instanceof Error ? error . message : 'unknown' ; throw new ParsingExtensionInvalidOptions ( `Validation failed in "${ keysToPath ( parentKeys ) }": ${ message }` ) ; } } ; }